Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b04a2342 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
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
Loading
Loading
Loading
Loading
+6 −2
Original line number Diff line number Diff line
@@ -22,7 +22,6 @@
#include <linux/ratelimit.h>
#include <linux/exportfs.h>
#include "overlayfs.h"
#include "ovl_entry.h"

#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)
static int ovl_copy_up_locked(struct ovl_copy_up_ctx *c)
{
	struct inode *udir = c->destdir->d_inode;
	struct inode *inode;
	struct dentry *newdentry = NULL;
	struct dentry *temp = NULL;
	int err;
@@ -508,7 +508,11 @@ static int ovl_copy_up_locked(struct ovl_copy_up_ctx *c)
	if (err)
		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:
	dput(temp);
	return err;
+16 −9
Original line number Diff line number Diff line
@@ -181,6 +181,11 @@ static bool ovl_type_origin(struct dentry *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,
			    struct cattr *attr, struct dentry *hardlink)
{
@@ -300,7 +305,6 @@ static struct dentry *ovl_check_empty_and_clear(struct dentry *dentry)
{
	int err;
	struct dentry *ret = NULL;
	enum ovl_path_type type = ovl_path_type(dentry);
	LIST_HEAD(list);

	err = ovl_check_empty_dir(dentry, &list);
@@ -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
	 * 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.
	 * Doesn't matter, since copy-up can't create a non-empty directory
	 * from an empty one.
	 */
	if (OVL_TYPE_UPPER(type) && OVL_TYPE_MERGE(type))
	if (!list_empty(&list))
		ret = ovl_clear_empty(dentry, &list);

out_free:
@@ -698,8 +702,9 @@ static int ovl_remove_upper(struct dentry *dentry, bool is_dir)
	struct dentry *opaquedir = NULL;
	int err;

	/* Redirect dir can be !ovl_lower_positive && OVL_TYPE_MERGE */
	if (is_dir && ovl_dentry_get_redirect(dentry)) {
	/* Redirect/origin dir can be !ovl_lower_positive && not clean */
	if (is_dir && (ovl_dentry_get_redirect(dentry) ||
		       ovl_may_have_whiteouts(dentry))) {
		opaquedir = ovl_check_empty_and_clear(dentry);
		err = PTR_ERR(opaquedir);
		if (IS_ERR(opaquedir))
@@ -946,7 +951,8 @@ static int ovl_rename(struct inode *olddir, struct dentry *old,

	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);
		err = PTR_ERR(opaquedir);
		if (IS_ERR(opaquedir)) {
@@ -1069,9 +1075,10 @@ static int ovl_rename(struct inode *olddir, struct dentry *old,
			drop_nlink(d_inode(new));
	}

	ovl_dentry_version_inc(old->d_parent,
			       !overwrite && ovl_type_origin(new));
	ovl_dentry_version_inc(new->d_parent, ovl_type_origin(old));
	ovl_dentry_version_inc(old->d_parent, ovl_type_origin(old) ||
			       (!overwrite && ovl_type_origin(new)));
	ovl_dentry_version_inc(new->d_parent, ovl_type_origin(old) ||
			       (d_inode(new) && ovl_type_origin(new)));

out_dput:
	dput(newdentry);
+50 −13
Original line number Diff line number Diff line
@@ -15,6 +15,14 @@
#include <linux/ratelimit.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 err;
@@ -66,6 +74,7 @@ int ovl_getattr(const struct path *path, struct kstat *stat,
	struct path realpath;
	const struct cred *old_cred;
	bool is_dir = S_ISDIR(dentry->d_inode->i_mode);
	bool samefs = ovl_same_sb(dentry->d_sb);
	int err;

	type = ovl_path_real(dentry, &realpath);
@@ -75,16 +84,13 @@ int ovl_getattr(const struct path *path, struct kstat *stat,
		goto out;

	/*
	 * 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.
	 *
	 * We also use st_ino of the copy up origin, if we know it.
	 * This guaranties constant st_dev/st_ino across copy up.
	 * For non-dir or same fs, we 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
	 * persistent st_ino across mount cycle.
	 */
	if (ovl_same_sb(dentry->d_sb)) {
	if (!is_dir || samefs) {
		if (OVL_TYPE_ORIGIN(type)) {
			struct kstat lowerstat;
			u32 lowermask = STATX_INO | (!is_dir ? STATX_NLINK : 0);
@@ -95,7 +101,6 @@ int ovl_getattr(const struct path *path, struct kstat *stat,
			if (err)
				goto out;

			WARN_ON_ONCE(stat->dev != lowerstat.dev);
			/*
			 * Lower hardlinks may be broken on copy up to different
			 * 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,
			if (is_dir || lowerstat.nlink == 1 ||
			    ovl_test_flag(OVL_INDEX, d_inode(dentry)))
				stat->ino = lowerstat.ino;

			if (samefs)
				WARN_ON_ONCE(stat->dev != lowerstat.dev);
			else
				stat->dev = ovl_get_pseudo_dev(dentry);
		}
		if (samefs) {
			/*
			 * 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 (is_dir) {
		} 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
		 * -xdev' will scan the entire overlay mount and won't cross the
		 * 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->ino = dentry->d_inode->i_ino;
@@ -409,6 +433,7 @@ static inline void ovl_lockdep_annotate_inode_mutex_key(struct inode *inode)
#ifdef CONFIG_LOCKDEP
	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_lock_key[OVL_MAX_NESTING];

	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)
		lockdep_set_class(&inode->i_rwsem, &ovl_i_mutex_dir_key[depth]);
	else
		lockdep_set_class(&inode->i_rwsem, &ovl_i_mutex_key[depth]);

	lockdep_set_class(&OVL_I(inode)->lock, &ovl_i_lock_key[depth]);
#endif
}

@@ -657,6 +684,16 @@ struct inode *ovl_get_inode(struct dentry *dentry, struct dentry *upperdentry,
	if (upperdentry && ovl_is_impuredir(upperdentry))
		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)
		unlock_new_inode(inode);
out:
+34 −25
Original line number Diff line number Diff line
@@ -15,7 +15,6 @@
#include <linux/mount.h>
#include <linux/exportfs.h>
#include "overlayfs.h"
#include "ovl_entry.h"

struct ovl_lookup_data {
	struct qstr name;
@@ -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,
			    struct path *lowerstack, unsigned int numlower,
			    struct path **stackp, unsigned int *ctrp)
			    struct ovl_path *lower, unsigned int numlower,
			    struct ovl_path **stackp, unsigned int *ctrp)
{
	struct vfsmount *mnt;
	struct dentry *origin = NULL;
	int i;


	for (i = 0; i < numlower; i++) {
		mnt = lowerstack[i].mnt;
		mnt = lower[i].layer->mnt;
		origin = ovl_get_origin(upperdentry, mnt);
		if (IS_ERR(origin))
			return PTR_ERR(origin);
@@ -309,12 +307,12 @@ static int ovl_check_origin(struct dentry *upperdentry,

	BUG_ON(*ctrp);
	if (!*stackp)
		*stackp = kmalloc(sizeof(struct path), GFP_KERNEL);
		*stackp = kmalloc(sizeof(struct ovl_path), GFP_KERNEL);
	if (!*stackp) {
		dput(origin);
		return -ENOMEM;
	}
	**stackp = (struct path) { .dentry = origin, .mnt = mnt };
	**stackp = (struct ovl_path){.dentry = origin, .layer = lower[i].layer};
	*ctrp = 1;

	return 0;
@@ -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.
 */
int ovl_verify_origin(struct dentry *dentry, struct vfsmount *mnt,
		      struct dentry *origin, bool is_upper, bool set)
int ovl_verify_origin(struct dentry *dentry, struct dentry *origin,
		      bool is_upper, bool set)
{
	struct inode *inode;
	struct ovl_fh *fh;
@@ -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.
 * 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)
{
	struct ovl_fh *fh = NULL;
	size_t len;
	struct path origin = { };
	struct path *stack = &origin;
	struct ovl_path origin = { };
	struct ovl_path *stack = &origin;
	unsigned int ctr = 0;
	int err;

@@ -429,7 +427,7 @@ int ovl_verify_index(struct dentry *index, struct path *lowerstack,
	if (err)
		goto fail;

	err = ovl_check_origin(index, lowerstack, numlower, &stack, &ctr);
	err = ovl_check_origin(index, lower, numlower, &stack, &ctr);
	if (!err && !ctr)
		err = -ESTALE;
	if (err)
@@ -568,11 +566,24 @@ int ovl_path_next(int idx, struct dentry *dentry, struct path *path)
		idx++;
	}
	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;
}

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,
			  unsigned int flags)
{
@@ -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_entry *poe = dentry->d_parent->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 *index = NULL;
	unsigned int ctr = 0;
@@ -630,7 +641,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
			err = ovl_check_origin(upperdentry, roe->lowerstack,
					       roe->numlower, &stack, &ctr);
			if (err)
				goto out;
				goto out_put_upper;
		}

		if (d.redirect) {
@@ -646,17 +657,17 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,

	if (!d.stop && poe->numlower) {
		err = -ENOMEM;
		stack = kcalloc(ofs->numlower, sizeof(struct path),
		stack = kcalloc(ofs->numlower, sizeof(struct ovl_path),
				GFP_KERNEL);
		if (!stack)
			goto out_put_upper;
	}

	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;
		err = ovl_lookup_layer(lowerpath.dentry, &d, &this);
		err = ovl_lookup_layer(lower.dentry, &d, &this);
		if (err)
			goto out_put;

@@ -664,7 +675,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
			continue;

		stack[ctr].dentry = this;
		stack[ctr].mnt = lowerpath.mnt;
		stack[ctr].layer = lower.layer;
		ctr++;

		if (d.stop)
@@ -674,10 +685,8 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
			poe = roe;

			/* Find the current layer on the root dentry */
			for (i = 0; i < poe->numlower; i++)
				if (poe->lowerstack[i].mnt == lowerpath.mnt)
					break;
			if (WARN_ON(i == poe->numlower))
			i = ovl_find_layer(ofs, &lower);
			if (WARN_ON(i == ofs->numlower))
				break;
		}
	}
@@ -700,7 +709,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
		goto out_put;

	oe->opaque = upperopaque;
	memcpy(oe->lowerstack, stack, sizeof(struct path) * ctr);
	memcpy(oe->lowerstack, stack, sizeof(struct ovl_path) * ctr);
	dentry->d_fsdata = oe;

	if (upperdentry)
+9 −4
Original line number Diff line number Diff line
@@ -9,6 +9,7 @@

#include <linux/kernel.h>
#include <linux/uuid.h>
#include "ovl_entry.h"

enum ovl_path_type {
	__OVL_PATH_UPPER	= (1 << 0),
@@ -28,7 +29,10 @@ enum ovl_path_type {
#define OVL_XATTR_NLINK OVL_XATTR_PREFIX "nlink"

enum ovl_flag {
	/* Pure upper dir that may contain non pure upper entries */
	OVL_IMPURE,
	/* Non-merge dir that may contain whiteout entries */
	OVL_WHITEOUTS,
	OVL_INDEX,
};

@@ -223,6 +227,7 @@ bool ovl_is_whiteout(struct dentry *dentry);
struct file *ovl_path_open(struct path *path, int flags);
int ovl_copy_up_start(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);
int ovl_check_setxattr(struct dentry *dentry, struct dentry *upperdentry,
		       const char *name, const void *value, size_t size,
@@ -244,9 +249,9 @@ static inline bool ovl_is_impuredir(struct dentry *dentry)


/* 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,
int ovl_verify_origin(struct dentry *dentry, struct dentry *origin,
		      bool is_upper, bool set);
int ovl_verify_index(struct dentry *index, struct ovl_path *lower,
		     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);
@@ -263,7 +268,7 @@ int ovl_check_d_type_supported(struct path *realpath);
void ovl_workdir_cleanup(struct inode *dir, struct vfsmount *mnt,
			 struct dentry *dentry, int level);
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 */
int ovl_set_nlink_upper(struct dentry *dentry);
Loading