/* 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; }