Commit ecde0064 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs

Pull vfs fixes from Al Viro:
 "Assorted fixes, most in overlayfs land"

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs:
  ovl: ovl_dir_fsync() cleanup
  ovl: update MAINTAINERS
  ovl: pass dentry into ovl_dir_read_merged()
  ovl: use lockless_dereference() for upperdentry
  ovl: allow filenames with comma
  ovl: fix race in private xattr checks
  ovl: fix remove/copy-up race
  ovl: rename filesystem type to "overlay"
  isofs: avoid unused function warning
  vfs: fix reference leak in d_prune_aliases()
parents 8a84e01e 3035b675
...@@ -64,7 +64,7 @@ is formed. ...@@ -64,7 +64,7 @@ is formed.
At mount time, the two directories given as mount options "lowerdir" and At mount time, the two directories given as mount options "lowerdir" and
"upperdir" are combined into a merged directory: "upperdir" are combined into a merged directory:
mount -t overlayfs overlayfs -olowerdir=/lower,upperdir=/upper,\ mount -t overlay overlay -olowerdir=/lower,upperdir=/upper,\
workdir=/work /merged workdir=/work /merged
The "workdir" needs to be an empty directory on the same filesystem The "workdir" needs to be an empty directory on the same filesystem
......
...@@ -6888,11 +6888,12 @@ F: drivers/scsi/osd/ ...@@ -6888,11 +6888,12 @@ F: drivers/scsi/osd/
F: include/scsi/osd_* F: include/scsi/osd_*
F: fs/exofs/ F: fs/exofs/
OVERLAYFS FILESYSTEM OVERLAY FILESYSTEM
M: Miklos Szeredi <miklos@szeredi.hu> M: Miklos Szeredi <miklos@szeredi.hu>
L: linux-fsdevel@vger.kernel.org L: linux-unionfs@vger.kernel.org
T: git git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs.git
S: Supported S: Supported
F: fs/overlayfs/* F: fs/overlayfs/
F: Documentation/filesystems/overlayfs.txt F: Documentation/filesystems/overlayfs.txt
P54 WIRELESS DRIVER P54 WIRELESS DRIVER
......
...@@ -104,7 +104,7 @@ obj-$(CONFIG_QNX6FS_FS) += qnx6/ ...@@ -104,7 +104,7 @@ obj-$(CONFIG_QNX6FS_FS) += qnx6/
obj-$(CONFIG_AUTOFS4_FS) += autofs4/ obj-$(CONFIG_AUTOFS4_FS) += autofs4/
obj-$(CONFIG_ADFS_FS) += adfs/ obj-$(CONFIG_ADFS_FS) += adfs/
obj-$(CONFIG_FUSE_FS) += fuse/ obj-$(CONFIG_FUSE_FS) += fuse/
obj-$(CONFIG_OVERLAYFS_FS) += overlayfs/ obj-$(CONFIG_OVERLAY_FS) += overlayfs/
obj-$(CONFIG_UDF_FS) += udf/ obj-$(CONFIG_UDF_FS) += udf/
obj-$(CONFIG_SUN_OPENPROMFS) += openpromfs/ obj-$(CONFIG_SUN_OPENPROMFS) += openpromfs/
obj-$(CONFIG_OMFS_FS) += omfs/ obj-$(CONFIG_OMFS_FS) += omfs/
......
...@@ -778,6 +778,7 @@ void d_prune_aliases(struct inode *inode) ...@@ -778,6 +778,7 @@ void d_prune_aliases(struct inode *inode)
struct dentry *parent = lock_parent(dentry); struct dentry *parent = lock_parent(dentry);
if (likely(!dentry->d_lockref.count)) { if (likely(!dentry->d_lockref.count)) {
__dentry_kill(dentry); __dentry_kill(dentry);
dput(parent);
goto restart; goto restart;
} }
if (parent) if (parent)
......
...@@ -170,27 +170,6 @@ struct iso9660_options{ ...@@ -170,27 +170,6 @@ struct iso9660_options{
s32 sbsector; s32 sbsector;
}; };
/*
* Compute the hash for the isofs name corresponding to the dentry.
*/
static int
isofs_hash_common(struct qstr *qstr, int ms)
{
const char *name;
int len;
len = qstr->len;
name = qstr->name;
if (ms) {
while (len && name[len-1] == '.')
len--;
}
qstr->hash = full_name_hash(name, len);
return 0;
}
/* /*
* Compute the hash for the isofs name corresponding to the dentry. * Compute the hash for the isofs name corresponding to the dentry.
*/ */
...@@ -263,6 +242,27 @@ isofs_dentry_cmpi(const struct dentry *parent, const struct dentry *dentry, ...@@ -263,6 +242,27 @@ isofs_dentry_cmpi(const struct dentry *parent, const struct dentry *dentry,
} }
#ifdef CONFIG_JOLIET #ifdef CONFIG_JOLIET
/*
* Compute the hash for the isofs name corresponding to the dentry.
*/
static int
isofs_hash_common(struct qstr *qstr, int ms)
{
const char *name;
int len;
len = qstr->len;
name = qstr->name;
if (ms) {
while (len && name[len-1] == '.')
len--;
}
qstr->hash = full_name_hash(name, len);
return 0;
}
static int static int
isofs_hash_ms(const struct dentry *dentry, struct qstr *qstr) isofs_hash_ms(const struct dentry *dentry, struct qstr *qstr)
{ {
......
config OVERLAYFS_FS config OVERLAY_FS
tristate "Overlay filesystem support" tristate "Overlay filesystem support"
help help
An overlay filesystem combines two filesystems - an 'upper' filesystem An overlay filesystem combines two filesystems - an 'upper' filesystem
......
...@@ -2,6 +2,6 @@ ...@@ -2,6 +2,6 @@
# Makefile for the overlay filesystem. # Makefile for the overlay filesystem.
# #
obj-$(CONFIG_OVERLAYFS_FS) += overlayfs.o obj-$(CONFIG_OVERLAY_FS) += overlay.o
overlayfs-objs := super.o inode.o dir.o readdir.o copy_up.o overlay-objs := super.o inode.o dir.o readdir.o copy_up.o
...@@ -284,8 +284,7 @@ static struct dentry *ovl_clear_empty(struct dentry *dentry, ...@@ -284,8 +284,7 @@ static struct dentry *ovl_clear_empty(struct dentry *dentry,
return ERR_PTR(err); return ERR_PTR(err);
} }
static struct dentry *ovl_check_empty_and_clear(struct dentry *dentry, static struct dentry *ovl_check_empty_and_clear(struct dentry *dentry)
enum ovl_path_type type)
{ {
int err; int err;
struct dentry *ret = NULL; struct dentry *ret = NULL;
...@@ -294,8 +293,17 @@ static struct dentry *ovl_check_empty_and_clear(struct dentry *dentry, ...@@ -294,8 +293,17 @@ static struct dentry *ovl_check_empty_and_clear(struct dentry *dentry,
err = ovl_check_empty_dir(dentry, &list); err = ovl_check_empty_dir(dentry, &list);
if (err) if (err)
ret = ERR_PTR(err); ret = ERR_PTR(err);
else if (type == OVL_PATH_MERGE) else {
ret = ovl_clear_empty(dentry, &list); /*
* If no upperdentry then skip clearing whiteouts.
*
* 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_dentry_upper(dentry))
ret = ovl_clear_empty(dentry, &list);
}
ovl_cache_free(&list); ovl_cache_free(&list);
...@@ -487,8 +495,7 @@ static int ovl_link(struct dentry *old, struct inode *newdir, ...@@ -487,8 +495,7 @@ static int ovl_link(struct dentry *old, struct inode *newdir,
return err; return err;
} }
static int ovl_remove_and_whiteout(struct dentry *dentry, static int ovl_remove_and_whiteout(struct dentry *dentry, bool is_dir)
enum ovl_path_type type, bool is_dir)
{ {
struct dentry *workdir = ovl_workdir(dentry); struct dentry *workdir = ovl_workdir(dentry);
struct inode *wdir = workdir->d_inode; struct inode *wdir = workdir->d_inode;
...@@ -500,7 +507,7 @@ static int ovl_remove_and_whiteout(struct dentry *dentry, ...@@ -500,7 +507,7 @@ static int ovl_remove_and_whiteout(struct dentry *dentry,
int err; int err;
if (is_dir) { if (is_dir) {
opaquedir = ovl_check_empty_and_clear(dentry, type); opaquedir = ovl_check_empty_and_clear(dentry);
err = PTR_ERR(opaquedir); err = PTR_ERR(opaquedir);
if (IS_ERR(opaquedir)) if (IS_ERR(opaquedir))
goto out; goto out;
...@@ -515,9 +522,10 @@ static int ovl_remove_and_whiteout(struct dentry *dentry, ...@@ -515,9 +522,10 @@ static int ovl_remove_and_whiteout(struct dentry *dentry,
if (IS_ERR(whiteout)) if (IS_ERR(whiteout))
goto out_unlock; goto out_unlock;
if (type == OVL_PATH_LOWER) { upper = ovl_dentry_upper(dentry);
if (!upper) {
upper = lookup_one_len(dentry->d_name.name, upperdir, upper = lookup_one_len(dentry->d_name.name, upperdir,
dentry->d_name.len); dentry->d_name.len);
err = PTR_ERR(upper); err = PTR_ERR(upper);
if (IS_ERR(upper)) if (IS_ERR(upper))
goto kill_whiteout; goto kill_whiteout;
...@@ -529,7 +537,6 @@ static int ovl_remove_and_whiteout(struct dentry *dentry, ...@@ -529,7 +537,6 @@ static int ovl_remove_and_whiteout(struct dentry *dentry,
} else { } else {
int flags = 0; int flags = 0;
upper = ovl_dentry_upper(dentry);
if (opaquedir) if (opaquedir)
upper = opaquedir; upper = opaquedir;
err = -ESTALE; err = -ESTALE;
...@@ -648,7 +655,7 @@ static int ovl_do_remove(struct dentry *dentry, bool is_dir) ...@@ -648,7 +655,7 @@ static int ovl_do_remove(struct dentry *dentry, bool is_dir)
cap_raise(override_cred->cap_effective, CAP_CHOWN); cap_raise(override_cred->cap_effective, CAP_CHOWN);
old_cred = override_creds(override_cred); old_cred = override_creds(override_cred);
err = ovl_remove_and_whiteout(dentry, type, is_dir); err = ovl_remove_and_whiteout(dentry, is_dir);
revert_creds(old_cred); revert_creds(old_cred);
put_cred(override_cred); put_cred(override_cred);
...@@ -781,7 +788,7 @@ static int ovl_rename2(struct inode *olddir, struct dentry *old, ...@@ -781,7 +788,7 @@ static int ovl_rename2(struct inode *olddir, struct dentry *old,
} }
if (overwrite && (new_type == OVL_PATH_LOWER || new_type == OVL_PATH_MERGE) && new_is_dir) { if (overwrite && (new_type == OVL_PATH_LOWER || new_type == OVL_PATH_MERGE) && new_is_dir) {
opaquedir = ovl_check_empty_and_clear(new, new_type); opaquedir = ovl_check_empty_and_clear(new);
err = PTR_ERR(opaquedir); err = PTR_ERR(opaquedir);
if (IS_ERR(opaquedir)) { if (IS_ERR(opaquedir)) {
opaquedir = NULL; opaquedir = NULL;
......
...@@ -235,26 +235,36 @@ int ovl_setxattr(struct dentry *dentry, const char *name, ...@@ -235,26 +235,36 @@ int ovl_setxattr(struct dentry *dentry, const char *name,
return err; return err;
} }
static bool ovl_need_xattr_filter(struct dentry *dentry,
enum ovl_path_type type)
{
return type == OVL_PATH_UPPER && S_ISDIR(dentry->d_inode->i_mode);
}
ssize_t ovl_getxattr(struct dentry *dentry, const char *name, ssize_t ovl_getxattr(struct dentry *dentry, const char *name,
void *value, size_t size) void *value, size_t size)
{ {
if (ovl_path_type(dentry->d_parent) == OVL_PATH_MERGE && struct path realpath;
ovl_is_private_xattr(name)) enum ovl_path_type type = ovl_path_real(dentry, &realpath);
if (ovl_need_xattr_filter(dentry, type) && ovl_is_private_xattr(name))
return -ENODATA; return -ENODATA;
return vfs_getxattr(ovl_dentry_real(dentry), name, value, size); return vfs_getxattr(realpath.dentry, name, value, size);
} }
ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size) ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size)
{ {
struct path realpath;
enum ovl_path_type type = ovl_path_real(dentry, &realpath);
ssize_t res; ssize_t res;
int off; int off;
res = vfs_listxattr(ovl_dentry_real(dentry), list, size); res = vfs_listxattr(realpath.dentry, list, size);
if (res <= 0 || size == 0) if (res <= 0 || size == 0)
return res; return res;
if (ovl_path_type(dentry->d_parent) != OVL_PATH_MERGE) if (!ovl_need_xattr_filter(dentry, type))
return res; return res;
/* filter out private xattrs */ /* filter out private xattrs */
...@@ -279,17 +289,16 @@ int ovl_removexattr(struct dentry *dentry, const char *name) ...@@ -279,17 +289,16 @@ int ovl_removexattr(struct dentry *dentry, const char *name)
{ {
int err; int err;
struct path realpath; struct path realpath;
enum ovl_path_type type; enum ovl_path_type type = ovl_path_real(dentry, &realpath);
err = ovl_want_write(dentry); err = ovl_want_write(dentry);
if (err) if (err)
goto out; goto out;
if (ovl_path_type(dentry->d_parent) == OVL_PATH_MERGE && err = -ENODATA;
ovl_is_private_xattr(name)) if (ovl_need_xattr_filter(dentry, type) && ovl_is_private_xattr(name))
goto out_drop_write; goto out_drop_write;
type = ovl_path_real(dentry, &realpath);
if (type == OVL_PATH_LOWER) { if (type == OVL_PATH_LOWER) {
err = vfs_getxattr(realpath.dentry, name, NULL, 0); err = vfs_getxattr(realpath.dentry, name, NULL, 0);
if (err < 0) if (err < 0)
......
...@@ -274,11 +274,11 @@ static int ovl_dir_mark_whiteouts(struct dentry *dir, ...@@ -274,11 +274,11 @@ static int ovl_dir_mark_whiteouts(struct dentry *dir,
return 0; return 0;
} }
static inline int ovl_dir_read_merged(struct path *upperpath, static int ovl_dir_read_merged(struct dentry *dentry, struct list_head *list)
struct path *lowerpath,
struct list_head *list)
{ {
int err; int err;
struct path lowerpath;
struct path upperpath;
struct ovl_readdir_data rdd = { struct ovl_readdir_data rdd = {
.ctx.actor = ovl_fill_merge, .ctx.actor = ovl_fill_merge,
.list = list, .list = list,
...@@ -286,25 +286,28 @@ static inline int ovl_dir_read_merged(struct path *upperpath, ...@@ -286,25 +286,28 @@ static inline int ovl_dir_read_merged(struct path *upperpath,
.is_merge = false, .is_merge = false,
}; };
if (upperpath->dentry) { ovl_path_lower(dentry, &lowerpath);
err = ovl_dir_read(upperpath, &rdd); ovl_path_upper(dentry, &upperpath);
if (upperpath.dentry) {
err = ovl_dir_read(&upperpath, &rdd);
if (err) if (err)
goto out; goto out;
if (lowerpath->dentry) { if (lowerpath.dentry) {
err = ovl_dir_mark_whiteouts(upperpath->dentry, &rdd); err = ovl_dir_mark_whiteouts(upperpath.dentry, &rdd);
if (err) if (err)
goto out; goto out;
} }
} }
if (lowerpath->dentry) { if (lowerpath.dentry) {
/* /*
* Insert lowerpath entries before upperpath ones, this allows * Insert lowerpath entries before upperpath ones, this allows
* offsets to be reasonably constant * offsets to be reasonably constant
*/ */
list_add(&rdd.middle, rdd.list); list_add(&rdd.middle, rdd.list);
rdd.is_merge = true; rdd.is_merge = true;
err = ovl_dir_read(lowerpath, &rdd); err = ovl_dir_read(&lowerpath, &rdd);
list_del(&rdd.middle); list_del(&rdd.middle);
} }
out: out:
...@@ -329,8 +332,6 @@ static void ovl_seek_cursor(struct ovl_dir_file *od, loff_t pos) ...@@ -329,8 +332,6 @@ static void ovl_seek_cursor(struct ovl_dir_file *od, loff_t pos)
static struct ovl_dir_cache *ovl_cache_get(struct dentry *dentry) static struct ovl_dir_cache *ovl_cache_get(struct dentry *dentry)
{ {
int res; int res;
struct path lowerpath;
struct path upperpath;
struct ovl_dir_cache *cache; struct ovl_dir_cache *cache;
cache = ovl_dir_cache(dentry); cache = ovl_dir_cache(dentry);
...@@ -347,10 +348,7 @@ static struct ovl_dir_cache *ovl_cache_get(struct dentry *dentry) ...@@ -347,10 +348,7 @@ static struct ovl_dir_cache *ovl_cache_get(struct dentry *dentry)
cache->refcount = 1; cache->refcount = 1;
INIT_LIST_HEAD(&cache->entries); INIT_LIST_HEAD(&cache->entries);
ovl_path_lower(dentry, &lowerpath); res = ovl_dir_read_merged(dentry, &cache->entries);
ovl_path_upper(dentry, &upperpath);
res = ovl_dir_read_merged(&upperpath, &lowerpath, &cache->entries);
if (res) { if (res) {
ovl_cache_free(&cache->entries); ovl_cache_free(&cache->entries);
kfree(cache); kfree(cache);
...@@ -452,10 +450,10 @@ static int ovl_dir_fsync(struct file *file, loff_t start, loff_t end, ...@@ -452,10 +450,10 @@ static int ovl_dir_fsync(struct file *file, loff_t start, loff_t end,
/* /*
* Need to check if we started out being a lower dir, but got copied up * Need to check if we started out being a lower dir, but got copied up
*/ */
if (!od->is_upper && ovl_path_type(dentry) == OVL_PATH_MERGE) { if (!od->is_upper && ovl_path_type(dentry) != OVL_PATH_LOWER) {
struct inode *inode = file_inode(file); struct inode *inode = file_inode(file);
realfile =lockless_dereference(od->upperfile); realfile = lockless_dereference(od->upperfile);
if (!realfile) { if (!realfile) {
struct path upperpath; struct path upperpath;
...@@ -538,14 +536,9 @@ const struct file_operations ovl_dir_operations = { ...@@ -538,14 +536,9 @@ 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 path lowerpath;
struct path upperpath;
struct ovl_cache_entry *p; struct ovl_cache_entry *p;
ovl_path_upper(dentry, &upperpath); err = ovl_dir_read_merged(dentry, list);
ovl_path_lower(dentry, &lowerpath);
err = ovl_dir_read_merged(&upperpath, &lowerpath, list);
if (err) if (err)
return err; return err;
......
...@@ -24,7 +24,7 @@ MODULE_AUTHOR("Miklos Szeredi <miklos@szeredi.hu>"); ...@@ -24,7 +24,7 @@ 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 0x794c764f #define OVERLAYFS_SUPER_MAGIC 0x794c7630
struct ovl_config { struct ovl_config {
char *lowerdir; char *lowerdir;
...@@ -84,12 +84,7 @@ enum ovl_path_type ovl_path_type(struct dentry *dentry) ...@@ -84,12 +84,7 @@ enum ovl_path_type ovl_path_type(struct dentry *dentry)
static struct dentry *ovl_upperdentry_dereference(struct ovl_entry *oe) static struct dentry *ovl_upperdentry_dereference(struct ovl_entry *oe)
{ {
struct dentry *upperdentry = ACCESS_ONCE(oe->__upperdentry); return lockless_dereference(oe->__upperdentry);
/*
* Make sure to order reads to upperdentry wrt ovl_dentry_update()
*/
smp_read_barrier_depends();
return upperdentry;
} }
void ovl_path_upper(struct dentry *dentry, struct path *path) void ovl_path_upper(struct dentry *dentry, struct path *path)
...@@ -462,11 +457,34 @@ static const match_table_t ovl_tokens = { ...@@ -462,11 +457,34 @@ static const match_table_t ovl_tokens = {
{OPT_ERR, NULL} {OPT_ERR, NULL}
}; };
static char *ovl_next_opt(char **s)
{
char *sbegin = *s;
char *p;
if (sbegin == NULL)
return NULL;
for (p = sbegin; *p; p++) {
if (*p == '\\') {
p++;
if (!*p)
break;
} else if (*p == ',') {
*p = '\0';
*s = p + 1;
return sbegin;
}
}
*s = NULL;
return sbegin;
}
static int ovl_parse_opt(char *opt, struct ovl_config *config) static int ovl_parse_opt(char *opt, struct ovl_config *config)
{ {
char *p; char *p;
while ((p = strsep(&opt, ",")) != NULL) { while ((p = ovl_next_opt(&opt)) != NULL) {
int token; int token;
substring_t args[MAX_OPT_ARGS]; substring_t args[MAX_OPT_ARGS];
...@@ -554,15 +572,34 @@ static struct dentry *ovl_workdir_create(struct vfsmount *mnt, ...@@ -554,15 +572,34 @@ static struct dentry *ovl_workdir_create(struct vfsmount *mnt,
goto out_unlock; goto out_unlock;
} }
static void ovl_unescape(char *s)
{
char *d = s;
for (;; s++, d++) {
if (*s == '\\')
s++;
*d = *s;
if (!*s)
break;
}
}
static int ovl_mount_dir(const char *name, struct path *path) static int ovl_mount_dir(const char *name, struct path *path)
{ {
int err; int err;
char *tmp = kstrdup(name, GFP_KERNEL);
if (!tmp)
return -ENOMEM;
err = kern_path(name, LOOKUP_FOLLOW, path); ovl_unescape(tmp);
err = kern_path(tmp, LOOKUP_FOLLOW, path);
if (err) { if (err) {
pr_err("overlayfs: failed to resolve '%s': %i\n", name, err); pr_err("overlayfs: failed to resolve '%s': %i\n", tmp, err);
err = -EINVAL; err = -EINVAL;
} }
kfree(tmp);
return err; return err;
} }
...@@ -776,11 +813,11 @@ static struct dentry *ovl_mount(struct file_system_type *fs_type, int flags, ...@@ -776,11 +813,11 @@ static struct dentry *ovl_mount(struct file_system_type *fs_type, int flags,
static struct file_system_type ovl_fs_type = { static struct file_system_type ovl_fs_type = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.name = "overlayfs", .name = "overlay",
.mount = ovl_mount, .mount = ovl_mount,
.kill_sb = kill_anon_super, .kill_sb = kill_anon_super,
}; };
MODULE_ALIAS_FS("overlayfs"); MODULE_ALIAS_FS("overlay");
static int __init ovl_init(void) static int __init ovl_init(void)
{ {
......
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