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:
commit
ecde00642c
|
@ -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/
|
||||||
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/
|
||||||
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 @@ restart:
|
||||||
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{
|
||||||
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,
|
||||||
}
|
}
|
||||||
|
|
||||||
#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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
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 @@
|
||||||
# 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 @@ out:
|
||||||
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,
|
||||||
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 @@ out:
|
||||||
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,
|
||||||
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,
|
||||||
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,
|
||||||
} 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)
|
||||||
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,
|
||||||
}
|
}
|
||||||
|
|
||||||
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 @@ out:
|
||||||
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)
|
||||||
{
|
{
|
||||||
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,
|
||||||
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,
|
||||||
.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)
|
||||||
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)
|
||||||
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,
|
||||||
/*
|
/*
|
||||||
* 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 = {
|
||||||
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>");
|
||||||
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)
|
||||||
|
|
||||||
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 = {
|
||||||
{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 @@ out_dput:
|
||||||
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);
|
||||||
|
|
||||||
err = kern_path(name, LOOKUP_FOLLOW, path);
|
if (!tmp)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
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,
|
||||||
|
|
||||||
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)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue