#include "config.h"
#include <unistd.h>
#include "xar.h"
#include "arcmod.h"
#include "b64.h"
#include "io.h"
#include "archive.h"
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#ifdef HAVE_SYS_EXTATTR_H
#include <sys/extattr.h>
#endif
#ifdef HAVE_LIBUTIL_H
#include <libutil.h>
#endif
#ifdef HAVE_SYS_EXTATTR_H
#include <sys/param.h>
#include <sys/mount.h>
#include <sys/extattr.h>
#endif
#ifdef HAVE_SYS_EXTATTR_H
struct _fbsdattr_context{
const char *file;
const char *attrname;
void *buf;
int off;
int bufsz;
int ns;
};
#define FBSDATTR_CONTEXT(x) ((struct _fbsdattr_context *)(x))
int32_t xar_fbsdattr_read(xar_t x, xar_file_t f, void *buf, size_t len, void *context) {
if( !FBSDATTR_CONTEXT(context)->buf ) {
FBSDATTR_CONTEXT(context)->bufsz = extattr_get_link(FBSDATTR_CONTEXT(context)->file, FBSDATTR_CONTEXT(context)->ns, FBSDATTR_CONTEXT(context)->attrname, NULL, 0);
if( FBSDATTR_CONTEXT(context)->bufsz < 0 )
return -1;
FBSDATTR_CONTEXT(context)->buf = malloc(FBSDATTR_CONTEXT(context)->bufsz);
if( !FBSDATTR_CONTEXT(context)->buf )
return -1;
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);
}
if( (FBSDATTR_CONTEXT(context)->bufsz - FBSDATTR_CONTEXT(context)->off) <= len ) {
int32_t ret;
ret = FBSDATTR_CONTEXT(context)->bufsz - FBSDATTR_CONTEXT(context)->off;
memcpy(buf, FBSDATTR_CONTEXT(context)->buf+FBSDATTR_CONTEXT(context)->off, ret);
FBSDATTR_CONTEXT(context)->off += ret;
return(ret);
} else {
memcpy(buf, FBSDATTR_CONTEXT(context)->buf+FBSDATTR_CONTEXT(context)->off, len);
FBSDATTR_CONTEXT(context)->buf += len;
return(len);
}
}
int32_t xar_fbsdattr_write(xar_t x, xar_file_t f, void *buf, size_t len, void *context) {
return extattr_set_link(FBSDATTR_CONTEXT(context)->file, FBSDATTR_CONTEXT(context)->ns, FBSDATTR_CONTEXT(context)->attrname, buf, len);
}
#endif
int32_t xar_fbsdattr_archive(xar_t x, xar_file_t f, const char* file, const char *buffer, size_t len)
{
#ifdef HAVE_SYS_EXTATTR_H
char *buf = NULL;
int ret, retval=0, bufsz, i;
#if defined(HAVE_STATVFS) && defined(HAVE_STRUCT_STATVFS_F_FSTYPENAME)
struct statvfs sfs;
#else
struct statfs sfs;
#endif
char *fsname = NULL;
int namespace = EXTATTR_NAMESPACE_USER;
struct _fbsdattr_context context;
memset(&context,0,sizeof(struct _fbsdattr_context));
if(len)
return 0;
if(file == NULL)
return 0;
if( !xar_check_prop(x, "ea") )
return 0;
TRYAGAIN:
ret = extattr_list_link(file, namespace, NULL, 0);
if( ret < 0 ) {
if( namespace == EXTATTR_NAMESPACE_USER ) {
namespace = EXTATTR_NAMESPACE_SYSTEM;
goto TRYAGAIN;
} else {
if( errno == EPERM )
ret = 0;
else if( errno == EOPNOTSUPP )
ret = 0;
else {
xar_err_new(x);
xar_err_set_file(x, f);
xar_err_set_string(x, "Error archiving EA");
xar_err_callback(x, XAR_SEVERITY_NONFATAL, XAR_ERR_ARCHIVE_CREATION);
ret = 0;
}
goto BAIL;
}
}
bufsz = ret;
buf = malloc(bufsz);
if( !buf ) {
retval = -1;
goto BAIL;
}
memset(buf, 0, bufsz);
ret = extattr_list_link(file, namespace, buf, bufsz);
if( ret < 0 ) {
switch(errno) {
case ENOTSUP: retval=0; goto BAIL;
default: retval=-1; goto BAIL;
};
}
if( ret == 0 ) {
if( namespace == EXTATTR_NAMESPACE_USER ) {
namespace = EXTATTR_NAMESPACE_SYSTEM;
goto TRYAGAIN;
} else {
if( errno == EPERM )
ret = 0;
else if( errno == EOPNOTSUPP )
ret = 0;
else {
xar_err_new(x);
xar_err_set_file(x, f);
xar_err_set_string(x, "Error archiving EA");
xar_err_callback(x, XAR_SEVERITY_NONFATAL, XAR_ERR_ARCHIVE_CREATION);
ret = 0;
}
goto BAIL;
}
}
#if defined(HAVE_STATVFS) && defined(HAVE_STRUCT_STATVFS_F_FSTYPENAME)
statvfs(file, &sfs);
#else
statfs(file, &sfs);
#endif
fsname = sfs.f_fstypename;
for( i=0; i < ret; i++ ) {
char key[256];
char *ns;
char tempnam[1024];
xar_ea_t e;
memset(key, 0, sizeof(key));
memcpy(key, buf+i+1, buf[i]);
i += buf[i] ;
extattr_namespace_to_string(namespace, &ns);
memset(tempnam, 0, sizeof(tempnam));
snprintf(tempnam, sizeof(tempnam)-1, "%s.%s", ns, key);
FBSDATTR_CONTEXT(&context)->ns = namespace;
FBSDATTR_CONTEXT(&context)->file = file;
FBSDATTR_CONTEXT(&context)->attrname = key;
e = xar_ea_new(f, tempnam);
xar_ea_pset(f, e, "fstype", fsname);
xar_attrcopy_to_heap(x, f, xar_ea_root(e), xar_fbsdattr_read, &context);
free(FBSDATTR_CONTEXT(&context)->buf);
FBSDATTR_CONTEXT(&context)->buf = NULL;
FBSDATTR_CONTEXT(&context)->off = 0;
}
if( namespace == EXTATTR_NAMESPACE_USER ) {
namespace = EXTATTR_NAMESPACE_SYSTEM;
free(buf);
buf = NULL;
goto TRYAGAIN;
}
BAIL:
free(buf);
return ret;
#else
return 0;
#endif
}
int32_t xar_fbsdattr_extract(xar_t x, xar_file_t f, const char* file, char *buffer, size_t len)
{
#ifdef HAVE_SYS_EXTATTR_H
char *fsname = "bogus";
xar_prop_t p;
#if defined(HAVE_STATVFS) && defined(HAVE_STRUCT_STATVFS_F_FSTYPENAME)
struct statvfs sfs;
#else
struct statfs sfs;
#endif
int eaopt = 0;
struct _fbsdattr_context context;
memset(&context,0,sizeof(struct _fbsdattr_context));
if(len){
return 0;
}
#if defined(HAVE_STATVFS) && defined(HAVE_STRUCT_STATVFS_F_FSTYPENAME)
statvfs(file, &sfs);
#else
statfs(file, &sfs);
#endif
fsname = sfs.f_fstypename;
for(p = xar_prop_pfirst(f); p; p = xar_prop_pnext(p)) {
const char *fs = NULL;
const char *prop;
const char *eaname = NULL;
xar_prop_t tmpp;
prop = xar_prop_getkey(p);
if( strncmp(prop, XAR_EA_FORK, strlen(XAR_EA_FORK) != 0 ) )
continue;
if( strlen(prop) != strlen(XAR_EA_FORK) )
continue;
tmpp = xar_prop_pget(p, "fstype");
if( tmpp )
fs = xar_prop_getvalue(tmpp);
if( !eaopt && fs && strcmp(fs, fsname) != 0 ) {
continue;
}
tmpp = xar_prop_pget(p, "name");
if( tmpp )
eaname = xar_prop_getvalue(tmpp);
if( !eaname )
continue;
if( strncmp(eaname, "user.", 5) == 0 ) {
FBSDATTR_CONTEXT(&context)->ns = EXTATTR_NAMESPACE_USER;
FBSDATTR_CONTEXT(&context)->attrname = eaname + 5;
} else if( strncmp(eaname, "system.", 7) == 0 ) {
FBSDATTR_CONTEXT(&context)->ns = EXTATTR_NAMESPACE_SYSTEM;
FBSDATTR_CONTEXT(&context)->attrname = eaname + 7;
} else {
continue;
}
FBSDATTR_CONTEXT(&context)->file = file;
xar_attrcopy_from_heap(x, f, p, xar_fbsdattr_write, &context);
}
#endif
return 0;
}