/* * Construct and return the list of xattr name:value pairs for the passed xattr * id * * There are two users for get_xattr(), Mksquashfs uses it to read the * xattrs from the filesystem on appending, and Unsquashfs uses it * to retrieve the xattrs for writing to disk. * * Unfortunately, the two users disagree on what to do with unknown * xattr prefixes, Mksquashfs wants to treat this as fatal otherwise * this will cause xattrs to be be lost on appending. Unsquashfs * on the otherhand wants to retrieve the xattrs which are known and * to ignore the rest, this allows Unsquashfs to cope more gracefully * with future versions which may have unknown xattrs, as long as the * general xattr structure is adhered to, Unsquashfs should be able * to safely ignore unknown xattrs, and to write the ones it knows about, * this is better than completely refusing to retrieve all the xattrs. * * If ignore is TRUE then don't treat unknown xattr prefixes as * a failure to read the xattr. */ struct xattr_list *get_xattr(int i, unsigned int *count, int ignore) { long long start; struct xattr_list *xattr_list = NULL; unsigned int offset; void *xptr; int j = 0, res = 1; TRACE("get_xattr\n"); *count = xattr_ids[i].count; start = SQUASHFS_XATTR_BLK(xattr_ids[i].xattr) + xattr_table_start; offset = SQUASHFS_XATTR_OFFSET(xattr_ids[i].xattr); xptr = xattrs + get_xattr_block(start) + offset; TRACE("get_xattr: xattr_id %d, count %d, start %lld, offset %d\n", i, *count, start, offset); while (j < *count) { struct squashfs_xattr_entry entry; struct squashfs_xattr_val val; if (res != 0) { xattr_list = realloc(xattr_list, (j + 1) * sizeof(struct xattr_list)); if (xattr_list == NULL) MEM_ERROR(); } SQUASHFS_SWAP_XATTR_ENTRY(xptr, &entry); xptr += sizeof(entry); res = read_xattr_entry(&xattr_list[j], &entry, xptr); if (ignore && res == 0) { /* unknown prefix, but ignore flag is set */ (*count)--; continue; } if (res != 1) goto failed; xptr += entry.size; TRACE("get_xattr: xattr %d, type %d, size %d, name %s\n", j, entry.type, entry.size, xattr_list[j].full_name); if (entry.type & SQUASHFS_XATTR_VALUE_OOL) { long long xattr; void *ool_xptr; xptr += sizeof(val); SQUASHFS_SWAP_LONG_LONGS(xptr, &xattr, 1); xptr += sizeof(xattr); start = SQUASHFS_XATTR_BLK(xattr) + xattr_table_start; offset = SQUASHFS_XATTR_OFFSET(xattr); ool_xptr = xattrs + get_xattr_block(start) + offset; SQUASHFS_SWAP_XATTR_VAL(ool_xptr, &val); xattr_list[j].value = ool_xptr + sizeof(val); } else { SQUASHFS_SWAP_XATTR_VAL(xptr, &val); xattr_list[j].value = xptr + sizeof(val); xptr += sizeof(val) + val.vsize; } TRACE("get_xattr: xattr %d, vsize %d\n", j, val.vsize); xattr_list[j++].vsize = val.vsize; } if (*count == 0) goto failed; return xattr_list; failed: free_xattr(xattr_list, j); return NULL; }
int generate_xattrs(int xattrs, struct xattr_list *xattr_list) { int total_size, i; int xattr_value_max; void *xp; long long xattr_disk; struct dupl_id *xattr_dupl; /* * check if the file xattrs are a complete duplicate of a pre-existing * id */ xattr_dupl = check_id_dupl(xattr_list, xattrs); if (xattr_dupl == NULL) return SQUASHFS_INVALID_XATTR; if(xattr_dupl->xattr_id != SQUASHFS_INVALID_XATTR) return xattr_dupl->xattr_id; /* * Scan the xattr_list deciding which type to assign to each * xattr. The choice is fairly straightforward, and depends on the * size of each xattr name/value and the overall size of the * resultant xattr list stored in the xattr metadata table. * * Choices are whether to store data inline or out of line. * * The overall goal is to optimise xattr scanning and lookup, and * to enable the file system layout to scale from a couple of * small xattr name/values to a large number of large xattr * names/values without affecting performance. While hopefully * enabling the common case of a couple of small xattr name/values * to be stored efficiently * * Code repeatedly scans, doing the following * move xattr data out of line if it exceeds * xattr_value_max. Where xattr_value_max is * initially XATTR_INLINE_MAX. If the final uncompressed * xattr list is larger than XATTR_TARGET_MAX then more * aggressively move xattr data out of line by repeatedly * setting inline threshold to 1/2, then 1/4, 1/8 of * XATTR_INLINE_MAX until target achieved or there's * nothing left to move out of line */ xattr_value_max = XATTR_INLINE_MAX; while(1) { for(total_size = 0, i = 0; i < xattrs; i++) { struct xattr_list *xattr = &xattr_list[i]; xattr->type &= XATTR_PREFIX_MASK; /* all inline */ if (xattr->vsize > xattr_value_max) xattr->type |= XATTR_VALUE_OOL; total_size += get_xattr_size(xattr); } /* * If the total size of the uncompressed xattr list is <= * XATTR_TARGET_MAX we're done */ if(total_size <= XATTR_TARGET_MAX) break; if(xattr_value_max == XATTR_VALUE_OOL_SIZE) break; /* * Inline target not yet at minimum and so reduce it, and * try again */ xattr_value_max /= 2; if(xattr_value_max < XATTR_VALUE_OOL_SIZE) xattr_value_max = XATTR_VALUE_OOL_SIZE; } /* * Check xattr values for duplicates */ for(i = 0; i < xattrs; i++) { check_value_dupl(&xattr_list[i]); } /* * Add each out of line value to the file system xattr table * if it doesn't already exist as a duplicate */ for(i = 0; i < xattrs; i++) { struct xattr_list *xattr = &xattr_list[i]; if((xattr->type & XATTR_VALUE_OOL) && (xattr->ool_value == SQUASHFS_INVALID_BLK)) { struct squashfs_xattr_val val; int size = sizeof(val) + xattr->vsize; xp = get_xattr_space(size, &xattr->ool_value); val.vsize = xattr->vsize; SQUASHFS_SWAP_XATTR_VAL(&val, xp); memcpy(xp + sizeof(val), xattr->value, xattr->vsize); } } /* * Create xattr list and add to file system xattr table */ get_xattr_space(0, &xattr_disk); for(i = 0; i < xattrs; i++) { struct xattr_list *xattr = &xattr_list[i]; struct squashfs_xattr_entry entry; struct squashfs_xattr_val val; xp = get_xattr_space(sizeof(entry) + xattr->size, NULL); entry.type = xattr->type; entry.size = xattr->size; SQUASHFS_SWAP_XATTR_ENTRY(&entry, xp); memcpy(xp + sizeof(entry), xattr->name, xattr->size); if(xattr->type & XATTR_VALUE_OOL) { int size = sizeof(val) + XATTR_VALUE_OOL_SIZE; xp = get_xattr_space(size, NULL); val.vsize = XATTR_VALUE_OOL_SIZE; SQUASHFS_SWAP_XATTR_VAL(&val, xp); SQUASHFS_SWAP_LONG_LONGS(&xattr->ool_value, xp + sizeof(val), 1); } else { int size = sizeof(val) + xattr->vsize; xp = get_xattr_space(size, &xattr->ool_value); val.vsize = xattr->vsize; SQUASHFS_SWAP_XATTR_VAL(&val, xp); memcpy(xp + sizeof(val), xattr->value, xattr->vsize); } } /* * Add to xattr id lookup table */ return get_xattr_id(xattrs, xattr_list, xattr_disk, xattr_dupl); }
/* * Construct and return the list of xattr name:value pairs for the passed xattr * id */ struct xattr_list *get_xattr(int i, unsigned int *count) { long long start; struct xattr_list *xattr_list = NULL; unsigned int offset; void *xptr; int j; TRACE("get_xattr\n"); *count = xattr_ids[i].count; start = SQUASHFS_XATTR_BLK(xattr_ids[i].xattr) + xattr_table_start; offset = SQUASHFS_XATTR_OFFSET(xattr_ids[i].xattr); xptr = xattrs + get_xattr_block(start) + offset; TRACE("get_xattr: xattr_id %d, count %d, start %lld, offset %d\n", i, *count, start, offset); for(j = 0; j < *count; j++) { struct squashfs_xattr_entry entry; struct squashfs_xattr_val val; int res; xattr_list = realloc(xattr_list, (j + 1) * sizeof(struct xattr_list)); if(xattr_list == NULL) { ERROR("Out of memory in get_xattrs\n"); goto failed; } SQUASHFS_SWAP_XATTR_ENTRY(&entry, xptr); xptr += sizeof(entry); res = read_xattr_entry(&xattr_list[j], &entry, xptr); if(res != 1) goto failed; xptr += entry.size; TRACE("get_xattr: xattr %d, type %d, size %d, name %s\n", j, entry.type, entry.size, xattr_list[j].full_name); if(entry.type & SQUASHFS_XATTR_VALUE_OOL) { long long xattr; void *ool_xptr; xptr += sizeof(val); SQUASHFS_SWAP_LONG_LONGS(&xattr, xptr, 1); xptr += sizeof(xattr); start = SQUASHFS_XATTR_BLK(xattr) + xattr_table_start; offset = SQUASHFS_XATTR_OFFSET(xattr); ool_xptr = xattrs + get_xattr_block(start) + offset; SQUASHFS_SWAP_XATTR_VAL(&val, ool_xptr); xattr_list[j].value = ool_xptr + sizeof(val); } else { SQUASHFS_SWAP_XATTR_VAL(&val, xptr); xattr_list[j].value = xptr + sizeof(val); xptr += sizeof(val) + val.vsize; } TRACE("get_xattr: xattr %d, vsize %d\n", j, val.vsize); xattr_list[j].vsize = val.vsize; } return xattr_list; failed: free(xattr_list); return NULL; }