Commit e9f57ebc 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:
 "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
...@@ -22,9 +22,9 @@ ...@@ -22,9 +22,9 @@
int ovl_copy_xattr(struct dentry *old, struct dentry *new) int ovl_copy_xattr(struct dentry *old, struct dentry *new)
{ {
ssize_t list_size, size; ssize_t list_size, size, value_size = 0;
char *buf, *name, *value; char *buf, *name, *value = NULL;
int error; int uninitialized_var(error);
if (!old->d_inode->i_op->getxattr || if (!old->d_inode->i_op->getxattr ||
!new->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) ...@@ -41,29 +41,40 @@ int ovl_copy_xattr(struct dentry *old, struct dentry *new)
if (!buf) if (!buf)
return -ENOMEM; return -ENOMEM;
error = -ENOMEM;
value = kmalloc(XATTR_SIZE_MAX, GFP_KERNEL);
if (!value)
goto out;
list_size = vfs_listxattr(old, buf, list_size); list_size = vfs_listxattr(old, buf, list_size);
if (list_size <= 0) { if (list_size <= 0) {
error = list_size; error = list_size;
goto out_free_value; goto out;
} }
for (name = buf; name < (buf + list_size); name += strlen(name) + 1) { for (name = buf; name < (buf + list_size); name += strlen(name) + 1) {
size = vfs_getxattr(old, name, value, XATTR_SIZE_MAX); retry:
if (size <= 0) { size = vfs_getxattr(old, name, value, value_size);
if (size == -ERANGE)
size = vfs_getxattr(old, name, NULL, 0);
if (size < 0) {
error = size; 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); error = vfs_setxattr(new, name, value, size, 0);
if (error) if (error)
goto out_free_value; break;
} }
out_free_value:
kfree(value); kfree(value);
out: out:
kfree(buf); kfree(buf);
......
...@@ -42,6 +42,19 @@ int ovl_setattr(struct dentry *dentry, struct iattr *attr) ...@@ -42,6 +42,19 @@ int ovl_setattr(struct dentry *dentry, struct iattr *attr)
int err; int err;
struct dentry *upperdentry; 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); err = ovl_want_write(dentry);
if (err) if (err)
goto out; goto out;
...@@ -95,6 +108,29 @@ int ovl_permission(struct inode *inode, int mask) ...@@ -95,6 +108,29 @@ int ovl_permission(struct inode *inode, int mask)
realdentry = ovl_entry_real(oe, &is_upper); 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 */ /* Careful in RCU walk mode */
realinode = ACCESS_ONCE(realdentry->d_inode); realinode = ACCESS_ONCE(realdentry->d_inode);
if (!realinode) { if (!realinode) {
......
...@@ -142,7 +142,10 @@ struct dentry *ovl_dentry_upper(struct dentry *dentry); ...@@ -142,7 +142,10 @@ struct dentry *ovl_dentry_upper(struct dentry *dentry);
struct dentry *ovl_dentry_lower(struct dentry *dentry); struct dentry *ovl_dentry_lower(struct dentry *dentry);
struct dentry *ovl_dentry_real(struct dentry *dentry); struct dentry *ovl_dentry_real(struct dentry *dentry);
struct dentry *ovl_entry_real(struct ovl_entry *oe, bool *is_upper); 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); 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); void ovl_set_dir_cache(struct dentry *dentry, struct ovl_dir_cache *cache);
struct dentry *ovl_workdir(struct dentry *dentry); struct dentry *ovl_workdir(struct dentry *dentry);
int ovl_want_write(struct dentry *dentry); int ovl_want_write(struct dentry *dentry);
......
...@@ -571,7 +571,8 @@ void ovl_cleanup_whiteouts(struct dentry *upper, struct list_head *list) ...@@ -571,7 +571,8 @@ void ovl_cleanup_whiteouts(struct dentry *upper, struct list_head *list)
(int) PTR_ERR(dentry)); (int) PTR_ERR(dentry));
continue; continue;
} }
ovl_cleanup(upper->d_inode, dentry); if (dentry->d_inode)
ovl_cleanup(upper->d_inode, dentry);
dput(dentry); dput(dentry);
} }
mutex_unlock(&upper->d_inode->i_mutex); mutex_unlock(&upper->d_inode->i_mutex);
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/namei.h> #include <linux/namei.h>
#include <linux/pagemap.h>
#include <linux/xattr.h> #include <linux/xattr.h>
#include <linux/security.h> #include <linux/security.h>
#include <linux/mount.h> #include <linux/mount.h>
...@@ -24,12 +25,11 @@ MODULE_AUTHOR("Miklos Szeredi <miklos@szeredi.hu>"); ...@@ -24,12 +25,11 @@ MODULE_AUTHOR("Miklos Szeredi <miklos@szeredi.hu>");
MODULE_DESCRIPTION("Overlay filesystem"); MODULE_DESCRIPTION("Overlay filesystem");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
#define OVERLAYFS_SUPER_MAGIC 0x794c7630
struct ovl_config { struct ovl_config {
char *lowerdir; char *lowerdir;
char *upperdir; char *upperdir;
char *workdir; char *workdir;
bool default_permissions;
}; };
/* private information held for overlayfs's superblock */ /* private information held for overlayfs's superblock */
...@@ -154,6 +154,18 @@ struct dentry *ovl_entry_real(struct ovl_entry *oe, bool *is_upper) ...@@ -154,6 +154,18 @@ struct dentry *ovl_entry_real(struct ovl_entry *oe, bool *is_upper)
return realdentry; 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_dir_cache *ovl_dir_cache(struct dentry *dentry)
{ {
struct ovl_entry *oe = dentry->d_fsdata; struct ovl_entry *oe = dentry->d_fsdata;
...@@ -161,6 +173,13 @@ struct ovl_dir_cache *ovl_dir_cache(struct dentry *dentry) ...@@ -161,6 +173,13 @@ struct ovl_dir_cache *ovl_dir_cache(struct dentry *dentry)
return oe->cache; 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) void ovl_set_dir_cache(struct dentry *dentry, struct ovl_dir_cache *cache)
{ {
struct ovl_entry *oe = dentry->d_fsdata; struct ovl_entry *oe = dentry->d_fsdata;
...@@ -594,6 +613,8 @@ static int ovl_show_options(struct seq_file *m, struct dentry *dentry) ...@@ -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, "upperdir", ufs->config.upperdir);
seq_show_option(m, "workdir", ufs->config.workdir); seq_show_option(m, "workdir", ufs->config.workdir);
} }
if (ufs->config.default_permissions)
seq_puts(m, ",default_permissions");
return 0; return 0;
} }
...@@ -618,6 +639,7 @@ enum { ...@@ -618,6 +639,7 @@ enum {
OPT_LOWERDIR, OPT_LOWERDIR,
OPT_UPPERDIR, OPT_UPPERDIR,
OPT_WORKDIR, OPT_WORKDIR,
OPT_DEFAULT_PERMISSIONS,
OPT_ERR, OPT_ERR,
}; };
...@@ -625,6 +647,7 @@ static const match_table_t ovl_tokens = { ...@@ -625,6 +647,7 @@ static const match_table_t ovl_tokens = {
{OPT_LOWERDIR, "lowerdir=%s"}, {OPT_LOWERDIR, "lowerdir=%s"},
{OPT_UPPERDIR, "upperdir=%s"}, {OPT_UPPERDIR, "upperdir=%s"},
{OPT_WORKDIR, "workdir=%s"}, {OPT_WORKDIR, "workdir=%s"},
{OPT_DEFAULT_PERMISSIONS, "default_permissions"},
{OPT_ERR, NULL} {OPT_ERR, NULL}
}; };
...@@ -685,6 +708,10 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config) ...@@ -685,6 +708,10 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config)
return -ENOMEM; return -ENOMEM;
break; break;
case OPT_DEFAULT_PERMISSIONS:
config->default_permissions = true;
break;
default: default:
pr_err("overlayfs: unrecognized mount option \"%s\" or missing value\n", p); pr_err("overlayfs: unrecognized mount option \"%s\" or missing value\n", p);
return -EINVAL; return -EINVAL;
...@@ -910,6 +937,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) ...@@ -910,6 +937,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
} }
sb->s_stack_depth = 0; sb->s_stack_depth = 0;
sb->s_maxbytes = MAX_LFS_FILESIZE;
if (ufs->config.upperdir) { if (ufs->config.upperdir) {
if (!ufs->config.workdir) { if (!ufs->config.workdir) {
pr_err("overlayfs: missing 'workdir'\n"); pr_err("overlayfs: missing 'workdir'\n");
...@@ -1053,6 +1081,9 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) ...@@ -1053,6 +1081,9 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
root_dentry->d_fsdata = oe; 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_magic = OVERLAYFS_SUPER_MAGIC;
sb->s_op = &ovl_super_operations; sb->s_op = &ovl_super_operations;
sb->s_root = root_dentry; sb->s_root = root_dentry;
......
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
#define PSTOREFS_MAGIC 0x6165676C #define PSTOREFS_MAGIC 0x6165676C
#define EFIVARFS_MAGIC 0xde5e81e4 #define EFIVARFS_MAGIC 0xde5e81e4
#define HOSTFS_SUPER_MAGIC 0x00c0ffee #define HOSTFS_SUPER_MAGIC 0x00c0ffee
#define OVERLAYFS_SUPER_MAGIC 0x794c7630
#define MINIX_SUPER_MAGIC 0x137F /* minix v1 fs, 14 char names */ #define MINIX_SUPER_MAGIC 0x137F /* minix v1 fs, 14 char names */
#define MINIX_SUPER_MAGIC2 0x138F /* minix v1 fs, 30 char names */ #define MINIX_SUPER_MAGIC2 0x138F /* minix v1 fs, 30 char names */
......
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