/* * BKL held by caller. * dentry->d_inode->i_mutex locked */ ssize_t unionfs_listxattr(struct dentry *dentry, char *list, size_t size) { struct dentry *lower_dentry = NULL; struct dentry *parent; int err = -EOPNOTSUPP; char *encoded_list = NULL; bool valid; unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD); parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); valid = __unionfs_d_revalidate(dentry, parent, false); if (unlikely(!valid)) { err = -ESTALE; goto out; } lower_dentry = unionfs_lower_dentry(dentry); encoded_list = list; err = vfs_listxattr(lower_dentry, encoded_list, size); out: unionfs_check_dentry(dentry); unionfs_unlock_dentry(dentry); unionfs_unlock_parent(dentry, parent); unionfs_read_unlock(dentry->d_sb); return err; }
/* * Extended attribute LIST operations */ static ssize_t listxattr(struct dentry *d, char __user *list, size_t size) { ssize_t error; char *klist = NULL; if (size) { if (size > XATTR_LIST_MAX) size = XATTR_LIST_MAX; klist = kmalloc(size, GFP_KERNEL); if (!klist) return -ENOMEM; } error = vfs_listxattr(d, klist, size); if (error > 0) { if (size && copy_to_user(list, klist, error)) error = -EFAULT; } else if (error == -ERANGE && size >= XATTR_LIST_MAX) { /* The file system tried to returned a list bigger than XATTR_LIST_MAX bytes. Not possible. */ error = -E2BIG; } kfree(klist); return error; }
ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size) { ssize_t res; int off; res = vfs_listxattr(ovl_dentry_real(dentry), list, size); if (res <= 0 || size == 0) return res; if (ovl_path_type(dentry->d_parent) != OVL_PATH_MERGE) return res; /* filter out private xattrs */ for (off = 0; off < res;) { char *s = list + off; size_t slen = strlen(s) + 1; BUG_ON(off + slen > res); if (ovl_is_private_xattr(s)) { res -= slen; memmove(s, s + slen, res - off); } else { off += slen; } } return res; }
ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size) { struct path realpath; enum ovl_path_type type = ovl_path_real(dentry, &realpath); ssize_t res; size_t len; char *s; res = vfs_listxattr(realpath.dentry, list, size); if (res <= 0 || size == 0) return res; if (!ovl_need_xattr_filter(dentry, type)) return res; /* filter out private xattrs */ for (s = list, len = res; len;) { size_t slen = strnlen(s, len) + 1; /* underlying fs providing us with an broken xattr list? */ if (WARN_ON(slen > len)) return -EIO; len -= slen; if (ovl_is_private_xattr(s)) { res -= slen; memmove(s, s + slen, len); } else { s += slen; } } return res; }
ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size) { struct dentry *realdentry = ovl_dentry_real(dentry); ssize_t res; size_t len; char *s; const struct cred *old_cred; old_cred = ovl_override_creds(dentry->d_sb); res = vfs_listxattr(realdentry, list, size); revert_creds(old_cred); if (res <= 0 || size == 0) return res; /* filter out private xattrs */ for (s = list, len = res; len;) { size_t slen = strnlen(s, len) + 1; /* underlying fs providing us with an broken xattr list? */ if (WARN_ON(slen > len)) return -EIO; len -= slen; if (!ovl_can_list(s)) { res -= slen; memmove(s, s + slen, len); } else { s += slen; } } return res; }
ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size) { struct path realpath; enum ovl_path_type type = ovl_path_real(dentry, &realpath); ssize_t res; int off; res = vfs_listxattr(realpath.dentry, list, size); if (res <= 0 || size == 0) return res; if (!ovl_need_xattr_filter(dentry, type)) return res; /* filter out private xattrs */ for (off = 0; off < res;) { char *s = list + off; size_t slen = strlen(s) + 1; BUG_ON(off + slen > res); if (ovl_is_private_xattr(s)) { res -= slen; memmove(s, s + slen, res - off); } else { off += slen; } } return res; }
static ssize_t diaryfs_listxattr(struct dentry * dentry, char * buffer, size_t buffer_size) { int err; struct dentry * lower_dentry; struct path lower_path; diaryfs_get_lower_path(dentry, &lower_path); lower_dentry = lower_path.dentry; if (!lower_dentry->d_inode->i_op->listxattr) { err = -EOPNOTSUPP; goto out; } err = vfs_listxattr(lower_dentry, buffer, buffer_size); if (err) goto out; fsstack_copy_attr_atime(dentry->d_inode, lower_path.dentry->d_inode); out: diaryfs_put_lower_path(dentry, &lower_path); return err; }
static ssize_t amfs_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size) { int err; struct dentry *lower_dentry; struct path lower_path; amfs_get_lower_path(dentry, &lower_path); lower_dentry = lower_path.dentry; if (!lower_dentry->d_inode->i_op || !lower_dentry->d_inode->i_op->listxattr) { err = -EINVAL; goto out; } err = vfs_listxattr(lower_dentry, buffer, buffer_size); out: amfs_put_lower_path(dentry, &lower_path); return err; }
/* copyup all extended attrs for a given dentry */ static int copyup_xattrs(struct dentry *old_lower_dentry, struct dentry *new_lower_dentry) { int err = 0; ssize_t list_size = -1; char *name_list = NULL; char *attr_value = NULL; char *name_list_buf = NULL; /* query the actual size of the xattr list */ list_size = vfs_listxattr(old_lower_dentry, NULL, 0); if (list_size <= 0) { err = list_size; goto out; } /* allocate space for the actual list */ name_list = unionfs_xattr_alloc(list_size + 1, XATTR_LIST_MAX); if (unlikely(!name_list || IS_ERR(name_list))) { err = PTR_ERR(name_list); goto out; } name_list_buf = name_list; /* save for kfree at end */ /* now get the actual xattr list of the source file */ list_size = vfs_listxattr(old_lower_dentry, name_list, list_size); if (list_size <= 0) { err = list_size; goto out; } /* allocate space to hold each xattr's value */ attr_value = unionfs_xattr_alloc(XATTR_SIZE_MAX, XATTR_SIZE_MAX); if (unlikely(!attr_value || IS_ERR(attr_value))) { err = PTR_ERR(name_list); goto out; } /* in a loop, get and set each xattr from src to dst file */ while (*name_list) { ssize_t size; /* Lock here since vfs_getxattr doesn't lock for us */ mutex_lock(&old_lower_dentry->d_inode->i_mutex); size = vfs_getxattr(old_lower_dentry, name_list, attr_value, XATTR_SIZE_MAX); mutex_unlock(&old_lower_dentry->d_inode->i_mutex); if (size < 0) { err = size; goto out; } if (size > XATTR_SIZE_MAX) { err = -E2BIG; goto out; } /* Don't lock here since vfs_setxattr does it for us. */ err = vfs_setxattr(new_lower_dentry, name_list, attr_value, size, 0); /* * Selinux depends on "security.*" xattrs, so to maintain * the security of copied-up files, if Selinux is active, * then we must copy these xattrs as well. So we need to * temporarily get FOWNER privileges. * XXX: move entire copyup code to SIOQ. */ if (err == -EPERM && !capable(CAP_FOWNER)) { cap_raise(current->cap_effective, CAP_FOWNER); err = vfs_setxattr(new_lower_dentry, name_list, attr_value, size, 0); cap_lower(current->cap_effective, CAP_FOWNER); } if (err < 0) goto out; name_list += strlen(name_list) + 1; } out: unionfs_xattr_kfree(name_list_buf); unionfs_xattr_kfree(attr_value); /* Ignore if xattr isn't supported */ if (err == -ENOTSUPP || err == -EOPNOTSUPP) err = 0; return err; }
/* copyup all extended attrs for a given dentry */ static int copyup_xattrs(struct dentry *old_hidden_dentry, struct dentry *new_hidden_dentry) { int err = 0; ssize_t list_size = -1; char *name_list = NULL; char *attr_value = NULL; char *name_list_orig = NULL; list_size = vfs_listxattr(old_hidden_dentry, NULL, 0); if (list_size <= 0) { err = list_size; goto out; } name_list = unionfs_xattr_alloc(list_size + 1, XATTR_LIST_MAX); if (!name_list || IS_ERR(name_list)) { err = PTR_ERR(name_list); goto out; } list_size = vfs_listxattr(old_hidden_dentry, name_list, list_size); attr_value = unionfs_xattr_alloc(XATTR_SIZE_MAX, XATTR_SIZE_MAX); if (!attr_value || IS_ERR(attr_value)) { err = PTR_ERR(name_list); goto out; } name_list_orig = name_list; while (*name_list) { ssize_t size; /* Lock here since vfs_getxattr doesn't lock for us */ mutex_lock(&old_hidden_dentry->d_inode->i_mutex); size = vfs_getxattr(old_hidden_dentry, name_list, attr_value, XATTR_SIZE_MAX); mutex_unlock(&old_hidden_dentry->d_inode->i_mutex); if (size < 0) { err = size; goto out; } if (size > XATTR_SIZE_MAX) { err = -E2BIG; goto out; } /* Don't lock here since vfs_setxattr does it for us. */ err = vfs_setxattr(new_hidden_dentry, name_list, attr_value, size, 0); if (err < 0) goto out; name_list += strlen(name_list) + 1; } out: name_list = name_list_orig; if (name_list) unionfs_xattr_free(name_list, list_size + 1); if (attr_value) unionfs_xattr_free(attr_value, XATTR_SIZE_MAX); /* It is no big deal if this fails, we just roll with the punches. */ if (err == -ENOTSUPP || err == -EOPNOTSUPP) err = 0; return err; }