ssize_t hmfs_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size) { struct inode *inode =dentry->d_inode; struct hmfs_xattr_entry *entry; void *xattr_block; int error = 0; size_t size, rest = buffer_size; xattr_block = get_xattr_block(inode); if (!xattr_block) return -ENODATA; list_for_each_xattr(entry, xattr_block) { const struct xattr_handler *handler = hmfs_xattr_handler(entry->e_name_index); if (!handler) continue; size = handler->list(dentry, buffer, rest,entry->e_name, entry->e_name_len, handler->flags); if (buffer && size > rest) { error = -ERANGE; goto out; } if (buffer) buffer += size; rest -= size; } error = buffer_size - rest; out: return error; }
static int __hmfs_getxattr(struct inode *inode, int index, const char *name, void *buffer, size_t buffer_size) { struct hmfs_xattr_entry *entry; void *xattr_block; int error = 0; size_t value_len, name_len; if (name == NULL) return -EINVAL; name_len = strlen(name); if (name_len > HMFS_NAME_LEN) return -ERANGE; xattr_block = get_xattr_block(inode); if (!xattr_block) { return -ENODATA; } entry = __find_xattr(xattr_block, index, name_len, name); if (IS_XATTR_LAST_ENTRY(entry)) { error = -ENODATA; goto out; } value_len = entry->e_value_len; if (buffer && value_len > buffer_size) { error = -ERANGE; goto out; } if (buffer) { memcpy(buffer, entry->e_name + name_len, value_len); } error = value_len; out: return error; }
/* * 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; }
static int __hmfs_setxattr(struct inode *inode, int index, const char *name, const void *value, size_t size, int flags) { struct hmfs_xattr_entry *this, *last, *next; void *base_addr, *new_xattr_blk; int newsize, cpy_size; size_t name_len; int error = -ENOMEM; if (name == NULL) return -EINVAL; if (value == NULL) size = 0; name_len = strlen(name); if (name_len > HMFS_NAME_LEN) return -ERANGE; if (name_len + size > HMFS_XATTR_VALUE_LEN) return -E2BIG; base_addr = get_xattr_block(inode); if (!base_addr) { error = -ENODATA; goto out; } if (!base_addr) { if (flags & XATTR_CREATE) goto create; error = -ENODATA; goto out; } this = __find_xattr(base_addr, index, name_len, name); if (this->e_name_index == HMFS_XATTR_INDEX_END && (flags & XATTR_REPLACE)) { error = -ENODATA; goto out; } else if ((flags & XATTR_CREATE) && this->e_name_index != HMFS_XATTR_INDEX_END) { error = -EEXIST; goto out; } newsize = XATTR_RAW_SIZE + name_len + size; /* Check Space */ if (value) { /* If value is NULL, it's a remove operation */ /* Add another hmfs_xattr_entry for end entry */ last = XATTR_ENTRY(JUMP(this, newsize + XATTR_RAW_SIZE)); if (DISTANCE(base_addr, last) > HMFS_XATTR_BLOCK_SIZE) { error = -ENOSPC; goto out; } } create: /* Allocate new xattr block */ new_xattr_blk = alloc_new_x_block(inode, HMFS_X_BLOCK_TAG_XATTR, false); init_xattr_block(new_xattr_blk); /* Remove old entry in old xattr block */ if (base_addr) { /* Copy first part */ next = XATTR_FIRST_ENTRY(base_addr); cpy_size = DISTANCE(next, this); hmfs_memcpy(XATTR_FIRST_ENTRY(new_xattr_blk), next, cpy_size); /* Get last xattr in source xattr block */ last = this; while (!IS_XATTR_LAST_ENTRY(last)) last = XATTR_NEXT_ENTRY(last); /* Copy second part */ next = XATTR_NEXT_ENTRY(this); cpy_size = DISTANCE(next, last); next = XATTR_ENTRY(JUMP(new_xattr_blk, DISTANCE(base_addr, this))); hmfs_memcpy(next, XATTR_NEXT_ENTRY(this), cpy_size); next = XATTR_ENTRY(JUMP(next, cpy_size)); } else { next = XATTR_FIRST_ENTRY(new_xattr_blk); } /* Write new entry */ if (value) { next->e_name_index = index; next->e_name_len = name_len; next->e_value_len = size; memcpy(next->e_name, name, name_len); memcpy(next->e_name + name_len, value, size); next = XATTR_ENTRY(next->e_name + name_len + size); } /* Write End entry */ next->e_name_index = HMFS_XATTR_INDEX_END; hmfs_bug_on(HMFS_I_SB(inode), DISTANCE(new_xattr_blk, JUMP(next, XATTR_RAW_SIZE)) > HMFS_XATTR_BLOCK_SIZE); inode->i_ctime = CURRENT_TIME; mark_inode_dirty(inode); out: return error; }
/* * 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; }