1/*
2 * Copyright (c) 2004 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 * 26-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 <stdio.h>
40#include <unistd.h>
41#include <libgen.h>
42#include "xar.h"
43#include "arcmod.h"
44#include "b64.h"
45#include <errno.h>
46#include <string.h>
47#include "util.h"
48#include "linuxattr.h"
49#include "io.h"
50
51#ifdef HAVE_SYS_PARAM_H
52#include <sys/param.h>
53#endif
54
55#ifdef HAVE_SYS_STATFS_H  /* Nonexistant future OS needs this */
56#include <sys/statfs.h>
57#endif
58
59#ifdef HAVE_SYS_MOUNT_H
60#include <sys/mount.h>
61#endif
62
63#ifdef HAVE_SYS_XATTR_H
64#include <sys/xattr.h>
65#endif
66
67#ifndef EXT3_SUPER_MAGIC
68#define EXT3_SUPER_MAGIC 0xEF53
69#endif
70
71#ifndef JFS_SUPER_MAGIC
72#define JFS_SUPER_MAGIC 0x3153464a
73#endif
74
75#ifndef REISERFS_SUPER_MAGIC
76#define REISERFS_SUPER_MAGIC 0x52654973
77#endif
78
79#ifndef XFS_SUPER_MAGIC
80#define XFS_SUPER_MAGIC 0x58465342
81#endif
82
83#if defined(HAVE_SYS_XATTR_H) && defined(HAVE_LGETXATTR) && !defined(__APPLE__)
84
85struct _linuxattr_context{
86	const char *file;
87	const char *attrname;
88	xar_ea_t ea;
89	void *buf;
90	int off;
91	int bufsz;
92};
93
94#define LINUXATTR_CONTEXT(x) ((struct _linuxattr_context *)(x))
95
96int32_t xar_linuxattr_read(xar_t x, xar_file_t f, void * buf, size_t len, void *context) {
97
98	if( !LINUXATTR_CONTEXT(context)->buf ) {
99		int r;
100		LINUXATTR_CONTEXT(context)->bufsz = 1024;
101AGAIN2:
102		LINUXATTR_CONTEXT(context)->buf = malloc(LINUXATTR_CONTEXT(context)->bufsz);
103		if(!LINUXATTR_CONTEXT(context)->buf)
104			goto AGAIN2;
105		memset(LINUXATTR_CONTEXT(context)->buf, 0, LINUXATTR_CONTEXT(context)->bufsz);
106		r = lgetxattr(LINUXATTR_CONTEXT(context)->file, LINUXATTR_CONTEXT(context)->attrname, LINUXATTR_CONTEXT(context)->buf, LINUXATTR_CONTEXT(context)->bufsz);
107		if( r < 0 ) {
108			switch(errno) {
109			case ERANGE: LINUXATTR_CONTEXT(context)->bufsz *= 2; free(LINUXATTR_CONTEXT(context)->buf); goto AGAIN2;
110			case ENOTSUP: free(LINUXATTR_CONTEXT(context)->buf); return 0;
111			default: break;
112			};
113			return -1;
114		}
115		LINUXATTR_CONTEXT(context)->bufsz = r;
116	}
117
118	if( (LINUXATTR_CONTEXT(context)->bufsz-LINUXATTR_CONTEXT(context)->off) <= len ) {
119		int32_t ret;
120		ret = LINUXATTR_CONTEXT(context)->bufsz - LINUXATTR_CONTEXT(context)->off;
121		memcpy(buf, ((char *)LINUXATTR_CONTEXT(context)->buf)+LINUXATTR_CONTEXT(context)->off, ret);
122		LINUXATTR_CONTEXT(context)->off += ret;
123		return(ret);
124	} else {
125		memcpy(buf, ((char *)LINUXATTR_CONTEXT(context)->buf)+LINUXATTR_CONTEXT(context)->off, len);
126		LINUXATTR_CONTEXT(context)->buf = ((char *)LINUXATTR_CONTEXT(context)->buf) + len;
127		return len;
128	}
129}
130
131int32_t xar_linuxattr_write(xar_t x, xar_file_t f, void *buf, size_t len, void *context) {
132	return lsetxattr(LINUXATTR_CONTEXT(context)->file, LINUXATTR_CONTEXT(context)->attrname, buf, len, 0);
133}
134#endif
135
136int32_t xar_linuxattr_archive(xar_t x, xar_file_t f, const char* file, const char *buffer, size_t len)
137{
138#if defined(HAVE_SYS_XATTR_H) && defined(HAVE_LGETXATTR) && !defined(__APPLE__)
139	char *i, *buf = NULL;
140	int ret, retval=0, bufsz = 1024;
141	struct statfs sfs;
142	char *fsname = NULL;
143	struct _linuxattr_context context;
144
145	memset(&context,0,sizeof(struct _linuxattr_context));
146
147	/* data from buffers don't have linuxattr */
148	if(len)
149		return 0;
150	if( file == NULL )
151		return 0;
152
153	if( !xar_check_prop(x, "ea") )
154		return 0;
155
156TRYAGAIN:
157	buf = malloc(bufsz);
158	if(!buf)
159		goto TRYAGAIN;
160	ret = llistxattr(file, buf, bufsz);
161	if( ret < 0 ) {
162		switch(errno) {
163		case ERANGE: bufsz = bufsz*2; free(buf); goto TRYAGAIN;
164		case ENOTSUP: retval = 0; goto BAIL;
165		default: retval = -1; goto BAIL;
166		};
167	}
168	if( ret == 0 ) goto BAIL;
169
170	memset(&sfs, 0, sizeof(sfs));
171	statfs(file, &sfs);
172
173	switch(sfs.f_type) {
174	case EXT3_SUPER_MAGIC: fsname = "ext3"; break; /* assume ext3 */
175	case JFS_SUPER_MAGIC:  fsname = "jfs" ; break;
176	case REISERFS_SUPER_MAGIC:fsname = "reiser" ; break;
177	case XFS_SUPER_MAGIC:  fsname = "xfs" ; break;
178	default: retval=0; goto BAIL;
179	};
180
181	for( i=buf; (i-buf) < ret; i += strlen(i)+1 ) {
182		xar_ea_t e;
183
184		context.bufsz = 0;
185		context.off = 0;
186		context.buf = NULL;
187		context.file = file;
188		e = xar_ea_new(f, i);
189		xar_ea_pset(f, e, "fstype", fsname);
190		context.attrname = i;
191		context.ea = e;
192		XAR(x)->attrcopy_to_heap(x, f, xar_ea_root(e), xar_linuxattr_read,&context);
193		free(context.buf);
194		context.attrname = NULL;
195	}
196
197BAIL:
198	free(buf);
199	return retval;
200#endif
201	return 0;
202}
203
204int32_t xar_linuxattr_extract(xar_t x, xar_file_t f, const char* file, char *buffer, size_t len)
205{
206#if defined HAVE_SYS_XATTR_H && defined(HAVE_LSETXATTR) && !defined(__APPLE__)
207	const char *fsname = "bogus";
208	struct statfs sfs;
209	int eaopt = 0;
210	struct _linuxattr_context context;
211	xar_prop_t p;
212
213	memset(&context,0,sizeof(struct _linuxattr_context));
214
215	/* data buffers, can't store linux attrs */
216	if(len){
217		return 0;
218	}
219
220	/* Check for EA extraction behavior */
221
222	memset(&sfs, 0, sizeof(sfs));
223	if( statfs(file, &sfs) != 0 ) {
224		char *tmp, *bname;
225		tmp = strdup(file);
226		bname = dirname(tmp);
227		statfs(bname, &sfs);
228		free(tmp);
229	}
230	switch(sfs.f_type) {
231	case EXT3_SUPER_MAGIC: fsname = "ext3"; break; /* assume ext3 */
232	case JFS_SUPER_MAGIC:  fsname = "jfs" ; break;
233	case REISERFS_SUPER_MAGIC:fsname = "reiser" ; break;
234	case XFS_SUPER_MAGIC:  fsname = "xfs" ; break;
235	};
236
237	for(p = xar_prop_pfirst(f); p; p = xar_prop_pnext(p)) {
238		const char *fs = NULL;
239		const char *prop;
240		const char *eaname = NULL;
241		xar_prop_t tmpp;
242		prop = xar_prop_getkey(p);
243
244		if( strncmp(prop, XAR_EA_FORK, strlen(XAR_EA_FORK)) != 0 )
245			continue;
246		if( strlen(prop) != strlen(XAR_EA_FORK) )
247			continue;
248
249		tmpp = xar_prop_pget(p, "fstype");
250		if( tmpp )
251			fs = xar_prop_getvalue(tmpp);
252		if( !eaopt && fs && strcmp(fs, fsname) != 0 ) {
253			continue;
254		}
255		if( !fs )
256			continue;
257
258		tmpp = xar_prop_pget(p, "name");
259		if( tmpp )
260			eaname = xar_prop_getvalue(tmpp);
261
262		context.file = file;
263		context.attrname = eaname;
264		XAR(x)->attrcopy_from_heap(x, f, p, xar_linuxattr_write, &context);
265
266	}
267
268#endif
269	return 0;
270}
271