diff options
Diffstat (limited to 'fs/overlayfs/util.c')
-rw-r--r-- | fs/overlayfs/util.c | 69 |
1 files changed, 60 insertions, 9 deletions
diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c index f46ad75dc96a..d6bb1c9f5e7a 100644 --- a/fs/overlayfs/util.c +++ b/fs/overlayfs/util.c @@ -17,7 +17,6 @@ #include <linux/namei.h> #include <linux/ratelimit.h> #include "overlayfs.h" -#include "ovl_entry.h" int ovl_want_write(struct dentry *dentry) { @@ -125,7 +124,12 @@ void ovl_path_lower(struct dentry *dentry, struct path *path) { struct ovl_entry *oe = dentry->d_fsdata; - *path = oe->numlower ? oe->lowerstack[0] : (struct path) { }; + if (oe->numlower) { + path->mnt = oe->lowerstack[0].layer->mnt; + path->dentry = oe->lowerstack[0].dentry; + } else { + *path = (struct path) { }; + } } enum ovl_path_type ovl_path_real(struct dentry *dentry, struct path *path) @@ -180,14 +184,14 @@ 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 inode *inode) { - return OVL_I(d_inode(dentry))->cache; + return OVL_I(inode)->cache; } -void ovl_set_dir_cache(struct dentry *dentry, struct ovl_dir_cache *cache) +void ovl_set_dir_cache(struct inode *inode, struct ovl_dir_cache *cache) { - OVL_I(d_inode(dentry))->cache = cache; + OVL_I(inode)->cache = cache; } bool ovl_dentry_is_opaque(struct dentry *dentry) @@ -275,12 +279,19 @@ void ovl_inode_update(struct inode *inode, struct dentry *upperdentry) } } -void ovl_dentry_version_inc(struct dentry *dentry) +void ovl_dentry_version_inc(struct dentry *dentry, bool impurity) { struct inode *inode = d_inode(dentry); WARN_ON(!inode_is_locked(inode)); - OVL_I(inode)->version++; + /* + * Version is used by readdir code to keep cache consistent. For merge + * dirs all changes need to be noted. For non-merge dirs, cache only + * contains impure (ones which have been copied up and have origins) + * entries, so only need to note changes to impure entries. + */ + if (OVL_TYPE_MERGE(ovl_path_type(dentry)) || impurity) + OVL_I(inode)->version++; } u64 ovl_dentry_version_get(struct dentry *dentry) @@ -322,6 +333,19 @@ void ovl_copy_up_end(struct dentry *dentry) mutex_unlock(&OVL_I(d_inode(dentry))->lock); } +bool ovl_check_origin_xattr(struct dentry *dentry) +{ + int res; + + res = vfs_getxattr(dentry, OVL_XATTR_ORIGIN, NULL, 0); + + /* Zero size value means "copied up but origin unknown" */ + if (res >= 0) + return true; + + return false; +} + bool ovl_check_dir_xattr(struct dentry *dentry, const char *name) { int res; @@ -382,6 +406,11 @@ void ovl_set_flag(unsigned long flag, struct inode *inode) set_bit(flag, &OVL_I(inode)->flags); } +void ovl_clear_flag(unsigned long flag, struct inode *inode) +{ + clear_bit(flag, &OVL_I(inode)->flags); +} + bool ovl_test_flag(unsigned long flag, struct inode *inode) { return test_bit(flag, &OVL_I(inode)->flags); @@ -418,7 +447,7 @@ void ovl_inuse_unlock(struct dentry *dentry) } } -/* Called must hold OVL_I(inode)->oi_lock */ +/* Caller must hold OVL_I(inode)->lock */ static void ovl_cleanup_index(struct dentry *dentry) { struct inode *dir = ovl_indexdir(dentry->d_sb)->d_inode; @@ -457,6 +486,9 @@ static void ovl_cleanup_index(struct dentry *dentry) err = PTR_ERR(index); if (!IS_ERR(index)) err = ovl_cleanup(dir, index); + else + index = NULL; + inode_unlock(dir); if (err) goto fail; @@ -545,3 +577,22 @@ void ovl_nlink_end(struct dentry *dentry, bool locked) mutex_unlock(&OVL_I(d_inode(dentry))->lock); } } + +int ovl_lock_rename_workdir(struct dentry *workdir, struct dentry *upperdir) +{ + /* Workdir should not be the same as upperdir */ + if (workdir == upperdir) + goto err; + + /* Workdir should not be subdir of upperdir and vice versa */ + if (lock_rename(workdir, upperdir) != NULL) + goto err_unlock; + + return 0; + +err_unlock: + unlock_rename(workdir, upperdir); +err: + pr_err("overlayfs: failed to lock workdir+upperdir\n"); + return -EIO; +} |