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_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_attrcopy_from_heap(x, f, p, xar_fbsdattr_write, &context); 335 } 336 337 338#endif 339 return 0; 340} 341