Commit b04a2342 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:

 - Report constant st_ino values across copy-up even if underlying
   layers are on different filesystems, but using different st_dev
   values for each layer.

   Ideally we'd report the same st_dev across the overlay, and it's
   possible to do for filesystems that use only 32bits for st_ino by
   unifying the inum space. It would be nice if it wasn't a choice of 32
   or 64, rather filesystems could report their current maximum (that
   could change on resize, so it wouldn't be set in stone).

 - miscellaneus fixes and a cleanup of ovl_fill_super(), that was long
   overdue.

 - created a path_put_init() helper that clears out the pointers after
   putting the ref.

   I think this could be useful elsewhere, so added it to <linux/path.h>

* 'overlayfs-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs: (30 commits)
  ovl: remove unneeded arg from ovl_verify_origin()
  ovl: Put upperdentry if ovl_check_origin() fails
  ovl: rename ufs to ofs
  ovl: clean up getting lower layers
  ovl: clean up workdir creation
  ovl: clean up getting upper layer
  ovl: move ovl_get_workdir() and ovl_get_lower_layers()
  ovl: reduce the number of arguments for ovl_workdir_create()
  ovl: change order of setup in ovl_fill_super()
  ovl: factor out ovl_free_fs() helper
  ovl: grab reference to workbasedir early
  ovl: split out ovl_get_indexdir() from ovl_fill_super()
  ovl: split out ovl_get_lower_layers() from ovl_fill_super()
  ovl: split out ovl_get_workdir() from ovl_fill_super()
  ovl: split out ovl_get_upper() from ovl_fill_super()
  ovl: split out ovl_get_lowerstack() from ovl_fill_super()
  ovl: split out ovl_get_workpath() from ovl_fill_super()
  ovl: split out ovl_get_upperpath() from ovl_fill_super()
  ovl: use path_put_init() in error paths for ovl_fill_super()
  vfs: add path_put_init()
  ...
parents 5a3e0b19 d9768076
...@@ -22,7 +22,6 @@ ...@@ -22,7 +22,6 @@
#include <linux/ratelimit.h> #include <linux/ratelimit.h>
#include <linux/exportfs.h> #include <linux/exportfs.h>
#include "overlayfs.h" #include "overlayfs.h"
#include "ovl_entry.h"
#define OVL_COPY_UP_CHUNK_SIZE (1 << 20) #define OVL_COPY_UP_CHUNK_SIZE (1 << 20)
...@@ -486,6 +485,7 @@ static int ovl_copy_up_inode(struct ovl_copy_up_ctx *c, struct dentry *temp) ...@@ -486,6 +485,7 @@ static int ovl_copy_up_inode(struct ovl_copy_up_ctx *c, struct dentry *temp)
static int ovl_copy_up_locked(struct ovl_copy_up_ctx *c) static int ovl_copy_up_locked(struct ovl_copy_up_ctx *c)
{ {
struct inode *udir = c->destdir->d_inode; struct inode *udir = c->destdir->d_inode;
struct inode *inode;
struct dentry *newdentry = NULL; struct dentry *newdentry = NULL;
struct dentry *temp = NULL; struct dentry *temp = NULL;
int err; int err;
...@@ -508,7 +508,11 @@ static int ovl_copy_up_locked(struct ovl_copy_up_ctx *c) ...@@ -508,7 +508,11 @@ static int ovl_copy_up_locked(struct ovl_copy_up_ctx *c)
if (err) if (err)
goto out_cleanup; goto out_cleanup;
ovl_inode_update(d_inode(c->dentry), newdentry); inode = d_inode(c->dentry);
ovl_inode_update(inode, newdentry);
if (S_ISDIR(inode->i_mode))
ovl_set_flag(OVL_WHITEOUTS, inode);
out: out:
dput(temp); dput(temp);
return err; return err;
......
...@@ -181,6 +181,11 @@ static bool ovl_type_origin(struct dentry *dentry) ...@@ -181,6 +181,11 @@ static bool ovl_type_origin(struct dentry *dentry)
return OVL_TYPE_ORIGIN(ovl_path_type(dentry)); return OVL_TYPE_ORIGIN(ovl_path_type(dentry));
} }
static bool ovl_may_have_whiteouts(struct dentry *dentry)
{
return ovl_test_flag(OVL_WHITEOUTS, d_inode(dentry));
}
static int ovl_create_upper(struct dentry *dentry, struct inode *inode, static int ovl_create_upper(struct dentry *dentry, struct inode *inode,
struct cattr *attr, struct dentry *hardlink) struct cattr *attr, struct dentry *hardlink)
{ {
...@@ -300,7 +305,6 @@ static struct dentry *ovl_check_empty_and_clear(struct dentry *dentry) ...@@ -300,7 +305,6 @@ static struct dentry *ovl_check_empty_and_clear(struct dentry *dentry)
{ {
int err; int err;
struct dentry *ret = NULL; struct dentry *ret = NULL;
enum ovl_path_type type = ovl_path_type(dentry);
LIST_HEAD(list); LIST_HEAD(list);
err = ovl_check_empty_dir(dentry, &list); err = ovl_check_empty_dir(dentry, &list);
...@@ -313,13 +317,13 @@ static struct dentry *ovl_check_empty_and_clear(struct dentry *dentry) ...@@ -313,13 +317,13 @@ static struct dentry *ovl_check_empty_and_clear(struct dentry *dentry)
* When removing an empty opaque directory, then it makes no sense to * When removing an empty opaque directory, then it makes no sense to
* replace it with an exact replica of itself. * replace it with an exact replica of itself.
* *
* If no upperdentry then skip clearing whiteouts. * If upperdentry has whiteouts, clear them.
* *
* Can race with copy-up, since we don't hold the upperdir mutex. * Can race with copy-up, since we don't hold the upperdir mutex.
* Doesn't matter, since copy-up can't create a non-empty directory * Doesn't matter, since copy-up can't create a non-empty directory
* from an empty one. * from an empty one.
*/ */
if (OVL_TYPE_UPPER(type) && OVL_TYPE_MERGE(type)) if (!list_empty(&list))
ret = ovl_clear_empty(dentry, &list); ret = ovl_clear_empty(dentry, &list);
out_free: out_free:
...@@ -698,8 +702,9 @@ static int ovl_remove_upper(struct dentry *dentry, bool is_dir) ...@@ -698,8 +702,9 @@ static int ovl_remove_upper(struct dentry *dentry, bool is_dir)
struct dentry *opaquedir = NULL; struct dentry *opaquedir = NULL;
int err; int err;
/* Redirect dir can be !ovl_lower_positive && OVL_TYPE_MERGE */ /* Redirect/origin dir can be !ovl_lower_positive && not clean */
if (is_dir && ovl_dentry_get_redirect(dentry)) { if (is_dir && (ovl_dentry_get_redirect(dentry) ||
ovl_may_have_whiteouts(dentry))) {
opaquedir = ovl_check_empty_and_clear(dentry); opaquedir = ovl_check_empty_and_clear(dentry);
err = PTR_ERR(opaquedir); err = PTR_ERR(opaquedir);
if (IS_ERR(opaquedir)) if (IS_ERR(opaquedir))
...@@ -946,7 +951,8 @@ static int ovl_rename(struct inode *olddir, struct dentry *old, ...@@ -946,7 +951,8 @@ static int ovl_rename(struct inode *olddir, struct dentry *old,
old_cred = ovl_override_creds(old->d_sb); old_cred = ovl_override_creds(old->d_sb);
if (overwrite && new_is_dir && ovl_type_merge_or_lower(new)) { if (overwrite && new_is_dir && (ovl_type_merge_or_lower(new) ||
ovl_may_have_whiteouts(new))) {
opaquedir = ovl_check_empty_and_clear(new); opaquedir = ovl_check_empty_and_clear(new);
err = PTR_ERR(opaquedir); err = PTR_ERR(opaquedir);
if (IS_ERR(opaquedir)) { if (IS_ERR(opaquedir)) {
...@@ -1069,9 +1075,10 @@ static int ovl_rename(struct inode *olddir, struct dentry *old, ...@@ -1069,9 +1075,10 @@ static int ovl_rename(struct inode *olddir, struct dentry *old,
drop_nlink(d_inode(new)); drop_nlink(d_inode(new));
} }
ovl_dentry_version_inc(old->d_parent, ovl_dentry_version_inc(old->d_parent, ovl_type_origin(old) ||
!overwrite && ovl_type_origin(new)); (!overwrite && ovl_type_origin(new)));
ovl_dentry_version_inc(new->d_parent, ovl_type_origin(old)); ovl_dentry_version_inc(new->d_parent, ovl_type_origin(old) ||
(d_inode(new) && ovl_type_origin(new)));
out_dput: out_dput:
dput(newdentry); dput(newdentry);
......
...@@ -15,6 +15,14 @@ ...@@ -15,6 +15,14 @@
#include <linux/ratelimit.h> #include <linux/ratelimit.h>
#include "overlayfs.h" #include "overlayfs.h"
static dev_t ovl_get_pseudo_dev(struct dentry *dentry)
{
struct ovl_entry *oe = dentry->d_fsdata;
return oe->lowerstack[0].layer->pseudo_dev;
}
int ovl_setattr(struct dentry *dentry, struct iattr *attr) int ovl_setattr(struct dentry *dentry, struct iattr *attr)
{ {
int err; int err;
...@@ -66,6 +74,7 @@ int ovl_getattr(const struct path *path, struct kstat *stat, ...@@ -66,6 +74,7 @@ int ovl_getattr(const struct path *path, struct kstat *stat,
struct path realpath; struct path realpath;
const struct cred *old_cred; const struct cred *old_cred;
bool is_dir = S_ISDIR(dentry->d_inode->i_mode); bool is_dir = S_ISDIR(dentry->d_inode->i_mode);
bool samefs = ovl_same_sb(dentry->d_sb);
int err; int err;
type = ovl_path_real(dentry, &realpath); type = ovl_path_real(dentry, &realpath);
...@@ -75,16 +84,13 @@ int ovl_getattr(const struct path *path, struct kstat *stat, ...@@ -75,16 +84,13 @@ int ovl_getattr(const struct path *path, struct kstat *stat,
goto out; goto out;
/* /*
* When all layers are on the same fs, all real inode number are * For non-dir or same fs, we use st_ino of the copy up origin, if we
* unique, so we use the overlay st_dev, which is friendly to du -x. * know it. This guaranties constant st_dev/st_ino across copy up.
*
* We also use st_ino of the copy up origin, if we know it.
* This guaranties constant st_dev/st_ino across copy up.
* *
* If filesystem supports NFS export ops, this also guaranties * If filesystem supports NFS export ops, this also guaranties
* persistent st_ino across mount cycle. * persistent st_ino across mount cycle.
*/ */
if (ovl_same_sb(dentry->d_sb)) { if (!is_dir || samefs) {
if (OVL_TYPE_ORIGIN(type)) { if (OVL_TYPE_ORIGIN(type)) {
struct kstat lowerstat; struct kstat lowerstat;
u32 lowermask = STATX_INO | (!is_dir ? STATX_NLINK : 0); u32 lowermask = STATX_INO | (!is_dir ? STATX_NLINK : 0);
...@@ -95,7 +101,6 @@ int ovl_getattr(const struct path *path, struct kstat *stat, ...@@ -95,7 +101,6 @@ int ovl_getattr(const struct path *path, struct kstat *stat,
if (err) if (err)
goto out; goto out;
WARN_ON_ONCE(stat->dev != lowerstat.dev);
/* /*
* Lower hardlinks may be 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
...@@ -107,17 +112,36 @@ int ovl_getattr(const struct path *path, struct kstat *stat, ...@@ -107,17 +112,36 @@ int ovl_getattr(const struct path *path, struct kstat *stat,
if (is_dir || lowerstat.nlink == 1 || if (is_dir || lowerstat.nlink == 1 ||
ovl_test_flag(OVL_INDEX, d_inode(dentry))) ovl_test_flag(OVL_INDEX, d_inode(dentry)))
stat->ino = lowerstat.ino; stat->ino = lowerstat.ino;
if (samefs)
WARN_ON_ONCE(stat->dev != lowerstat.dev);
else
stat->dev = ovl_get_pseudo_dev(dentry);
} }
stat->dev = dentry->d_sb->s_dev; if (samefs) {
} else if (is_dir) { /*
* When all layers are on the same fs, all real inode
* number are unique, so we use the overlay st_dev,
* which is friendly to du -x.
*/
stat->dev = dentry->d_sb->s_dev;
} else if (!OVL_TYPE_UPPER(type)) {
/*
* For non-samefs setup, to make sure that st_dev/st_ino
* pair is unique across the system, we use a unique
* anonymous st_dev for lower layer inode.
*/
stat->dev = ovl_get_pseudo_dev(dentry);
}
} else {
/* /*
* If not all layers are on the same fs the pair {real st_ino;
* overlay st_dev} is not unique, so use the non persistent
* overlay st_ino.
*
* Always use the overlay st_dev for directories, so 'find * Always use the overlay st_dev for directories, so 'find
* -xdev' will scan the entire overlay mount and won't cross the * -xdev' will scan the entire overlay mount and won't cross the
* overlay mount boundaries. * overlay mount boundaries.
*
* If not all layers are on the same fs the pair {real st_ino;
* overlay st_dev} is not unique, so use the non persistent
* overlay st_ino for directories.
*/ */
stat->dev = dentry->d_sb->s_dev; stat->dev = dentry->d_sb->s_dev;
stat->ino = dentry->d_inode->i_ino; stat->ino = dentry->d_inode->i_ino;
...@@ -409,6 +433,7 @@ static inline void ovl_lockdep_annotate_inode_mutex_key(struct inode *inode) ...@@ -409,6 +433,7 @@ static inline void ovl_lockdep_annotate_inode_mutex_key(struct inode *inode)
#ifdef CONFIG_LOCKDEP #ifdef CONFIG_LOCKDEP
static struct lock_class_key ovl_i_mutex_key[OVL_MAX_NESTING]; static struct lock_class_key ovl_i_mutex_key[OVL_MAX_NESTING];
static struct lock_class_key ovl_i_mutex_dir_key[OVL_MAX_NESTING]; static struct lock_class_key ovl_i_mutex_dir_key[OVL_MAX_NESTING];
static struct lock_class_key ovl_i_lock_key[OVL_MAX_NESTING];
int depth = inode->i_sb->s_stack_depth - 1; int depth = inode->i_sb->s_stack_depth - 1;
...@@ -419,6 +444,8 @@ static inline void ovl_lockdep_annotate_inode_mutex_key(struct inode *inode) ...@@ -419,6 +444,8 @@ static inline void ovl_lockdep_annotate_inode_mutex_key(struct inode *inode)
lockdep_set_class(&inode->i_rwsem, &ovl_i_mutex_dir_key[depth]); lockdep_set_class(&inode->i_rwsem, &ovl_i_mutex_dir_key[depth]);
else else
lockdep_set_class(&inode->i_rwsem, &ovl_i_mutex_key[depth]); lockdep_set_class(&inode->i_rwsem, &ovl_i_mutex_key[depth]);
lockdep_set_class(&OVL_I(inode)->lock, &ovl_i_lock_key[depth]);
#endif #endif
} }
...@@ -657,6 +684,16 @@ struct inode *ovl_get_inode(struct dentry *dentry, struct dentry *upperdentry, ...@@ -657,6 +684,16 @@ struct inode *ovl_get_inode(struct dentry *dentry, struct dentry *upperdentry,
if (upperdentry && ovl_is_impuredir(upperdentry)) if (upperdentry && ovl_is_impuredir(upperdentry))
ovl_set_flag(OVL_IMPURE, inode); ovl_set_flag(OVL_IMPURE, inode);
/* Check for non-merge dir that may have whiteouts */
if (S_ISDIR(realinode->i_mode)) {
struct ovl_entry *oe = dentry->d_fsdata;
if (((upperdentry && lowerdentry) || oe->numlower > 1) ||
ovl_check_origin_xattr(upperdentry ?: lowerdentry)) {
ovl_set_flag(OVL_WHITEOUTS, inode);
}
}
if (inode->i_state & I_NEW) if (inode->i_state & I_NEW)
unlock_new_inode(inode); unlock_new_inode(inode);
out: out:
......
...@@ -15,7 +15,6 @@ ...@@ -15,7 +15,6 @@
#include <linux/mount.h> #include <linux/mount.h>
#include <linux/exportfs.h> #include <linux/exportfs.h>
#include "overlayfs.h" #include "overlayfs.h"
#include "ovl_entry.h"
struct ovl_lookup_data { struct ovl_lookup_data {
struct qstr name; struct qstr name;
...@@ -286,16 +285,15 @@ static int ovl_lookup_layer(struct dentry *base, struct ovl_lookup_data *d, ...@@ -286,16 +285,15 @@ static int ovl_lookup_layer(struct dentry *base, struct ovl_lookup_data *d,
static int ovl_check_origin(struct dentry *upperdentry, static int ovl_check_origin(struct dentry *upperdentry,
struct path *lowerstack, unsigned int numlower, struct ovl_path *lower, unsigned int numlower,
struct path **stackp, unsigned int *ctrp) struct ovl_path **stackp, unsigned int *ctrp)
{ {
struct vfsmount *mnt; struct vfsmount *mnt;
struct dentry *origin = NULL; struct dentry *origin = NULL;
int i; int i;
for (i = 0; i < numlower; i++) { for (i = 0; i < numlower; i++) {
mnt = lowerstack[i].mnt; mnt = lower[i].layer->mnt;
origin = ovl_get_origin(upperdentry, mnt); origin = ovl_get_origin(upperdentry, mnt);
if (IS_ERR(origin)) if (IS_ERR(origin))
return PTR_ERR(origin); return PTR_ERR(origin);
...@@ -309,12 +307,12 @@ static int ovl_check_origin(struct dentry *upperdentry, ...@@ -309,12 +307,12 @@ static int ovl_check_origin(struct dentry *upperdentry,
BUG_ON(*ctrp); BUG_ON(*ctrp);
if (!*stackp) if (!*stackp)
*stackp = kmalloc(sizeof(struct path), GFP_KERNEL); *stackp = kmalloc(sizeof(struct ovl_path), GFP_KERNEL);
if (!*stackp) { if (!*stackp) {
dput(origin); dput(origin);
return -ENOMEM; return -ENOMEM;
} }
**stackp = (struct path) { .dentry = origin, .mnt = mnt }; **stackp = (struct ovl_path){.dentry = origin, .layer = lower[i].layer};
*ctrp = 1; *ctrp = 1;
return 0; return 0;
...@@ -350,8 +348,8 @@ static int ovl_verify_origin_fh(struct dentry *dentry, const struct ovl_fh *fh) ...@@ -350,8 +348,8 @@ static int ovl_verify_origin_fh(struct dentry *dentry, const struct ovl_fh *fh)
* *
* Return 0 on match, -ESTALE on mismatch, < 0 on error. * Return 0 on match, -ESTALE on mismatch, < 0 on error.
*/ */
int ovl_verify_origin(struct dentry *dentry, struct vfsmount *mnt, int ovl_verify_origin(struct dentry *dentry, struct dentry *origin,
struct dentry *origin, bool is_upper, bool set) bool is_upper, bool set)
{ {
struct inode *inode; struct inode *inode;
struct ovl_fh *fh; struct ovl_fh *fh;
...@@ -384,13 +382,13 @@ int ovl_verify_origin(struct dentry *dentry, struct vfsmount *mnt, ...@@ -384,13 +382,13 @@ int ovl_verify_origin(struct dentry *dentry, struct vfsmount *mnt,
* 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.
* Return 0 on match, -ESTALE on mismatch or stale origin, < 0 on error. * Return 0 on match, -ESTALE on mismatch or stale origin, < 0 on error.
*/ */
int ovl_verify_index(struct dentry *index, struct path *lowerstack, int ovl_verify_index(struct dentry *index, struct ovl_path *lower,
unsigned int numlower) unsigned int numlower)
{ {
struct ovl_fh *fh = NULL; struct ovl_fh *fh = NULL;
size_t len; size_t len;
struct path origin = { }; struct ovl_path origin = { };
struct path *stack = &origin; struct ovl_path *stack = &origin;
unsigned int ctr = 0; unsigned int ctr = 0;
int err; int err;
...@@ -429,7 +427,7 @@ int ovl_verify_index(struct dentry *index, struct path *lowerstack, ...@@ -429,7 +427,7 @@ int ovl_verify_index(struct dentry *index, struct path *lowerstack,
if (err) if (err)
goto fail; goto fail;
err = ovl_check_origin(index, lowerstack, numlower, &stack, &ctr); err = ovl_check_origin(index, lower, numlower, &stack, &ctr);
if (!err && !ctr) if (!err && !ctr)
err = -ESTALE; err = -ESTALE;
if (err) if (err)
...@@ -568,11 +566,24 @@ int ovl_path_next(int idx, struct dentry *dentry, struct path *path) ...@@ -568,11 +566,24 @@ int ovl_path_next(int idx, struct dentry *dentry, struct path *path)
idx++; idx++;
} }
BUG_ON(idx > oe->numlower); BUG_ON(idx > oe->numlower);
*path = oe->lowerstack[idx - 1]; path->dentry = oe->lowerstack[idx - 1].dentry;
path->mnt = oe->lowerstack[idx - 1].layer->mnt;
return (idx < oe->numlower) ? idx + 1 : -1; return (idx < oe->numlower) ? idx + 1 : -1;
} }
static int ovl_find_layer(struct ovl_fs *ofs, struct ovl_path *path)
{
int i;
for (i = 0; i < ofs->numlower; i++) {
if (ofs->lower_layers[i].mnt == path->layer->mnt)
break;
}
return i;
}
struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
unsigned int flags) unsigned int flags)
{ {
...@@ -581,7 +592,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, ...@@ -581,7 +592,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
struct ovl_fs *ofs = dentry->d_sb->s_fs_info; struct ovl_fs *ofs = dentry->d_sb->s_fs_info;
struct ovl_entry *poe = dentry->d_parent->d_fsdata; struct ovl_entry *poe = dentry->d_parent->d_fsdata;
struct ovl_entry *roe = dentry->d_sb->s_root->d_fsdata; struct ovl_entry *roe = dentry->d_sb->s_root->d_fsdata;
struct path *stack = NULL; struct ovl_path *stack = NULL;
struct dentry *upperdir, *upperdentry = NULL; struct dentry *upperdir, *upperdentry = NULL;
struct dentry *index = NULL; struct dentry *index = NULL;
unsigned int ctr = 0; unsigned int ctr = 0;
...@@ -630,7 +641,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, ...@@ -630,7 +641,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
err = ovl_check_origin(upperdentry, roe->lowerstack, err = ovl_check_origin(upperdentry, roe->lowerstack,
roe->numlower, &stack, &ctr); roe->numlower, &stack, &ctr);
if (err) if (err)
goto out; goto out_put_upper;
} }
if (d.redirect) { if (d.redirect) {
...@@ -646,17 +657,17 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, ...@@ -646,17 +657,17 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
if (!d.stop && poe->numlower) { if (!d.stop && poe->numlower) {
err = -ENOMEM; err = -ENOMEM;
stack = kcalloc(ofs->numlower, sizeof(struct path), stack = kcalloc(ofs->numlower, sizeof(struct ovl_path),
GFP_KERNEL); GFP_KERNEL);
if (!stack) if (!stack)
goto out_put_upper; goto out_put_upper;
} }
for (i = 0; !d.stop && i < poe->numlower; i++) { for (i = 0; !d.stop && i < poe->numlower; i++) {
struct path lowerpath = poe->lowerstack[i]; struct ovl_path lower = poe->lowerstack[i];
d.last = i == poe->numlower - 1; d.last = i == poe->numlower - 1;
err = ovl_lookup_layer(lowerpath.dentry, &d, &this); err = ovl_lookup_layer(lower.dentry, &d, &this);
if (err) if (err)
goto out_put; goto out_put;
...@@ -664,7 +675,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, ...@@ -664,7 +675,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
continue; continue;
stack[ctr].dentry = this; stack[ctr].dentry = this;
stack[ctr].mnt = lowerpath.mnt; stack[ctr].layer = lower.layer;
ctr++; ctr++;
if (d.stop) if (d.stop)
...@@ -674,10 +685,8 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, ...@@ -674,10 +685,8 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
poe = roe; poe = roe;
/* Find the current layer on the root dentry */ /* Find the current layer on the root dentry */
for (i = 0; i < poe->numlower; i++) i = ovl_find_layer(ofs, &lower);
if (poe->lowerstack[i].mnt == lowerpath.mnt) if (WARN_ON(i == ofs->numlower))
break;
if (WARN_ON(i == poe->numlower))
break; break;
} }
} }
...@@ -700,7 +709,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, ...@@ -700,7 +709,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
goto out_put; goto out_put;
oe->opaque = upperopaque; oe->opaque = upperopaque;
memcpy(oe->lowerstack, stack, sizeof(struct path) * ctr); memcpy(oe->lowerstack, stack, sizeof(struct ovl_path) * ctr);
dentry->d_fsdata = oe; dentry->d_fsdata = oe;
if (upperdentry) if (upperdentry)
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/uuid.h> #include <linux/uuid.h>
#include "ovl_entry.h"
enum ovl_path_type { enum ovl_path_type {
__OVL_PATH_UPPER = (1 << 0), __OVL_PATH_UPPER = (1 << 0),
...@@ -28,7 +29,10 @@ enum ovl_path_type { ...@@ -28,7 +29,10 @@ enum ovl_path_type {
#define OVL_XATTR_NLINK OVL_XATTR_PREFIX "nlink" #define OVL_XATTR_NLINK OVL_XATTR_PREFIX "nlink"
enum ovl_flag { enum ovl_flag {
/* Pure upper dir that may contain non pure upper entries */
OVL_IMPURE, OVL_IMPURE,
/* Non-merge dir that may contain whiteout entries */
OVL_WHITEOUTS,
OVL_INDEX, OVL_INDEX,
}; };
...@@ -223,6 +227,7 @@ bool ovl_is_whiteout(struct dentry *dentry); ...@@ -223,6 +227,7 @@ 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);
int ovl_copy_up_start(struct dentry *dentry); int ovl_copy_up_start(struct dentry *dentry);
void ovl_copy_up_end(struct dentry *dentry); void ovl_copy_up_end(struct dentry *dentry);
bool ovl_check_origin_xattr(struct dentry *dentry);
bool ovl_check_dir_xattr(struct dentry *dentry, const char *name); bool ovl_check_dir_xattr(struct dentry *dentry, const char *name);
int ovl_check_setxattr(struct dentry *dentry, struct dentry *upperdentry, 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,
...@@ -244,9 +249,9 @@ static inline bool ovl_is_impuredir(struct dentry *dentry) ...@@ -244,9 +249,9 @@ static inline bool ovl_is_impuredir(struct dentry *dentry)
/* namei.c */ /* namei.c */
int ovl_verify_origin(struct dentry *dentry, struct vfsmount *mnt, int ovl_verify_origin(struct dentry *dentry, struct dentry *origin,
struct dentry *origin, bool is_upper, bool set); bool is_upper, bool set);
int ovl_verify_index(struct dentry *index, struct path *lowerstack, int ovl_verify_index(struct dentry *index, struct ovl_path *lower,
unsigned int numlower); unsigned int numlower);
int ovl_get_index_name(struct dentry *origin, struct qstr *name); 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);
...@@ -263,7 +268,7 @@ int ovl_check_d_type_supported(struct path *realpath); ...@@ -263,7 +268,7 @@ 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, int ovl_indexdir_cleanup(struct dentry *dentry, struct vfsmount *mnt,
struct path *lowerstack, unsigned int numlower); struct ovl_path *lower, unsigned int numlower);
/* inode.c */ /* inode.c */
int ovl_set_nlink_upper(struct dentry *dentry); int ovl_set_nlink_upper(struct dentry *dentry);
......
...@@ -17,11 +17,21 @@ struct ovl_config { ...@@ -17,11 +17,21 @@ struct ovl_config {
bool index; bool index;
}; };
struct ovl_layer {
struct vfsmount *mnt;
dev_t pseudo_dev;
};
struct ovl_path {
struct ovl_layer *layer;
struct dentry *dentry;
};
/* private information held for overlayfs's superblock */ /* private information held for overlayfs's superblock */
struct ovl_fs { struct ovl_fs {
struct vfsmount *upper_mnt; struct vfsmount *upper_mnt;
unsigned numlower; unsigned numlower;
struct vfsmount **lower_mnt; struct ovl_layer *lower_layers;
/* workbasedir is the path at workdir= mount option */ /* workbasedir is the path at workdir= mount option */
struct dentry *workbasedir; struct dentry *workbasedir;
/* workdir is the 'work' directory under workbasedir */ /* workdir is the 'work' directory under workbasedir */
...@@ -52,7 +62,7 @@ struct ovl_entry { ...@@ -52,7 +62,7 @@ struct ovl_entry {
struct rcu_head rcu; struct rcu_head rcu;
}; };
unsigned numlower; unsigned numlower;
struct path lowerstack[]; struct ovl_path lowerstack[];
}; };
struct ovl_entry *ovl_alloc_entry(unsigned int numlower); struct ovl_entry *ovl_alloc_entry(unsigned int numlower);
......
...@@ -26,6 +26,7 @@ struct ovl_cache_entry { ...@@ -26,6 +26,7 @@ struct ovl_cache_entry {
struct list_head l_node; struct list_head l_node;
struct rb_node node; struct rb_node node;
struct ovl_cache_entry *next_maybe_whiteout; struct ovl_cache_entry *next_maybe_whiteout;
bool is_upper;
bool is_whiteout; bool is_whiteout;
char name[]; char name[];
}; };
...@@ -158,6 +159,7 @@ static struct ovl_cache_entry *ovl_cache_entry_new(struct ovl_readdir_data *rdd, ...@@ -158,6 +159,7 @@ static struct ovl_cache_entry *ovl_cache_entry_new(struct ovl_readdir_data *rdd,
/* Defer setting d_ino for upper entry to ovl_iterate() */ /* Defer setting d_ino for upper entry to ovl_iterate() */
if (ovl_calc_d_ino(rdd, p)) if (ovl_calc_d_ino(rdd, p))
p->ino = 0; p->ino = 0;
p->is_upper = rdd->is_upper;
p->is_whiteout = false; p->is_whiteout = false;
if (d_type == DT_CHR) { if (d_type == DT_CHR) {
...@@ -316,21 +318,37 @@ static inline int ovl_dir_read(struct path *realpath, ...@@ -316,21 +318,37 @@ static inline int ovl_dir_read(struct path *realpath,
return err; return err;
} }
/*
* Can we iterate real dir directly?
*
* Non-merge dir may contain whiteouts from a time it was a merge upper, before
* lower dir was removed under it and possibly before it was rotated from upper
* to lower layer.
*/
static bool ovl_dir_is_real(struct dentry *dir)
{
return !ovl_test_flag(OVL_WHITEOUTS, d_inode(dir));
}
static void ovl_dir_reset(struct file *file) static void ovl_dir_reset(struct file *file)
{ {
struct ovl_dir_file *od = file->private_data; struct ovl_dir_file *od = file->private_data;
struct ovl_dir_cache *cache = od->cache; struct ovl_dir_cache *cache = od->cache;
struct dentry *dentry = file->f_path.dentry; struct dentry *dentry = file->f_path.dentry;
enum ovl_path_type type = ovl_path_type(dentry); bool is_real;
if (cache && ovl_dentry_version_get(dentry) != cache->version) { if (cache && ovl_dentry_version_get(dentry) != cache->version) {
ovl_cache_put(od, dentry); ovl_cache_put(od, dentry);
od->cache = NULL; od->cache = NULL;
od->cursor = NULL; od->cursor = NULL;
} }
WARN_ON(!od->is_real && !OVL_TYPE_MERGE(type)); is_real = ovl_dir_is_real(dentry);
if (od->is_real && OVL_TYPE_MERGE(type)) if (od->is_real != is_real) {
/* is_real can only become false when dir is copied up */
if (WARN_ON(is_real))
return;
od->is_real = false; od->is_real = false;
}
} }
static int ovl_dir_read_merged(struct dentry *dentry, struct list_head *list, static int ovl_dir_read_merged(struct dentry *dentry, struct list_head *list,
...@@ -816,7 +834,7 @@ static int ovl_dir_open(struct inode *inode, struct file *file) ...@@ -816,7 +834,7 @@ static int ovl_dir_open(struct inode *inode, struct file *file)
return PTR_ERR(realfile); return PTR_ERR(realfile);
} }
od->realfile = realfile; od->realfile = realfile;
od->is_real = !OVL_TYPE_MERGE(type); od->is_real = ovl_dir_is_real(file->f_path.dentry);
od->is_upper = OVL_TYPE_UPPER(type); od->is_upper = OVL_TYPE_UPPER(type);
file->private_data = od; file->private_data = od;
...@@ -835,7 +853,7 @@ const struct file_operations ovl_dir_operations = { ...@@ -835,7 +853,7 @@ const struct file_operations ovl_dir_operations = {
int ovl_check_empty_dir(struct dentry *dentry, struct list_head *list) int ovl_check_empty_dir(struct dentry *dentry, struct list_head *list)
{ {
int err; int err;
struct ovl_cache_entry *p; struct ovl_cache_entry *p, *n;
struct rb_root root = RB_ROOT; struct rb_root root = RB_ROOT;
err = ovl_dir_read_merged(dentry, list, &root); err = ovl_dir_read_merged(dentry, list, &root);
...@@ -844,18 +862,29 @@ int ovl_check_empty_dir(struct dentry *dentry, struct list_head *list) ...@@ -844,18 +862,29 @@ int ovl_check_empty_dir(struct dentry *dentry, struct list_head *list)
err = 0; err = 0;
list_for_each_entry(p, list, l_node) { list_for_each_entry_safe(p, n, list, l_node) {
if (p->is_whiteout) /*
continue; * Select whiteouts in upperdir, they should
* be cleared when deleting this directory.
*/
if (p->is_whiteout) {
if (p->is_upper)
continue;
goto del_entry;
}
if (p->name[0] == '.') { if (p->name[0] == '.') {
if (p->len == 1) if (p->len == 1)
continue; goto del_entry;
if (p->len == 2 && p->name[1] == '.') if (p->len == 2 && p->name[1] == '.')
continue; goto del_entry;
} }
err = -ENOTEMPTY; err = -ENOTEMPTY;
break; break;
del_entry:
list_del(&p->l_node);
kfree(p);
} }
return err; return err;
...@@ -869,7 +898,7 @@ void ovl_cleanup_whiteouts(struct dentry *upper, struct list_head *list) ...@@ -869,7 +898,7 @@ void ovl_cleanup_whiteouts(struct dentry *upper, struct list_head *list)
list_for_each_entry(p, list, l_node) { list_for_each_entry(p, list, l_node) {
struct dentry *dentry; struct dentry *dentry;
if (!p->is_whiteout) if (WARN_ON(!p->is_whiteout || !p->is_upper))
continue; continue;
dentry = lookup_one_len(p->name, upper, p->len); dentry = lookup_one_len(p->name, upper, p->len);
...@@ -985,7 +1014,7 @@ void ovl_workdir_cleanup(struct inode *dir, struct vfsmount *mnt, ...@@ -985,7 +1014,7 @@ void ovl_workdir_cleanup(struct inode *dir, struct vfsmount *mnt,
} }
int ovl_indexdir_cleanup(struct dentry *dentry, struct vfsmount *mnt, int ovl_indexdir_cleanup(struct dentry *dentry, struct vfsmount *mnt,
struct path *lowerstack, unsigned int numlower) struct ovl_path *lower, unsigned int numlower)
{ {
int err; int err;
struct dentry *index = NULL; struct dentry *index = NULL;
...@@ -1020,7 +1049,7 @@ int ovl_indexdir_cleanup(struct dentry *dentry, struct vfsmount *mnt, ...@@ -1020,7 +1049,7 @@ int ovl_indexdir_cleanup(struct dentry *dentry, struct vfsmount *mnt,
index = NULL; index = NULL;
break; break;
} }
err = ovl_verify_index(index, lowerstack, numlower); err = ovl_verify_index(index, lower, numlower);
/* Cleanup stale and orphan index entries */ /* Cleanup stale and orphan index entries */
if (err && (err == -ESTALE || err == -ENOENT)) if (err && (err == -ESTALE || err == -ENOENT))
err = ovl_cleanup(dir, index); err = ovl_cleanup(dir, index);
......
...@@ -18,7 +18,6 @@ ...@@ -18,7 +18,6 @@
#include <linux/seq_file.h> #include <linux/seq_file.h>
#include <linux/posix_acl_xattr.h> #include <linux/posix_acl_xattr.h>
#include "overlayfs.h" #include "overlayfs.h"
#include "ovl_entry.h"
MODULE_AUTHOR("Miklos Szeredi <miklos@szeredi.hu>"); MODULE_AUTHOR("Miklos Szeredi <miklos@szeredi.hu>");
MODULE_DESCRIPTION("Overlay filesystem"); MODULE_DESCRIPTION("Overlay filesystem");
...@@ -39,15 +38,20 @@ module_param_named(index, ovl_index_def, bool, 0644); ...@@ -39,15 +38,20 @@ module_param_named(index, ovl_index_def, bool, 0644);
MODULE_PARM_DESC(ovl_index_def, MODULE_PARM_DESC(ovl_index_def,
"Default to on or off for the inodes index feature"); "Default to on or off for the inodes index feature");
static void ovl_entry_stack_free(struct ovl_entry *oe)
{
unsigned int i;
for (i = 0; i < oe->numlower; i++)
dput(oe->lowerstack[i].dentry);
}
static void ovl_dentry_release(struct dentry *dentry) static void ovl_dentry_release(struct dentry *dentry)
{ {
struct ovl_entry *oe = dentry->d_fsdata; struct ovl_entry *oe = dentry->d_fsdata;
if (oe) { if (oe) {
unsigned int i; ovl_entry_stack_free(oe);
for (i = 0; i < oe->numlower; i++)
dput(oe->lowerstack[i].dentry);
kfree_rcu(oe, rcu); kfree_rcu(oe, rcu);
} }
} }
...@@ -207,39 +211,48 @@ static void ovl_destroy_inode(struct inode *inode) ...@@ -207,39 +211,48 @@ static void ovl_destroy_inode(struct inode *inode)
call_rcu(&inode->i_rcu, ovl_i_callback); call_rcu(&inode->i_rcu, ovl_i_callback);
} }
static void ovl_put_super(struct super_block *sb) static void ovl_free_fs(struct ovl_fs *ofs)
{ {
struct ovl_fs *ufs = sb->s_fs_info;
unsigned i; unsigned i;
dput(ufs->indexdir); dput(ofs->indexdir);
dput(ufs->workdir); dput(ofs->workdir);
if (ufs->workdir_locked) if (ofs->workdir_locked)
ovl_inuse_unlock(ufs->workbasedir); ovl_inuse_unlock(ofs->workbasedir);
dput(ufs->workbasedir); dput(ofs->workbasedir);
if (ufs->upper_mnt && ufs->upperdir_locked) if (ofs->upperdir_locked)
ovl_inuse_unlock(ufs->upper_mnt->mnt_root); ovl_inuse_unlock(ofs->upper_mnt->mnt_root);
mntput(ufs->upper_mnt); mntput(ofs->upper_mnt);
for (i = 0; i < ufs->numlower; i++) for (i = 0; i < ofs->numlower; i++) {
mntput(ufs->lower_mnt[i]); mntput(ofs->lower_layers[i].mnt);
kfree(ufs->lower_mnt); free_anon_bdev(ofs->lower_layers[i].pseudo_dev);
}
kfree(ufs->config.lowerdir); kfree(ofs->lower_layers);
kfree(ufs->config.upperdir);
kfree(ufs->config.workdir); kfree(ofs->config.lowerdir);
put_cred(ufs->creator_cred); kfree(ofs->config.upperdir);
kfree(ufs); kfree(ofs->config.workdir);
if (ofs->creator_cred)
put_cred(ofs->creator_cred);
kfree(ofs);
}
static void ovl_put_super(struct super_block *sb)
{
struct ovl_fs *ofs = sb->s_fs_info;
ovl_free_fs(ofs);
} }
static int ovl_sync_fs(struct super_block *sb, int wait) static int ovl_sync_fs(struct super_block *sb, int wait)
{ {
struct ovl_fs *ufs = sb->s_fs_info; struct ovl_fs *ofs = sb->s_fs_info;
struct super_block *upper_sb; struct super_block *upper_sb;
int ret; int ret;
if (!ufs->upper_mnt) if (!ofs->upper_mnt)
return 0; return 0;
upper_sb = ufs->upper_mnt->mnt_sb; upper_sb = ofs->upper_mnt->mnt_sb;
if (!upper_sb->s_op->sync_fs) if (!upper_sb->s_op->sync_fs)
return 0; return 0;
...@@ -277,9 +290,9 @@ static int ovl_statfs(struct dentry *dentry, struct kstatfs *buf) ...@@ -277,9 +290,9 @@ static int ovl_statfs(struct dentry *dentry, struct kstatfs *buf)
} }
/* Will this overlay be forced to mount/remount ro? */ /* Will this overlay be forced to mount/remount ro? */
static bool ovl_force_readonly(struct ovl_fs *ufs) static bool ovl_force_readonly(struct ovl_fs *ofs)
{ {
return (!ufs->upper_mnt || !ufs->workdir); return (!ofs->upper_mnt || !ofs->workdir);
} }
/** /**
...@@ -291,29 +304,29 @@ static bool ovl_force_readonly(struct ovl_fs *ufs) ...@@ -291,29 +304,29 @@ static bool ovl_force_readonly(struct ovl_fs *ufs)
static int ovl_show_options(struct seq_file *m, struct dentry *dentry) static int ovl_show_options(struct seq_file *m, struct dentry *dentry)
{ {
struct super_block *sb = dentry->d_sb; struct super_block *sb = dentry->d_sb;
struct ovl_fs *ufs = sb->s_fs_info; struct ovl_fs *ofs = sb->s_fs_info;
seq_show_option(m, "lowerdir", ufs->config.lowerdir); seq_show_option(m, "lowerdir", ofs->config.lowerdir);
if (ufs->config.upperdir) { if (ofs->config.upperdir) {
seq_show_option(m, "upperdir", ufs->config.upperdir); seq_show_option(m, "upperdir", ofs->config.upperdir);
seq_show_option(m, "workdir", ufs->config.workdir); seq_show_option(m, "workdir", ofs->config.workdir);
} }
if (ufs->config.default_permissions) if (ofs->config.default_permissions)
seq_puts(m, ",default_permissions"); seq_puts(m, ",default_permissions");
if (ufs->config.redirect_dir != ovl_redirect_dir_def) if (ofs->config.redirect_dir != ovl_redirect_dir_def)
seq_printf(m, ",redirect_dir=%s", seq_printf(m, ",redirect_dir=%s",
ufs->config.redirect_dir ? "on" : "off"); ofs->config.redirect_dir ? "on" : "off");
if (ufs->config.index != ovl_index_def) if (ofs->config.index != ovl_index_def)
seq_printf(m, ",index=%s", seq_printf(m, ",index=%s",
ufs->config.index ? "on" : "off"); ofs->config.index ? "on" : "off");
return 0; return 0;
} }
static int ovl_remount(struct super_block *sb, int *flags, char *data) static int ovl_remount(struct super_block *sb, int *flags, char *data)
{ {
struct ovl_fs *ufs = sb->s_fs_info; struct ovl_fs *ofs = sb->s_fs_info;
if (!(*flags & MS_RDONLY) && ovl_force_readonly(ufs)) if (!(*flags & MS_RDONLY) && ovl_force_readonly(ofs))
return -EROFS; return -EROFS;
return 0; return 0;
...@@ -451,13 +464,11 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config) ...@@ -451,13 +464,11 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config)
#define OVL_WORKDIR_NAME "work" #define OVL_WORKDIR_NAME "work"
#define OVL_INDEXDIR_NAME "index" #define OVL_INDEXDIR_NAME "index"
static struct dentry *ovl_workdir_create(struct super_block *sb, static struct dentry *ovl_workdir_create(struct ovl_fs *ofs,
struct ovl_fs *ufs,
struct dentry *dentry,
const char *name, bool persist) const char *name, bool persist)
{ {
struct inode *dir = dentry->d_inode; struct inode *dir = ofs->workbasedir->d_inode;
struct vfsmount *mnt = ufs->upper_mnt; struct vfsmount *mnt = ofs->upper_mnt;
struct dentry *work; struct dentry *work;
int err; int err;
bool retried = false; bool retried = false;
...@@ -471,7 +482,7 @@ static struct dentry *ovl_workdir_create(struct super_block *sb, ...@@ -471,7 +482,7 @@ static struct dentry *ovl_workdir_create(struct super_block *sb,
locked = true; locked = true;
retry: retry:
work = lookup_one_len(name, dentry, strlen(name)); work = lookup_one_len(name, ofs->workbasedir, strlen(name));
if (!IS_ERR(work)) { if (!IS_ERR(work)) {
struct iattr attr = { struct iattr attr = {
...@@ -541,8 +552,7 @@ static struct dentry *ovl_workdir_create(struct super_block *sb, ...@@ -541,8 +552,7 @@ static struct dentry *ovl_workdir_create(struct super_block *sb,
dput(work); dput(work);
out_err: out_err:
pr_warn("overlayfs: failed to create directory %s/%s (errno: %i); mounting read-only\n", pr_warn("overlayfs: failed to create directory %s/%s (errno: %i); mounting read-only\n",
ufs->config.workdir, name, -err); ofs->config.workdir, name, -err);
sb->s_flags |= MS_RDONLY;
work = NULL; work = NULL;
goto out_unlock; goto out_unlock;
} }
...@@ -585,7 +595,7 @@ static int ovl_mount_dir_noesc(const char *name, struct path *path) ...@@ -585,7 +595,7 @@ static int ovl_mount_dir_noesc(const char *name, struct path *path)
return 0; return 0;
out_put: out_put:
path_put(path); path_put_init(path);
out: out:
return err; return err;
} }
...@@ -603,7 +613,7 @@ static int ovl_mount_dir(const char *name, struct path *path) ...@@ -603,7 +613,7 @@ static int ovl_mount_dir(const char *name, struct path *path)
if (ovl_dentry_remote(path->dentry)) { if (ovl_dentry_remote(path->dentry)) {
pr_err("overlayfs: filesystem on '%s' not supported as upperdir\n", pr_err("overlayfs: filesystem on '%s' not supported as upperdir\n",
tmp); tmp);
path_put(path); path_put_init(path);
err = -EINVAL; err = -EINVAL;
} }
kfree(tmp); kfree(tmp);
...@@ -655,7 +665,7 @@ static int ovl_lower_dir(const char *name, struct path *path, ...@@ -655,7 +665,7 @@ static int ovl_lower_dir(const char *name, struct path *path,
return 0; return 0;
out_put: out_put:
path_put(path); path_put_init(path);
out: out:
return err; return err;
} }
...@@ -826,129 +836,269 @@ static const struct xattr_handler *ovl_xattr_handlers[] = { ...@@ -826,129 +836,269 @@ static const struct xattr_handler *ovl_xattr_handlers[] = {
NULL NULL
}; };
static int ovl_fill_super(struct super_block *sb, void *data, int silent) static int ovl_get_upper(struct ovl_fs *ofs, struct path *upperpath)
{ {
struct path upperpath = { }; struct vfsmount *upper_mnt;
struct path workpath = { };
struct dentry *root_dentry;
struct ovl_entry *oe;
struct ovl_fs *ufs;
struct path *stack = NULL;
char *lowertmp;
char *lower;
unsigned int numlower;
unsigned int stacklen = 0;
unsigned int i;
bool remote = false;
struct cred *cred;
int err; int err;
err = -ENOMEM; err = ovl_mount_dir(ofs->config.upperdir, upperpath);
ufs = kzalloc(sizeof(struct ovl_fs), GFP_KERNEL); if (err)
if (!ufs)
goto out; goto out;
ufs->config.redirect_dir = ovl_redirect_dir_def; /* Upper fs should not be r/o */
ufs->config.index = ovl_index_def; if (sb_rdonly(upperpath->mnt->mnt_sb)) {
err = ovl_parse_opt((char *) data, &ufs->config); pr_err("overlayfs: upper fs is r/o, try multi-lower layers mount\n");
err = -EINVAL;
goto out;
}
err = ovl_check_namelen(upperpath, ofs, ofs->config.upperdir);
if (err) if (err)
goto out_free_config; goto out;
err = -EBUSY;
if (ovl_inuse_trylock(upperpath->dentry)) {
ofs->upperdir_locked = true;
} else if (ofs->config.index) {
pr_err("overlayfs: upperdir is in-use by another mount, mount with '-o index=off' to override exclusive upperdir protection.\n");
goto out;
} else {
pr_warn("overlayfs: upperdir is in-use by another mount, accessing files from both mounts will result in undefined behavior.\n");
}
upper_mnt = clone_private_mount(upperpath);
err = PTR_ERR(upper_mnt);
if (IS_ERR(upper_mnt)) {
pr_err("overlayfs: failed to clone upperpath\n");
goto out;
}
/* Don't inherit atime flags */
upper_mnt->mnt_flags &= ~(MNT_NOATIME | MNT_NODIRATIME | MNT_RELATIME);
ofs->upper_mnt = upper_mnt;
err = 0;
out:
return err;
}
static int ovl_make_workdir(struct ovl_fs *ofs, struct path *workpath)
{
struct dentry *temp;
int err;
ofs->workdir = ovl_workdir_create(ofs, OVL_WORKDIR_NAME, false);
if (!ofs->workdir)
return 0;
/*
* Upper should support d_type, else whiteouts are visible. Given
* workdir and upper are on same fs, we can do iterate_dir() on
* workdir. This check requires successful creation of workdir in
* previous step.
*/
err = ovl_check_d_type_supported(workpath);
if (err < 0)
return err;
/*
* We allowed this configuration and don't want to break users over
* kernel upgrade. So warn instead of erroring out.
*/
if (!err)
pr_warn("overlayfs: upper fs needs to support d_type.\n");
/* Check if upper/work fs supports O_TMPFILE */
temp = ovl_do_tmpfile(ofs->workdir, S_IFREG | 0);
ofs->tmpfile = !IS_ERR(temp);
if (ofs->tmpfile)
dput(temp);
else
pr_warn("overlayfs: upper fs does not support tmpfile.\n");
/*
* Check if upper/work fs supports trusted.overlay.* xattr
*/
err = ovl_do_setxattr(ofs->workdir, OVL_XATTR_OPAQUE, "0", 1, 0);
if (err) {
ofs->noxattr = true;
pr_warn("overlayfs: upper fs does not support xattr.\n");
} else {
vfs_removexattr(ofs->workdir, OVL_XATTR_OPAQUE);
}
/* Check if upper/work fs supports file handles */
if (ofs->config.index &&
!ovl_can_decode_fh(ofs->workdir->d_sb)) {
ofs->config.index = false;
pr_warn("overlayfs: upper fs does not support file handles, falling back to index=off.\n");
}
return 0;
}
static int ovl_get_workdir(struct ovl_fs *ofs, struct path *upperpath)
{
int err;
struct path workpath = { };
err = ovl_mount_dir(ofs->config.workdir, &workpath);
if (err)
goto out;
err = -EINVAL; err = -EINVAL;
if (!ufs->config.lowerdir) { if (upperpath->mnt != workpath.mnt) {
if (!silent) pr_err("overlayfs: workdir and upperdir must reside under the same mount\n");
pr_err("overlayfs: missing 'lowerdir'\n"); goto out;
goto out_free_config; }
if (!ovl_workdir_ok(workpath.dentry, upperpath->dentry)) {
pr_err("overlayfs: workdir and upperdir must be separate subtrees\n");
goto out;
} }
sb->s_stack_depth = 0; err = -EBUSY;
sb->s_maxbytes = MAX_LFS_FILESIZE; if (ovl_inuse_trylock(workpath.dentry)) {
if (ufs->config.upperdir) { ofs->workdir_locked = true;
if (!ufs->config.workdir) { } else if (ofs->config.index) {
pr_err("overlayfs: missing 'workdir'\n"); pr_err("overlayfs: workdir is in-use by another mount, mount with '-o index=off' to override exclusive workdir protection.\n");
goto out_free_config; goto out;
} } else {
pr_warn("overlayfs: workdir is in-use by another mount, accessing files from both mounts will result in undefined behavior.\n");
}
err = ovl_mount_dir(ufs->config.upperdir, &upperpath); ofs->workbasedir = dget(workpath.dentry);
if (err) err = ovl_make_workdir(ofs, &workpath);
goto out_free_config; if (err)
goto out;
/* Upper fs should not be r/o */ err = 0;
if (sb_rdonly(upperpath.mnt->mnt_sb)) { out:
pr_err("overlayfs: upper fs is r/o, try multi-lower layers mount\n"); path_put(&workpath);
err = -EINVAL;
goto out_put_upperpath;
}
err = ovl_check_namelen(&upperpath, ufs, ufs->config.upperdir); return err;
if (err) }
goto out_put_upperpath;
static int ovl_get_indexdir(struct ovl_fs *ofs, struct ovl_entry *oe,
err = -EBUSY; struct path *upperpath)
if (ovl_inuse_trylock(upperpath.dentry)) { {
ufs->upperdir_locked = true; int err;
} else if (ufs->config.index) {
pr_err("overlayfs: upperdir is in-use by another mount, mount with '-o index=off' to override exclusive upperdir protection.\n");
goto out_put_upperpath;
} else {
pr_warn("overlayfs: upperdir is in-use by another mount, accessing files from both mounts will result in undefined behavior.\n");
}
err = ovl_mount_dir(ufs->config.workdir, &workpath); /* Verify lower root is upper root origin */
err = ovl_verify_origin(upperpath->dentry, oe->lowerstack[0].dentry,
false, true);
if (err) {
pr_err("overlayfs: failed to verify upper root origin\n");
goto out;
}
ofs->indexdir = ovl_workdir_create(ofs, OVL_INDEXDIR_NAME, true);
if (ofs->indexdir) {
/* Verify upper root is index dir origin */
err = ovl_verify_origin(ofs->indexdir, upperpath->dentry,
true, true);
if (err) if (err)
goto out_unlock_upperdentry; pr_err("overlayfs: failed to verify index dir origin\n");
err = -EINVAL; /* Cleanup bad/stale/orphan index entries */
if (upperpath.mnt != workpath.mnt) { if (!err)
pr_err("overlayfs: workdir and upperdir must reside under the same mount\n"); err = ovl_indexdir_cleanup(ofs->indexdir,
goto out_put_workpath; ofs->upper_mnt,
} oe->lowerstack,
if (!ovl_workdir_ok(workpath.dentry, upperpath.dentry)) { oe->numlower);
pr_err("overlayfs: workdir and upperdir must be separate subtrees\n"); }
goto out_put_workpath; if (err || !ofs->indexdir)
pr_warn("overlayfs: try deleting index dir or mounting with '-o index=off' to disable inodes index.\n");
out:
return err;
}
static int ovl_get_lower_layers(struct ovl_fs *ofs, struct path *stack,
unsigned int numlower)
{
int err;
unsigned int i;
err = -ENOMEM;
ofs->lower_layers = kcalloc(numlower, sizeof(struct ovl_layer),
GFP_KERNEL);
if (ofs->lower_layers == NULL)
goto out;
for (i = 0; i < numlower; i++) {
struct vfsmount *mnt;
dev_t dev;
err = get_anon_bdev(&dev);
if (err) {
pr_err("overlayfs: failed to get anonymous bdev for lowerpath\n");
goto out;
} }
err = -EBUSY; mnt = clone_private_mount(&stack[i]);
if (ovl_inuse_trylock(workpath.dentry)) { err = PTR_ERR(mnt);
ufs->workdir_locked = true; if (IS_ERR(mnt)) {
} else if (ufs->config.index) { pr_err("overlayfs: failed to clone lowerpath\n");
pr_err("overlayfs: workdir is in-use by another mount, mount with '-o index=off' to override exclusive workdir protection.\n"); free_anon_bdev(dev);
goto out_put_workpath; goto out;
} else {
pr_warn("overlayfs: workdir is in-use by another mount, accessing files from both mounts will result in undefined behavior.\n");
} }
/*
* Make lower layers R/O. That way fchmod/fchown on lower file
* will fail instead of modifying lower fs.
*/
mnt->mnt_flags |= MNT_READONLY | MNT_NOATIME;
ufs->workbasedir = workpath.dentry; ofs->lower_layers[ofs->numlower].mnt = mnt;
sb->s_stack_depth = upperpath.mnt->mnt_sb->s_stack_depth; ofs->lower_layers[ofs->numlower].pseudo_dev = dev;
ofs->numlower++;
/* Check if all lower layers are on same sb */
if (i == 0)
ofs->same_sb = mnt->mnt_sb;
else if (ofs->same_sb != mnt->mnt_sb)
ofs->same_sb = NULL;
} }
err = 0;
out:
return err;
}
static struct ovl_entry *ovl_get_lowerstack(struct super_block *sb,
struct ovl_fs *ofs)
{
int err;
char *lowertmp, *lower;
struct path *stack = NULL;
unsigned int stacklen, numlower = 0, i;
bool remote = false;
struct ovl_entry *oe;
err = -ENOMEM; err = -ENOMEM;
lowertmp = kstrdup(ufs->config.lowerdir, GFP_KERNEL); lowertmp = kstrdup(ofs->config.lowerdir, GFP_KERNEL);
if (!lowertmp) if (!lowertmp)
goto out_unlock_workdentry; goto out_err;
err = -EINVAL; err = -EINVAL;
stacklen = ovl_split_lowerdirs(lowertmp); stacklen = ovl_split_lowerdirs(lowertmp);
if (stacklen > OVL_MAX_STACK) { if (stacklen > OVL_MAX_STACK) {
pr_err("overlayfs: too many lower directories, limit is %d\n", pr_err("overlayfs: too many lower directories, limit is %d\n",
OVL_MAX_STACK); OVL_MAX_STACK);
goto out_free_lowertmp; goto out_err;
} else if (!ufs->config.upperdir && stacklen == 1) { } else if (!ofs->config.upperdir && stacklen == 1) {
pr_err("overlayfs: at least 2 lowerdir are needed while upperdir nonexistent\n"); pr_err("overlayfs: at least 2 lowerdir are needed while upperdir nonexistent\n");
goto out_free_lowertmp; goto out_err;
} }
err = -ENOMEM; err = -ENOMEM;
stack = kcalloc(stacklen, sizeof(struct path), GFP_KERNEL); stack = kcalloc(stacklen, sizeof(struct path), GFP_KERNEL);
if (!stack) if (!stack)
goto out_free_lowertmp; goto out_err;
err = -EINVAL; err = -EINVAL;
lower = lowertmp; lower = lowertmp;
for (numlower = 0; numlower < stacklen; numlower++) { for (numlower = 0; numlower < stacklen; numlower++) {
err = ovl_lower_dir(lower, &stack[numlower], ufs, err = ovl_lower_dir(lower, &stack[numlower], ofs,
&sb->s_stack_depth, &remote); &sb->s_stack_depth, &remote);
if (err) if (err)
goto out_put_lowerpath; goto out_err;
lower = strchr(lower, '\0') + 1; lower = strchr(lower, '\0') + 1;
} }
...@@ -957,190 +1107,144 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) ...@@ -957,190 +1107,144 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
sb->s_stack_depth++; sb->s_stack_depth++;
if (sb->s_stack_depth > FILESYSTEM_MAX_STACK_DEPTH) { if (sb->s_stack_depth > FILESYSTEM_MAX_STACK_DEPTH) {
pr_err("overlayfs: maximum fs stacking depth exceeded\n"); pr_err("overlayfs: maximum fs stacking depth exceeded\n");
goto out_put_lowerpath; goto out_err;
} }
if (ufs->config.upperdir) { err = ovl_get_lower_layers(ofs, stack, numlower);
ufs->upper_mnt = clone_private_mount(&upperpath); if (err)
err = PTR_ERR(ufs->upper_mnt); goto out_err;
if (IS_ERR(ufs->upper_mnt)) {
pr_err("overlayfs: failed to clone upperpath\n"); err = -ENOMEM;
goto out_put_lowerpath; oe = ovl_alloc_entry(numlower);
} if (!oe)
goto out_err;
for (i = 0; i < numlower; i++) {
oe->lowerstack[i].dentry = dget(stack[i].dentry);
oe->lowerstack[i].layer = &ofs->lower_layers[i];
}
/* Don't inherit atime flags */ if (remote)
ufs->upper_mnt->mnt_flags &= ~(MNT_NOATIME | MNT_NODIRATIME | MNT_RELATIME); sb->s_d_op = &ovl_reval_dentry_operations;
else
sb->s_d_op = &ovl_dentry_operations;
sb->s_time_gran = ufs->upper_mnt->mnt_sb->s_time_gran; out:
for (i = 0; i < numlower; i++)
path_put(&stack[i]);
kfree(stack);
kfree(lowertmp);
ufs->workdir = ovl_workdir_create(sb, ufs, workpath.dentry, return oe;
OVL_WORKDIR_NAME, false);
/*
* Upper should support d_type, else whiteouts are visible.
* Given workdir and upper are on same fs, we can do
* iterate_dir() on workdir. This check requires successful
* creation of workdir in previous step.
*/
if (ufs->workdir) {
struct dentry *temp;
err = ovl_check_d_type_supported(&workpath);
if (err < 0)
goto out_put_workdir;
/*
* We allowed this configuration and don't want to
* break users over kernel upgrade. So warn instead
* of erroring out.
*/
if (!err)
pr_warn("overlayfs: upper fs needs to support d_type.\n");
/* Check if upper/work fs supports O_TMPFILE */
temp = ovl_do_tmpfile(ufs->workdir, S_IFREG | 0);
ufs->tmpfile = !IS_ERR(temp);
if (ufs->tmpfile)
dput(temp);
else
pr_warn("overlayfs: upper fs does not support tmpfile.\n");
/*
* Check if upper/work fs supports trusted.overlay.*
* xattr
*/
err = ovl_do_setxattr(ufs->workdir, OVL_XATTR_OPAQUE,
"0", 1, 0);
if (err) {
ufs->noxattr = true;
pr_warn("overlayfs: upper fs does not support xattr.\n");
} else {
vfs_removexattr(ufs->workdir, OVL_XATTR_OPAQUE);
}
/* Check if upper/work fs supports file handles */ out_err:
if (ufs->config.index && oe = ERR_PTR(err);
!ovl_can_decode_fh(ufs->workdir->d_sb)) { goto out;
ufs->config.index = false; }
pr_warn("overlayfs: upper fs does not support file handles, falling back to index=off.\n");
} static int ovl_fill_super(struct super_block *sb, void *data, int silent)
} {
} struct path upperpath = { };
struct dentry *root_dentry;
struct ovl_entry *oe;
struct ovl_fs *ofs;
struct cred *cred;
int err;
err = -ENOMEM; err = -ENOMEM;
ufs->lower_mnt = kcalloc(numlower, sizeof(struct vfsmount *), GFP_KERNEL); ofs = kzalloc(sizeof(struct ovl_fs), GFP_KERNEL);
if (ufs->lower_mnt == NULL) if (!ofs)
goto out_put_workdir; goto out;
for (i = 0; i < numlower; i++) {
struct vfsmount *mnt = clone_private_mount(&stack[i]);
err = PTR_ERR(mnt); ofs->creator_cred = cred = prepare_creds();
if (IS_ERR(mnt)) { if (!cred)
pr_err("overlayfs: failed to clone lowerpath\n"); goto out_err;
goto out_put_lower_mnt;
}
/*
* Make lower_mnt R/O. That way fchmod/fchown on lower file
* will fail instead of modifying lower fs.
*/
mnt->mnt_flags |= MNT_READONLY | MNT_NOATIME;
ufs->lower_mnt[ufs->numlower] = mnt; ofs->config.redirect_dir = ovl_redirect_dir_def;
ufs->numlower++; ofs->config.index = ovl_index_def;
err = ovl_parse_opt((char *) data, &ofs->config);
if (err)
goto out_err;
/* Check if all lower layers are on same sb */ err = -EINVAL;
if (i == 0) if (!ofs->config.lowerdir) {
ufs->same_sb = mnt->mnt_sb; if (!silent)
else if (ufs->same_sb != mnt->mnt_sb) pr_err("overlayfs: missing 'lowerdir'\n");
ufs->same_sb = NULL; goto out_err;
} }
/* If the upper fs is nonexistent, we mark overlayfs r/o too */ sb->s_stack_depth = 0;
if (!ufs->upper_mnt) sb->s_maxbytes = MAX_LFS_FILESIZE;
sb->s_flags |= MS_RDONLY; if (ofs->config.upperdir) {
else if (ufs->upper_mnt->mnt_sb != ufs->same_sb) if (!ofs->config.workdir) {
ufs->same_sb = NULL; pr_err("overlayfs: missing 'workdir'\n");
goto out_err;
if (!(ovl_force_readonly(ufs)) && ufs->config.index) {
/* Verify lower root is upper root origin */
err = ovl_verify_origin(upperpath.dentry, ufs->lower_mnt[0],
stack[0].dentry, false, true);
if (err) {
pr_err("overlayfs: failed to verify upper root origin\n");
goto out_put_lower_mnt;
} }
ufs->indexdir = ovl_workdir_create(sb, ufs, workpath.dentry, err = ovl_get_upper(ofs, &upperpath);
OVL_INDEXDIR_NAME, true); if (err)
if (ufs->indexdir) { goto out_err;
/* Verify upper root is index dir origin */
err = ovl_verify_origin(ufs->indexdir, ufs->upper_mnt,
upperpath.dentry, true, true);
if (err)
pr_err("overlayfs: failed to verify index dir origin\n");
/* Cleanup bad/stale/orphan index entries */ err = ovl_get_workdir(ofs, &upperpath);
if (!err)
err = ovl_indexdir_cleanup(ufs->indexdir,
ufs->upper_mnt,
stack, numlower);
}
if (err || !ufs->indexdir)
pr_warn("overlayfs: try deleting index dir or mounting with '-o index=off' to disable inodes index.\n");
if (err) if (err)
goto out_put_indexdir; goto out_err;
if (!ofs->workdir)
sb->s_flags |= MS_RDONLY;
sb->s_stack_depth = ofs->upper_mnt->mnt_sb->s_stack_depth;
sb->s_time_gran = ofs->upper_mnt->mnt_sb->s_time_gran;
} }
oe = ovl_get_lowerstack(sb, ofs);
err = PTR_ERR(oe);
if (IS_ERR(oe))
goto out_err;
/* Show index=off/on in /proc/mounts for any of the reasons above */ /* If the upper fs is nonexistent, we mark overlayfs r/o too */
if (!ufs->indexdir) if (!ofs->upper_mnt)
ufs->config.index = false; sb->s_flags |= MS_RDONLY;
else if (ofs->upper_mnt->mnt_sb != ofs->same_sb)
ofs->same_sb = NULL;
if (remote) if (!(ovl_force_readonly(ofs)) && ofs->config.index) {
sb->s_d_op = &ovl_reval_dentry_operations; err = ovl_get_indexdir(ofs, oe, &upperpath);
else if (err)
sb->s_d_op = &ovl_dentry_operations; goto out_free_oe;
err = -ENOMEM; if (!ofs->indexdir)
ufs->creator_cred = cred = prepare_creds(); sb->s_flags |= MS_RDONLY;
if (!cred) }
goto out_put_indexdir;
/* Show index=off/on in /proc/mounts for any of the reasons above */
if (!ofs->indexdir)
ofs->config.index = false;
/* Never override disk quota limits or use reserved space */ /* Never override disk quota limits or use reserved space */
cap_lower(cred->cap_effective, CAP_SYS_RESOURCE); cap_lower(cred->cap_effective, CAP_SYS_RESOURCE);
err = -ENOMEM;
oe = ovl_alloc_entry(numlower);
if (!oe)
goto out_put_cred;
sb->s_magic = OVERLAYFS_SUPER_MAGIC; sb->s_magic = OVERLAYFS_SUPER_MAGIC;
sb->s_op = &ovl_super_operations; sb->s_op = &ovl_super_operations;
sb->s_xattr = ovl_xattr_handlers; sb->s_xattr = ovl_xattr_handlers;
sb->s_fs_info = ufs; sb->s_fs_info = ofs;
sb->s_flags |= MS_POSIXACL | MS_NOREMOTELOCK; sb->s_flags |= MS_POSIXACL | MS_NOREMOTELOCK;
err = -ENOMEM;
root_dentry = d_make_root(ovl_new_inode(sb, S_IFDIR, 0)); root_dentry = d_make_root(ovl_new_inode(sb, S_IFDIR, 0));
if (!root_dentry) if (!root_dentry)
goto out_free_oe; goto out_free_oe;
mntput(upperpath.mnt); mntput(upperpath.mnt);
for (i = 0; i < numlower; i++)
mntput(stack[i].mnt);
mntput(workpath.mnt);
kfree(lowertmp);
if (upperpath.dentry) { if (upperpath.dentry) {
oe->has_upper = true; oe->has_upper = true;
if (ovl_is_impuredir(upperpath.dentry)) if (ovl_is_impuredir(upperpath.dentry))
ovl_set_flag(OVL_IMPURE, d_inode(root_dentry)); ovl_set_flag(OVL_IMPURE, d_inode(root_dentry));
} }
for (i = 0; i < numlower; i++) {
oe->lowerstack[i].dentry = stack[i].dentry;
oe->lowerstack[i].mnt = ufs->lower_mnt[i];
}
kfree(stack);
root_dentry->d_fsdata = oe; root_dentry->d_fsdata = oe;
/* Root is always merge -> can have whiteouts */
ovl_set_flag(OVL_WHITEOUTS, d_inode(root_dentry));
ovl_inode_init(d_inode(root_dentry), upperpath.dentry, ovl_inode_init(d_inode(root_dentry), upperpath.dentry,
ovl_dentry_lower(root_dentry)); ovl_dentry_lower(root_dentry));
...@@ -1149,39 +1253,11 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) ...@@ -1149,39 +1253,11 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
return 0; return 0;
out_free_oe: out_free_oe:
ovl_entry_stack_free(oe);
kfree(oe); kfree(oe);
out_put_cred: out_err:
put_cred(ufs->creator_cred);
out_put_indexdir:
dput(ufs->indexdir);
out_put_lower_mnt:
for (i = 0; i < ufs->numlower; i++)
mntput(ufs->lower_mnt[i]);
kfree(ufs->lower_mnt);
out_put_workdir:
dput(ufs->workdir);
mntput(ufs->upper_mnt);
out_put_lowerpath:
for (i = 0; i < numlower; i++)
path_put(&stack[i]);
kfree(stack);
out_free_lowertmp:
kfree(lowertmp);
out_unlock_workdentry:
if (ufs->workdir_locked)
ovl_inuse_unlock(workpath.dentry);
out_put_workpath:
path_put(&workpath);
out_unlock_upperdentry:
if (ufs->upperdir_locked)
ovl_inuse_unlock(upperpath.dentry);
out_put_upperpath:
path_put(&upperpath); path_put(&upperpath);
out_free_config: ovl_free_fs(ofs);
kfree(ufs->config.lowerdir);
kfree(ufs->config.upperdir);
kfree(ufs->config.workdir);
kfree(ufs);
out: out:
return err; return err;
} }
......
...@@ -17,7 +17,6 @@ ...@@ -17,7 +17,6 @@
#include <linux/namei.h> #include <linux/namei.h>
#include <linux/ratelimit.h> #include <linux/ratelimit.h>
#include "overlayfs.h" #include "overlayfs.h"
#include "ovl_entry.h"
int ovl_want_write(struct dentry *dentry) int ovl_want_write(struct dentry *dentry)
{ {
...@@ -125,7 +124,12 @@ void ovl_path_lower(struct dentry *dentry, struct path *path) ...@@ -125,7 +124,12 @@ void ovl_path_lower(struct dentry *dentry, struct path *path)
{ {
struct ovl_entry *oe = dentry->d_fsdata; 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) enum ovl_path_type ovl_path_real(struct dentry *dentry, struct path *path)
...@@ -329,6 +333,19 @@ void ovl_copy_up_end(struct dentry *dentry) ...@@ -329,6 +333,19 @@ void ovl_copy_up_end(struct dentry *dentry)
mutex_unlock(&OVL_I(d_inode(dentry))->lock); 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) bool ovl_check_dir_xattr(struct dentry *dentry, const char *name)
{ {
int res; int res;
......
...@@ -18,4 +18,10 @@ static inline int path_equal(const struct path *path1, const struct path *path2) ...@@ -18,4 +18,10 @@ static inline int path_equal(const struct path *path1, const struct path *path2)
return path1->mnt == path2->mnt && path1->dentry == path2->dentry; return path1->mnt == path2->mnt && path1->dentry == path2->dentry;
} }
static inline void path_put_init(struct path *path)
{
path_put(path);
*path = (struct path) { };
}
#endif /* _LINUX_PATH_H */ #endif /* _LINUX_PATH_H */
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