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

Commit e9f57ebc authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull overlayfs updates from Miklos Szeredi:
 "This contains several bug fixes and a new mount option
  'default_permissions' that allows read-only exported NFS
  filesystems to be used as lower layer"

* 'overlayfs-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs:
  ovl: check dentry positiveness in ovl_cleanup_whiteouts()
  ovl: setattr: check permissions before copy-up
  ovl: root: copy attr
  ovl: move super block magic number to magic.h
  ovl: use a minimal buffer in ovl_copy_xattr
  ovl: allow zero size xattr
  ovl: default permissions
parents 5c89e9ea 84889d49
Loading
Loading
Loading
Loading
+26 −15
Original line number Diff line number Diff line
@@ -22,9 +22,9 @@

int ovl_copy_xattr(struct dentry *old, struct dentry *new)
{
	ssize_t list_size, size;
	char *buf, *name, *value;
	int error;
	ssize_t list_size, size, value_size = 0;
	char *buf, *name, *value = NULL;
	int uninitialized_var(error);

	if (!old->d_inode->i_op->getxattr ||
	    !new->d_inode->i_op->getxattr)
@@ -41,29 +41,40 @@ int ovl_copy_xattr(struct dentry *old, struct dentry *new)
	if (!buf)
		return -ENOMEM;

	error = -ENOMEM;
	value = kmalloc(XATTR_SIZE_MAX, GFP_KERNEL);
	if (!value)
		goto out;

	list_size = vfs_listxattr(old, buf, list_size);
	if (list_size <= 0) {
		error = list_size;
		goto out_free_value;
		goto out;
	}

	for (name = buf; name < (buf + list_size); name += strlen(name) + 1) {
		size = vfs_getxattr(old, name, value, XATTR_SIZE_MAX);
		if (size <= 0) {
retry:
		size = vfs_getxattr(old, name, value, value_size);
		if (size == -ERANGE)
			size = vfs_getxattr(old, name, NULL, 0);

		if (size < 0) {
			error = size;
			goto out_free_value;
			break;
		}

		if (size > value_size) {
			void *new;

			new = krealloc(value, size, GFP_KERNEL);
			if (!new) {
				error = -ENOMEM;
				break;
			}
			value = new;
			value_size = size;
			goto retry;
		}

		error = vfs_setxattr(new, name, value, size, 0);
		if (error)
			goto out_free_value;
			break;
	}

out_free_value:
	kfree(value);
out:
	kfree(buf);
+36 −0
Original line number Diff line number Diff line
@@ -42,6 +42,19 @@ int ovl_setattr(struct dentry *dentry, struct iattr *attr)
	int err;
	struct dentry *upperdentry;

	/*
	 * Check for permissions before trying to copy-up.  This is redundant
	 * since it will be rechecked later by ->setattr() on upper dentry.  But
	 * without this, copy-up can be triggered by just about anybody.
	 *
	 * We don't initialize inode->size, which just means that
	 * inode_newsize_ok() will always check against MAX_LFS_FILESIZE and not
	 * check for a swapfile (which this won't be anyway).
	 */
	err = inode_change_ok(dentry->d_inode, attr);
	if (err)
		return err;

	err = ovl_want_write(dentry);
	if (err)
		goto out;
@@ -95,6 +108,29 @@ int ovl_permission(struct inode *inode, int mask)

	realdentry = ovl_entry_real(oe, &is_upper);

	if (ovl_is_default_permissions(inode)) {
		struct kstat stat;
		struct path realpath = { .dentry = realdentry };

		if (mask & MAY_NOT_BLOCK)
			return -ECHILD;

		realpath.mnt = ovl_entry_mnt_real(oe, inode, is_upper);

		err = vfs_getattr(&realpath, &stat);
		if (err)
			return err;

		if ((stat.mode ^ inode->i_mode) & S_IFMT)
			return -ESTALE;

		inode->i_mode = stat.mode;
		inode->i_uid = stat.uid;
		inode->i_gid = stat.gid;

		return generic_permission(inode, mask);
	}

	/* Careful in RCU walk mode */
	realinode = ACCESS_ONCE(realdentry->d_inode);
	if (!realinode) {
+3 −0
Original line number Diff line number Diff line
@@ -142,7 +142,10 @@ struct dentry *ovl_dentry_upper(struct dentry *dentry);
struct dentry *ovl_dentry_lower(struct dentry *dentry);
struct dentry *ovl_dentry_real(struct dentry *dentry);
struct dentry *ovl_entry_real(struct ovl_entry *oe, bool *is_upper);
struct vfsmount *ovl_entry_mnt_real(struct ovl_entry *oe, struct inode *inode,
				    bool is_upper);
struct ovl_dir_cache *ovl_dir_cache(struct dentry *dentry);
bool ovl_is_default_permissions(struct inode *inode);
void ovl_set_dir_cache(struct dentry *dentry, struct ovl_dir_cache *cache);
struct dentry *ovl_workdir(struct dentry *dentry);
int ovl_want_write(struct dentry *dentry);
+2 −1
Original line number Diff line number Diff line
@@ -571,6 +571,7 @@ void ovl_cleanup_whiteouts(struct dentry *upper, struct list_head *list)
			       (int) PTR_ERR(dentry));
			continue;
		}
		if (dentry->d_inode)
			ovl_cleanup(upper->d_inode, dentry);
		dput(dentry);
	}
+33 −2
Original line number Diff line number Diff line
@@ -9,6 +9,7 @@

#include <linux/fs.h>
#include <linux/namei.h>
#include <linux/pagemap.h>
#include <linux/xattr.h>
#include <linux/security.h>
#include <linux/mount.h>
@@ -24,12 +25,11 @@ MODULE_AUTHOR("Miklos Szeredi <miklos@szeredi.hu>");
MODULE_DESCRIPTION("Overlay filesystem");
MODULE_LICENSE("GPL");

#define OVERLAYFS_SUPER_MAGIC 0x794c7630

struct ovl_config {
	char *lowerdir;
	char *upperdir;
	char *workdir;
	bool default_permissions;
};

/* private information held for overlayfs's superblock */
@@ -154,6 +154,18 @@ struct dentry *ovl_entry_real(struct ovl_entry *oe, bool *is_upper)
	return realdentry;
}

struct vfsmount *ovl_entry_mnt_real(struct ovl_entry *oe, struct inode *inode,
				    bool is_upper)
{
	if (is_upper) {
		struct ovl_fs *ofs = inode->i_sb->s_fs_info;

		return ofs->upper_mnt;
	} else {
		return oe->numlower ? oe->lowerstack[0].mnt : NULL;
	}
}

struct ovl_dir_cache *ovl_dir_cache(struct dentry *dentry)
{
	struct ovl_entry *oe = dentry->d_fsdata;
@@ -161,6 +173,13 @@ struct ovl_dir_cache *ovl_dir_cache(struct dentry *dentry)
	return oe->cache;
}

bool ovl_is_default_permissions(struct inode *inode)
{
	struct ovl_fs *ofs = inode->i_sb->s_fs_info;

	return ofs->config.default_permissions;
}

void ovl_set_dir_cache(struct dentry *dentry, struct ovl_dir_cache *cache)
{
	struct ovl_entry *oe = dentry->d_fsdata;
@@ -594,6 +613,8 @@ static int ovl_show_options(struct seq_file *m, struct dentry *dentry)
		seq_show_option(m, "upperdir", ufs->config.upperdir);
		seq_show_option(m, "workdir", ufs->config.workdir);
	}
	if (ufs->config.default_permissions)
		seq_puts(m, ",default_permissions");
	return 0;
}

@@ -618,6 +639,7 @@ enum {
	OPT_LOWERDIR,
	OPT_UPPERDIR,
	OPT_WORKDIR,
	OPT_DEFAULT_PERMISSIONS,
	OPT_ERR,
};

@@ -625,6 +647,7 @@ static const match_table_t ovl_tokens = {
	{OPT_LOWERDIR,			"lowerdir=%s"},
	{OPT_UPPERDIR,			"upperdir=%s"},
	{OPT_WORKDIR,			"workdir=%s"},
	{OPT_DEFAULT_PERMISSIONS,	"default_permissions"},
	{OPT_ERR,			NULL}
};

@@ -685,6 +708,10 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config)
				return -ENOMEM;
			break;

		case OPT_DEFAULT_PERMISSIONS:
			config->default_permissions = true;
			break;

		default:
			pr_err("overlayfs: unrecognized mount option \"%s\" or missing value\n", p);
			return -EINVAL;
@@ -910,6 +937,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
	}

	sb->s_stack_depth = 0;
	sb->s_maxbytes = MAX_LFS_FILESIZE;
	if (ufs->config.upperdir) {
		if (!ufs->config.workdir) {
			pr_err("overlayfs: missing 'workdir'\n");
@@ -1053,6 +1081,9 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)

	root_dentry->d_fsdata = oe;

	ovl_copyattr(ovl_dentry_real(root_dentry)->d_inode,
		     root_dentry->d_inode);

	sb->s_magic = OVERLAYFS_SUPER_MAGIC;
	sb->s_op = &ovl_super_operations;
	sb->s_root = root_dentry;
Loading