Commit 52435c86 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'ovl-update-5.8' of git://git.kernel.org/pub/scm/linux/kernel/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
  ...
parents 4964dd29 2068cf7d
...@@ -365,8 +365,8 @@ pointed by REDIRECT. This should not be possible on local system as setting ...@@ -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 "trusted." xattrs will require CAP_SYS_ADMIN. But it should be possible
for untrusted layers like from a pen drive. for untrusted layers like from a pen drive.
Note: redirect_dir={off|nofollow|follow[*]} conflicts with metacopy=on, and Note: redirect_dir={off|nofollow|follow[*]} and nfs_export=on mount options
results in an error. conflict with metacopy=on, and will result in an error.
[*] redirect_dir=follow only conflicts with metacopy=on if upperdir=... is [*] redirect_dir=follow only conflicts with metacopy=on if upperdir=... is
given. given.
...@@ -560,6 +560,9 @@ When the NFS export feature is enabled, all directory index entries are ...@@ -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. verified on mount time to check that upper file handles are not stale.
This verification may cause significant overhead in some cases. 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 Testsuite
--------- ---------
......
...@@ -858,3 +858,10 @@ be misspelled d_alloc_anon(). ...@@ -858,3 +858,10 @@ be misspelled d_alloc_anon().
[should've been added in 2016] stale comment in finish_open() nonwithstanding, [should've been added in 2016] stale comment in finish_open() nonwithstanding,
failure exits in ->atomic_open() instances should *NOT* fput() the file, failure exits in ->atomic_open() instances should *NOT* fput() the file,
no matter what. Everything is handled by the caller. 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().
...@@ -1937,6 +1937,9 @@ struct vfsmount *clone_private_mount(const struct path *path) ...@@ -1937,6 +1937,9 @@ struct vfsmount *clone_private_mount(const struct path *path)
if (IS_ERR(new_mnt)) if (IS_ERR(new_mnt))
return ERR_CAST(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; return &new_mnt->mnt;
} }
EXPORT_SYMBOL_GPL(clone_private_mount); EXPORT_SYMBOL_GPL(clone_private_mount);
...@@ -3863,6 +3866,19 @@ void kern_unmount(struct vfsmount *mnt) ...@@ -3863,6 +3866,19 @@ void kern_unmount(struct vfsmount *mnt)
} }
EXPORT_SYMBOL(kern_unmount); 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) bool our_mnt(struct vfsmount *mnt)
{ {
return check_mnt(real_mount(mnt)); return check_mnt(real_mount(mnt));
......
...@@ -47,7 +47,7 @@ int ovl_copy_xattr(struct dentry *old, struct dentry *new) ...@@ -47,7 +47,7 @@ int ovl_copy_xattr(struct dentry *old, struct dentry *new)
{ {
ssize_t list_size, size, value_size = 0; ssize_t list_size, size, value_size = 0;
char *buf, *name, *value = NULL; char *buf, *name, *value = NULL;
int uninitialized_var(error); int error = 0;
size_t slen; size_t slen;
if (!(old->d_inode->i_opflags & IOP_XATTR) || if (!(old->d_inode->i_opflags & IOP_XATTR) ||
...@@ -584,9 +584,10 @@ static int ovl_copy_up_workdir(struct ovl_copy_up_ctx *c) ...@@ -584,9 +584,10 @@ static int ovl_copy_up_workdir(struct ovl_copy_up_ctx *c)
.link = c->link .link = c->link
}; };
err = ovl_lock_rename_workdir(c->workdir, c->destdir); /* workdir and destdir could be the same when copying up to indexdir */
if (err) err = -EIO;
return err; if (lock_rename(c->workdir, c->destdir) != NULL)
goto unlock;
err = ovl_prep_cu_creds(c->dentry, &cc); err = ovl_prep_cu_creds(c->dentry, &cc);
if (err) if (err)
......
...@@ -62,35 +62,59 @@ struct dentry *ovl_lookup_temp(struct dentry *workdir) ...@@ -62,35 +62,59 @@ struct dentry *ovl_lookup_temp(struct dentry *workdir)
} }
/* caller holds i_mutex on 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; int err;
struct dentry *whiteout; struct dentry *whiteout;
struct dentry *workdir = ofs->workdir;
struct inode *wdir = workdir->d_inode; struct inode *wdir = workdir->d_inode;
whiteout = ovl_lookup_temp(workdir); if (!ofs->whiteout) {
if (IS_ERR(whiteout)) whiteout = ovl_lookup_temp(workdir);
return whiteout; if (IS_ERR(whiteout))
goto out;
err = ovl_do_whiteout(wdir, whiteout); err = ovl_do_whiteout(wdir, whiteout);
if (err) { if (err) {
dput(whiteout); dput(whiteout);
whiteout = ERR_PTR(err); 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; return whiteout;
} }
/* Caller must hold i_mutex on both workdir and dir */ /* 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 dentry *dentry)
{ {
struct inode *wdir = workdir->d_inode; struct inode *wdir = ofs->workdir->d_inode;
struct dentry *whiteout; struct dentry *whiteout;
int err; int err;
int flags = 0; int flags = 0;
whiteout = ovl_whiteout(workdir); whiteout = ovl_whiteout(ofs);
err = PTR_ERR(whiteout); err = PTR_ERR(whiteout);
if (IS_ERR(whiteout)) if (IS_ERR(whiteout))
return err; return err;
...@@ -262,6 +286,8 @@ static int ovl_instantiate(struct dentry *dentry, struct inode *inode, ...@@ -262,6 +286,8 @@ static int ovl_instantiate(struct dentry *dentry, struct inode *inode,
inode = ovl_get_inode(dentry->d_sb, &oip); inode = ovl_get_inode(dentry->d_sb, &oip);
if (IS_ERR(inode)) if (IS_ERR(inode))
return PTR_ERR(inode); return PTR_ERR(inode);
if (inode == oip.newinode)
ovl_set_flag(OVL_UPPERDATA, inode);
} else { } else {
WARN_ON(ovl_inode_real(inode) != d_inode(newdentry)); WARN_ON(ovl_inode_real(inode) != d_inode(newdentry));
dput(newdentry); dput(newdentry);
...@@ -715,6 +741,7 @@ static bool ovl_matches_upper(struct dentry *dentry, struct dentry *upper) ...@@ -715,6 +741,7 @@ static bool ovl_matches_upper(struct dentry *dentry, struct dentry *upper)
static int ovl_remove_and_whiteout(struct dentry *dentry, static int ovl_remove_and_whiteout(struct dentry *dentry,
struct list_head *list) struct list_head *list)
{ {
struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
struct dentry *workdir = ovl_workdir(dentry); struct dentry *workdir = ovl_workdir(dentry);
struct dentry *upperdir = ovl_dentry_upper(dentry->d_parent); struct dentry *upperdir = ovl_dentry_upper(dentry->d_parent);
struct dentry *upper; struct dentry *upper;
...@@ -748,7 +775,7 @@ static int ovl_remove_and_whiteout(struct dentry *dentry, ...@@ -748,7 +775,7 @@ static int ovl_remove_and_whiteout(struct dentry *dentry,
goto out_dput_upper; 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) if (err)
goto out_d_drop; goto out_d_drop;
......
...@@ -204,7 +204,7 @@ static int ovl_check_encode_origin(struct dentry *dentry) ...@@ -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 * ovl_connect_layer() will try to make origin's layer "connected" by
* copying up a "connectable" ancestor. * 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); return ovl_connect_layer(dentry);
/* Lower file handle for indexed and non-upper dir/non-dir */ /* Lower file handle for indexed and non-upper dir/non-dir */
...@@ -231,12 +231,9 @@ static int ovl_dentry_to_fid(struct dentry *dentry, u32 *fid, int buflen) ...@@ -231,12 +231,9 @@ static int ovl_dentry_to_fid(struct dentry *dentry, u32 *fid, int buflen)
if (IS_ERR(fh)) if (IS_ERR(fh))
return PTR_ERR(fh); return PTR_ERR(fh);
err = -EOVERFLOW;
len = OVL_FH_LEN(fh); len = OVL_FH_LEN(fh);
if (len > buflen) if (len <= buflen)
goto fail; memcpy(fid, fh, len);
memcpy(fid, fh, len);
err = len; err = len;
out: out:
...@@ -244,9 +241,8 @@ static int ovl_dentry_to_fid(struct dentry *dentry, u32 *fid, int buflen) ...@@ -244,9 +241,8 @@ static int ovl_dentry_to_fid(struct dentry *dentry, u32 *fid, int buflen)
return err; return err;
fail: fail:
pr_warn_ratelimited("failed to encode file handle (%pd2, err=%i, buflen=%d, len=%d, type=%d)\n", pr_warn_ratelimited("failed to encode file handle (%pd2, err=%i)\n",
dentry, err, buflen, fh ? (int)fh->fb.len : 0, dentry, err);
fh ? fh->fb.type : 0);
goto out; goto out;
} }
...@@ -254,7 +250,7 @@ static int ovl_encode_fh(struct inode *inode, u32 *fid, int *max_len, ...@@ -254,7 +250,7 @@ static int ovl_encode_fh(struct inode *inode, u32 *fid, int *max_len,
struct inode *parent) struct inode *parent)
{ {
struct dentry *dentry; struct dentry *dentry;
int bytes = *max_len << 2; int bytes, buflen = *max_len << 2;
/* TODO: encode connectable file handles */ /* TODO: encode connectable file handles */
if (parent) if (parent)
...@@ -264,12 +260,14 @@ static int ovl_encode_fh(struct inode *inode, u32 *fid, int *max_len, ...@@ -264,12 +260,14 @@ static int ovl_encode_fh(struct inode *inode, u32 *fid, int *max_len,
if (WARN_ON(!dentry)) if (WARN_ON(!dentry))
return FILEID_INVALID; return FILEID_INVALID;
bytes = ovl_dentry_to_fid(dentry, fid, bytes); bytes = ovl_dentry_to_fid(dentry, fid, buflen);
dput(dentry); dput(dentry);
if (bytes <= 0) if (bytes <= 0)
return FILEID_INVALID; return FILEID_INVALID;
*max_len = bytes >> 2; *max_len = bytes >> 2;
if (bytes > buflen)
return FILEID_INVALID;
return OVL_FILEID_V1; return OVL_FILEID_V1;
} }
...@@ -679,10 +677,10 @@ static struct dentry *ovl_upper_fh_to_d(struct super_block *sb, ...@@ -679,10 +677,10 @@ static struct dentry *ovl_upper_fh_to_d(struct super_block *sb,
struct dentry *dentry; struct dentry *dentry;
struct dentry *upper; struct dentry *upper;
if (!ofs->upper_mnt) if (!ovl_upper_mnt(ofs))
return ERR_PTR(-EACCES); 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)) if (IS_ERR_OR_NULL(upper))
return upper; return upper;
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include <linux/uio.h> #include <linux/uio.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <linux/splice.h> #include <linux/splice.h>
#include <linux/security.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/fs.h> #include <linux/fs.h>
#include "overlayfs.h" #include "overlayfs.h"
...@@ -39,10 +40,22 @@ static struct file *ovl_open_realfile(const struct file *file, ...@@ -39,10 +40,22 @@ static struct file *ovl_open_realfile(const struct file *file,
struct file *realfile; struct file *realfile;
const struct cred *old_cred; const struct cred *old_cred;
int flags = file->f_flags | O_NOATIME | FMODE_NONOTIFY; 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); old_cred = ovl_override_creds(inode->i_sb);
realfile = open_with_fake_path(&file->f_path, flags, realinode, err = inode_permission(realinode, MAY_OPEN | acc_mode);
current_cred()); 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); revert_creds(old_cred);
pr_debug("open(%p[%pD2/%c], 0%o) -> (%p, 0%o)\n", pr_debug("open(%p[%pD2/%c], 0%o) -> (%p, 0%o)\n",
...@@ -219,9 +232,8 @@ static void ovl_file_accessed(struct file *file) ...@@ -219,9 +232,8 @@ static void ovl_file_accessed(struct file *file)
touch_atime(&file->f_path); 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; rwf_t flags = 0;
if (ifl & IOCB_NOWAIT) if (ifl & IOCB_NOWAIT)
...@@ -283,7 +295,7 @@ static ssize_t ovl_read_iter(struct kiocb *iocb, struct iov_iter *iter) ...@@ -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); old_cred = ovl_override_creds(file_inode(file)->i_sb);
if (is_sync_kiocb(iocb)) { if (is_sync_kiocb(iocb)) {
ret = vfs_iter_read(real.file, iter, &iocb->ki_pos, ret = vfs_iter_read(real.file, iter, &iocb->ki_pos,
ovl_iocb_to_rwf(iocb)); ovl_iocb_to_rwf(iocb->ki_flags));
} else { } else {
struct ovl_aio_req *aio_req; struct ovl_aio_req *aio_req;
...@@ -336,7 +348,7 @@ static ssize_t ovl_write_iter(struct kiocb *iocb, struct iov_iter *iter) ...@@ -336,7 +348,7 @@ static ssize_t ovl_write_iter(struct kiocb *iocb, struct iov_iter *iter)
if (is_sync_kiocb(iocb)) { if (is_sync_kiocb(iocb)) {
file_start_write(real.file); file_start_write(real.file);
ret = vfs_iter_write(real.file, iter, &iocb->ki_pos, 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); file_end_write(real.file);
/* Update size */ /* Update size */
ovl_copyattr(ovl_inode_real(inode), inode); ovl_copyattr(ovl_inode_real(inode), inode);
...@@ -520,7 +532,9 @@ static long ovl_real_ioctl(struct file *file, unsigned int cmd, ...@@ -520,7 +532,9 @@ static long ovl_real_ioctl(struct file *file, unsigned int cmd,
return ret; return ret;
old_cred = ovl_override_creds(file_inode(file)->i_sb); 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); revert_creds(old_cred);
fdput(real); fdput(real);
......
...@@ -457,7 +457,7 @@ int ovl_update_time(struct inode *inode, struct timespec64 *ts, int flags) ...@@ -457,7 +457,7 @@ int ovl_update_time(struct inode *inode, struct timespec64 *ts, int flags)
if (flags & S_ATIME) { if (flags & S_ATIME) {
struct ovl_fs *ofs = inode->i_sb->s_fs_info; struct ovl_fs *ofs = inode->i_sb->s_fs_info;
struct path upperpath = { struct path upperpath = {
.mnt = ofs->upper_mnt, .mnt = ovl_upper_mnt(ofs),
.dentry = ovl_upperdentry_dereference(OVL_I(inode)), .dentry = ovl_upperdentry_dereference(OVL_I(inode)),
}; };
...@@ -905,7 +905,7 @@ struct inode *ovl_get_trap_inode(struct super_block *sb, struct dentry *dir) ...@@ -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? * Does overlay inode need to be hashed by lower inode?
*/ */
static bool ovl_hash_bylower(struct super_block *sb, struct dentry *upper, 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; struct ovl_fs *ofs = sb->s_fs_info;
...@@ -918,7 +918,7 @@ static bool ovl_hash_bylower(struct super_block *sb, struct dentry *upper, ...@@ -918,7 +918,7 @@ static bool ovl_hash_bylower(struct super_block *sb, struct dentry *upper,
return true; return true;
/* Yes, if won't be copied up */ /* Yes, if won't be copied up */
if (!ofs->upper_mnt) if (!ovl_upper_mnt(ofs))
return true; return true;
/* No, if lower hardlink is or will be broken on copy up */ /* No, if lower hardlink is or will be broken on copy up */
...@@ -954,7 +954,7 @@ struct inode *ovl_get_inode(struct super_block *sb, ...@@ -954,7 +954,7 @@ struct inode *ovl_get_inode(struct super_block *sb,
bool bylower = ovl_hash_bylower(sb, upperdentry, lowerdentry, bool bylower = ovl_hash_bylower(sb, upperdentry, lowerdentry,
oip->index); oip->index);
int fsid = bylower ? lowerpath->layer->fsid : 0; int fsid = bylower ? lowerpath->layer->fsid : 0;
bool is_dir, metacopy = false; bool is_dir;
unsigned long ino = 0; unsigned long ino = 0;
int err = oip->newinode ? -EEXIST : -ENOMEM; int err = oip->newinode ? -EEXIST : -ENOMEM;
...@@ -1015,15 +1015,6 @@ struct inode *ovl_get_inode(struct super_block *sb, ...@@ -1015,15 +1015,6 @@ struct inode *ovl_get_inode(struct super_block *sb,
if (oip->index) if (oip->index)
ovl_set_flag(OVL_INDEX, inode); 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; OVL_I(inode)->redirect = oip->redirect;
if (bylower) if (bylower)
......
...@@ -191,16 +191,36 @@ static bool ovl_is_opaquedir(struct dentry *dentry) ...@@ -191,16 +191,36 @@ static bool ovl_is_opaquedir(struct dentry *dentry)
return ovl_check_dir_xattr(dentry, OVL_XATTR_OPAQUE); return ovl_check_dir_xattr(dentry, OVL_XATTR_OPAQUE);
} }
static struct dentry *ovl_lookup_positive_unlocked(const char *name,
struct dentry *base, int len,
bool drop_negative)
{
struct dentry *ret = lookup_one_len_unlocked(name, base, len);
if (!IS_ERR(ret) && d_flags_negative(smp_load_acquire(&ret->d_flags))) {
if (drop_negative && ret->d_lockref.count == 1) {
spin_lock(&ret->d_lock);
/* Recheck condition under lock */
if (d_is_negative(ret) && ret->d_lockref.count == 1)
__d_drop(ret);
spin_unlock(&ret->d_lock);
}
dput(ret);
ret = ERR_PTR(-ENOENT);
}
return ret;
}
static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d, static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d,
const char *name, unsigned int namelen, const char *name, unsigned int namelen,
size_t prelen, const char *post, size_t prelen, const char *post,
struct dentry **ret) struct dentry **ret, bool drop_negative)
{ {
struct dentry *this; struct dentry *this;
int err; int err;
bool last_element = !post[0]; bool last_element = !post[0];
this = lookup_positive_unlocked(name, base, namelen); this = ovl_lookup_positive_unlocked(name, base, namelen, drop_negative);
if (IS_ERR(this)) { if (IS_ERR(this)) {
err = PTR_ERR(this); err = PTR_ERR(this);
this = NULL; this = NULL;
...@@ -276,7 +296,7 @@ static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d, ...@@ -276,7 +296,7 @@ static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d,
} }
static int ovl_lookup_layer(struct dentry *base, struct ovl_lookup_data *d, static int ovl_lookup_layer(struct dentry *base, struct ovl_lookup_data *d,
struct dentry **ret) struct dentry **ret, bool drop_negative)
{ {
/* Counting down from the end, since the prefix can change */ /* Counting down from the end, since the prefix can change */
size_t rem = d->name.len - 1; size_t rem = d->name.len - 1;
...@@ -285,7 +305,7 @@ static int ovl_lookup_layer(struct dentry *base, struct ovl_lookup_data *d, ...@@ -285,7 +305,7 @@ static int ovl_lookup_layer(struct dentry *base, struct ovl_lookup_data *d,
if (d->name.name[0] != '/') if (d->name.name[0] != '/')
return ovl_lookup_single(base, d, d->name.name, d->name.len, return ovl_lookup_single(base, d, d->name.name, d->name.len,
0, "", ret); 0, "", ret, drop_negative);
while (!IS_ERR_OR_NULL(base) && d_can_lookup(base)) { while (!IS_ERR_OR_NULL(base) && d_can_lookup(base)) {
const char *s = d->name.name + d->name.len - rem; const char *s = d->name.name + d->name.len - rem;
...@@ -298,7 +318,8 @@ static int ovl_lookup_layer(struct dentry *base, struct ovl_lookup_data *d, ...@@ -298,7 +318,8 @@ static int ovl_lookup_layer(struct dentry *base, struct ovl_lookup_data *d,
return -EIO; return -EIO;
err = ovl_lookup_single(base, d, s, thislen, err = ovl_lookup_single(base, d, s, thislen,
d->name.len - rem, next, &base); d->name.len - rem, next, &base,
drop_negative);
dput(dentry); dput(dentry);
if (err) if (err)
return err; return err;
...@@ -468,7 +489,7 @@ struct dentry *ovl_index_upper(struct ovl_fs *ofs, struct dentry *index) ...@@ -468,7 +489,7 @@ struct dentry *ovl_index_upper(struct ovl_fs *ofs, struct dentry *index)
if (IS_ERR_OR_NULL(fh)) if (IS_ERR_OR_NULL(fh))
return ERR_CAST(fh); return ERR_CAST(fh);
upper = ovl_decode_real_fh(fh, ofs->upper_mnt, true); upper = ovl_decode_real_fh(fh, ovl_upper_mnt(ofs), true);
kfree(fh); kfree(fh);
if (IS_ERR_OR_NULL(upper)) if (IS_ERR_OR_NULL(upper))
...@@ -484,12 +505,6 @@ struct dentry *ovl_index_upper(struct ovl_fs *ofs, struct dentry *index) ...@@ -484,12 +505,6 @@ struct dentry *ovl_index_upper(struct ovl_fs *ofs, struct dentry *index)
return upper; return upper;
} }
/* Is this a leftover from create/whiteout of directory index entry? */
static bool ovl_is_temp_index(struct dentry *index)
{
return index->d_name.name[0] == '#';
}
/* /*
* Verify that an index entry name matches the origin file handle stored in * Verify that an index entry name matches the origin file handle stored in
* OVL_XATTR_ORIGIN and that origin file handle can be decoded to lower path. * OVL_XATTR_ORIGIN and that origin file handle can be decoded to lower path.
...@@ -507,11 +522,6 @@ int ovl_verify_index(struct ovl_fs *ofs, struct dentry *index) ...@@ -507,11 +522,6 @@ int ovl_verify_index(struct ovl_fs *ofs, struct dentry *index)
if (!d_inode(index)) if (!d_inode(index))
return 0; return 0;
/* Cleanup leftover from index create/cleanup attempt */
err = -ESTALE;
if (ovl_is_temp_index(index))
goto fail;
err = -EINVAL; err = -EINVAL;
if (index->d_name.len < sizeof(struct ovl_fb)*2) if (index->d_name.len < sizeof(struct ovl_fb)*2)
goto fail; goto fail;
...@@ -823,7 +833,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, ...@@ -823,7 +833,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
struct dentry *this; struct dentry *this;
unsigned int i; unsigned int i;
int err; int err;
bool metacopy = false; bool uppermetacopy = false;
struct ovl_lookup_data d = { struct ovl_lookup_data d = {
.sb = dentry->d_sb, .sb = dentry->d_sb,
.name = dentry->d_name, .name = dentry->d_name,
...@@ -841,7 +851,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, ...@@ -841,7 +851,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
old_cred = ovl_override_creds(dentry->d_sb); old_cred = ovl_override_creds(dentry->d_sb);
upperdir = ovl_dentry_upper(dentry->d_parent); upperdir = ovl_dentry_upper(dentry->d_parent);
if (upperdir) { if (upperdir) {
err = ovl_lookup_layer(upperdir, &d, &upperdentry); err = ovl_lookup_layer(upperdir, &d, &upperdentry, true);
if (err) if (err)
goto out; goto out;
...@@ -869,7 +879,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, ...@@ -869,7 +879,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
goto out_put_upper; goto out_put_upper;
if (d.metacopy) if (d.metacopy)
metacopy = true; uppermetacopy = true;
} }
if (d.redirect) { if (d.redirect) {
...@@ -899,13 +909,19 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, ...@@ -899,13 +909,19 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
else else
d.last = lower.layer->idx == roe->numlower; d.last = lower.layer->idx == roe->numlower;
err = ovl_lookup_layer(lower.dentry, &d, &this); err = ovl_lookup_layer(lower.dentry, &d, &this, false);
if (err) if (err)
goto out_put; goto out_put;
if (!this) if (!this)
continue; continue;
if ((uppermetacopy || d.metacopy) && !ofs->config.metacopy) {
err = -EPERM;
pr_warn_ratelimited("refusing to follow metacopy origin for (%pd2)\n", dentry);
goto out_put;
}
/* /*
* If no origin fh is stored in upper of a merge dir, store fh * If no origin fh is stored in upper of a merge dir, store fh
* of lower dir and set upper parent "impure". * of lower dir and set upper parent "impure".
...@@ -940,21 +956,21 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, ...@@ -940,21 +956,21 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
origin = this; origin = this;
} }
if (d.metacopy)
metacopy = true;
/*
* Do not store intermediate metacopy dentries in chain,
* except top most lower metacopy dentry
*/
if (d.metacopy && ctr) { if (d.metacopy && ctr) {
/*
* Do not store intermediate metacopy dentries in
* lower chain, except top most lower metacopy dentry.
* Continue the loop so that if there is an absolute
* redirect on this dentry, poe can be reset to roe.
*/
dput(this); dput(this);
continue; this = NULL;
} else {
stack[ctr].dentry = this;
stack[ctr].layer = lower.layer;
ctr++;
} }
stack[ctr].dentry = this;
stack[ctr].layer = lower.layer;
ctr++;
/* /*
* Following redirects can have security consequences: it's like * Following redirects can have security consequences: it's like
* a symlink into the lower layer without the permission checks. * a symlink into the lower layer without the permission checks.
...@@ -982,22 +998,17 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, ...@@ -982,22 +998,17 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
} }
} }
if (metacopy) { /*
/* * For regular non-metacopy upper dentries, there is no lower
* Found a metacopy dentry but did not find corresponding * path based lookup, hence ctr will be zero. If a dentry is found
* data dentry * using ORIGIN xattr on upper, install it in stack.
*/ *
if (d.metacopy) { * For metacopy dentry, path based lookup will find lower dentries.
err = -EIO; * Just make sure a corresponding data dentry has been found.
goto out_put; */
} if (d.metacopy || (uppermetacopy && !ctr)) {
err = -EIO;
err = -EPERM; goto out_put;
if (!ofs->config.metacopy) {
pr_warn_ratelimited("refusing to follow metacopy origin for (%pd2)\n",
dentry);
goto out_put;
}
} else if (!d.is_dir && upperdentry && !ctr && origin_path) { } else if (!d.is_dir && upperdentry && !ctr && origin_path) {
if (WARN_ON(stack != NULL)) { if (WARN_ON(stack != NULL)) {
err = -EIO; err = -EIO;
...@@ -1005,25 +1016,30 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, ...@@ -1005,25 +1016,30 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
} }
stack = origin_path; stack = origin_path;
ctr = 1; ctr = 1;
origin = origin_path->dentry;
origin_path = NULL; origin_path = NULL;
} }
/* /*
* Lookup index by lower inode and verify it matches upper inode. * Always lookup index if there is no-upperdentry.
* We only trust dir index if we verified that lower dir matches *
* origin, otherwise dir index entries may be inconsistent and we * For the case of upperdentry, we have set origin by now if it
* ignore them. * needed to be set. There are basically three cases.
*
* For directories, lookup index by lower inode and verify it matches
* upper inode. We only trust dir index if we verified that lower dir
* matches origin, otherwise dir index entries may be inconsistent
* and we ignore them.
*
* For regular upper, we already set origin if upper had ORIGIN
* xattr. There is no verification though as there is no path
* based dentry lookup in lower in this case.
* *
* For non-dir upper metacopy dentry, we already set "origin" if we * For metacopy upper, we set a verified origin already if index
* verified that lower matched upper origin. If upper origin was * is enabled and if upper had an ORIGIN xattr.
* not present (because lower layer did not support fh encode/decode),
* or indexing is not enabled, do not set "origin" and skip looking up
* index. This case should be handled in same way as a non-dir upper
* without ORIGIN is handled.
* *
* Always lookup index of non-dir non-metacopy and non-upper.
*/ */
if (ctr && (!upperdentry || (!d.is_dir && !metacopy))) if (!upperdentry && ctr)
origin = stack[0].dentry; origin = stack[0].dentry;
if (origin && ovl_indexdir(dentry->d_sb) && if (origin && ovl_indexdir(dentry->d_sb) &&
...@@ -1074,6 +1090,8 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, ...@@ -1074,6 +1090,8 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
err = PTR_ERR(inode); err = PTR_ERR(inode);
if (IS_ERR(inode)) if (IS_ERR(inode))
goto out_free_oe; goto out_free_oe;
if (upperdentry && !uppermetacopy)
ovl_set_flag(OVL_UPPERDATA, inode);
} }
ovl_dentry_update_reval(dentry, upperdentry, ovl_dentry_update_reval(dentry, upperdentry,
......
...@@ -355,6 +355,9 @@ int ovl_check_fb_len(struct ovl_fb *fb, int fb_len); ...@@ -355,6 +355,9 @@ int ovl_check_fb_len(struct ovl_fb *fb, int fb_len);
static inline int ovl_check_fh_len(struct ovl_fh *fh, int fh_len) static inline int ovl_check_fh_len(struct ovl_fh *fh, int fh_len)
{ {
if (fh_len < sizeof(struct ovl_fh))
return -EINVAL;
return ovl_check_fb_len(&fh->fb, fh_len - OVL_FH_WIRE_OFFSET); return ovl_check_fb_len(&fh->fb, fh_len - OVL_FH_WIRE_OFFSET);
} }
...@@ -394,8 +397,8 @@ void ovl_cleanup_whiteouts(struct dentry *upper, struct list_head *list); ...@@ -394,8 +397,8 @@ void ovl_cleanup_whiteouts(struct dentry *upper, struct list_head *list);
void ovl_cache_free(struct list_head *list); void ovl_cache_free(struct list_head *list);
void ovl_dir_cache_free(struct inode *inode); void ovl_dir_cache_free(struct inode *inode);
int ovl_check_d_type_supported(struct path *realpath); int ovl_check_d_type_supported(struct path *realpath);
void ovl_workdir_cleanup(struct inode *dir, struct vfsmount *mnt, int ovl_workdir_cleanup(struct inode *dir, struct vfsmount *mnt,
struct dentry *dentry, int level); struct dentry *dentry, int level);
int ovl_indexdir_cleanup(struct ovl_fs *ofs); int ovl_indexdir_cleanup(struct ovl_fs *ofs);
/* inode.c */ /* inode.c */
...@@ -421,7 +424,7 @@ struct ovl_inode_params { ...@@ -421,7 +424,7 @@ struct ovl_inode_params {
struct inode *newinode; struct inode *newinode;
struct dentry *upperdentry; struct dentry *upperdentry;
struct ovl_path *lowerpath; struct ovl_path *lowerpath;
struct dentry *index; bool index;
unsigned int numlower; unsigned int numlower;
char *redirect; char *redirect;
struct dentry *lowerdata; struct dentry *lowerdata;
...@@ -455,7 +458,7 @@ static inline void ovl_copyflags(struct inode *from, struct inode *to) ...@@ -455,7 +458,7 @@ static inline void ovl_copyflags(struct inode *from, struct inode *to)
/* dir.c */ /* dir.c */
extern const struct inode_operations ovl_dir_inode_operations; extern const struct inode_operations ovl_dir_inode_operations;
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 dentry *dentry);
struct ovl_cattr { struct ovl_cattr {
dev_t rdev; dev_t rdev;
......
...@@ -46,7 +46,6 @@ struct ovl_path { ...@@ -46,7 +46,6 @@ struct ovl_path {
/* private information held for overlayfs's superblock */ /* private information held for overlayfs's superblock */
struct ovl_fs { struct ovl_fs {
struct vfsmount *upper_mnt;
unsigned int numlayer; unsigned int numlayer;
/* Number of unique fs among layers including upper fs */ /* Number of unique fs among layers including upper fs */
unsigned int numfs; unsigned int numfs;
...@@ -68,8 +67,8 @@ struct ovl_fs { ...@@ -68,8 +67,8 @@ struct ovl_fs {
/* Did we take the inuse lock? */ /* Did we take the inuse lock? */
bool upperdir_locked; bool upperdir_locked;
bool workdir_locked; bool workdir_locked;
bool share_whiteout;
/* Traps in ovl inode cache */ /* Traps in ovl inode cache */
struct inode *upperdir_trap;
struct inode *workbasedir_trap; struct inode *workbasedir_trap;
struct inode *workdir_trap; struct inode *workdir_trap;
struct inode *indexdir_trap; struct inode *indexdir_trap;
...@@ -77,8 +76,15 @@ struct ovl_fs { ...@@ -77,8 +76,15 @@ struct ovl_fs {
int xino_mode; int xino_mode;
/* For allocation of non-persistent inode numbers */ /* For allocation of non-persistent inode numbers */
atomic_long_t last_ino; atomic_long_t last_ino;
/* Whiteout dentry cache */
struct dentry *whiteout;
}; };
static inline struct vfsmount *ovl_upper_mnt(struct ovl_fs *ofs)
{
return ofs->layers[0].mnt;
}
static inline struct ovl_fs *OVL_FS(struct super_block *sb) static inline struct ovl_fs *OVL_FS(struct super_block *sb)
{ {
return (struct ovl_fs *)sb->s_fs_info; return (struct ovl_fs *)sb->s_fs_info;
......
...@@ -297,7 +297,7 @@ static inline int ovl_dir_read(struct path *realpath, ...@@ -297,7 +297,7 @@ static inline int ovl_dir_read(struct path *realpath,
struct file *realfile; struct file *realfile;
int err; int err;
realfile = ovl_path_open(realpath, O_RDONLY | O_DIRECTORY); realfile = ovl_path_open(realpath, O_RDONLY | O_LARGEFILE);
if (IS_ERR(realfile)) if (IS_ERR(realfile))
return PTR_ERR(realfile); return PTR_ERR(realfile);
...@@ -743,8 +743,10 @@ static int ovl_iterate(struct file *file, struct dir_context *ctx) ...@@ -743,8 +743,10 @@ static int ovl_iterate(struct file *file, struct dir_context *ctx)
struct ovl_dir_file *od = file->private_data; struct ovl_dir_file *od = file->private_data;
struct dentry *dentry = file->f_path.dentry; struct dentry *dentry = file->f_path.dentry;
struct ovl_cache_entry *p; struct ovl_cache_entry *p;
const struct cred *old_cred;
int err; int err;
old_cred = ovl_override_creds(dentry->d_sb);
if (!ctx->pos) if (!ctx->pos)
ovl_dir_reset(file); ovl_dir_reset(file);
...@@ -758,17 +760,20 @@ static int ovl_iterate(struct file *file, struct dir_context *ctx) ...@@ -758,17 +760,20 @@ static int ovl_iterate(struct file *file, struct dir_context *ctx)
(ovl_same_fs(dentry->d_sb) && (ovl_same_fs(dentry->d_sb) &&
(ovl_is_impure_dir(file) || (ovl_is_impure_dir(file) ||
OVL_TYPE_MERGE(ovl_path_type(dentry->d_parent))))) { OVL_TYPE_MERGE(ovl_path_type(dentry->d_parent))))) {
return ovl_iterate_real(file, ctx); err = ovl_iterate_real(file, ctx);
} else {
err = iterate_dir(od->realfile, ctx);
} }
return iterate_dir(od->realfile, ctx); goto out;
} }
if (!od->cache) { if (!od->cache) {
struct ovl_dir_cache *cache; struct ovl_dir_cache *cache;
cache = ovl_cache_get(dentry); cache = ovl_cache_get(dentry);
err = PTR_ERR(cache);
if (IS_ERR(cache)) if (IS_ERR(cache))
return PTR_ERR(cache); goto out;
od->cache = cache; od->cache = cache;
ovl_seek_cursor(od, ctx->pos); ovl_seek_cursor(od, ctx->pos);
...@@ -780,7 +785,7 @@ static int ovl_iterate(struct file *file, struct dir_context *ctx) ...@@ -780,7 +785,7 @@ static int ovl_iterate(struct file *file, struct dir_context *ctx)
if (!p->ino) { if (!p->ino) {
err = ovl_cache_update_ino(&file->f_path, p); err = ovl_cache_update_ino(&file->f_path, p);
if (err) if (err)
return err; goto out;
} }
if (!dir_emit(ctx, p->name, p->len, p->ino, p->type)) if (!dir_emit(ctx, p->name, p->len, p->ino, p->type))
break; break;
...@@ -788,7 +793,10 @@ static int ovl_iterate(struct file *file, struct dir_context *ctx) ...@@ -788,7 +793,10 @@ static int ovl_iterate(struct file *file, struct dir_context *ctx)
od->cursor = p->l_node.next; od->cursor = p->l_node.next;
ctx->pos++; ctx->pos++;
} }
return 0; err = 0;
out:
revert_creds(old_cred);
return err;
} }
static loff_t ovl_dir_llseek(struct file *file, loff_t offset, int origin) static loff_t ovl_dir_llseek(struct file *file, loff_t offset, int origin)
...@@ -831,6 +839,19 @@ static loff_t ovl_dir_llseek(struct file *file, loff_t offset, int origin) ...@@ -831,6 +839,19 @@ static loff_t ovl_dir_llseek(struct file *file, loff_t offset, int origin)
return res; return res;
} }
static struct file *ovl_dir_open_realfile(struct file *file,
struct path *realpath)
{
struct file *res;
const struct cred *old_cred;
old_cred = ovl_override_creds(file_inode(file)->i_sb);
res = ovl_path_open(realpath, O_RDONLY | (file->f_flags & O_LARGEFILE));
revert_creds(old_cred);
return res;
}
static int ovl_dir_fsync(struct file *file, loff_t start, loff_t end, static int ovl_dir_fsync(struct file *file, loff_t start, loff_t end,
int datasync) int datasync)
{ {
...@@ -853,7 +874,7 @@ static int ovl_dir_fsync(struct file *file, loff_t start, loff_t end, ...@@ -853,7 +874,7 @@ static int ovl_dir_fsync(struct file *file, loff_t start, loff_t end,
struct path upperpath; struct path upperpath;
ovl_path_upper(dentry, &upperpath); ovl_path_upper(dentry, &upperpath);
realfile = ovl_path_open(&upperpath, O_RDONLY); realfile = ovl_dir_open_realfile(file, &upperpath);
inode_lock(inode); inode_lock(inode);
if (!od->upperfile) { if (!od->upperfile) {
...@@ -904,7 +925,7 @@ static int ovl_dir_open(struct inode *inode, struct file *file) ...@@ -904,7 +925,7 @@ static int ovl_dir_open(struct inode *inode, struct file *file)
return -ENOMEM; return -ENOMEM;
type = ovl_path_real(file->f_path.dentry, &realpath); type = ovl_path_real(file->f_path.dentry, &realpath);
realfile = ovl_path_open(&realpath, file->f_flags); realfile = ovl_dir_open_realfile(file, &realpath);
if (IS_ERR(realfile)) { if (IS_ERR(realfile)) {
kfree(od); kfree(od);
return PTR_ERR(realfile); return PTR_ERR(realfile);
...@@ -1071,14 +1092,13 @@ static void ovl_workdir_cleanup_recurse(struct path *path, int level) ...@@ -1071,14 +1092,13 @@ static void ovl_workdir_cleanup_recurse(struct path *path, int level)
ovl_cache_free(&list); ovl_cache_free(&list);
} }
void ovl_workdir_cleanup(struct inode *dir, struct vfsmount *mnt, int ovl_workdir_cleanup(struct inode *dir, struct vfsmount *mnt,
struct dentry *dentry, int level) struct dentry *dentry, int level)
{ {
int err; int err;
if (!d_is_dir(dentry) || level > 1) { if (!d_is_dir(dentry) || level > 1) {
ovl_cleanup(dir, dentry); return ovl_cleanup(dir, dentry);
return;
} }
err = ovl_do_rmdir(dir, dentry); err = ovl_do_rmdir(dir, dentry);
...@@ -1088,8 +1108,10 @@ void ovl_workdir_cleanup(struct inode *dir, struct vfsmount *mnt, ...@@ -1088,8 +1108,10 @@ void ovl_workdir_cleanup(struct inode *dir, struct vfsmount *mnt,
inode_unlock(dir); inode_unlock(dir);
ovl_workdir_cleanup_recurse(&path, level + 1); ovl_workdir_cleanup_recurse(&path, level + 1);
inode_lock_nested(dir, I_MUTEX_PARENT); inode_lock_nested(dir, I_MUTEX_PARENT);
ovl_cleanup(dir, dentry); err = ovl_cleanup(dir, dentry);
} }
return err;
} }
int ovl_indexdir_cleanup(struct ovl_fs *ofs) int ovl_indexdir_cleanup(struct ovl_fs *ofs)
...@@ -1098,7 +1120,7 @@ int ovl_indexdir_cleanup(struct ovl_fs *ofs) ...@@ -1098,7 +1120,7 @@ int ovl_indexdir_cleanup(struct ovl_fs *ofs)
struct dentry *indexdir = ofs->indexdir; struct dentry *indexdir = ofs->indexdir;
struct dentry *index = NULL; struct dentry *index = NULL;
struct inode *dir = indexdir->d_inode; struct inode *dir = indexdir->d_inode;
struct path path = { .mnt = ofs->upper_mnt, .dentry = indexdir }; struct path path = { .mnt = ovl_upper_mnt(ofs), .dentry = indexdir };
LIST_HEAD(list); LIST_HEAD(list);
struct rb_root root = RB_ROOT; struct rb_root root = RB_ROOT;
struct ovl_cache_entry *p; struct ovl_cache_entry *p;
...@@ -1128,6 +1150,13 @@ int ovl_indexdir_cleanup(struct ovl_fs *ofs) ...@@ -1128,6 +1150,13 @@ int ovl_indexdir_cleanup(struct ovl_fs *ofs)
index = NULL; index = NULL;
break; break;
} }
/* Cleanup leftover from index create/cleanup attempt */
if (index->d_name.name[0] == '#') {
err = ovl_workdir_cleanup(dir, path.mnt, index, 1);
if (err)
break;
goto next;
}
err = ovl_verify_index(ofs, index); err = ovl_verify_index(ofs, index);
if (!err) { if (!err) {
goto next; goto next;
...@@ -1146,7 +1175,7 @@ int ovl_indexdir_cleanup(struct ovl_fs *ofs) ...@@ -1146,7 +1175,7 @@ int ovl_indexdir_cleanup(struct ovl_fs *ofs)
* Whiteout orphan index to block future open by * Whiteout orphan index to block future open by
* handle after overlay nlink dropped to zero. * handle after overlay nlink dropped to zero.
*/ */
err = ovl_cleanup_and_whiteout(indexdir, dir, index); err = ovl_cleanup_and_whiteout(ofs, dir, index);
} else { } else {
/* Cleanup orphan index entries */ /* Cleanup orphan index entries */
err = ovl_cleanup(dir, index); err = ovl_cleanup(dir, index);
......
This diff is collapsed.
...@@ -18,13 +18,13 @@ ...@@ -18,13 +18,13 @@
int ovl_want_write(struct dentry *dentry) int ovl_want_write(struct dentry *dentry)
{ {
struct ovl_fs *ofs = dentry->d_sb->s_fs_info; struct ovl_fs *ofs = dentry->d_sb->s_fs_info;
return mnt_want_write(ofs->upper_mnt); return mnt_want_write(ovl_upper_mnt(ofs));
} }
void ovl_drop_write(struct dentry *dentry) void ovl_drop_write(struct dentry *dentry)
{ {
struct ovl_fs *ofs = dentry->d_sb->s_fs_info; struct ovl_fs *ofs = dentry->d_sb->s_fs_info;
mnt_drop_write(ofs->upper_mnt); mnt_drop_write(ovl_upper_mnt(ofs));
} }
struct dentry *ovl_workdir(struct dentry *dentry) struct dentry *ovl_workdir(struct dentry *dentry)
...@@ -150,7 +150,7 @@ void ovl_path_upper(struct dentry *dentry, struct path *path) ...@@ -150,7 +150,7 @@ void ovl_path_upper(struct dentry *dentry, struct path *path)
{ {
struct ovl_fs *ofs = dentry->d_sb->s_fs_info; struct ovl_fs *ofs = dentry->d_sb->s_fs_info;
path->mnt = ofs->upper_mnt; path->mnt = ovl_upper_mnt(ofs);
path->dentry = ovl_dentry_upper(dentry); path->dentry = ovl_dentry_upper(dentry);
} }
...@@ -459,7 +459,32 @@ bool ovl_is_whiteout(struct dentry *dentry) ...@@ -459,7 +459,32 @@ bool ovl_is_whiteout(struct dentry *dentry)
struct file *ovl_path_open(struct path *path, int flags) struct file *ovl_path_open(struct path *path, int flags)
{ {
return dentry_open(path, flags | O_NOATIME, current_cred()); struct inode *inode = d_inode(path->dentry);
int err, acc_mode;
if (flags & ~(O_ACCMODE | O_LARGEFILE))
BUG();
switch (flags & O_ACCMODE) {
case O_RDONLY:
acc_mode = MAY_READ;
break;
case O_WRONLY:
acc_mode = MAY_WRITE;
break;
default:
BUG();
}
err = inode_permission(inode, acc_mode | MAY_OPEN);
if (err)
return ERR_PTR(err);
/* O_NOATIME is an optimization, don't fail if not permitted */
if (inode_owner_or_capable(inode))
flags |= O_NOATIME;
return dentry_open(path, flags, current_cred());
} }
/* Caller should hold ovl_inode->lock */ /* Caller should hold ovl_inode->lock */
...@@ -707,7 +732,8 @@ static void ovl_cleanup_index(struct dentry *dentry) ...@@ -707,7 +732,8 @@ static void ovl_cleanup_index(struct dentry *dentry)
index = NULL; index = NULL;
} else if (ovl_index_all(dentry->d_sb)) { } else if (ovl_index_all(dentry->d_sb)) {
/* Whiteout orphan index to block future open by handle */ /* Whiteout orphan index to block future open by handle */
err = ovl_cleanup_and_whiteout(indexdir, dir, index); err = ovl_cleanup_and_whiteout(OVL_FS(dentry->d_sb),
dir, index);
} else { } else {
/* Cleanup orphan index entries */ /* Cleanup orphan index entries */
err = ovl_cleanup(dir, index); err = ovl_cleanup(dir, index);
......
...@@ -76,7 +76,8 @@ static void sync_inodes_one_sb(struct super_block *sb, void *arg) ...@@ -76,7 +76,8 @@ static void sync_inodes_one_sb(struct super_block *sb, void *arg)
static void sync_fs_one_sb(struct super_block *sb, void *arg) static void sync_fs_one_sb(struct super_block *sb, void *arg)
{ {
if (!sb_rdonly(sb) && sb->s_op->sync_fs) if (!sb_rdonly(sb) && !(sb->s_iflags & SB_I_SKIP_SYNC) &&
sb->s_op->sync_fs)
sb->s_op->sync_fs(sb, *(int *)arg); sb->s_op->sync_fs(sb, *(int *)arg);
} }
......
...@@ -1412,6 +1412,8 @@ extern int send_sigurg(struct fown_struct *fown); ...@@ -1412,6 +1412,8 @@ extern int send_sigurg(struct fown_struct *fown);
#define SB_I_IMA_UNVERIFIABLE_SIGNATURE 0x00000020 #define SB_I_IMA_UNVERIFIABLE_SIGNATURE 0x00000020
#define SB_I_UNTRUSTED_MOUNTER 0x00000040 #define SB_I_UNTRUSTED_MOUNTER 0x00000040
#define SB_I_SKIP_SYNC 0x00000100 /* Skip superblock at global sync */
/* Possible states of 'frozen' field */ /* Possible states of 'frozen' field */
enum { enum {
SB_UNFROZEN = 0, /* FS is unfrozen */ SB_UNFROZEN = 0, /* FS is unfrozen */
......
...@@ -111,4 +111,6 @@ extern unsigned int sysctl_mount_max; ...@@ -111,4 +111,6 @@ extern unsigned int sysctl_mount_max;
extern bool path_is_mountpoint(const struct path *path); extern bool path_is_mountpoint(const struct path *path);
extern void kern_unmount_array(struct vfsmount *mnt[], unsigned int num);
#endif /* _LINUX_MOUNT_H */ #endif /* _LINUX_MOUNT_H */
...@@ -1464,6 +1464,7 @@ int security_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ...@@ -1464,6 +1464,7 @@ int security_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{ {
return call_int_hook(file_ioctl, 0, file, cmd, arg); return call_int_hook(file_ioctl, 0, file, cmd, arg);
} }
EXPORT_SYMBOL_GPL(security_file_ioctl);
static inline unsigned long mmap_prot(struct file *file, unsigned long prot) static inline unsigned long mmap_prot(struct file *file, unsigned long prot)
{ {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment