mirror of
https://github.com/torvalds/linux.git
synced 2024-12-27 21:33:00 +00:00
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs-2.6
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs-2.6: (23 commits) sanitize vfsmount refcounting changes fix old umount_tree() breakage autofs4: Merge the remaining dentry ops tables Unexport do_add_mount() and add in follow_automount(), not ->d_automount() Allow d_manage() to be used in RCU-walk mode Remove a further kludge from __do_follow_link() autofs4: Bump version autofs4: Add v4 pseudo direct mount support autofs4: Fix wait validation autofs4: Clean up autofs4_free_ino() autofs4: Clean up dentry operations autofs4: Clean up inode operations autofs4: Remove unused code autofs4: Add d_manage() dentry operation autofs4: Add d_automount() dentry operation Remove the automount through follow_link() kludge code from pathwalk CIFS: Use d_automount() rather than abusing follow_link() NFS: Use d_automount() rather than abusing follow_link() AFS: Use d_automount() rather than abusing follow_link() Add an AT_NO_AUTOMOUNT flag to suppress terminal automount ...
This commit is contained in:
commit
f8206b925f
@ -19,6 +19,8 @@ prototypes:
|
||||
void (*d_release)(struct dentry *);
|
||||
void (*d_iput)(struct dentry *, struct inode *);
|
||||
char *(*d_dname)((struct dentry *dentry, char *buffer, int buflen);
|
||||
struct vfsmount *(*d_automount)(struct path *path);
|
||||
int (*d_manage)(struct dentry *, bool);
|
||||
|
||||
locking rules:
|
||||
rename_lock ->d_lock may block rcu-walk
|
||||
@ -29,6 +31,8 @@ d_delete: no yes no no
|
||||
d_release: no no yes no
|
||||
d_iput: no no yes no
|
||||
d_dname: no no no no
|
||||
d_automount: no no yes no
|
||||
d_manage: no no yes (ref-walk) maybe
|
||||
|
||||
--------------------------- inode_operations ---------------------------
|
||||
prototypes:
|
||||
|
@ -864,6 +864,8 @@ struct dentry_operations {
|
||||
void (*d_release)(struct dentry *);
|
||||
void (*d_iput)(struct dentry *, struct inode *);
|
||||
char *(*d_dname)(struct dentry *, char *, int);
|
||||
struct vfsmount *(*d_automount)(struct path *);
|
||||
int (*d_manage)(struct dentry *, bool, bool);
|
||||
};
|
||||
|
||||
d_revalidate: called when the VFS needs to revalidate a dentry. This
|
||||
@ -930,6 +932,47 @@ struct dentry_operations {
|
||||
at the end of the buffer, and returns a pointer to the first char.
|
||||
dynamic_dname() helper function is provided to take care of this.
|
||||
|
||||
d_automount: called when an automount dentry is to be traversed (optional).
|
||||
This should create a new VFS mount record and return the record to the
|
||||
caller. The caller is supplied with a path parameter giving the
|
||||
automount directory to describe the automount target and the parent
|
||||
VFS mount record to provide inheritable mount parameters. NULL should
|
||||
be returned if someone else managed to make the automount first. If
|
||||
the vfsmount creation failed, then an error code should be returned.
|
||||
If -EISDIR is returned, then the directory will be treated as an
|
||||
ordinary directory and returned to pathwalk to 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
|
||||
dentry. This is set by __d_instantiate() if S_AUTOMOUNT is set on the
|
||||
inode being added.
|
||||
|
||||
d_manage: called to allow the filesystem to manage the transition from a
|
||||
dentry (optional). This allows autofs, for example, to hold up clients
|
||||
waiting to explore behind a 'mountpoint' whilst letting the daemon go
|
||||
past and construct the subtree there. 0 should be returned to let the
|
||||
calling process continue. -EISDIR can be returned to tell pathwalk to
|
||||
use this directory as an ordinary directory and to ignore anything
|
||||
mounted on it and not to check the automount flag. Any other error
|
||||
code will abort pathwalk completely.
|
||||
|
||||
If the 'mounting_here' parameter is true, then namespace_sem is being
|
||||
held by the caller and the function should not initiate any mounts or
|
||||
unmounts that it will then wait for.
|
||||
|
||||
If the 'rcu_walk' parameter is true, then the caller is doing a
|
||||
pathwalk in RCU-walk mode. Sleeping is not permitted in this mode,
|
||||
and the caller can be asked to leave it and call again by returing
|
||||
-ECHILD.
|
||||
|
||||
This function is only used if DCACHE_MANAGE_TRANSIT is set on the
|
||||
dentry being transited from.
|
||||
|
||||
Example :
|
||||
|
||||
static char *pipefs_dname(struct dentry *dent, char *buffer, int buflen)
|
||||
|
@ -1201,7 +1201,7 @@ err_unregister_chdev:
|
||||
static void __exit cleanup_mtdchar(void)
|
||||
{
|
||||
unregister_mtd_user(&mtdchar_notifier);
|
||||
mntput_long(mtd_inode_mnt);
|
||||
mntput(mtd_inode_mnt);
|
||||
unregister_filesystem(&mtd_inodefs_type);
|
||||
__unregister_chrdev(MTD_CHAR_MAJOR, 0, 1 << MINORBITS, "mtd");
|
||||
}
|
||||
|
@ -88,14 +88,13 @@ struct autofs_dir_ent *autofs_expire(struct super_block *sb,
|
||||
}
|
||||
path.mnt = mnt;
|
||||
path_get(&path);
|
||||
if (!follow_down(&path)) {
|
||||
if (!follow_down_one(&path)) {
|
||||
path_put(&path);
|
||||
DPRINTK(("autofs: not expirable\
|
||||
(not a mounted directory): %s\n", ent->name));
|
||||
continue;
|
||||
}
|
||||
while (d_mountpoint(path.dentry) && follow_down(&path))
|
||||
;
|
||||
follow_down(&path, false); // TODO: need to check error
|
||||
umount_ok = may_umount(path.mnt);
|
||||
path_put(&path);
|
||||
|
||||
|
@ -66,6 +66,7 @@ const struct dentry_operations afs_fs_dentry_operations = {
|
||||
.d_revalidate = afs_d_revalidate,
|
||||
.d_delete = afs_d_delete,
|
||||
.d_release = afs_d_release,
|
||||
.d_automount = afs_d_automount,
|
||||
};
|
||||
|
||||
#define AFS_DIR_HASHTBL_SIZE 128
|
||||
|
@ -184,7 +184,8 @@ struct inode *afs_iget_autocell(struct inode *dir, const char *dev_name,
|
||||
inode->i_generation = 0;
|
||||
|
||||
set_bit(AFS_VNODE_PSEUDODIR, &vnode->flags);
|
||||
inode->i_flags |= S_NOATIME;
|
||||
set_bit(AFS_VNODE_MOUNTPOINT, &vnode->flags);
|
||||
inode->i_flags |= S_AUTOMOUNT | S_NOATIME;
|
||||
unlock_new_inode(inode);
|
||||
_leave(" = %p", inode);
|
||||
return inode;
|
||||
|
@ -592,6 +592,7 @@ extern const struct inode_operations afs_mntpt_inode_operations;
|
||||
extern const struct inode_operations afs_autocell_inode_operations;
|
||||
extern const struct file_operations afs_mntpt_file_operations;
|
||||
|
||||
extern struct vfsmount *afs_d_automount(struct path *);
|
||||
extern int afs_mntpt_check_symlink(struct afs_vnode *, struct key *);
|
||||
extern void afs_mntpt_kill_timer(void);
|
||||
|
||||
|
@ -24,7 +24,6 @@ static struct dentry *afs_mntpt_lookup(struct inode *dir,
|
||||
struct dentry *dentry,
|
||||
struct nameidata *nd);
|
||||
static int afs_mntpt_open(struct inode *inode, struct file *file);
|
||||
static void *afs_mntpt_follow_link(struct dentry *dentry, struct nameidata *nd);
|
||||
static void afs_mntpt_expiry_timed_out(struct work_struct *work);
|
||||
|
||||
const struct file_operations afs_mntpt_file_operations = {
|
||||
@ -34,13 +33,11 @@ const struct file_operations afs_mntpt_file_operations = {
|
||||
|
||||
const struct inode_operations afs_mntpt_inode_operations = {
|
||||
.lookup = afs_mntpt_lookup,
|
||||
.follow_link = afs_mntpt_follow_link,
|
||||
.readlink = page_readlink,
|
||||
.getattr = afs_getattr,
|
||||
};
|
||||
|
||||
const struct inode_operations afs_autocell_inode_operations = {
|
||||
.follow_link = afs_mntpt_follow_link,
|
||||
.getattr = afs_getattr,
|
||||
};
|
||||
|
||||
@ -88,6 +85,7 @@ int afs_mntpt_check_symlink(struct afs_vnode *vnode, struct key *key)
|
||||
_debug("symlink is a mountpoint");
|
||||
spin_lock(&vnode->lock);
|
||||
set_bit(AFS_VNODE_MOUNTPOINT, &vnode->flags);
|
||||
vnode->vfs_inode.i_flags |= S_AUTOMOUNT;
|
||||
spin_unlock(&vnode->lock);
|
||||
}
|
||||
|
||||
@ -238,52 +236,24 @@ error_no_devname:
|
||||
}
|
||||
|
||||
/*
|
||||
* follow a link from a mountpoint directory, thus causing it to be mounted
|
||||
* handle an automount point
|
||||
*/
|
||||
static void *afs_mntpt_follow_link(struct dentry *dentry, struct nameidata *nd)
|
||||
struct vfsmount *afs_d_automount(struct path *path)
|
||||
{
|
||||
struct vfsmount *newmnt;
|
||||
int err;
|
||||
|
||||
_enter("%p{%s},{%s:%p{%s},}",
|
||||
dentry,
|
||||
dentry->d_name.name,
|
||||
nd->path.mnt->mnt_devname,
|
||||
dentry,
|
||||
nd->path.dentry->d_name.name);
|
||||
_enter("{%s,%s}", path->mnt->mnt_devname, path->dentry->d_name.name);
|
||||
|
||||
dput(nd->path.dentry);
|
||||
nd->path.dentry = dget(dentry);
|
||||
newmnt = afs_mntpt_do_automount(path->dentry);
|
||||
if (IS_ERR(newmnt))
|
||||
return newmnt;
|
||||
|
||||
newmnt = afs_mntpt_do_automount(nd->path.dentry);
|
||||
if (IS_ERR(newmnt)) {
|
||||
path_put(&nd->path);
|
||||
return (void *)newmnt;
|
||||
}
|
||||
|
||||
mntget(newmnt);
|
||||
err = do_add_mount(newmnt, &nd->path, MNT_SHRINKABLE, &afs_vfsmounts);
|
||||
switch (err) {
|
||||
case 0:
|
||||
path_put(&nd->path);
|
||||
nd->path.mnt = newmnt;
|
||||
nd->path.dentry = dget(newmnt->mnt_root);
|
||||
queue_delayed_work(afs_wq, &afs_mntpt_expiry_timer,
|
||||
afs_mntpt_expiry_timeout * HZ);
|
||||
break;
|
||||
case -EBUSY:
|
||||
/* someone else made a mount here whilst we were busy */
|
||||
while (d_mountpoint(nd->path.dentry) &&
|
||||
follow_down(&nd->path))
|
||||
;
|
||||
err = 0;
|
||||
default:
|
||||
mntput(newmnt);
|
||||
break;
|
||||
}
|
||||
|
||||
_leave(" = %d", err);
|
||||
return ERR_PTR(err);
|
||||
mntget(newmnt); /* prevent immediate expiration */
|
||||
mnt_set_expiry(newmnt, &afs_vfsmounts);
|
||||
queue_delayed_work(afs_wq, &afs_mntpt_expiry_timer,
|
||||
afs_mntpt_expiry_timeout * HZ);
|
||||
_leave(" = %p {%s}", newmnt, newmnt->mnt_devname);
|
||||
return newmnt;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -233,7 +233,7 @@ static int __init anon_inode_init(void)
|
||||
return 0;
|
||||
|
||||
err_mntput:
|
||||
mntput_long(anon_inode_mnt);
|
||||
mntput(anon_inode_mnt);
|
||||
err_unregister_filesystem:
|
||||
unregister_filesystem(&anon_inode_fs_type);
|
||||
err_exit:
|
||||
|
@ -99,7 +99,6 @@ struct autofs_info {
|
||||
};
|
||||
|
||||
#define AUTOFS_INF_EXPIRING (1<<0) /* dentry is in the process of expiring */
|
||||
#define AUTOFS_INF_MOUNTPOINT (1<<1) /* mountpoint status for direct expire */
|
||||
#define AUTOFS_INF_PENDING (1<<2) /* dentry pending mount */
|
||||
|
||||
struct autofs_wait_queue {
|
||||
@ -176,13 +175,6 @@ static inline int autofs4_ispending(struct dentry *dentry)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void autofs4_copy_atime(struct file *src, struct file *dst)
|
||||
{
|
||||
dst->f_path.dentry->d_inode->i_atime =
|
||||
src->f_path.dentry->d_inode->i_atime;
|
||||
return;
|
||||
}
|
||||
|
||||
struct inode *autofs4_get_inode(struct super_block *, struct autofs_info *);
|
||||
void autofs4_free_ino(struct autofs_info *);
|
||||
|
||||
@ -212,11 +204,83 @@ void autofs_dev_ioctl_exit(void);
|
||||
|
||||
extern const struct inode_operations autofs4_symlink_inode_operations;
|
||||
extern const struct inode_operations autofs4_dir_inode_operations;
|
||||
extern const struct inode_operations autofs4_root_inode_operations;
|
||||
extern const struct inode_operations autofs4_indirect_root_inode_operations;
|
||||
extern const struct inode_operations autofs4_direct_root_inode_operations;
|
||||
extern const struct file_operations autofs4_dir_operations;
|
||||
extern const struct file_operations autofs4_root_operations;
|
||||
extern const struct dentry_operations autofs4_dentry_operations;
|
||||
|
||||
/* VFS automount flags management functions */
|
||||
|
||||
static inline void __managed_dentry_set_automount(struct dentry *dentry)
|
||||
{
|
||||
dentry->d_flags |= DCACHE_NEED_AUTOMOUNT;
|
||||
}
|
||||
|
||||
static inline void managed_dentry_set_automount(struct dentry *dentry)
|
||||
{
|
||||
spin_lock(&dentry->d_lock);
|
||||
__managed_dentry_set_automount(dentry);
|
||||
spin_unlock(&dentry->d_lock);
|
||||
}
|
||||
|
||||
static inline void __managed_dentry_clear_automount(struct dentry *dentry)
|
||||
{
|
||||
dentry->d_flags &= ~DCACHE_NEED_AUTOMOUNT;
|
||||
}
|
||||
|
||||
static inline void managed_dentry_clear_automount(struct dentry *dentry)
|
||||
{
|
||||
spin_lock(&dentry->d_lock);
|
||||
__managed_dentry_clear_automount(dentry);
|
||||
spin_unlock(&dentry->d_lock);
|
||||
}
|
||||
|
||||
static inline void __managed_dentry_set_transit(struct dentry *dentry)
|
||||
{
|
||||
dentry->d_flags |= DCACHE_MANAGE_TRANSIT;
|
||||
}
|
||||
|
||||
static inline void managed_dentry_set_transit(struct dentry *dentry)
|
||||
{
|
||||
spin_lock(&dentry->d_lock);
|
||||
__managed_dentry_set_transit(dentry);
|
||||
spin_unlock(&dentry->d_lock);
|
||||
}
|
||||
|
||||
static inline void __managed_dentry_clear_transit(struct dentry *dentry)
|
||||
{
|
||||
dentry->d_flags &= ~DCACHE_MANAGE_TRANSIT;
|
||||
}
|
||||
|
||||
static inline void managed_dentry_clear_transit(struct dentry *dentry)
|
||||
{
|
||||
spin_lock(&dentry->d_lock);
|
||||
__managed_dentry_clear_transit(dentry);
|
||||
spin_unlock(&dentry->d_lock);
|
||||
}
|
||||
|
||||
static inline void __managed_dentry_set_managed(struct dentry *dentry)
|
||||
{
|
||||
dentry->d_flags |= (DCACHE_NEED_AUTOMOUNT|DCACHE_MANAGE_TRANSIT);
|
||||
}
|
||||
|
||||
static inline void managed_dentry_set_managed(struct dentry *dentry)
|
||||
{
|
||||
spin_lock(&dentry->d_lock);
|
||||
__managed_dentry_set_managed(dentry);
|
||||
spin_unlock(&dentry->d_lock);
|
||||
}
|
||||
|
||||
static inline void __managed_dentry_clear_managed(struct dentry *dentry)
|
||||
{
|
||||
dentry->d_flags &= ~(DCACHE_NEED_AUTOMOUNT|DCACHE_MANAGE_TRANSIT);
|
||||
}
|
||||
|
||||
static inline void managed_dentry_clear_managed(struct dentry *dentry)
|
||||
{
|
||||
spin_lock(&dentry->d_lock);
|
||||
__managed_dentry_clear_managed(dentry);
|
||||
spin_unlock(&dentry->d_lock);
|
||||
}
|
||||
|
||||
/* Initializing function */
|
||||
|
||||
@ -229,19 +293,6 @@ int autofs4_wait(struct autofs_sb_info *,struct dentry *, enum autofs_notify);
|
||||
int autofs4_wait_release(struct autofs_sb_info *,autofs_wqt_t,int);
|
||||
void autofs4_catatonic_mode(struct autofs_sb_info *);
|
||||
|
||||
static inline int autofs4_follow_mount(struct path *path)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
while (d_mountpoint(path->dentry)) {
|
||||
int followed = follow_down(path);
|
||||
if (!followed)
|
||||
break;
|
||||
res = 1;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static inline u32 autofs4_get_dev(struct autofs_sb_info *sbi)
|
||||
{
|
||||
return new_encode_dev(sbi->sb->s_dev);
|
||||
|
@ -551,7 +551,7 @@ static int autofs_dev_ioctl_ismountpoint(struct file *fp,
|
||||
|
||||
err = have_submounts(path.dentry);
|
||||
|
||||
if (follow_down(&path))
|
||||
if (follow_down_one(&path))
|
||||
magic = path.mnt->mnt_sb->s_magic;
|
||||
}
|
||||
|
||||
|
@ -26,10 +26,6 @@ static inline int autofs4_can_expire(struct dentry *dentry,
|
||||
if (ino == NULL)
|
||||
return 0;
|
||||
|
||||
/* No point expiring a pending mount */
|
||||
if (ino->flags & AUTOFS_INF_PENDING)
|
||||
return 0;
|
||||
|
||||
if (!do_now) {
|
||||
/* Too young to die */
|
||||
if (!timeout || time_after(ino->last_used + timeout, now))
|
||||
@ -56,7 +52,7 @@ static int autofs4_mount_busy(struct vfsmount *mnt, struct dentry *dentry)
|
||||
|
||||
path_get(&path);
|
||||
|
||||
if (!follow_down(&path))
|
||||
if (!follow_down_one(&path))
|
||||
goto done;
|
||||
|
||||
if (is_autofs4_dentry(path.dentry)) {
|
||||
@ -283,6 +279,7 @@ struct dentry *autofs4_expire_direct(struct super_block *sb,
|
||||
unsigned long timeout;
|
||||
struct dentry *root = dget(sb->s_root);
|
||||
int do_now = how & AUTOFS_EXP_IMMEDIATE;
|
||||
struct autofs_info *ino;
|
||||
|
||||
if (!root)
|
||||
return NULL;
|
||||
@ -291,19 +288,21 @@ struct dentry *autofs4_expire_direct(struct super_block *sb,
|
||||
timeout = sbi->exp_timeout;
|
||||
|
||||
spin_lock(&sbi->fs_lock);
|
||||
ino = autofs4_dentry_ino(root);
|
||||
/* No point expiring a pending mount */
|
||||
if (ino->flags & AUTOFS_INF_PENDING) {
|
||||
spin_unlock(&sbi->fs_lock);
|
||||
return NULL;
|
||||
}
|
||||
managed_dentry_set_transit(root);
|
||||
if (!autofs4_direct_busy(mnt, root, timeout, do_now)) {
|
||||
struct autofs_info *ino = autofs4_dentry_ino(root);
|
||||
if (d_mountpoint(root)) {
|
||||
ino->flags |= AUTOFS_INF_MOUNTPOINT;
|
||||
spin_lock(&root->d_lock);
|
||||
root->d_flags &= ~DCACHE_MOUNTED;
|
||||
spin_unlock(&root->d_lock);
|
||||
}
|
||||
ino->flags |= AUTOFS_INF_EXPIRING;
|
||||
init_completion(&ino->expire_complete);
|
||||
spin_unlock(&sbi->fs_lock);
|
||||
return root;
|
||||
}
|
||||
managed_dentry_clear_transit(root);
|
||||
spin_unlock(&sbi->fs_lock);
|
||||
dput(root);
|
||||
|
||||
@ -340,6 +339,10 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb,
|
||||
while ((dentry = get_next_positive_dentry(dentry, root))) {
|
||||
spin_lock(&sbi->fs_lock);
|
||||
ino = autofs4_dentry_ino(dentry);
|
||||
/* No point expiring a pending mount */
|
||||
if (ino->flags & AUTOFS_INF_PENDING)
|
||||
goto cont;
|
||||
managed_dentry_set_transit(dentry);
|
||||
|
||||
/*
|
||||
* Case 1: (i) indirect mount or top level pseudo direct mount
|
||||
@ -399,6 +402,8 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb,
|
||||
}
|
||||
}
|
||||
next:
|
||||
managed_dentry_clear_transit(dentry);
|
||||
cont:
|
||||
spin_unlock(&sbi->fs_lock);
|
||||
}
|
||||
return NULL;
|
||||
@ -479,6 +484,8 @@ int autofs4_expire_run(struct super_block *sb,
|
||||
spin_lock(&sbi->fs_lock);
|
||||
ino = autofs4_dentry_ino(dentry);
|
||||
ino->flags &= ~AUTOFS_INF_EXPIRING;
|
||||
if (!d_unhashed(dentry))
|
||||
managed_dentry_clear_transit(dentry);
|
||||
complete_all(&ino->expire_complete);
|
||||
spin_unlock(&sbi->fs_lock);
|
||||
|
||||
@ -504,18 +511,18 @@ int autofs4_do_expire_multi(struct super_block *sb, struct vfsmount *mnt,
|
||||
ret = autofs4_wait(sbi, dentry, NFY_EXPIRE);
|
||||
|
||||
spin_lock(&sbi->fs_lock);
|
||||
if (ino->flags & AUTOFS_INF_MOUNTPOINT) {
|
||||
spin_lock(&sb->s_root->d_lock);
|
||||
/*
|
||||
* If we haven't been expired away, then reset
|
||||
* mounted status.
|
||||
*/
|
||||
if (mnt->mnt_parent != mnt)
|
||||
sb->s_root->d_flags |= DCACHE_MOUNTED;
|
||||
spin_unlock(&sb->s_root->d_lock);
|
||||
ino->flags &= ~AUTOFS_INF_MOUNTPOINT;
|
||||
}
|
||||
ino->flags &= ~AUTOFS_INF_EXPIRING;
|
||||
spin_lock(&dentry->d_lock);
|
||||
if (ret)
|
||||
__managed_dentry_clear_transit(dentry);
|
||||
else {
|
||||
if ((IS_ROOT(dentry) ||
|
||||
(autofs_type_indirect(sbi->type) &&
|
||||
IS_ROOT(dentry->d_parent))) &&
|
||||
!(dentry->d_flags & DCACHE_NEED_AUTOMOUNT))
|
||||
__managed_dentry_set_automount(dentry);
|
||||
}
|
||||
spin_unlock(&dentry->d_lock);
|
||||
complete_all(&ino->expire_complete);
|
||||
spin_unlock(&sbi->fs_lock);
|
||||
dput(dentry);
|
||||
|
@ -45,7 +45,6 @@ struct autofs_info *autofs4_init_ino(struct autofs_info *ino,
|
||||
|
||||
if (!reinit) {
|
||||
ino->flags = 0;
|
||||
ino->inode = NULL;
|
||||
ino->dentry = NULL;
|
||||
ino->size = 0;
|
||||
INIT_LIST_HEAD(&ino->active);
|
||||
@ -76,19 +75,8 @@ struct autofs_info *autofs4_init_ino(struct autofs_info *ino,
|
||||
|
||||
void autofs4_free_ino(struct autofs_info *ino)
|
||||
{
|
||||
struct autofs_info *p_ino;
|
||||
|
||||
if (ino->dentry) {
|
||||
ino->dentry->d_fsdata = NULL;
|
||||
if (ino->dentry->d_inode) {
|
||||
struct dentry *parent = ino->dentry->d_parent;
|
||||
if (atomic_dec_and_test(&ino->count)) {
|
||||
p_ino = autofs4_dentry_ino(parent);
|
||||
if (p_ino && parent != ino->dentry)
|
||||
atomic_dec(&p_ino->count);
|
||||
}
|
||||
dput(ino->dentry);
|
||||
}
|
||||
ino->dentry = NULL;
|
||||
}
|
||||
if (ino->free)
|
||||
@ -251,10 +239,6 @@ static struct autofs_info *autofs4_mkroot(struct autofs_sb_info *sbi)
|
||||
return ino;
|
||||
}
|
||||
|
||||
static const struct dentry_operations autofs4_sb_dentry_operations = {
|
||||
.d_release = autofs4_dentry_release,
|
||||
};
|
||||
|
||||
int autofs4_fill_super(struct super_block *s, void *data, int silent)
|
||||
{
|
||||
struct inode * root_inode;
|
||||
@ -292,6 +276,7 @@ int autofs4_fill_super(struct super_block *s, void *data, int silent)
|
||||
s->s_blocksize_bits = 10;
|
||||
s->s_magic = AUTOFS_SUPER_MAGIC;
|
||||
s->s_op = &autofs4_sops;
|
||||
s->s_d_op = &autofs4_dentry_operations;
|
||||
s->s_time_gran = 1;
|
||||
|
||||
/*
|
||||
@ -309,7 +294,6 @@ int autofs4_fill_super(struct super_block *s, void *data, int silent)
|
||||
goto fail_iput;
|
||||
pipe = NULL;
|
||||
|
||||
d_set_d_op(root, &autofs4_sb_dentry_operations);
|
||||
root->d_fsdata = ino;
|
||||
|
||||
/* Can this call block? */
|
||||
@ -320,10 +304,11 @@ int autofs4_fill_super(struct super_block *s, void *data, int silent)
|
||||
goto fail_dput;
|
||||
}
|
||||
|
||||
if (autofs_type_trigger(sbi->type))
|
||||
__managed_dentry_set_managed(root);
|
||||
|
||||
root_inode->i_fop = &autofs4_root_operations;
|
||||
root_inode->i_op = autofs_type_trigger(sbi->type) ?
|
||||
&autofs4_direct_root_inode_operations :
|
||||
&autofs4_indirect_root_inode_operations;
|
||||
root_inode->i_op = &autofs4_dir_inode_operations;
|
||||
|
||||
/* Couldn't this be tested earlier? */
|
||||
if (sbi->max_proto < AUTOFS_MIN_PROTO_VERSION ||
|
||||
@ -391,7 +376,6 @@ struct inode *autofs4_get_inode(struct super_block *sb,
|
||||
if (inode == NULL)
|
||||
return NULL;
|
||||
|
||||
inf->inode = inode;
|
||||
inode->i_mode = inf->mode;
|
||||
if (sb->s_root) {
|
||||
inode->i_uid = sb->s_root->d_inode->i_uid;
|
||||
|
@ -35,10 +35,8 @@ static long autofs4_root_compat_ioctl(struct file *,unsigned int,unsigned long);
|
||||
#endif
|
||||
static int autofs4_dir_open(struct inode *inode, struct file *file);
|
||||
static struct dentry *autofs4_lookup(struct inode *,struct dentry *, struct nameidata *);
|
||||
static void *autofs4_follow_link(struct dentry *, struct nameidata *);
|
||||
|
||||
#define TRIGGER_FLAGS (LOOKUP_CONTINUE | LOOKUP_DIRECTORY)
|
||||
#define TRIGGER_INTENTS (LOOKUP_OPEN | LOOKUP_CREATE)
|
||||
static struct vfsmount *autofs4_d_automount(struct path *);
|
||||
static int autofs4_d_manage(struct dentry *, bool, bool);
|
||||
|
||||
const struct file_operations autofs4_root_operations = {
|
||||
.open = dcache_dir_open,
|
||||
@ -60,22 +58,6 @@ const struct file_operations autofs4_dir_operations = {
|
||||
.llseek = dcache_dir_lseek,
|
||||
};
|
||||
|
||||
const struct inode_operations autofs4_indirect_root_inode_operations = {
|
||||
.lookup = autofs4_lookup,
|
||||
.unlink = autofs4_dir_unlink,
|
||||
.symlink = autofs4_dir_symlink,
|
||||
.mkdir = autofs4_dir_mkdir,
|
||||
.rmdir = autofs4_dir_rmdir,
|
||||
};
|
||||
|
||||
const struct inode_operations autofs4_direct_root_inode_operations = {
|
||||
.lookup = autofs4_lookup,
|
||||
.unlink = autofs4_dir_unlink,
|
||||
.mkdir = autofs4_dir_mkdir,
|
||||
.rmdir = autofs4_dir_rmdir,
|
||||
.follow_link = autofs4_follow_link,
|
||||
};
|
||||
|
||||
const struct inode_operations autofs4_dir_inode_operations = {
|
||||
.lookup = autofs4_lookup,
|
||||
.unlink = autofs4_dir_unlink,
|
||||
@ -84,6 +66,12 @@ const struct inode_operations autofs4_dir_inode_operations = {
|
||||
.rmdir = autofs4_dir_rmdir,
|
||||
};
|
||||
|
||||
const struct dentry_operations autofs4_dentry_operations = {
|
||||
.d_automount = autofs4_d_automount,
|
||||
.d_manage = autofs4_d_manage,
|
||||
.d_release = autofs4_dentry_release,
|
||||
};
|
||||
|
||||
static void autofs4_add_active(struct dentry *dentry)
|
||||
{
|
||||
struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
|
||||
@ -116,14 +104,6 @@ static void autofs4_del_active(struct dentry *dentry)
|
||||
return;
|
||||
}
|
||||
|
||||
static unsigned int autofs4_need_mount(unsigned int flags)
|
||||
{
|
||||
unsigned int res = 0;
|
||||
if (flags & (TRIGGER_FLAGS | TRIGGER_INTENTS))
|
||||
res = 1;
|
||||
return res;
|
||||
}
|
||||
|
||||
static int autofs4_dir_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct dentry *dentry = file->f_path.dentry;
|
||||
@ -158,239 +138,6 @@ out:
|
||||
return dcache_dir_open(inode, file);
|
||||
}
|
||||
|
||||
static int try_to_fill_dentry(struct dentry *dentry, int flags)
|
||||
{
|
||||
struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
|
||||
struct autofs_info *ino = autofs4_dentry_ino(dentry);
|
||||
int status;
|
||||
|
||||
DPRINTK("dentry=%p %.*s ino=%p",
|
||||
dentry, dentry->d_name.len, dentry->d_name.name, dentry->d_inode);
|
||||
|
||||
/*
|
||||
* Wait for a pending mount, triggering one if there
|
||||
* isn't one already
|
||||
*/
|
||||
if (dentry->d_inode == NULL) {
|
||||
DPRINTK("waiting for mount name=%.*s",
|
||||
dentry->d_name.len, dentry->d_name.name);
|
||||
|
||||
status = autofs4_wait(sbi, dentry, NFY_MOUNT);
|
||||
|
||||
DPRINTK("mount done status=%d", status);
|
||||
|
||||
/* Turn this into a real negative dentry? */
|
||||
if (status == -ENOENT) {
|
||||
spin_lock(&sbi->fs_lock);
|
||||
ino->flags &= ~AUTOFS_INF_PENDING;
|
||||
spin_unlock(&sbi->fs_lock);
|
||||
return status;
|
||||
} else if (status) {
|
||||
/* Return a negative dentry, but leave it "pending" */
|
||||
return status;
|
||||
}
|
||||
/* Trigger mount for path component or follow link */
|
||||
} else if (ino->flags & AUTOFS_INF_PENDING ||
|
||||
autofs4_need_mount(flags)) {
|
||||
DPRINTK("waiting for mount name=%.*s",
|
||||
dentry->d_name.len, dentry->d_name.name);
|
||||
|
||||
spin_lock(&sbi->fs_lock);
|
||||
ino->flags |= AUTOFS_INF_PENDING;
|
||||
spin_unlock(&sbi->fs_lock);
|
||||
status = autofs4_wait(sbi, dentry, NFY_MOUNT);
|
||||
|
||||
DPRINTK("mount done status=%d", status);
|
||||
|
||||
if (status) {
|
||||
spin_lock(&sbi->fs_lock);
|
||||
ino->flags &= ~AUTOFS_INF_PENDING;
|
||||
spin_unlock(&sbi->fs_lock);
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
/* Initialize expiry counter after successful mount */
|
||||
ino->last_used = jiffies;
|
||||
|
||||
spin_lock(&sbi->fs_lock);
|
||||
ino->flags &= ~AUTOFS_INF_PENDING;
|
||||
spin_unlock(&sbi->fs_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* For autofs direct mounts the follow link triggers the mount */
|
||||
static void *autofs4_follow_link(struct dentry *dentry, struct nameidata *nd)
|
||||
{
|
||||
struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
|
||||
struct autofs_info *ino = autofs4_dentry_ino(dentry);
|
||||
int oz_mode = autofs4_oz_mode(sbi);
|
||||
unsigned int lookup_type;
|
||||
int status;
|
||||
|
||||
DPRINTK("dentry=%p %.*s oz_mode=%d nd->flags=%d",
|
||||
dentry, dentry->d_name.len, dentry->d_name.name, oz_mode,
|
||||
nd->flags);
|
||||
/*
|
||||
* For an expire of a covered direct or offset mount we need
|
||||
* to break out of follow_down() at the autofs mount trigger
|
||||
* (d_mounted--), so we can see the expiring flag, and manage
|
||||
* the blocking and following here until the expire is completed.
|
||||
*/
|
||||
if (oz_mode) {
|
||||
spin_lock(&sbi->fs_lock);
|
||||
if (ino->flags & AUTOFS_INF_EXPIRING) {
|
||||
spin_unlock(&sbi->fs_lock);
|
||||
/* Follow down to our covering mount. */
|
||||
if (!follow_down(&nd->path))
|
||||
goto done;
|
||||
goto follow;
|
||||
}
|
||||
spin_unlock(&sbi->fs_lock);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* If an expire request is pending everyone must wait. */
|
||||
autofs4_expire_wait(dentry);
|
||||
|
||||
/* We trigger a mount for almost all flags */
|
||||
lookup_type = autofs4_need_mount(nd->flags);
|
||||
spin_lock(&sbi->fs_lock);
|
||||
spin_lock(&autofs4_lock);
|
||||
spin_lock(&dentry->d_lock);
|
||||
if (!(lookup_type || ino->flags & AUTOFS_INF_PENDING)) {
|
||||
spin_unlock(&dentry->d_lock);
|
||||
spin_unlock(&autofs4_lock);
|
||||
spin_unlock(&sbi->fs_lock);
|
||||
goto follow;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the dentry contains directories then it is an autofs
|
||||
* multi-mount with no root mount offset. So don't try to
|
||||
* mount it again.
|
||||
*/
|
||||
if (ino->flags & AUTOFS_INF_PENDING ||
|
||||
(!d_mountpoint(dentry) && list_empty(&dentry->d_subdirs))) {
|
||||
spin_unlock(&dentry->d_lock);
|
||||
spin_unlock(&autofs4_lock);
|
||||
spin_unlock(&sbi->fs_lock);
|
||||
|
||||
status = try_to_fill_dentry(dentry, nd->flags);
|
||||
if (status)
|
||||
goto out_error;
|
||||
|
||||
goto follow;
|
||||
}
|
||||
spin_unlock(&dentry->d_lock);
|
||||
spin_unlock(&autofs4_lock);
|
||||
spin_unlock(&sbi->fs_lock);
|
||||
follow:
|
||||
/*
|
||||
* If there is no root mount it must be an autofs
|
||||
* multi-mount with no root offset so we don't need
|
||||
* to follow it.
|
||||
*/
|
||||
if (d_mountpoint(dentry)) {
|
||||
if (!autofs4_follow_mount(&nd->path)) {
|
||||
status = -ENOENT;
|
||||
goto out_error;
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
return NULL;
|
||||
|
||||
out_error:
|
||||
path_put(&nd->path);
|
||||
return ERR_PTR(status);
|
||||
}
|
||||
|
||||
/*
|
||||
* Revalidate is called on every cache lookup. Some of those
|
||||
* cache lookups may actually happen while the dentry is not
|
||||
* yet completely filled in, and revalidate has to delay such
|
||||
* lookups..
|
||||
*/
|
||||
static int autofs4_revalidate(struct dentry *dentry, struct nameidata *nd)
|
||||
{
|
||||
struct inode *dir;
|
||||
struct autofs_sb_info *sbi;
|
||||
int oz_mode;
|
||||
int flags = nd ? nd->flags : 0;
|
||||
int status = 1;
|
||||
|
||||
if (flags & LOOKUP_RCU)
|
||||
return -ECHILD;
|
||||
|
||||
dir = dentry->d_parent->d_inode;
|
||||
sbi = autofs4_sbi(dir->i_sb);
|
||||
oz_mode = autofs4_oz_mode(sbi);
|
||||
|
||||
/* Pending dentry */
|
||||
spin_lock(&sbi->fs_lock);
|
||||
if (autofs4_ispending(dentry)) {
|
||||
/* The daemon never causes a mount to trigger */
|
||||
spin_unlock(&sbi->fs_lock);
|
||||
|
||||
if (oz_mode)
|
||||
return 1;
|
||||
|
||||
/*
|
||||
* If the directory has gone away due to an expire
|
||||
* we have been called as ->d_revalidate() and so
|
||||
* we need to return false and proceed to ->lookup().
|
||||
*/
|
||||
if (autofs4_expire_wait(dentry) == -EAGAIN)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* A zero status is success otherwise we have a
|
||||
* negative error code.
|
||||
*/
|
||||
status = try_to_fill_dentry(dentry, flags);
|
||||
if (status == 0)
|
||||
return 1;
|
||||
|
||||
return status;
|
||||
}
|
||||
spin_unlock(&sbi->fs_lock);
|
||||
|
||||
/* Negative dentry.. invalidate if "old" */
|
||||
if (dentry->d_inode == NULL)
|
||||
return 0;
|
||||
|
||||
/* Check for a non-mountpoint directory with no contents */
|
||||
spin_lock(&autofs4_lock);
|
||||
spin_lock(&dentry->d_lock);
|
||||
if (S_ISDIR(dentry->d_inode->i_mode) &&
|
||||
!d_mountpoint(dentry) && list_empty(&dentry->d_subdirs)) {
|
||||
DPRINTK("dentry=%p %.*s, emptydir",
|
||||
dentry, dentry->d_name.len, dentry->d_name.name);
|
||||
spin_unlock(&dentry->d_lock);
|
||||
spin_unlock(&autofs4_lock);
|
||||
|
||||
/* The daemon never causes a mount to trigger */
|
||||
if (oz_mode)
|
||||
return 1;
|
||||
|
||||
/*
|
||||
* A zero status is success otherwise we have a
|
||||
* negative error code.
|
||||
*/
|
||||
status = try_to_fill_dentry(dentry, flags);
|
||||
if (status == 0)
|
||||
return 1;
|
||||
|
||||
return status;
|
||||
}
|
||||
spin_unlock(&dentry->d_lock);
|
||||
spin_unlock(&autofs4_lock);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void autofs4_dentry_release(struct dentry *de)
|
||||
{
|
||||
struct autofs_info *inf;
|
||||
@ -398,11 +145,8 @@ void autofs4_dentry_release(struct dentry *de)
|
||||
DPRINTK("releasing %p", de);
|
||||
|
||||
inf = autofs4_dentry_ino(de);
|
||||
de->d_fsdata = NULL;
|
||||
|
||||
if (inf) {
|
||||
struct autofs_sb_info *sbi = autofs4_sbi(de->d_sb);
|
||||
|
||||
if (sbi) {
|
||||
spin_lock(&sbi->lookup_lock);
|
||||
if (!list_empty(&inf->active))
|
||||
@ -411,26 +155,10 @@ void autofs4_dentry_release(struct dentry *de)
|
||||
list_del(&inf->expiring);
|
||||
spin_unlock(&sbi->lookup_lock);
|
||||
}
|
||||
|
||||
inf->dentry = NULL;
|
||||
inf->inode = NULL;
|
||||
|
||||
autofs4_free_ino(inf);
|
||||
}
|
||||
}
|
||||
|
||||
/* For dentries of directories in the root dir */
|
||||
static const struct dentry_operations autofs4_root_dentry_operations = {
|
||||
.d_revalidate = autofs4_revalidate,
|
||||
.d_release = autofs4_dentry_release,
|
||||
};
|
||||
|
||||
/* For other dentries */
|
||||
static const struct dentry_operations autofs4_dentry_operations = {
|
||||
.d_revalidate = autofs4_revalidate,
|
||||
.d_release = autofs4_dentry_release,
|
||||
};
|
||||
|
||||
static struct dentry *autofs4_lookup_active(struct dentry *dentry)
|
||||
{
|
||||
struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
|
||||
@ -541,50 +269,244 @@ next:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int autofs4_mount_wait(struct dentry *dentry)
|
||||
{
|
||||
struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
|
||||
struct autofs_info *ino = autofs4_dentry_ino(dentry);
|
||||
int status;
|
||||
|
||||
if (ino->flags & AUTOFS_INF_PENDING) {
|
||||
DPRINTK("waiting for mount name=%.*s",
|
||||
dentry->d_name.len, dentry->d_name.name);
|
||||
status = autofs4_wait(sbi, dentry, NFY_MOUNT);
|
||||
DPRINTK("mount wait done status=%d", status);
|
||||
ino->last_used = jiffies;
|
||||
return status;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_expire_wait(struct dentry *dentry)
|
||||
{
|
||||
struct dentry *expiring;
|
||||
|
||||
expiring = autofs4_lookup_expiring(dentry);
|
||||
if (!expiring)
|
||||
return autofs4_expire_wait(dentry);
|
||||
else {
|
||||
/*
|
||||
* If we are racing with expire the request might not
|
||||
* be quite complete, but the directory has been removed
|
||||
* so it must have been successful, just wait for it.
|
||||
*/
|
||||
autofs4_expire_wait(expiring);
|
||||
autofs4_del_expiring(expiring);
|
||||
dput(expiring);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct dentry *autofs4_mountpoint_changed(struct path *path)
|
||||
{
|
||||
struct dentry *dentry = path->dentry;
|
||||
struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
|
||||
|
||||
/*
|
||||
* If this is an indirect mount the dentry could have gone away
|
||||
* as a result of an expire and a new one created.
|
||||
*/
|
||||
if (autofs_type_indirect(sbi->type) && d_unhashed(dentry)) {
|
||||
struct dentry *parent = dentry->d_parent;
|
||||
struct dentry *new = d_lookup(parent, &dentry->d_name);
|
||||
if (!new)
|
||||
return NULL;
|
||||
dput(path->dentry);
|
||||
path->dentry = new;
|
||||
}
|
||||
return path->dentry;
|
||||
}
|
||||
|
||||
static struct vfsmount *autofs4_d_automount(struct path *path)
|
||||
{
|
||||
struct dentry *dentry = path->dentry;
|
||||
struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
|
||||
struct autofs_info *ino = autofs4_dentry_ino(dentry);
|
||||
int status;
|
||||
|
||||
DPRINTK("dentry=%p %.*s",
|
||||
dentry, dentry->d_name.len, dentry->d_name.name);
|
||||
|
||||
/*
|
||||
* Someone may have manually umounted this or it was a submount
|
||||
* that has gone away.
|
||||
*/
|
||||
spin_lock(&dentry->d_lock);
|
||||
if (!d_mountpoint(dentry) && list_empty(&dentry->d_subdirs)) {
|
||||
if (!(dentry->d_flags & DCACHE_MANAGE_TRANSIT) &&
|
||||
(dentry->d_flags & DCACHE_NEED_AUTOMOUNT))
|
||||
__managed_dentry_set_transit(path->dentry);
|
||||
}
|
||||
spin_unlock(&dentry->d_lock);
|
||||
|
||||
/* The daemon never triggers a mount. */
|
||||
if (autofs4_oz_mode(sbi))
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* If an expire request is pending everyone must wait.
|
||||
* If the expire fails we're still mounted so continue
|
||||
* the follow and return. A return of -EAGAIN (which only
|
||||
* happens with indirect mounts) means the expire completed
|
||||
* and the directory was removed, so just go ahead and try
|
||||
* the mount.
|
||||
*/
|
||||
status = do_expire_wait(dentry);
|
||||
if (status && status != -EAGAIN)
|
||||
return NULL;
|
||||
|
||||
/* Callback to the daemon to perform the mount or wait */
|
||||
spin_lock(&sbi->fs_lock);
|
||||
if (ino->flags & AUTOFS_INF_PENDING) {
|
||||
spin_unlock(&sbi->fs_lock);
|
||||
status = autofs4_mount_wait(dentry);
|
||||
if (status)
|
||||
return ERR_PTR(status);
|
||||
spin_lock(&sbi->fs_lock);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the dentry is a symlink it's equivalent to a directory
|
||||
* having d_mountpoint() true, so there's no need to call back
|
||||
* to the daemon.
|
||||
*/
|
||||
if (dentry->d_inode && S_ISLNK(dentry->d_inode->i_mode))
|
||||
goto done;
|
||||
if (!d_mountpoint(dentry)) {
|
||||
/*
|
||||
* It's possible that user space hasn't removed directories
|
||||
* after umounting a rootless multi-mount, although it
|
||||
* should. For v5 have_submounts() is sufficient to handle
|
||||
* this because the leaves of the directory tree under the
|
||||
* mount never trigger mounts themselves (they have an autofs
|
||||
* trigger mount mounted on them). But v4 pseudo direct mounts
|
||||
* do need the leaves to to trigger mounts. In this case we
|
||||
* have no choice but to use the list_empty() check and
|
||||
* require user space behave.
|
||||
*/
|
||||
if (sbi->version > 4) {
|
||||
if (have_submounts(dentry))
|
||||
goto done;
|
||||
} else {
|
||||
spin_lock(&dentry->d_lock);
|
||||
if (!list_empty(&dentry->d_subdirs)) {
|
||||
spin_unlock(&dentry->d_lock);
|
||||
goto done;
|
||||
}
|
||||
spin_unlock(&dentry->d_lock);
|
||||
}
|
||||
ino->flags |= AUTOFS_INF_PENDING;
|
||||
spin_unlock(&sbi->fs_lock);
|
||||
status = autofs4_mount_wait(dentry);
|
||||
if (status)
|
||||
return ERR_PTR(status);
|
||||
spin_lock(&sbi->fs_lock);
|
||||
ino->flags &= ~AUTOFS_INF_PENDING;
|
||||
}
|
||||
done:
|
||||
if (!(ino->flags & AUTOFS_INF_EXPIRING)) {
|
||||
/*
|
||||
* Any needed mounting has been completed and the path updated
|
||||
* so turn this into a normal dentry so we don't continually
|
||||
* call ->d_automount() and ->d_manage().
|
||||
*/
|
||||
spin_lock(&dentry->d_lock);
|
||||
__managed_dentry_clear_transit(dentry);
|
||||
/*
|
||||
* Only clear DMANAGED_AUTOMOUNT for rootless multi-mounts and
|
||||
* symlinks as in all other cases the dentry will be covered by
|
||||
* an actual mount so ->d_automount() won't be called during
|
||||
* the follow.
|
||||
*/
|
||||
if ((!d_mountpoint(dentry) &&
|
||||
!list_empty(&dentry->d_subdirs)) ||
|
||||
(dentry->d_inode && S_ISLNK(dentry->d_inode->i_mode)))
|
||||
__managed_dentry_clear_automount(dentry);
|
||||
spin_unlock(&dentry->d_lock);
|
||||
}
|
||||
spin_unlock(&sbi->fs_lock);
|
||||
|
||||
/* Mount succeeded, check if we ended up with a new dentry */
|
||||
dentry = autofs4_mountpoint_changed(path);
|
||||
if (!dentry)
|
||||
return ERR_PTR(-ENOENT);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int autofs4_d_manage(struct dentry *dentry, bool mounting_here, bool rcu_walk)
|
||||
{
|
||||
struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
|
||||
|
||||
DPRINTK("dentry=%p %.*s",
|
||||
dentry, dentry->d_name.len, dentry->d_name.name);
|
||||
|
||||
/* The daemon never waits. */
|
||||
if (autofs4_oz_mode(sbi) || mounting_here) {
|
||||
if (!d_mountpoint(dentry))
|
||||
return -EISDIR;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* We need to sleep, so we need pathwalk to be in ref-mode */
|
||||
if (rcu_walk)
|
||||
return -ECHILD;
|
||||
|
||||
/* Wait for pending expires */
|
||||
do_expire_wait(dentry);
|
||||
|
||||
/*
|
||||
* This dentry may be under construction so wait on mount
|
||||
* completion.
|
||||
*/
|
||||
return autofs4_mount_wait(dentry);
|
||||
}
|
||||
|
||||
/* Lookups in the root directory */
|
||||
static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
|
||||
{
|
||||
struct autofs_sb_info *sbi;
|
||||
struct autofs_info *ino;
|
||||
struct dentry *expiring, *active;
|
||||
int oz_mode;
|
||||
struct dentry *active;
|
||||
|
||||
DPRINTK("name = %.*s",
|
||||
dentry->d_name.len, dentry->d_name.name);
|
||||
DPRINTK("name = %.*s", dentry->d_name.len, dentry->d_name.name);
|
||||
|
||||
/* File name too long to exist */
|
||||
if (dentry->d_name.len > NAME_MAX)
|
||||
return ERR_PTR(-ENAMETOOLONG);
|
||||
|
||||
sbi = autofs4_sbi(dir->i_sb);
|
||||
oz_mode = autofs4_oz_mode(sbi);
|
||||
|
||||
DPRINTK("pid = %u, pgrp = %u, catatonic = %d, oz_mode = %d",
|
||||
current->pid, task_pgrp_nr(current), sbi->catatonic, oz_mode);
|
||||
current->pid, task_pgrp_nr(current), sbi->catatonic, oz_mode);
|
||||
|
||||
active = autofs4_lookup_active(dentry);
|
||||
if (active) {
|
||||
dentry = active;
|
||||
ino = autofs4_dentry_ino(dentry);
|
||||
return active;
|
||||
} else {
|
||||
/*
|
||||
* Mark the dentry incomplete but don't hash it. We do this
|
||||
* to serialize our inode creation operations (symlink and
|
||||
* mkdir) which prevents deadlock during the callback to
|
||||
* the daemon. Subsequent user space lookups for the same
|
||||
* dentry are placed on the wait queue while the daemon
|
||||
* itself is allowed passage unresticted so the create
|
||||
* operation itself can then hash the dentry. Finally,
|
||||
* we check for the hashed dentry and return the newly
|
||||
* hashed dentry.
|
||||
* A dentry that is not within the root can never trigger a
|
||||
* mount operation, unless the directory already exists, so we
|
||||
* can return fail immediately. The daemon however does need
|
||||
* to create directories within the file system.
|
||||
*/
|
||||
d_set_d_op(dentry, &autofs4_root_dentry_operations);
|
||||
if (!autofs4_oz_mode(sbi) && !IS_ROOT(dentry->d_parent))
|
||||
return ERR_PTR(-ENOENT);
|
||||
|
||||
/* Mark entries in the root as mount triggers */
|
||||
if (autofs_type_indirect(sbi->type) && IS_ROOT(dentry->d_parent))
|
||||
__managed_dentry_set_managed(dentry);
|
||||
|
||||
/*
|
||||
* And we need to ensure that the same dentry is used for
|
||||
* all following lookup calls until it is hashed so that
|
||||
* the dentry flags are persistent throughout the request.
|
||||
*/
|
||||
ino = autofs4_init_ino(NULL, sbi, 0555);
|
||||
if (!ino)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
@ -596,82 +518,6 @@ static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, s
|
||||
|
||||
d_instantiate(dentry, NULL);
|
||||
}
|
||||
|
||||
if (!oz_mode) {
|
||||
mutex_unlock(&dir->i_mutex);
|
||||
expiring = autofs4_lookup_expiring(dentry);
|
||||
if (expiring) {
|
||||
/*
|
||||
* If we are racing with expire the request might not
|
||||
* be quite complete but the directory has been removed
|
||||
* so it must have been successful, so just wait for it.
|
||||
*/
|
||||
autofs4_expire_wait(expiring);
|
||||
autofs4_del_expiring(expiring);
|
||||
dput(expiring);
|
||||
}
|
||||
|
||||
spin_lock(&sbi->fs_lock);
|
||||
ino->flags |= AUTOFS_INF_PENDING;
|
||||
spin_unlock(&sbi->fs_lock);
|
||||
if (dentry->d_op && dentry->d_op->d_revalidate)
|
||||
(dentry->d_op->d_revalidate)(dentry, nd);
|
||||
mutex_lock(&dir->i_mutex);
|
||||
}
|
||||
|
||||
/*
|
||||
* If we are still pending, check if we had to handle
|
||||
* a signal. If so we can force a restart..
|
||||
*/
|
||||
if (ino->flags & AUTOFS_INF_PENDING) {
|
||||
/* See if we were interrupted */
|
||||
if (signal_pending(current)) {
|
||||
sigset_t *sigset = ¤t->pending.signal;
|
||||
if (sigismember (sigset, SIGKILL) ||
|
||||
sigismember (sigset, SIGQUIT) ||
|
||||
sigismember (sigset, SIGINT)) {
|
||||
if (active)
|
||||
dput(active);
|
||||
return ERR_PTR(-ERESTARTNOINTR);
|
||||
}
|
||||
}
|
||||
if (!oz_mode) {
|
||||
spin_lock(&sbi->fs_lock);
|
||||
ino->flags &= ~AUTOFS_INF_PENDING;
|
||||
spin_unlock(&sbi->fs_lock);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If this dentry is unhashed, then we shouldn't honour this
|
||||
* lookup. Returning ENOENT here doesn't do the right thing
|
||||
* for all system calls, but it should be OK for the operations
|
||||
* we permit from an autofs.
|
||||
*/
|
||||
if (!oz_mode && d_unhashed(dentry)) {
|
||||
/*
|
||||
* A user space application can (and has done in the past)
|
||||
* remove and re-create this directory during the callback.
|
||||
* This can leave us with an unhashed dentry, but a
|
||||
* successful mount! So we need to perform another
|
||||
* cached lookup in case the dentry now exists.
|
||||
*/
|
||||
struct dentry *parent = dentry->d_parent;
|
||||
struct dentry *new = d_lookup(parent, &dentry->d_name);
|
||||
if (new != NULL)
|
||||
dentry = new;
|
||||
else
|
||||
dentry = ERR_PTR(-ENOENT);
|
||||
|
||||
if (active)
|
||||
dput(active);
|
||||
|
||||
return dentry;
|
||||
}
|
||||
|
||||
if (active)
|
||||
return active;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -716,18 +562,12 @@ static int autofs4_dir_symlink(struct inode *dir,
|
||||
}
|
||||
d_add(dentry, inode);
|
||||
|
||||
if (dir == dir->i_sb->s_root->d_inode)
|
||||
d_set_d_op(dentry, &autofs4_root_dentry_operations);
|
||||
else
|
||||
d_set_d_op(dentry, &autofs4_dentry_operations);
|
||||
|
||||
dentry->d_fsdata = ino;
|
||||
ino->dentry = dget(dentry);
|
||||
atomic_inc(&ino->count);
|
||||
p_ino = autofs4_dentry_ino(dentry->d_parent);
|
||||
if (p_ino && dentry->d_parent != dentry)
|
||||
atomic_inc(&p_ino->count);
|
||||
ino->inode = inode;
|
||||
|
||||
ino->u.symlink = cp;
|
||||
dir->i_mtime = CURRENT_TIME;
|
||||
@ -782,6 +622,58 @@ static int autofs4_dir_unlink(struct inode *dir, struct dentry *dentry)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Version 4 of autofs provides a pseudo direct mount implementation
|
||||
* that relies on directories at the leaves of a directory tree under
|
||||
* an indirect mount to trigger mounts. To allow for this we need to
|
||||
* set the DMANAGED_AUTOMOUNT and DMANAGED_TRANSIT flags on the leaves
|
||||
* of the directory tree. There is no need to clear the automount flag
|
||||
* following a mount or restore it after an expire because these mounts
|
||||
* are always covered. However, it is neccessary to ensure that these
|
||||
* flags are clear on non-empty directories to avoid unnecessary calls
|
||||
* during path walks.
|
||||
*/
|
||||
static void autofs_set_leaf_automount_flags(struct dentry *dentry)
|
||||
{
|
||||
struct dentry *parent;
|
||||
|
||||
/* root and dentrys in the root are already handled */
|
||||
if (IS_ROOT(dentry->d_parent))
|
||||
return;
|
||||
|
||||
managed_dentry_set_managed(dentry);
|
||||
|
||||
parent = dentry->d_parent;
|
||||
/* only consider parents below dentrys in the root */
|
||||
if (IS_ROOT(parent->d_parent))
|
||||
return;
|
||||
managed_dentry_clear_managed(parent);
|
||||
return;
|
||||
}
|
||||
|
||||
static void autofs_clear_leaf_automount_flags(struct dentry *dentry)
|
||||
{
|
||||
struct list_head *d_child;
|
||||
struct dentry *parent;
|
||||
|
||||
/* flags for dentrys in the root are handled elsewhere */
|
||||
if (IS_ROOT(dentry->d_parent))
|
||||
return;
|
||||
|
||||
managed_dentry_clear_managed(dentry);
|
||||
|
||||
parent = dentry->d_parent;
|
||||
/* only consider parents below dentrys in the root */
|
||||
if (IS_ROOT(parent->d_parent))
|
||||
return;
|
||||
d_child = &dentry->d_u.d_child;
|
||||
/* Set parent managed if it's becoming empty */
|
||||
if (d_child->next == &parent->d_subdirs &&
|
||||
d_child->prev == &parent->d_subdirs)
|
||||
managed_dentry_set_managed(parent);
|
||||
return;
|
||||
}
|
||||
|
||||
static int autofs4_dir_rmdir(struct inode *dir, struct dentry *dentry)
|
||||
{
|
||||
struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb);
|
||||
@ -809,6 +701,9 @@ static int autofs4_dir_rmdir(struct inode *dir, struct dentry *dentry)
|
||||
spin_unlock(&dentry->d_lock);
|
||||
spin_unlock(&autofs4_lock);
|
||||
|
||||
if (sbi->version < 5)
|
||||
autofs_clear_leaf_automount_flags(dentry);
|
||||
|
||||
if (atomic_dec_and_test(&ino->count)) {
|
||||
p_ino = autofs4_dentry_ino(dentry->d_parent);
|
||||
if (p_ino && dentry->d_parent != dentry)
|
||||
@ -851,10 +746,8 @@ static int autofs4_dir_mkdir(struct inode *dir, struct dentry *dentry, int mode)
|
||||
}
|
||||
d_add(dentry, inode);
|
||||
|
||||
if (dir == dir->i_sb->s_root->d_inode)
|
||||
d_set_d_op(dentry, &autofs4_root_dentry_operations);
|
||||
else
|
||||
d_set_d_op(dentry, &autofs4_dentry_operations);
|
||||
if (sbi->version < 5)
|
||||
autofs_set_leaf_automount_flags(dentry);
|
||||
|
||||
dentry->d_fsdata = ino;
|
||||
ino->dentry = dget(dentry);
|
||||
@ -862,7 +755,6 @@ static int autofs4_dir_mkdir(struct inode *dir, struct dentry *dentry, int mode)
|
||||
p_ino = autofs4_dentry_ino(dentry->d_parent);
|
||||
if (p_ino && dentry->d_parent != dentry)
|
||||
atomic_inc(&p_ino->count);
|
||||
ino->inode = inode;
|
||||
inc_nlink(dir);
|
||||
dir->i_mtime = CURRENT_TIME;
|
||||
|
||||
@ -944,8 +836,7 @@ static inline int autofs4_ask_umount(struct vfsmount *mnt, int __user *p)
|
||||
int is_autofs4_dentry(struct dentry *dentry)
|
||||
{
|
||||
return dentry && dentry->d_inode &&
|
||||
(dentry->d_op == &autofs4_root_dentry_operations ||
|
||||
dentry->d_op == &autofs4_dentry_operations) &&
|
||||
dentry->d_op == &autofs4_dentry_operations &&
|
||||
dentry->d_fsdata != NULL;
|
||||
}
|
||||
|
||||
|
@ -309,6 +309,9 @@ static int validate_request(struct autofs_wait_queue **wait,
|
||||
* completed while we waited on the mutex ...
|
||||
*/
|
||||
if (notify == NFY_MOUNT) {
|
||||
struct dentry *new = NULL;
|
||||
int valid = 1;
|
||||
|
||||
/*
|
||||
* If the dentry was successfully mounted while we slept
|
||||
* on the wait queue mutex we can return success. If it
|
||||
@ -316,8 +319,20 @@ static int validate_request(struct autofs_wait_queue **wait,
|
||||
* a multi-mount with no mount at it's base) we can
|
||||
* continue on and create a new request.
|
||||
*/
|
||||
if (!IS_ROOT(dentry)) {
|
||||
if (dentry->d_inode && d_unhashed(dentry)) {
|
||||
struct dentry *parent = dentry->d_parent;
|
||||
new = d_lookup(parent, &dentry->d_name);
|
||||
if (new)
|
||||
dentry = new;
|
||||
}
|
||||
}
|
||||
if (have_submounts(dentry))
|
||||
return 0;
|
||||
valid = 0;
|
||||
|
||||
if (new)
|
||||
dput(new);
|
||||
return valid;
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
@ -255,35 +255,6 @@ static struct vfsmount *cifs_dfs_do_refmount(struct cifs_sb_info *cifs_sb,
|
||||
|
||||
}
|
||||
|
||||
static int add_mount_helper(struct vfsmount *newmnt, struct nameidata *nd,
|
||||
struct list_head *mntlist)
|
||||
{
|
||||
/* stolen from afs code */
|
||||
int err;
|
||||
|
||||
mntget(newmnt);
|
||||
err = do_add_mount(newmnt, &nd->path, nd->path.mnt->mnt_flags | MNT_SHRINKABLE, mntlist);
|
||||
switch (err) {
|
||||
case 0:
|
||||
path_put(&nd->path);
|
||||
nd->path.mnt = newmnt;
|
||||
nd->path.dentry = dget(newmnt->mnt_root);
|
||||
schedule_delayed_work(&cifs_dfs_automount_task,
|
||||
cifs_dfs_mountpoint_expiry_timeout);
|
||||
break;
|
||||
case -EBUSY:
|
||||
/* someone else made a mount here whilst we were busy */
|
||||
while (d_mountpoint(nd->path.dentry) &&
|
||||
follow_down(&nd->path))
|
||||
;
|
||||
err = 0;
|
||||
default:
|
||||
mntput(newmnt);
|
||||
break;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static void dump_referral(const struct dfs_info3_param *ref)
|
||||
{
|
||||
cFYI(1, "DFS: ref path: %s", ref->path_name);
|
||||
@ -293,45 +264,43 @@ static void dump_referral(const struct dfs_info3_param *ref)
|
||||
ref->path_consumed);
|
||||
}
|
||||
|
||||
|
||||
static void*
|
||||
cifs_dfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd)
|
||||
/*
|
||||
* Create a vfsmount that we can automount
|
||||
*/
|
||||
static struct vfsmount *cifs_dfs_do_automount(struct dentry *mntpt)
|
||||
{
|
||||
struct dfs_info3_param *referrals = NULL;
|
||||
unsigned int num_referrals = 0;
|
||||
struct cifs_sb_info *cifs_sb;
|
||||
struct cifsSesInfo *ses;
|
||||
char *full_path = NULL;
|
||||
char *full_path;
|
||||
int xid, i;
|
||||
int rc = 0;
|
||||
struct vfsmount *mnt = ERR_PTR(-ENOENT);
|
||||
int rc;
|
||||
struct vfsmount *mnt;
|
||||
struct tcon_link *tlink;
|
||||
|
||||
cFYI(1, "in %s", __func__);
|
||||
BUG_ON(IS_ROOT(dentry));
|
||||
BUG_ON(IS_ROOT(mntpt));
|
||||
|
||||
xid = GetXid();
|
||||
|
||||
dput(nd->path.dentry);
|
||||
nd->path.dentry = dget(dentry);
|
||||
|
||||
/*
|
||||
* The MSDFS spec states that paths in DFS referral requests and
|
||||
* responses must be prefixed by a single '\' character instead of
|
||||
* the double backslashes usually used in the UNC. This function
|
||||
* gives us the latter, so we must adjust the result.
|
||||
*/
|
||||
full_path = build_path_from_dentry(dentry);
|
||||
if (full_path == NULL) {
|
||||
rc = -ENOMEM;
|
||||
goto out_err;
|
||||
}
|
||||
mnt = ERR_PTR(-ENOMEM);
|
||||
full_path = build_path_from_dentry(mntpt);
|
||||
if (full_path == NULL)
|
||||
goto free_xid;
|
||||
|
||||
cifs_sb = CIFS_SB(dentry->d_inode->i_sb);
|
||||
cifs_sb = CIFS_SB(mntpt->d_inode->i_sb);
|
||||
tlink = cifs_sb_tlink(cifs_sb);
|
||||
mnt = ERR_PTR(-EINVAL);
|
||||
if (IS_ERR(tlink)) {
|
||||
rc = PTR_ERR(tlink);
|
||||
goto out_err;
|
||||
mnt = ERR_CAST(tlink);
|
||||
goto free_full_path;
|
||||
}
|
||||
ses = tlink_tcon(tlink)->ses;
|
||||
|
||||
@ -341,46 +310,63 @@ cifs_dfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd)
|
||||
|
||||
cifs_put_tlink(tlink);
|
||||
|
||||
mnt = ERR_PTR(-ENOENT);
|
||||
for (i = 0; i < num_referrals; i++) {
|
||||
int len;
|
||||
dump_referral(referrals+i);
|
||||
dump_referral(referrals + i);
|
||||
/* connect to a node */
|
||||
len = strlen(referrals[i].node_name);
|
||||
if (len < 2) {
|
||||
cERROR(1, "%s: Net Address path too short: %s",
|
||||
__func__, referrals[i].node_name);
|
||||
rc = -EINVAL;
|
||||
goto out_err;
|
||||
mnt = ERR_PTR(-EINVAL);
|
||||
break;
|
||||
}
|
||||
mnt = cifs_dfs_do_refmount(cifs_sb,
|
||||
full_path, referrals + i);
|
||||
cFYI(1, "%s: cifs_dfs_do_refmount:%s , mnt:%p", __func__,
|
||||
referrals[i].node_name, mnt);
|
||||
|
||||
/* complete mount procedure if we accured submount */
|
||||
if (!IS_ERR(mnt))
|
||||
break;
|
||||
goto success;
|
||||
}
|
||||
|
||||
/* we need it cause for() above could exit without valid submount */
|
||||
rc = PTR_ERR(mnt);
|
||||
if (IS_ERR(mnt))
|
||||
goto out_err;
|
||||
/* no valid submounts were found; return error from get_dfs_path() by
|
||||
* preference */
|
||||
if (rc != 0)
|
||||
mnt = ERR_PTR(rc);
|
||||
|
||||
rc = add_mount_helper(mnt, nd, &cifs_dfs_automount_list);
|
||||
|
||||
out:
|
||||
FreeXid(xid);
|
||||
success:
|
||||
free_dfs_info_array(referrals, num_referrals);
|
||||
free_full_path:
|
||||
kfree(full_path);
|
||||
free_xid:
|
||||
FreeXid(xid);
|
||||
cFYI(1, "leaving %s" , __func__);
|
||||
return ERR_PTR(rc);
|
||||
out_err:
|
||||
path_put(&nd->path);
|
||||
goto out;
|
||||
return mnt;
|
||||
}
|
||||
|
||||
/*
|
||||
* Attempt to automount the referral
|
||||
*/
|
||||
struct vfsmount *cifs_dfs_d_automount(struct path *path)
|
||||
{
|
||||
struct vfsmount *newmnt;
|
||||
|
||||
cFYI(1, "in %s", __func__);
|
||||
|
||||
newmnt = cifs_dfs_do_automount(path->dentry);
|
||||
if (IS_ERR(newmnt)) {
|
||||
cFYI(1, "leaving %s [automount failed]" , __func__);
|
||||
return newmnt;
|
||||
}
|
||||
|
||||
mntget(newmnt); /* prevent immediate expiration */
|
||||
mnt_set_expiry(newmnt, &cifs_dfs_automount_list);
|
||||
schedule_delayed_work(&cifs_dfs_automount_task,
|
||||
cifs_dfs_mountpoint_expiry_timeout);
|
||||
cFYI(1, "leaving %s [ok]" , __func__);
|
||||
return newmnt;
|
||||
}
|
||||
|
||||
const struct inode_operations cifs_dfs_referral_inode_operations = {
|
||||
.follow_link = cifs_dfs_follow_mountpoint,
|
||||
};
|
||||
|
||||
|
@ -93,6 +93,12 @@ extern int cifs_readdir(struct file *file, void *direntry, filldir_t filldir);
|
||||
extern const struct dentry_operations cifs_dentry_ops;
|
||||
extern const struct dentry_operations cifs_ci_dentry_ops;
|
||||
|
||||
#ifdef CONFIG_CIFS_DFS_UPCALL
|
||||
extern struct vfsmount *cifs_dfs_d_automount(struct path *path);
|
||||
#else
|
||||
#define cifs_dfs_d_automount NULL
|
||||
#endif
|
||||
|
||||
/* Functions related to symlinks */
|
||||
extern void *cifs_follow_link(struct dentry *direntry, struct nameidata *nd);
|
||||
extern void cifs_put_link(struct dentry *direntry,
|
||||
|
@ -675,6 +675,7 @@ cifs_d_revalidate(struct dentry *direntry, struct nameidata *nd)
|
||||
|
||||
const struct dentry_operations cifs_dentry_ops = {
|
||||
.d_revalidate = cifs_d_revalidate,
|
||||
.d_automount = cifs_dfs_d_automount,
|
||||
/* d_delete: cifs_d_delete, */ /* not needed except for debugging */
|
||||
};
|
||||
|
||||
@ -711,4 +712,5 @@ const struct dentry_operations cifs_ci_dentry_ops = {
|
||||
.d_revalidate = cifs_d_revalidate,
|
||||
.d_hash = cifs_ci_hash,
|
||||
.d_compare = cifs_ci_compare,
|
||||
.d_automount = cifs_dfs_d_automount,
|
||||
};
|
||||
|
@ -32,7 +32,7 @@
|
||||
#include "fscache.h"
|
||||
|
||||
|
||||
static void cifs_set_ops(struct inode *inode, const bool is_dfs_referral)
|
||||
static void cifs_set_ops(struct inode *inode)
|
||||
{
|
||||
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
|
||||
|
||||
@ -60,7 +60,7 @@ static void cifs_set_ops(struct inode *inode, const bool is_dfs_referral)
|
||||
break;
|
||||
case S_IFDIR:
|
||||
#ifdef CONFIG_CIFS_DFS_UPCALL
|
||||
if (is_dfs_referral) {
|
||||
if (IS_AUTOMOUNT(inode)) {
|
||||
inode->i_op = &cifs_dfs_referral_inode_operations;
|
||||
} else {
|
||||
#else /* NO DFS support, treat as a directory */
|
||||
@ -167,7 +167,9 @@ cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr)
|
||||
}
|
||||
spin_unlock(&inode->i_lock);
|
||||
|
||||
cifs_set_ops(inode, fattr->cf_flags & CIFS_FATTR_DFS_REFERRAL);
|
||||
if (fattr->cf_flags & CIFS_FATTR_DFS_REFERRAL)
|
||||
inode->i_flags |= S_AUTOMOUNT;
|
||||
cifs_set_ops(inode);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1380,8 +1380,11 @@ EXPORT_SYMBOL(d_set_d_op);
|
||||
static void __d_instantiate(struct dentry *dentry, struct inode *inode)
|
||||
{
|
||||
spin_lock(&dentry->d_lock);
|
||||
if (inode)
|
||||
if (inode) {
|
||||
if (unlikely(IS_AUTOMOUNT(inode)))
|
||||
dentry->d_flags |= DCACHE_NEED_AUTOMOUNT;
|
||||
list_add(&dentry->d_alias, &inode->i_dentry);
|
||||
}
|
||||
dentry->d_inode = inode;
|
||||
dentry_rcuwalk_barrier(dentry);
|
||||
spin_unlock(&dentry->d_lock);
|
||||
|
@ -4,6 +4,19 @@
|
||||
#include <linux/path.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/fs_struct.h>
|
||||
#include "internal.h"
|
||||
|
||||
static inline void path_get_longterm(struct path *path)
|
||||
{
|
||||
path_get(path);
|
||||
mnt_make_longterm(path->mnt);
|
||||
}
|
||||
|
||||
static inline void path_put_longterm(struct path *path)
|
||||
{
|
||||
mnt_make_shortterm(path->mnt);
|
||||
path_put(path);
|
||||
}
|
||||
|
||||
/*
|
||||
* Replace the fs->{rootmnt,root} with {mnt,dentry}. Put the old values.
|
||||
@ -17,11 +30,11 @@ void set_fs_root(struct fs_struct *fs, struct path *path)
|
||||
write_seqcount_begin(&fs->seq);
|
||||
old_root = fs->root;
|
||||
fs->root = *path;
|
||||
path_get_long(path);
|
||||
path_get_longterm(path);
|
||||
write_seqcount_end(&fs->seq);
|
||||
spin_unlock(&fs->lock);
|
||||
if (old_root.dentry)
|
||||
path_put_long(&old_root);
|
||||
path_put_longterm(&old_root);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -36,12 +49,12 @@ void set_fs_pwd(struct fs_struct *fs, struct path *path)
|
||||
write_seqcount_begin(&fs->seq);
|
||||
old_pwd = fs->pwd;
|
||||
fs->pwd = *path;
|
||||
path_get_long(path);
|
||||
path_get_longterm(path);
|
||||
write_seqcount_end(&fs->seq);
|
||||
spin_unlock(&fs->lock);
|
||||
|
||||
if (old_pwd.dentry)
|
||||
path_put_long(&old_pwd);
|
||||
path_put_longterm(&old_pwd);
|
||||
}
|
||||
|
||||
void chroot_fs_refs(struct path *old_root, struct path *new_root)
|
||||
@ -59,13 +72,13 @@ void chroot_fs_refs(struct path *old_root, struct path *new_root)
|
||||
write_seqcount_begin(&fs->seq);
|
||||
if (fs->root.dentry == old_root->dentry
|
||||
&& fs->root.mnt == old_root->mnt) {
|
||||
path_get_long(new_root);
|
||||
path_get_longterm(new_root);
|
||||
fs->root = *new_root;
|
||||
count++;
|
||||
}
|
||||
if (fs->pwd.dentry == old_root->dentry
|
||||
&& fs->pwd.mnt == old_root->mnt) {
|
||||
path_get_long(new_root);
|
||||
path_get_longterm(new_root);
|
||||
fs->pwd = *new_root;
|
||||
count++;
|
||||
}
|
||||
@ -76,13 +89,13 @@ void chroot_fs_refs(struct path *old_root, struct path *new_root)
|
||||
} while_each_thread(g, p);
|
||||
read_unlock(&tasklist_lock);
|
||||
while (count--)
|
||||
path_put_long(old_root);
|
||||
path_put_longterm(old_root);
|
||||
}
|
||||
|
||||
void free_fs_struct(struct fs_struct *fs)
|
||||
{
|
||||
path_put_long(&fs->root);
|
||||
path_put_long(&fs->pwd);
|
||||
path_put_longterm(&fs->root);
|
||||
path_put_longterm(&fs->pwd);
|
||||
kmem_cache_free(fs_cachep, fs);
|
||||
}
|
||||
|
||||
@ -118,9 +131,9 @@ struct fs_struct *copy_fs_struct(struct fs_struct *old)
|
||||
|
||||
spin_lock(&old->lock);
|
||||
fs->root = old->root;
|
||||
path_get_long(&fs->root);
|
||||
path_get_longterm(&fs->root);
|
||||
fs->pwd = old->pwd;
|
||||
path_get_long(&fs->pwd);
|
||||
path_get_longterm(&fs->pwd);
|
||||
spin_unlock(&old->lock);
|
||||
}
|
||||
return fs;
|
||||
|
@ -70,6 +70,11 @@ extern void mnt_set_mountpoint(struct vfsmount *, struct dentry *,
|
||||
extern void release_mounts(struct list_head *);
|
||||
extern void umount_tree(struct vfsmount *, int, struct list_head *);
|
||||
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 mnt_make_longterm(struct vfsmount *);
|
||||
extern void mnt_make_shortterm(struct vfsmount *);
|
||||
|
||||
extern void __init mnt_init(void);
|
||||
|
||||
|
383
fs/namei.c
383
fs/namei.c
@ -367,18 +367,6 @@ void path_get(struct path *path)
|
||||
}
|
||||
EXPORT_SYMBOL(path_get);
|
||||
|
||||
/**
|
||||
* path_get_long - get a long reference to a path
|
||||
* @path: path to get the reference to
|
||||
*
|
||||
* Given a path increment the reference count to the dentry and the vfsmount.
|
||||
*/
|
||||
void path_get_long(struct path *path)
|
||||
{
|
||||
mntget_long(path->mnt);
|
||||
dget(path->dentry);
|
||||
}
|
||||
|
||||
/**
|
||||
* path_put - put a reference to a path
|
||||
* @path: path to put the reference to
|
||||
@ -392,18 +380,6 @@ void path_put(struct path *path)
|
||||
}
|
||||
EXPORT_SYMBOL(path_put);
|
||||
|
||||
/**
|
||||
* path_put_long - put a long reference to a path
|
||||
* @path: path to put the reference to
|
||||
*
|
||||
* Given a path decrement the reference count to the dentry and the vfsmount.
|
||||
*/
|
||||
void path_put_long(struct path *path)
|
||||
{
|
||||
dput(path->dentry);
|
||||
mntput_long(path->mnt);
|
||||
}
|
||||
|
||||
/**
|
||||
* nameidata_drop_rcu - drop this nameidata out of rcu-walk
|
||||
* @nd: nameidata pathwalk data to drop
|
||||
@ -800,12 +776,8 @@ __do_follow_link(const struct path *link, struct nameidata *nd, void **p)
|
||||
touch_atime(link->mnt, dentry);
|
||||
nd_set_link(nd, NULL);
|
||||
|
||||
if (link->mnt != nd->path.mnt) {
|
||||
path_to_nameidata(link, nd);
|
||||
nd->inode = nd->path.dentry->d_inode;
|
||||
dget(dentry);
|
||||
}
|
||||
mntget(link->mnt);
|
||||
if (link->mnt == nd->path.mnt)
|
||||
mntget(link->mnt);
|
||||
|
||||
nd->last_type = LAST_BIND;
|
||||
*p = dentry->d_inode->i_op->follow_link(dentry, nd);
|
||||
@ -896,54 +868,169 @@ int follow_up(struct path *path)
|
||||
}
|
||||
|
||||
/*
|
||||
* serialization is taken care of in namespace.c
|
||||
* Perform an automount
|
||||
* - return -EISDIR to tell follow_managed() to stop and return the path we
|
||||
* were called with.
|
||||
*/
|
||||
static void __follow_mount_rcu(struct nameidata *nd, struct path *path,
|
||||
struct inode **inode)
|
||||
static int follow_automount(struct path *path, unsigned flags,
|
||||
bool *need_mntput)
|
||||
{
|
||||
while (d_mountpoint(path->dentry)) {
|
||||
struct vfsmount *mounted;
|
||||
mounted = __lookup_mnt(path->mnt, path->dentry, 1);
|
||||
if (!mounted)
|
||||
return;
|
||||
path->mnt = mounted;
|
||||
path->dentry = mounted->mnt_root;
|
||||
nd->seq = read_seqcount_begin(&path->dentry->d_seq);
|
||||
*inode = path->dentry->d_inode;
|
||||
}
|
||||
}
|
||||
struct vfsmount *mnt;
|
||||
int err;
|
||||
|
||||
static int __follow_mount(struct path *path)
|
||||
{
|
||||
int res = 0;
|
||||
while (d_mountpoint(path->dentry)) {
|
||||
struct vfsmount *mounted = lookup_mnt(path);
|
||||
if (!mounted)
|
||||
break;
|
||||
if (!path->dentry->d_op || !path->dentry->d_op->d_automount)
|
||||
return -EREMOTE;
|
||||
|
||||
/* We don't want to mount if someone supplied AT_NO_AUTOMOUNT
|
||||
* and this is the terminal part of the path.
|
||||
*/
|
||||
if ((flags & LOOKUP_NO_AUTOMOUNT) && !(flags & LOOKUP_CONTINUE))
|
||||
return -EISDIR; /* we actually want to stop here */
|
||||
|
||||
/* We want to mount if someone is trying to open/create a file of any
|
||||
* type under the mountpoint, wants to traverse through the mountpoint
|
||||
* or wants to open the mounted directory.
|
||||
*
|
||||
* We don't want to mount if someone's just doing a stat and they've
|
||||
* set AT_SYMLINK_NOFOLLOW - unless they're stat'ing a directory and
|
||||
* appended a '/' to the name.
|
||||
*/
|
||||
if (!(flags & LOOKUP_FOLLOW) &&
|
||||
!(flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY |
|
||||
LOOKUP_OPEN | LOOKUP_CREATE)))
|
||||
return -EISDIR;
|
||||
|
||||
current->total_link_count++;
|
||||
if (current->total_link_count >= 40)
|
||||
return -ELOOP;
|
||||
|
||||
mnt = path->dentry->d_op->d_automount(path);
|
||||
if (IS_ERR(mnt)) {
|
||||
/*
|
||||
* The filesystem is allowed to return -EISDIR here to indicate
|
||||
* it doesn't want to automount. For instance, autofs would do
|
||||
* this so that its userspace daemon can mount on this dentry.
|
||||
*
|
||||
* However, we can only permit this if it's a terminal point in
|
||||
* the path being looked up; if it wasn't then the remainder of
|
||||
* the path is inaccessible and we should say so.
|
||||
*/
|
||||
if (PTR_ERR(mnt) == -EISDIR && (flags & LOOKUP_CONTINUE))
|
||||
return -EREMOTE;
|
||||
return PTR_ERR(mnt);
|
||||
}
|
||||
|
||||
if (!mnt) /* mount collision */
|
||||
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 &&
|
||||
mnt->mnt_root == path->dentry) {
|
||||
mnt_clear_expiry(mnt);
|
||||
mntput(mnt);
|
||||
mntput(mnt);
|
||||
return -ELOOP;
|
||||
}
|
||||
|
||||
/* We need to add the mountpoint to the parent. The filesystem may
|
||||
* have placed it on an expiry list, and so we need to make sure it
|
||||
* won't be expired under us if do_add_mount() fails (do_add_mount()
|
||||
* will eat a reference unconditionally).
|
||||
*/
|
||||
mntget(mnt);
|
||||
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 (res)
|
||||
if (*need_mntput)
|
||||
mntput(path->mnt);
|
||||
path->mnt = mounted;
|
||||
path->dentry = dget(mounted->mnt_root);
|
||||
res = 1;
|
||||
path->mnt = mnt;
|
||||
path->dentry = dget(mnt->mnt_root);
|
||||
*need_mntput = true;
|
||||
return 0;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static void follow_mount(struct path *path)
|
||||
/*
|
||||
* Handle a dentry that is managed in some way.
|
||||
* - Flagged for transit management (autofs)
|
||||
* - Flagged as mountpoint
|
||||
* - Flagged as automount point
|
||||
*
|
||||
* This may only be called in refwalk mode.
|
||||
*
|
||||
* Serialization is taken care of in namespace.c
|
||||
*/
|
||||
static int follow_managed(struct path *path, unsigned flags)
|
||||
{
|
||||
while (d_mountpoint(path->dentry)) {
|
||||
struct vfsmount *mounted = lookup_mnt(path);
|
||||
if (!mounted)
|
||||
break;
|
||||
dput(path->dentry);
|
||||
mntput(path->mnt);
|
||||
path->mnt = mounted;
|
||||
path->dentry = dget(mounted->mnt_root);
|
||||
unsigned managed;
|
||||
bool need_mntput = false;
|
||||
int ret;
|
||||
|
||||
/* Given that we're not holding a lock here, we retain the value in a
|
||||
* local variable for each dentry as we look at it so that we don't see
|
||||
* the components of that value change under us */
|
||||
while (managed = ACCESS_ONCE(path->dentry->d_flags),
|
||||
managed &= DCACHE_MANAGED_DENTRY,
|
||||
unlikely(managed != 0)) {
|
||||
/* Allow the filesystem to manage the transit without i_mutex
|
||||
* being held. */
|
||||
if (managed & DCACHE_MANAGE_TRANSIT) {
|
||||
BUG_ON(!path->dentry->d_op);
|
||||
BUG_ON(!path->dentry->d_op->d_manage);
|
||||
ret = path->dentry->d_op->d_manage(path->dentry,
|
||||
false, false);
|
||||
if (ret < 0)
|
||||
return ret == -EISDIR ? 0 : ret;
|
||||
}
|
||||
|
||||
/* Transit to a mounted filesystem. */
|
||||
if (managed & DCACHE_MOUNTED) {
|
||||
struct vfsmount *mounted = lookup_mnt(path);
|
||||
if (mounted) {
|
||||
dput(path->dentry);
|
||||
if (need_mntput)
|
||||
mntput(path->mnt);
|
||||
path->mnt = mounted;
|
||||
path->dentry = dget(mounted->mnt_root);
|
||||
need_mntput = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Something is mounted on this dentry in another
|
||||
* namespace and/or whatever was mounted there in this
|
||||
* namespace got unmounted before we managed to get the
|
||||
* vfsmount_lock */
|
||||
}
|
||||
|
||||
/* Handle an automount point */
|
||||
if (managed & DCACHE_NEED_AUTOMOUNT) {
|
||||
ret = follow_automount(path, flags, &need_mntput);
|
||||
if (ret < 0)
|
||||
return ret == -EISDIR ? 0 : ret;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* We didn't change the current path point */
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int follow_down(struct path *path)
|
||||
int follow_down_one(struct path *path)
|
||||
{
|
||||
struct vfsmount *mounted;
|
||||
|
||||
@ -958,13 +1045,41 @@ int follow_down(struct path *path)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Skip to top of mountpoint pile in rcuwalk mode. We abort the rcu-walk if we
|
||||
* meet a managed dentry and we're not walking to "..". True is returned to
|
||||
* continue, false to abort.
|
||||
*/
|
||||
static bool __follow_mount_rcu(struct nameidata *nd, struct path *path,
|
||||
struct inode **inode, bool reverse_transit)
|
||||
{
|
||||
while (d_mountpoint(path->dentry)) {
|
||||
struct vfsmount *mounted;
|
||||
if (unlikely(path->dentry->d_flags & DCACHE_MANAGE_TRANSIT) &&
|
||||
!reverse_transit &&
|
||||
path->dentry->d_op->d_manage(path->dentry, false, true) < 0)
|
||||
return false;
|
||||
mounted = __lookup_mnt(path->mnt, path->dentry, 1);
|
||||
if (!mounted)
|
||||
break;
|
||||
path->mnt = mounted;
|
||||
path->dentry = mounted->mnt_root;
|
||||
nd->seq = read_seqcount_begin(&path->dentry->d_seq);
|
||||
*inode = path->dentry->d_inode;
|
||||
}
|
||||
|
||||
if (unlikely(path->dentry->d_flags & DCACHE_NEED_AUTOMOUNT))
|
||||
return reverse_transit;
|
||||
return true;
|
||||
}
|
||||
|
||||
static int follow_dotdot_rcu(struct nameidata *nd)
|
||||
{
|
||||
struct inode *inode = nd->inode;
|
||||
|
||||
set_root_rcu(nd);
|
||||
|
||||
while(1) {
|
||||
while (1) {
|
||||
if (nd->path.dentry == nd->root.dentry &&
|
||||
nd->path.mnt == nd->root.mnt) {
|
||||
break;
|
||||
@ -987,12 +1102,80 @@ static int follow_dotdot_rcu(struct nameidata *nd)
|
||||
nd->seq = read_seqcount_begin(&nd->path.dentry->d_seq);
|
||||
inode = nd->path.dentry->d_inode;
|
||||
}
|
||||
__follow_mount_rcu(nd, &nd->path, &inode);
|
||||
__follow_mount_rcu(nd, &nd->path, &inode, true);
|
||||
nd->inode = inode;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Follow down to the covering mount currently visible to userspace. At each
|
||||
* point, the filesystem owning that dentry may be queried as to whether the
|
||||
* caller is permitted to proceed or not.
|
||||
*
|
||||
* Care must be taken as namespace_sem may be held (indicated by mounting_here
|
||||
* being true).
|
||||
*/
|
||||
int follow_down(struct path *path, bool mounting_here)
|
||||
{
|
||||
unsigned managed;
|
||||
int ret;
|
||||
|
||||
while (managed = ACCESS_ONCE(path->dentry->d_flags),
|
||||
unlikely(managed & DCACHE_MANAGED_DENTRY)) {
|
||||
/* Allow the filesystem to manage the transit without i_mutex
|
||||
* being held.
|
||||
*
|
||||
* We indicate to the filesystem if someone is trying to mount
|
||||
* something here. This gives autofs the chance to deny anyone
|
||||
* other than its daemon the right to mount on its
|
||||
* superstructure.
|
||||
*
|
||||
* The filesystem may sleep at this point.
|
||||
*/
|
||||
if (managed & DCACHE_MANAGE_TRANSIT) {
|
||||
BUG_ON(!path->dentry->d_op);
|
||||
BUG_ON(!path->dentry->d_op->d_manage);
|
||||
ret = path->dentry->d_op->d_manage(
|
||||
path->dentry, mounting_here, false);
|
||||
if (ret < 0)
|
||||
return ret == -EISDIR ? 0 : ret;
|
||||
}
|
||||
|
||||
/* Transit to a mounted filesystem. */
|
||||
if (managed & DCACHE_MOUNTED) {
|
||||
struct vfsmount *mounted = lookup_mnt(path);
|
||||
if (!mounted)
|
||||
break;
|
||||
dput(path->dentry);
|
||||
mntput(path->mnt);
|
||||
path->mnt = mounted;
|
||||
path->dentry = dget(mounted->mnt_root);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Don't handle automount points here */
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Skip to top of mountpoint pile in refwalk mode for follow_dotdot()
|
||||
*/
|
||||
static void follow_mount(struct path *path)
|
||||
{
|
||||
while (d_mountpoint(path->dentry)) {
|
||||
struct vfsmount *mounted = lookup_mnt(path);
|
||||
if (!mounted)
|
||||
break;
|
||||
dput(path->dentry);
|
||||
mntput(path->mnt);
|
||||
path->mnt = mounted;
|
||||
path->dentry = dget(mounted->mnt_root);
|
||||
}
|
||||
}
|
||||
|
||||
static void follow_dotdot(struct nameidata *nd)
|
||||
{
|
||||
set_root(nd);
|
||||
@ -1057,12 +1240,14 @@ static int do_lookup(struct nameidata *nd, struct qstr *name,
|
||||
struct vfsmount *mnt = nd->path.mnt;
|
||||
struct dentry *dentry, *parent = nd->path.dentry;
|
||||
struct inode *dir;
|
||||
int err;
|
||||
|
||||
/*
|
||||
* See if the low-level filesystem might want
|
||||
* to use its own hash..
|
||||
*/
|
||||
if (unlikely(parent->d_flags & DCACHE_OP_HASH)) {
|
||||
int err = parent->d_op->d_hash(parent, nd->inode, name);
|
||||
err = parent->d_op->d_hash(parent, nd->inode, name);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
@ -1089,22 +1274,28 @@ static int do_lookup(struct nameidata *nd, struct qstr *name,
|
||||
nd->seq = seq;
|
||||
if (dentry->d_flags & DCACHE_OP_REVALIDATE)
|
||||
goto need_revalidate;
|
||||
done2:
|
||||
path->mnt = mnt;
|
||||
path->dentry = dentry;
|
||||
__follow_mount_rcu(nd, path, inode);
|
||||
} else {
|
||||
dentry = __d_lookup(parent, name);
|
||||
if (!dentry)
|
||||
goto need_lookup;
|
||||
found:
|
||||
if (dentry->d_flags & DCACHE_OP_REVALIDATE)
|
||||
goto need_revalidate;
|
||||
done:
|
||||
path->mnt = mnt;
|
||||
path->dentry = dentry;
|
||||
__follow_mount(path);
|
||||
*inode = path->dentry->d_inode;
|
||||
if (likely(__follow_mount_rcu(nd, path, inode, false)))
|
||||
return 0;
|
||||
if (nameidata_drop_rcu(nd))
|
||||
return -ECHILD;
|
||||
/* fallthru */
|
||||
}
|
||||
dentry = __d_lookup(parent, name);
|
||||
if (!dentry)
|
||||
goto need_lookup;
|
||||
found:
|
||||
if (dentry->d_flags & DCACHE_OP_REVALIDATE)
|
||||
goto need_revalidate;
|
||||
done:
|
||||
path->mnt = mnt;
|
||||
path->dentry = dentry;
|
||||
err = follow_managed(path, nd->flags);
|
||||
if (unlikely(err < 0))
|
||||
return err;
|
||||
*inode = path->dentry->d_inode;
|
||||
return 0;
|
||||
|
||||
need_lookup:
|
||||
@ -1143,23 +1334,14 @@ need_revalidate:
|
||||
goto need_lookup;
|
||||
if (IS_ERR(dentry))
|
||||
goto fail;
|
||||
if (nd->flags & LOOKUP_RCU)
|
||||
goto done2;
|
||||
goto done;
|
||||
|
||||
fail:
|
||||
return PTR_ERR(dentry);
|
||||
}
|
||||
|
||||
/*
|
||||
* This is a temporary kludge to deal with "automount" symlinks; proper
|
||||
* solution is to trigger them on follow_mount(), so that do_lookup()
|
||||
* would DTRT. To be killed before 2.6.34-final.
|
||||
*/
|
||||
static inline int follow_on_final(struct inode *inode, unsigned lookup_flags)
|
||||
{
|
||||
return inode && unlikely(inode->i_op->follow_link) &&
|
||||
((lookup_flags & LOOKUP_FOLLOW) || S_ISDIR(inode->i_mode));
|
||||
}
|
||||
|
||||
/*
|
||||
* Name resolution.
|
||||
* This is the basic name resolution function, turning a pathname into
|
||||
@ -1298,7 +1480,8 @@ last_component:
|
||||
err = do_lookup(nd, &this, &next, &inode);
|
||||
if (err)
|
||||
break;
|
||||
if (follow_on_final(inode, lookup_flags)) {
|
||||
if (inode && unlikely(inode->i_op->follow_link) &&
|
||||
(lookup_flags & LOOKUP_FOLLOW)) {
|
||||
if (nameidata_dentry_drop_rcu_maybe(nd, next.dentry))
|
||||
return -ECHILD;
|
||||
BUG_ON(inode != next.dentry->d_inode);
|
||||
@ -2200,11 +2383,9 @@ static struct file *do_last(struct nameidata *nd, struct path *path,
|
||||
if (open_flag & O_EXCL)
|
||||
goto exit_dput;
|
||||
|
||||
if (__follow_mount(path)) {
|
||||
error = -ELOOP;
|
||||
if (open_flag & O_NOFOLLOW)
|
||||
goto exit_dput;
|
||||
}
|
||||
error = follow_managed(path, nd->flags);
|
||||
if (error < 0)
|
||||
goto exit_dput;
|
||||
|
||||
error = -ENOENT;
|
||||
if (!path->dentry->d_inode)
|
||||
@ -2353,8 +2534,7 @@ reval:
|
||||
struct inode *linki = link.dentry->d_inode;
|
||||
void *cookie;
|
||||
error = -ELOOP;
|
||||
/* S_ISDIR part is a temporary automount kludge */
|
||||
if (!(nd.flags & LOOKUP_FOLLOW) && !S_ISDIR(linki->i_mode))
|
||||
if (!(nd.flags & LOOKUP_FOLLOW))
|
||||
goto exit_dput;
|
||||
if (count++ == 32)
|
||||
goto exit_dput;
|
||||
@ -3413,6 +3593,7 @@ const struct inode_operations page_symlink_inode_operations = {
|
||||
};
|
||||
|
||||
EXPORT_SYMBOL(user_path_at);
|
||||
EXPORT_SYMBOL(follow_down_one);
|
||||
EXPORT_SYMBOL(follow_down);
|
||||
EXPORT_SYMBOL(follow_up);
|
||||
EXPORT_SYMBOL(get_write_access); /* binfmt_aout */
|
||||
|
179
fs/namespace.c
179
fs/namespace.c
@ -183,7 +183,7 @@ static inline void mnt_dec_count(struct vfsmount *mnt)
|
||||
unsigned int mnt_get_count(struct vfsmount *mnt)
|
||||
{
|
||||
#ifdef CONFIG_SMP
|
||||
unsigned int count = atomic_read(&mnt->mnt_longrefs);
|
||||
unsigned int count = 0;
|
||||
int cpu;
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
@ -217,7 +217,7 @@ struct vfsmount *alloc_vfsmnt(const char *name)
|
||||
if (!mnt->mnt_pcp)
|
||||
goto out_free_devname;
|
||||
|
||||
atomic_set(&mnt->mnt_longrefs, 1);
|
||||
this_cpu_add(mnt->mnt_pcp->mnt_count, 1);
|
||||
#else
|
||||
mnt->mnt_count = 1;
|
||||
mnt->mnt_writers = 0;
|
||||
@ -624,8 +624,11 @@ static void commit_tree(struct vfsmount *mnt)
|
||||
BUG_ON(parent == mnt);
|
||||
|
||||
list_add_tail(&head, &mnt->mnt_list);
|
||||
list_for_each_entry(m, &head, mnt_list)
|
||||
list_for_each_entry(m, &head, mnt_list) {
|
||||
m->mnt_ns = n;
|
||||
atomic_inc(&m->mnt_longterm);
|
||||
}
|
||||
|
||||
list_splice(&head, n->list.prev);
|
||||
|
||||
list_add_tail(&mnt->mnt_hash, mount_hashtable +
|
||||
@ -734,51 +737,30 @@ static inline void mntfree(struct vfsmount *mnt)
|
||||
deactivate_super(sb);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
static inline void __mntput(struct vfsmount *mnt, int longrefs)
|
||||
static void mntput_no_expire(struct vfsmount *mnt)
|
||||
{
|
||||
if (!longrefs) {
|
||||
put_again:
|
||||
br_read_lock(vfsmount_lock);
|
||||
if (likely(atomic_read(&mnt->mnt_longrefs))) {
|
||||
mnt_dec_count(mnt);
|
||||
br_read_unlock(vfsmount_lock);
|
||||
return;
|
||||
}
|
||||
#ifdef CONFIG_SMP
|
||||
br_read_lock(vfsmount_lock);
|
||||
if (likely(atomic_read(&mnt->mnt_longterm))) {
|
||||
mnt_dec_count(mnt);
|
||||
br_read_unlock(vfsmount_lock);
|
||||
} else {
|
||||
BUG_ON(!atomic_read(&mnt->mnt_longrefs));
|
||||
if (atomic_add_unless(&mnt->mnt_longrefs, -1, 1))
|
||||
return;
|
||||
return;
|
||||
}
|
||||
br_read_unlock(vfsmount_lock);
|
||||
|
||||
br_write_lock(vfsmount_lock);
|
||||
if (!longrefs)
|
||||
mnt_dec_count(mnt);
|
||||
else
|
||||
atomic_dec(&mnt->mnt_longrefs);
|
||||
mnt_dec_count(mnt);
|
||||
if (mnt_get_count(mnt)) {
|
||||
br_write_unlock(vfsmount_lock);
|
||||
return;
|
||||
}
|
||||
if (unlikely(mnt->mnt_pinned)) {
|
||||
mnt_add_count(mnt, mnt->mnt_pinned + 1);
|
||||
mnt->mnt_pinned = 0;
|
||||
br_write_unlock(vfsmount_lock);
|
||||
acct_auto_close_mnt(mnt);
|
||||
goto put_again;
|
||||
}
|
||||
br_write_unlock(vfsmount_lock);
|
||||
mntfree(mnt);
|
||||
}
|
||||
#else
|
||||
static inline void __mntput(struct vfsmount *mnt, int longrefs)
|
||||
{
|
||||
put_again:
|
||||
mnt_dec_count(mnt);
|
||||
if (likely(mnt_get_count(mnt)))
|
||||
return;
|
||||
br_write_lock(vfsmount_lock);
|
||||
#endif
|
||||
if (unlikely(mnt->mnt_pinned)) {
|
||||
mnt_add_count(mnt, mnt->mnt_pinned + 1);
|
||||
mnt->mnt_pinned = 0;
|
||||
@ -789,12 +771,6 @@ put_again:
|
||||
br_write_unlock(vfsmount_lock);
|
||||
mntfree(mnt);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void mntput_no_expire(struct vfsmount *mnt)
|
||||
{
|
||||
__mntput(mnt, 0);
|
||||
}
|
||||
|
||||
void mntput(struct vfsmount *mnt)
|
||||
{
|
||||
@ -802,7 +778,7 @@ void mntput(struct vfsmount *mnt)
|
||||
/* avoid cacheline pingpong, hope gcc doesn't get "smart" */
|
||||
if (unlikely(mnt->mnt_expiry_mark))
|
||||
mnt->mnt_expiry_mark = 0;
|
||||
__mntput(mnt, 0);
|
||||
mntput_no_expire(mnt);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(mntput);
|
||||
@ -815,33 +791,6 @@ struct vfsmount *mntget(struct vfsmount *mnt)
|
||||
}
|
||||
EXPORT_SYMBOL(mntget);
|
||||
|
||||
void mntput_long(struct vfsmount *mnt)
|
||||
{
|
||||
#ifdef CONFIG_SMP
|
||||
if (mnt) {
|
||||
/* avoid cacheline pingpong, hope gcc doesn't get "smart" */
|
||||
if (unlikely(mnt->mnt_expiry_mark))
|
||||
mnt->mnt_expiry_mark = 0;
|
||||
__mntput(mnt, 1);
|
||||
}
|
||||
#else
|
||||
mntput(mnt);
|
||||
#endif
|
||||
}
|
||||
EXPORT_SYMBOL(mntput_long);
|
||||
|
||||
struct vfsmount *mntget_long(struct vfsmount *mnt)
|
||||
{
|
||||
#ifdef CONFIG_SMP
|
||||
if (mnt)
|
||||
atomic_inc(&mnt->mnt_longrefs);
|
||||
return mnt;
|
||||
#else
|
||||
return mntget(mnt);
|
||||
#endif
|
||||
}
|
||||
EXPORT_SYMBOL(mntget_long);
|
||||
|
||||
void mnt_pin(struct vfsmount *mnt)
|
||||
{
|
||||
br_write_lock(vfsmount_lock);
|
||||
@ -1216,7 +1165,7 @@ void release_mounts(struct list_head *head)
|
||||
dput(dentry);
|
||||
mntput(m);
|
||||
}
|
||||
mntput_long(mnt);
|
||||
mntput(mnt);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1226,19 +1175,21 @@ void release_mounts(struct list_head *head)
|
||||
*/
|
||||
void umount_tree(struct vfsmount *mnt, int propagate, struct list_head *kill)
|
||||
{
|
||||
LIST_HEAD(tmp_list);
|
||||
struct vfsmount *p;
|
||||
|
||||
for (p = mnt; p; p = next_mnt(p, mnt))
|
||||
list_move(&p->mnt_hash, kill);
|
||||
list_move(&p->mnt_hash, &tmp_list);
|
||||
|
||||
if (propagate)
|
||||
propagate_umount(kill);
|
||||
propagate_umount(&tmp_list);
|
||||
|
||||
list_for_each_entry(p, kill, mnt_hash) {
|
||||
list_for_each_entry(p, &tmp_list, mnt_hash) {
|
||||
list_del_init(&p->mnt_expire);
|
||||
list_del_init(&p->mnt_list);
|
||||
__touch_mnt_namespace(p->mnt_ns);
|
||||
p->mnt_ns = NULL;
|
||||
atomic_dec(&p->mnt_longterm);
|
||||
list_del_init(&p->mnt_child);
|
||||
if (p->mnt_parent != p) {
|
||||
p->mnt_parent->mnt_ghosts++;
|
||||
@ -1246,6 +1197,7 @@ void umount_tree(struct vfsmount *mnt, int propagate, struct list_head *kill)
|
||||
}
|
||||
change_mnt_propagation(p, MS_PRIVATE);
|
||||
}
|
||||
list_splice(&tmp_list, kill);
|
||||
}
|
||||
|
||||
static void shrink_submounts(struct vfsmount *mnt, struct list_head *umounts);
|
||||
@ -1844,9 +1796,10 @@ static int do_move_mount(struct path *path, char *old_name)
|
||||
return err;
|
||||
|
||||
down_write(&namespace_sem);
|
||||
while (d_mountpoint(path->dentry) &&
|
||||
follow_down(path))
|
||||
;
|
||||
err = follow_down(path, true);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
err = -EINVAL;
|
||||
if (!check_mnt(path->mnt) || !check_mnt(old_path.mnt))
|
||||
goto out;
|
||||
@ -1924,15 +1877,14 @@ static int do_new_mount(struct path *path, char *type, int flags,
|
||||
if (IS_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
|
||||
* - 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 mnt_flags, struct list_head *fslist)
|
||||
int do_add_mount(struct vfsmount *newmnt, struct path *path, int mnt_flags)
|
||||
{
|
||||
int err;
|
||||
|
||||
@ -1940,9 +1892,10 @@ int do_add_mount(struct vfsmount *newmnt, struct path *path,
|
||||
|
||||
down_write(&namespace_sem);
|
||||
/* Something was mounted here while we slept */
|
||||
while (d_mountpoint(path->dentry) &&
|
||||
follow_down(path))
|
||||
;
|
||||
err = follow_down(path, true);
|
||||
if (err < 0)
|
||||
goto unlock;
|
||||
|
||||
err = -EINVAL;
|
||||
if (!(mnt_flags & MNT_SHRINKABLE) && !check_mnt(path->mnt))
|
||||
goto unlock;
|
||||
@ -1961,19 +1914,45 @@ int do_add_mount(struct vfsmount *newmnt, struct path *path,
|
||||
if ((err = graft_tree(newmnt, path)))
|
||||
goto unlock;
|
||||
|
||||
if (fslist) /* add to the specified expiration list */
|
||||
list_add_tail(&newmnt->mnt_expire, fslist);
|
||||
|
||||
up_write(&namespace_sem);
|
||||
return 0;
|
||||
|
||||
unlock:
|
||||
up_write(&namespace_sem);
|
||||
mntput_long(newmnt);
|
||||
mntput(newmnt);
|
||||
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
|
||||
@ -2262,6 +2241,20 @@ static struct mnt_namespace *alloc_mnt_ns(void)
|
||||
return new_ns;
|
||||
}
|
||||
|
||||
void mnt_make_longterm(struct vfsmount *mnt)
|
||||
{
|
||||
atomic_inc(&mnt->mnt_longterm);
|
||||
}
|
||||
|
||||
void mnt_make_shortterm(struct vfsmount *mnt)
|
||||
{
|
||||
if (atomic_add_unless(&mnt->mnt_longterm, -1, 1))
|
||||
return;
|
||||
br_write_lock(vfsmount_lock);
|
||||
atomic_dec(&mnt->mnt_longterm);
|
||||
br_write_unlock(vfsmount_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate a new namespace structure and populate it with contents
|
||||
* copied from the namespace of the passed in task structure.
|
||||
@ -2299,14 +2292,19 @@ static struct mnt_namespace *dup_mnt_ns(struct mnt_namespace *mnt_ns,
|
||||
q = new_ns->root;
|
||||
while (p) {
|
||||
q->mnt_ns = new_ns;
|
||||
atomic_inc(&q->mnt_longterm);
|
||||
if (fs) {
|
||||
if (p == fs->root.mnt) {
|
||||
fs->root.mnt = mntget(q);
|
||||
atomic_inc(&q->mnt_longterm);
|
||||
mnt_make_shortterm(p);
|
||||
rootmnt = p;
|
||||
fs->root.mnt = mntget_long(q);
|
||||
}
|
||||
if (p == fs->pwd.mnt) {
|
||||
fs->pwd.mnt = mntget(q);
|
||||
atomic_inc(&q->mnt_longterm);
|
||||
mnt_make_shortterm(p);
|
||||
pwdmnt = p;
|
||||
fs->pwd.mnt = mntget_long(q);
|
||||
}
|
||||
}
|
||||
p = next_mnt(p, mnt_ns->root);
|
||||
@ -2315,9 +2313,9 @@ static struct mnt_namespace *dup_mnt_ns(struct mnt_namespace *mnt_ns,
|
||||
up_write(&namespace_sem);
|
||||
|
||||
if (rootmnt)
|
||||
mntput_long(rootmnt);
|
||||
mntput(rootmnt);
|
||||
if (pwdmnt)
|
||||
mntput_long(pwdmnt);
|
||||
mntput(pwdmnt);
|
||||
|
||||
return new_ns;
|
||||
}
|
||||
@ -2350,6 +2348,7 @@ struct mnt_namespace *create_mnt_ns(struct vfsmount *mnt)
|
||||
new_ns = alloc_mnt_ns();
|
||||
if (!IS_ERR(new_ns)) {
|
||||
mnt->mnt_ns = new_ns;
|
||||
atomic_inc(&mnt->mnt_longterm);
|
||||
new_ns->root = mnt;
|
||||
list_add(&new_ns->list, &new_ns->root->mnt_list);
|
||||
}
|
||||
|
@ -970,7 +970,7 @@ int nfs_lookup_verify_inode(struct inode *inode, struct nameidata *nd)
|
||||
{
|
||||
struct nfs_server *server = NFS_SERVER(inode);
|
||||
|
||||
if (test_bit(NFS_INO_MOUNTPOINT, &NFS_I(inode)->flags))
|
||||
if (IS_AUTOMOUNT(inode))
|
||||
return 0;
|
||||
if (nd != NULL) {
|
||||
/* VFS wants an on-the-wire revalidation */
|
||||
@ -1173,6 +1173,7 @@ const struct dentry_operations nfs_dentry_operations = {
|
||||
.d_revalidate = nfs_lookup_revalidate,
|
||||
.d_delete = nfs_dentry_delete,
|
||||
.d_iput = nfs_dentry_iput,
|
||||
.d_automount = nfs_d_automount,
|
||||
};
|
||||
|
||||
static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, struct nameidata *nd)
|
||||
@ -1246,6 +1247,7 @@ const struct dentry_operations nfs4_dentry_operations = {
|
||||
.d_revalidate = nfs_open_revalidate,
|
||||
.d_delete = nfs_dentry_delete,
|
||||
.d_iput = nfs_dentry_iput,
|
||||
.d_automount = nfs_d_automount,
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -300,7 +300,7 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
|
||||
else
|
||||
inode->i_op = &nfs_mountpoint_inode_operations;
|
||||
inode->i_fop = NULL;
|
||||
set_bit(NFS_INO_MOUNTPOINT, &nfsi->flags);
|
||||
inode->i_flags |= S_AUTOMOUNT;
|
||||
}
|
||||
} else if (S_ISLNK(inode->i_mode))
|
||||
inode->i_op = &nfs_symlink_inode_operations;
|
||||
@ -1208,7 +1208,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
|
||||
/* Update the fsid? */
|
||||
if (S_ISDIR(inode->i_mode) && (fattr->valid & NFS_ATTR_FATTR_FSID) &&
|
||||
!nfs_fsid_equal(&server->fsid, &fattr->fsid) &&
|
||||
!test_bit(NFS_INO_MOUNTPOINT, &nfsi->flags))
|
||||
!IS_AUTOMOUNT(inode))
|
||||
server->fsid = fattr->fsid;
|
||||
|
||||
/*
|
||||
|
@ -252,6 +252,7 @@ extern char *nfs_path(const char *base,
|
||||
const struct dentry *droot,
|
||||
const struct dentry *dentry,
|
||||
char *buffer, ssize_t buflen);
|
||||
extern struct vfsmount *nfs_d_automount(struct path *path);
|
||||
|
||||
/* getroot.c */
|
||||
extern struct dentry *nfs_get_root(struct super_block *, struct nfs_fh *);
|
||||
|
@ -97,9 +97,8 @@ Elong:
|
||||
}
|
||||
|
||||
/*
|
||||
* nfs_follow_mountpoint - handle crossing a mountpoint on the server
|
||||
* @dentry - dentry of mountpoint
|
||||
* @nd - nameidata info
|
||||
* nfs_d_automount - Handle crossing a mountpoint on the server
|
||||
* @path - The mountpoint
|
||||
*
|
||||
* When we encounter a mountpoint on the server, we want to set up
|
||||
* a mountpoint on the client too, to prevent inode numbers from
|
||||
@ -109,87 +108,65 @@ Elong:
|
||||
* situation, and that different filesystems may want to use
|
||||
* different security flavours.
|
||||
*/
|
||||
static void * nfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd)
|
||||
struct vfsmount *nfs_d_automount(struct path *path)
|
||||
{
|
||||
struct vfsmount *mnt;
|
||||
struct nfs_server *server = NFS_SERVER(dentry->d_inode);
|
||||
struct nfs_server *server = NFS_SERVER(path->dentry->d_inode);
|
||||
struct dentry *parent;
|
||||
struct nfs_fh *fh = NULL;
|
||||
struct nfs_fattr *fattr = NULL;
|
||||
int err;
|
||||
|
||||
dprintk("--> nfs_follow_mountpoint()\n");
|
||||
dprintk("--> nfs_d_automount()\n");
|
||||
|
||||
err = -ESTALE;
|
||||
if (IS_ROOT(dentry))
|
||||
goto out_err;
|
||||
mnt = ERR_PTR(-ESTALE);
|
||||
if (IS_ROOT(path->dentry))
|
||||
goto out_nofree;
|
||||
|
||||
err = -ENOMEM;
|
||||
mnt = ERR_PTR(-ENOMEM);
|
||||
fh = nfs_alloc_fhandle();
|
||||
fattr = nfs_alloc_fattr();
|
||||
if (fh == NULL || fattr == NULL)
|
||||
goto out_err;
|
||||
goto out;
|
||||
|
||||
dprintk("%s: enter\n", __func__);
|
||||
dput(nd->path.dentry);
|
||||
nd->path.dentry = dget(dentry);
|
||||
|
||||
/* Look it up again */
|
||||
parent = dget_parent(nd->path.dentry);
|
||||
/* Look it up again to get its attributes */
|
||||
parent = dget_parent(path->dentry);
|
||||
err = server->nfs_client->rpc_ops->lookup(parent->d_inode,
|
||||
&nd->path.dentry->d_name,
|
||||
&path->dentry->d_name,
|
||||
fh, fattr);
|
||||
dput(parent);
|
||||
if (err != 0)
|
||||
goto out_err;
|
||||
if (err != 0) {
|
||||
mnt = ERR_PTR(err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL)
|
||||
mnt = nfs_do_refmount(nd->path.mnt, nd->path.dentry);
|
||||
mnt = nfs_do_refmount(path->mnt, path->dentry);
|
||||
else
|
||||
mnt = nfs_do_submount(nd->path.mnt, nd->path.dentry, fh,
|
||||
fattr);
|
||||
err = PTR_ERR(mnt);
|
||||
mnt = nfs_do_submount(path->mnt, path->dentry, fh, fattr);
|
||||
if (IS_ERR(mnt))
|
||||
goto out_err;
|
||||
goto out;
|
||||
|
||||
mntget(mnt);
|
||||
err = do_add_mount(mnt, &nd->path, nd->path.mnt->mnt_flags|MNT_SHRINKABLE,
|
||||
&nfs_automount_list);
|
||||
if (err < 0) {
|
||||
mntput(mnt);
|
||||
if (err == -EBUSY)
|
||||
goto out_follow;
|
||||
goto out_err;
|
||||
}
|
||||
path_put(&nd->path);
|
||||
nd->path.mnt = mnt;
|
||||
nd->path.dentry = dget(mnt->mnt_root);
|
||||
dprintk("%s: done, success\n", __func__);
|
||||
mntget(mnt); /* prevent immediate expiration */
|
||||
mnt_set_expiry(mnt, &nfs_automount_list);
|
||||
schedule_delayed_work(&nfs_automount_task, nfs_mountpoint_expiry_timeout);
|
||||
|
||||
out:
|
||||
nfs_free_fattr(fattr);
|
||||
nfs_free_fhandle(fh);
|
||||
dprintk("%s: done, returned %d\n", __func__, err);
|
||||
|
||||
dprintk("<-- nfs_follow_mountpoint() = %d\n", err);
|
||||
return ERR_PTR(err);
|
||||
out_err:
|
||||
path_put(&nd->path);
|
||||
goto out;
|
||||
out_follow:
|
||||
while (d_mountpoint(nd->path.dentry) &&
|
||||
follow_down(&nd->path))
|
||||
;
|
||||
err = 0;
|
||||
goto out;
|
||||
out_nofree:
|
||||
dprintk("<-- nfs_follow_mountpoint() = %p\n", mnt);
|
||||
return mnt;
|
||||
}
|
||||
|
||||
const struct inode_operations nfs_mountpoint_inode_operations = {
|
||||
.follow_link = nfs_follow_mountpoint,
|
||||
.getattr = nfs_getattr,
|
||||
};
|
||||
|
||||
const struct inode_operations nfs_referral_inode_operations = {
|
||||
.follow_link = nfs_follow_mountpoint,
|
||||
};
|
||||
|
||||
static void nfs_expire_automounts(struct work_struct *work)
|
||||
|
@ -87,8 +87,9 @@ nfsd_cross_mnt(struct svc_rqst *rqstp, struct dentry **dpp,
|
||||
.dentry = dget(dentry)};
|
||||
int err = 0;
|
||||
|
||||
while (d_mountpoint(path.dentry) && follow_down(&path))
|
||||
;
|
||||
err = follow_down(&path, false);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
exp2 = rqst_exp_get_by_name(rqstp, &path);
|
||||
if (IS_ERR(exp2)) {
|
||||
|
@ -1292,7 +1292,7 @@ static int __init init_pipe_fs(void)
|
||||
static void __exit exit_pipe_fs(void)
|
||||
{
|
||||
unregister_filesystem(&pipe_fs_type);
|
||||
mntput_long(pipe_mnt);
|
||||
mntput(pipe_mnt);
|
||||
}
|
||||
|
||||
fs_initcall(init_pipe_fs);
|
||||
|
@ -75,11 +75,13 @@ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
|
||||
int error = -EINVAL;
|
||||
int lookup_flags = 0;
|
||||
|
||||
if ((flag & ~AT_SYMLINK_NOFOLLOW) != 0)
|
||||
if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT)) != 0)
|
||||
goto out;
|
||||
|
||||
if (!(flag & AT_SYMLINK_NOFOLLOW))
|
||||
lookup_flags |= LOOKUP_FOLLOW;
|
||||
if (flag & AT_NO_AUTOMOUNT)
|
||||
lookup_flags |= LOOKUP_NO_AUTOMOUNT;
|
||||
|
||||
error = user_path_at(dfd, filename, lookup_flags, &path);
|
||||
if (error)
|
||||
|
@ -1141,7 +1141,7 @@ static struct vfsmount *fs_set_subtype(struct vfsmount *mnt, const char *fstype)
|
||||
return mnt;
|
||||
|
||||
err:
|
||||
mntput_long(mnt);
|
||||
mntput(mnt);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
|
@ -24,7 +24,7 @@
|
||||
#define AUTOFS_MIN_PROTO_VERSION 3
|
||||
#define AUTOFS_MAX_PROTO_VERSION 5
|
||||
|
||||
#define AUTOFS_PROTO_SUBVERSION 1
|
||||
#define AUTOFS_PROTO_SUBVERSION 2
|
||||
|
||||
/* Mask for expire behaviour */
|
||||
#define AUTOFS_EXP_IMMEDIATE 1
|
||||
|
@ -167,6 +167,8 @@ struct dentry_operations {
|
||||
void (*d_release)(struct dentry *);
|
||||
void (*d_iput)(struct dentry *, struct inode *);
|
||||
char *(*d_dname)(struct dentry *, char *, int);
|
||||
struct vfsmount *(*d_automount)(struct path *);
|
||||
int (*d_manage)(struct dentry *, bool, bool);
|
||||
} ____cacheline_aligned;
|
||||
|
||||
/*
|
||||
@ -205,13 +207,18 @@ struct dentry_operations {
|
||||
|
||||
#define DCACHE_CANT_MOUNT 0x0100
|
||||
#define DCACHE_GENOCIDE 0x0200
|
||||
#define DCACHE_MOUNTED 0x0400 /* is a mountpoint */
|
||||
|
||||
#define DCACHE_OP_HASH 0x1000
|
||||
#define DCACHE_OP_COMPARE 0x2000
|
||||
#define DCACHE_OP_REVALIDATE 0x4000
|
||||
#define DCACHE_OP_DELETE 0x8000
|
||||
|
||||
#define DCACHE_MOUNTED 0x10000 /* is a mountpoint */
|
||||
#define DCACHE_NEED_AUTOMOUNT 0x20000 /* handle automount on this dir */
|
||||
#define DCACHE_MANAGE_TRANSIT 0x40000 /* manage transit from this dirent */
|
||||
#define DCACHE_MANAGED_DENTRY \
|
||||
(DCACHE_MOUNTED|DCACHE_NEED_AUTOMOUNT|DCACHE_MANAGE_TRANSIT)
|
||||
|
||||
extern seqlock_t rename_lock;
|
||||
|
||||
static inline int dname_external(struct dentry *dentry)
|
||||
@ -399,7 +406,12 @@ static inline void dont_mount(struct dentry *dentry)
|
||||
|
||||
extern void dput(struct dentry *);
|
||||
|
||||
static inline int d_mountpoint(struct dentry *dentry)
|
||||
static inline bool d_managed(struct dentry *dentry)
|
||||
{
|
||||
return dentry->d_flags & DCACHE_MANAGED_DENTRY;
|
||||
}
|
||||
|
||||
static inline bool d_mountpoint(struct dentry *dentry)
|
||||
{
|
||||
return dentry->d_flags & DCACHE_MOUNTED;
|
||||
}
|
||||
|
@ -45,6 +45,7 @@
|
||||
#define AT_REMOVEDIR 0x200 /* Remove directory instead of
|
||||
unlinking file. */
|
||||
#define AT_SYMLINK_FOLLOW 0x400 /* Follow symbolic links. */
|
||||
#define AT_NO_AUTOMOUNT 0x800 /* Suppress terminal automount traversal */
|
||||
|
||||
#ifdef __KERNEL__
|
||||
|
||||
|
@ -242,6 +242,7 @@ struct inodes_stat_t {
|
||||
#define S_SWAPFILE 256 /* Do not truncate: swapon got its bmaps */
|
||||
#define S_PRIVATE 512 /* Inode is fs-internal */
|
||||
#define S_IMA 1024 /* Inode has an associated IMA struct */
|
||||
#define S_AUTOMOUNT 2048 /* Automount/referral quasi-directory */
|
||||
|
||||
/*
|
||||
* Note that nosuid etc flags are inode-specific: setting some file-system
|
||||
@ -277,6 +278,7 @@ struct inodes_stat_t {
|
||||
#define IS_SWAPFILE(inode) ((inode)->i_flags & S_SWAPFILE)
|
||||
#define IS_PRIVATE(inode) ((inode)->i_flags & S_PRIVATE)
|
||||
#define IS_IMA(inode) ((inode)->i_flags & S_IMA)
|
||||
#define IS_AUTOMOUNT(inode) ((inode)->i_flags & S_AUTOMOUNT)
|
||||
|
||||
/* the read-only stuff doesn't really belong here, but any other place is
|
||||
probably as bad and I don't want to create yet another include file. */
|
||||
|
@ -60,7 +60,7 @@ struct vfsmount {
|
||||
struct super_block *mnt_sb; /* pointer to superblock */
|
||||
#ifdef CONFIG_SMP
|
||||
struct mnt_pcp __percpu *mnt_pcp;
|
||||
atomic_t mnt_longrefs;
|
||||
atomic_t mnt_longterm; /* how many of the refs are longterm */
|
||||
#else
|
||||
int mnt_count;
|
||||
int mnt_writers;
|
||||
@ -96,8 +96,6 @@ extern int mnt_clone_write(struct vfsmount *mnt);
|
||||
extern void mnt_drop_write(struct vfsmount *mnt);
|
||||
extern void mntput(struct vfsmount *mnt);
|
||||
extern struct vfsmount *mntget(struct vfsmount *mnt);
|
||||
extern void mntput_long(struct vfsmount *mnt);
|
||||
extern struct vfsmount *mntget_long(struct vfsmount *mnt);
|
||||
extern void mnt_pin(struct vfsmount *mnt);
|
||||
extern void mnt_unpin(struct vfsmount *mnt);
|
||||
extern int __mnt_is_readonly(struct vfsmount *mnt);
|
||||
@ -110,12 +108,7 @@ extern struct vfsmount *vfs_kern_mount(struct file_system_type *type,
|
||||
int flags, const char *name,
|
||||
void *data);
|
||||
|
||||
struct nameidata;
|
||||
|
||||
struct path;
|
||||
extern int do_add_mount(struct vfsmount *newmnt, struct path *path,
|
||||
int mnt_flags, struct list_head *fslist);
|
||||
|
||||
extern void mnt_set_expiry(struct vfsmount *mnt, struct list_head *expiry_list);
|
||||
extern void mark_mounts_for_expiry(struct list_head *mounts);
|
||||
|
||||
extern dev_t name_to_dev_t(char *name);
|
||||
|
@ -45,6 +45,7 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT, LAST_BIND};
|
||||
* - ending slashes ok even for nonexistent files
|
||||
* - internal "there are more path components" flag
|
||||
* - dentry cache is untrusted; force a real lookup
|
||||
* - suppress terminal automount
|
||||
*/
|
||||
#define LOOKUP_FOLLOW 0x0001
|
||||
#define LOOKUP_DIRECTORY 0x0002
|
||||
@ -53,6 +54,7 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT, LAST_BIND};
|
||||
#define LOOKUP_PARENT 0x0010
|
||||
#define LOOKUP_REVAL 0x0020
|
||||
#define LOOKUP_RCU 0x0040
|
||||
#define LOOKUP_NO_AUTOMOUNT 0x0080
|
||||
/*
|
||||
* Intent data
|
||||
*/
|
||||
@ -79,7 +81,8 @@ extern struct file *lookup_instantiate_filp(struct nameidata *nd, struct dentry
|
||||
|
||||
extern struct dentry *lookup_one_len(const char *, struct dentry *, int);
|
||||
|
||||
extern int follow_down(struct path *);
|
||||
extern int follow_down_one(struct path *);
|
||||
extern int follow_down(struct path *, bool);
|
||||
extern int follow_up(struct path *);
|
||||
|
||||
extern struct dentry *lock_rename(struct dentry *, struct dentry *);
|
||||
|
@ -215,7 +215,6 @@ struct nfs_inode {
|
||||
#define NFS_INO_ADVISE_RDPLUS (0) /* advise readdirplus */
|
||||
#define NFS_INO_STALE (1) /* possible stale inode */
|
||||
#define NFS_INO_ACL_LRU_SET (2) /* Inode is on the LRU list */
|
||||
#define NFS_INO_MOUNTPOINT (3) /* inode is remote mountpoint */
|
||||
#define NFS_INO_FLUSHING (4) /* inode is flushing out data */
|
||||
#define NFS_INO_FSCACHE (5) /* inode can be cached by FS-Cache */
|
||||
#define NFS_INO_FSCACHE_LOCK (6) /* FS-Cache cookie management lock */
|
||||
|
@ -10,9 +10,7 @@ struct path {
|
||||
};
|
||||
|
||||
extern void path_get(struct path *);
|
||||
extern void path_get_long(struct path *);
|
||||
extern void path_put(struct path *);
|
||||
extern void path_put_long(struct path *);
|
||||
|
||||
static inline int path_equal(const struct path *path1, const struct path *path2)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user