overlayfs update for 5.10
-----BEGIN PGP SIGNATURE----- iHUEABYIAB0WIQSQHSd0lITzzeNWNm3h3BK/laaZPAUCX4n9mwAKCRDh3BK/laaZ PB7EAP0cxydeomN0m29SpugawMFxzGpB/GnBEr0Qdonz5BJG7wD9EaF9dsLmGbXY Q2P/nbTYmNFC3Kz7xJAZNqmg86AgmQU= =o+01 -----END PGP SIGNATURE----- Merge tag 'ovl-update-5.10' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs Pull overlayfs updates from Miklos Szeredi: - Improve performance for certain container setups by introducing a "volatile" mode - ioctl improvements - continue preparation for unprivileged overlay mounts * tag 'ovl-update-5.10' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs: ovl: use generic vfs_ioc_setflags_prepare() helper ovl: support [S|G]ETFLAGS and FS[S|G]ETXATTR ioctls for directories ovl: rearrange ovl_can_list() ovl: enumerate private xattrs ovl: pass ovl_fs down to functions accessing private xattrs ovl: drop flags argument from ovl_do_setxattr() ovl: adhere to the vfs_ vs. ovl_do_ conventions for xattrs ovl: use ovl_do_getxattr() for private xattr ovl: fold ovl_getxattr() into ovl_get_redirect_xattr() ovl: clean up ovl_getxattr() in copy_up.c duplicate ovl_getxattr() ovl: provide a mount option "volatile" ovl: check for incompatible features in work dir
This commit is contained in:
commit
071a0578b0
|
@ -564,6 +564,25 @@ Note: the mount options index=off,nfs_export=on are conflicting for a
|
|||
read-write mount and will result in an error.
|
||||
|
||||
|
||||
Volatile mount
|
||||
--------------
|
||||
|
||||
This is enabled with the "volatile" mount option. Volatile mounts are not
|
||||
guaranteed to survive a crash. It is strongly recommended that volatile
|
||||
mounts are only used if data written to the overlay can be recreated
|
||||
without significant effort.
|
||||
|
||||
The advantage of mounting with the "volatile" option is that all forms of
|
||||
sync calls to the upper filesystem are omitted.
|
||||
|
||||
When overlay is mounted with "volatile" option, the directory
|
||||
"$workdir/work/incompat/volatile" is created. During next mount, overlay
|
||||
checks for this directory and refuses to mount if present. This is a strong
|
||||
indicator that user should throw away upper and work directories and create
|
||||
fresh one. In very limited cases where the user knows that the system has
|
||||
not crashed and contents of upperdir are intact, The "volatile" directory
|
||||
can be removed.
|
||||
|
||||
Testsuite
|
||||
---------
|
||||
|
||||
|
|
|
@ -43,7 +43,8 @@ static bool ovl_must_copy_xattr(const char *name)
|
|||
!strncmp(name, XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN);
|
||||
}
|
||||
|
||||
int ovl_copy_xattr(struct dentry *old, struct dentry *new)
|
||||
int ovl_copy_xattr(struct super_block *sb, struct dentry *old,
|
||||
struct dentry *new)
|
||||
{
|
||||
ssize_t list_size, size, value_size = 0;
|
||||
char *buf, *name, *value = NULL;
|
||||
|
@ -81,7 +82,7 @@ int ovl_copy_xattr(struct dentry *old, struct dentry *new)
|
|||
}
|
||||
list_size -= slen;
|
||||
|
||||
if (ovl_is_private_xattr(name))
|
||||
if (ovl_is_private_xattr(sb, name))
|
||||
continue;
|
||||
retry:
|
||||
size = vfs_getxattr(old, name, value, value_size);
|
||||
|
@ -128,7 +129,8 @@ out:
|
|||
return error;
|
||||
}
|
||||
|
||||
static int ovl_copy_up_data(struct path *old, struct path *new, loff_t len)
|
||||
static int ovl_copy_up_data(struct ovl_fs *ofs, struct path *old,
|
||||
struct path *new, loff_t len)
|
||||
{
|
||||
struct file *old_file;
|
||||
struct file *new_file;
|
||||
|
@ -218,7 +220,7 @@ static int ovl_copy_up_data(struct path *old, struct path *new, loff_t len)
|
|||
len -= bytes;
|
||||
}
|
||||
out:
|
||||
if (!error)
|
||||
if (!error && ovl_should_sync(ofs))
|
||||
error = vfs_fsync(new_file, 0);
|
||||
fput(new_file);
|
||||
out_fput:
|
||||
|
@ -354,7 +356,8 @@ int ovl_set_origin(struct dentry *dentry, struct dentry *lower,
|
|||
}
|
||||
|
||||
/* Store file handle of @upper dir in @index dir entry */
|
||||
static int ovl_set_upper_fh(struct dentry *upper, struct dentry *index)
|
||||
static int ovl_set_upper_fh(struct ovl_fs *ofs, struct dentry *upper,
|
||||
struct dentry *index)
|
||||
{
|
||||
const struct ovl_fh *fh;
|
||||
int err;
|
||||
|
@ -363,7 +366,7 @@ static int ovl_set_upper_fh(struct dentry *upper, struct dentry *index)
|
|||
if (IS_ERR(fh))
|
||||
return PTR_ERR(fh);
|
||||
|
||||
err = ovl_do_setxattr(index, OVL_XATTR_UPPER, fh->buf, fh->fb.len, 0);
|
||||
err = ovl_do_setxattr(ofs, index, OVL_XATTR_UPPER, fh->buf, fh->fb.len);
|
||||
|
||||
kfree(fh);
|
||||
return err;
|
||||
|
@ -408,7 +411,7 @@ static int ovl_create_index(struct dentry *dentry, struct dentry *origin,
|
|||
if (IS_ERR(temp))
|
||||
goto free_name;
|
||||
|
||||
err = ovl_set_upper_fh(upper, temp);
|
||||
err = ovl_set_upper_fh(OVL_FS(dentry->d_sb), upper, temp);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
|
@ -484,6 +487,7 @@ static int ovl_link_up(struct ovl_copy_up_ctx *c)
|
|||
|
||||
static int ovl_copy_up_inode(struct ovl_copy_up_ctx *c, struct dentry *temp)
|
||||
{
|
||||
struct ovl_fs *ofs = OVL_FS(c->dentry->d_sb);
|
||||
int err;
|
||||
|
||||
/*
|
||||
|
@ -499,12 +503,13 @@ static int ovl_copy_up_inode(struct ovl_copy_up_ctx *c, struct dentry *temp)
|
|||
upperpath.dentry = temp;
|
||||
|
||||
ovl_path_lowerdata(c->dentry, &datapath);
|
||||
err = ovl_copy_up_data(&datapath, &upperpath, c->stat.size);
|
||||
err = ovl_copy_up_data(ofs, &datapath, &upperpath,
|
||||
c->stat.size);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
err = ovl_copy_xattr(c->lowerpath.dentry, temp);
|
||||
err = ovl_copy_xattr(c->dentry->d_sb, c->lowerpath.dentry, temp);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
|
@ -781,9 +786,33 @@ static bool ovl_need_meta_copy_up(struct dentry *dentry, umode_t mode,
|
|||
return true;
|
||||
}
|
||||
|
||||
static ssize_t ovl_getxattr(struct dentry *dentry, char *name, char **value)
|
||||
{
|
||||
ssize_t res;
|
||||
char *buf;
|
||||
|
||||
res = vfs_getxattr(dentry, name, NULL, 0);
|
||||
if (res == -ENODATA || res == -EOPNOTSUPP)
|
||||
res = 0;
|
||||
|
||||
if (res > 0) {
|
||||
buf = kzalloc(res, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
res = vfs_getxattr(dentry, name, buf, res);
|
||||
if (res < 0)
|
||||
kfree(buf);
|
||||
else
|
||||
*value = buf;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Copy up data of an inode which was copied up metadata only in the past. */
|
||||
static int ovl_copy_up_meta_inode_data(struct ovl_copy_up_ctx *c)
|
||||
{
|
||||
struct ovl_fs *ofs = OVL_FS(c->dentry->d_sb);
|
||||
struct path upperpath, datapath;
|
||||
int err;
|
||||
char *capability = NULL;
|
||||
|
@ -799,12 +828,12 @@ static int ovl_copy_up_meta_inode_data(struct ovl_copy_up_ctx *c)
|
|||
|
||||
if (c->stat.size) {
|
||||
err = cap_size = ovl_getxattr(upperpath.dentry, XATTR_NAME_CAPS,
|
||||
&capability, 0);
|
||||
if (err < 0 && err != -ENODATA)
|
||||
&capability);
|
||||
if (cap_size < 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = ovl_copy_up_data(&datapath, &upperpath, c->stat.size);
|
||||
err = ovl_copy_up_data(ofs, &datapath, &upperpath, c->stat.size);
|
||||
if (err)
|
||||
goto out_free;
|
||||
|
||||
|
@ -813,14 +842,14 @@ static int ovl_copy_up_meta_inode_data(struct ovl_copy_up_ctx *c)
|
|||
* don't want that to happen for normal copy-up operation.
|
||||
*/
|
||||
if (capability) {
|
||||
err = ovl_do_setxattr(upperpath.dentry, XATTR_NAME_CAPS,
|
||||
capability, cap_size, 0);
|
||||
err = vfs_setxattr(upperpath.dentry, XATTR_NAME_CAPS,
|
||||
capability, cap_size, 0);
|
||||
if (err)
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
|
||||
err = vfs_removexattr(upperpath.dentry, OVL_XATTR_METACOPY);
|
||||
err = ovl_do_removexattr(ofs, upperpath.dentry, OVL_XATTR_METACOPY);
|
||||
if (err)
|
||||
goto out_free;
|
||||
|
||||
|
|
|
@ -394,7 +394,7 @@ static struct dentry *ovl_clear_empty(struct dentry *dentry,
|
|||
if (IS_ERR(opaquedir))
|
||||
goto out_unlock;
|
||||
|
||||
err = ovl_copy_xattr(upper, opaquedir);
|
||||
err = ovl_copy_xattr(dentry->d_sb, upper, opaquedir);
|
||||
if (err)
|
||||
goto out_cleanup;
|
||||
|
||||
|
|
|
@ -752,7 +752,7 @@ static struct dentry *ovl_lower_fh_to_d(struct super_block *sb,
|
|||
goto out_err;
|
||||
}
|
||||
if (index) {
|
||||
err = ovl_verify_origin(index, origin.dentry, false);
|
||||
err = ovl_verify_origin(ofs, index, origin.dentry, false);
|
||||
if (err)
|
||||
goto out_err;
|
||||
}
|
||||
|
|
|
@ -136,6 +136,13 @@ static int ovl_real_fdget_meta(const struct file *file, struct fd *real,
|
|||
|
||||
static int ovl_real_fdget(const struct file *file, struct fd *real)
|
||||
{
|
||||
if (d_is_dir(file_dentry(file))) {
|
||||
real->flags = 0;
|
||||
real->file = ovl_dir_real_file(file, false);
|
||||
|
||||
return PTR_ERR_OR_ZERO(real->file);
|
||||
}
|
||||
|
||||
return ovl_real_fdget_meta(file, real, false);
|
||||
}
|
||||
|
||||
|
@ -331,6 +338,7 @@ static ssize_t ovl_write_iter(struct kiocb *iocb, struct iov_iter *iter)
|
|||
struct fd real;
|
||||
const struct cred *old_cred;
|
||||
ssize_t ret;
|
||||
int ifl = iocb->ki_flags;
|
||||
|
||||
if (!iov_iter_count(iter))
|
||||
return 0;
|
||||
|
@ -346,11 +354,14 @@ static ssize_t ovl_write_iter(struct kiocb *iocb, struct iov_iter *iter)
|
|||
if (ret)
|
||||
goto out_unlock;
|
||||
|
||||
if (!ovl_should_sync(OVL_FS(inode->i_sb)))
|
||||
ifl &= ~(IOCB_DSYNC | IOCB_SYNC);
|
||||
|
||||
old_cred = ovl_override_creds(file_inode(file)->i_sb);
|
||||
if (is_sync_kiocb(iocb)) {
|
||||
file_start_write(real.file);
|
||||
ret = vfs_iter_write(real.file, iter, &iocb->ki_pos,
|
||||
ovl_iocb_to_rwf(iocb->ki_flags));
|
||||
ovl_iocb_to_rwf(ifl));
|
||||
file_end_write(real.file);
|
||||
/* Update size */
|
||||
ovl_copyattr(ovl_inode_real(inode), inode);
|
||||
|
@ -370,6 +381,7 @@ static ssize_t ovl_write_iter(struct kiocb *iocb, struct iov_iter *iter)
|
|||
real.flags = 0;
|
||||
aio_req->orig_iocb = iocb;
|
||||
kiocb_clone(&aio_req->iocb, iocb, real.file);
|
||||
aio_req->iocb.ki_flags = ifl;
|
||||
aio_req->iocb.ki_complete = ovl_aio_rw_complete;
|
||||
ret = vfs_iocb_iter_write(real.file, &aio_req->iocb, iter);
|
||||
if (ret != -EIOCBQUEUED)
|
||||
|
@ -433,6 +445,9 @@ static int ovl_fsync(struct file *file, loff_t start, loff_t end, int datasync)
|
|||
const struct cred *old_cred;
|
||||
int ret;
|
||||
|
||||
if (!ovl_should_sync(OVL_FS(file_inode(file)->i_sb)))
|
||||
return 0;
|
||||
|
||||
ret = ovl_real_fdget_meta(file, &real, !datasync);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -544,12 +559,28 @@ static long ovl_real_ioctl(struct file *file, unsigned int cmd,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static unsigned int ovl_iflags_to_fsflags(unsigned int iflags)
|
||||
{
|
||||
unsigned int flags = 0;
|
||||
|
||||
if (iflags & S_SYNC)
|
||||
flags |= FS_SYNC_FL;
|
||||
if (iflags & S_APPEND)
|
||||
flags |= FS_APPEND_FL;
|
||||
if (iflags & S_IMMUTABLE)
|
||||
flags |= FS_IMMUTABLE_FL;
|
||||
if (iflags & S_NOATIME)
|
||||
flags |= FS_NOATIME_FL;
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
static long ovl_ioctl_set_flags(struct file *file, unsigned int cmd,
|
||||
unsigned long arg, unsigned int iflags)
|
||||
unsigned long arg, unsigned int flags)
|
||||
{
|
||||
long ret;
|
||||
struct inode *inode = file_inode(file);
|
||||
unsigned int old_iflags;
|
||||
unsigned int oldflags;
|
||||
|
||||
if (!inode_owner_or_capable(inode))
|
||||
return -EACCES;
|
||||
|
@ -561,10 +592,9 @@ static long ovl_ioctl_set_flags(struct file *file, unsigned int cmd,
|
|||
inode_lock(inode);
|
||||
|
||||
/* Check the capability before cred override */
|
||||
ret = -EPERM;
|
||||
old_iflags = READ_ONCE(inode->i_flags);
|
||||
if (((iflags ^ old_iflags) & (S_APPEND | S_IMMUTABLE)) &&
|
||||
!capable(CAP_LINUX_IMMUTABLE))
|
||||
oldflags = ovl_iflags_to_fsflags(READ_ONCE(inode->i_flags));
|
||||
ret = vfs_ioc_setflags_prepare(inode, oldflags, flags);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
|
||||
ret = ovl_maybe_copy_up(file_dentry(file), O_WRONLY);
|
||||
|
@ -583,22 +613,6 @@ unlock:
|
|||
|
||||
}
|
||||
|
||||
static unsigned int ovl_fsflags_to_iflags(unsigned int flags)
|
||||
{
|
||||
unsigned int iflags = 0;
|
||||
|
||||
if (flags & FS_SYNC_FL)
|
||||
iflags |= S_SYNC;
|
||||
if (flags & FS_APPEND_FL)
|
||||
iflags |= S_APPEND;
|
||||
if (flags & FS_IMMUTABLE_FL)
|
||||
iflags |= S_IMMUTABLE;
|
||||
if (flags & FS_NOATIME_FL)
|
||||
iflags |= S_NOATIME;
|
||||
|
||||
return iflags;
|
||||
}
|
||||
|
||||
static long ovl_ioctl_set_fsflags(struct file *file, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
|
@ -607,24 +621,23 @@ static long ovl_ioctl_set_fsflags(struct file *file, unsigned int cmd,
|
|||
if (get_user(flags, (int __user *) arg))
|
||||
return -EFAULT;
|
||||
|
||||
return ovl_ioctl_set_flags(file, cmd, arg,
|
||||
ovl_fsflags_to_iflags(flags));
|
||||
return ovl_ioctl_set_flags(file, cmd, arg, flags);
|
||||
}
|
||||
|
||||
static unsigned int ovl_fsxflags_to_iflags(unsigned int xflags)
|
||||
static unsigned int ovl_fsxflags_to_fsflags(unsigned int xflags)
|
||||
{
|
||||
unsigned int iflags = 0;
|
||||
unsigned int flags = 0;
|
||||
|
||||
if (xflags & FS_XFLAG_SYNC)
|
||||
iflags |= S_SYNC;
|
||||
flags |= FS_SYNC_FL;
|
||||
if (xflags & FS_XFLAG_APPEND)
|
||||
iflags |= S_APPEND;
|
||||
flags |= FS_APPEND_FL;
|
||||
if (xflags & FS_XFLAG_IMMUTABLE)
|
||||
iflags |= S_IMMUTABLE;
|
||||
flags |= FS_IMMUTABLE_FL;
|
||||
if (xflags & FS_XFLAG_NOATIME)
|
||||
iflags |= S_NOATIME;
|
||||
flags |= FS_NOATIME_FL;
|
||||
|
||||
return iflags;
|
||||
return flags;
|
||||
}
|
||||
|
||||
static long ovl_ioctl_set_fsxflags(struct file *file, unsigned int cmd,
|
||||
|
@ -637,10 +650,10 @@ static long ovl_ioctl_set_fsxflags(struct file *file, unsigned int cmd,
|
|||
return -EFAULT;
|
||||
|
||||
return ovl_ioctl_set_flags(file, cmd, arg,
|
||||
ovl_fsxflags_to_iflags(fa.fsx_xflags));
|
||||
ovl_fsxflags_to_fsflags(fa.fsx_xflags));
|
||||
}
|
||||
|
||||
static long ovl_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
long ovl_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
long ret;
|
||||
|
||||
|
@ -665,8 +678,8 @@ static long ovl_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static long ovl_compat_ioctl(struct file *file, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
#ifdef CONFIG_COMPAT
|
||||
long ovl_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
switch (cmd) {
|
||||
case FS_IOC32_GETFLAGS:
|
||||
|
@ -683,6 +696,7 @@ static long ovl_compat_ioctl(struct file *file, unsigned int cmd,
|
|||
|
||||
return ovl_ioctl(file, cmd, arg);
|
||||
}
|
||||
#endif
|
||||
|
||||
enum ovl_copyop {
|
||||
OVL_COPY,
|
||||
|
@ -784,7 +798,9 @@ const struct file_operations ovl_file_operations = {
|
|||
.fallocate = ovl_fallocate,
|
||||
.fadvise = ovl_fadvise,
|
||||
.unlocked_ioctl = ovl_ioctl,
|
||||
#ifdef CONFIG_COMPAT
|
||||
.compat_ioctl = ovl_compat_ioctl,
|
||||
#endif
|
||||
.splice_read = ovl_splice_read,
|
||||
.splice_write = ovl_splice_write,
|
||||
|
||||
|
|
|
@ -327,7 +327,7 @@ static const char *ovl_get_link(struct dentry *dentry,
|
|||
return p;
|
||||
}
|
||||
|
||||
bool ovl_is_private_xattr(const char *name)
|
||||
bool ovl_is_private_xattr(struct super_block *sb, const char *name)
|
||||
{
|
||||
return strncmp(name, OVL_XATTR_PREFIX,
|
||||
sizeof(OVL_XATTR_PREFIX) - 1) == 0;
|
||||
|
@ -391,15 +391,18 @@ int ovl_xattr_get(struct dentry *dentry, struct inode *inode, const char *name,
|
|||
return res;
|
||||
}
|
||||
|
||||
static bool ovl_can_list(const char *s)
|
||||
static bool ovl_can_list(struct super_block *sb, const char *s)
|
||||
{
|
||||
/* Never list private (.overlay) */
|
||||
if (ovl_is_private_xattr(sb, s))
|
||||
return false;
|
||||
|
||||
/* List all non-trusted xatts */
|
||||
if (strncmp(s, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN) != 0)
|
||||
return true;
|
||||
|
||||
/* Never list trusted.overlay, list other trusted for superuser only */
|
||||
return !ovl_is_private_xattr(s) &&
|
||||
ns_capable_noaudit(&init_user_ns, CAP_SYS_ADMIN);
|
||||
/* list other trusted for superuser only */
|
||||
return ns_capable_noaudit(&init_user_ns, CAP_SYS_ADMIN);
|
||||
}
|
||||
|
||||
ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size)
|
||||
|
@ -425,7 +428,7 @@ ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size)
|
|||
return -EIO;
|
||||
|
||||
len -= slen;
|
||||
if (!ovl_can_list(s)) {
|
||||
if (!ovl_can_list(dentry->d_sb, s)) {
|
||||
res -= slen;
|
||||
memmove(s, s + slen, len);
|
||||
} else {
|
||||
|
@ -722,8 +725,8 @@ static int ovl_set_nlink_common(struct dentry *dentry,
|
|||
if (WARN_ON(len >= sizeof(buf)))
|
||||
return -EIO;
|
||||
|
||||
return ovl_do_setxattr(ovl_dentry_upper(dentry),
|
||||
OVL_XATTR_NLINK, buf, len, 0);
|
||||
return ovl_do_setxattr(OVL_FS(inode->i_sb), ovl_dentry_upper(dentry),
|
||||
OVL_XATTR_NLINK, buf, len);
|
||||
}
|
||||
|
||||
int ovl_set_nlink_upper(struct dentry *dentry)
|
||||
|
@ -736,7 +739,7 @@ int ovl_set_nlink_lower(struct dentry *dentry)
|
|||
return ovl_set_nlink_common(dentry, ovl_dentry_lower(dentry), "L%+i");
|
||||
}
|
||||
|
||||
unsigned int ovl_get_nlink(struct dentry *lowerdentry,
|
||||
unsigned int ovl_get_nlink(struct ovl_fs *ofs, struct dentry *lowerdentry,
|
||||
struct dentry *upperdentry,
|
||||
unsigned int fallback)
|
||||
{
|
||||
|
@ -748,7 +751,8 @@ unsigned int ovl_get_nlink(struct dentry *lowerdentry,
|
|||
if (!lowerdentry || !upperdentry || d_inode(lowerdentry)->i_nlink == 1)
|
||||
return fallback;
|
||||
|
||||
err = vfs_getxattr(upperdentry, OVL_XATTR_NLINK, &buf, sizeof(buf) - 1);
|
||||
err = ovl_do_getxattr(ofs, upperdentry, OVL_XATTR_NLINK,
|
||||
&buf, sizeof(buf) - 1);
|
||||
if (err < 0)
|
||||
goto fail;
|
||||
|
||||
|
@ -946,6 +950,7 @@ static struct inode *ovl_iget5(struct super_block *sb, struct inode *newinode,
|
|||
struct inode *ovl_get_inode(struct super_block *sb,
|
||||
struct ovl_inode_params *oip)
|
||||
{
|
||||
struct ovl_fs *ofs = OVL_FS(sb);
|
||||
struct dentry *upperdentry = oip->upperdentry;
|
||||
struct ovl_path *lowerpath = oip->lowerpath;
|
||||
struct inode *realinode = upperdentry ? d_inode(upperdentry) : NULL;
|
||||
|
@ -993,7 +998,8 @@ struct inode *ovl_get_inode(struct super_block *sb,
|
|||
|
||||
/* Recalculate nlink for non-dir due to indexing */
|
||||
if (!is_dir)
|
||||
nlink = ovl_get_nlink(lowerdentry, upperdentry, nlink);
|
||||
nlink = ovl_get_nlink(ofs, lowerdentry, upperdentry,
|
||||
nlink);
|
||||
set_nlink(inode, nlink);
|
||||
ino = key->i_ino;
|
||||
} else {
|
||||
|
@ -1009,7 +1015,7 @@ struct inode *ovl_get_inode(struct super_block *sb,
|
|||
ovl_fill_inode(inode, realinode->i_mode, realinode->i_rdev);
|
||||
ovl_inode_init(inode, oip, ino, fsid);
|
||||
|
||||
if (upperdentry && ovl_is_impuredir(upperdentry))
|
||||
if (upperdentry && ovl_is_impuredir(sb, upperdentry))
|
||||
ovl_set_flag(OVL_IMPURE, inode);
|
||||
|
||||
if (oip->index)
|
||||
|
@ -1023,7 +1029,7 @@ struct inode *ovl_get_inode(struct super_block *sb,
|
|||
/* Check for non-merge dir that may have whiteouts */
|
||||
if (is_dir) {
|
||||
if (((upperdentry && lowerdentry) || oip->numlower > 1) ||
|
||||
ovl_check_origin_xattr(upperdentry ?: lowerdentry)) {
|
||||
ovl_check_origin_xattr(ofs, upperdentry ?: lowerdentry)) {
|
||||
ovl_set_flag(OVL_WHITEOUTS, inode);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,8 +30,9 @@ static int ovl_check_redirect(struct dentry *dentry, struct ovl_lookup_data *d,
|
|||
{
|
||||
int res;
|
||||
char *buf;
|
||||
struct ovl_fs *ofs = OVL_FS(d->sb);
|
||||
|
||||
buf = ovl_get_redirect_xattr(dentry, prelen + strlen(post));
|
||||
buf = ovl_get_redirect_xattr(ofs, dentry, prelen + strlen(post));
|
||||
if (IS_ERR_OR_NULL(buf))
|
||||
return PTR_ERR(buf);
|
||||
|
||||
|
@ -104,12 +105,13 @@ int ovl_check_fb_len(struct ovl_fb *fb, int fb_len)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct ovl_fh *ovl_get_fh(struct dentry *dentry, const char *name)
|
||||
static struct ovl_fh *ovl_get_fh(struct ovl_fs *ofs, struct dentry *dentry,
|
||||
enum ovl_xattr ox)
|
||||
{
|
||||
int res, err;
|
||||
struct ovl_fh *fh = NULL;
|
||||
|
||||
res = vfs_getxattr(dentry, name, NULL, 0);
|
||||
res = ovl_do_getxattr(ofs, dentry, ox, NULL, 0);
|
||||
if (res < 0) {
|
||||
if (res == -ENODATA || res == -EOPNOTSUPP)
|
||||
return NULL;
|
||||
|
@ -123,7 +125,7 @@ static struct ovl_fh *ovl_get_fh(struct dentry *dentry, const char *name)
|
|||
if (!fh)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
res = vfs_getxattr(dentry, name, fh->buf, res);
|
||||
res = ovl_do_getxattr(ofs, dentry, ox, fh->buf, res);
|
||||
if (res < 0)
|
||||
goto fail;
|
||||
|
||||
|
@ -186,9 +188,9 @@ struct dentry *ovl_decode_real_fh(struct ovl_fh *fh, struct vfsmount *mnt,
|
|||
return real;
|
||||
}
|
||||
|
||||
static bool ovl_is_opaquedir(struct dentry *dentry)
|
||||
static bool ovl_is_opaquedir(struct super_block *sb, struct dentry *dentry)
|
||||
{
|
||||
return ovl_check_dir_xattr(dentry, OVL_XATTR_OPAQUE);
|
||||
return ovl_check_dir_xattr(sb, dentry, OVL_XATTR_OPAQUE);
|
||||
}
|
||||
|
||||
static struct dentry *ovl_lookup_positive_unlocked(const char *name,
|
||||
|
@ -251,7 +253,7 @@ static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d,
|
|||
d->stop = true;
|
||||
goto put_and_out;
|
||||
}
|
||||
err = ovl_check_metacopy_xattr(this);
|
||||
err = ovl_check_metacopy_xattr(OVL_FS(d->sb), this);
|
||||
if (err < 0)
|
||||
goto out_err;
|
||||
|
||||
|
@ -271,7 +273,7 @@ static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d,
|
|||
if (d->last)
|
||||
goto out;
|
||||
|
||||
if (ovl_is_opaquedir(this)) {
|
||||
if (ovl_is_opaquedir(d->sb, this)) {
|
||||
d->stop = true;
|
||||
if (last_element)
|
||||
d->opaque = true;
|
||||
|
@ -391,7 +393,7 @@ invalid:
|
|||
static int ovl_check_origin(struct ovl_fs *ofs, struct dentry *upperdentry,
|
||||
struct ovl_path **stackp)
|
||||
{
|
||||
struct ovl_fh *fh = ovl_get_fh(upperdentry, OVL_XATTR_ORIGIN);
|
||||
struct ovl_fh *fh = ovl_get_fh(ofs, upperdentry, OVL_XATTR_ORIGIN);
|
||||
int err;
|
||||
|
||||
if (IS_ERR_OR_NULL(fh))
|
||||
|
@ -413,10 +415,10 @@ static int ovl_check_origin(struct ovl_fs *ofs, struct dentry *upperdentry,
|
|||
* Verify that @fh matches the file handle stored in xattr @name.
|
||||
* Return 0 on match, -ESTALE on mismatch, < 0 on error.
|
||||
*/
|
||||
static int ovl_verify_fh(struct dentry *dentry, const char *name,
|
||||
const struct ovl_fh *fh)
|
||||
static int ovl_verify_fh(struct ovl_fs *ofs, struct dentry *dentry,
|
||||
enum ovl_xattr ox, const struct ovl_fh *fh)
|
||||
{
|
||||
struct ovl_fh *ofh = ovl_get_fh(dentry, name);
|
||||
struct ovl_fh *ofh = ovl_get_fh(ofs, dentry, ox);
|
||||
int err = 0;
|
||||
|
||||
if (!ofh)
|
||||
|
@ -440,8 +442,9 @@ static int ovl_verify_fh(struct dentry *dentry, const char *name,
|
|||
*
|
||||
* Return 0 on match, -ESTALE on mismatch, -ENODATA on no xattr, < 0 on error.
|
||||
*/
|
||||
int ovl_verify_set_fh(struct dentry *dentry, const char *name,
|
||||
struct dentry *real, bool is_upper, bool set)
|
||||
int ovl_verify_set_fh(struct ovl_fs *ofs, struct dentry *dentry,
|
||||
enum ovl_xattr ox, struct dentry *real, bool is_upper,
|
||||
bool set)
|
||||
{
|
||||
struct inode *inode;
|
||||
struct ovl_fh *fh;
|
||||
|
@ -454,9 +457,9 @@ int ovl_verify_set_fh(struct dentry *dentry, const char *name,
|
|||
goto fail;
|
||||
}
|
||||
|
||||
err = ovl_verify_fh(dentry, name, fh);
|
||||
err = ovl_verify_fh(ofs, dentry, ox, fh);
|
||||
if (set && err == -ENODATA)
|
||||
err = ovl_do_setxattr(dentry, name, fh->buf, fh->fb.len, 0);
|
||||
err = ovl_do_setxattr(ofs, dentry, ox, fh->buf, fh->fb.len);
|
||||
if (err)
|
||||
goto fail;
|
||||
|
||||
|
@ -481,7 +484,7 @@ struct dentry *ovl_index_upper(struct ovl_fs *ofs, struct dentry *index)
|
|||
if (!d_is_dir(index))
|
||||
return dget(index);
|
||||
|
||||
fh = ovl_get_fh(index, OVL_XATTR_UPPER);
|
||||
fh = ovl_get_fh(ofs, index, OVL_XATTR_UPPER);
|
||||
if (IS_ERR_OR_NULL(fh))
|
||||
return ERR_CAST(fh);
|
||||
|
||||
|
@ -574,7 +577,7 @@ int ovl_verify_index(struct ovl_fs *ofs, struct dentry *index)
|
|||
goto fail;
|
||||
}
|
||||
|
||||
err = ovl_verify_fh(upper, OVL_XATTR_ORIGIN, fh);
|
||||
err = ovl_verify_fh(ofs, upper, OVL_XATTR_ORIGIN, fh);
|
||||
dput(upper);
|
||||
if (err)
|
||||
goto fail;
|
||||
|
@ -585,7 +588,7 @@ int ovl_verify_index(struct ovl_fs *ofs, struct dentry *index)
|
|||
if (err)
|
||||
goto fail;
|
||||
|
||||
if (ovl_get_nlink(origin.dentry, index, 0) == 0)
|
||||
if (ovl_get_nlink(ofs, origin.dentry, index, 0) == 0)
|
||||
goto orphan;
|
||||
}
|
||||
|
||||
|
@ -741,7 +744,7 @@ struct dentry *ovl_lookup_index(struct ovl_fs *ofs, struct dentry *upper,
|
|||
}
|
||||
|
||||
/* Verify that dir index 'upper' xattr points to upper dir */
|
||||
err = ovl_verify_upper(index, upper, false);
|
||||
err = ovl_verify_upper(ofs, index, upper, false);
|
||||
if (err) {
|
||||
if (err == -ESTALE) {
|
||||
pr_warn_ratelimited("suspected multiply redirected dir found (upper=%pd2, origin=%pd2, index=%pd2).\n",
|
||||
|
@ -790,12 +793,12 @@ int ovl_path_next(int idx, struct dentry *dentry, struct path *path)
|
|||
}
|
||||
|
||||
/* Fix missing 'origin' xattr */
|
||||
static int ovl_fix_origin(struct dentry *dentry, struct dentry *lower,
|
||||
struct dentry *upper)
|
||||
static int ovl_fix_origin(struct ovl_fs *ofs, struct dentry *dentry,
|
||||
struct dentry *lower, struct dentry *upper)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (ovl_check_origin_xattr(upper))
|
||||
if (ovl_check_origin_xattr(ofs, upper))
|
||||
return 0;
|
||||
|
||||
err = ovl_want_write(dentry);
|
||||
|
@ -920,7 +923,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
|
|||
* of lower dir and set upper parent "impure".
|
||||
*/
|
||||
if (upperdentry && !ctr && !ofs->noxattr && d.is_dir) {
|
||||
err = ovl_fix_origin(dentry, this, upperdentry);
|
||||
err = ovl_fix_origin(ofs, dentry, this, upperdentry);
|
||||
if (err) {
|
||||
dput(this);
|
||||
goto out_put;
|
||||
|
@ -939,7 +942,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
|
|||
if (upperdentry && !ctr &&
|
||||
((d.is_dir && ovl_verify_lower(dentry->d_sb)) ||
|
||||
(!d.is_dir && ofs->config.index && origin_path))) {
|
||||
err = ovl_verify_origin(upperdentry, this, false);
|
||||
err = ovl_verify_origin(ofs, upperdentry, this, false);
|
||||
if (err) {
|
||||
dput(this);
|
||||
if (d.is_dir)
|
||||
|
@ -1060,13 +1063,13 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
|
|||
ovl_dentry_set_upper_alias(dentry);
|
||||
else if (index) {
|
||||
upperdentry = dget(index);
|
||||
upperredirect = ovl_get_redirect_xattr(upperdentry, 0);
|
||||
upperredirect = ovl_get_redirect_xattr(ofs, upperdentry, 0);
|
||||
if (IS_ERR(upperredirect)) {
|
||||
err = PTR_ERR(upperredirect);
|
||||
upperredirect = NULL;
|
||||
goto out_free_oe;
|
||||
}
|
||||
err = ovl_check_metacopy_xattr(upperdentry);
|
||||
err = ovl_check_metacopy_xattr(ofs, upperdentry);
|
||||
if (err < 0)
|
||||
goto out_free_oe;
|
||||
uppermetacopy = err;
|
||||
|
|
|
@ -23,13 +23,16 @@ enum ovl_path_type {
|
|||
#define OVL_TYPE_ORIGIN(type) ((type) & __OVL_PATH_ORIGIN)
|
||||
|
||||
#define OVL_XATTR_PREFIX XATTR_TRUSTED_PREFIX "overlay."
|
||||
#define OVL_XATTR_OPAQUE OVL_XATTR_PREFIX "opaque"
|
||||
#define OVL_XATTR_REDIRECT OVL_XATTR_PREFIX "redirect"
|
||||
#define OVL_XATTR_ORIGIN OVL_XATTR_PREFIX "origin"
|
||||
#define OVL_XATTR_IMPURE OVL_XATTR_PREFIX "impure"
|
||||
#define OVL_XATTR_NLINK OVL_XATTR_PREFIX "nlink"
|
||||
#define OVL_XATTR_UPPER OVL_XATTR_PREFIX "upper"
|
||||
#define OVL_XATTR_METACOPY OVL_XATTR_PREFIX "metacopy"
|
||||
|
||||
enum ovl_xattr {
|
||||
OVL_XATTR_OPAQUE,
|
||||
OVL_XATTR_REDIRECT,
|
||||
OVL_XATTR_ORIGIN,
|
||||
OVL_XATTR_IMPURE,
|
||||
OVL_XATTR_NLINK,
|
||||
OVL_XATTR_UPPER,
|
||||
OVL_XATTR_METACOPY,
|
||||
};
|
||||
|
||||
enum ovl_inode_flag {
|
||||
/* Pure upper dir that may contain non pure upper entries */
|
||||
|
@ -110,6 +113,12 @@ struct ovl_fh {
|
|||
#define OVL_FH_FID_OFFSET (OVL_FH_WIRE_OFFSET + \
|
||||
offsetof(struct ovl_fb, fid))
|
||||
|
||||
extern const char *ovl_xattr_table[];
|
||||
static inline const char *ovl_xattr(struct ovl_fs *ofs, enum ovl_xattr ox)
|
||||
{
|
||||
return ovl_xattr_table[ox];
|
||||
}
|
||||
|
||||
static inline int ovl_do_rmdir(struct inode *dir, struct dentry *dentry)
|
||||
{
|
||||
int err = vfs_rmdir(dir, dentry);
|
||||
|
@ -170,17 +179,29 @@ static inline int ovl_do_symlink(struct inode *dir, struct dentry *dentry,
|
|||
return err;
|
||||
}
|
||||
|
||||
static inline int ovl_do_setxattr(struct dentry *dentry, const char *name,
|
||||
const void *value, size_t size, int flags)
|
||||
static inline ssize_t ovl_do_getxattr(struct ovl_fs *ofs, struct dentry *dentry,
|
||||
enum ovl_xattr ox, void *value,
|
||||
size_t size)
|
||||
{
|
||||
int err = vfs_setxattr(dentry, name, value, size, flags);
|
||||
pr_debug("setxattr(%pd2, \"%s\", \"%*pE\", %zu, 0x%x) = %i\n",
|
||||
dentry, name, min((int)size, 48), value, size, flags, err);
|
||||
const char *name = ovl_xattr(ofs, ox);
|
||||
return vfs_getxattr(dentry, name, value, size);
|
||||
}
|
||||
|
||||
static inline int ovl_do_setxattr(struct ovl_fs *ofs, struct dentry *dentry,
|
||||
enum ovl_xattr ox, const void *value,
|
||||
size_t size)
|
||||
{
|
||||
const char *name = ovl_xattr(ofs, ox);
|
||||
int err = vfs_setxattr(dentry, name, value, size, 0);
|
||||
pr_debug("setxattr(%pd2, \"%s\", \"%*pE\", %zu, 0) = %i\n",
|
||||
dentry, name, min((int)size, 48), value, size, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline int ovl_do_removexattr(struct dentry *dentry, const char *name)
|
||||
static inline int ovl_do_removexattr(struct ovl_fs *ofs, struct dentry *dentry,
|
||||
enum ovl_xattr ox)
|
||||
{
|
||||
const char *name = ovl_xattr(ofs, ox);
|
||||
int err = vfs_removexattr(dentry, name);
|
||||
pr_debug("removexattr(%pd2, \"%s\") = %i\n", dentry, name, err);
|
||||
return err;
|
||||
|
@ -280,10 +301,11 @@ struct file *ovl_path_open(struct path *path, int flags);
|
|||
int ovl_copy_up_start(struct dentry *dentry, int flags);
|
||||
void ovl_copy_up_end(struct dentry *dentry);
|
||||
bool ovl_already_copied_up(struct dentry *dentry, int flags);
|
||||
bool ovl_check_origin_xattr(struct dentry *dentry);
|
||||
bool ovl_check_dir_xattr(struct dentry *dentry, const char *name);
|
||||
bool ovl_check_origin_xattr(struct ovl_fs *ofs, struct dentry *dentry);
|
||||
bool ovl_check_dir_xattr(struct super_block *sb, struct dentry *dentry,
|
||||
enum ovl_xattr ox);
|
||||
int ovl_check_setxattr(struct dentry *dentry, struct dentry *upperdentry,
|
||||
const char *name, const void *value, size_t size,
|
||||
enum ovl_xattr ox, const void *value, size_t size,
|
||||
int xerr);
|
||||
int ovl_set_impure(struct dentry *dentry, struct dentry *upperdentry);
|
||||
void ovl_set_flag(unsigned long flag, struct inode *inode);
|
||||
|
@ -296,15 +318,15 @@ bool ovl_need_index(struct dentry *dentry);
|
|||
int ovl_nlink_start(struct dentry *dentry);
|
||||
void ovl_nlink_end(struct dentry *dentry);
|
||||
int ovl_lock_rename_workdir(struct dentry *workdir, struct dentry *upperdir);
|
||||
int ovl_check_metacopy_xattr(struct dentry *dentry);
|
||||
int ovl_check_metacopy_xattr(struct ovl_fs *ofs, struct dentry *dentry);
|
||||
bool ovl_is_metacopy_dentry(struct dentry *dentry);
|
||||
char *ovl_get_redirect_xattr(struct dentry *dentry, int padding);
|
||||
ssize_t ovl_getxattr(struct dentry *dentry, char *name, char **value,
|
||||
size_t padding);
|
||||
char *ovl_get_redirect_xattr(struct ovl_fs *ofs, struct dentry *dentry,
|
||||
int padding);
|
||||
|
||||
static inline bool ovl_is_impuredir(struct dentry *dentry)
|
||||
static inline bool ovl_is_impuredir(struct super_block *sb,
|
||||
struct dentry *dentry)
|
||||
{
|
||||
return ovl_check_dir_xattr(dentry, OVL_XATTR_IMPURE);
|
||||
return ovl_check_dir_xattr(sb, dentry, OVL_XATTR_IMPURE);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -365,8 +387,9 @@ struct dentry *ovl_decode_real_fh(struct ovl_fh *fh, struct vfsmount *mnt,
|
|||
bool connected);
|
||||
int ovl_check_origin_fh(struct ovl_fs *ofs, struct ovl_fh *fh, bool connected,
|
||||
struct dentry *upperdentry, struct ovl_path **stackp);
|
||||
int ovl_verify_set_fh(struct dentry *dentry, const char *name,
|
||||
struct dentry *real, bool is_upper, bool set);
|
||||
int ovl_verify_set_fh(struct ovl_fs *ofs, struct dentry *dentry,
|
||||
enum ovl_xattr ox, struct dentry *real, bool is_upper,
|
||||
bool set);
|
||||
struct dentry *ovl_index_upper(struct ovl_fs *ofs, struct dentry *index);
|
||||
int ovl_verify_index(struct ovl_fs *ofs, struct dentry *index);
|
||||
int ovl_get_index_name(struct dentry *origin, struct qstr *name);
|
||||
|
@ -378,20 +401,22 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
|
|||
unsigned int flags);
|
||||
bool ovl_lower_positive(struct dentry *dentry);
|
||||
|
||||
static inline int ovl_verify_origin(struct dentry *upper,
|
||||
static inline int ovl_verify_origin(struct ovl_fs *ofs, struct dentry *upper,
|
||||
struct dentry *origin, bool set)
|
||||
{
|
||||
return ovl_verify_set_fh(upper, OVL_XATTR_ORIGIN, origin, false, set);
|
||||
return ovl_verify_set_fh(ofs, upper, OVL_XATTR_ORIGIN, origin,
|
||||
false, set);
|
||||
}
|
||||
|
||||
static inline int ovl_verify_upper(struct dentry *index,
|
||||
struct dentry *upper, bool set)
|
||||
static inline int ovl_verify_upper(struct ovl_fs *ofs, struct dentry *index,
|
||||
struct dentry *upper, bool set)
|
||||
{
|
||||
return ovl_verify_set_fh(index, OVL_XATTR_UPPER, upper, true, set);
|
||||
return ovl_verify_set_fh(ofs, index, OVL_XATTR_UPPER, upper, true, set);
|
||||
}
|
||||
|
||||
/* readdir.c */
|
||||
extern const struct file_operations ovl_dir_operations;
|
||||
struct file *ovl_dir_real_file(const struct file *file, bool want_upper);
|
||||
int ovl_check_empty_dir(struct dentry *dentry, struct list_head *list);
|
||||
void ovl_cleanup_whiteouts(struct dentry *upper, struct list_head *list);
|
||||
void ovl_cache_free(struct list_head *list);
|
||||
|
@ -404,7 +429,7 @@ int ovl_indexdir_cleanup(struct ovl_fs *ofs);
|
|||
/* inode.c */
|
||||
int ovl_set_nlink_upper(struct dentry *dentry);
|
||||
int ovl_set_nlink_lower(struct dentry *dentry);
|
||||
unsigned int ovl_get_nlink(struct dentry *lowerdentry,
|
||||
unsigned int ovl_get_nlink(struct ovl_fs *ofs, struct dentry *lowerdentry,
|
||||
struct dentry *upperdentry,
|
||||
unsigned int fallback);
|
||||
int ovl_setattr(struct dentry *dentry, struct iattr *attr);
|
||||
|
@ -418,7 +443,7 @@ int ovl_xattr_get(struct dentry *dentry, struct inode *inode, const char *name,
|
|||
ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size);
|
||||
struct posix_acl *ovl_get_acl(struct inode *inode, int type);
|
||||
int ovl_update_time(struct inode *inode, struct timespec64 *ts, int flags);
|
||||
bool ovl_is_private_xattr(const char *name);
|
||||
bool ovl_is_private_xattr(struct super_block *sb, const char *name);
|
||||
|
||||
struct ovl_inode_params {
|
||||
struct inode *newinode;
|
||||
|
@ -479,12 +504,15 @@ struct dentry *ovl_create_temp(struct dentry *workdir, struct ovl_cattr *attr);
|
|||
extern const struct file_operations ovl_file_operations;
|
||||
int __init ovl_aio_request_cache_init(void);
|
||||
void ovl_aio_request_cache_destroy(void);
|
||||
long ovl_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
|
||||
long ovl_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
|
||||
|
||||
/* copy_up.c */
|
||||
int ovl_copy_up(struct dentry *dentry);
|
||||
int ovl_copy_up_with_data(struct dentry *dentry);
|
||||
int ovl_maybe_copy_up(struct dentry *dentry, int flags);
|
||||
int ovl_copy_xattr(struct dentry *old, struct dentry *new);
|
||||
int ovl_copy_xattr(struct super_block *sb, struct dentry *old,
|
||||
struct dentry *new);
|
||||
int ovl_set_attr(struct dentry *upper, struct kstat *stat);
|
||||
struct ovl_fh *ovl_encode_real_fh(struct dentry *real, bool is_upper);
|
||||
int ovl_set_origin(struct dentry *dentry, struct dentry *lower,
|
||||
|
|
|
@ -17,6 +17,7 @@ struct ovl_config {
|
|||
bool nfs_export;
|
||||
int xino;
|
||||
bool metacopy;
|
||||
bool ovl_volatile;
|
||||
};
|
||||
|
||||
struct ovl_sb {
|
||||
|
@ -90,6 +91,11 @@ static inline struct ovl_fs *OVL_FS(struct super_block *sb)
|
|||
return (struct ovl_fs *)sb->s_fs_info;
|
||||
}
|
||||
|
||||
static inline bool ovl_should_sync(struct ovl_fs *ofs)
|
||||
{
|
||||
return !ofs->config.ovl_volatile;
|
||||
}
|
||||
|
||||
/* private information held for every overlayfs dentry */
|
||||
struct ovl_entry {
|
||||
union {
|
||||
|
|
|
@ -606,6 +606,7 @@ static struct ovl_dir_cache *ovl_cache_get_impure(struct path *path)
|
|||
{
|
||||
int res;
|
||||
struct dentry *dentry = path->dentry;
|
||||
struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
|
||||
struct ovl_dir_cache *cache;
|
||||
|
||||
cache = ovl_dir_cache(d_inode(dentry));
|
||||
|
@ -632,7 +633,7 @@ static struct ovl_dir_cache *ovl_cache_get_impure(struct path *path)
|
|||
* Removing the "impure" xattr is best effort.
|
||||
*/
|
||||
if (!ovl_want_write(dentry)) {
|
||||
ovl_do_removexattr(ovl_dentry_upper(dentry),
|
||||
ovl_do_removexattr(ofs, ovl_dentry_upper(dentry),
|
||||
OVL_XATTR_IMPURE);
|
||||
ovl_drop_write(dentry);
|
||||
}
|
||||
|
@ -839,7 +840,7 @@ out_unlock:
|
|||
return res;
|
||||
}
|
||||
|
||||
static struct file *ovl_dir_open_realfile(struct file *file,
|
||||
static struct file *ovl_dir_open_realfile(const struct file *file,
|
||||
struct path *realpath)
|
||||
{
|
||||
struct file *res;
|
||||
|
@ -852,16 +853,22 @@ static struct file *ovl_dir_open_realfile(struct file *file,
|
|||
return res;
|
||||
}
|
||||
|
||||
static int ovl_dir_fsync(struct file *file, loff_t start, loff_t end,
|
||||
int datasync)
|
||||
/*
|
||||
* Like ovl_real_fdget(), returns upperfile if dir was copied up since open.
|
||||
* Unlike ovl_real_fdget(), this caches upperfile in file->private_data.
|
||||
*
|
||||
* TODO: use same abstract type for file->private_data of dir and file so
|
||||
* upperfile could also be cached for files as well.
|
||||
*/
|
||||
struct file *ovl_dir_real_file(const struct file *file, bool want_upper)
|
||||
{
|
||||
|
||||
struct ovl_dir_file *od = file->private_data;
|
||||
struct dentry *dentry = file->f_path.dentry;
|
||||
struct file *realfile = od->realfile;
|
||||
|
||||
/* Nothing to sync for lower */
|
||||
if (!OVL_TYPE_UPPER(ovl_path_type(dentry)))
|
||||
return 0;
|
||||
return want_upper ? NULL : realfile;
|
||||
|
||||
/*
|
||||
* Need to check if we started out being a lower dir, but got copied up
|
||||
|
@ -880,7 +887,7 @@ static int ovl_dir_fsync(struct file *file, loff_t start, loff_t end,
|
|||
if (!od->upperfile) {
|
||||
if (IS_ERR(realfile)) {
|
||||
inode_unlock(inode);
|
||||
return PTR_ERR(realfile);
|
||||
return realfile;
|
||||
}
|
||||
smp_store_release(&od->upperfile, realfile);
|
||||
} else {
|
||||
|
@ -893,6 +900,25 @@ static int ovl_dir_fsync(struct file *file, loff_t start, loff_t end,
|
|||
}
|
||||
}
|
||||
|
||||
return realfile;
|
||||
}
|
||||
|
||||
static int ovl_dir_fsync(struct file *file, loff_t start, loff_t end,
|
||||
int datasync)
|
||||
{
|
||||
struct file *realfile;
|
||||
int err;
|
||||
|
||||
if (!ovl_should_sync(OVL_FS(file->f_path.dentry->d_sb)))
|
||||
return 0;
|
||||
|
||||
realfile = ovl_dir_real_file(file, true);
|
||||
err = PTR_ERR_OR_ZERO(realfile);
|
||||
|
||||
/* Nothing to sync for lower */
|
||||
if (!realfile || err)
|
||||
return err;
|
||||
|
||||
return vfs_fsync_range(realfile, start, end, datasync);
|
||||
}
|
||||
|
||||
|
@ -945,6 +971,10 @@ const struct file_operations ovl_dir_operations = {
|
|||
.llseek = ovl_dir_llseek,
|
||||
.fsync = ovl_dir_fsync,
|
||||
.release = ovl_dir_release,
|
||||
.unlocked_ioctl = ovl_ioctl,
|
||||
#ifdef CONFIG_COMPAT
|
||||
.compat_ioctl = ovl_compat_ioctl,
|
||||
#endif
|
||||
};
|
||||
|
||||
int ovl_check_empty_dir(struct dentry *dentry, struct list_head *list)
|
||||
|
@ -1051,7 +1081,9 @@ int ovl_check_d_type_supported(struct path *realpath)
|
|||
return rdd.d_type_supported;
|
||||
}
|
||||
|
||||
static void ovl_workdir_cleanup_recurse(struct path *path, int level)
|
||||
#define OVL_INCOMPATDIR_NAME "incompat"
|
||||
|
||||
static int ovl_workdir_cleanup_recurse(struct path *path, int level)
|
||||
{
|
||||
int err;
|
||||
struct inode *dir = path->dentry->d_inode;
|
||||
|
@ -1065,6 +1097,19 @@ static void ovl_workdir_cleanup_recurse(struct path *path, int level)
|
|||
.root = &root,
|
||||
.is_lowest = false,
|
||||
};
|
||||
bool incompat = false;
|
||||
|
||||
/*
|
||||
* The "work/incompat" directory is treated specially - if it is not
|
||||
* empty, instead of printing a generic error and mounting read-only,
|
||||
* we will error about incompat features and fail the mount.
|
||||
*
|
||||
* When called from ovl_indexdir_cleanup(), path->dentry->d_name.name
|
||||
* starts with '#'.
|
||||
*/
|
||||
if (level == 2 &&
|
||||
!strcmp(path->dentry->d_name.name, OVL_INCOMPATDIR_NAME))
|
||||
incompat = true;
|
||||
|
||||
err = ovl_dir_read(path, &rdd);
|
||||
if (err)
|
||||
|
@ -1079,17 +1124,25 @@ static void ovl_workdir_cleanup_recurse(struct path *path, int level)
|
|||
continue;
|
||||
if (p->len == 2 && p->name[1] == '.')
|
||||
continue;
|
||||
} else if (incompat) {
|
||||
pr_err("overlay with incompat feature '%s' cannot be mounted\n",
|
||||
p->name);
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
dentry = lookup_one_len(p->name, path->dentry, p->len);
|
||||
if (IS_ERR(dentry))
|
||||
continue;
|
||||
if (dentry->d_inode)
|
||||
ovl_workdir_cleanup(dir, path->mnt, dentry, level);
|
||||
err = ovl_workdir_cleanup(dir, path->mnt, dentry, level);
|
||||
dput(dentry);
|
||||
if (err)
|
||||
break;
|
||||
}
|
||||
inode_unlock(dir);
|
||||
out:
|
||||
ovl_cache_free(&list);
|
||||
return err;
|
||||
}
|
||||
|
||||
int ovl_workdir_cleanup(struct inode *dir, struct vfsmount *mnt,
|
||||
|
@ -1106,9 +1159,10 @@ int ovl_workdir_cleanup(struct inode *dir, struct vfsmount *mnt,
|
|||
struct path path = { .mnt = mnt, .dentry = dentry };
|
||||
|
||||
inode_unlock(dir);
|
||||
ovl_workdir_cleanup_recurse(&path, level + 1);
|
||||
err = ovl_workdir_cleanup_recurse(&path, level + 1);
|
||||
inode_lock_nested(dir, I_MUTEX_PARENT);
|
||||
err = ovl_cleanup(dir, dentry);
|
||||
if (!err)
|
||||
err = ovl_cleanup(dir, dentry);
|
||||
}
|
||||
|
||||
return err;
|
||||
|
|
|
@ -264,6 +264,8 @@ static int ovl_sync_fs(struct super_block *sb, int wait)
|
|||
if (!ovl_upper_mnt(ofs))
|
||||
return 0;
|
||||
|
||||
if (!ovl_should_sync(ofs))
|
||||
return 0;
|
||||
/*
|
||||
* Not called for sync(2) call or an emergency sync (SB_I_SKIP_SYNC).
|
||||
* All the super blocks will be iterated, including upper_sb.
|
||||
|
@ -362,6 +364,8 @@ static int ovl_show_options(struct seq_file *m, struct dentry *dentry)
|
|||
if (ofs->config.metacopy != ovl_metacopy_def)
|
||||
seq_printf(m, ",metacopy=%s",
|
||||
ofs->config.metacopy ? "on" : "off");
|
||||
if (ofs->config.ovl_volatile)
|
||||
seq_puts(m, ",volatile");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -376,9 +380,11 @@ static int ovl_remount(struct super_block *sb, int *flags, char *data)
|
|||
|
||||
if (*flags & SB_RDONLY && !sb_rdonly(sb)) {
|
||||
upper_sb = ovl_upper_mnt(ofs)->mnt_sb;
|
||||
down_read(&upper_sb->s_umount);
|
||||
ret = sync_filesystem(upper_sb);
|
||||
up_read(&upper_sb->s_umount);
|
||||
if (ovl_should_sync(ofs)) {
|
||||
down_read(&upper_sb->s_umount);
|
||||
ret = sync_filesystem(upper_sb);
|
||||
up_read(&upper_sb->s_umount);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
@ -411,6 +417,7 @@ enum {
|
|||
OPT_XINO_AUTO,
|
||||
OPT_METACOPY_ON,
|
||||
OPT_METACOPY_OFF,
|
||||
OPT_VOLATILE,
|
||||
OPT_ERR,
|
||||
};
|
||||
|
||||
|
@ -429,6 +436,7 @@ static const match_table_t ovl_tokens = {
|
|||
{OPT_XINO_AUTO, "xino=auto"},
|
||||
{OPT_METACOPY_ON, "metacopy=on"},
|
||||
{OPT_METACOPY_OFF, "metacopy=off"},
|
||||
{OPT_VOLATILE, "volatile"},
|
||||
{OPT_ERR, NULL}
|
||||
};
|
||||
|
||||
|
@ -573,6 +581,10 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config)
|
|||
metacopy_opt = true;
|
||||
break;
|
||||
|
||||
case OPT_VOLATILE:
|
||||
config->ovl_volatile = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
pr_err("unrecognized mount option \"%s\" or missing value\n",
|
||||
p);
|
||||
|
@ -595,6 +607,11 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config)
|
|||
config->index = false;
|
||||
}
|
||||
|
||||
if (!config->upperdir && config->ovl_volatile) {
|
||||
pr_info("option \"volatile\" is meaningless in a non-upper mount, ignoring it.\n");
|
||||
config->ovl_volatile = false;
|
||||
}
|
||||
|
||||
err = ovl_parse_redirect_mode(config, config->redirect_mode);
|
||||
if (err)
|
||||
return err;
|
||||
|
@ -705,8 +722,12 @@ retry:
|
|||
goto out_unlock;
|
||||
|
||||
retried = true;
|
||||
ovl_workdir_cleanup(dir, mnt, work, 0);
|
||||
err = ovl_workdir_cleanup(dir, mnt, work, 0);
|
||||
dput(work);
|
||||
if (err == -EINVAL) {
|
||||
work = ERR_PTR(err);
|
||||
goto out_unlock;
|
||||
}
|
||||
goto retry;
|
||||
}
|
||||
|
||||
|
@ -1199,11 +1220,50 @@ out_unlock:
|
|||
return err;
|
||||
}
|
||||
|
||||
static struct dentry *ovl_lookup_or_create(struct dentry *parent,
|
||||
const char *name, umode_t mode)
|
||||
{
|
||||
size_t len = strlen(name);
|
||||
struct dentry *child;
|
||||
|
||||
inode_lock_nested(parent->d_inode, I_MUTEX_PARENT);
|
||||
child = lookup_one_len(name, parent, len);
|
||||
if (!IS_ERR(child) && !child->d_inode)
|
||||
child = ovl_create_real(parent->d_inode, child,
|
||||
OVL_CATTR(mode));
|
||||
inode_unlock(parent->d_inode);
|
||||
dput(parent);
|
||||
|
||||
return child;
|
||||
}
|
||||
|
||||
/*
|
||||
* Creates $workdir/work/incompat/volatile/dirty file if it is not already
|
||||
* present.
|
||||
*/
|
||||
static int ovl_create_volatile_dirty(struct ovl_fs *ofs)
|
||||
{
|
||||
unsigned int ctr;
|
||||
struct dentry *d = dget(ofs->workbasedir);
|
||||
static const char *const volatile_path[] = {
|
||||
OVL_WORKDIR_NAME, "incompat", "volatile", "dirty"
|
||||
};
|
||||
const char *const *name = volatile_path;
|
||||
|
||||
for (ctr = ARRAY_SIZE(volatile_path); ctr; ctr--, name++) {
|
||||
d = ovl_lookup_or_create(d, *name, ctr > 1 ? S_IFDIR : S_IFREG);
|
||||
if (IS_ERR(d))
|
||||
return PTR_ERR(d);
|
||||
}
|
||||
dput(d);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ovl_make_workdir(struct super_block *sb, struct ovl_fs *ofs,
|
||||
struct path *workpath)
|
||||
{
|
||||
struct vfsmount *mnt = ovl_upper_mnt(ofs);
|
||||
struct dentry *temp;
|
||||
struct dentry *temp, *workdir;
|
||||
bool rename_whiteout;
|
||||
bool d_type;
|
||||
int fh_type;
|
||||
|
@ -1213,10 +1273,13 @@ static int ovl_make_workdir(struct super_block *sb, struct ovl_fs *ofs,
|
|||
if (err)
|
||||
return err;
|
||||
|
||||
ofs->workdir = ovl_workdir_create(ofs, OVL_WORKDIR_NAME, false);
|
||||
if (!ofs->workdir)
|
||||
workdir = ovl_workdir_create(ofs, OVL_WORKDIR_NAME, false);
|
||||
err = PTR_ERR(workdir);
|
||||
if (IS_ERR_OR_NULL(workdir))
|
||||
goto out;
|
||||
|
||||
ofs->workdir = workdir;
|
||||
|
||||
err = ovl_setup_trap(sb, ofs->workdir, &ofs->workdir_trap, "workdir");
|
||||
if (err)
|
||||
goto out;
|
||||
|
@ -1256,7 +1319,7 @@ static int ovl_make_workdir(struct super_block *sb, struct ovl_fs *ofs,
|
|||
/*
|
||||
* Check if upper/work fs supports trusted.overlay.* xattr
|
||||
*/
|
||||
err = ovl_do_setxattr(ofs->workdir, OVL_XATTR_OPAQUE, "0", 1, 0);
|
||||
err = ovl_do_setxattr(ofs, ofs->workdir, OVL_XATTR_OPAQUE, "0", 1);
|
||||
if (err) {
|
||||
ofs->noxattr = true;
|
||||
ofs->config.index = false;
|
||||
|
@ -1264,7 +1327,7 @@ static int ovl_make_workdir(struct super_block *sb, struct ovl_fs *ofs,
|
|||
pr_warn("upper fs does not support xattr, falling back to index=off and metacopy=off.\n");
|
||||
err = 0;
|
||||
} else {
|
||||
vfs_removexattr(ofs->workdir, OVL_XATTR_OPAQUE);
|
||||
ovl_do_removexattr(ofs, ofs->workdir, OVL_XATTR_OPAQUE);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1279,6 +1342,18 @@ static int ovl_make_workdir(struct super_block *sb, struct ovl_fs *ofs,
|
|||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* For volatile mount, create a incompat/volatile/dirty file to keep
|
||||
* track of it.
|
||||
*/
|
||||
if (ofs->config.ovl_volatile) {
|
||||
err = ovl_create_volatile_dirty(ofs);
|
||||
if (err < 0) {
|
||||
pr_err("Failed to create volatile/dirty file.\n");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if upper/work fs supports file handles */
|
||||
fh_type = ovl_can_decode_fh(ofs->workdir->d_sb);
|
||||
if (ofs->config.index && !fh_type) {
|
||||
|
@ -1347,6 +1422,7 @@ static int ovl_get_indexdir(struct super_block *sb, struct ovl_fs *ofs,
|
|||
struct ovl_entry *oe, struct path *upperpath)
|
||||
{
|
||||
struct vfsmount *mnt = ovl_upper_mnt(ofs);
|
||||
struct dentry *indexdir;
|
||||
int err;
|
||||
|
||||
err = mnt_want_write(mnt);
|
||||
|
@ -1354,8 +1430,8 @@ static int ovl_get_indexdir(struct super_block *sb, struct ovl_fs *ofs,
|
|||
return err;
|
||||
|
||||
/* Verify lower root is upper root origin */
|
||||
err = ovl_verify_origin(upperpath->dentry, oe->lowerstack[0].dentry,
|
||||
true);
|
||||
err = ovl_verify_origin(ofs, upperpath->dentry,
|
||||
oe->lowerstack[0].dentry, true);
|
||||
if (err) {
|
||||
pr_err("failed to verify upper root origin\n");
|
||||
goto out;
|
||||
|
@ -1366,9 +1442,12 @@ static int ovl_get_indexdir(struct super_block *sb, struct ovl_fs *ofs,
|
|||
ofs->workdir_trap = NULL;
|
||||
dput(ofs->workdir);
|
||||
ofs->workdir = NULL;
|
||||
ofs->indexdir = ovl_workdir_create(ofs, OVL_INDEXDIR_NAME, true);
|
||||
if (ofs->indexdir) {
|
||||
ofs->workdir = dget(ofs->indexdir);
|
||||
indexdir = ovl_workdir_create(ofs, OVL_INDEXDIR_NAME, true);
|
||||
if (IS_ERR(indexdir)) {
|
||||
err = PTR_ERR(indexdir);
|
||||
} else if (indexdir) {
|
||||
ofs->indexdir = indexdir;
|
||||
ofs->workdir = dget(indexdir);
|
||||
|
||||
err = ovl_setup_trap(sb, ofs->indexdir, &ofs->indexdir_trap,
|
||||
"indexdir");
|
||||
|
@ -1383,13 +1462,15 @@ static int ovl_get_indexdir(struct super_block *sb, struct ovl_fs *ofs,
|
|||
* "trusted.overlay.upper" to indicate that index may have
|
||||
* directory entries.
|
||||
*/
|
||||
if (ovl_check_origin_xattr(ofs->indexdir)) {
|
||||
err = ovl_verify_set_fh(ofs->indexdir, OVL_XATTR_ORIGIN,
|
||||
if (ovl_check_origin_xattr(ofs, ofs->indexdir)) {
|
||||
err = ovl_verify_set_fh(ofs, ofs->indexdir,
|
||||
OVL_XATTR_ORIGIN,
|
||||
upperpath->dentry, true, false);
|
||||
if (err)
|
||||
pr_err("failed to verify index dir 'origin' xattr\n");
|
||||
}
|
||||
err = ovl_verify_upper(ofs->indexdir, upperpath->dentry, true);
|
||||
err = ovl_verify_upper(ofs, ofs->indexdir, upperpath->dentry,
|
||||
true);
|
||||
if (err)
|
||||
pr_err("failed to verify index dir 'upper' xattr\n");
|
||||
|
||||
|
@ -1755,7 +1836,7 @@ static struct dentry *ovl_get_root(struct super_block *sb,
|
|||
ino = d_inode(upperdentry)->i_ino;
|
||||
fsid = 0;
|
||||
ovl_dentry_set_upper_alias(root);
|
||||
if (ovl_is_impuredir(upperdentry))
|
||||
if (ovl_is_impuredir(sb, upperdentry))
|
||||
ovl_set_flag(OVL_IMPURE, d_inode(root));
|
||||
}
|
||||
|
||||
|
|
|
@ -544,11 +544,11 @@ void ovl_copy_up_end(struct dentry *dentry)
|
|||
ovl_inode_unlock(d_inode(dentry));
|
||||
}
|
||||
|
||||
bool ovl_check_origin_xattr(struct dentry *dentry)
|
||||
bool ovl_check_origin_xattr(struct ovl_fs *ofs, struct dentry *dentry)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = vfs_getxattr(dentry, OVL_XATTR_ORIGIN, NULL, 0);
|
||||
res = ovl_do_getxattr(ofs, dentry, OVL_XATTR_ORIGIN, NULL, 0);
|
||||
|
||||
/* Zero size value means "copied up but origin unknown" */
|
||||
if (res >= 0)
|
||||
|
@ -557,7 +557,8 @@ bool ovl_check_origin_xattr(struct dentry *dentry)
|
|||
return false;
|
||||
}
|
||||
|
||||
bool ovl_check_dir_xattr(struct dentry *dentry, const char *name)
|
||||
bool ovl_check_dir_xattr(struct super_block *sb, struct dentry *dentry,
|
||||
enum ovl_xattr ox)
|
||||
{
|
||||
int res;
|
||||
char val;
|
||||
|
@ -565,15 +566,36 @@ bool ovl_check_dir_xattr(struct dentry *dentry, const char *name)
|
|||
if (!d_is_dir(dentry))
|
||||
return false;
|
||||
|
||||
res = vfs_getxattr(dentry, name, &val, 1);
|
||||
res = ovl_do_getxattr(OVL_FS(sb), dentry, ox, &val, 1);
|
||||
if (res == 1 && val == 'y')
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#define OVL_XATTR_OPAQUE_POSTFIX "opaque"
|
||||
#define OVL_XATTR_REDIRECT_POSTFIX "redirect"
|
||||
#define OVL_XATTR_ORIGIN_POSTFIX "origin"
|
||||
#define OVL_XATTR_IMPURE_POSTFIX "impure"
|
||||
#define OVL_XATTR_NLINK_POSTFIX "nlink"
|
||||
#define OVL_XATTR_UPPER_POSTFIX "upper"
|
||||
#define OVL_XATTR_METACOPY_POSTFIX "metacopy"
|
||||
|
||||
#define OVL_XATTR_TAB_ENTRY(x) \
|
||||
[x] = OVL_XATTR_PREFIX x ## _POSTFIX
|
||||
|
||||
const char *ovl_xattr_table[] = {
|
||||
OVL_XATTR_TAB_ENTRY(OVL_XATTR_OPAQUE),
|
||||
OVL_XATTR_TAB_ENTRY(OVL_XATTR_REDIRECT),
|
||||
OVL_XATTR_TAB_ENTRY(OVL_XATTR_ORIGIN),
|
||||
OVL_XATTR_TAB_ENTRY(OVL_XATTR_IMPURE),
|
||||
OVL_XATTR_TAB_ENTRY(OVL_XATTR_NLINK),
|
||||
OVL_XATTR_TAB_ENTRY(OVL_XATTR_UPPER),
|
||||
OVL_XATTR_TAB_ENTRY(OVL_XATTR_METACOPY),
|
||||
};
|
||||
|
||||
int ovl_check_setxattr(struct dentry *dentry, struct dentry *upperdentry,
|
||||
const char *name, const void *value, size_t size,
|
||||
enum ovl_xattr ox, const void *value, size_t size,
|
||||
int xerr)
|
||||
{
|
||||
int err;
|
||||
|
@ -582,10 +604,10 @@ int ovl_check_setxattr(struct dentry *dentry, struct dentry *upperdentry,
|
|||
if (ofs->noxattr)
|
||||
return xerr;
|
||||
|
||||
err = ovl_do_setxattr(upperdentry, name, value, size, 0);
|
||||
err = ovl_do_setxattr(ofs, upperdentry, ox, value, size);
|
||||
|
||||
if (err == -EOPNOTSUPP) {
|
||||
pr_warn("cannot set %s xattr on upper\n", name);
|
||||
pr_warn("cannot set %s xattr on upper\n", ovl_xattr(ofs, ox));
|
||||
ofs->noxattr = true;
|
||||
return xerr;
|
||||
}
|
||||
|
@ -845,7 +867,7 @@ err:
|
|||
}
|
||||
|
||||
/* err < 0, 0 if no metacopy xattr, 1 if metacopy xattr found */
|
||||
int ovl_check_metacopy_xattr(struct dentry *dentry)
|
||||
int ovl_check_metacopy_xattr(struct ovl_fs *ofs, struct dentry *dentry)
|
||||
{
|
||||
int res;
|
||||
|
||||
|
@ -853,7 +875,7 @@ int ovl_check_metacopy_xattr(struct dentry *dentry)
|
|||
if (!S_ISREG(d_inode(dentry)->i_mode))
|
||||
return 0;
|
||||
|
||||
res = vfs_getxattr(dentry, OVL_XATTR_METACOPY, NULL, 0);
|
||||
res = ovl_do_getxattr(ofs, dentry, OVL_XATTR_METACOPY, NULL, 0);
|
||||
if (res < 0) {
|
||||
if (res == -ENODATA || res == -EOPNOTSUPP)
|
||||
return 0;
|
||||
|
@ -882,49 +904,27 @@ bool ovl_is_metacopy_dentry(struct dentry *dentry)
|
|||
return (oe->numlower > 1);
|
||||
}
|
||||
|
||||
ssize_t ovl_getxattr(struct dentry *dentry, char *name, char **value,
|
||||
size_t padding)
|
||||
{
|
||||
ssize_t res;
|
||||
char *buf = NULL;
|
||||
|
||||
res = vfs_getxattr(dentry, name, NULL, 0);
|
||||
if (res < 0) {
|
||||
if (res == -ENODATA || res == -EOPNOTSUPP)
|
||||
return -ENODATA;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (res != 0) {
|
||||
buf = kzalloc(res + padding, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
res = vfs_getxattr(dentry, name, buf, res);
|
||||
if (res < 0)
|
||||
goto fail;
|
||||
}
|
||||
*value = buf;
|
||||
|
||||
return res;
|
||||
|
||||
fail:
|
||||
pr_warn_ratelimited("failed to get xattr %s: err=%zi)\n",
|
||||
name, res);
|
||||
kfree(buf);
|
||||
return res;
|
||||
}
|
||||
|
||||
char *ovl_get_redirect_xattr(struct dentry *dentry, int padding)
|
||||
char *ovl_get_redirect_xattr(struct ovl_fs *ofs, struct dentry *dentry,
|
||||
int padding)
|
||||
{
|
||||
int res;
|
||||
char *s, *next, *buf = NULL;
|
||||
|
||||
res = ovl_getxattr(dentry, OVL_XATTR_REDIRECT, &buf, padding + 1);
|
||||
if (res == -ENODATA)
|
||||
res = ovl_do_getxattr(ofs, dentry, OVL_XATTR_REDIRECT, NULL, 0);
|
||||
if (res == -ENODATA || res == -EOPNOTSUPP)
|
||||
return NULL;
|
||||
if (res < 0)
|
||||
return ERR_PTR(res);
|
||||
goto fail;
|
||||
if (res == 0)
|
||||
goto invalid;
|
||||
|
||||
buf = kzalloc(res + padding + 1, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
res = ovl_do_getxattr(ofs, dentry, OVL_XATTR_REDIRECT, buf, res);
|
||||
if (res < 0)
|
||||
goto fail;
|
||||
if (res == 0)
|
||||
goto invalid;
|
||||
|
||||
|
@ -943,6 +943,10 @@ char *ovl_get_redirect_xattr(struct dentry *dentry, int padding)
|
|||
invalid:
|
||||
pr_warn_ratelimited("invalid redirect (%s)\n", buf);
|
||||
res = -EINVAL;
|
||||
goto err_free;
|
||||
fail:
|
||||
pr_warn_ratelimited("failed to get redirect (%i)\n", res);
|
||||
err_free:
|
||||
kfree(buf);
|
||||
return ERR_PTR(res);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue