#include <fcntl.h>
#include <sys/errno.h>
#include <sys/stat.h>
+#include <sys/time.h>
#include <sys/xattr.h>
#include <sys/syscall.h>
#include <sys/param.h>
}
copyfile_debug(1, "debug value set to: %d\n", s->debug);
}
-
if (COPYFILE_CHECK & flags)
return copyfile_check(s);
}
} else if (COPYFILE_UNPACK & flags)
{
+ if (!(COPYFILE_STAT & flags || COPYFILE_ACL & flags))
+ fix_perms = !copyfile_fix_perms(s, &original_fsec, 1);
+
if (copyfile_unpack(s) < 0)
ret = -1;
} else
filesec_free(original_fsec);
- if (state == NULL)
- ret -= copyfile_free(s);
+ if (state == NULL) {
+ if (copyfile_free(s) < 0) {
+ ret = -1;
+ }
+ }
+
return ret;
}
static int copyfile_close(copyfile_state_t s)
{
- if (s->src_fd != -2)
+ if (s->src_fd >= 0)
close(s->src_fd);
- if (s->dst_fd != -2 && close(s->dst_fd))
+ if (s->dst_fd >= 0 && close(s->dst_fd))
{
copyfile_warn("close on %s", s->dst);
return -1;
if (on)
{
- if(statx_np(s->dst, &sb, *fsec))
+ if(fstatx_np(s->dst_fd, &sb, *fsec))
goto error;
tmp_fsec = filesec_dup(*fsec);
return -1;
}
- while((s->dst_fd = openx_np(s->dst, oflags, s->fsec)) < 0)
+ while((s->dst_fd = open(s->dst, oflags, s->sb.st_mode | S_IWUSR)) < 0)
{
- if (EEXIST == errno)
+ /*
+ * We set S_IWUSR because fsetxattr does not -- at the time this comment
+ * was written -- allow one to set an extended attribute on a file descriptor
+ * for a read-only file, even if the file descriptor is opened for writing.
+ * This will only matter if the file does not already exist.
+ */
+ switch(errno)
{
- oflags = oflags & ~O_CREAT;
- continue;
+ case EEXIST:
+ copyfile_debug(3, "open failed, retrying (%s)", s->dst);
+ if (s->flags & COPYFILE_EXCL)
+ break;
+ oflags = oflags & ~O_CREAT;
+ if (s->flags & (COPYFILE_PACK | COPYFILE_DATA))
+ {
+ copyfile_debug(4, "truncating existing file (%s)", s->dst);
+ oflags |= O_TRUNC;
+ }
+ continue;
+ case EACCES:
+ if(chmod(s->dst, (s->sb.st_mode | S_IWUSR) & ~S_IFMT) == 0)
+ continue;
+ else {
+ break;
+ }
}
copyfile_warn("open on %s", s->dst);
return -1;
}
}
- if (!filesec_set_property(s->fsec, FILESEC_ACL, &acl_dst))
+ if (!filesec_set_property(fsec_dst, FILESEC_ACL, &acl_dst))
{
copyfile_debug(1, "altered acl");
}
}
no_acl:
- if (fchmodx_np(s->dst_fd, s->fsec) < 0 && errno != ENOTSUP)
+ if (fchmodx_np(s->dst_fd, fsec_dst) < 0 && errno != ENOTSUP)
+ {
copyfile_warn("setting security information: %s", s->dst);
+ ret = -1;
+ }
cleanup:
filesec_free(fsec_dst);
u_int32_t type; /* entry type: see list, 0 invalid */
u_int32_t offset; /* entry data offset from the beginning of the file. */
u_int32_t length; /* entry data length in bytes. */
-} __attribute__((packed)) apple_double_entry_t;
+} apple_double_entry_t;
typedef struct apple_double_header
apple_double_entry_t entries[2]; /* 'finfo' & 'rsrc' always exist */
u_int8_t finfo[FINDERINFOSIZE]; /* Must start with Finder Info (32 bytes) */
u_int8_t pad[2]; /* get better alignment inside attr_header */
-} __attribute__((packed)) apple_double_header_t;
+} apple_double_header_t;
/* Entries are aligned on 4 byte boundaries */
u_int16_t flags;
u_int8_t namelen; /* length of name including NULL termination char */
u_int8_t name[1]; /* NULL-terminated UTF-8 name (up to 128 bytes max) */
-} __attribute__((packed)) attr_entry_t;
+} attr_entry_t;
/* Header + entries must fit into 64K */
u_int32_t reserved[3];
u_int16_t flags;
u_int16_t num_attrs;
-} __attribute__((packed)) attr_header_t;
+} attr_header_t;
#pragma options align=reset
#endif
}
-static u_int32_t emptyfinfo[8] = {0};
+static const u_int32_t emptyfinfo[8] = {0};
static int copyfile_unpack(copyfile_state_t s)
{
- int bytes;
- void * buffer;
+ ssize_t bytes;
+ void * buffer, * endptr;
apple_double_header_t *adhdr;
size_t hdrsize;
int error = 0;
hdrsize = ATTR_MAX_HDR_SIZE;
buffer = calloc(1, hdrsize);
+ if (buffer == NULL) {
+ copyfile_debug(1, "copyfile_unpack: calloc(1, %u) returned NULL", hdrsize);
+ error = -1;
+ goto exit;
+ } else
+ endptr = (char*)buffer + hdrsize;
+
bytes = pread(s->src_fd, buffer, hdrsize, 0);
if (bytes < 0)
int count;
int i;
+ if (hdrsize < sizeof(attr_header_t)) {
+ copyfile_warn("bad attribute header: %u < %u", hdrsize, sizeof(attr_header_t));
+ error = -1;
+ goto exit;
+ }
+
attrhdr = (attr_header_t *)buffer;
swap_attrhdr(attrhdr);
if (attrhdr->magic != ATTR_HDR_MAGIC)
}
count = attrhdr->num_attrs;
entry = (attr_entry_t *)&attrhdr[1];
+
for (i = 0; i < count; i++)
{
void * dataptr;
+ /*
+ * First we do some simple sanity checking.
+ * +) See if entry is within the buffer's range;
+ *
+ * +) Check the attribute name length; if it's longer than the
+ * maximum, we truncate it down. (We could error out as well;
+ * I'm not sure which is the better way to go here.)
+ *
+ * +) If, given the name length, it goes beyond the end of
+ * the buffer, error out.
+ *
+ * +) If the last byte isn't a NUL, make it a NUL. (Since we
+ * truncated the name length above, we truncate the name here.)
+ *
+ * +) If entry->offset is so large that it causes dataptr to
+ * go beyond the end of the buffer -- or, worse, so large that
+ * it wraps around! -- we error out.
+ *
+ * +) If entry->length would cause the entry to go beyond the
+ * end of the buffer (or, worse, wrap around to before it),
+ * *or* if the length is larger than the hdrsize, we error out.
+ * (An explanation of that: what we're checking for there is
+ * the small range of values such that offset+length would cause
+ * it to go beyond endptr, and then wrap around past buffer. We
+ * care about this because we are passing entry->length down to
+ * fgetxattr() below, and an erroneously large value could cause
+ * problems there. By making sure that it's less than hdrsize,
+ * which has already been sanity-checked above, we're safe.
+ * That may mean that the check against < buffer is unnecessary.)
+ */
+ if ((void*)entry >= endptr || (void*)entry < buffer) {
+ if (COPYFILE_VERBOSE & s->flags)
+ copyfile_warn("Incomplete or corrupt attribute entry");
+ error = -1;
+ goto exit;
+ }
+
+ if (((void*)entry + sizeof(*entry)) > endptr) {
+ if (COPYFILE_VERBOSE & s->flags)
+ copyfile_warn("Incomplete or corrupt attribute entry");
+ error = -1;
+ goto exit;
+ }
+
+ if (entry->namelen < 2) {
+ if (COPYFILE_VERBOSE & s->flags)
+ copyfile_warn("Corrupt attribute entry (only %d bytes)", entry->namelen);
+ error = -1;
+ goto exit;
+ }
+ if (entry->namelen > ATTR_MAX_NAME_LEN + 1) {
+ if (COPYFILE_VERBOSE & s->flags)
+ copyfile_warn("Corrupt attribute entry (name length is %d bytes)", entry->namelen);
+ error = -1;
+ goto exit;
+ }
+ if ((void*)(entry->name + entry->namelen) >= endptr) {
+ if (COPYFILE_VERBOSE & s->flags)
+ copyfile_warn("Incomplete or corrupt attribute entry");
+ error = -1;
+ goto exit;
+ }
+
+ /* Because namelen includes the NUL, we check one byte back */
+ if (entry->name[entry->namelen-1] != 0) {
+ if (COPYFILE_VERBOSE & s->flags)
+ copyfile_warn("Corrupt attribute entry (name is not NUL-terminated)");
+ error = -1;
+ goto exit;
+ }
+
+ copyfile_debug(3, "extracting \"%s\" (%d bytes) at offset %u",
+ entry->name, entry->length, entry->offset);
+
+ dataptr = (char *)attrhdr + entry->offset;
+
+ if (dataptr >= endptr || dataptr < buffer) {
+ copyfile_debug(1, "Entry %d overflows: offset = %u", entry->offset);
+ error = -1;
+ goto exit;
+ }
+ if ((dataptr + entry->length) > endptr ||
+ ((dataptr + entry->length) < buffer) ||
+ (entry->length > hdrsize)) {
+ if (COPYFILE_VERBOSE & s->flags)
+ copyfile_warn("Incomplete or corrupt attribute entry");
+ copyfile_debug(1, "Entry %d length overflows: dataptr = %u, offset = %u, length = %u, buffer = %u, endptr = %u",
+ i, dataptr, entry->offset, entry->length, buffer, endptr);
+ error = -1;
+ goto exit;
+ }
+
+ if (COPYFILE_ACL & s->flags && strcmp((char*)entry->name, XATTR_SECURITY_NAME) == 0)
copyfile_debug(2, "extracting \"%s\" (%d bytes)",
entry->name, entry->length);
dataptr = (char *)attrhdr + entry->offset;
if (COPYFILE_ACL & s->flags && strncmp(entry->name, XATTR_SECURITY_NAME, strlen(XATTR_SECURITY_NAME)) == 0)
{
acl_t acl;
- if ((acl = acl_from_text(dataptr)) != NULL)
+ char *tacl = strdup(dataptr);
+ if (tacl)
{
- if (filesec_set_property(s->fsec, FILESEC_ACL, &acl) < 0)
+ tacl[entry->length] = 0; /* Ensure it is NUL-terminated */
+ if (acl = acl_from_text(tacl))
{
- acl_t acl;
- if ((acl = acl_from_text(dataptr)) != NULL)
+ filesec_t tfsec = filesec_init();
+ if (tfsec)
+ {
+ if (filesec_set_property(tfsec, FILESEC_ACL, &acl) < 0)
{
- if (filesec_set_property(s->fsec, FILESEC_ACL, &acl) < 0)
- {
- copyfile_debug(1, "setting acl");
- }
- else if (fchmodx_np(s->dst_fd, s->fsec) < 0 && errno != ENOTSUP)
- copyfile_warn("setting security information");
- acl_free(acl);
+ copyfile_debug(1, "setting acl");
+ error = -1;
}
- } else
- if (COPYFILE_XATTR & s->flags && (fsetxattr(s->dst_fd, entry->name, dataptr, entry->length, 0, 0))) {
- if (COPYFILE_VERBOSE & s->flags)
- copyfile_warn("error %d setting attribute %s", error, entry->name);
- goto exit;
+ else if (fchmodx_np(s->dst_fd, tfsec) < 0 && errno != ENOTSUP)
+ {
+ error = -1;
+ copyfile_debug(1, "applying acl to file");
+ }
+ filesec_free(tfsec);
+ }
+ acl_free(acl);
}
- else if (fchmodx_np(s->dst_fd, s->fsec) < 0 && errno != ENOTSUP)
- copyfile_warn("setting security information");
- acl_free(acl);
+ free(tacl);
}
+ if (error)
+ goto exit;
} else
if (COPYFILE_XATTR & s->flags && (fsetxattr(s->dst_fd, entry->name, dataptr, entry->length, 0, 0))) {
if (COPYFILE_VERBOSE & s->flags)
/*
* Extract the Finder Info.
*/
+ if (adhdr->entries[0].offset > (hdrsize - sizeof(emptyfinfo))) {
+ error = -1;
+ goto exit;
+ }
+
if (bcmp((u_int8_t*)buffer + adhdr->entries[0].offset, emptyfinfo, sizeof(emptyfinfo)) != 0)
{
copyfile_debug(1, " extracting \"%s\" (32 bytes)", XATTR_FINDERINFO_NAME);
if (adhdr->entries[1].type == AD_RESOURCE &&
adhdr->entries[1].length > 0)
{
- void * rsrcforkdata;
+ void * rsrcforkdata = NULL;
size_t length;
off_t offset;
+ struct stat sb;
+ struct timeval tval[2];
length = adhdr->entries[1].length;
offset = adhdr->entries[1].offset;
rsrcforkdata = malloc(length);
+ if (rsrcforkdata == NULL) {
+ copyfile_debug(1, "could not allocate %u bytes for rsrcforkdata",
+ length);
+ error = -1;
+ goto bad;
+ }
+
+ if (fstat(s->dst_fd, &sb) < 0)
+ {
+ copyfile_debug(1, "couldn't stat destination file");
+ error = -1;
+ goto exit;
+ }
+
bytes = pread(s->src_fd, rsrcforkdata, length, offset);
if (bytes < length)
{
(int)bytes, (int)length);
}
error = -1;
- goto exit;
+ goto bad;
}
error = fsetxattr(s->dst_fd, XATTR_RESOURCEFORK_NAME, rsrcforkdata, bytes, 0, 0);
if (error)
{
copyfile_debug(1, "error %d setting resource fork attribute", error);
error = -1;
- goto exit;
+ goto bad;
}
- copyfile_debug(1, "extracting \"%s\" (%d bytes)",
+ copyfile_debug(1, "extracting \"%s\" (%d bytes)",
XATTR_RESOURCEFORK_NAME, (int)length);
- free(rsrcforkdata);
+
+ tval[0].tv_sec = sb.st_atime;
+ tval[1].tv_sec = sb.st_mtime;
+ tval[0].tv_usec = tval[1].tv_usec = 0;
+
+ if (futimes(s->dst_fd, tval))
+ {
+ copyfile_warn("cannot set time on destination file %s", s->dst ? s->dst : "<no filename>");
+ }
+
+bad:
+ if (rsrcforkdata)
+ free(rsrcforkdata);
}
exit:
- free(buffer);
+ if (buffer) free(buffer);
return error;
}
static int copyfile_pack(copyfile_state_t s)
{
- char *attrnamebuf;
- void *databuf;
- attr_header_t *filehdr;
+ char *attrnamebuf = NULL, *endnamebuf;
+ void *databuf = NULL;
+ attr_header_t *filehdr, *endfilehdr;
attr_entry_t *entry;
- ssize_t listsize;
+ ssize_t listsize = 0;
char *nameptr;
int namelen;
int entrylen;
int error = 0;
filehdr = (attr_header_t *) calloc(1, ATTR_MAX_SIZE);
+ if (filehdr == NULL) {
+ error = -1;
+ goto exit;
+ } else {
+ endfilehdr = ((void*)filehdr) + ATTR_MAX_SIZE;
+ }
+
attrnamebuf = calloc(1, ATTR_MAX_HDR_SIZE);
+ if (attrnamebuf == NULL) {
+ error = -1;
+ goto exit;
+ } else {
+ endnamebuf = ((char*)attrnamebuf) + ATTR_MAX_HDR_SIZE;
+ }
/*
* Fill in the Apple Double Header defaults.
*/
- filehdr->appledouble.magic = ADH_MAGIC;
- filehdr->appledouble.version = ADH_VERSION;
- filehdr->appledouble.numEntries = 2;
- filehdr->appledouble.entries[0].type = AD_FINDERINFO;
- filehdr->appledouble.entries[0].offset = offsetof(apple_double_header_t, finfo);
- filehdr->appledouble.entries[0].length = FINDERINFOSIZE;
- filehdr->appledouble.entries[1].type = AD_RESOURCE;
- filehdr->appledouble.entries[1].offset = offsetof(apple_double_header_t, pad);
+ filehdr->appledouble.magic = SWAP32 (ADH_MAGIC);
+ filehdr->appledouble.version = SWAP32 (ADH_VERSION);
+ filehdr->appledouble.numEntries = SWAP16 (2);
+ filehdr->appledouble.entries[0].type = SWAP32 (AD_FINDERINFO);
+ filehdr->appledouble.entries[0].offset = SWAP32 (offsetof(apple_double_header_t, finfo));
+ filehdr->appledouble.entries[0].length = SWAP32 (FINDERINFOSIZE);
+ filehdr->appledouble.entries[1].type = SWAP32 (AD_RESOURCE);
+ filehdr->appledouble.entries[1].offset = SWAP32 (offsetof(apple_double_header_t, pad));
filehdr->appledouble.entries[1].length = 0;
bcopy(ADH_MACOSX, filehdr->appledouble.filler, sizeof(filehdr->appledouble.filler));
/*
* Fill in the initial Attribute Header.
*/
- filehdr->magic = ATTR_HDR_MAGIC;
- filehdr->debug_tag = s->sb.st_ino;
- filehdr->data_start = sizeof(attr_header_t);
+ filehdr->magic = SWAP32 (ATTR_HDR_MAGIC);
+ filehdr->debug_tag = SWAP32 (s->sb.st_ino);
+ filehdr->data_start = SWAP32 (sizeof(attr_header_t));
/*
* Collect the attribute names.
if (COPYFILE_XATTR & s->flags)
{
- if ((listsize = flistxattr(s->src_fd, attrnamebuf + offset, ATTR_MAX_HDR_SIZE, 0)) <= 0)
+ ssize_t left = ATTR_MAX_HDR_SIZE - offset;
+ if ((listsize = flistxattr(s->src_fd, attrnamebuf + offset, left, 0)) <= 0)
{
copyfile_debug(1, "no extended attributes found (%d)", errno);
}
- if (listsize > ATTR_MAX_HDR_SIZE)
+ if (listsize > left)
{
copyfile_debug(1, "extended attribute list too long");
listsize = ATTR_MAX_HDR_SIZE;
}
listsize += offset;
+ endnamebuf = attrnamebuf + listsize;
+ if (endnamebuf > (attrnamebuf + ATTR_MAX_HDR_SIZE)) {
+ error = -1;
+ goto exit;
+ }
- for (nameptr = attrnamebuf; nameptr < attrnamebuf + listsize; nameptr += namelen)
+ for (nameptr = attrnamebuf; nameptr <endnamebuf; nameptr += namelen)
{
namelen = strlen(nameptr) + 1;
/* Skip over FinderInfo or Resource Fork names */
- if (strncmp(nameptr, XATTR_FINDERINFO_NAME, strlen(XATTR_FINDERINFO_NAME)) == 0 ||
- strncmp(nameptr, XATTR_RESOURCEFORK_NAME, strlen(XATTR_RESOURCEFORK_NAME)) == 0)
+ if (strcmp(nameptr, XATTR_FINDERINFO_NAME) == 0 ||
+ strcmp(nameptr, XATTR_RESOURCEFORK_NAME) == 0)
continue;
+ /* The system should prevent this from happening, but... */
+ if (namelen > XATTR_MAXNAMELEN + 1) {
+ namelen = XATTR_MAXNAMELEN + 1;
+ }
entry->namelen = namelen;
entry->flags = 0;
bcopy(nameptr, &entry->name[0], namelen);
entrylen = ATTR_ENTRY_LENGTH(namelen);
entry = (attr_entry_t *)(((char *)entry) + entrylen);
-
+
+ if ((void*)entry > (void*)endfilehdr) {
+ error = -1;
+ goto exit;
+ }
+
/* Update the attributes header. */
filehdr->num_attrs++;
filehdr->data_start += entrylen;
for (nameptr = attrnamebuf; nameptr < attrnamebuf + listsize; nameptr += namelen + 1)
{
- nameptr = nameptr;
namelen = strlen(nameptr);
- if (strncmp(nameptr, XATTR_SECURITY_NAME, strlen(XATTR_SECURITY_NAME)) == 0)
+ if (strcmp(nameptr, XATTR_SECURITY_NAME) == 0)
copyfile_pack_acl(s, &databuf, &datasize);
else
/* Check for Finder Info. */
- if (strncmp(nameptr, XATTR_FINDERINFO_NAME, strlen(XATTR_FINDERINFO_NAME)) == 0)
+ if (strcmp(nameptr, XATTR_FINDERINFO_NAME) == 0)
{
datasize = fgetxattr(s->src_fd, nameptr, (u_int8_t*)filehdr + filehdr->appledouble.entries[0].offset, 32, 0, 0);
if (datasize < 0)
continue; /* finder info doesn't have an attribute entry */
} else
/* Check for Resource Fork. */
- if (strncmp(nameptr, XATTR_RESOURCEFORK_NAME, strlen(XATTR_RESOURCEFORK_NAME)) == 0)
+ if (strcmp(nameptr, XATTR_RESOURCEFORK_NAME) == 0)
{
hasrsrcfork = 1;
continue;
goto next;
}
databuf = malloc(datasize);
+ if (databuf == NULL) {
+ error = -1;
+ continue;
+ }
datasize = fgetxattr(s->src_fd, nameptr, databuf, datasize, 0, 0);
}
* the case when there are lots of attributes or one of
* the attributes is very large.
*/
- bcopy(databuf, (char*)filehdr + entry->offset, datasize);
+ if (entry->offset > ATTR_MAX_SIZE ||
+ (entry->offset + datasize > ATTR_MAX_SIZE)) {
+ error = -1;
+ } else {
+ bcopy(databuf, (char*)filehdr + entry->offset, datasize);
+ }
free(databuf);
copyfile_debug(1, "copied %ld bytes of \"%s\" data @ offset 0x%08x", datasize, nameptr, entry->offset);
filehdr->appledouble.entries[0].length =
filehdr->appledouble.entries[1].offset - filehdr->appledouble.entries[0].offset;
- filehdr->total_size = filehdr->appledouble.entries[1].offset;
+ filehdr->total_size = SWAP32 (filehdr->appledouble.entries[1].offset);
}
/* Copy Resource Fork. */
/* Write the header to disk. */
datasize = filehdr->appledouble.entries[1].offset;
- swap_adhdr(&filehdr->appledouble);
- swap_attrhdr(filehdr);
-
if (pwrite(s->dst_fd, filehdr, datasize, 0) != datasize)
{
if (COPYFILE_VERBOSE & s->flags)
goto exit;
}
exit:
- free(filehdr);
- free(attrnamebuf);
+ if (filehdr) free(filehdr);
+ if (attrnamebuf) free(attrnamebuf);
if (error)
return error;