Commit 6b1c776d authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'overlayfs-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs

Pull overlayfs updates from Miklos Szeredi:
 "This work from Amir introduces the inodes index feature, which
  provides:

   - hardlinks are not broken on copy up

   - infrastructure for overlayfs NFS export

  This also fixes constant st_ino for samefs case for lower hardlinks"

* 'overlayfs-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs: (33 commits)
  ovl: mark parent impure and restore timestamp on ovl_link_up()
  ovl: document copying layers restrictions with inodes index
  ovl: cleanup orphan index entries
  ovl: persistent overlay inode nlink for indexed inodes
  ovl: implement index dir copy up
  ovl: move copy up lock out
  ovl: rearrange copy up
  ovl: add flag for upper in ovl_entry
  ovl: use struct copy_up_ctx as function argument
  ovl: base tmpfile in workdir too
  ovl: factor out ovl_copy_up_inode() helper
  ovl: extract helper to get temp file in copy up
  ovl: defer upper dir lock to tempfile link
  ovl: hash overlay non-dir inodes by copy up origin
  ovl: cleanup bad and stale index entries on mount
  ovl: lookup index entry for copy up origin
  ovl: verify index dir matches upper dir
  ovl: verify upper root dir matches lower root dir
  ovl: introduce the inodes index dir feature
  ovl: generalize ovl_create_workdir()
  ...
parents 58c7ffc0 f4439de1
...@@ -201,6 +201,40 @@ rightmost one and going left. In the above example lower1 will be the ...@@ -201,6 +201,40 @@ rightmost one and going left. In the above example lower1 will be the
top, lower2 the middle and lower3 the bottom layer. top, lower2 the middle and lower3 the bottom layer.
Sharing and copying layers
--------------------------
Lower layers may be shared among several overlay mounts and that is indeed
a very common practice. An overlay mount may use the same lower layer
path as another overlay mount and it may use a lower layer path that is
beneath or above the path of another overlay lower layer path.
Using an upper layer path and/or a workdir path that are already used by
another overlay mount is not allowed and will fail with EBUSY. Using
partially overlapping paths is not allowed but will not fail with EBUSY.
Mounting an overlay using an upper layer path, where the upper layer path
was previously used by another mounted overlay in combination with a
different lower layer path, is allowed, unless the "inodes index" feature
is enabled.
With the "inodes index" feature, on the first time mount, an NFS file
handle of the lower layer root directory, along with the UUID of the lower
filesystem, are encoded and stored in the "trusted.overlay.origin" extended
attribute on the upper layer root directory. On subsequent mount attempts,
the lower root directory file handle and lower filesystem UUID are compared
to the stored origin in upper root directory. On failure to verify the
lower root origin, mount will fail with ESTALE. An overlayfs mount with
"inodes index" enabled will fail with EOPNOTSUPP if the lower filesystem
does not support NFS export, lower filesystem does not have a valid UUID or
if the upper filesystem does not support extended attributes.
It is quite a common practice to copy overlay layers to a different
directory tree on the same or different underlying filesystem, and even
to a different machine. With the "inodes index" feature, trying to mount
the copied layers will fail the verification of the lower root file handle.
Non-standard behavior Non-standard behavior
--------------------- ---------------------
......
...@@ -23,3 +23,23 @@ config OVERLAY_FS_REDIRECT_DIR ...@@ -23,3 +23,23 @@ config OVERLAY_FS_REDIRECT_DIR
Note, that redirects are not backward compatible. That is, mounting Note, that redirects are not backward compatible. That is, mounting
an overlay which has redirects on a kernel that doesn't support this an overlay which has redirects on a kernel that doesn't support this
feature will have unexpected results. feature will have unexpected results.
config OVERLAY_FS_INDEX
bool "Overlayfs: turn on inodes index feature by default"
depends on OVERLAY_FS
help
If this config option is enabled then overlay filesystems will use
the inodes index dir to map lower inodes to upper inodes by default.
In this case it is still possible to turn off index globally with the
"index=off" module option or on a filesystem instance basis with the
"index=off" mount option.
The inodes index feature prevents breaking of lower hardlinks on copy
up.
Note, that the inodes index feature is read-only backward compatible.
That is, mounting an overlay which has an index dir on a kernel that
doesn't support this feature read-only, will not have any negative
outcomes. However, mounting the same overlay with an old kernel
read-write and then mounting it again with a new kernel, will have
unexpected results.
This diff is collapsed.
...@@ -24,7 +24,7 @@ module_param_named(redirect_max, ovl_redirect_max, ushort, 0644); ...@@ -24,7 +24,7 @@ module_param_named(redirect_max, ovl_redirect_max, ushort, 0644);
MODULE_PARM_DESC(ovl_redirect_max, MODULE_PARM_DESC(ovl_redirect_max,
"Maximum length of absolute redirect xattr value"); "Maximum length of absolute redirect xattr value");
void ovl_cleanup(struct inode *wdir, struct dentry *wdentry) int ovl_cleanup(struct inode *wdir, struct dentry *wdentry)
{ {
int err; int err;
...@@ -39,6 +39,8 @@ void ovl_cleanup(struct inode *wdir, struct dentry *wdentry) ...@@ -39,6 +39,8 @@ void ovl_cleanup(struct inode *wdir, struct dentry *wdentry)
pr_err("overlayfs: cleanup of '%pd2' failed (%i)\n", pr_err("overlayfs: cleanup of '%pd2' failed (%i)\n",
wdentry, err); wdentry, err);
} }
return err;
} }
struct dentry *ovl_lookup_temp(struct dentry *workdir) struct dentry *ovl_lookup_temp(struct dentry *workdir)
...@@ -154,12 +156,13 @@ static void ovl_instantiate(struct dentry *dentry, struct inode *inode, ...@@ -154,12 +156,13 @@ static void ovl_instantiate(struct dentry *dentry, struct inode *inode,
struct dentry *newdentry, bool hardlink) struct dentry *newdentry, bool hardlink)
{ {
ovl_dentry_version_inc(dentry->d_parent); ovl_dentry_version_inc(dentry->d_parent);
ovl_dentry_update(dentry, newdentry); ovl_dentry_set_upper_alias(dentry);
if (!hardlink) { if (!hardlink) {
ovl_inode_update(inode, d_inode(newdentry)); ovl_inode_update(inode, newdentry);
ovl_copyattr(newdentry->d_inode, inode); ovl_copyattr(newdentry->d_inode, inode);
} else { } else {
WARN_ON(ovl_inode_real(inode, NULL) != d_inode(newdentry)); WARN_ON(ovl_inode_real(inode) != d_inode(newdentry));
dput(newdentry);
inc_nlink(inode); inc_nlink(inode);
} }
d_instantiate(dentry, inode); d_instantiate(dentry, inode);
...@@ -588,6 +591,7 @@ static int ovl_link(struct dentry *old, struct inode *newdir, ...@@ -588,6 +591,7 @@ static int ovl_link(struct dentry *old, struct inode *newdir,
struct dentry *new) struct dentry *new)
{ {
int err; int err;
bool locked = false;
struct inode *inode; struct inode *inode;
err = ovl_want_write(old); err = ovl_want_write(old);
...@@ -598,6 +602,10 @@ static int ovl_link(struct dentry *old, struct inode *newdir, ...@@ -598,6 +602,10 @@ static int ovl_link(struct dentry *old, struct inode *newdir,
if (err) if (err)
goto out_drop_write; goto out_drop_write;
err = ovl_nlink_start(old, &locked);
if (err)
goto out_drop_write;
inode = d_inode(old); inode = d_inode(old);
ihold(inode); ihold(inode);
...@@ -605,12 +613,18 @@ static int ovl_link(struct dentry *old, struct inode *newdir, ...@@ -605,12 +613,18 @@ static int ovl_link(struct dentry *old, struct inode *newdir,
if (err) if (err)
iput(inode); iput(inode);
ovl_nlink_end(old, locked);
out_drop_write: out_drop_write:
ovl_drop_write(old); ovl_drop_write(old);
out: out:
return err; return err;
} }
static bool ovl_matches_upper(struct dentry *dentry, struct dentry *upper)
{
return d_inode(ovl_dentry_upper(dentry)) == d_inode(upper);
}
static int ovl_remove_and_whiteout(struct dentry *dentry, bool is_dir) static int ovl_remove_and_whiteout(struct dentry *dentry, bool is_dir)
{ {
struct dentry *workdir = ovl_workdir(dentry); struct dentry *workdir = ovl_workdir(dentry);
...@@ -646,7 +660,7 @@ static int ovl_remove_and_whiteout(struct dentry *dentry, bool is_dir) ...@@ -646,7 +660,7 @@ static int ovl_remove_and_whiteout(struct dentry *dentry, bool is_dir)
err = -ESTALE; err = -ESTALE;
if ((opaquedir && upper != opaquedir) || if ((opaquedir && upper != opaquedir) ||
(!opaquedir && ovl_dentry_upper(dentry) && (!opaquedir && ovl_dentry_upper(dentry) &&
upper != ovl_dentry_upper(dentry))) { !ovl_matches_upper(dentry, upper))) {
goto out_dput_upper; goto out_dput_upper;
} }
...@@ -707,7 +721,7 @@ static int ovl_remove_upper(struct dentry *dentry, bool is_dir) ...@@ -707,7 +721,7 @@ static int ovl_remove_upper(struct dentry *dentry, bool is_dir)
err = -ESTALE; err = -ESTALE;
if ((opaquedir && upper != opaquedir) || if ((opaquedir && upper != opaquedir) ||
(!opaquedir && upper != ovl_dentry_upper(dentry))) (!opaquedir && !ovl_matches_upper(dentry, upper)))
goto out_dput_upper; goto out_dput_upper;
if (is_dir) if (is_dir)
...@@ -735,8 +749,8 @@ static int ovl_remove_upper(struct dentry *dentry, bool is_dir) ...@@ -735,8 +749,8 @@ static int ovl_remove_upper(struct dentry *dentry, bool is_dir)
static int ovl_do_remove(struct dentry *dentry, bool is_dir) static int ovl_do_remove(struct dentry *dentry, bool is_dir)
{ {
enum ovl_path_type type;
int err; int err;
bool locked = false;
const struct cred *old_cred; const struct cred *old_cred;
err = ovl_want_write(dentry); err = ovl_want_write(dentry);
...@@ -747,7 +761,9 @@ static int ovl_do_remove(struct dentry *dentry, bool is_dir) ...@@ -747,7 +761,9 @@ static int ovl_do_remove(struct dentry *dentry, bool is_dir)
if (err) if (err)
goto out_drop_write; goto out_drop_write;
type = ovl_path_type(dentry); err = ovl_nlink_start(dentry, &locked);
if (err)
goto out_drop_write;
old_cred = ovl_override_creds(dentry->d_sb); old_cred = ovl_override_creds(dentry->d_sb);
if (!ovl_lower_positive(dentry)) if (!ovl_lower_positive(dentry))
...@@ -761,6 +777,7 @@ static int ovl_do_remove(struct dentry *dentry, bool is_dir) ...@@ -761,6 +777,7 @@ static int ovl_do_remove(struct dentry *dentry, bool is_dir)
else else
drop_nlink(dentry->d_inode); drop_nlink(dentry->d_inode);
} }
ovl_nlink_end(dentry, locked);
out_drop_write: out_drop_write:
ovl_drop_write(dentry); ovl_drop_write(dentry);
out: out:
...@@ -883,6 +900,7 @@ static int ovl_rename(struct inode *olddir, struct dentry *old, ...@@ -883,6 +900,7 @@ static int ovl_rename(struct inode *olddir, struct dentry *old,
unsigned int flags) unsigned int flags)
{ {
int err; int err;
bool locked = false;
struct dentry *old_upperdir; struct dentry *old_upperdir;
struct dentry *new_upperdir; struct dentry *new_upperdir;
struct dentry *olddentry; struct dentry *olddentry;
...@@ -926,6 +944,10 @@ static int ovl_rename(struct inode *olddir, struct dentry *old, ...@@ -926,6 +944,10 @@ static int ovl_rename(struct inode *olddir, struct dentry *old,
err = ovl_copy_up(new); err = ovl_copy_up(new);
if (err) if (err)
goto out_drop_write; goto out_drop_write;
} else {
err = ovl_nlink_start(new, &locked);
if (err)
goto out_drop_write;
} }
old_cred = ovl_override_creds(old->d_sb); old_cred = ovl_override_creds(old->d_sb);
...@@ -985,7 +1007,7 @@ static int ovl_rename(struct inode *olddir, struct dentry *old, ...@@ -985,7 +1007,7 @@ static int ovl_rename(struct inode *olddir, struct dentry *old,
goto out_unlock; goto out_unlock;
err = -ESTALE; err = -ESTALE;
if (olddentry != ovl_dentry_upper(old)) if (!ovl_matches_upper(old, olddentry))
goto out_dput_old; goto out_dput_old;
newdentry = lookup_one_len(new->d_name.name, new_upperdir, newdentry = lookup_one_len(new->d_name.name, new_upperdir,
...@@ -998,12 +1020,12 @@ static int ovl_rename(struct inode *olddir, struct dentry *old, ...@@ -998,12 +1020,12 @@ static int ovl_rename(struct inode *olddir, struct dentry *old,
new_opaque = ovl_dentry_is_opaque(new); new_opaque = ovl_dentry_is_opaque(new);
err = -ESTALE; err = -ESTALE;
if (ovl_dentry_upper(new)) { if (d_inode(new) && ovl_dentry_upper(new)) {
if (opaquedir) { if (opaquedir) {
if (newdentry != opaquedir) if (newdentry != opaquedir)
goto out_dput; goto out_dput;
} else { } else {
if (newdentry != ovl_dentry_upper(new)) if (!ovl_matches_upper(new, newdentry))
goto out_dput; goto out_dput;
} }
} else { } else {
...@@ -1046,6 +1068,13 @@ static int ovl_rename(struct inode *olddir, struct dentry *old, ...@@ -1046,6 +1068,13 @@ static int ovl_rename(struct inode *olddir, struct dentry *old,
if (cleanup_whiteout) if (cleanup_whiteout)
ovl_cleanup(old_upperdir->d_inode, newdentry); ovl_cleanup(old_upperdir->d_inode, newdentry);
if (overwrite && d_inode(new)) {
if (new_is_dir)
clear_nlink(d_inode(new));
else
drop_nlink(d_inode(new));
}
ovl_dentry_version_inc(old->d_parent); ovl_dentry_version_inc(old->d_parent);
ovl_dentry_version_inc(new->d_parent); ovl_dentry_version_inc(new->d_parent);
...@@ -1057,6 +1086,7 @@ static int ovl_rename(struct inode *olddir, struct dentry *old, ...@@ -1057,6 +1086,7 @@ static int ovl_rename(struct inode *olddir, struct dentry *old,
unlock_rename(new_upperdir, old_upperdir); unlock_rename(new_upperdir, old_upperdir);
out_revert_creds: out_revert_creds:
revert_creds(old_cred); revert_creds(old_cred);
ovl_nlink_end(new, locked);
out_drop_write: out_drop_write:
ovl_drop_write(old); ovl_drop_write(old);
out: out:
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include <linux/cred.h> #include <linux/cred.h>
#include <linux/xattr.h> #include <linux/xattr.h>
#include <linux/posix_acl.h> #include <linux/posix_acl.h>
#include <linux/ratelimit.h>
#include "overlayfs.h" #include "overlayfs.h"
int ovl_setattr(struct dentry *dentry, struct iattr *attr) int ovl_setattr(struct dentry *dentry, struct iattr *attr)
...@@ -96,11 +97,15 @@ int ovl_getattr(const struct path *path, struct kstat *stat, ...@@ -96,11 +97,15 @@ int ovl_getattr(const struct path *path, struct kstat *stat,
WARN_ON_ONCE(stat->dev != lowerstat.dev); WARN_ON_ONCE(stat->dev != lowerstat.dev);
/* /*
* Lower hardlinks are broken on copy up to different * Lower hardlinks may be broken on copy up to different
* upper files, so we cannot use the lower origin st_ino * upper files, so we cannot use the lower origin st_ino
* for those different files, even for the same fs case. * for those different files, even for the same fs case.
* With inodes index enabled, it is safe to use st_ino
* of an indexed hardlinked origin. The index validates
* that the upper hardlink is not broken.
*/ */
if (is_dir || lowerstat.nlink == 1) if (is_dir || lowerstat.nlink == 1 ||
ovl_test_flag(OVL_INDEX, d_inode(dentry)))
stat->ino = lowerstat.ino; stat->ino = lowerstat.ino;
} }
stat->dev = dentry->d_sb->s_dev; stat->dev = dentry->d_sb->s_dev;
...@@ -126,6 +131,15 @@ int ovl_getattr(const struct path *path, struct kstat *stat, ...@@ -126,6 +131,15 @@ int ovl_getattr(const struct path *path, struct kstat *stat,
if (is_dir && OVL_TYPE_MERGE(type)) if (is_dir && OVL_TYPE_MERGE(type))
stat->nlink = 1; stat->nlink = 1;
/*
* Return the overlay inode nlinks for indexed upper inodes.
* Overlay inode nlink counts the union of the upper hardlinks
* and non-covered lower hardlinks. It does not include the upper
* index hardlink.
*/
if (!is_dir && ovl_test_flag(OVL_INDEX, d_inode(dentry)))
stat->nlink = dentry->d_inode->i_nlink;
out: out:
revert_creds(old_cred); revert_creds(old_cred);
...@@ -134,8 +148,8 @@ int ovl_getattr(const struct path *path, struct kstat *stat, ...@@ -134,8 +148,8 @@ int ovl_getattr(const struct path *path, struct kstat *stat,
int ovl_permission(struct inode *inode, int mask) int ovl_permission(struct inode *inode, int mask)
{ {
bool is_upper; struct inode *upperinode = ovl_inode_upper(inode);
struct inode *realinode = ovl_inode_real(inode, &is_upper); struct inode *realinode = upperinode ?: ovl_inode_lower(inode);
const struct cred *old_cred; const struct cred *old_cred;
int err; int err;
...@@ -154,7 +168,8 @@ int ovl_permission(struct inode *inode, int mask) ...@@ -154,7 +168,8 @@ int ovl_permission(struct inode *inode, int mask)
return err; return err;
old_cred = ovl_override_creds(inode->i_sb); old_cred = ovl_override_creds(inode->i_sb);
if (!is_upper && !special_file(realinode->i_mode) && mask & MAY_WRITE) { if (!upperinode &&
!special_file(realinode->i_mode) && mask & MAY_WRITE) {
mask &= ~(MAY_WRITE | MAY_APPEND); mask &= ~(MAY_WRITE | MAY_APPEND);
/* Make sure mounter can read file for copy up later */ /* Make sure mounter can read file for copy up later */
mask |= MAY_READ; mask |= MAY_READ;
...@@ -286,7 +301,7 @@ ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size) ...@@ -286,7 +301,7 @@ ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size)
struct posix_acl *ovl_get_acl(struct inode *inode, int type) struct posix_acl *ovl_get_acl(struct inode *inode, int type)
{ {
struct inode *realinode = ovl_inode_real(inode, NULL); struct inode *realinode = ovl_inode_real(inode);
const struct cred *old_cred; const struct cred *old_cred;
struct posix_acl *acl; struct posix_acl *acl;
...@@ -300,13 +315,13 @@ struct posix_acl *ovl_get_acl(struct inode *inode, int type) ...@@ -300,13 +315,13 @@ struct posix_acl *ovl_get_acl(struct inode *inode, int type)
return acl; return acl;
} }
static bool ovl_open_need_copy_up(int flags, enum ovl_path_type type, static bool ovl_open_need_copy_up(struct dentry *dentry, int flags)
struct dentry *realdentry)
{ {
if (OVL_TYPE_UPPER(type)) if (ovl_dentry_upper(dentry) &&
ovl_dentry_has_upper_alias(dentry))
return false; return false;
if (special_file(realdentry->d_inode->i_mode)) if (special_file(d_inode(dentry)->i_mode))
return false; return false;
if (!(OPEN_FMODE(flags) & FMODE_WRITE) && !(flags & O_TRUNC)) if (!(OPEN_FMODE(flags) & FMODE_WRITE) && !(flags & O_TRUNC))
...@@ -318,11 +333,8 @@ static bool ovl_open_need_copy_up(int flags, enum ovl_path_type type, ...@@ -318,11 +333,8 @@ static bool ovl_open_need_copy_up(int flags, enum ovl_path_type type,
int ovl_open_maybe_copy_up(struct dentry *dentry, unsigned int file_flags) int ovl_open_maybe_copy_up(struct dentry *dentry, unsigned int file_flags)
{ {
int err = 0; int err = 0;
struct path realpath;
enum ovl_path_type type;
type = ovl_path_real(dentry, &realpath); if (ovl_open_need_copy_up(dentry, file_flags)) {
if (ovl_open_need_copy_up(file_flags, type, realpath.dentry)) {
err = ovl_want_write(dentry); err = ovl_want_write(dentry);
if (!err) { if (!err) {
err = ovl_copy_up_flags(dentry, file_flags); err = ovl_copy_up_flags(dentry, file_flags);
...@@ -440,6 +452,103 @@ static void ovl_fill_inode(struct inode *inode, umode_t mode, dev_t rdev) ...@@ -440,6 +452,103 @@ static void ovl_fill_inode(struct inode *inode, umode_t mode, dev_t rdev)
} }
} }
/*
* With inodes index enabled, an overlay inode nlink counts the union of upper
* hardlinks and non-covered lower hardlinks. During the lifetime of a non-pure
* upper inode, the following nlink modifying operations can happen:
*
* 1. Lower hardlink copy up
* 2. Upper hardlink created, unlinked or renamed over
* 3. Lower hardlink whiteout or renamed over
*
* For the first, copy up case, the union nlink does not change, whether the
* operation succeeds or fails, but the upper inode nlink may change.
* Therefore, before copy up, we store the union nlink value relative to the
* lower inode nlink in the index inode xattr trusted.overlay.nlink.
*
* For the second, upper hardlink case, the union nlink should be incremented
* or decremented IFF the operation succeeds, aligned with nlink change of the
* upper inode. Therefore, before link/unlink/rename, we store the union nlink
* value relative to the upper inode nlink in the index inode.
*
* For the last, lower cover up case, we simplify things by preceding the
* whiteout or cover up with copy up. This makes sure that there is an index
* upper inode where the nlink xattr can be stored before the copied up upper
* entry is unlink.
*/
#define OVL_NLINK_ADD_UPPER (1 << 0)
/*
* On-disk format for indexed nlink:
*
* nlink relative to the upper inode - "U[+-]NUM"
* nlink relative to the lower inode - "L[+-]NUM"
*/
static int ovl_set_nlink_common(struct dentry *dentry,
struct dentry *realdentry, const char *format)
{
struct inode *inode = d_inode(dentry);
struct inode *realinode = d_inode(realdentry);
char buf[13];
int len;
len = snprintf(buf, sizeof(buf), format,
(int) (inode->i_nlink - realinode->i_nlink));
return ovl_do_setxattr(ovl_dentry_upper(dentry),
OVL_XATTR_NLINK, buf, len, 0);
}
int ovl_set_nlink_upper(struct dentry *dentry)
{
return ovl_set_nlink_common(dentry, ovl_dentry_upper(dentry), "U%+i");
}
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,
struct dentry *upperdentry,
unsigned int fallback)
{
int nlink_diff;
int nlink;
char buf[13];
int err;
if (!lowerdentry || !upperdentry || d_inode(lowerdentry)->i_nlink == 1)
return fallback;
err = vfs_getxattr(upperdentry, OVL_XATTR_NLINK, &buf, sizeof(buf) - 1);
if (err < 0)
goto fail;
buf[err] = '\0';
if ((buf[0] != 'L' && buf[0] != 'U') ||
(buf[1] != '+' && buf[1] != '-'))
goto fail;
err = kstrtoint(buf + 1, 10, &nlink_diff);
if (err < 0)
goto fail;
nlink = d_inode(buf[0] == 'L' ? lowerdentry : upperdentry)->i_nlink;
nlink += nlink_diff;
if (nlink <= 0)
goto fail;
return nlink;
fail:
pr_warn_ratelimited("overlayfs: failed to get index nlink (%pd2, err=%i)\n",
upperdentry, err);
return fallback;
}
struct inode *ovl_new_inode(struct super_block *sb, umode_t mode, dev_t rdev) struct inode *ovl_new_inode(struct super_block *sb, umode_t mode, dev_t rdev)
{ {
struct inode *inode; struct inode *inode;
...@@ -453,27 +562,87 @@ struct inode *ovl_new_inode(struct super_block *sb, umode_t mode, dev_t rdev) ...@@ -453,27 +562,87 @@ struct inode *ovl_new_inode(struct super_block *sb, umode_t mode, dev_t rdev)
static int ovl_inode_test(struct inode *inode, void *data) static int ovl_inode_test(struct inode *inode, void *data)
{ {
return ovl_inode_real(inode, NULL) == data; return inode->i_private == data;
} }
static int ovl_inode_set(struct inode *inode, void *data) static int ovl_inode_set(struct inode *inode, void *data)
{ {
inode->i_private = (void *) (((unsigned long) data) | OVL_ISUPPER_MASK); inode->i_private = data;
return 0; return 0;
} }
struct inode *ovl_get_inode(struct super_block *sb, struct inode *realinode) static bool ovl_verify_inode(struct inode *inode, struct dentry *lowerdentry,
struct dentry *upperdentry)
{
struct inode *lowerinode = lowerdentry ? d_inode(lowerdentry) : NULL;
/* Lower (origin) inode must match, even if NULL */
if (ovl_inode_lower(inode) != lowerinode)
return false;
/*
* Allow non-NULL __upperdentry in inode even if upperdentry is NULL.
* This happens when finding a lower alias for a copied up hard link.
*/
if (upperdentry && ovl_inode_upper(inode) != d_inode(upperdentry))
return false;
return true;
}
struct inode *ovl_get_inode(struct dentry *dentry, struct dentry *upperdentry)
{ {
struct dentry *lowerdentry = ovl_dentry_lower(dentry);
struct inode *realinode = upperdentry ? d_inode(upperdentry) : NULL;
struct inode *inode; struct inode *inode;
inode = iget5_locked(sb, (unsigned long) realinode, if (!realinode)
ovl_inode_test, ovl_inode_set, realinode); realinode = d_inode(lowerdentry);
if (inode && inode->i_state & I_NEW) {
ovl_fill_inode(inode, realinode->i_mode, realinode->i_rdev); if (!S_ISDIR(realinode->i_mode) &&
set_nlink(inode, realinode->i_nlink); (upperdentry || (lowerdentry && ovl_indexdir(dentry->d_sb)))) {
unlock_new_inode(inode); struct inode *key = d_inode(lowerdentry ?: upperdentry);
unsigned int nlink;
inode = iget5_locked(dentry->d_sb, (unsigned long) key,
ovl_inode_test, ovl_inode_set, key);
if (!inode)
goto out_nomem;
if (!(inode->i_state & I_NEW)) {
/*
* Verify that the underlying files stored in the inode
* match those in the dentry.
*/
if (!ovl_verify_inode(inode, lowerdentry, upperdentry)) {
iput(inode);
inode = ERR_PTR(-ESTALE);
goto out;
}
dput(upperdentry);
goto out;
}
nlink = ovl_get_nlink(lowerdentry, upperdentry,
realinode->i_nlink);
set_nlink(inode, nlink);
} else {
inode = new_inode(dentry->d_sb);
if (!inode)
goto out_nomem;
} }
ovl_fill_inode(inode, realinode->i_mode, realinode->i_rdev);
ovl_inode_init(inode, upperdentry, lowerdentry);
if (upperdentry && ovl_is_impuredir(upperdentry))
ovl_set_flag(OVL_IMPURE, inode);
if (inode->i_state & I_NEW)
unlock_new_inode(inode);
out:
return inode; return inode;
out_nomem:
inode = ERR_PTR(-ENOMEM);
goto out;
} }
This diff is collapsed.
...@@ -25,6 +25,12 @@ enum ovl_path_type { ...@@ -25,6 +25,12 @@ enum ovl_path_type {
#define OVL_XATTR_REDIRECT OVL_XATTR_PREFIX "redirect" #define OVL_XATTR_REDIRECT OVL_XATTR_PREFIX "redirect"
#define OVL_XATTR_ORIGIN OVL_XATTR_PREFIX "origin" #define OVL_XATTR_ORIGIN OVL_XATTR_PREFIX "origin"
#define OVL_XATTR_IMPURE OVL_XATTR_PREFIX "impure" #define OVL_XATTR_IMPURE OVL_XATTR_PREFIX "impure"
#define OVL_XATTR_NLINK OVL_XATTR_PREFIX "nlink"
enum ovl_flag {
OVL_IMPURE,
OVL_INDEX,
};
/* /*
* The tuple (fh,uuid) is a universal unique identifier for a copy up origin, * The tuple (fh,uuid) is a universal unique identifier for a copy up origin,
...@@ -38,6 +44,8 @@ enum ovl_path_type { ...@@ -38,6 +44,8 @@ enum ovl_path_type {
/* CPU byte order required for fid decoding: */ /* CPU byte order required for fid decoding: */
#define OVL_FH_FLAG_BIG_ENDIAN (1 << 0) #define OVL_FH_FLAG_BIG_ENDIAN (1 << 0)
#define OVL_FH_FLAG_ANY_ENDIAN (1 << 1) #define OVL_FH_FLAG_ANY_ENDIAN (1 << 1)
/* Is the real inode encoded in fid an upper inode? */
#define OVL_FH_FLAG_PATH_UPPER (1 << 2)
#define OVL_FH_FLAG_ALL (OVL_FH_FLAG_BIG_ENDIAN | OVL_FH_FLAG_ANY_ENDIAN) #define OVL_FH_FLAG_ALL (OVL_FH_FLAG_BIG_ENDIAN | OVL_FH_FLAG_ANY_ENDIAN)
...@@ -60,8 +68,6 @@ struct ovl_fh { ...@@ -60,8 +68,6 @@ struct ovl_fh {
u8 fid[0]; /* file identifier */ u8 fid[0]; /* file identifier */
} __packed; } __packed;
#define OVL_ISUPPER_MASK 1UL
static inline int ovl_do_rmdir(struct inode *dir, struct dentry *dentry) static inline int ovl_do_rmdir(struct inode *dir, struct dentry *dentry)
{ {
int err = vfs_rmdir(dir, dentry); int err = vfs_rmdir(dir, dentry);
...@@ -175,22 +181,14 @@ static inline struct dentry *ovl_do_tmpfile(struct dentry *dentry, umode_t mode) ...@@ -175,22 +181,14 @@ static inline struct dentry *ovl_do_tmpfile(struct dentry *dentry, umode_t mode)
return ret; return ret;
} }
static inline struct inode *ovl_inode_real(struct inode *inode, bool *is_upper)
{
unsigned long x = (unsigned long) READ_ONCE(inode->i_private);
if (is_upper)
*is_upper = x & OVL_ISUPPER_MASK;
return (struct inode *) (x & ~OVL_ISUPPER_MASK);
}
/* util.c */ /* util.c */
int ovl_want_write(struct dentry *dentry); int ovl_want_write(struct dentry *dentry);
void ovl_drop_write(struct dentry *dentry); void ovl_drop_write(struct dentry *dentry);
struct dentry *ovl_workdir(struct dentry *dentry); struct dentry *ovl_workdir(struct dentry *dentry);
const struct cred *ovl_override_creds(struct super_block *sb); const struct cred *ovl_override_creds(struct super_block *sb);
struct super_block *ovl_same_sb(struct super_block *sb); struct super_block *ovl_same_sb(struct super_block *sb);
bool ovl_can_decode_fh(struct super_block *sb);
struct dentry *ovl_indexdir(struct super_block *sb);
struct ovl_entry *ovl_alloc_entry(unsigned int numlower); struct ovl_entry *ovl_alloc_entry(unsigned int numlower);
bool ovl_dentry_remote(struct dentry *dentry); bool ovl_dentry_remote(struct dentry *dentry);
bool ovl_dentry_weird(struct dentry *dentry); bool ovl_dentry_weird(struct dentry *dentry);
...@@ -201,19 +199,22 @@ enum ovl_path_type ovl_path_real(struct dentry *dentry, struct path *path); ...@@ -201,19 +199,22 @@ enum ovl_path_type ovl_path_real(struct dentry *dentry, struct path *path);
struct dentry *ovl_dentry_upper(struct dentry *dentry); struct dentry *ovl_dentry_upper(struct dentry *dentry);
struct dentry *ovl_dentry_lower(struct dentry *dentry); struct dentry *ovl_dentry_lower(struct dentry *dentry);
struct dentry *ovl_dentry_real(struct dentry *dentry); struct dentry *ovl_dentry_real(struct dentry *dentry);
struct inode *ovl_inode_upper(struct inode *inode);
struct inode *ovl_inode_lower(struct inode *inode);
struct inode *ovl_inode_real(struct inode *inode);
struct ovl_dir_cache *ovl_dir_cache(struct dentry *dentry); struct ovl_dir_cache *ovl_dir_cache(struct dentry *dentry);
void ovl_set_dir_cache(struct dentry *dentry, struct ovl_dir_cache *cache); void ovl_set_dir_cache(struct dentry *dentry, struct ovl_dir_cache *cache);
bool ovl_dentry_is_opaque(struct dentry *dentry); bool ovl_dentry_is_opaque(struct dentry *dentry);
bool ovl_dentry_is_impure(struct dentry *dentry);
bool ovl_dentry_is_whiteout(struct dentry *dentry); bool ovl_dentry_is_whiteout(struct dentry *dentry);
void ovl_dentry_set_opaque(struct dentry *dentry); void ovl_dentry_set_opaque(struct dentry *dentry);
bool ovl_dentry_has_upper_alias(struct dentry *dentry);
void ovl_dentry_set_upper_alias(struct dentry *dentry);
bool ovl_redirect_dir(struct super_block *sb); bool ovl_redirect_dir(struct super_block *sb);
const char *ovl_dentry_get_redirect(struct dentry *dentry); const char *ovl_dentry_get_redirect(struct dentry *dentry);
void ovl_dentry_set_redirect(struct dentry *dentry, const char *redirect); void ovl_dentry_set_redirect(struct dentry *dentry, const char *redirect);
void ovl_dentry_update(struct dentry *dentry, struct dentry *upperdentry); void ovl_inode_init(struct inode *inode, struct dentry *upperdentry,
void ovl_inode_init(struct inode *inode, struct inode *realinode, struct dentry *lowerdentry);
bool is_upper); void ovl_inode_update(struct inode *inode, struct dentry *upperdentry);
void ovl_inode_update(struct inode *inode, struct inode *upperinode);
void ovl_dentry_version_inc(struct dentry *dentry); void ovl_dentry_version_inc(struct dentry *dentry);
u64 ovl_dentry_version_get(struct dentry *dentry); u64 ovl_dentry_version_get(struct dentry *dentry);
bool ovl_is_whiteout(struct dentry *dentry); bool ovl_is_whiteout(struct dentry *dentry);
...@@ -225,6 +226,12 @@ int ovl_check_setxattr(struct dentry *dentry, struct dentry *upperdentry, ...@@ -225,6 +226,12 @@ int ovl_check_setxattr(struct dentry *dentry, struct dentry *upperdentry,
const char *name, const void *value, size_t size, const char *name, const void *value, size_t size,
int xerr); int xerr);
int ovl_set_impure(struct dentry *dentry, struct dentry *upperdentry); int ovl_set_impure(struct dentry *dentry, struct dentry *upperdentry);
void ovl_set_flag(unsigned long flag, struct inode *inode);
bool ovl_test_flag(unsigned long flag, struct inode *inode);
bool ovl_inuse_trylock(struct dentry *dentry);
void ovl_inuse_unlock(struct dentry *dentry);
int ovl_nlink_start(struct dentry *dentry, bool *locked);
void ovl_nlink_end(struct dentry *dentry, bool locked);
static inline bool ovl_is_impuredir(struct dentry *dentry) static inline bool ovl_is_impuredir(struct dentry *dentry)
{ {
...@@ -233,6 +240,11 @@ static inline bool ovl_is_impuredir(struct dentry *dentry) ...@@ -233,6 +240,11 @@ static inline bool ovl_is_impuredir(struct dentry *dentry)
/* namei.c */ /* namei.c */
int ovl_verify_origin(struct dentry *dentry, struct vfsmount *mnt,
struct dentry *origin, bool is_upper, bool set);
int ovl_verify_index(struct dentry *index, struct path *lowerstack,
unsigned int numlower);
int ovl_get_index_name(struct dentry *origin, struct qstr *name);
int ovl_path_next(int idx, struct dentry *dentry, struct path *path); int ovl_path_next(int idx, struct dentry *dentry, struct path *path);
struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags); struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags);
bool ovl_lower_positive(struct dentry *dentry); bool ovl_lower_positive(struct dentry *dentry);
...@@ -245,8 +257,15 @@ void ovl_cache_free(struct list_head *list); ...@@ -245,8 +257,15 @@ void ovl_cache_free(struct list_head *list);
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, void ovl_workdir_cleanup(struct inode *dir, struct vfsmount *mnt,
struct dentry *dentry, int level); struct dentry *dentry, int level);
int ovl_indexdir_cleanup(struct dentry *dentry, struct vfsmount *mnt,
struct path *lowerstack, unsigned int numlower);
/* inode.c */ /* 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,
struct dentry *upperdentry,
unsigned int fallback);
int ovl_setattr(struct dentry *dentry, struct iattr *attr); int ovl_setattr(struct dentry *dentry, struct iattr *attr);
int ovl_getattr(const struct path *path, struct kstat *stat, int ovl_getattr(const struct path *path, struct kstat *stat,
u32 request_mask, unsigned int flags); u32 request_mask, unsigned int flags);
...@@ -262,7 +281,7 @@ int ovl_update_time(struct inode *inode, struct timespec *ts, int flags); ...@@ -262,7 +281,7 @@ int ovl_update_time(struct inode *inode, struct timespec *ts, int flags);
bool ovl_is_private_xattr(const char *name); bool ovl_is_private_xattr(const char *name);
struct inode *ovl_new_inode(struct super_block *sb, umode_t mode, dev_t rdev); struct inode *ovl_new_inode(struct super_block *sb, umode_t mode, dev_t rdev);
struct inode *ovl_get_inode(struct super_block *sb, struct inode *realinode); struct inode *ovl_get_inode(struct dentry *dentry, struct dentry *upperdentry);
static inline void ovl_copyattr(struct inode *from, struct inode *to) static inline void ovl_copyattr(struct inode *from, struct inode *to)
{ {
to->i_uid = from->i_uid; to->i_uid = from->i_uid;
...@@ -284,10 +303,11 @@ struct cattr { ...@@ -284,10 +303,11 @@ struct cattr {
int ovl_create_real(struct inode *dir, struct dentry *newdentry, int ovl_create_real(struct inode *dir, struct dentry *newdentry,
struct cattr *attr, struct cattr *attr,
struct dentry *hardlink, bool debug); struct dentry *hardlink, bool debug);
void ovl_cleanup(struct inode *dir, struct dentry *dentry); int ovl_cleanup(struct inode *dir, struct dentry *dentry);
/* copy_up.c */ /* copy_up.c */
int ovl_copy_up(struct dentry *dentry); int ovl_copy_up(struct dentry *dentry);
int ovl_copy_up_flags(struct dentry *dentry, int flags); int ovl_copy_up_flags(struct dentry *dentry, int flags);
int ovl_copy_xattr(struct dentry *old, struct dentry *new); int ovl_copy_xattr(struct dentry *old, struct dentry *new);
int ovl_set_attr(struct dentry *upper, struct kstat *stat); int ovl_set_attr(struct dentry *upper, struct kstat *stat);
struct ovl_fh *ovl_encode_fh(struct dentry *lower, bool is_upper);
...@@ -14,6 +14,7 @@ struct ovl_config { ...@@ -14,6 +14,7 @@ struct ovl_config {
char *workdir; char *workdir;
bool default_permissions; bool default_permissions;
bool redirect_dir; bool redirect_dir;
bool index;
}; };
/* private information held for overlayfs's superblock */ /* private information held for overlayfs's superblock */
...@@ -21,7 +22,12 @@ struct ovl_fs { ...@@ -21,7 +22,12 @@ struct ovl_fs {
struct vfsmount *upper_mnt; struct vfsmount *upper_mnt;
unsigned numlower; unsigned numlower;
struct vfsmount **lower_mnt; struct vfsmount **lower_mnt;
/* workbasedir is the path at workdir= mount option */
struct dentry *workbasedir;
/* workdir is the 'work' directory under workbasedir */
struct dentry *workdir; struct dentry *workdir;
/* index directory listing overlay inodes by origin file handle */
struct dentry *indexdir;
long namelen; long namelen;
/* pathnames of lower and upper dirs, for show_options */ /* pathnames of lower and upper dirs, for show_options */
struct ovl_config config; struct ovl_config config;
...@@ -29,22 +35,16 @@ struct ovl_fs { ...@@ -29,22 +35,16 @@ struct ovl_fs {
const struct cred *creator_cred; const struct cred *creator_cred;
bool tmpfile; bool tmpfile;
bool noxattr; bool noxattr;
wait_queue_head_t copyup_wq;
/* sb common to all layers */ /* sb common to all layers */
struct super_block *same_sb; struct super_block *same_sb;
}; };
/* private information held for every overlayfs dentry */ /* private information held for every overlayfs dentry */
struct ovl_entry { struct ovl_entry {
struct dentry *__upperdentry;
struct ovl_dir_cache *cache;
union { union {
struct { struct {
u64 version; unsigned long has_upper;
const char *redirect;
bool opaque; bool opaque;
bool impure;
bool copying;
}; };
struct rcu_head rcu; struct rcu_head rcu;
}; };
...@@ -54,7 +54,25 @@ struct ovl_entry { ...@@ -54,7 +54,25 @@ struct ovl_entry {
struct ovl_entry *ovl_alloc_entry(unsigned int numlower); struct ovl_entry *ovl_alloc_entry(unsigned int numlower);
static inline struct dentry *ovl_upperdentry_dereference(struct ovl_entry *oe) struct ovl_inode {
struct ovl_dir_cache *cache;
const char *redirect;
u64 version;
unsigned long flags;
struct inode vfs_inode;
struct dentry *__upperdentry;
struct inode *lower;
/* synchronize copy up and more */
struct mutex lock;
};
static inline struct ovl_inode *OVL_I(struct inode *inode)
{
return container_of(inode, struct ovl_inode, vfs_inode);
}
static inline struct dentry *ovl_upperdentry_dereference(struct ovl_inode *oi)
{ {
return lockless_dereference(oe->__upperdentry); return lockless_dereference(oi->__upperdentry);
} }
...@@ -667,3 +667,53 @@ void ovl_workdir_cleanup(struct inode *dir, struct vfsmount *mnt, ...@@ -667,3 +667,53 @@ void ovl_workdir_cleanup(struct inode *dir, struct vfsmount *mnt,
ovl_cleanup(dir, dentry); ovl_cleanup(dir, dentry);
} }
} }
int ovl_indexdir_cleanup(struct dentry *dentry, struct vfsmount *mnt,
struct path *lowerstack, unsigned int numlower)
{
int err;
struct inode *dir = dentry->d_inode;
struct path path = { .mnt = mnt, .dentry = dentry };
LIST_HEAD(list);
struct ovl_cache_entry *p;
struct ovl_readdir_data rdd = {
.ctx.actor = ovl_fill_merge,
.dentry = NULL,
.list = &list,
.root = RB_ROOT,
.is_lowest = false,
};
err = ovl_dir_read(&path, &rdd);
if (err)
goto out;
inode_lock_nested(dir, I_MUTEX_PARENT);
list_for_each_entry(p, &list, l_node) {
struct dentry *index;
if (p->name[0] == '.') {
if (p->len == 1)
continue;
if (p->len == 2 && p->name[1] == '.')
continue;
}
index = lookup_one_len(p->name, dentry, p->len);
if (IS_ERR(index)) {
err = PTR_ERR(index);
break;
}
if (ovl_verify_index(index, lowerstack, numlower)) {
err = ovl_cleanup(dir, index);
if (err)
break;
}
dput(index);
}
inode_unlock(dir);
out:
ovl_cache_free(&list);
if (err)
pr_err("overlayfs: failed index dir cleanup (%i)\n", err);
return err;
}
This diff is collapsed.
This diff is collapsed.
...@@ -1955,6 +1955,9 @@ static inline void init_sync_kiocb(struct kiocb *kiocb, struct file *filp) ...@@ -1955,6 +1955,9 @@ static inline void init_sync_kiocb(struct kiocb *kiocb, struct file *filp)
* wb stat updates to grab mapping->tree_lock. See * wb stat updates to grab mapping->tree_lock. See
* inode_switch_wb_work_fn() for details. * inode_switch_wb_work_fn() for details.
* *
* I_OVL_INUSE Used by overlayfs to get exclusive ownership on upper
* and work dirs among overlayfs mounts.
*
* Q: What is the difference between I_WILL_FREE and I_FREEING? * Q: What is the difference between I_WILL_FREE and I_FREEING?
*/ */
#define I_DIRTY_SYNC (1 << 0) #define I_DIRTY_SYNC (1 << 0)
...@@ -1975,6 +1978,7 @@ static inline void init_sync_kiocb(struct kiocb *kiocb, struct file *filp) ...@@ -1975,6 +1978,7 @@ static inline void init_sync_kiocb(struct kiocb *kiocb, struct file *filp)
#define __I_DIRTY_TIME_EXPIRED 12 #define __I_DIRTY_TIME_EXPIRED 12
#define I_DIRTY_TIME_EXPIRED (1 << __I_DIRTY_TIME_EXPIRED) #define I_DIRTY_TIME_EXPIRED (1 << __I_DIRTY_TIME_EXPIRED)
#define I_WB_SWITCH (1 << 13) #define I_WB_SWITCH (1 << 13)
#define I_OVL_INUSE (1 << 14)
#define I_DIRTY (I_DIRTY_SYNC | I_DIRTY_DATASYNC | I_DIRTY_PAGES) #define I_DIRTY (I_DIRTY_SYNC | I_DIRTY_DATASYNC | I_DIRTY_PAGES)
#define I_DIRTY_ALL (I_DIRTY | I_DIRTY_TIME) #define I_DIRTY_ALL (I_DIRTY | I_DIRTY_TIME)
......
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