mirror of
https://github.com/Fishwaldo/Star64_linux.git
synced 2025-06-05 22:28:00 +00:00
Unexport do_add_mount() and add in follow_automount(), not ->d_automount()
Unexport do_add_mount() and make ->d_automount() return the vfsmount to be added rather than calling do_add_mount() itself. follow_automount() will then do the addition. This slightly complicates things as ->d_automount() normally wants to add the new vfsmount to an expiration list and start an expiration timer. The problem with that is that the vfsmount will be deleted if it has a refcount of 1 and the timer will not repeat if the expiration list is empty. To this end, we require the vfsmount to be returned from d_automount() with a refcount of (at least) 2. One of these refs will be dropped unconditionally. In addition, follow_automount() must get a 3rd ref around the call to do_add_mount() lest it eat a ref and return an error, leaving the mount we have open to being expired as we would otherwise have only 1 ref on it. d_automount() should also add the the vfsmount to the expiration list (by calling mnt_set_expiry()) and start the expiration timer before returning, if this mechanism is to be used. The vfsmount will be unlinked from the expiration list by follow_automount() if do_add_mount() fails. This patch also fixes the call to do_add_mount() for AFS to propagate the mount flags from the parent vfsmount. Signed-off-by: David Howells <dhowells@redhat.com> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
parent
ab90911ff9
commit
ea5b778a8b
8 changed files with 101 additions and 89 deletions
|
@ -933,15 +933,20 @@ struct dentry_operations {
|
||||||
dynamic_dname() helper function is provided to take care of this.
|
dynamic_dname() helper function is provided to take care of this.
|
||||||
|
|
||||||
d_automount: called when an automount dentry is to be traversed (optional).
|
d_automount: called when an automount dentry is to be traversed (optional).
|
||||||
This should create a new VFS mount record, mount it on the directory
|
This should create a new VFS mount record and return the record to the
|
||||||
and return the record to the caller. The caller is supplied with a
|
caller. The caller is supplied with a path parameter giving the
|
||||||
path parameter giving the automount directory to describe the automount
|
automount directory to describe the automount target and the parent
|
||||||
target and the parent VFS mount record to provide inheritable mount
|
VFS mount record to provide inheritable mount parameters. NULL should
|
||||||
parameters. NULL should be returned if someone else managed to make
|
be returned if someone else managed to make the automount first. If
|
||||||
the automount first. If the automount failed, then an error code
|
the vfsmount creation failed, then an error code should be returned.
|
||||||
should be returned. If -EISDIR is returned, then the directory will
|
If -EISDIR is returned, then the directory will be treated as an
|
||||||
be treated as an ordinary directory and returned to pathwalk to
|
ordinary directory and returned to pathwalk to continue walking.
|
||||||
continue walking.
|
|
||||||
|
If a vfsmount is returned, the caller will attempt to mount it on the
|
||||||
|
mountpoint and will remove the vfsmount from its expiration list in
|
||||||
|
the case of failure. The vfsmount should be returned with 2 refs on
|
||||||
|
it to prevent automatic expiration - the caller will clean up the
|
||||||
|
additional ref.
|
||||||
|
|
||||||
This function is only used if DCACHE_NEED_AUTOMOUNT is set on the
|
This function is only used if DCACHE_NEED_AUTOMOUNT is set on the
|
||||||
dentry. This is set by __d_instantiate() if S_AUTOMOUNT is set on the
|
dentry. This is set by __d_instantiate() if S_AUTOMOUNT is set on the
|
||||||
|
|
|
@ -241,7 +241,6 @@ error_no_devname:
|
||||||
struct vfsmount *afs_d_automount(struct path *path)
|
struct vfsmount *afs_d_automount(struct path *path)
|
||||||
{
|
{
|
||||||
struct vfsmount *newmnt;
|
struct vfsmount *newmnt;
|
||||||
int err;
|
|
||||||
|
|
||||||
_enter("{%s,%s}", path->mnt->mnt_devname, path->dentry->d_name.name);
|
_enter("{%s,%s}", path->mnt->mnt_devname, path->dentry->d_name.name);
|
||||||
|
|
||||||
|
@ -249,24 +248,12 @@ struct vfsmount *afs_d_automount(struct path *path)
|
||||||
if (IS_ERR(newmnt))
|
if (IS_ERR(newmnt))
|
||||||
return newmnt;
|
return newmnt;
|
||||||
|
|
||||||
mntget(newmnt);
|
mntget(newmnt); /* prevent immediate expiration */
|
||||||
err = do_add_mount(newmnt, path, MNT_SHRINKABLE, &afs_vfsmounts);
|
mnt_set_expiry(newmnt, &afs_vfsmounts);
|
||||||
switch (err) {
|
queue_delayed_work(afs_wq, &afs_mntpt_expiry_timer,
|
||||||
case 0:
|
afs_mntpt_expiry_timeout * HZ);
|
||||||
queue_delayed_work(afs_wq, &afs_mntpt_expiry_timer,
|
_leave(" = %p {%s}", newmnt, newmnt->mnt_devname);
|
||||||
afs_mntpt_expiry_timeout * HZ);
|
return newmnt;
|
||||||
_leave(" = %p {%s}", newmnt, newmnt->mnt_devname);
|
|
||||||
return newmnt;
|
|
||||||
case -EBUSY:
|
|
||||||
/* someone else made a mount here whilst we were busy */
|
|
||||||
mntput(newmnt);
|
|
||||||
_leave(" = NULL [EBUSY]");
|
|
||||||
return NULL;
|
|
||||||
default:
|
|
||||||
mntput(newmnt);
|
|
||||||
_leave(" = %d", err);
|
|
||||||
return ERR_PTR(err);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -351,7 +351,6 @@ free_xid:
|
||||||
struct vfsmount *cifs_dfs_d_automount(struct path *path)
|
struct vfsmount *cifs_dfs_d_automount(struct path *path)
|
||||||
{
|
{
|
||||||
struct vfsmount *newmnt;
|
struct vfsmount *newmnt;
|
||||||
int err;
|
|
||||||
|
|
||||||
cFYI(1, "in %s", __func__);
|
cFYI(1, "in %s", __func__);
|
||||||
|
|
||||||
|
@ -361,25 +360,12 @@ struct vfsmount *cifs_dfs_d_automount(struct path *path)
|
||||||
return newmnt;
|
return newmnt;
|
||||||
}
|
}
|
||||||
|
|
||||||
mntget(newmnt);
|
mntget(newmnt); /* prevent immediate expiration */
|
||||||
err = do_add_mount(newmnt, path, path->mnt->mnt_flags | MNT_SHRINKABLE,
|
mnt_set_expiry(newmnt, &cifs_dfs_automount_list);
|
||||||
&cifs_dfs_automount_list);
|
schedule_delayed_work(&cifs_dfs_automount_task,
|
||||||
switch (err) {
|
cifs_dfs_mountpoint_expiry_timeout);
|
||||||
case 0:
|
cFYI(1, "leaving %s [ok]" , __func__);
|
||||||
schedule_delayed_work(&cifs_dfs_automount_task,
|
return newmnt;
|
||||||
cifs_dfs_mountpoint_expiry_timeout);
|
|
||||||
cFYI(1, "leaving %s [ok]" , __func__);
|
|
||||||
return newmnt;
|
|
||||||
case -EBUSY:
|
|
||||||
/* someone else made a mount here whilst we were busy */
|
|
||||||
mntput(newmnt);
|
|
||||||
cFYI(1, "leaving %s [EBUSY]" , __func__);
|
|
||||||
return NULL;
|
|
||||||
default:
|
|
||||||
mntput(newmnt);
|
|
||||||
cFYI(1, "leaving %s [error %d]" , __func__, err);
|
|
||||||
return ERR_PTR(err);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const struct inode_operations cifs_dfs_referral_inode_operations = {
|
const struct inode_operations cifs_dfs_referral_inode_operations = {
|
||||||
|
|
|
@ -70,6 +70,8 @@ extern void mnt_set_mountpoint(struct vfsmount *, struct dentry *,
|
||||||
extern void release_mounts(struct list_head *);
|
extern void release_mounts(struct list_head *);
|
||||||
extern void umount_tree(struct vfsmount *, int, struct list_head *);
|
extern void umount_tree(struct vfsmount *, int, struct list_head *);
|
||||||
extern struct vfsmount *copy_tree(struct vfsmount *, struct dentry *, int);
|
extern struct vfsmount *copy_tree(struct vfsmount *, struct dentry *, int);
|
||||||
|
extern int do_add_mount(struct vfsmount *, struct path *, int);
|
||||||
|
extern void mnt_clear_expiry(struct vfsmount *);
|
||||||
|
|
||||||
extern void __init mnt_init(void);
|
extern void __init mnt_init(void);
|
||||||
|
|
||||||
|
|
42
fs/namei.c
42
fs/namei.c
|
@ -900,6 +900,7 @@ static int follow_automount(struct path *path, unsigned flags,
|
||||||
bool *need_mntput)
|
bool *need_mntput)
|
||||||
{
|
{
|
||||||
struct vfsmount *mnt;
|
struct vfsmount *mnt;
|
||||||
|
int err;
|
||||||
|
|
||||||
if (!path->dentry->d_op || !path->dentry->d_op->d_automount)
|
if (!path->dentry->d_op || !path->dentry->d_op->d_automount)
|
||||||
return -EREMOTE;
|
return -EREMOTE;
|
||||||
|
@ -942,22 +943,49 @@ static int follow_automount(struct path *path, unsigned flags,
|
||||||
return -EREMOTE;
|
return -EREMOTE;
|
||||||
return PTR_ERR(mnt);
|
return PTR_ERR(mnt);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!mnt) /* mount collision */
|
if (!mnt) /* mount collision */
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
/* The new mount record should have at least 2 refs to prevent it being
|
||||||
|
* expired before we get a chance to add it
|
||||||
|
*/
|
||||||
|
BUG_ON(mnt_get_count(mnt) < 2);
|
||||||
|
|
||||||
if (mnt->mnt_sb == path->mnt->mnt_sb &&
|
if (mnt->mnt_sb == path->mnt->mnt_sb &&
|
||||||
mnt->mnt_root == path->dentry) {
|
mnt->mnt_root == path->dentry) {
|
||||||
|
mnt_clear_expiry(mnt);
|
||||||
|
mntput(mnt);
|
||||||
mntput(mnt);
|
mntput(mnt);
|
||||||
return -ELOOP;
|
return -ELOOP;
|
||||||
}
|
}
|
||||||
|
|
||||||
dput(path->dentry);
|
/* We need to add the mountpoint to the parent. The filesystem may
|
||||||
if (*need_mntput)
|
* have placed it on an expiry list, and so we need to make sure it
|
||||||
mntput(path->mnt);
|
* won't be expired under us if do_add_mount() fails (do_add_mount()
|
||||||
path->mnt = mnt;
|
* will eat a reference unconditionally).
|
||||||
path->dentry = dget(mnt->mnt_root);
|
*/
|
||||||
*need_mntput = true;
|
mntget(mnt);
|
||||||
return 0;
|
err = do_add_mount(mnt, path, path->mnt->mnt_flags | MNT_SHRINKABLE);
|
||||||
|
switch (err) {
|
||||||
|
case -EBUSY:
|
||||||
|
/* Someone else made a mount here whilst we were busy */
|
||||||
|
err = 0;
|
||||||
|
default:
|
||||||
|
mnt_clear_expiry(mnt);
|
||||||
|
mntput(mnt);
|
||||||
|
mntput(mnt);
|
||||||
|
return err;
|
||||||
|
case 0:
|
||||||
|
mntput(mnt);
|
||||||
|
dput(path->dentry);
|
||||||
|
if (*need_mntput)
|
||||||
|
mntput(path->mnt);
|
||||||
|
path->mnt = mnt;
|
||||||
|
path->dentry = dget(mnt->mnt_root);
|
||||||
|
*need_mntput = true;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -1925,15 +1925,14 @@ static int do_new_mount(struct path *path, char *type, int flags,
|
||||||
if (IS_ERR(mnt))
|
if (IS_ERR(mnt))
|
||||||
return PTR_ERR(mnt);
|
return PTR_ERR(mnt);
|
||||||
|
|
||||||
return do_add_mount(mnt, path, mnt_flags, NULL);
|
return do_add_mount(mnt, path, mnt_flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* add a mount into a namespace's mount tree
|
* add a mount into a namespace's mount tree
|
||||||
* - provide the option of adding the new mount to an expiration list
|
* - this unconditionally eats one of the caller's references to newmnt.
|
||||||
*/
|
*/
|
||||||
int do_add_mount(struct vfsmount *newmnt, struct path *path,
|
int do_add_mount(struct vfsmount *newmnt, struct path *path, int mnt_flags)
|
||||||
int mnt_flags, struct list_head *fslist)
|
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
|
@ -1963,9 +1962,6 @@ int do_add_mount(struct vfsmount *newmnt, struct path *path,
|
||||||
if ((err = graft_tree(newmnt, path)))
|
if ((err = graft_tree(newmnt, path)))
|
||||||
goto unlock;
|
goto unlock;
|
||||||
|
|
||||||
if (fslist) /* add to the specified expiration list */
|
|
||||||
list_add_tail(&newmnt->mnt_expire, fslist);
|
|
||||||
|
|
||||||
up_write(&namespace_sem);
|
up_write(&namespace_sem);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
@ -1975,7 +1971,36 @@ unlock:
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
EXPORT_SYMBOL_GPL(do_add_mount);
|
/**
|
||||||
|
* mnt_set_expiry - Put a mount on an expiration list
|
||||||
|
* @mnt: The mount to list.
|
||||||
|
* @expiry_list: The list to add the mount to.
|
||||||
|
*/
|
||||||
|
void mnt_set_expiry(struct vfsmount *mnt, struct list_head *expiry_list)
|
||||||
|
{
|
||||||
|
down_write(&namespace_sem);
|
||||||
|
br_write_lock(vfsmount_lock);
|
||||||
|
|
||||||
|
list_add_tail(&mnt->mnt_expire, expiry_list);
|
||||||
|
|
||||||
|
br_write_unlock(vfsmount_lock);
|
||||||
|
up_write(&namespace_sem);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(mnt_set_expiry);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Remove a vfsmount from any expiration list it may be on
|
||||||
|
*/
|
||||||
|
void mnt_clear_expiry(struct vfsmount *mnt)
|
||||||
|
{
|
||||||
|
if (!list_empty(&mnt->mnt_expire)) {
|
||||||
|
down_write(&namespace_sem);
|
||||||
|
br_write_lock(vfsmount_lock);
|
||||||
|
list_del_init(&mnt->mnt_expire);
|
||||||
|
br_write_unlock(vfsmount_lock);
|
||||||
|
up_write(&namespace_sem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* process a list of expirable mountpoints with the intent of discarding any
|
* process a list of expirable mountpoints with the intent of discarding any
|
||||||
|
|
|
@ -149,26 +149,10 @@ struct vfsmount *nfs_d_automount(struct path *path)
|
||||||
if (IS_ERR(mnt))
|
if (IS_ERR(mnt))
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
mntget(mnt);
|
dprintk("%s: done, success\n", __func__);
|
||||||
err = do_add_mount(mnt, path, path->mnt->mnt_flags | MNT_SHRINKABLE,
|
mntget(mnt); /* prevent immediate expiration */
|
||||||
&nfs_automount_list);
|
mnt_set_expiry(mnt, &nfs_automount_list);
|
||||||
switch (err) {
|
schedule_delayed_work(&nfs_automount_task, nfs_mountpoint_expiry_timeout);
|
||||||
case 0:
|
|
||||||
dprintk("%s: done, success\n", __func__);
|
|
||||||
schedule_delayed_work(&nfs_automount_task, nfs_mountpoint_expiry_timeout);
|
|
||||||
break;
|
|
||||||
case -EBUSY:
|
|
||||||
/* someone else made a mount here whilst we were busy */
|
|
||||||
mntput(mnt);
|
|
||||||
dprintk("%s: done, collision\n", __func__);
|
|
||||||
mnt = NULL;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
mntput(mnt);
|
|
||||||
dprintk("%s: done, error %d\n", __func__, err);
|
|
||||||
mnt = ERR_PTR(err);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
out:
|
out:
|
||||||
nfs_free_fattr(fattr);
|
nfs_free_fattr(fattr);
|
||||||
|
|
|
@ -110,12 +110,7 @@ extern struct vfsmount *vfs_kern_mount(struct file_system_type *type,
|
||||||
int flags, const char *name,
|
int flags, const char *name,
|
||||||
void *data);
|
void *data);
|
||||||
|
|
||||||
struct nameidata;
|
extern void mnt_set_expiry(struct vfsmount *mnt, struct list_head *expiry_list);
|
||||||
|
|
||||||
struct path;
|
|
||||||
extern int do_add_mount(struct vfsmount *newmnt, struct path *path,
|
|
||||||
int mnt_flags, struct list_head *fslist);
|
|
||||||
|
|
||||||
extern void mark_mounts_for_expiry(struct list_head *mounts);
|
extern void mark_mounts_for_expiry(struct list_head *mounts);
|
||||||
|
|
||||||
extern dev_t name_to_dev_t(char *name);
|
extern dev_t name_to_dev_t(char *name);
|
||||||
|
|
Loading…
Add table
Reference in a new issue