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:
Linus Torvalds
2011-01-16 11:31:50 -08:00
40 changed files with 1012 additions and 857 deletions
+4
View File
@@ -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:
+43
View File
@@ -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)
+1 -1
View File
@@ -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");
}
+2 -3
View File
@@ -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);
+1
View File
@@ -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
View File
@@ -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;
+1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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);
+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+16 -1
View File
@@ -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
View File
@@ -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,
};
+6
View File
@@ -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,
+2
View File
@@ -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
View File
@@ -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
View File
@@ -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