1/*
2 * Copyright (c) 2007 Rob Braun
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of Rob Braun nor the names of its contributors
14 *    may be used to endorse or promote products derived from this software
15 *    without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 */
29/*
30 * 28-Oct-2004
31 * DRI: Rob Braun <bbraun@synack.net>
32 */
33/*
34 * Portions Copyright 2006, Apple Computer, Inc.
35 * Christopher Ryan <ryanc@apple.com>
36*/
37
38#include "config.h"
39#include <unistd.h>
40#include "xar.h"
41#include "arcmod.h"
42#include "b64.h"
43#include "io.h"
44#include "archive.h"
45#include <errno.h>
46#include <string.h>
47#include <sys/types.h>
48
49/* FreeBSD Extended Attribute Headers */
50#ifdef HAVE_SYS_EXTATTR_H
51#include <sys/extattr.h>
52#endif
53#ifdef HAVE_LIBUTIL_H
54#include <libutil.h>
55#endif
56
57#ifdef HAVE_SYS_EXTATTR_H
58#include <sys/param.h>
59#include <sys/mount.h>
60#include <sys/extattr.h>
61#endif
62
63#ifdef HAVE_SYS_EXTATTR_H
64struct _fbsdattr_context{
65	const char *file;
66	const char *attrname;
67	void *buf;
68	int off;
69	int bufsz;
70	int ns;
71};
72
73#define FBSDATTR_CONTEXT(x) ((struct _fbsdattr_context *)(x))
74
75int32_t xar_fbsdattr_read(xar_t x, xar_file_t f, void *buf, size_t len, void *context) {
76	if( !FBSDATTR_CONTEXT(context)->buf ) {
77		FBSDATTR_CONTEXT(context)->bufsz = extattr_get_link(FBSDATTR_CONTEXT(context)->file, FBSDATTR_CONTEXT(context)->ns, FBSDATTR_CONTEXT(context)->attrname, NULL, 0);
78		if( FBSDATTR_CONTEXT(context)->bufsz < 0 )
79			return -1;
80		FBSDATTR_CONTEXT(context)->buf = malloc(FBSDATTR_CONTEXT(context)->bufsz);
81		if( !FBSDATTR_CONTEXT(context)->buf )
82			return -1;
83
84		FBSDATTR_CONTEXT(context)->bufsz = extattr_get_link(FBSDATTR_CONTEXT(context)->file, FBSDATTR_CONTEXT(context)->ns, FBSDATTR_CONTEXT(context)->attrname, FBSDATTR_CONTEXT(context)->buf, FBSDATTR_CONTEXT(context)->bufsz);
85	}
86
87	if( (FBSDATTR_CONTEXT(context)->bufsz - FBSDATTR_CONTEXT(context)->off) <= len ) {
88		int32_t ret;
89
90		ret = FBSDATTR_CONTEXT(context)->bufsz - FBSDATTR_CONTEXT(context)->off;
91		memcpy(buf, FBSDATTR_CONTEXT(context)->buf+FBSDATTR_CONTEXT(context)->off, ret);
92		FBSDATTR_CONTEXT(context)->off += ret;
93		return(ret);
94	} else {
95		memcpy(buf, FBSDATTR_CONTEXT(context)->buf+FBSDATTR_CONTEXT(context)->off, len);
96		FBSDATTR_CONTEXT(context)->buf += len;
97		return(len);
98	}
99
100}
101int32_t xar_fbsdattr_write(xar_t x, xar_file_t f, void *buf, size_t len, void *context) {
102	return extattr_set_link(FBSDATTR_CONTEXT(context)->file, FBSDATTR_CONTEXT(context)->ns, FBSDATTR_CONTEXT(context)->attrname, buf, len);
103}
104#endif
105
106int32_t xar_fbsdattr_archive(xar_t x, xar_file_t f, const char* file, const char *buffer, size_t len)
107{
108#ifdef HAVE_SYS_EXTATTR_H
109	char *buf = NULL;
110	int ret, retval=0, bufsz, i;
111#if defined(HAVE_STATVFS) && defined(HAVE_STRUCT_STATVFS_F_FSTYPENAME)
112	struct statvfs sfs;
113#else
114	struct statfs sfs;
115#endif
116	char *fsname = NULL;
117	int namespace = EXTATTR_NAMESPACE_USER;
118	struct _fbsdattr_context context;
119
120	memset(&context,0,sizeof(struct _fbsdattr_context));
121
122	/* no fbsdattr attributes for data to a buffer */
123	if(len)
124		return 0;
125	if(file == NULL)
126		return 0;
127
128	if( !xar_check_prop(x, "ea") )
129		return 0;
130
131TRYAGAIN:
132	/* extattr_list_link()'s man page does not define the return
133	 * value.  The kernel source comments say 0 for success, -1 for
134	 * failure.  However, the observed behavior is # of bytes
135	 * used, 0 if none, -1 on error.
136	 * Also, errno is not documented to be set to anything useful
137	 * if buf is too small.  We are using an undocumented "feature"
138	 * that if the data argument is NULL, it will return the number
139	 * of bytes that would have been written (beware, return value
140	 * does not indicate success or failure on it's own.  Need to
141	 * check the return value *and* the parameters.
142	 */
143	ret = extattr_list_link(file, namespace, NULL, 0);
144	if( ret < 0 ) {
145		if( namespace == EXTATTR_NAMESPACE_USER ) {
146			namespace = EXTATTR_NAMESPACE_SYSTEM;
147			goto TRYAGAIN;
148		} else {
149			/* If we get eperm on system namespace, don't
150			 * return error.  This is expected for normal
151			 * users trying to archive the system namespace
152			 * on freebsd 6.2.  On netbsd 3.1, they've decided
153			 * to return EOPNOTSUPP instead.
154			 */
155			if( errno == EPERM )
156				ret = 0;
157			else if( errno == EOPNOTSUPP )
158				ret = 0;
159			else {
160				xar_err_new(x);
161				xar_err_set_file(x, f);
162				xar_err_set_string(x, "Error archiving EA");
163				xar_err_callback(x, XAR_SEVERITY_NONFATAL, XAR_ERR_ARCHIVE_CREATION);
164				ret = 0;
165			}
166			goto BAIL;
167		}
168	}
169	bufsz = ret;
170	buf = malloc(bufsz);
171	if( !buf ) {
172		retval = -1;
173		goto BAIL;
174	}
175	memset(buf, 0, bufsz);
176	ret = extattr_list_link(file, namespace, buf, bufsz);
177	if( ret < 0 ) {
178		switch(errno) {
179		case ENOTSUP: retval=0; goto BAIL;
180		default: retval=-1; goto BAIL;
181		};
182	}
183	/* Even though 0 is a documented success, observed behavior
184	 * indicates 0 means all perms were satisfied, etc, but
185	 * no extattrs were found to list.
186	 */
187	if( ret == 0 ) {
188		if( namespace == EXTATTR_NAMESPACE_USER ) {
189			namespace = EXTATTR_NAMESPACE_SYSTEM;
190			goto TRYAGAIN;
191		} else {
192			/* If we get eperm on system namespace, don't
193			 * return error.  This is expected for normal
194			 * users trying to archive the system namespace
195			 * on freebsd 6.2.  On netbsd 3.1, they've decided
196			 * to return EOPNOTSUPP instead.
197			 */
198			if( errno == EPERM )
199				ret = 0;
200			else if( errno == EOPNOTSUPP )
201				ret = 0;
202			else {
203				xar_err_new(x);
204				xar_err_set_file(x, f);
205				xar_err_set_string(x, "Error archiving EA");
206				xar_err_callback(x, XAR_SEVERITY_NONFATAL, XAR_ERR_ARCHIVE_CREATION);
207				ret = 0;
208			}
209			goto BAIL;
210		}
211	}
212
213
214#if defined(HAVE_STATVFS) && defined(HAVE_STRUCT_STATVFS_F_FSTYPENAME)
215	statvfs(file, &sfs);
216#else
217	statfs(file, &sfs);
218#endif
219
220	fsname = sfs.f_fstypename;
221
222	/* extattr_list_link() does not return the series of keys NUL
223	 * separated, as documented in the man page.  Instead, it
224	 * returns things DNS style, with a 1 byte length followed by
225	 * the key, repeated for as many keys as there are.
226	 */
227	for( i=0; i < ret; i++ ) {
228		char key[256];
229		char *ns;
230		char tempnam[1024];
231		xar_ea_t e;
232
233		memset(key, 0, sizeof(key));
234		memcpy(key, buf+i+1, buf[i]);
235		i += buf[i] ;
236
237		extattr_namespace_to_string(namespace, &ns);
238		memset(tempnam, 0, sizeof(tempnam));
239		snprintf(tempnam, sizeof(tempnam)-1, "%s.%s", ns, key);
240		FBSDATTR_CONTEXT(&context)->ns = namespace;
241		FBSDATTR_CONTEXT(&context)->file = file;
242		FBSDATTR_CONTEXT(&context)->attrname = key;
243
244		e = xar_ea_new(f, tempnam);
245		xar_ea_pset(f, e, "fstype", fsname);
246		XAR(x)->attrcopy_to_heap(x, f, xar_ea_root(e), xar_fbsdattr_read, &context);
247
248		free(FBSDATTR_CONTEXT(&context)->buf);
249		FBSDATTR_CONTEXT(&context)->buf = NULL;
250		FBSDATTR_CONTEXT(&context)->off = 0;
251	}
252
253	if( namespace == EXTATTR_NAMESPACE_USER ) {
254		namespace = EXTATTR_NAMESPACE_SYSTEM;
255		free(buf);
256		buf = NULL;
257		goto TRYAGAIN;
258	}
259
260BAIL:
261	free(buf);
262	return ret;
263#else
264	return 0;
265#endif
266}
267
268int32_t xar_fbsdattr_extract(xar_t x, xar_file_t f, const char* file, char *buffer, size_t len)
269{
270#ifdef HAVE_SYS_EXTATTR_H
271	char *fsname = "bogus";
272	xar_prop_t p;
273#if defined(HAVE_STATVFS) && defined(HAVE_STRUCT_STATVFS_F_FSTYPENAME)
274	struct statvfs sfs;
275#else
276	struct statfs sfs;
277#endif
278	int eaopt = 0;
279	struct _fbsdattr_context context;
280
281	memset(&context,0,sizeof(struct _fbsdattr_context));
282
283	/* no fbsdattr attributes for data to a buffer */
284	if(len){
285		return 0;
286	}
287
288#if defined(HAVE_STATVFS) && defined(HAVE_STRUCT_STATVFS_F_FSTYPENAME)
289	statvfs(file, &sfs);
290#else
291	statfs(file, &sfs);
292#endif
293	fsname = sfs.f_fstypename;
294
295	for(p = xar_prop_pfirst(f); p; p = xar_prop_pnext(p)) {
296		const char *fs = NULL;
297		const char *prop;
298		const char *eaname = NULL;
299		xar_prop_t tmpp;
300
301		prop = xar_prop_getkey(p);
302
303		if( strncmp(prop, XAR_EA_FORK, strlen(XAR_EA_FORK) != 0 ) )
304			continue;
305		if( strlen(prop) != strlen(XAR_EA_FORK) )
306			continue;
307
308		tmpp = xar_prop_pget(p, "fstype");
309		if( tmpp )
310			fs = xar_prop_getvalue(tmpp);
311
312		if( !eaopt && fs && strcmp(fs, fsname) != 0 ) {
313			continue;
314		}
315
316		tmpp = xar_prop_pget(p, "name");
317		if( tmpp )
318			eaname = xar_prop_getvalue(tmpp);
319
320		if( !eaname )
321			continue;
322
323		if( strncmp(eaname, "user.", 5) == 0 ) {
324			FBSDATTR_CONTEXT(&context)->ns = EXTATTR_NAMESPACE_USER;
325			FBSDATTR_CONTEXT(&context)->attrname = eaname + 5;
326		} else if( strncmp(eaname, "system.", 7) == 0 ) {
327			FBSDATTR_CONTEXT(&context)->ns = EXTATTR_NAMESPACE_SYSTEM;
328			FBSDATTR_CONTEXT(&context)->attrname = eaname + 7;
329		} else {
330			continue;
331		}
332
333		FBSDATTR_CONTEXT(&context)->file = file;
334		XAR(x)->attrcopy_from_heap(x, f, p, xar_fbsdattr_write, &context);
335	}
336
337
338#endif
339	return 0;
340}
341