X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/378393581903b274cb7a4d18e0d978071a6b592d..6601e61aa18bf4f09af135ff61fc7f4771d23b06:/bsd/vfs/vfs_xattr.c diff --git a/bsd/vfs/vfs_xattr.c b/bsd/vfs/vfs_xattr.c index 7ecca5261..4d225635f 100644 --- a/bsd/vfs/vfs_xattr.c +++ b/bsd/vfs/vfs_xattr.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2005 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2004-2007 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -37,7 +37,7 @@ #include -#include +#include #include /* @@ -241,13 +241,13 @@ xattr_validatename(const char *name) if (name == NULL || name[0] == '\0') { return (EINVAL); } - namelen = strlen(name); - if (namelen > XATTR_MAXNAMELEN) { + namelen = strnlen(name, XATTR_MAXNAMELEN); + if (name[namelen] != '\0') return (ENAMETOOLONG); - } - if (utf8_validatestr(name, namelen) != 0) { + + if (utf8_validatestr(name, namelen) != 0) return (EINVAL); - } + return (0); } @@ -363,7 +363,15 @@ xattr_protected(const char *attrname) * the attribute entries must reside within this limit. If * any of the attribute data crosses the ATTR_MAX_HDR_SIZE * boundry, then all of the attribute data I/O is performed - * seperately from the attribute header I/O. + * separately from the attribute header I/O. + * + * In particular, all of the attr_entry structures must lie + * completely within the first ATTR_MAX_HDR_SIZE bytes of the + * AppleDouble file. However, the attribute data (i.e. the + * contents of the extended attributes) may extend beyond the + * first ATTR_MAX_HDR_SIZE bytes of the file. Note that this + * limit is to allow the implementation to optimize by reading + * the first ATTR_MAX_HDR_SIZE bytes of the file. */ @@ -400,12 +408,12 @@ typedef struct attr_entry { } attr_entry_t; -/* Header + entries must fit into 64K */ +/* Header + entries must fit into 64K. Data may extend beyond 64k. */ typedef struct attr_header { apple_double_header_t appledouble; u_int32_t magic; /* == ATTR_HDR_MAGIC */ u_int32_t debug_tag; /* for debugging == file id of owning file */ - u_int32_t total_size; /* total size of attribute header + entries + data */ + u_int32_t total_size; /* file offset of end of attribute header + entries + data */ u_int32_t data_start; /* file offset to attribute data area */ u_int32_t data_length; /* length of attribute data area */ u_int32_t reserved[3]; @@ -448,7 +456,7 @@ typedef struct attr_info { size_t filesize; size_t iosize; u_int8_t *rawdata; - size_t rawsize; /* raw size of AppleDouble file */ + size_t rawsize; /* minimum of filesize or ATTR_MAX_HDR_SIZE */ apple_double_header_t *filehdr; apple_double_entry_t *finderinfo; apple_double_entry_t *rsrcfork; @@ -472,10 +480,9 @@ typedef struct attr_info { #define ATTR_VALID(ae, ai) \ ((u_int8_t *)ATTR_NEXT(ae) <= ((ai).rawdata + (ai).rawsize)) - -#define SWAP16(x) NXSwapBigShortToHost((x)) -#define SWAP32(x) NXSwapBigIntToHost((x)) -#define SWAP64(x) NXSwapBigLongLongToHost((x)) +#define SWAP16(x) OSSwapBigToHostInt16((x)) +#define SWAP32(x) OSSwapBigToHostInt32((x)) +#define SWAP64(x) OSSwapBigToHostInt64((x)) static u_int32_t emptyfinfo[8] = {0}; @@ -507,17 +514,108 @@ static int unlock_xattrfile(vnode_t xvp, vfs_context_t context); #if BYTE_ORDER == LITTLE_ENDIAN static void swap_adhdr(apple_double_header_t *adh); - static void swap_attrhdr(attr_header_t *ah); + static void swap_attrhdr(attr_header_t *ah, attr_info_t* info); #else #define swap_adhdr(x) -#define swap_attrhdr(x) +#define swap_attrhdr(x,y) #endif static int validate_attrhdr(attr_header_t *ah, size_t bufsize); +static int check_and_swap_attrhdr(attr_header_t *ah, attr_info_t* ainfop); static int shift_data_down(vnode_t xvp, off_t start, size_t len, off_t delta, vfs_context_t context); static int shift_data_up(vnode_t xvp, off_t start, size_t len, off_t delta, vfs_context_t context); +/* + * Sanity check and swap the header of an AppleDouble file. Assumes the buffer + * is in big endian (as it would exist on disk). Verifies the following: + * - magic field + * - version field + * - number of entries + * - that each entry fits within the file size + * + * If the header is invalid, ENOATTR is returned. + * + * NOTE: Does not attempt to validate the extended attributes header that + * may be embedded in the Finder Info entry. + */ + +static int check_and_swap_apple_double_header(attr_info_t *ainfop) +{ + int i, j; + u_int32_t header_end; + u_int32_t entry_end; + size_t rawsize; + apple_double_header_t *header; + + rawsize = ainfop->rawsize; + header = (apple_double_header_t *) ainfop->rawdata; + + /* Is the file big enough to contain an AppleDouble header? */ + if (rawsize < offsetof(apple_double_header_t, entries)) + return ENOATTR; + + /* Swap the AppleDouble header fields to native order */ + header->magic = SWAP32(header->magic); + header->version = SWAP32(header->version); + header->numEntries = SWAP16(header->numEntries); + + /* Sanity check the AppleDouble header fields */ + if (header->magic != ADH_MAGIC || + header->version != ADH_VERSION || + header->numEntries < 1 || + header->numEntries > 15) { + return ENOATTR; + } + + /* Calculate where the entries[] array ends */ + header_end = offsetof(apple_double_header_t, entries) + + header->numEntries * sizeof(apple_double_entry_t); + + /* Is the file big enough to contain the AppleDouble entries? */ + if (rawsize < header_end) { + return ENOATTR; + } + + /* Swap and sanity check each AppleDouble entry */ + for (i=0; inumEntries; i++) { + /* Swap the per-entry fields to native order */ + header->entries[i].type = SWAP32(header->entries[i].type); + header->entries[i].offset = SWAP32(header->entries[i].offset); + header->entries[i].length = SWAP32(header->entries[i].length); + + entry_end = header->entries[i].offset + header->entries[i].length; + + /* + * Does the entry's content start within the header itself, + * did the addition overflow, or does the entry's content + * extend past the end of the file? + */ + if (header->entries[i].offset < header_end || + entry_end < header->entries[i].offset || + entry_end > ainfop->filesize) { + return ENOATTR; + } + + /* + * Does the current entry's content overlap with a previous + * entry's content? + * + * Yes, this is O(N**2), and there are more efficient algorithms + * for testing pairwise overlap of N ranges when N is large. + * But we have already ensured N < 16, and N is almost always 2. + * So there's no point in using a more complex algorithm. + */ + for (j=0; j header->entries[j].offset && + header->entries[j].offset + header->entries[j].length > header->entries[i].offset) { + return ENOATTR; + } + } + } + + return 0; +} /* * Retrieve the data of an extended attribute. @@ -1331,8 +1429,9 @@ lookup: if (fileflags & O_CREAT) { nd.ni_cnd.cn_nameiop = CREATE; - nd.ni_cnd.cn_flags |= LOCKPARENT; - + if (dvp != vp) { + nd.ni_cnd.cn_flags |= LOCKPARENT; + } if ( (error = namei(&nd))) { nd.ni_dvp = NULLVP; error = ENOATTR; @@ -1378,8 +1477,9 @@ lookup: xvp = nd.ni_vp; } nameidone(&nd); - vnode_put(dvp); /* drop iocount from LOCKPARENT request above */ - + if (dvp != vp) { + vnode_put(dvp); /* drop iocount from LOCKPARENT request above */ + } if (error) goto out; } else { @@ -1526,14 +1626,31 @@ remove_xattrfile(vnode_t xvp, vfs_context_t context) return (error); } - +/* + * Read in and parse the AppleDouble header and entries, and the extended + * attribute header and entries if any. Populates the fields of ainfop + * based on the headers and entries found. + * + * The basic idea is to: + * - Read in up to ATTR_MAX_HDR_SIZE bytes of the start of the file. All + * AppleDouble entries, the extended attribute header, and extended + * attribute entries must lie within this part of the file; the rest of + * the AppleDouble handling code assumes this. Plus it allows us to + * somewhat optimize by doing a smaller number of larger I/Os. + * - Swap and sanity check the AppleDouble header (including the AppleDouble + * entries). + * - Find the Finder Info and Resource Fork entries, if any. + * - If we're going to be writing, try to make sure the Finder Info entry has + * room to store the extended attribute header, plus some space for extended + * attributes. + * - Swap and sanity check the extended attribute header and entries (if any). + */ static int get_xattrinfo(vnode_t xvp, int setting, attr_info_t *ainfop, vfs_context_t context) { uio_t auio = NULL; void * buffer = NULL; apple_double_header_t *filehdr; - attr_header_t *attrhdr; struct vnode_attr va; size_t iosize; int i; @@ -1562,6 +1679,11 @@ get_xattrinfo(vnode_t xvp, int setting, attr_info_t *ainfop, vfs_context_t conte } ainfop->iosize = iosize; MALLOC(buffer, void *, iosize, M_TEMP, M_WAITOK); + if (buffer == NULL){ + error = ENOMEM; + goto bail; + } + auio = uio_create(1, 0, UIO_SYSSPACE32, UIO_READ); uio_addiov(auio, (uintptr_t)buffer, iosize); @@ -1572,114 +1694,75 @@ get_xattrinfo(vnode_t xvp, int setting, attr_info_t *ainfop, vfs_context_t conte } ainfop->rawsize = iosize - uio_resid(auio); ainfop->rawdata = (u_int8_t *)buffer; - + filehdr = (apple_double_header_t *)buffer; - /* Check for Apple Double file. */ - if (SWAP32(filehdr->magic) != ADH_MAGIC || - SWAP32(filehdr->version) != ADH_VERSION || - SWAP16(filehdr->numEntries) < 1 || - SWAP16(filehdr->numEntries) > 15) { - error = ENOATTR; - goto bail; - } - if (ADHDRSIZE + (SWAP16(filehdr->numEntries) * sizeof(apple_double_entry_t)) > ainfop->rawsize) { - error = EINVAL; + error = check_and_swap_apple_double_header(ainfop); + if (error) goto bail; - } - swap_adhdr(filehdr); ainfop->filehdr = filehdr; /* valid AppleDouble header */ + /* rel_xattrinfo is responsible for freeing the header buffer */ buffer = NULL; - /* Check the AppleDouble entries. */ + /* Find the Finder Info and Resource Fork entries, if any */ for (i = 0; i < filehdr->numEntries; ++i) { if (filehdr->entries[i].type == AD_FINDERINFO && - filehdr->entries[i].length > 0) { + filehdr->entries[i].length >= FINDERINFOSIZE) { + /* We found the Finder Info entry. */ ainfop->finderinfo = &filehdr->entries[i]; - attrhdr = (attr_header_t *)filehdr; - if (bcmp((u_int8_t*)ainfop->filehdr + ainfop->finderinfo->offset, - emptyfinfo, sizeof(emptyfinfo)) == 0) { + /* + * Is the Finder Info "empty" (all zeroes)? If so, + * we'll pretend like the Finder Info extended attribute + * does not exist. + * + * Note: we have to make sure the Finder Info is + * contained within the buffer we have already read, + * to avoid accidentally accessing a bogus address. + * If it is outside the buffer, we just assume the + * Finder Info is non-empty. + */ + if (ainfop->finderinfo->offset + FINDERINFOSIZE <= ainfop->rawsize && + bcmp((u_int8_t*)ainfop->filehdr + ainfop->finderinfo->offset, emptyfinfo, sizeof(emptyfinfo)) == 0) { ainfop->emptyfinderinfo = 1; } - if (i != 0) { + } + + if (filehdr->entries[i].type == AD_RESOURCE) { + /* + * Ignore zero-length resource forks when getting. If setting, + * we need to remember the resource fork entry so it can be + * updated once the new content has been written. + */ + if (filehdr->entries[i].length == 0 && !setting) continue; - } - /* See if we need to convert this AppleDouble file. */ - if (filehdr->entries[0].length == FINDERINFOSIZE) { - size_t delta; - size_t writesize; - - if (!setting || - filehdr->entries[1].type != AD_RESOURCE || - filehdr->numEntries > 2) { - continue; /* not expected layout */ - } - delta = ATTR_BUF_SIZE - (filehdr->entries[0].offset + FINDERINFOSIZE); - if (filehdr->entries[1].length) { - /* Make some room. */ - shift_data_down(xvp, - filehdr->entries[1].offset, - filehdr->entries[1].length, - delta, context); - writesize = sizeof(attr_header_t); - } else { - rsrcfork_header_t *rsrcforkhdr; - - vnode_setsize(xvp, filehdr->entries[1].offset + delta, 0, context); - - /* Steal some space for an empty RF header. */ - delta -= sizeof(rsrcfork_header_t); - - bzero(&attrhdr->appledouble.pad[0], delta); - rsrcforkhdr = (rsrcfork_header_t *)((char *)filehdr + filehdr->entries[1].offset + delta); - - /* Fill in Empty Resource Fork Header. */ - init_empty_resource_fork(rsrcforkhdr); - - filehdr->entries[1].length = sizeof(rsrcfork_header_t); - writesize = ATTR_BUF_SIZE; + + /* + * Check to see if any "empty" resource fork is ours (i.e. is ignorable). + * + * The "empty" resource headers we created have a system data tag of: + * "This resource fork intentionally left blank " + */ + if (filehdr->entries[i].length == sizeof(rsrcfork_header_t) && !setting) { + uio_t rf_uio; + u_int8_t systemData[64]; + int rf_err; + + /* Read the system data which starts at byte 16 */ + rf_uio = uio_create(1, 0, UIO_SYSSPACE32, UIO_READ); + uio_addiov(rf_uio, (uintptr_t)systemData, sizeof(systemData)); + uio_setoffset(rf_uio, filehdr->entries[i].offset + 16); + rf_err = VNOP_READ(xvp, rf_uio, 0, context); + uio_free(rf_uio); + + if (rf_err != 0 || + bcmp(systemData, RF_EMPTY_TAG, sizeof(RF_EMPTY_TAG)) == 0) { + continue; /* skip this resource fork */ } - filehdr->entries[0].length += delta; - filehdr->entries[1].offset += delta; - - /* Fill in Attribute Header. */ - attrhdr->magic = ATTR_HDR_MAGIC; - attrhdr->debug_tag = (u_int32_t)va.va_fileid; - attrhdr->total_size = filehdr->entries[1].offset; - attrhdr->data_start = sizeof(attr_header_t); - attrhdr->data_length = 0; - attrhdr->reserved[0] = 0; - attrhdr->reserved[1] = 0; - attrhdr->reserved[2] = 0; - attrhdr->flags = 0; - attrhdr->num_attrs = 0; - - /* Push out new header */ - uio_reset(auio, 0, UIO_SYSSPACE32, UIO_WRITE); - uio_addiov(auio, (uintptr_t)filehdr, writesize); - - swap_adhdr(filehdr); - swap_attrhdr(attrhdr); - error = VNOP_WRITE(xvp, auio, 0, context); - swap_adhdr(filehdr); - /* The attribute header gets swapped below. */ - } - if (SWAP32 (attrhdr->magic) != ATTR_HDR_MAGIC || - validate_attrhdr(attrhdr, ainfop->rawsize) != 0) { - printf("get_xattrinfo: invalid attribute header\n"); - continue; } - swap_attrhdr(attrhdr); - ainfop->attrhdr = attrhdr; /* valid attribute header */ - ainfop->attr_entry = (attr_entry_t *)&attrhdr[1]; - continue; - } - if (filehdr->entries[i].type == AD_RESOURCE && - (filehdr->entries[i].length > sizeof(rsrcfork_header_t) || setting)) { ainfop->rsrcfork = &filehdr->entries[i]; if (i != (filehdr->numEntries - 1)) { printf("get_xattrinfo: resource fork not last entry\n"); @@ -1688,6 +1771,111 @@ get_xattrinfo(vnode_t xvp, int setting, attr_info_t *ainfop, vfs_context_t conte continue; } } + + /* + * See if this file looks like it is laid out correctly to contain + * extended attributes. If so, then do the following: + * + * - If we're going to be writing, try to make sure the Finder Info + * entry has room to store the extended attribute header, plus some + * space for extended attributes. + * + * - Swap and sanity check the extended attribute header and entries + * (if any). + */ + if (filehdr->numEntries == 2 && + ainfop->finderinfo == &filehdr->entries[0] && + ainfop->rsrcfork == &filehdr->entries[1] && + ainfop->finderinfo->offset == offsetof(apple_double_header_t, finfo)) { + attr_header_t *attrhdr; + attrhdr = (attr_header_t *)filehdr; + /* + * If we're going to be writing, try to make sure the Finder + * Info entry has room to store the extended attribute header, + * plus some space for extended attributes. + */ + if (setting && ainfop->finderinfo->length == FINDERINFOSIZE) { + size_t delta; + size_t writesize; + + delta = ATTR_BUF_SIZE - (filehdr->entries[0].offset + FINDERINFOSIZE); + if (ainfop->rsrcfork && filehdr->entries[1].length) { + /* Make some room before existing resource fork. */ + shift_data_down(xvp, + filehdr->entries[1].offset, + filehdr->entries[1].length, + delta, context); + writesize = sizeof(attr_header_t); + } else { + /* Create a new, empty resource fork. */ + rsrcfork_header_t *rsrcforkhdr; + + vnode_setsize(xvp, filehdr->entries[1].offset + delta, 0, context); + + /* Steal some space for an empty RF header. */ + delta -= sizeof(rsrcfork_header_t); + + bzero(&attrhdr->appledouble.pad[0], delta); + rsrcforkhdr = (rsrcfork_header_t *)((char *)filehdr + filehdr->entries[1].offset + delta); + + /* Fill in Empty Resource Fork Header. */ + init_empty_resource_fork(rsrcforkhdr); + + filehdr->entries[1].length = sizeof(rsrcfork_header_t); + writesize = ATTR_BUF_SIZE; + } + filehdr->entries[0].length += delta; + filehdr->entries[1].offset += delta; + + /* Fill in Attribute Header. */ + attrhdr->magic = ATTR_HDR_MAGIC; + attrhdr->debug_tag = (u_int32_t)va.va_fileid; + attrhdr->total_size = filehdr->entries[1].offset; + attrhdr->data_start = sizeof(attr_header_t); + attrhdr->data_length = 0; + attrhdr->reserved[0] = 0; + attrhdr->reserved[1] = 0; + attrhdr->reserved[2] = 0; + attrhdr->flags = 0; + attrhdr->num_attrs = 0; + + /* Push out new header */ + uio_reset(auio, 0, UIO_SYSSPACE32, UIO_WRITE); + uio_addiov(auio, (uintptr_t)filehdr, writesize); + + swap_adhdr(filehdr); /* to big endian */ + swap_attrhdr(attrhdr, ainfop); /* to big endian */ + error = VNOP_WRITE(xvp, auio, 0, context); + swap_adhdr(filehdr); /* back to native */ + /* The attribute header gets swapped below. */ + } + } + + /* + * Swap and sanity check the extended attribute header and + * entries (if any). The Finder Info content must be big enough + * to include the extended attribute header; if not, we just + * ignore it. + * + * Note that we're passing the offset + length (i.e. the end) + * of the Finder Info instead of rawsize to validate_attrhdr. + * This ensures that all extended attributes lie within the + * Finder Info content according to the AppleDouble entry. + * + * Sets ainfop->attrhdr and ainfop->attr_entry if a valid + * header was found. + */ + if (ainfop->finderinfo && + ainfop->finderinfo == &filehdr->entries[0] && + ainfop->finderinfo->length >= (sizeof(attr_header_t) - sizeof(apple_double_header_t))) { + attr_header_t *attrhdr = (attr_header_t*)filehdr; + if ((error = check_and_swap_attrhdr(attrhdr, ainfop)) == 0) { + ainfop->attrhdr = attrhdr; /* valid attribute header */ + /* First attr_entry starts immediately following attribute header */ + ainfop->attr_entry = (attr_entry_t *)&attrhdr[1]; + } + } + error = 0; bail: if (auio != NULL) @@ -1780,12 +1968,16 @@ write_xattrinfo(attr_info_t *ainfop) uio_addiov(auio, (uintptr_t)ainfop->filehdr, ainfop->iosize); swap_adhdr(ainfop->filehdr); - swap_attrhdr(ainfop->attrhdr); + if (ainfop->attrhdr != NULL) + swap_attrhdr(ainfop->attrhdr, ainfop); error = VNOP_WRITE(ainfop->filevp, auio, 0, ainfop->context); swap_adhdr(ainfop->filehdr); - swap_attrhdr(ainfop->attrhdr); + if (ainfop->attrhdr != NULL) + swap_attrhdr(ainfop->attrhdr, ainfop); + + uio_free(auio); return (error); } @@ -1816,7 +2008,7 @@ swap_adhdr(apple_double_header_t *adh) * Endian swap extended attributes header */ static void -swap_attrhdr(attr_header_t *ah) +swap_attrhdr(attr_header_t *ah, attr_info_t* info) { attr_entry_t *ae; int count; @@ -1833,7 +2025,7 @@ swap_attrhdr(attr_header_t *ah) ah->num_attrs = SWAP16 (ah->num_attrs); ae = (attr_entry_t *)(&ah[1]); - for (i = 0; i < count; i++, ae = ATTR_NEXT(ae)) { + for (i = 0; i < count && ATTR_VALID(ae, *info); i++, ae = ATTR_NEXT(ae)) { ae->offset = SWAP32 (ae->offset); ae->length = SWAP32 (ae->length); ae->flags = SWAP16 (ae->flags); @@ -1842,28 +2034,93 @@ swap_attrhdr(attr_header_t *ah) #endif /* - * Validate attributes header contents + * Validate and swap the attributes header contents, and each attribute's + * attr_entry_t. + * + * Note: Assumes the caller has verified that the Finder Info content is large + * enough to contain the attr_header structure itself. Therefore, we can + * swap the header fields before sanity checking them. */ -static int -validate_attrhdr(attr_header_t *ah, size_t bufsize) + static int +check_and_swap_attrhdr(attr_header_t *ah, attr_info_t *ainfop) { attr_entry_t *ae; - u_int8_t *bufend; + u_int8_t *buf_end; + u_int32_t end; int count; int i; if (ah == NULL) - return (EINVAL); + return EINVAL; - bufend = (u_int8_t *)ah + bufsize; - count = (ah->magic == ATTR_HDR_MAGIC) ? ah->num_attrs : SWAP16(ah->num_attrs); + if (SWAP32(ah->magic) != ATTR_HDR_MAGIC) + return EINVAL; + + /* Swap the basic header fields */ + ah->magic = SWAP32(ah->magic); + ah->debug_tag = SWAP32 (ah->debug_tag); + ah->total_size = SWAP32 (ah->total_size); + ah->data_start = SWAP32 (ah->data_start); + ah->data_length = SWAP32 (ah->data_length); + ah->flags = SWAP16 (ah->flags); + ah->num_attrs = SWAP16 (ah->num_attrs); + /* + * Make sure the total_size fits within the Finder Info area, and the + * extended attribute data area fits within total_size. + */ + end = ah->data_start + ah->data_length; + if (ah->total_size > ainfop->finderinfo->offset + ainfop->finderinfo->length || + end < ah->data_start || + end > ah->total_size) { + return EINVAL; + } + + /* + * Make sure each of the attr_entry_t's fits within total_size. + */ + buf_end = ainfop->rawdata + ah->total_size; + count = ah->num_attrs; ae = (attr_entry_t *)(&ah[1]); - for (i = 0; i < count && (u_int8_t *)ae < bufend; i++, ae = ATTR_NEXT(ae)) { + + for (i=0; i buf_end) + return EINVAL; + + /* Make sure the variable-length name fits (+1 is for NUL terminator) */ + /* TODO: Make sure namelen matches strnlen(name,namelen+1)? */ + if (&ae->name[ae->namelen+1] > buf_end) + return EINVAL; + + /* Swap the attribute entry fields */ + ae->offset = SWAP32(ae->offset); + ae->length = SWAP32(ae->length); + ae->flags = SWAP16(ae->flags); + + /* Make sure the attribute content fits. */ + end = ae->offset + ae->length; + if (end < ae->offset || end > ah->total_size) + return EINVAL; + + ae = ATTR_NEXT(ae); } - return (i < count ? EINVAL : 0); + + /* + * TODO: Make sure the contents of attributes don't overlap the header + * and don't overlap each other. The hard part is that we don't know + * what the actual header size is until we have looped over all of the + * variable-sized attribute entries. + * + * XXX Is there any guarantee that attribute entries are stored in + * XXX order sorted by the contents' file offset? If so, that would + * XXX make the pairwise overlap check much easier. + */ + + return 0; } + // // "start" & "end" are byte offsets in the file. // "to" is the byte offset we want to move the