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()
This commit is contained in:
Linus Torvalds 2014-11-22 14:15:27 -08:00
commit ecde00642c
11 changed files with 133 additions and 85 deletions

View File

@ -64,7 +64,7 @@ is formed.
At mount time, the two directories given as mount options "lowerdir" and
"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
The "workdir" needs to be an empty directory on the same filesystem

View File

@ -6888,11 +6888,12 @@ F: drivers/scsi/osd/
F: include/scsi/osd_*
F: fs/exofs/
OVERLAYFS FILESYSTEM
OVERLAY FILESYSTEM
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
F: fs/overlayfs/*
F: fs/overlayfs/
F: Documentation/filesystems/overlayfs.txt
P54 WIRELESS DRIVER

View File

@ -104,7 +104,7 @@ obj-$(CONFIG_QNX6FS_FS) += qnx6/
obj-$(CONFIG_AUTOFS4_FS) += autofs4/
obj-$(CONFIG_ADFS_FS) += adfs/
obj-$(CONFIG_FUSE_FS) += fuse/
obj-$(CONFIG_OVERLAYFS_FS) += overlayfs/
obj-$(CONFIG_OVERLAY_FS) += overlayfs/
obj-$(CONFIG_UDF_FS) += udf/
obj-$(CONFIG_SUN_OPENPROMFS) += openpromfs/
obj-$(CONFIG_OMFS_FS) += omfs/

View File

@ -778,6 +778,7 @@ void d_prune_aliases(struct inode *inode)
struct dentry *parent = lock_parent(dentry);
if (likely(!dentry->d_lockref.count)) {
__dentry_kill(dentry);
dput(parent);
goto restart;
}
if (parent)

View File

@ -170,27 +170,6 @@ struct iso9660_options{
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.
*/
@ -263,6 +242,27 @@ isofs_dentry_cmpi(const struct dentry *parent, const struct dentry *dentry,
}
#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
isofs_hash_ms(const struct dentry *dentry, struct qstr *qstr)
{

View File

@ -1,4 +1,4 @@
config OVERLAYFS_FS
config OVERLAY_FS
tristate "Overlay filesystem support"
help
An overlay filesystem combines two filesystems - an 'upper' filesystem

View File

@ -2,6 +2,6 @@
# 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

View File

@ -284,8 +284,7 @@ static struct dentry *ovl_clear_empty(struct dentry *dentry,
return ERR_PTR(err);
}
static struct dentry *ovl_check_empty_and_clear(struct dentry *dentry,
enum ovl_path_type type)
static struct dentry *ovl_check_empty_and_clear(struct dentry *dentry)
{
int err;
struct dentry *ret = NULL;
@ -294,8 +293,17 @@ static struct dentry *ovl_check_empty_and_clear(struct dentry *dentry,
err = ovl_check_empty_dir(dentry, &list);
if (err)
ret = ERR_PTR(err);
else if (type == OVL_PATH_MERGE)
ret = ovl_clear_empty(dentry, &list);
else {
/*
* 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);
@ -487,8 +495,7 @@ static int ovl_link(struct dentry *old, struct inode *newdir,
return err;
}
static int ovl_remove_and_whiteout(struct dentry *dentry,
enum ovl_path_type type, bool is_dir)
static int ovl_remove_and_whiteout(struct dentry *dentry, bool is_dir)
{
struct dentry *workdir = ovl_workdir(dentry);
struct inode *wdir = workdir->d_inode;
@ -500,7 +507,7 @@ static int ovl_remove_and_whiteout(struct dentry *dentry,
int err;
if (is_dir) {
opaquedir = ovl_check_empty_and_clear(dentry, type);
opaquedir = ovl_check_empty_and_clear(dentry);
err = PTR_ERR(opaquedir);
if (IS_ERR(opaquedir))
goto out;
@ -515,9 +522,10 @@ static int ovl_remove_and_whiteout(struct dentry *dentry,
if (IS_ERR(whiteout))
goto out_unlock;
if (type == OVL_PATH_LOWER) {
upper = ovl_dentry_upper(dentry);
if (!upper) {
upper = lookup_one_len(dentry->d_name.name, upperdir,
dentry->d_name.len);
dentry->d_name.len);
err = PTR_ERR(upper);
if (IS_ERR(upper))
goto kill_whiteout;
@ -529,7 +537,6 @@ static int ovl_remove_and_whiteout(struct dentry *dentry,
} else {
int flags = 0;
upper = ovl_dentry_upper(dentry);
if (opaquedir)
upper = opaquedir;
err = -ESTALE;
@ -648,7 +655,7 @@ static int ovl_do_remove(struct dentry *dentry, bool is_dir)
cap_raise(override_cred->cap_effective, CAP_CHOWN);
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);
put_cred(override_cred);
@ -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) {
opaquedir = ovl_check_empty_and_clear(new, new_type);
opaquedir = ovl_check_empty_and_clear(new);
err = PTR_ERR(opaquedir);
if (IS_ERR(opaquedir)) {
opaquedir = NULL;

View File

@ -235,26 +235,36 @@ int ovl_setxattr(struct dentry *dentry, const char *name,
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,
void *value, size_t size)
{
if (ovl_path_type(dentry->d_parent) == OVL_PATH_MERGE &&
ovl_is_private_xattr(name))
struct path realpath;
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 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)
{
struct path realpath;
enum ovl_path_type type = ovl_path_real(dentry, &realpath);
ssize_t res;
int off;
res = vfs_listxattr(ovl_dentry_real(dentry), list, size);
res = vfs_listxattr(realpath.dentry, list, size);
if (res <= 0 || size == 0)
return res;
if (ovl_path_type(dentry->d_parent) != OVL_PATH_MERGE)
if (!ovl_need_xattr_filter(dentry, type))
return res;
/* filter out private xattrs */
@ -279,17 +289,16 @@ int ovl_removexattr(struct dentry *dentry, const char *name)
{
int err;
struct path realpath;
enum ovl_path_type type;
enum ovl_path_type type = ovl_path_real(dentry, &realpath);
err = ovl_want_write(dentry);
if (err)
goto out;
if (ovl_path_type(dentry->d_parent) == OVL_PATH_MERGE &&
ovl_is_private_xattr(name))
err = -ENODATA;
if (ovl_need_xattr_filter(dentry, type) && ovl_is_private_xattr(name))
goto out_drop_write;
type = ovl_path_real(dentry, &realpath);
if (type == OVL_PATH_LOWER) {
err = vfs_getxattr(realpath.dentry, name, NULL, 0);
if (err < 0)

View File

@ -274,11 +274,11 @@ static int ovl_dir_mark_whiteouts(struct dentry *dir,
return 0;
}
static inline int ovl_dir_read_merged(struct path *upperpath,
struct path *lowerpath,
struct list_head *list)
static int ovl_dir_read_merged(struct dentry *dentry, struct list_head *list)
{
int err;
struct path lowerpath;
struct path upperpath;
struct ovl_readdir_data rdd = {
.ctx.actor = ovl_fill_merge,
.list = list,
@ -286,25 +286,28 @@ static inline int ovl_dir_read_merged(struct path *upperpath,
.is_merge = false,
};
if (upperpath->dentry) {
err = ovl_dir_read(upperpath, &rdd);
ovl_path_lower(dentry, &lowerpath);
ovl_path_upper(dentry, &upperpath);
if (upperpath.dentry) {
err = ovl_dir_read(&upperpath, &rdd);
if (err)
goto out;
if (lowerpath->dentry) {
err = ovl_dir_mark_whiteouts(upperpath->dentry, &rdd);
if (lowerpath.dentry) {
err = ovl_dir_mark_whiteouts(upperpath.dentry, &rdd);
if (err)
goto out;
}
}
if (lowerpath->dentry) {
if (lowerpath.dentry) {
/*
* Insert lowerpath entries before upperpath ones, this allows
* offsets to be reasonably constant
*/
list_add(&rdd.middle, rdd.list);
rdd.is_merge = true;
err = ovl_dir_read(lowerpath, &rdd);
err = ovl_dir_read(&lowerpath, &rdd);
list_del(&rdd.middle);
}
out:
@ -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)
{
int res;
struct path lowerpath;
struct path upperpath;
struct ovl_dir_cache *cache;
cache = ovl_dir_cache(dentry);
@ -347,10 +348,7 @@ static struct ovl_dir_cache *ovl_cache_get(struct dentry *dentry)
cache->refcount = 1;
INIT_LIST_HEAD(&cache->entries);
ovl_path_lower(dentry, &lowerpath);
ovl_path_upper(dentry, &upperpath);
res = ovl_dir_read_merged(&upperpath, &lowerpath, &cache->entries);
res = ovl_dir_read_merged(dentry, &cache->entries);
if (res) {
ovl_cache_free(&cache->entries);
kfree(cache);
@ -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
*/
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);
realfile =lockless_dereference(od->upperfile);
realfile = lockless_dereference(od->upperfile);
if (!realfile) {
struct path upperpath;
@ -538,14 +536,9 @@ const struct file_operations ovl_dir_operations = {
int ovl_check_empty_dir(struct dentry *dentry, struct list_head *list)
{
int err;
struct path lowerpath;
struct path upperpath;
struct ovl_cache_entry *p;
ovl_path_upper(dentry, &upperpath);
ovl_path_lower(dentry, &lowerpath);
err = ovl_dir_read_merged(&upperpath, &lowerpath, list);
err = ovl_dir_read_merged(dentry, list);
if (err)
return err;

View File

@ -24,7 +24,7 @@ MODULE_AUTHOR("Miklos Szeredi <miklos@szeredi.hu>");
MODULE_DESCRIPTION("Overlay filesystem");
MODULE_LICENSE("GPL");
#define OVERLAYFS_SUPER_MAGIC 0x794c764f
#define OVERLAYFS_SUPER_MAGIC 0x794c7630
struct ovl_config {
char *lowerdir;
@ -84,12 +84,7 @@ enum ovl_path_type ovl_path_type(struct dentry *dentry)
static struct dentry *ovl_upperdentry_dereference(struct ovl_entry *oe)
{
struct dentry *upperdentry = ACCESS_ONCE(oe->__upperdentry);
/*
* Make sure to order reads to upperdentry wrt ovl_dentry_update()
*/
smp_read_barrier_depends();
return upperdentry;
return lockless_dereference(oe->__upperdentry);
}
void ovl_path_upper(struct dentry *dentry, struct path *path)
@ -462,11 +457,34 @@ static const match_table_t ovl_tokens = {
{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)
{
char *p;
while ((p = strsep(&opt, ",")) != NULL) {
while ((p = ovl_next_opt(&opt)) != NULL) {
int token;
substring_t args[MAX_OPT_ARGS];
@ -554,15 +572,34 @@ static struct dentry *ovl_workdir_create(struct vfsmount *mnt,
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)
{
int err;
char *tmp = kstrdup(name, GFP_KERNEL);
err = kern_path(name, LOOKUP_FOLLOW, path);
if (!tmp)
return -ENOMEM;
ovl_unescape(tmp);
err = kern_path(tmp, LOOKUP_FOLLOW, path);
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;
}
kfree(tmp);
return err;
}
@ -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 = {
.owner = THIS_MODULE,
.name = "overlayfs",
.name = "overlay",
.mount = ovl_mount,
.kill_sb = kill_anon_super,
};
MODULE_ALIAS_FS("overlayfs");
MODULE_ALIAS_FS("overlay");
static int __init ovl_init(void)
{