You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07: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:
@@ -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
|
||||
|
||||
+2
-1
@@ -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);
|
||||
|
||||
|
||||
+13
-43
@@ -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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
+1
-1
@@ -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:
|
||||
|
||||
+75
-24
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+29
-22
@@ -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);
|
||||
|
||||
+5
-21
@@ -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;
|
||||
|
||||
+283
-392
File diff suppressed because it is too large
Load Diff
+16
-1
@@ -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;
|
||||
|
||||
+53
-67
@@ -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,
|
||||
};
|
||||
|
||||
+5
-3
@@ -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
|
||||
|
||||
+4
-1
@@ -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);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user