Skip to content

Commit

Permalink
Merge tag 'ovl-update-5.8' of git://git.kernel.org/pub/scm/linux/kern…
Browse files Browse the repository at this point in the history
…el/git/mszeredi/vfs

Pull overlayfs updates from Miklos Szeredi:
 "Fixes:

   - Resolve mount option conflicts consistently

   - Sync before remount R/O

   - Fix file handle encoding corner cases

   - Fix metacopy related issues

   - Fix an unintialized return value

   - Add missing permission checks for underlying layers

  Optimizations:

   - Allow multipe whiteouts to share an inode

   - Optimize small writes by inheriting SB_NOSEC from upper layer

   - Do not call ->syncfs() multiple times for sync(2)

   - Do not cache negative lookups on upper layer

   - Make private internal mounts longterm"

* tag 'ovl-update-5.8' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs: (27 commits)
  ovl: remove unnecessary lock check
  ovl: make oip->index bool
  ovl: only pass ->ki_flags to ovl_iocb_to_rwf()
  ovl: make private mounts longterm
  ovl: get rid of redundant members in struct ovl_fs
  ovl: add accessor for ofs->upper_mnt
  ovl: initialize error in ovl_copy_xattr
  ovl: drop negative dentry in upper layer
  ovl: check permission to open real file
  ovl: call secutiry hook in ovl_real_ioctl()
  ovl: verify permissions in ovl_path_open()
  ovl: switch to mounter creds in readdir
  ovl: pass correct flags for opening real directory
  ovl: fix redirect traversal on metacopy dentries
  ovl: initialize OVL_UPPERDATA in ovl_lookup()
  ovl: use only uppermetacopy state in ovl_lookup()
  ovl: simplify setting of origin for index lookup
  ovl: fix out of bounds access warning in ovl_check_fb_len()
  ovl: return required buffer size for file handles
  ovl: sync dirty data when remounting to ro mode
  ...
  • Loading branch information
Linus Torvalds committed Jun 9, 2020
2 parents 4964dd2 + 2068cf7 commit 52435c8
Show file tree
Hide file tree
Showing 18 changed files with 440 additions and 222 deletions.
7 changes: 5 additions & 2 deletions Documentation/filesystems/overlayfs.rst
Original file line number Diff line number Diff line change
Expand Up @@ -365,8 +365,8 @@ pointed by REDIRECT. This should not be possible on local system as setting
"trusted." xattrs will require CAP_SYS_ADMIN. But it should be possible
for untrusted layers like from a pen drive.

Note: redirect_dir={off|nofollow|follow[*]} conflicts with metacopy=on, and
results in an error.
Note: redirect_dir={off|nofollow|follow[*]} and nfs_export=on mount options
conflict with metacopy=on, and will result in an error.

[*] redirect_dir=follow only conflicts with metacopy=on if upperdir=... is
given.
Expand Down Expand Up @@ -560,6 +560,9 @@ When the NFS export feature is enabled, all directory index entries are
verified on mount time to check that upper file handles are not stale.
This verification may cause significant overhead in some cases.

Note: the mount options index=off,nfs_export=on are conflicting and will
result in an error.


Testsuite
---------
Expand Down
7 changes: 7 additions & 0 deletions Documentation/filesystems/porting.rst
Original file line number Diff line number Diff line change
Expand Up @@ -858,3 +858,10 @@ be misspelled d_alloc_anon().
[should've been added in 2016] stale comment in finish_open() nonwithstanding,
failure exits in ->atomic_open() instances should *NOT* fput() the file,
no matter what. Everything is handled by the caller.

---

**mandatory**

clone_private_mount() returns a longterm mount now, so the proper destructor of
its result is kern_unmount() or kern_unmount_array().
16 changes: 16 additions & 0 deletions fs/namespace.c
Original file line number Diff line number Diff line change
Expand Up @@ -1937,6 +1937,9 @@ struct vfsmount *clone_private_mount(const struct path *path)
if (IS_ERR(new_mnt))
return ERR_CAST(new_mnt);

/* Longterm mount to be removed by kern_unmount*() */
new_mnt->mnt_ns = MNT_NS_INTERNAL;

return &new_mnt->mnt;
}
EXPORT_SYMBOL_GPL(clone_private_mount);
Expand Down Expand Up @@ -3863,6 +3866,19 @@ void kern_unmount(struct vfsmount *mnt)
}
EXPORT_SYMBOL(kern_unmount);

void kern_unmount_array(struct vfsmount *mnt[], unsigned int num)
{
unsigned int i;

for (i = 0; i < num; i++)
if (mnt[i])
real_mount(mnt[i])->mnt_ns = NULL;
synchronize_rcu_expedited();
for (i = 0; i < num; i++)
mntput(mnt[i]);
}
EXPORT_SYMBOL(kern_unmount_array);

bool our_mnt(struct vfsmount *mnt)
{
return check_mnt(real_mount(mnt));
Expand Down
9 changes: 5 additions & 4 deletions fs/overlayfs/copy_up.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ int ovl_copy_xattr(struct dentry *old, struct dentry *new)
{
ssize_t list_size, size, value_size = 0;
char *buf, *name, *value = NULL;
int uninitialized_var(error);
int error = 0;
size_t slen;

if (!(old->d_inode->i_opflags & IOP_XATTR) ||
Expand Down Expand Up @@ -584,9 +584,10 @@ static int ovl_copy_up_workdir(struct ovl_copy_up_ctx *c)
.link = c->link
};

err = ovl_lock_rename_workdir(c->workdir, c->destdir);
if (err)
return err;
/* workdir and destdir could be the same when copying up to indexdir */
err = -EIO;
if (lock_rename(c->workdir, c->destdir) != NULL)
goto unlock;

err = ovl_prep_cu_creds(c->dentry, &cc);
if (err)
Expand Down
51 changes: 39 additions & 12 deletions fs/overlayfs/dir.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,35 +62,59 @@ struct dentry *ovl_lookup_temp(struct dentry *workdir)
}

/* caller holds i_mutex on workdir */
static struct dentry *ovl_whiteout(struct dentry *workdir)
static struct dentry *ovl_whiteout(struct ovl_fs *ofs)
{
int err;
struct dentry *whiteout;
struct dentry *workdir = ofs->workdir;
struct inode *wdir = workdir->d_inode;

whiteout = ovl_lookup_temp(workdir);
if (IS_ERR(whiteout))
return whiteout;
if (!ofs->whiteout) {
whiteout = ovl_lookup_temp(workdir);
if (IS_ERR(whiteout))
goto out;

err = ovl_do_whiteout(wdir, whiteout);
if (err) {
dput(whiteout);
whiteout = ERR_PTR(err);
err = ovl_do_whiteout(wdir, whiteout);
if (err) {
dput(whiteout);
whiteout = ERR_PTR(err);
goto out;
}
ofs->whiteout = whiteout;
}

if (ofs->share_whiteout) {
whiteout = ovl_lookup_temp(workdir);
if (IS_ERR(whiteout))
goto out;

err = ovl_do_link(ofs->whiteout, wdir, whiteout);
if (!err)
goto out;

if (err != -EMLINK) {
pr_warn("Failed to link whiteout - disabling whiteout inode sharing(nlink=%u, err=%i)\n",
ofs->whiteout->d_inode->i_nlink, err);
ofs->share_whiteout = false;
}
dput(whiteout);
}
whiteout = ofs->whiteout;
ofs->whiteout = NULL;
out:
return whiteout;
}

/* Caller must hold i_mutex on both workdir and dir */
int ovl_cleanup_and_whiteout(struct dentry *workdir, struct inode *dir,
int ovl_cleanup_and_whiteout(struct ovl_fs *ofs, struct inode *dir,
struct dentry *dentry)
{
struct inode *wdir = workdir->d_inode;
struct inode *wdir = ofs->workdir->d_inode;
struct dentry *whiteout;
int err;
int flags = 0;

whiteout = ovl_whiteout(workdir);
whiteout = ovl_whiteout(ofs);
err = PTR_ERR(whiteout);
if (IS_ERR(whiteout))
return err;
Expand Down Expand Up @@ -262,6 +286,8 @@ static int ovl_instantiate(struct dentry *dentry, struct inode *inode,
inode = ovl_get_inode(dentry->d_sb, &oip);
if (IS_ERR(inode))
return PTR_ERR(inode);
if (inode == oip.newinode)
ovl_set_flag(OVL_UPPERDATA, inode);
} else {
WARN_ON(ovl_inode_real(inode) != d_inode(newdentry));
dput(newdentry);
Expand Down Expand Up @@ -715,6 +741,7 @@ static bool ovl_matches_upper(struct dentry *dentry, struct dentry *upper)
static int ovl_remove_and_whiteout(struct dentry *dentry,
struct list_head *list)
{
struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
struct dentry *workdir = ovl_workdir(dentry);
struct dentry *upperdir = ovl_dentry_upper(dentry->d_parent);
struct dentry *upper;
Expand Down Expand Up @@ -748,7 +775,7 @@ static int ovl_remove_and_whiteout(struct dentry *dentry,
goto out_dput_upper;
}

err = ovl_cleanup_and_whiteout(workdir, d_inode(upperdir), upper);
err = ovl_cleanup_and_whiteout(ofs, d_inode(upperdir), upper);
if (err)
goto out_d_drop;

Expand Down
24 changes: 11 additions & 13 deletions fs/overlayfs/export.c
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ static int ovl_check_encode_origin(struct dentry *dentry)
* ovl_connect_layer() will try to make origin's layer "connected" by
* copying up a "connectable" ancestor.
*/
if (d_is_dir(dentry) && ofs->upper_mnt)
if (d_is_dir(dentry) && ovl_upper_mnt(ofs))
return ovl_connect_layer(dentry);

/* Lower file handle for indexed and non-upper dir/non-dir */
Expand All @@ -231,30 +231,26 @@ static int ovl_dentry_to_fid(struct dentry *dentry, u32 *fid, int buflen)
if (IS_ERR(fh))
return PTR_ERR(fh);

err = -EOVERFLOW;
len = OVL_FH_LEN(fh);
if (len > buflen)
goto fail;

memcpy(fid, fh, len);
if (len <= buflen)
memcpy(fid, fh, len);
err = len;

out:
kfree(fh);
return err;

fail:
pr_warn_ratelimited("failed to encode file handle (%pd2, err=%i, buflen=%d, len=%d, type=%d)\n",
dentry, err, buflen, fh ? (int)fh->fb.len : 0,
fh ? fh->fb.type : 0);
pr_warn_ratelimited("failed to encode file handle (%pd2, err=%i)\n",
dentry, err);
goto out;
}

static int ovl_encode_fh(struct inode *inode, u32 *fid, int *max_len,
struct inode *parent)
{
struct dentry *dentry;
int bytes = *max_len << 2;
int bytes, buflen = *max_len << 2;

/* TODO: encode connectable file handles */
if (parent)
Expand All @@ -264,12 +260,14 @@ static int ovl_encode_fh(struct inode *inode, u32 *fid, int *max_len,
if (WARN_ON(!dentry))
return FILEID_INVALID;

bytes = ovl_dentry_to_fid(dentry, fid, bytes);
bytes = ovl_dentry_to_fid(dentry, fid, buflen);
dput(dentry);
if (bytes <= 0)
return FILEID_INVALID;

*max_len = bytes >> 2;
if (bytes > buflen)
return FILEID_INVALID;

return OVL_FILEID_V1;
}
Expand Down Expand Up @@ -679,10 +677,10 @@ static struct dentry *ovl_upper_fh_to_d(struct super_block *sb,
struct dentry *dentry;
struct dentry *upper;

if (!ofs->upper_mnt)
if (!ovl_upper_mnt(ofs))
return ERR_PTR(-EACCES);

upper = ovl_decode_real_fh(fh, ofs->upper_mnt, true);
upper = ovl_decode_real_fh(fh, ovl_upper_mnt(ofs), true);
if (IS_ERR_OR_NULL(upper))
return upper;

Expand Down
28 changes: 21 additions & 7 deletions fs/overlayfs/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <linux/uio.h>
#include <linux/uaccess.h>
#include <linux/splice.h>
#include <linux/security.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include "overlayfs.h"
Expand Down Expand Up @@ -39,10 +40,22 @@ static struct file *ovl_open_realfile(const struct file *file,
struct file *realfile;
const struct cred *old_cred;
int flags = file->f_flags | O_NOATIME | FMODE_NONOTIFY;
int acc_mode = ACC_MODE(flags);
int err;

if (flags & O_APPEND)
acc_mode |= MAY_APPEND;

old_cred = ovl_override_creds(inode->i_sb);
realfile = open_with_fake_path(&file->f_path, flags, realinode,
current_cred());
err = inode_permission(realinode, MAY_OPEN | acc_mode);
if (err) {
realfile = ERR_PTR(err);
} else if (!inode_owner_or_capable(realinode)) {
realfile = ERR_PTR(-EPERM);
} else {
realfile = open_with_fake_path(&file->f_path, flags, realinode,
current_cred());
}
revert_creds(old_cred);

pr_debug("open(%p[%pD2/%c], 0%o) -> (%p, 0%o)\n",
Expand Down Expand Up @@ -219,9 +232,8 @@ static void ovl_file_accessed(struct file *file)
touch_atime(&file->f_path);
}

static rwf_t ovl_iocb_to_rwf(struct kiocb *iocb)
static rwf_t ovl_iocb_to_rwf(int ifl)
{
int ifl = iocb->ki_flags;
rwf_t flags = 0;

if (ifl & IOCB_NOWAIT)
Expand Down Expand Up @@ -283,7 +295,7 @@ static ssize_t ovl_read_iter(struct kiocb *iocb, struct iov_iter *iter)
old_cred = ovl_override_creds(file_inode(file)->i_sb);
if (is_sync_kiocb(iocb)) {
ret = vfs_iter_read(real.file, iter, &iocb->ki_pos,
ovl_iocb_to_rwf(iocb));
ovl_iocb_to_rwf(iocb->ki_flags));
} else {
struct ovl_aio_req *aio_req;

Expand Down Expand Up @@ -336,7 +348,7 @@ static ssize_t ovl_write_iter(struct kiocb *iocb, struct iov_iter *iter)
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));
ovl_iocb_to_rwf(iocb->ki_flags));
file_end_write(real.file);
/* Update size */
ovl_copyattr(ovl_inode_real(inode), inode);
Expand Down Expand Up @@ -520,7 +532,9 @@ static long ovl_real_ioctl(struct file *file, unsigned int cmd,
return ret;

old_cred = ovl_override_creds(file_inode(file)->i_sb);
ret = vfs_ioctl(real.file, cmd, arg);
ret = security_file_ioctl(real.file, cmd, arg);
if (!ret)
ret = vfs_ioctl(real.file, cmd, arg);
revert_creds(old_cred);

fdput(real);
Expand Down
17 changes: 4 additions & 13 deletions fs/overlayfs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -457,7 +457,7 @@ int ovl_update_time(struct inode *inode, struct timespec64 *ts, int flags)
if (flags & S_ATIME) {
struct ovl_fs *ofs = inode->i_sb->s_fs_info;
struct path upperpath = {
.mnt = ofs->upper_mnt,
.mnt = ovl_upper_mnt(ofs),
.dentry = ovl_upperdentry_dereference(OVL_I(inode)),
};

Expand Down Expand Up @@ -905,7 +905,7 @@ struct inode *ovl_get_trap_inode(struct super_block *sb, struct dentry *dir)
* Does overlay inode need to be hashed by lower inode?
*/
static bool ovl_hash_bylower(struct super_block *sb, struct dentry *upper,
struct dentry *lower, struct dentry *index)
struct dentry *lower, bool index)
{
struct ovl_fs *ofs = sb->s_fs_info;

Expand All @@ -918,7 +918,7 @@ static bool ovl_hash_bylower(struct super_block *sb, struct dentry *upper,
return true;

/* Yes, if won't be copied up */
if (!ofs->upper_mnt)
if (!ovl_upper_mnt(ofs))
return true;

/* No, if lower hardlink is or will be broken on copy up */
Expand Down Expand Up @@ -954,7 +954,7 @@ struct inode *ovl_get_inode(struct super_block *sb,
bool bylower = ovl_hash_bylower(sb, upperdentry, lowerdentry,
oip->index);
int fsid = bylower ? lowerpath->layer->fsid : 0;
bool is_dir, metacopy = false;
bool is_dir;
unsigned long ino = 0;
int err = oip->newinode ? -EEXIST : -ENOMEM;

Expand Down Expand Up @@ -1015,15 +1015,6 @@ struct inode *ovl_get_inode(struct super_block *sb,
if (oip->index)
ovl_set_flag(OVL_INDEX, inode);

if (upperdentry) {
err = ovl_check_metacopy_xattr(upperdentry);
if (err < 0)
goto out_err;
metacopy = err;
if (!metacopy)
ovl_set_flag(OVL_UPPERDATA, inode);
}

OVL_I(inode)->redirect = oip->redirect;

if (bylower)
Expand Down
Loading

0 comments on commit 52435c8

Please sign in to comment.