#include "config.h"
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <libgen.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <arpa/inet.h>
#include "xar.h"
#include "arcmod.h"
#include "b64.h"
#include <errno.h>
#include <string.h>
#include "util.h"
#include "linuxattr.h"
#include "io.h"
#include "appledouble.h"
#include "stat.h"
#include "archive.h"
#if defined(HAVE_SYS_XATTR_H)
#include <sys/xattr.h>
#endif
struct _darwinattr_context{
int fd;
char *finfo;
char *buf;
int len;
int off;
};
#define DARWINATTR_CONTEXT(x) ((struct _darwinattr_context *)(x))
#if defined(__APPLE__)
#ifdef HAVE_GETATTRLIST
#include <sys/attr.h>
#include <sys/vnode.h>
struct fi {
uint32_t length;
fsobj_type_t objtype;
char finderinfo[32];
};
static int32_t finfo_read(xar_t x, xar_file_t f, void *buf, size_t len, void *context) {
if( len < 32 )
return -1;
if( DARWINATTR_CONTEXT(context)->finfo == NULL )
return 0;
memcpy(buf, DARWINATTR_CONTEXT(context)->finfo, 32);
DARWINATTR_CONTEXT(context)->finfo = NULL;
return 32;
}
static int32_t finfo_write(xar_t x, xar_file_t f, void *buf, size_t len, void *context) {
struct attrlist attrs;
struct fi finfo;
if( len < 32 )
return -1;
if( DARWINATTR_CONTEXT(context)->finfo == NULL )
return 0;
memset(&attrs, 0, sizeof(attrs));
attrs.bitmapcount = ATTR_BIT_MAP_COUNT;
attrs.commonattr = ATTR_CMN_OBJTYPE | ATTR_CMN_FNDRINFO;
getattrlist(DARWINATTR_CONTEXT(context)->finfo, &attrs, &finfo, sizeof(finfo), 0);
attrs.commonattr = ATTR_CMN_FNDRINFO;
if( setattrlist(DARWINATTR_CONTEXT(context)->finfo, &attrs, buf, 32, 0) != 0 )
return -1;
DARWINATTR_CONTEXT(context)->finfo = NULL;
return 32;
}
#endif
static int32_t xar_rsrc_read(xar_t x, xar_file_t f, void *inbuf, size_t bsize, void *context) {
int32_t r;
while(1) {
r = read(DARWINATTR_CONTEXT(context)->fd, inbuf, bsize);
if( (r < 0) && (errno == EINTR) )
continue;
return r;
}
}
#endif
static int32_t xar_rsrc_write(xar_t x, xar_file_t f, void *buf, size_t len, void *context) {
int32_t r;
size_t off = 0;
do {
r = write(DARWINATTR_CONTEXT(context)->fd, ((char *)buf)+off, len-off);
if( (r < 0) && (errno != EINTR) )
return r;
off += r;
} while( off < len );
return off;
}
#ifdef __APPLE__
#if defined(HAVE_GETXATTR)
static int32_t xar_ea_read(xar_t x, xar_file_t f, void *buf, size_t len, void *context) {
if( DARWINATTR_CONTEXT(context)->buf == NULL )
return 0;
if( ((DARWINATTR_CONTEXT(context)->len)-(DARWINATTR_CONTEXT(context)->off)) <= len ) {
int siz = (DARWINATTR_CONTEXT(context)->len)-(DARWINATTR_CONTEXT(context)->off);
memcpy(buf, DARWINATTR_CONTEXT(context)->buf+DARWINATTR_CONTEXT(context)->off, siz);
free(DARWINATTR_CONTEXT(context)->buf);
DARWINATTR_CONTEXT(context)->buf = NULL;
DARWINATTR_CONTEXT(context)->off = 0;
DARWINATTR_CONTEXT(context)->len = 0;
return siz;
}
memcpy(buf, DARWINATTR_CONTEXT(context)->buf+DARWINATTR_CONTEXT(context)->off, len);
DARWINATTR_CONTEXT(context)->off += len;
if( DARWINATTR_CONTEXT(context)->off == DARWINATTR_CONTEXT(context)->len ) {
free(DARWINATTR_CONTEXT(context)->buf);
DARWINATTR_CONTEXT(context)->buf = NULL;
DARWINATTR_CONTEXT(context)->off = 0;
DARWINATTR_CONTEXT(context)->len = 0;
}
return len;
}
static int32_t xar_ea_write(xar_t x, xar_file_t f, void *buf, size_t len, void *context) {
if( DARWINATTR_CONTEXT(context)->buf == NULL )
return 0;
if( DARWINATTR_CONTEXT(context)->off == DARWINATTR_CONTEXT(context)->len )
return 0;
if( ((DARWINATTR_CONTEXT(context)->len)-(DARWINATTR_CONTEXT(context)->off)) <= len ) {
int siz = (DARWINATTR_CONTEXT(context)->len)-(DARWINATTR_CONTEXT(context)->off);
memcpy((DARWINATTR_CONTEXT(context)->buf)+(DARWINATTR_CONTEXT(context)->off), buf, siz);
return siz;
}
memcpy((DARWINATTR_CONTEXT(context)->buf)+(DARWINATTR_CONTEXT(context)->off), buf, len);
DARWINATTR_CONTEXT(context)->off += len;
return len;
}
static int32_t ea_archive(xar_t x, xar_file_t f, const char* file, void *context) {
char *buf, *i;
int ret, bufsz, attrsz;
int32_t retval = 0;
if( file == NULL )
return 0;
ret = listxattr(file, NULL, 0, XATTR_NOFOLLOW);
if( ret < 0 )
return -1;
if( ret == 0 )
return 0;
bufsz = ret;
TRYAGAIN:
buf = malloc(bufsz);
if( !buf )
goto TRYAGAIN;
ret = listxattr(file, buf, bufsz, XATTR_NOFOLLOW);
if( ret < 0 ) {
switch(errno) {
case ERANGE: bufsz = bufsz*2; free(buf); goto TRYAGAIN;
case ENOTSUP: retval = 0; goto BAIL;
default: retval = -1; goto BAIL;
};
}
if( ret == 0 ) {
retval = 0;
goto BAIL;
}
attrsz = ret;
for( i = buf; (i-buf) < attrsz; i += strlen(i)+1 ) {
xar_ea_t e;
ret = getxattr(file, i, NULL, 0, 0, XATTR_NOFOLLOW);
if( ret < 0 )
continue;
DARWINATTR_CONTEXT(context)->len = ret;
DARWINATTR_CONTEXT(context)->buf = malloc(DARWINATTR_CONTEXT(context)->len);
if( !DARWINATTR_CONTEXT(context)->buf )
goto BAIL;
ret = getxattr(file, i, DARWINATTR_CONTEXT(context)->buf, DARWINATTR_CONTEXT(context)->len, 0, XATTR_NOFOLLOW);
if( ret < 0 ) {
free(DARWINATTR_CONTEXT(context)->buf);
DARWINATTR_CONTEXT(context)->buf = NULL;
DARWINATTR_CONTEXT(context)->len = 0;
continue;
}
e = xar_ea_new(f, i);
xar_attrcopy_to_heap(x, f, xar_ea_root(e), xar_ea_read, context);
}
BAIL:
free(buf);
return retval;
}
static int32_t ea_extract(xar_t x, xar_file_t f, const char* file, void *context) {
xar_prop_t p;
for(p = xar_prop_pfirst(f); p; p = xar_prop_pnext(p)) {
const char *opt;
const char *name = NULL;
int len;
xar_prop_t tmpp;
name = xar_prop_getkey(p);
if( strncmp(name, XAR_EA_FORK, strlen(XAR_EA_FORK)) )
continue;
if( strlen(name) != strlen(XAR_EA_FORK) )
continue;
opt = NULL;
tmpp = xar_prop_pget(p, "size");
if( tmpp )
opt = xar_prop_getvalue(tmpp);
if( !opt )
continue;
len = strtol(opt, NULL, 10);
DARWINATTR_CONTEXT(context)->buf = malloc(len);
if( !DARWINATTR_CONTEXT(context)->buf )
return -1;
DARWINATTR_CONTEXT(context)->len = len;
xar_attrcopy_from_heap(x, f, p, xar_ea_write, context);
name = NULL;
tmpp = xar_prop_pget(p, "name");
if( tmpp )
name = xar_prop_getvalue(tmpp);
if( !name )
continue;
setxattr(file, name, DARWINATTR_CONTEXT(context)->buf, DARWINATTR_CONTEXT(context)->len, 0, XATTR_NOFOLLOW);
free(DARWINATTR_CONTEXT(context)->buf);
DARWINATTR_CONTEXT(context)->buf = NULL;
DARWINATTR_CONTEXT(context)->len = 0;
DARWINATTR_CONTEXT(context)->off = 0;
}
return 0;
}
#endif
static int32_t nonea_archive(xar_t x, xar_file_t f, const char* file, void *context) {
char rsrcname[4096];
struct stat sb;
xar_ea_t e;
#ifdef HAVE_GETATTRLIST
struct attrlist attrs;
struct fi finfo;
int ret;
char z[32];
memset(&attrs, 0, sizeof(attrs));
attrs.bitmapcount = ATTR_BIT_MAP_COUNT;
attrs.commonattr = ATTR_CMN_OBJTYPE | ATTR_CMN_FNDRINFO;
ret = getattrlist(file, &attrs, &finfo, sizeof(finfo), 0);
if( ret != 0 )
return -1;
memset(z, 0, sizeof(z));
if( memcmp(finfo.finderinfo, z, sizeof(finfo.finderinfo)) != 0 ) {
e = xar_ea_new(f, "com.apple.FinderInfo");
DARWINATTR_CONTEXT(context)->finfo = finfo.finderinfo;
xar_attrcopy_to_heap(x, f, xar_ea_root(e), finfo_read, context);
}
#endif
memset(rsrcname, 0, sizeof(rsrcname));
snprintf(rsrcname, sizeof(rsrcname)-1, "%s/..namedfork/rsrc", file);
if( lstat(rsrcname, &sb) != 0 )
return 0;
if( sb.st_size == 0 )
return 0;
DARWINATTR_CONTEXT(context)->fd = open(rsrcname, O_RDONLY, 0);
if( DARWINATTR_CONTEXT(context)->fd < 0 )
return -1;
e = xar_ea_new(f, "com.apple.ResourceFork");
xar_attrcopy_to_heap(x, f, xar_ea_root(e), xar_rsrc_read, context);
close(DARWINATTR_CONTEXT(context)->fd);
return 0;
}
static int32_t nonea_extract(xar_t x, xar_file_t f, const char* file, void *context) {
char rsrcname[4096];
xar_prop_t p;
#ifdef HAVE_SETATTRLIST
struct attrlist attrs;
struct fi finfo;
int ret;
memset(&attrs, 0, sizeof(attrs));
attrs.bitmapcount = ATTR_BIT_MAP_COUNT;
attrs.commonattr = ATTR_CMN_OBJTYPE | ATTR_CMN_FNDRINFO;
ret = getattrlist(file, &attrs, &finfo, sizeof(finfo), 0);
if( ret != 0 )
return -1;
DARWINATTR_CONTEXT(context)->finfo = (char *)file;
p = xar_ea_find(f, "com.apple.ResourceFork");
if( p )
xar_attrcopy_from_heap(x, f, p, finfo_write, context);
#endif
memset(rsrcname, 0, sizeof(rsrcname));
snprintf(rsrcname, sizeof(rsrcname)-1, "%s/..namedfork/rsrc", file);
DARWINATTR_CONTEXT(context)->fd = open(rsrcname, O_RDWR|O_TRUNC);
if( DARWINATTR_CONTEXT(context)->fd < 0 )
return 0;
p = xar_ea_find(f, "com.apple.ResourceFork");
if( p )
xar_attrcopy_from_heap(x, f, p, xar_rsrc_write, context);
close(DARWINATTR_CONTEXT(context)->fd);
return 0;
}
#endif
xar_file_t xar_underbar_check(xar_t x, xar_file_t f, const char* file, void *context) {
char *bname, *tmp;
tmp = strdup(file);
bname = basename(tmp);
if(bname && (bname[0] == '.') && (bname[1] == '_')) {
char *nonunderbar, *nupath, *tmp2, *dname;
struct stat sb;
nonunderbar = bname+2;
tmp2 = strdup(file);
dname = dirname(tmp2);
asprintf(&nupath, "%s/%s", dname, nonunderbar);
free(tmp2);
if( stat(nupath, &sb) ) {
free(tmp);
free(nupath);
return NULL;
}
asprintf(&tmp2, "%s/..namedfork/rsrc", nupath);
if( stat(tmp2, &sb) ) {
xar_file_t tmpf;
tmpf = xar_file_find(XAR(x)->files, nupath);
if( !tmpf ) {
tmpf = xar_add(x, nupath);
}
free(nupath);
free(tmp2);
free(tmp);
return tmpf;
}
free(nupath);
free(tmp2);
free(tmp);
return NULL;
}
free(tmp);
return NULL;
}
#ifdef __APPLE__
static int32_t underbar_archive(xar_t x, xar_file_t f, const char* file, void *context) {
struct stat sb;
char underbarname[4096], z[32];
char *dname, *bname, *tmp, *tmp2;
struct AppleSingleHeader ash;
struct AppleSingleEntry ase;
int num_entries = 0, i, r;
off_t off;
if( !file )
return 0;
tmp = strdup(file);
tmp2 = strdup(file);
dname = dirname(tmp2);
bname = basename(tmp);
memset(underbarname, 0, sizeof(underbarname));
snprintf(underbarname, sizeof(underbarname)-1, "%s/._%s", dname, bname);
free(tmp);
free(tmp2);
if( stat(underbarname, &sb) != 0 )
return 0;
DARWINATTR_CONTEXT(context)->fd = open(underbarname, O_RDONLY);
if( DARWINATTR_CONTEXT(context)->fd < 0 )
return -1;
memset(&ash, 0, sizeof(ash));
memset(&ase, 0, sizeof(ase));
r = read(DARWINATTR_CONTEXT(context)->fd, &ash, XAR_ASH_SIZE);
if( r < XAR_ASH_SIZE ) {
close(DARWINATTR_CONTEXT(context)->fd);
return -1;
}
if( ntohl(ash.magic) != APPLEDOUBLE_MAGIC ) {
close(DARWINATTR_CONTEXT(context)->fd);
return -1;
}
if( ntohl(ash.version) != APPLEDOUBLE_VERSION ) {
close(DARWINATTR_CONTEXT(context)->fd);
return -1;
}
off = XAR_ASH_SIZE;
num_entries = ntohs(ash.entries);
for(i = 0; i < num_entries; i++) {
off_t entoff;
r = read(DARWINATTR_CONTEXT(context)->fd, &ase, sizeof(ase));
if( r < sizeof(ase) ) {
close(DARWINATTR_CONTEXT(context)->fd);
return -1;
}
off+=r;
if( ntohl(ase.entry_id) == AS_ID_FINDER ) {
xar_ea_t e;
entoff = (off_t)ntohl(ase.offset);
if( lseek(DARWINATTR_CONTEXT(context)->fd, entoff, SEEK_SET) == -1 ) {
close(DARWINATTR_CONTEXT(context)->fd);
return -1;
}
r = read(DARWINATTR_CONTEXT(context)->fd, z, sizeof(z));
if( r < sizeof(z) ) {
close(DARWINATTR_CONTEXT(context)->fd);
return -1;
}
DARWINATTR_CONTEXT(context)->finfo = z;
e = xar_ea_new(f, "com.apple.FinderInfo");
xar_attrcopy_to_heap(x, f, xar_ea_root(e), finfo_read, context);
if( lseek(DARWINATTR_CONTEXT(context)->fd, (off_t)off, SEEK_SET) == -1 ) {
close(DARWINATTR_CONTEXT(context)->fd);
return -1;
}
}
if( ntohl(ase.entry_id) == AS_ID_RESOURCE ) {
xar_ea_t e;
entoff = (off_t)ntohl(ase.offset);
if( lseek(DARWINATTR_CONTEXT(context)->fd, entoff, SEEK_SET) == -1 ) {
close(DARWINATTR_CONTEXT(context)->fd);
return -1;
}
e = xar_ea_new(f, "com.apple.ResourceFork");
xar_attrcopy_to_heap(x, f, xar_ea_root(e), xar_rsrc_read, context);
if( lseek(DARWINATTR_CONTEXT(context)->fd, (off_t)off, SEEK_SET) == -1 ) {
close(DARWINATTR_CONTEXT(context)->fd);
return -1;
}
}
}
close(DARWINATTR_CONTEXT(context)->fd);
DARWINATTR_CONTEXT(context)->fd = 0;
return 0;
}
#endif
static int32_t underbar_extract(xar_t x, xar_file_t f, const char* file, void *context) {
char underbarname[4096];
char *dname, *bname, *tmp, *tmp2;
const char *rsrclenstr;
struct AppleSingleHeader ash;
struct AppleSingleEntry ase;
int num_entries = 0, rsrclen = 0, have_rsrc = 0, have_fi = 0;
xar_prop_t p;
xar_prop_t rfprop = NULL, fiprop = NULL;
fiprop = xar_ea_find(f, "com.apple.FinderInfo");
if( fiprop ) {
have_fi = 1;
num_entries++;
}
rfprop = xar_ea_find(f, "com.apple.ResourceFork");
if( rfprop ) {
have_rsrc = 1;
num_entries++;
}
if( num_entries == 0 )
return 0;
tmp = strdup(file);
tmp2 = strdup(file);
dname = dirname(tmp2);
bname = basename(tmp);
memset(underbarname, 0, sizeof(underbarname));
snprintf(underbarname, sizeof(underbarname)-1, "%s/._%s", dname, bname);
free(tmp);
free(tmp2);
DARWINATTR_CONTEXT(context)->fd = open(underbarname, O_RDWR | O_CREAT | O_TRUNC, 0);
if( DARWINATTR_CONTEXT(context)->fd < 0 )
return -1;
rsrclenstr = NULL;
if( rfprop ) {
p = xar_prop_pget(rfprop, "size");
if( p )
rsrclenstr = xar_prop_getvalue(p);
if( rsrclenstr )
rsrclen = strtol(rsrclenstr, NULL, 10);
}
memset(&ash, 0, sizeof(ash));
memset(&ase, 0, sizeof(ase));
ash.magic = htonl(APPLEDOUBLE_MAGIC);
ash.version = htonl(APPLEDOUBLE_VERSION);
ash.entries = htons(num_entries);
write(DARWINATTR_CONTEXT(context)->fd, &ash, XAR_ASH_SIZE);
ase.offset = htonl(XAR_ASH_SIZE + ntohs(ash.entries)*12);
if( have_fi ) {
ase.entry_id = htonl(AS_ID_FINDER);
ase.length = htonl(32);
write(DARWINATTR_CONTEXT(context)->fd, &ase, 12);
}
if( have_rsrc ) {
ase.entry_id = htonl(AS_ID_RESOURCE);
ase.offset = htonl(ntohl(ase.offset) + ntohl(ase.length));
ase.length = htonl(rsrclen);
write(DARWINATTR_CONTEXT(context)->fd, &ase, 12);
}
if( have_fi )
xar_attrcopy_from_heap(x, f, fiprop, xar_rsrc_write, context);
if( have_rsrc )
xar_attrcopy_from_heap(x, f, rfprop, xar_rsrc_write, context);
close(DARWINATTR_CONTEXT(context)->fd);
DARWINATTR_CONTEXT(context)->fd = 0;
xar_set_perm(x, f, underbarname, NULL, 0 );
return 0;
}
#if defined(__APPLE__)
static int32_t stragglers_archive(xar_t x, xar_file_t f, const char* file, void *context) {
#ifdef HAVE_GETATTRLIST
struct fits {
uint32_t length;
struct timespec ts;
};
struct fits fts;
struct attrlist attrs;
int ret;
if( !xar_check_prop(x, "FinderCreateTime") )
return 0;
memset(&attrs, 0, sizeof(attrs));
attrs.bitmapcount = ATTR_BIT_MAP_COUNT;
attrs.commonattr = ATTR_CMN_CRTIME;
ret = getattrlist(file, &attrs, &fts, sizeof(fts), 0);
if( ret == 0 ) {
xar_prop_t tmpp;
tmpp = xar_prop_new(f, NULL);
if( tmpp ) {
char tmpc[128];
struct tm tm;
xar_prop_setkey(tmpp, "FinderCreateTime");
xar_prop_setvalue(tmpp, NULL);
memset(tmpc, 0, sizeof(tmpc));
gmtime_r(&fts.ts.tv_sec, &tm);
strftime(tmpc, sizeof(tmpc), "%FT%T", &tm);
xar_prop_pset(f, tmpp, "time", tmpc);
memset(tmpc, 0, sizeof(tmpc));
sprintf(tmpc, "%ld", fts.ts.tv_nsec);
xar_prop_pset(f, tmpp, "nanoseconds", tmpc);
}
}
#endif
return 0;
}
static int32_t stragglers_extract(xar_t x, xar_file_t f, const char* file, void *context) {
#ifdef HAVE_GETATTRLIST
const char *tmpc = NULL;
struct tm tm;
struct timespec ts;
struct attrlist attrs;
xar_prop_get(f, "FinderCreateTime/time", &tmpc);
if( tmpc ) {
strptime(tmpc, "%FT%T", &tm);
ts.tv_sec = timegm(&tm);
xar_prop_get(f, "FinderCreateTime/nanoseconds", &tmpc);
ts.tv_nsec = strtol(tmpc, NULL, 10);
memset(&attrs, 0, sizeof(attrs));
attrs.bitmapcount = ATTR_BIT_MAP_COUNT;
attrs.commonattr = ATTR_CMN_CRTIME;
setattrlist(file, &attrs, &ts, sizeof(ts), 0);
}
#endif
return 0;
}
#endif
int32_t xar_darwinattr_archive(xar_t x, xar_file_t f, const char* file, const char *buffer, size_t len)
{
struct _darwinattr_context context;
memset(&context,0,sizeof(struct _darwinattr_context));
#if defined(__APPLE__)
if( len )
return 0;
stragglers_archive(x, f, file, (void *)&context);
if( !xar_check_prop(x, "ea") )
return 0;
#if defined(HAVE_GETXATTR)
if( ea_archive(x, f, file, (void *)&context) == 0 )
return 0;
#endif
if( nonea_archive(x, f, file, (void *)&context) == 0 )
return 0;
return underbar_archive(x, f, file, (void *)&context);
#endif
return 0;
}
int32_t xar_darwinattr_extract(xar_t x, xar_file_t f, const char* file, char *buffer, size_t len)
{
struct _darwinattr_context context;
memset(&context,0,sizeof(struct _darwinattr_context));
#if defined(__APPLE__)
if( len )
return 0;
stragglers_extract(x, f, file, (void *)&context);
#if defined(HAVE_GETXATTR)
if( ea_extract(x, f, file, (void *)&context) == 0 )
return 0;
#endif
if( nonea_extract(x, f, file, (void *)&context) == 0 )
return 0;
#endif
return underbar_extract(x, f, file, (void *)&context);
}