Libc-391.2.9.tar.gz
[apple/libc.git] / darwin / copyfile.c
index 3e5fc22aba2f66fa78c207e166126d82865b510d..ac3a3b7542e2976a706e661e82b89385ea6fc929 100644 (file)
@@ -34,6 +34,7 @@
 #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>
@@ -123,7 +124,6 @@ int copyfile(const char *src, const char *dst, copyfile_state_t state, copyfile_
        }
        copyfile_debug(1, "debug value set to: %d\n", s->debug);
     }
-
     if (COPYFILE_CHECK & flags)
        return copyfile_check(s);
 
@@ -143,6 +143,9 @@ int copyfile(const char *src, const char *dst, copyfile_state_t state, copyfile_
            }
        } 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
@@ -196,8 +199,12 @@ exit:
 
     filesec_free(original_fsec);
 
-    if (state == NULL)
-       ret -= copyfile_free(s);
+    if (state == NULL) {
+       if (copyfile_free(s) < 0) {
+               ret = -1;
+       }
+    }
+
     return ret;
 }
 
@@ -238,10 +245,10 @@ int copyfile_free(copyfile_state_t s)
 
 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;
@@ -258,7 +265,7 @@ static int copyfile_fix_perms(copyfile_state_t s, filesec_t *fsec, int on)
 
     if (on)
     {
-       if(statx_np(s->dst, &sb, *fsec))
+       if(fstatx_np(s->dst_fd, &sb, *fsec))
            goto error;
 
        tmp_fsec = filesec_dup(*fsec);
@@ -388,12 +395,33 @@ static int copyfile_open(copyfile_state_t s)
            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;
@@ -530,14 +558,17 @@ static int copyfile_security(copyfile_state_t s)
            }
        }
 
-       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);
@@ -835,7 +866,7 @@ typedef struct apple_double_entry
        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
@@ -847,7 +878,7 @@ 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 */
@@ -858,7 +889,7 @@ typedef struct attr_entry
        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 */
@@ -873,7 +904,7 @@ typedef struct attr_header
        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
@@ -948,12 +979,12 @@ swap_attrhdr(attr_header_t *ah)
 #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;
@@ -964,6 +995,13 @@ static int copyfile_unpack(copyfile_state_t s)
        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)
@@ -1013,6 +1051,12 @@ static int copyfile_unpack(copyfile_state_t s)
        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)
@@ -1024,10 +1068,104 @@ static int copyfile_unpack(copyfile_state_t s)
        }
        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;
@@ -1035,31 +1173,33 @@ static int copyfile_unpack(copyfile_state_t s)
            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)
@@ -1073,6 +1213,11 @@ static int copyfile_unpack(copyfile_state_t s)
     /*
      * 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);
@@ -1087,14 +1232,30 @@ static int copyfile_unpack(copyfile_state_t s)
     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)
        {
@@ -1109,21 +1270,33 @@ static int copyfile_unpack(copyfile_state_t s)
                    (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;
 }
 
@@ -1198,11 +1371,11 @@ static int copyfile_pack_rsrcfork(copyfile_state_t s, attr_header_t *filehdr)
 
 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;
@@ -1212,28 +1385,41 @@ static int copyfile_pack(copyfile_state_t s)
     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.
@@ -1257,26 +1443,36 @@ static int copyfile_pack(copyfile_state_t s)
 
     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);
@@ -1284,7 +1480,12 @@ static int copyfile_pack(copyfile_state_t s)
 
            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;
@@ -1298,14 +1499,13 @@ static int copyfile_pack(copyfile_state_t s)
 
     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)
@@ -1325,7 +1525,7 @@ static int copyfile_pack(copyfile_state_t s)
            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;
@@ -1348,6 +1548,10 @@ static int copyfile_pack(copyfile_state_t s)
                goto next;
            }
            databuf = malloc(datasize);
+           if (databuf == NULL) {
+               error = -1;
+               continue;
+           }
            datasize = fgetxattr(s->src_fd, nameptr, databuf, datasize, 0, 0);
        }
 
@@ -1361,7 +1565,12 @@ static int copyfile_pack(copyfile_state_t s)
         * 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);
@@ -1380,7 +1589,7 @@ next:
        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. */
@@ -1390,9 +1599,6 @@ next:
     /* 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)
@@ -1401,8 +1607,8 @@ next:
        goto exit;
     }
 exit:
-    free(filehdr);
-    free(attrnamebuf);
+    if (filehdr) free(filehdr);
+    if (attrnamebuf) free(attrnamebuf);
 
     if (error)
        return error;