Commit f8206b92 authored by Linus Torvalds's avatar Linus Torvalds

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
  ...
parents 1b59be2a f03c6599
...@@ -19,6 +19,8 @@ prototypes: ...@@ -19,6 +19,8 @@ prototypes:
void (*d_release)(struct dentry *); void (*d_release)(struct dentry *);
void (*d_iput)(struct dentry *, struct inode *); void (*d_iput)(struct dentry *, struct inode *);
char *(*d_dname)((struct dentry *dentry, char *buffer, int buflen); 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: locking rules:
rename_lock ->d_lock may block rcu-walk rename_lock ->d_lock may block rcu-walk
...@@ -29,6 +31,8 @@ d_delete: no yes no no ...@@ -29,6 +31,8 @@ d_delete: no yes no no
d_release: no no yes no d_release: no no yes no
d_iput: no no yes no d_iput: no no yes no
d_dname: no no no no d_dname: no no no no
d_automount: no no yes no
d_manage: no no yes (ref-walk) maybe
--------------------------- inode_operations --------------------------- --------------------------- inode_operations ---------------------------
prototypes: prototypes:
......
...@@ -864,6 +864,8 @@ struct dentry_operations { ...@@ -864,6 +864,8 @@ struct dentry_operations {
void (*d_release)(struct dentry *); void (*d_release)(struct dentry *);
void (*d_iput)(struct dentry *, struct inode *); void (*d_iput)(struct dentry *, struct inode *);
char *(*d_dname)(struct dentry *, char *, int); 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 d_revalidate: called when the VFS needs to revalidate a dentry. This
...@@ -930,6 +932,47 @@ struct dentry_operations { ...@@ -930,6 +932,47 @@ struct dentry_operations {
at the end of the buffer, and returns a pointer to the first char. 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. 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 : Example :
static char *pipefs_dname(struct dentry *dent, char *buffer, int buflen) static char *pipefs_dname(struct dentry *dent, char *buffer, int buflen)
......
...@@ -1201,7 +1201,7 @@ static int __init init_mtdchar(void) ...@@ -1201,7 +1201,7 @@ static int __init init_mtdchar(void)
static void __exit cleanup_mtdchar(void) static void __exit cleanup_mtdchar(void)
{ {
unregister_mtd_user(&mtdchar_notifier); unregister_mtd_user(&mtdchar_notifier);
mntput_long(mtd_inode_mnt); mntput(mtd_inode_mnt);
unregister_filesystem(&mtd_inodefs_type); unregister_filesystem(&mtd_inodefs_type);
__unregister_chrdev(MTD_CHAR_MAJOR, 0, 1 << MINORBITS, "mtd"); __unregister_chrdev(MTD_CHAR_MAJOR, 0, 1 << MINORBITS, "mtd");
} }
......
...@@ -88,14 +88,13 @@ struct autofs_dir_ent *autofs_expire(struct super_block *sb, ...@@ -88,14 +88,13 @@ struct autofs_dir_ent *autofs_expire(struct super_block *sb,
} }
path.mnt = mnt; path.mnt = mnt;
path_get(&path); path_get(&path);
if (!follow_down(&path)) { if (!follow_down_one(&path)) {
path_put(&path); path_put(&path);
DPRINTK(("autofs: not expirable\ DPRINTK(("autofs: not expirable\
(not a mounted directory): %s\n", ent->name)); (not a mounted directory): %s\n", ent->name));
continue; continue;
} }
while (d_mountpoint(path.dentry) && follow_down(&path)) follow_down(&path, false); // TODO: need to check error
;
umount_ok = may_umount(path.mnt); umount_ok = may_umount(path.mnt);
path_put(&path); path_put(&path);
......
...@@ -66,6 +66,7 @@ const struct dentry_operations afs_fs_dentry_operations = { ...@@ -66,6 +66,7 @@ const struct dentry_operations afs_fs_dentry_operations = {
.d_revalidate = afs_d_revalidate, .d_revalidate = afs_d_revalidate,
.d_delete = afs_d_delete, .d_delete = afs_d_delete,
.d_release = afs_d_release, .d_release = afs_d_release,
.d_automount = afs_d_automount,
}; };
#define AFS_DIR_HASHTBL_SIZE 128 #define AFS_DIR_HASHTBL_SIZE 128
......
...@@ -184,7 +184,8 @@ struct inode *afs_iget_autocell(struct inode *dir, const char *dev_name, ...@@ -184,7 +184,8 @@ struct inode *afs_iget_autocell(struct inode *dir, const char *dev_name,
inode->i_generation = 0; inode->i_generation = 0;
set_bit(AFS_VNODE_PSEUDODIR, &vnode->flags); 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); unlock_new_inode(inode);
_leave(" = %p", inode); _leave(" = %p", inode);
return inode; return inode;
......
...@@ -592,6 +592,7 @@ extern const struct inode_operations afs_mntpt_inode_operations; ...@@ -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 inode_operations afs_autocell_inode_operations;
extern const struct file_operations afs_mntpt_file_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 int afs_mntpt_check_symlink(struct afs_vnode *, struct key *);
extern void afs_mntpt_kill_timer(void); extern void afs_mntpt_kill_timer(void);
......
...@@ -24,7 +24,6 @@ static struct dentry *afs_mntpt_lookup(struct inode *dir, ...@@ -24,7 +24,6 @@ static struct dentry *afs_mntpt_lookup(struct inode *dir,
struct dentry *dentry, struct dentry *dentry,
struct nameidata *nd); struct nameidata *nd);
static int afs_mntpt_open(struct inode *inode, struct file *file); 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); static void afs_mntpt_expiry_timed_out(struct work_struct *work);
const struct file_operations afs_mntpt_file_operations = { const struct file_operations afs_mntpt_file_operations = {
...@@ -34,13 +33,11 @@ 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 = { const struct inode_operations afs_mntpt_inode_operations = {
.lookup = afs_mntpt_lookup, .lookup = afs_mntpt_lookup,
.follow_link = afs_mntpt_follow_link,
.readlink = page_readlink, .readlink = page_readlink,
.getattr = afs_getattr, .getattr = afs_getattr,
}; };
const struct inode_operations afs_autocell_inode_operations = { const struct inode_operations afs_autocell_inode_operations = {
.follow_link = afs_mntpt_follow_link,
.getattr = afs_getattr, .getattr = afs_getattr,
}; };
...@@ -88,6 +85,7 @@ int afs_mntpt_check_symlink(struct afs_vnode *vnode, struct key *key) ...@@ -88,6 +85,7 @@ int afs_mntpt_check_symlink(struct afs_vnode *vnode, struct key *key)
_debug("symlink is a mountpoint"); _debug("symlink is a mountpoint");
spin_lock(&vnode->lock); spin_lock(&vnode->lock);
set_bit(AFS_VNODE_MOUNTPOINT, &vnode->flags); set_bit(AFS_VNODE_MOUNTPOINT, &vnode->flags);
vnode->vfs_inode.i_flags |= S_AUTOMOUNT;
spin_unlock(&vnode->lock); spin_unlock(&vnode->lock);
} }
...@@ -238,52 +236,24 @@ static struct vfsmount *afs_mntpt_do_automount(struct dentry *mntpt) ...@@ -238,52 +236,24 @@ static struct vfsmount *afs_mntpt_do_automount(struct dentry *mntpt)
} }
/* /*
* 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; struct vfsmount *newmnt;
int err;
_enter("%p{%s},{%s:%p{%s},}", _enter("{%s,%s}", path->mnt->mnt_devname, path->dentry->d_name.name);
dentry,
dentry->d_name.name,
nd->path.mnt->mnt_devname,
dentry,
nd->path.dentry->d_name.name);
dput(nd->path.dentry);
nd->path.dentry = dget(dentry);
newmnt = afs_mntpt_do_automount(nd->path.dentry);
if (IS_ERR(newmnt)) {
path_put(&nd->path);
return (void *)newmnt;
}
mntget(newmnt); newmnt = afs_mntpt_do_automount(path->dentry);
err = do_add_mount(newmnt, &nd->path, MNT_SHRINKABLE, &afs_vfsmounts); if (IS_ERR(newmnt))
switch (err) { return newmnt;
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); mntget(newmnt); /* prevent immediate expiration */
return ERR_PTR(err); 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) ...@@ -233,7 +233,7 @@ static int __init anon_inode_init(void)
return 0; return 0;
err_mntput: err_mntput:
mntput_long(anon_inode_mnt); mntput(anon_inode_mnt);
err_unregister_filesystem: err_unregister_filesystem:
unregister_filesystem(&anon_inode_fs_type); unregister_filesystem(&anon_inode_fs_type);
err_exit: err_exit:
......
...@@ -99,7 +99,6 @@ struct autofs_info { ...@@ -99,7 +99,6 @@ struct autofs_info {
}; };
#define AUTOFS_INF_EXPIRING (1<<0) /* dentry is in the process of expiring */ #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 */ #define AUTOFS_INF_PENDING (1<<2) /* dentry pending mount */
struct autofs_wait_queue { struct autofs_wait_queue {
...@@ -176,13 +175,6 @@ static inline int autofs4_ispending(struct dentry *dentry) ...@@ -176,13 +175,6 @@ static inline int autofs4_ispending(struct dentry *dentry)
return 0; 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 *); struct inode *autofs4_get_inode(struct super_block *, struct autofs_info *);
void autofs4_free_ino(struct autofs_info *); void autofs4_free_ino(struct autofs_info *);
...@@ -212,11 +204,83 @@ void autofs_dev_ioctl_exit(void); ...@@ -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_symlink_inode_operations;
extern const struct inode_operations autofs4_dir_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_dir_operations;
extern const struct file_operations autofs4_root_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 */ /* Initializing function */
...@@ -229,19 +293,6 @@ int autofs4_wait(struct autofs_sb_info *,struct dentry *, enum autofs_notify); ...@@ -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); int autofs4_wait_release(struct autofs_sb_info *,autofs_wqt_t,int);
void autofs4_catatonic_mode(struct autofs_sb_info *); 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) static inline u32 autofs4_get_dev(struct autofs_sb_info *sbi)
{ {
return new_encode_dev(sbi->sb->s_dev); return new_encode_dev(sbi->sb->s_dev);
......
...@@ -551,7 +551,7 @@ static int autofs_dev_ioctl_ismountpoint(struct file *fp, ...@@ -551,7 +551,7 @@ static int autofs_dev_ioctl_ismountpoint(struct file *fp,
err = have_submounts(path.dentry); err = have_submounts(path.dentry);
if (follow_down(&path)) if (follow_down_one(&path))
magic = path.mnt->mnt_sb->s_magic; magic = path.mnt->mnt_sb->s_magic;
} }
......
...@@ -26,10 +26,6 @@ static inline int autofs4_can_expire(struct dentry *dentry, ...@@ -26,10 +26,6 @@ static inline int autofs4_can_expire(struct dentry *dentry,
if (ino == NULL) if (ino == NULL)
return 0; return 0;
/* No point expiring a pending mount */
if (ino->flags & AUTOFS_INF_PENDING)
return 0;
if (!do_now) { if (!do_now) {
/* Too young to die */ /* Too young to die */
if (!timeout || time_after(ino->last_used + timeout, now)) if (!timeout || time_after(ino->last_used + timeout, now))
...@@ -56,7 +52,7 @@ static int autofs4_mount_busy(struct vfsmount *mnt, struct dentry *dentry) ...@@ -56,7 +52,7 @@ static int autofs4_mount_busy(struct vfsmount *mnt, struct dentry *dentry)
path_get(&path); path_get(&path);
if (!follow_down(&path)) if (!follow_down_one(&path))
goto done; goto done;
if (is_autofs4_dentry(path.dentry)) { if (is_autofs4_dentry(path.dentry)) {
...@@ -283,6 +279,7 @@ struct dentry *autofs4_expire_direct(struct super_block *sb, ...@@ -283,6 +279,7 @@ struct dentry *autofs4_expire_direct(struct super_block *sb,
unsigned long timeout; unsigned long timeout;
struct dentry *root = dget(sb->s_root); struct dentry *root = dget(sb->s_root);
int do_now = how & AUTOFS_EXP_IMMEDIATE; int do_now = how & AUTOFS_EXP_IMMEDIATE;
struct autofs_info *ino;
if (!root) if (!root)
return NULL; return NULL;
...@@ -291,19 +288,21 @@ struct dentry *autofs4_expire_direct(struct super_block *sb, ...@@ -291,19 +288,21 @@ struct dentry *autofs4_expire_direct(struct super_block *sb,
timeout = sbi->exp_timeout; timeout = sbi->exp_timeout;
spin_lock(&sbi->fs_lock); 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)) { if (!autofs4_direct_busy(mnt, root, timeout, do_now)) {
struct autofs_info *ino = autofs4_dentry_ino(root); 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; ino->flags |= AUTOFS_INF_EXPIRING;
init_completion(&ino->expire_complete); init_completion(&ino->expire_complete);
spin_unlock(&sbi->fs_lock); spin_unlock(&sbi->fs_lock);
return root; return root;
} }
managed_dentry_clear_transit(root);
spin_unlock(&sbi->fs_lock); spin_unlock(&sbi->fs_lock);
dput(root); dput(root);
...@@ -340,6 +339,10 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb, ...@@ -340,6 +339,10 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb,
while ((dentry = get_next_positive_dentry(dentry, root))) { while ((dentry = get_next_positive_dentry(dentry, root))) {
spin_lock(&sbi->fs_lock); spin_lock(&sbi->fs_lock);
ino = autofs4_dentry_ino(dentry); 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 * Case 1: (i) indirect mount or top level pseudo direct mount
...@@ -399,6 +402,8 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb, ...@@ -399,6 +402,8 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb,
} }
} }
next: next:
managed_dentry_clear_transit(dentry);
cont:
spin_unlock(&sbi->fs_lock); spin_unlock(&sbi->fs_lock);
} }
return NULL; return NULL;
...@@ -479,6 +484,8 @@ int autofs4_expire_run(struct super_block *sb, ...@@ -479,6 +484,8 @@ int autofs4_expire_run(struct super_block *sb,
spin_lock(&sbi->fs_lock); spin_lock(&sbi->fs_lock);
ino = autofs4_dentry_ino(dentry); ino = autofs4_dentry_ino(dentry);
ino->flags &= ~AUTOFS_INF_EXPIRING; ino->flags &= ~AUTOFS_INF_EXPIRING;
if (!d_unhashed(dentry))
managed_dentry_clear_transit(dentry);
complete_all(&ino->expire_complete); complete_all(&ino->expire_complete);
spin_unlock(&sbi->fs_lock); spin_unlock(&sbi->fs_lock);
...@@ -504,18 +511,18 @@ int autofs4_do_expire_multi(struct super_block *sb, struct vfsmount *mnt, ...@@ -504,18 +511,18 @@ int autofs4_do_expire_multi(struct super_block *sb, struct vfsmount *mnt,
ret = autofs4_wait(sbi, dentry, NFY_EXPIRE); ret = autofs4_wait(sbi, dentry, NFY_EXPIRE);
spin_lock(&sbi->fs_lock); 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; 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); complete_all(&ino->expire_complete);
spin_unlock(&sbi->fs_lock); spin_unlock(&sbi->fs_lock);
dput(dentry); dput(dentry);
......
...@@ -45,7 +45,6 @@ struct autofs_info *autofs4_init_ino(struct autofs_info *ino, ...@@ -45,7 +45,6 @@ struct autofs_info *autofs4_init_ino(struct autofs_info *ino,
if (!reinit) { if (!reinit) {
ino->flags = 0; ino->flags = 0;
ino->inode = NULL;
ino->dentry = NULL; ino->dentry = NULL;
ino->size = 0; ino->size = 0;
INIT_LIST_HEAD(&ino->active); INIT_LIST_HEAD(&ino->active);
...@@ -76,19 +75,8 @@ struct autofs_info *autofs4_init_ino(struct autofs_info *ino, ...@@ -76,19 +75,8 @@ struct autofs_info *autofs4_init_ino(struct autofs_info *ino,
void autofs4_free_ino(struct autofs_info *ino) void autofs4_free_ino(struct autofs_info *ino)
{ {
struct autofs_info *p_ino;
if (ino->dentry) { if (ino->dentry) {
ino->dentry->d_fsdata = NULL; 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; ino->dentry = NULL;
} }
if (ino->free) if (ino->free)
...@@ -251,10 +239,6 @@ static struct autofs_info *autofs4_mkroot(struct autofs_sb_info *sbi) ...@@ -251,10 +239,6 @@ static struct autofs_info *autofs4_mkroot(struct autofs_sb_info *sbi)
return ino; 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) int autofs4_fill_super(struct super_block *s, void *data, int silent)
{ {
struct inode * root_inode; struct inode * root_inode;
...@@ -292,6 +276,7 @@ int autofs4_fill_super(struct super_block *s, void *data, int silent) ...@@ -292,6 +276,7 @@ int autofs4_fill_super(struct super_block *s, void *data, int silent)
s->s_blocksize_bits = 10; s->s_blocksize_bits = 10;
s->s_magic = AUTOFS_SUPER_MAGIC; s->s_magic = AUTOFS_SUPER_MAGIC;
s->s_op = &autofs4_sops; s->s_op = &autofs4_sops;
s->s_d_op = &autofs4_dentry_operations;
s->s_time_gran = 1; s->s_time_gran = 1;
/* /*
...@@ -309,7 +294,6 @@ int autofs4_fill_super(struct super_block *s, void *data, int silent) ...@@ -309,7 +294,6 @@ int autofs4_fill_super(struct super_block *s, void *data, int silent)
goto fail_iput; goto fail_iput;
pipe = NULL; pipe = NULL;
d_set_d_op(root, &autofs4_sb_dentry_operations);
root->d_fsdata = ino; root->d_fsdata = ino;
/* Can this call block? */ /* Can this call block? */
...@@ -320,10 +304,11 @@ int autofs4_fill_super(struct super_block *s, void *data, int silent) ...@@ -320,10 +304,11 @@ int autofs4_fill_super(struct super_block *s, void *data, int silent)
goto fail_dput; goto fail_dput;
} }
if (autofs_type_trigger(sbi->type))
__managed_dentry_set_managed(root);
root_inode->i_fop = &autofs4_root_operations; root_inode->i_fop = &autofs4_root_operations;
root_inode->i_op = autofs_type_trigger(sbi->type) ? root_inode->i_op = &autofs4_dir_inode_operations;
&autofs4_direct_root_inode_operations :
&autofs4_indirect_root_inode_operations;
/* Couldn't this be tested earlier? */ /* Couldn't this be tested earlier? */
if (sbi->max_proto < AUTOFS_MIN_PROTO_VERSION || if (sbi->max_proto < AUTOFS_MIN_PROTO_VERSION ||
...@@ -391,7 +376,6 @@ struct inode *autofs4_get_inode(struct super_block *sb, ...@@ -391,7 +376,6 @@ struct inode *autofs4_get_inode(struct super_block *sb,
if (inode == NULL) if (inode == NULL)
return NULL; return NULL;
inf->inode = inode;
inode->i_mode = inf->mode; inode->i_mode = inf->mode;
if (sb->s_root) { if (sb->s_root) {
inode->i_uid = sb->s_root->d_inode->i_uid; inode->i_uid = sb->s_root->d_inode->i_uid;
......
This diff is collapsed.
...@@ -309,6 +309,9 @@ static int validate_request(struct autofs_wait_queue **wait, ...@@ -309,6 +309,9 @@ static int validate_request(struct autofs_wait_queue **wait,
* completed while we waited on the mutex ... * completed while we waited on the mutex ...
*/ */
if (notify == NFY_MOUNT) { if (notify == NFY_MOUNT) {
struct dentry *new = NULL;
int valid = 1;
/* /*
* If the dentry was successfully mounted while we slept * If the dentry was successfully mounted while we slept
* on the wait queue mutex we can return success. If it * on the wait queue mutex we can return success. If it
...@@ -316,8 +319,20 @@ static int validate_request(struct autofs_wait_queue **wait, ...@@ -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 * a multi-mount with no mount at it's base) we can
* continue on and create a new request. * 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)) if (have_submounts(dentry))
return 0; valid = 0;
if (new)
dput(new);
return valid;
} }
return 1; return 1;
......
...@@ -255,35 +255,6 @@ static struct vfsmount *cifs_dfs_do_refmount(struct cifs_sb_info *cifs_sb, ...@@ -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) static void dump_referral(const struct dfs_info3_param *ref)
{ {
cFYI(1, "DFS: ref path: %s", ref->path_name); cFYI(1, "DFS: ref path: %s", ref->path_name);
...@@ -293,45 +264,43 @@ static void dump_referral(const struct dfs_info3_param *ref) ...@@ -293,45 +264,43 @@ static void dump_referral(const struct dfs_info3_param *ref)
ref->path_consumed); ref->path_consumed);
} }
/*
static void* * Create a vfsmount that we can automount
cifs_dfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd) */
static struct vfsmount *cifs_dfs_do_automount(struct dentry *mntpt)
{ {
struct dfs_info3_param *referrals = NULL; struct dfs_info3_param *referrals = NULL;
unsigned int num_referrals = 0; unsigned int num_referrals = 0;
struct cifs_sb_info *cifs_sb; struct cifs_sb_info *cifs_sb;
struct cifsSesInfo *ses; struct cifsSesInfo *ses;
char *full_path = NULL; char *full_path;
int xid, i; int xid, i;
int rc = 0; int rc;
struct vfsmount *mnt = ERR_PTR(-ENOENT); struct vfsmount *mnt;
struct tcon_link *tlink; struct tcon_link *tlink;
cFYI(1, "in %s", __func__); cFYI(1, "in %s", __func__);
BUG_ON(IS_ROOT(dentry)); BUG_ON(IS_ROOT(mntpt));
xid = GetXid(); xid = GetXid();
dput(nd->path.dentry);
nd->path.dentry = dget(dentry);
/* /*
* The MSDFS spec states that paths in DFS referral requests and * The MSDFS spec states that paths in DFS referral requests and
* responses must be prefixed by a single '\' character instead of * responses must be prefixed by a single '\' character instead of
* the double backslashes usually used in the UNC. This function * the double backslashes usually used in the UNC. This function
* gives us the latter, so we must adjust the result. * gives us the latter, so we must adjust the result.
*/ */
full_path = build_path_from_dentry(dentry); mnt = ERR_PTR(-ENOMEM);
if (full_path == NULL) { full_path = build_path_from_dentry(mntpt);
rc = -ENOMEM; if (full_path == NULL)
goto out_err; 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); tlink = cifs_sb_tlink(cifs_sb);
mnt = ERR_PTR(-EINVAL);
if (IS_ERR(tlink)) { if (IS_ERR(tlink)) {
rc = PTR_ERR(tlink); mnt = ERR_CAST(tlink);
goto out_err; goto free_full_path;
} }
ses = tlink_tcon(tlink)->ses; ses = tlink_tcon(tlink)->ses;
...@@ -341,46 +310,63 @@ cifs_dfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd) ...@@ -341,46 +310,63 @@ cifs_dfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd)
cifs_put_tlink(tlink); cifs_put_tlink(tlink);
mnt = ERR_PTR(-ENOENT);
for (i = 0; i < num_referrals; i++) { for (i = 0; i < num_referrals; i++) {
int len; int len;
dump_referral(referrals+i); dump_referral(referrals + i);
/* connect to a node */ /* connect to a node */
len = strlen(referrals[i].node_name); len = strlen(referrals[i].node_name);
if (len < 2) { if (len < 2) {
cERROR(1, "%s: Net Address path too short: %s", cERROR(1, "%s: Net Address path too short: %s",
__func__, referrals[i].node_name); __func__, referrals[i].node_name);
rc = -EINVAL; mnt = ERR_PTR(-EINVAL);
goto out_err; break;
} }
mnt = cifs_dfs_do_refmount(cifs_sb, mnt = cifs_dfs_do_refmount(cifs_sb,
full_path, referrals + i); full_path, referrals + i);
cFYI(1, "%s: cifs_dfs_do_refmount:%s , mnt:%p", __func__, cFYI(1, "%s: cifs_dfs_do_refmount:%s , mnt:%p", __func__,
referrals[i].node_name, mnt); referrals[i].node_name, mnt);
/* complete mount procedure if we accured submount */
if (!IS_ERR(mnt)) if (!IS_ERR(mnt))
break; goto success;
} }
/* we need it cause for() above could exit without valid submount */ /* no valid submounts were found; return error from get_dfs_path() by
rc = PTR_ERR(mnt); * preference */
if (IS_ERR(mnt)) if (rc != 0)
goto out_err; mnt = ERR_PTR(rc);
rc = add_mount_helper(mnt, nd, &cifs_dfs_automount_list);
out: success:
FreeXid(xid);
free_dfs_info_array(referrals, num_referrals); free_dfs_info_array(referrals, num_referrals);
free_full_path:
kfree(full_path); kfree(full_path);
free_xid:
FreeXid(xid);
cFYI(1, "leaving %s" , __func__); cFYI(1, "leaving %s" , __func__);
return ERR_PTR(rc); return mnt;
out_err: }
path_put(&nd->path);
goto out; /*
* 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 = { 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); ...@@ -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_dentry_ops;
extern const struct dentry_operations cifs_ci_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 */ /* Functions related to symlinks */
extern void *cifs_follow_link(struct dentry *direntry, struct nameidata *nd); extern void *cifs_follow_link(struct dentry *direntry, struct nameidata *nd);
extern void cifs_put_link(struct dentry *direntry, extern void cifs_put_link(struct dentry *direntry,
......
...@@ -675,6 +675,7 @@ cifs_d_revalidate(struct dentry *direntry, struct nameidata *nd) ...@@ -675,6 +675,7 @@ cifs_d_revalidate(struct dentry *direntry, struct nameidata *nd)
const struct dentry_operations cifs_dentry_ops = { const struct dentry_operations cifs_dentry_ops = {
.d_revalidate = cifs_d_revalidate, .d_revalidate = cifs_d_revalidate,
.d_automount = cifs_dfs_d_automount,
/* d_delete: cifs_d_delete, */ /* not needed except for debugging */ /* d_delete: cifs_d_delete, */ /* not needed except for debugging */
}; };
...@@ -711,4 +712,5 @@ const struct dentry_operations cifs_ci_dentry_ops = { ...@@ -711,4 +712,5 @@ const struct dentry_operations cifs_ci_dentry_ops = {
.d_revalidate = cifs_d_revalidate, .d_revalidate = cifs_d_revalidate,
.d_hash = cifs_ci_hash, .d_hash = cifs_ci_hash,
.d_compare = cifs_ci_compare, .d_compare = cifs_ci_compare,
.d_automount = cifs_dfs_d_automount,
}; };
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
#include "fscache.h" #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); 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) ...@@ -60,7 +60,7 @@ static void cifs_set_ops(struct inode *inode, const bool is_dfs_referral)
break; break;
case S_IFDIR: case S_IFDIR:
#ifdef CONFIG_CIFS_DFS_UPCALL #ifdef CONFIG_CIFS_DFS_UPCALL
if (is_dfs_referral) { if (IS_AUTOMOUNT(inode)) {
inode->i_op = &cifs_dfs_referral_inode_operations; inode->i_op = &cifs_dfs_referral_inode_operations;
} else { } else {
#else /* NO DFS support, treat as a directory */ #else /* NO DFS support, treat as a directory */
...@@ -167,7 +167,9 @@ cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr) ...@@ -167,7 +167,9 @@ cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr)
} }
spin_unlock(&inode->i_lock); 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 void
......
...@@ -1380,8 +1380,11 @@ EXPORT_SYMBOL(d_set_d_op); ...@@ -1380,8 +1380,11 @@ EXPORT_SYMBOL(d_set_d_op);
static void __d_instantiate(struct dentry *dentry, struct inode *inode) static void __d_instantiate(struct dentry *dentry, struct inode *inode)
{ {
spin_lock(&dentry->d_lock); 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); list_add(&dentry->d_alias, &inode->i_dentry);
}
dentry->d_inode = inode; dentry->d_inode = inode;
dentry_rcuwalk_barrier(dentry); dentry_rcuwalk_barrier(dentry);
spin_unlock(&dentry->d_lock); spin_unlock(&dentry->d_lock);
......
...@@ -4,6 +4,19 @@ ...@@ -4,6 +4,19 @@
#include <linux/path.h> #include <linux/path.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/fs_struct.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. * 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) ...@@ -17,11 +30,11 @@ void set_fs_root(struct fs_struct *fs, struct path *path)
write_seqcount_begin(&fs->seq); write_seqcount_begin(&fs->seq);
old_root = fs->root; old_root = fs->root;
fs->root = *path; fs->root = *path;
path_get_long(path); path_get_longterm(path);
write_seqcount_end(&fs->seq); write_seqcount_end(&fs->seq);
spin_unlock(&fs->lock); spin_unlock(&fs->lock);
if (old_root.dentry) 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) ...@@ -36,12 +49,12 @@ void set_fs_pwd(struct fs_struct *fs, struct path *path)
write_seqcount_begin(&fs->seq); write_seqcount_begin(&fs->seq);
old_pwd = fs->pwd; old_pwd = fs->pwd;
fs->pwd = *path; fs->pwd = *path;
path_get_long(path); path_get_longterm(path);
write_seqcount_end(&fs->seq); write_seqcount_end(&fs->seq);
spin_unlock(&fs->lock); spin_unlock(&fs->lock);
if (old_pwd.dentry) 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) 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) ...@@ -59,13 +72,13 @@ void chroot_fs_refs(struct path *old_root, struct path *new_root)
write_seqcount_begin(&fs->seq); write_seqcount_begin(&fs->seq);
if (fs->root.dentry == old_root->dentry if (fs->root.dentry == old_root->dentry
&& fs->root.mnt == old_root->mnt) { && fs->root.mnt == old_root->mnt) {
path_get_long(new_root); path_get_longterm(new_root);
fs->root = *new_root; fs->root = *new_root;
count++; count++;
} }
if (fs->pwd.dentry == old_root->dentry if (fs->pwd.dentry == old_root->dentry
&& fs->pwd.mnt == old_root->mnt) { && fs->pwd.mnt == old_root->mnt) {
path_get_long(new_root); path_get_longterm(new_root);
fs->pwd = *new_root; fs->pwd = *new_root;
count++; count++;
} }
...@@ -76,13 +89,13 @@ void chroot_fs_refs(struct path *old_root, struct path *new_root) ...@@ -76,13 +89,13 @@ void chroot_fs_refs(struct path *old_root, struct path *new_root)
} while_each_thread(g, p); } while_each_thread(g, p);
read_unlock(&tasklist_lock); read_unlock(&tasklist_lock);
while (count--) while (count--)
path_put_long(old_root); path_put_longterm(old_root);
} }
void free_fs_struct(struct fs_struct *fs) void free_fs_struct(struct fs_struct *fs)
{ {
path_put_long(&fs->root); path_put_longterm(&fs->root);
path_put_long(&fs->pwd); path_put_longterm(&fs->pwd);
kmem_cache_free(fs_cachep, fs); kmem_cache_free(fs_cachep, fs);
} }
...@@ -118,9 +131,9 @@ struct fs_struct *copy_fs_struct(struct fs_struct *old) ...@@ -118,9 +131,9 @@ struct fs_struct *copy_fs_struct(struct fs_struct *old)
spin_lock(&old->lock); spin_lock(&old->lock);
fs->root = old->root; fs->root = old->root;
path_get_long(&fs->root); path_get_longterm(&fs->root);
fs->pwd = old->pwd; fs->pwd = old->pwd;
path_get_long(&fs->pwd); path_get_longterm(&fs->pwd);
spin_unlock(&old->lock); spin_unlock(&old->lock);
} }
return fs; return fs;
......
...@@ -70,6 +70,11 @@ extern void mnt_set_mountpoint(struct vfsmount *, struct dentry *, ...@@ -70,6 +70,11 @@ extern void mnt_set_mountpoint(struct vfsmount *, struct dentry *,
extern void release_mounts(struct list_head *); extern void release_mounts(struct list_head *);
extern void umount_tree(struct vfsmount *, int, struct list_head *); extern void umount_tree(struct vfsmount *, int, struct list_head *);
extern struct vfsmount *copy_tree(struct vfsmount *, struct dentry *, int); extern struct vfsmount *copy_tree(struct vfsmount *, struct dentry *, int);
extern int do_add_mount(struct vfsmount *, struct path *, int);
extern void mnt_clear_expiry(struct vfsmount *);
extern void mnt_make_longterm(struct vfsmount *);
extern void mnt_make_shortterm(struct vfsmount *);
extern void __init mnt_init(void); extern void __init mnt_init(void);
......
This diff is collapsed.
...@@ -183,7 +183,7 @@ static inline void mnt_dec_count(struct vfsmount *mnt) ...@@ -183,7 +183,7 @@ static inline void mnt_dec_count(struct vfsmount *mnt)
unsigned int mnt_get_count(struct vfsmount *mnt) unsigned int mnt_get_count(struct vfsmount *mnt)
{ {
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
unsigned int count = atomic_read(&mnt->mnt_longrefs); unsigned int count = 0;
int cpu; int cpu;
for_each_possible_cpu(cpu) { for_each_possible_cpu(cpu) {
...@@ -217,7 +217,7 @@ struct vfsmount *alloc_vfsmnt(const char *name) ...@@ -217,7 +217,7 @@ struct vfsmount *alloc_vfsmnt(const char *name)
if (!mnt->mnt_pcp) if (!mnt->mnt_pcp)
goto out_free_devname; goto out_free_devname;
atomic_set(&mnt->mnt_longrefs, 1); this_cpu_add(mnt->mnt_pcp->mnt_count, 1);
#else #else
mnt->mnt_count = 1; mnt->mnt_count = 1;
mnt->mnt_writers = 0; mnt->mnt_writers = 0;
...@@ -624,8 +624,11 @@ static void commit_tree(struct vfsmount *mnt) ...@@ -624,8 +624,11 @@ static void commit_tree(struct vfsmount *mnt)
BUG_ON(parent == mnt); BUG_ON(parent == mnt);
list_add_tail(&head, &mnt->mnt_list); 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; m->mnt_ns = n;
atomic_inc(&m->mnt_longterm);
}
list_splice(&head, n->list.prev); list_splice(&head, n->list.prev);
list_add_tail(&mnt->mnt_hash, mount_hashtable + list_add_tail(&mnt->mnt_hash, mount_hashtable +
...@@ -734,51 +737,30 @@ static inline void mntfree(struct vfsmount *mnt) ...@@ -734,51 +737,30 @@ static inline void mntfree(struct vfsmount *mnt)
deactivate_super(sb); deactivate_super(sb);
} }
#ifdef CONFIG_SMP static void mntput_no_expire(struct vfsmount *mnt)
static inline void __mntput(struct vfsmount *mnt, int longrefs)
{ {
if (!longrefs) {
put_again: put_again:
br_read_lock(vfsmount_lock); #ifdef CONFIG_SMP
if (likely(atomic_read(&mnt->mnt_longrefs))) { br_read_lock(vfsmount_lock);
mnt_dec_count(mnt); if (likely(atomic_read(&mnt->mnt_longterm))) {
br_read_unlock(vfsmount_lock); mnt_dec_count(mnt);
return;
}
br_read_unlock(vfsmount_lock); br_read_unlock(vfsmount_lock);
} else { return;
BUG_ON(!atomic_read(&mnt->mnt_longrefs));
if (atomic_add_unless(&mnt->mnt_longrefs, -1, 1))
return;
} }
br_read_unlock(vfsmount_lock);
br_write_lock(vfsmount_lock); br_write_lock(vfsmount_lock);
if (!longrefs) mnt_dec_count(mnt);
mnt_dec_count(mnt);
else
atomic_dec(&mnt->mnt_longrefs);
if (mnt_get_count(mnt)) { if (mnt_get_count(mnt)) {
br_write_unlock(vfsmount_lock); br_write_unlock(vfsmount_lock);
return; 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 #else
static inline void __mntput(struct vfsmount *mnt, int longrefs)
{
put_again:
mnt_dec_count(mnt); mnt_dec_count(mnt);
if (likely(mnt_get_count(mnt))) if (likely(mnt_get_count(mnt)))
return; return;
br_write_lock(vfsmount_lock); br_write_lock(vfsmount_lock);
#endif
if (unlikely(mnt->mnt_pinned)) { if (unlikely(mnt->mnt_pinned)) {
mnt_add_count(mnt, mnt->mnt_pinned + 1); mnt_add_count(mnt, mnt->mnt_pinned + 1);
mnt->mnt_pinned = 0; mnt->mnt_pinned = 0;
...@@ -789,12 +771,6 @@ static inline void __mntput(struct vfsmount *mnt, int longrefs) ...@@ -789,12 +771,6 @@ static inline void __mntput(struct vfsmount *mnt, int longrefs)
br_write_unlock(vfsmount_lock); br_write_unlock(vfsmount_lock);
mntfree(mnt); mntfree(mnt);
} }
#endif
static void mntput_no_expire(struct vfsmount *mnt)
{
__mntput(mnt, 0);
}
void mntput(struct vfsmount *mnt) void mntput(struct vfsmount *mnt)
{ {
...@@ -802,7 +778,7 @@ void mntput(struct vfsmount *mnt) ...@@ -802,7 +778,7 @@ void mntput(struct vfsmount *mnt)
/* avoid cacheline pingpong, hope gcc doesn't get "smart" */ /* avoid cacheline pingpong, hope gcc doesn't get "smart" */
if (unlikely(mnt->mnt_expiry_mark)) if (unlikely(mnt->mnt_expiry_mark))
mnt->mnt_expiry_mark = 0; mnt->mnt_expiry_mark = 0;
__mntput(mnt, 0); mntput_no_expire(mnt);
} }
} }
EXPORT_SYMBOL(mntput); EXPORT_SYMBOL(mntput);
...@@ -815,33 +791,6 @@ struct vfsmount *mntget(struct vfsmount *mnt) ...@@ -815,33 +791,6 @@ struct vfsmount *mntget(struct vfsmount *mnt)
} }
EXPORT_SYMBOL(mntget); 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) void mnt_pin(struct vfsmount *mnt)
{ {
br_write_lock(vfsmount_lock); br_write_lock(vfsmount_lock);
...@@ -1216,7 +1165,7 @@ void release_mounts(struct list_head *head) ...@@ -1216,7 +1165,7 @@ void release_mounts(struct list_head *head)
dput(dentry); dput(dentry);
mntput(m); mntput(m);
} }
mntput_long(mnt); mntput(mnt);
} }
} }
...@@ -1226,19 +1175,21 @@ void release_mounts(struct list_head *head) ...@@ -1226,19 +1175,21 @@ void release_mounts(struct list_head *head)
*/ */
void umount_tree(struct vfsmount *mnt, int propagate, struct list_head *kill) void umount_tree(struct vfsmount *mnt, int propagate, struct list_head *kill)
{ {
LIST_HEAD(tmp_list);
struct vfsmount *p; struct vfsmount *p;
for (p = mnt; p; p = next_mnt(p, mnt)) for (p = mnt; p; p = next_mnt(p, mnt))
list_move(&p->mnt_hash, kill); list_move(&p->mnt_hash, &tmp_list);
if (propagate) 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_expire);
list_del_init(&p->mnt_list); list_del_init(&p->mnt_list);
__touch_mnt_namespace(p->mnt_ns); __touch_mnt_namespace(p->mnt_ns);
p->mnt_ns = NULL; p->mnt_ns = NULL;
atomic_dec(&p->mnt_longterm);
list_del_init(&p->mnt_child); list_del_init(&p->mnt_child);
if (p->mnt_parent != p) { if (p->mnt_parent != p) {
p->mnt_parent->mnt_ghosts++; p->mnt_parent->mnt_ghosts++;
...@@ -1246,6 +1197,7 @@ void umount_tree(struct vfsmount *mnt, int propagate, struct list_head *kill) ...@@ -1246,6 +1197,7 @@ void umount_tree(struct vfsmount *mnt, int propagate, struct list_head *kill)
} }
change_mnt_propagation(p, MS_PRIVATE); change_mnt_propagation(p, MS_PRIVATE);
} }
list_splice(&tmp_list, kill);
} }
static void shrink_submounts(struct vfsmount *mnt, struct list_head *umounts); 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) ...@@ -1844,9 +1796,10 @@ static int do_move_mount(struct path *path, char *old_name)
return err; return err;
down_write(&namespace_sem); down_write(&namespace_sem);
while (d_mountpoint(path->dentry) && err = follow_down(path, true);
follow_down(path)) if (err < 0)
; goto out;
err = -EINVAL; err = -EINVAL;
if (!check_mnt(path->mnt) || !check_mnt(old_path.mnt)) if (!check_mnt(path->mnt) || !check_mnt(old_path.mnt))
goto out; goto out;
...@@ -1924,15 +1877,14 @@ static int do_new_mount(struct path *path, char *type, int flags, ...@@ -1924,15 +1877,14 @@ static int do_new_mount(struct path *path, char *type, int flags,
if (IS_ERR(mnt)) if (IS_ERR(mnt))
return PTR_ERR(mnt); return PTR_ERR(mnt);
return do_add_mount(mnt, path, mnt_flags, NULL); return do_add_mount(mnt, path, mnt_flags);
} }
/* /*
* add a mount into a namespace's mount tree * add a mount into a namespace's mount tree
* - provide the option of adding the new mount to an expiration list * - this unconditionally eats one of the caller's references to newmnt.
*/ */
int do_add_mount(struct vfsmount *newmnt, struct path *path, int do_add_mount(struct vfsmount *newmnt, struct path *path, int mnt_flags)
int mnt_flags, struct list_head *fslist)
{ {
int err; int err;
...@@ -1940,9 +1892,10 @@ int do_add_mount(struct vfsmount *newmnt, struct path *path, ...@@ -1940,9 +1892,10 @@ int do_add_mount(struct vfsmount *newmnt, struct path *path,
down_write(&namespace_sem); down_write(&namespace_sem);
/* Something was mounted here while we slept */ /* Something was mounted here while we slept */
while (d_mountpoint(path->dentry) && err = follow_down(path, true);
follow_down(path)) if (err < 0)
; goto unlock;
err = -EINVAL; err = -EINVAL;
if (!(mnt_flags & MNT_SHRINKABLE) && !check_mnt(path->mnt)) if (!(mnt_flags & MNT_SHRINKABLE) && !check_mnt(path->mnt))
goto unlock; goto unlock;
...@@ -1961,19 +1914,45 @@ int do_add_mount(struct vfsmount *newmnt, struct path *path, ...@@ -1961,19 +1914,45 @@ int do_add_mount(struct vfsmount *newmnt, struct path *path,
if ((err = graft_tree(newmnt, path))) if ((err = graft_tree(newmnt, path)))
goto unlock; goto unlock;
if (fslist) /* add to the specified expiration list */
list_add_tail(&newmnt->mnt_expire, fslist);
up_write(&namespace_sem); up_write(&namespace_sem);
return 0; return 0;
unlock: unlock:
up_write(&namespace_sem); up_write(&namespace_sem);
mntput_long(newmnt); mntput(newmnt);
return err; return err;
} }
EXPORT_SYMBOL_GPL(do_add_mount); /**
* mnt_set_expiry - Put a mount on an expiration list
* @mnt: The mount to list.
* @expiry_list: The list to add the mount to.
*/
void mnt_set_expiry(struct vfsmount *mnt, struct list_head *expiry_list)
{
down_write(&namespace_sem);
br_write_lock(vfsmount_lock);
list_add_tail(&mnt->mnt_expire, expiry_list);
br_write_unlock(vfsmount_lock);
up_write(&namespace_sem);
}
EXPORT_SYMBOL(mnt_set_expiry);
/*
* Remove a vfsmount from any expiration list it may be on
*/
void mnt_clear_expiry(struct vfsmount *mnt)
{
if (!list_empty(&mnt->mnt_expire)) {
down_write(&namespace_sem);
br_write_lock(vfsmount_lock);
list_del_init(&mnt->mnt_expire);
br_write_unlock(vfsmount_lock);
up_write(&namespace_sem);
}
}
/* /*
* process a list of expirable mountpoints with the intent of discarding any * process a list of expirable mountpoints with the intent of discarding any
...@@ -2262,6 +2241,20 @@ static struct mnt_namespace *alloc_mnt_ns(void) ...@@ -2262,6 +2241,20 @@ static struct mnt_namespace *alloc_mnt_ns(void)
return new_ns; 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 * Allocate a new namespace structure and populate it with contents
* copied from the namespace of the passed in task structure. * 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, ...@@ -2299,14 +2292,19 @@ static struct mnt_namespace *dup_mnt_ns(struct mnt_namespace *mnt_ns,
q = new_ns->root; q = new_ns->root;
while (p) { while (p) {
q->mnt_ns = new_ns; q->mnt_ns = new_ns;
atomic_inc(&q->mnt_longterm);
if (fs) { if (fs) {
if (p == fs->root.mnt) { if (p == fs->root.mnt) {
fs->root.mnt = mntget(q);
atomic_inc(&q->mnt_longterm);
mnt_make_shortterm(p);
rootmnt = p; rootmnt = p;
fs->root.mnt = mntget_long(q);
} }
if (p == fs->pwd.mnt) { if (p == fs->pwd.mnt) {
fs->pwd.mnt = mntget(q);
atomic_inc(&q->mnt_longterm);
mnt_make_shortterm(p);
pwdmnt = p; pwdmnt = p;
fs->pwd.mnt = mntget_long(q);
} }
} }
p = next_mnt(p, mnt_ns->root); p = next_mnt(p, mnt_ns->root);
...@@ -2315,9 +2313,9 @@ static struct mnt_namespace *dup_mnt_ns(struct mnt_namespace *mnt_ns, ...@@ -2315,9 +2313,9 @@ static struct mnt_namespace *dup_mnt_ns(struct mnt_namespace *mnt_ns,
up_write(&namespace_sem); up_write(&namespace_sem);
if (rootmnt) if (rootmnt)
mntput_long(rootmnt); mntput(rootmnt);
if (pwdmnt) if (pwdmnt)
mntput_long(pwdmnt); mntput(pwdmnt);
return new_ns; return new_ns;
} }
...@@ -2350,6 +2348,7 @@ struct mnt_namespace *create_mnt_ns(struct vfsmount *mnt) ...@@ -2350,6 +2348,7 @@ struct mnt_namespace *create_mnt_ns(struct vfsmount *mnt)
new_ns = alloc_mnt_ns(); new_ns = alloc_mnt_ns();
if (!IS_ERR(new_ns)) { if (!IS_ERR(new_ns)) {
mnt->mnt_ns = new_ns; mnt->mnt_ns = new_ns;
atomic_inc(&mnt->mnt_longterm);
new_ns->root = mnt; new_ns->root = mnt;
list_add(&new_ns->list, &new_ns->root->mnt_list); 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) ...@@ -970,7 +970,7 @@ int nfs_lookup_verify_inode(struct inode *inode, struct nameidata *nd)
{ {
struct nfs_server *server = NFS_SERVER(inode); struct nfs_server *server = NFS_SERVER(inode);
if (test_bit(NFS_INO_MOUNTPOINT, &NFS_I(inode)->flags)) if (IS_AUTOMOUNT(inode))
return 0; return 0;
if (nd != NULL) { if (nd != NULL) {
/* VFS wants an on-the-wire revalidation */ /* VFS wants an on-the-wire revalidation */
...@@ -1173,6 +1173,7 @@ const struct dentry_operations nfs_dentry_operations = { ...@@ -1173,6 +1173,7 @@ const struct dentry_operations nfs_dentry_operations = {
.d_revalidate = nfs_lookup_revalidate, .d_revalidate = nfs_lookup_revalidate,
.d_delete = nfs_dentry_delete, .d_delete = nfs_dentry_delete,
.d_iput = nfs_dentry_iput, .d_iput = nfs_dentry_iput,
.d_automount = nfs_d_automount,
}; };
static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, struct nameidata *nd) 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 = { ...@@ -1246,6 +1247,7 @@ const struct dentry_operations nfs4_dentry_operations = {
.d_revalidate = nfs_open_revalidate, .d_revalidate = nfs_open_revalidate,
.d_delete = nfs_dentry_delete, .d_delete = nfs_dentry_delete,
.d_iput = nfs_dentry_iput, .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) ...@@ -300,7 +300,7 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
else else
inode->i_op = &nfs_mountpoint_inode_operations; inode->i_op = &nfs_mountpoint_inode_operations;
inode->i_fop = NULL; inode->i_fop = NULL;
set_bit(NFS_INO_MOUNTPOINT, &nfsi->flags); inode->i_flags |= S_AUTOMOUNT;
} }
} else if (S_ISLNK(inode->i_mode)) } else if (S_ISLNK(inode->i_mode))
inode->i_op = &nfs_symlink_inode_operations; inode->i_op = &nfs_symlink_inode_operations;
...@@ -1208,7 +1208,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr) ...@@ -1208,7 +1208,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
/* Update the fsid? */ /* Update the fsid? */
if (S_ISDIR(inode->i_mode) && (fattr->valid & NFS_ATTR_FATTR_FSID) && if (S_ISDIR(inode->i_mode) && (fattr->valid & NFS_ATTR_FATTR_FSID) &&
!nfs_fsid_equal(&server->fsid, &fattr->fsid) && !nfs_fsid_equal(&server->fsid, &fattr->fsid) &&
!test_bit(NFS_INO_MOUNTPOINT, &nfsi->flags)) !IS_AUTOMOUNT(inode))
server->fsid = fattr->fsid; server->fsid = fattr->fsid;
/* /*
......
...@@ -252,6 +252,7 @@ extern char *nfs_path(const char *base, ...@@ -252,6 +252,7 @@ extern char *nfs_path(const char *base,
const struct dentry *droot, const struct dentry *droot,
const struct dentry *dentry, const struct dentry *dentry,
char *buffer, ssize_t buflen); char *buffer, ssize_t buflen);
extern struct vfsmount *nfs_d_automount(struct path *path);
/* getroot.c */ /* getroot.c */
extern struct dentry *nfs_get_root(struct super_block *, struct nfs_fh *); extern struct dentry *nfs_get_root(struct super_block *, struct nfs_fh *);
......
...@@ -97,9 +97,8 @@ char *nfs_path(const char *base, ...@@ -97,9 +97,8 @@ char *nfs_path(const char *base,
} }
/* /*
* nfs_follow_mountpoint - handle crossing a mountpoint on the server * nfs_d_automount - Handle crossing a mountpoint on the server
* @dentry - dentry of mountpoint * @path - The mountpoint
* @nd - nameidata info
* *
* When we encounter a mountpoint on the server, we want to set up * When we encounter a mountpoint on the server, we want to set up
* a mountpoint on the client too, to prevent inode numbers from * a mountpoint on the client too, to prevent inode numbers from
...@@ -109,87 +108,65 @@ char *nfs_path(const char *base, ...@@ -109,87 +108,65 @@ char *nfs_path(const char *base,
* situation, and that different filesystems may want to use * situation, and that different filesystems may want to use
* different security flavours. * 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 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 dentry *parent;
struct nfs_fh *fh = NULL; struct nfs_fh *fh = NULL;
struct nfs_fattr *fattr = NULL; struct nfs_fattr *fattr = NULL;
int err; int err;
dprintk("--> nfs_follow_mountpoint()\n"); dprintk("--> nfs_d_automount()\n");
err = -ESTALE; mnt = ERR_PTR(-ESTALE);
if (IS_ROOT(dentry)) if (IS_ROOT(path->dentry))
goto out_err; goto out_nofree;
err = -ENOMEM; mnt = ERR_PTR(-ENOMEM);
fh = nfs_alloc_fhandle(); fh = nfs_alloc_fhandle();
fattr = nfs_alloc_fattr(); fattr = nfs_alloc_fattr();
if (fh == NULL || fattr == NULL) if (fh == NULL || fattr == NULL)
goto out_err; goto out;
dprintk("%s: enter\n", __func__); dprintk("%s: enter\n", __func__);
dput(nd->path.dentry);
nd->path.dentry = dget(dentry);
/* Look it up again */ /* Look it up again to get its attributes */
parent = dget_parent(nd->path.dentry); parent = dget_parent(path->dentry);
err = server->nfs_client->rpc_ops->lookup(parent->d_inode, err = server->nfs_client->rpc_ops->lookup(parent->d_inode,
&nd->path.dentry->d_name, &path->dentry->d_name,
fh, fattr); fh, fattr);
dput(parent); dput(parent);
if (err != 0) if (err != 0) {
goto out_err; mnt = ERR_PTR(err);
goto out;
}
if (fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL) 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 else
mnt = nfs_do_submount(nd->path.mnt, nd->path.dentry, fh, mnt = nfs_do_submount(path->mnt, path->dentry, fh, fattr);
fattr);
err = PTR_ERR(mnt);
if (IS_ERR(mnt)) if (IS_ERR(mnt))
goto out_err; goto out;
mntget(mnt); dprintk("%s: done, success\n", __func__);
err = do_add_mount(mnt, &nd->path, nd->path.mnt->mnt_flags|MNT_SHRINKABLE, mntget(mnt); /* prevent immediate expiration */
&nfs_automount_list); mnt_set_expiry(mnt, &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);
schedule_delayed_work(&nfs_automount_task, nfs_mountpoint_expiry_timeout); schedule_delayed_work(&nfs_automount_task, nfs_mountpoint_expiry_timeout);
out: out:
nfs_free_fattr(fattr); nfs_free_fattr(fattr);
nfs_free_fhandle(fh); nfs_free_fhandle(fh);
dprintk("%s: done, returned %d\n", __func__, err); out_nofree:
dprintk("<-- nfs_follow_mountpoint() = %p\n", mnt);
dprintk("<-- nfs_follow_mountpoint() = %d\n", err); return mnt;
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;
} }
const struct inode_operations nfs_mountpoint_inode_operations = { const struct inode_operations nfs_mountpoint_inode_operations = {
.follow_link = nfs_follow_mountpoint,
.getattr = nfs_getattr, .getattr = nfs_getattr,
}; };
const struct inode_operations nfs_referral_inode_operations = { const struct inode_operations nfs_referral_inode_operations = {
.follow_link = nfs_follow_mountpoint,
}; };
static void nfs_expire_automounts(struct work_struct *work) static void nfs_expire_automounts(struct work_struct *work)
......
...@@ -87,8 +87,9 @@ nfsd_cross_mnt(struct svc_rqst *rqstp, struct dentry **dpp, ...@@ -87,8 +87,9 @@ nfsd_cross_mnt(struct svc_rqst *rqstp, struct dentry **dpp,
.dentry = dget(dentry)}; .dentry = dget(dentry)};
int err = 0; 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); exp2 = rqst_exp_get_by_name(rqstp, &path);
if (IS_ERR(exp2)) { if (IS_ERR(exp2)) {
......
...@@ -1292,7 +1292,7 @@ static int __init init_pipe_fs(void) ...@@ -1292,7 +1292,7 @@ static int __init init_pipe_fs(void)
static void __exit exit_pipe_fs(void) static void __exit exit_pipe_fs(void)
{ {
unregister_filesystem(&pipe_fs_type); unregister_filesystem(&pipe_fs_type);
mntput_long(pipe_mnt); mntput(pipe_mnt);
} }
fs_initcall(init_pipe_fs); fs_initcall(init_pipe_fs);
......
...@@ -75,11 +75,13 @@ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat, ...@@ -75,11 +75,13 @@ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
int error = -EINVAL; int error = -EINVAL;
int lookup_flags = 0; int lookup_flags = 0;
if ((flag & ~AT_SYMLINK_NOFOLLOW) != 0) if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT)) != 0)
goto out; goto out;
if (!(flag & AT_SYMLINK_NOFOLLOW)) if (!(flag & AT_SYMLINK_NOFOLLOW))
lookup_flags |= LOOKUP_FOLLOW; lookup_flags |= LOOKUP_FOLLOW;
if (flag & AT_NO_AUTOMOUNT)
lookup_flags |= LOOKUP_NO_AUTOMOUNT;
error = user_path_at(dfd, filename, lookup_flags, &path); error = user_path_at(dfd, filename, lookup_flags, &path);
if (error) if (error)
......
...@@ -1141,7 +1141,7 @@ static struct vfsmount *fs_set_subtype(struct vfsmount *mnt, const char *fstype) ...@@ -1141,7 +1141,7 @@ static struct vfsmount *fs_set_subtype(struct vfsmount *mnt, const char *fstype)
return mnt; return mnt;
err: err:
mntput_long(mnt); mntput(mnt);
return ERR_PTR(err); return ERR_PTR(err);
} }
......
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
#define AUTOFS_MIN_PROTO_VERSION 3 #define AUTOFS_MIN_PROTO_VERSION 3
#define AUTOFS_MAX_PROTO_VERSION 5 #define AUTOFS_MAX_PROTO_VERSION 5
#define AUTOFS_PROTO_SUBVERSION 1 #define AUTOFS_PROTO_SUBVERSION 2
/* Mask for expire behaviour */ /* Mask for expire behaviour */
#define AUTOFS_EXP_IMMEDIATE 1 #define AUTOFS_EXP_IMMEDIATE 1
......
...@@ -167,6 +167,8 @@ struct dentry_operations { ...@@ -167,6 +167,8 @@ struct dentry_operations {
void (*d_release)(struct dentry *); void (*d_release)(struct dentry *);
void (*d_iput)(struct dentry *, struct inode *); void (*d_iput)(struct dentry *, struct inode *);
char *(*d_dname)(struct dentry *, char *, int); char *(*d_dname)(struct dentry *, char *, int);
struct vfsmount *(*d_automount)(struct path *);
int (*d_manage)(struct dentry *, bool, bool);
} ____cacheline_aligned; } ____cacheline_aligned;
/* /*
...@@ -205,13 +207,18 @@ struct dentry_operations { ...@@ -205,13 +207,18 @@ struct dentry_operations {
#define DCACHE_CANT_MOUNT 0x0100 #define DCACHE_CANT_MOUNT 0x0100
#define DCACHE_GENOCIDE 0x0200 #define DCACHE_GENOCIDE 0x0200
#define DCACHE_MOUNTED 0x0400 /* is a mountpoint */
#define DCACHE_OP_HASH 0x1000 #define DCACHE_OP_HASH 0x1000
#define DCACHE_OP_COMPARE 0x2000 #define DCACHE_OP_COMPARE 0x2000
#define DCACHE_OP_REVALIDATE 0x4000 #define DCACHE_OP_REVALIDATE 0x4000
#define DCACHE_OP_DELETE 0x8000 #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; extern seqlock_t rename_lock;
static inline int dname_external(struct dentry *dentry) static inline int dname_external(struct dentry *dentry)
...@@ -399,7 +406,12 @@ static inline void dont_mount(struct dentry *dentry) ...@@ -399,7 +406,12 @@ static inline void dont_mount(struct dentry *dentry)
extern void dput(struct 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; return dentry->d_flags & DCACHE_MOUNTED;
} }
......
...@@ -45,6 +45,7 @@ ...@@ -45,6 +45,7 @@
#define AT_REMOVEDIR 0x200 /* Remove directory instead of #define AT_REMOVEDIR 0x200 /* Remove directory instead of
unlinking file. */ unlinking file. */
#define AT_SYMLINK_FOLLOW 0x400 /* Follow symbolic links. */ #define AT_SYMLINK_FOLLOW 0x400 /* Follow symbolic links. */
#define AT_NO_AUTOMOUNT 0x800 /* Suppress terminal automount traversal */
#ifdef __KERNEL__ #ifdef __KERNEL__
......
...@@ -242,6 +242,7 @@ struct inodes_stat_t { ...@@ -242,6 +242,7 @@ struct inodes_stat_t {
#define S_SWAPFILE 256 /* Do not truncate: swapon got its bmaps */ #define S_SWAPFILE 256 /* Do not truncate: swapon got its bmaps */
#define S_PRIVATE 512 /* Inode is fs-internal */ #define S_PRIVATE 512 /* Inode is fs-internal */
#define S_IMA 1024 /* Inode has an associated IMA struct */ #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 * Note that nosuid etc flags are inode-specific: setting some file-system
...@@ -277,6 +278,7 @@ struct inodes_stat_t { ...@@ -277,6 +278,7 @@ struct inodes_stat_t {
#define IS_SWAPFILE(inode) ((inode)->i_flags & S_SWAPFILE) #define IS_SWAPFILE(inode) ((inode)->i_flags & S_SWAPFILE)
#define IS_PRIVATE(inode) ((inode)->i_flags & S_PRIVATE) #define IS_PRIVATE(inode) ((inode)->i_flags & S_PRIVATE)
#define IS_IMA(inode) ((inode)->i_flags & S_IMA) #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 /* 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. */ probably as bad and I don't want to create yet another include file. */
......
...@@ -60,7 +60,7 @@ struct vfsmount { ...@@ -60,7 +60,7 @@ struct vfsmount {
struct super_block *mnt_sb; /* pointer to superblock */ struct super_block *mnt_sb; /* pointer to superblock */
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
struct mnt_pcp __percpu *mnt_pcp; struct mnt_pcp __percpu *mnt_pcp;
atomic_t mnt_longrefs; atomic_t mnt_longterm; /* how many of the refs are longterm */
#else #else
int mnt_count; int mnt_count;
int mnt_writers; int mnt_writers;
...@@ -96,8 +96,6 @@ extern int mnt_clone_write(struct vfsmount *mnt); ...@@ -96,8 +96,6 @@ extern int mnt_clone_write(struct vfsmount *mnt);
extern void mnt_drop_write(struct vfsmount *mnt); extern void mnt_drop_write(struct vfsmount *mnt);
extern void mntput(struct vfsmount *mnt); extern void mntput(struct vfsmount *mnt);
extern struct vfsmount *mntget(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_pin(struct vfsmount *mnt);
extern void mnt_unpin(struct vfsmount *mnt); extern void mnt_unpin(struct vfsmount *mnt);
extern int __mnt_is_readonly(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, ...@@ -110,12 +108,7 @@ extern struct vfsmount *vfs_kern_mount(struct file_system_type *type,
int flags, const char *name, int flags, const char *name,
void *data); void *data);
struct nameidata; extern void mnt_set_expiry(struct vfsmount *mnt, struct list_head *expiry_list);
struct path;
extern int do_add_mount(struct vfsmount *newmnt, struct path *path,
int mnt_flags, struct list_head *fslist);
extern void mark_mounts_for_expiry(struct list_head *mounts); extern void mark_mounts_for_expiry(struct list_head *mounts);
extern dev_t name_to_dev_t(char *name); extern dev_t name_to_dev_t(char *name);
......
...@@ -45,6 +45,7 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT, LAST_BIND}; ...@@ -45,6 +45,7 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT, LAST_BIND};
* - ending slashes ok even for nonexistent files * - ending slashes ok even for nonexistent files
* - internal "there are more path components" flag * - internal "there are more path components" flag
* - dentry cache is untrusted; force a real lookup * - dentry cache is untrusted; force a real lookup
* - suppress terminal automount
*/ */
#define LOOKUP_FOLLOW 0x0001 #define LOOKUP_FOLLOW 0x0001
#define LOOKUP_DIRECTORY 0x0002 #define LOOKUP_DIRECTORY 0x0002
...@@ -53,6 +54,7 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT, LAST_BIND}; ...@@ -53,6 +54,7 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT, LAST_BIND};
#define LOOKUP_PARENT 0x0010 #define LOOKUP_PARENT 0x0010
#define LOOKUP_REVAL 0x0020 #define LOOKUP_REVAL 0x0020
#define LOOKUP_RCU 0x0040 #define LOOKUP_RCU 0x0040
#define LOOKUP_NO_AUTOMOUNT 0x0080
/* /*
* Intent data * Intent data
*/ */
...@@ -79,7 +81,8 @@ extern struct file *lookup_instantiate_filp(struct nameidata *nd, struct dentry ...@@ -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 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 int follow_up(struct path *);
extern struct dentry *lock_rename(struct dentry *, struct dentry *); extern struct dentry *lock_rename(struct dentry *, struct dentry *);
......
...@@ -215,7 +215,6 @@ struct nfs_inode { ...@@ -215,7 +215,6 @@ struct nfs_inode {
#define NFS_INO_ADVISE_RDPLUS (0) /* advise readdirplus */ #define NFS_INO_ADVISE_RDPLUS (0) /* advise readdirplus */
#define NFS_INO_STALE (1) /* possible stale inode */ #define NFS_INO_STALE (1) /* possible stale inode */
#define NFS_INO_ACL_LRU_SET (2) /* Inode is on the LRU list */ #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_FLUSHING (4) /* inode is flushing out data */
#define NFS_INO_FSCACHE (5) /* inode can be cached by FS-Cache */ #define NFS_INO_FSCACHE (5) /* inode can be cached by FS-Cache */
#define NFS_INO_FSCACHE_LOCK (6) /* FS-Cache cookie management lock */ #define NFS_INO_FSCACHE_LOCK (6) /* FS-Cache cookie management lock */
......
...@@ -10,9 +10,7 @@ struct path { ...@@ -10,9 +10,7 @@ struct path {
}; };
extern void path_get(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(struct path *);
extern void path_put_long(struct path *);
static inline int path_equal(const struct path *path1, const struct path *path2) static inline int path_equal(const struct path *path1, const struct path *path2)
{ {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment