Merge branch 'overlayfs-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs
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
This commit is contained in:
commit
e9f57ebcba
|
@ -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;
|
||||
}
|
||||
error = vfs_setxattr(new, name, value, size, 0);
|
||||
if (error)
|
||||
goto out_free_value;
|
||||
break;
|
||||
}
|
||||
|
||||
out_free_value:
|
||||
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)
|
||||
break;
|
||||
}
|
||||
kfree(value);
|
||||
out:
|
||||
kfree(buf);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#define PSTOREFS_MAGIC 0x6165676C
|
||||
#define EFIVARFS_MAGIC 0xde5e81e4
|
||||
#define HOSTFS_SUPER_MAGIC 0x00c0ffee
|
||||
#define OVERLAYFS_SUPER_MAGIC 0x794c7630
|
||||
|
||||
#define MINIX_SUPER_MAGIC 0x137F /* minix v1 fs, 14 char names */
|
||||
#define MINIX_SUPER_MAGIC2 0x138F /* minix v1 fs, 30 char names */
|
||||
|
|
Loading…
Reference in a new issue