From: Ian Kent a. Implement readdir and friends for directory lookup for late mounting. This is done largely by replacing a catch all condition in try_to_fill_dentry with appropriate cases. b. Add path calc. function in waitq.c to get extended path to return to daemon (for direct mounts). c. Add revalidate calls to sys_chdir and sys_chroot so that pwd lookups work correctly. d. Add ioctl to retrieve minor version for automount daemon (and me) to recognise module fix level. Bumped minor version to 5. --- 25-akpm/fs/autofs4/autofs_i.h | 10 + 25-akpm/fs/autofs4/expire.c | 2 25-akpm/fs/autofs4/inode.c | 2 25-akpm/fs/autofs4/root.c | 320 +++++++++++++++++++++++++++++++++------ 25-akpm/fs/autofs4/waitq.c | 70 ++++++-- 25-akpm/fs/open.c | 46 +++++ 25-akpm/include/linux/auto_fs4.h | 3 7 files changed, 389 insertions(+), 64 deletions(-) diff -puN fs/autofs4/autofs_i.h~5-autofs4-2.6.0-readdir-20040405 fs/autofs4/autofs_i.h --- 25/fs/autofs4/autofs_i.h~5-autofs4-2.6.0-readdir-20040405 Tue Apr 6 15:50:40 2004 +++ 25-akpm/fs/autofs4/autofs_i.h Tue Apr 6 15:50:40 2004 @@ -95,6 +95,7 @@ struct autofs_sb_info { pid_t oz_pgrp; int catatonic; int version; + int sub_version; unsigned long exp_timeout; struct super_block *sb; struct autofs_wait_queue *queues; /* Wait queue pointer */ @@ -127,6 +128,12 @@ static inline int autofs4_ispending(stru (inf != NULL && inf->flags & AUTOFS_INF_EXPIRING); } +static inline void autofs4_copy_atime(struct file *src, struct file *dst) +{ + dst->f_dentry->d_inode->i_atime = src->f_dentry->d_inode->i_atime; + return; +} + struct inode *autofs4_get_inode(struct super_block *, struct autofs_info *); struct autofs_info *autofs4_init_inf(struct autofs_sb_info *, mode_t mode); void autofs4_free_ino(struct autofs_info *); @@ -143,6 +150,7 @@ int autofs4_expire_multi(struct super_bl extern struct inode_operations autofs4_symlink_inode_operations; extern struct inode_operations autofs4_dir_inode_operations; extern struct inode_operations autofs4_root_inode_operations; +extern struct file_operations autofs4_dir_operations; extern struct file_operations autofs4_root_operations; /* Initializing function */ @@ -159,7 +167,7 @@ enum autofs_notify NFY_EXPIRE }; -int autofs4_wait(struct autofs_sb_info *,struct qstr *, enum autofs_notify); +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 *); diff -puN fs/autofs4/expire.c~5-autofs4-2.6.0-readdir-20040405 fs/autofs4/expire.c --- 25/fs/autofs4/expire.c~5-autofs4-2.6.0-readdir-20040405 Tue Apr 6 15:50:40 2004 +++ 25-akpm/fs/autofs4/expire.c Tue Apr 6 15:50:40 2004 @@ -390,7 +390,7 @@ int autofs4_expire_multi(struct super_bl /* This is synchronous because it makes the daemon a little easier */ de_info->flags |= AUTOFS_INF_EXPIRING; - ret = autofs4_wait(sbi, &dentry->d_name, NFY_EXPIRE); + ret = autofs4_wait(sbi, dentry, NFY_EXPIRE); de_info->flags &= ~AUTOFS_INF_EXPIRING; dput(dentry); } diff -puN fs/autofs4/inode.c~5-autofs4-2.6.0-readdir-20040405 fs/autofs4/inode.c --- 25/fs/autofs4/inode.c~5-autofs4-2.6.0-readdir-20040405 Tue Apr 6 15:50:40 2004 +++ 25-akpm/fs/autofs4/inode.c Tue Apr 6 15:50:40 2004 @@ -310,7 +310,7 @@ struct inode *autofs4_get_inode(struct s if (S_ISDIR(inf->mode)) { inode->i_nlink = 2; inode->i_op = &autofs4_dir_inode_operations; - inode->i_fop = &simple_dir_operations; + inode->i_fop = &autofs4_dir_operations; } else if (S_ISLNK(inf->mode)) { inode->i_size = inf->size; inode->i_op = &autofs4_symlink_inode_operations; diff -puN fs/autofs4/root.c~5-autofs4-2.6.0-readdir-20040405 fs/autofs4/root.c --- 25/fs/autofs4/root.c~5-autofs4-2.6.0-readdir-20040405 Tue Apr 6 15:50:40 2004 +++ 25-akpm/fs/autofs4/root.c Tue Apr 6 15:50:40 2004 @@ -4,6 +4,7 @@ * * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved * Copyright 1999-2000 Jeremy Fitzhardinge + * Copyright 2001-2003 Ian Kent * * This file is part of the Linux kernel and is made available under * the terms of the GNU General Public License, version 2, or at your @@ -24,17 +25,26 @@ static int autofs4_dir_unlink(struct ino static int autofs4_dir_rmdir(struct inode *,struct dentry *); static int autofs4_dir_mkdir(struct inode *,struct dentry *,int); static int autofs4_root_ioctl(struct inode *, struct file *,unsigned int,unsigned long); +static int autofs4_dir_open(struct inode *inode, struct file *file); +static int autofs4_dir_close(struct inode *inode, struct file *file); +static int autofs4_dir_readdir(struct file * filp, void * dirent, filldir_t filldir); static struct dentry *autofs4_root_lookup(struct inode *,struct dentry *, struct nameidata *); struct file_operations autofs4_root_operations = { .open = dcache_dir_open, .release = dcache_dir_close, - .llseek = dcache_dir_lseek, .read = generic_read_dir, .readdir = dcache_readdir, .ioctl = autofs4_root_ioctl, }; +struct file_operations autofs4_dir_operations = { + .open = autofs4_dir_open, + .release = autofs4_dir_close, + .read = generic_read_dir, + .readdir = autofs4_dir_readdir, +}; + struct inode_operations autofs4_root_inode_operations = { .lookup = autofs4_root_lookup, .unlink = autofs4_dir_unlink, @@ -67,9 +77,210 @@ static void autofs4_update_usage(struct } } +static void autofs4_check_pwd(struct file *file, struct file *fp) +{ + struct dentry *pwd = file->f_dentry; + struct dentry *new_pwd = fp->f_dentry; + struct vfsmount *new_mnt = fp->f_vfsmnt; + + /* dentry is a pwd of mountpoint so move to it */ + if (current->fs->pwd == pwd) + set_fs_pwd(current->fs, new_mnt, new_pwd); + + /* dentry is root of a chrooted mountpoint so move to it */ + if (current->fs->root == pwd) { + set_fs_root(current->fs, new_mnt, new_pwd); + /* alternate os ABI not supported */ + /* set_fs_altroot(); */ + } +} + +/* + * From 2.4 kernel readdir.c + */ +static int autofs4_dcache_readdir(struct file * filp, void * dirent, filldir_t filldir) +{ + int i; + struct dentry *dentry = filp->f_dentry; + + i = filp->f_pos; + switch (i) { + case 0: + if (filldir(dirent, ".", 1, i, dentry->d_inode->i_ino, DT_DIR) < 0) + break; + i++; + filp->f_pos++; + /* fallthrough */ + case 1: + if (filldir(dirent, "..", 2, i, dentry->d_parent->d_inode->i_ino, DT_DIR) < 0) + break; + i++; + filp->f_pos++; + /* fallthrough */ + default: { + struct list_head *list; + int j = i-2; + + spin_lock(&dcache_lock); + list = dentry->d_subdirs.next; + + for (;;) { + if (list == &dentry->d_subdirs) { + spin_unlock(&dcache_lock); + return 0; + } + if (!j) + break; + j--; + list = list->next; + } + + while(1) { + struct dentry *de = list_entry(list, struct dentry, d_child); + + if (!d_unhashed(de) && de->d_inode) { + spin_unlock(&dcache_lock); + if (filldir(dirent, de->d_name.name, de->d_name.len, filp->f_pos, de->d_inode->i_ino, DT_UNKNOWN) < 0) + break; + spin_lock(&dcache_lock); + } + filp->f_pos++; + list = list->next; + if (list != &dentry->d_subdirs) + continue; + spin_unlock(&dcache_lock); + break; + } + } + } + return 0; +} + +static int autofs4_dir_open(struct inode *inode, struct file *file) +{ + struct dentry *dentry = file->f_dentry; + struct vfsmount *mnt = file->f_vfsmnt; + struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); + int status; + + DPRINTK(("autofs4_dir_open: file=%p dentry=%p %.*s\n", + file, dentry, dentry->d_name.len, dentry->d_name.name)); + + if (autofs4_oz_mode(sbi)) + goto out; + + if (autofs4_ispending(dentry)) { + DPRINTK(("autofs4_dir_open: dentry busy\n")); + return -EBUSY; + } + + if (!d_mountpoint(dentry) && dentry->d_op && dentry->d_op->d_revalidate) { + struct nameidata nd; + int empty; + + /* In case there are stale directory dentrys from a failed mount */ + spin_lock(&dcache_lock); + empty = list_empty(&dentry->d_subdirs); + spin_unlock(&dcache_lock); + + if (!empty) + d_invalidate(dentry); + + nd.flags = LOOKUP_CONTINUE; + status = (dentry->d_op->d_revalidate)(dentry, &nd); + + if (!status) + return -ENOENT; + } + + if (d_mountpoint(dentry)) { + struct file *fp = NULL; + struct vfsmount *fp_mnt = mntget(mnt); + struct dentry *fp_dentry = dget(dentry); + + while (follow_down(&fp_mnt, &fp_dentry) && d_mountpoint(fp_dentry)); + + fp = dentry_open(fp_dentry, fp_mnt, file->f_flags); + status = PTR_ERR(fp); + if (IS_ERR(fp)) { + file->private_data = NULL; + return status; + } + autofs4_check_pwd(file, fp); + file->private_data = fp; + } +out: + return 0; +} + +static int autofs4_dir_close(struct inode *inode, struct file *file) +{ + struct dentry *dentry = file->f_dentry; + struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); + + DPRINTK(("autofs4_dir_close: file=%p dentry=%p %.*s\n", + file, dentry, dentry->d_name.len, dentry->d_name.name)); + + if (autofs4_oz_mode(sbi)) + goto out; + + if (autofs4_ispending(dentry)) { + DPRINTK(("autofs4_dir_close: dentry busy\n")); + return -EBUSY; + } + + if (d_mountpoint(dentry)) { + struct file *fp = file->private_data; + + if (!fp) + return -ENOENT; + + filp_close(fp, current->files); + file->private_data = NULL; + } +out: + return 0; +} + +static int autofs4_dir_readdir(struct file *file, void *dirent, filldir_t filldir) +{ + struct dentry *dentry = file->f_dentry; + struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); + int status; + + DPRINTK(("autofs4_readdir: file=%p dentry=%p %.*s\n", + file, dentry, dentry->d_name.len, dentry->d_name.name)); + + if (autofs4_oz_mode(sbi)) + goto out; + + if (autofs4_ispending(dentry)) { + DPRINTK(("autofs4_readdir: dentry busy\n")); + return -EBUSY; + } + + if (d_mountpoint(dentry)) { + struct file *fp = file->private_data; + + if (!fp) + return -ENOENT; + + if (!fp->f_op || !fp->f_op->readdir) + goto out; + + status = vfs_readdir(fp, filldir, dirent); + file->f_pos = fp->f_pos; + if (status) + autofs4_copy_atime(file, fp); + return status; + } +out: + return autofs4_dcache_readdir(file, dirent, filldir); +} + static int try_to_fill_dentry(struct dentry *dentry, struct super_block *sb, - struct autofs_sb_info *sbi) + struct autofs_sb_info *sbi, int flags) { struct autofs_info *de_info = autofs4_dentry_ino(dentry); int status = 0; @@ -81,7 +292,7 @@ static int try_to_fill_dentry(struct den DPRINTK(("try_to_fill_entry: waiting for expire %p name=%.*s\n", dentry, dentry->d_name.len, dentry->d_name.name)); - status = autofs4_wait(sbi, &dentry->d_name, NFY_NONE); + status = autofs4_wait(sbi, dentry, NFY_NONE); DPRINTK(("try_to_fill_entry: expire done status=%d\n", status)); @@ -92,11 +303,11 @@ static int try_to_fill_dentry(struct den dentry, dentry->d_name.len, dentry->d_name.name, dentry->d_inode)); /* Wait for a pending mount, triggering one if there isn't one already */ - while(dentry->d_inode == NULL) { - DPRINTK(("try_to_fill_entry: waiting for mount name=%.*s, de_info=%p de_info->flags=%x\n", - dentry->d_name.len, dentry->d_name.name, - de_info, de_info?de_info->flags:0)); - status = autofs4_wait(sbi, &dentry->d_name, NFY_MOUNT); + if (dentry->d_inode == NULL) { + DPRINTK(("try_to_fill_entry: waiting for mount name=%.*s\n", + dentry->d_name.len, dentry->d_name.name)); + + status = autofs4_wait(sbi, dentry, NFY_MOUNT); DPRINTK(("try_to_fill_entry: mount done status=%d\n", status)); @@ -112,19 +323,46 @@ static int try_to_fill_dentry(struct den /* Return a negative dentry, but leave it "pending" */ return 1; } - } + /* Trigger mount for path component or follow link */ + } else if (flags & LOOKUP_CONTINUE || current->link_count) { + DPRINTK(("try_to_fill_entry: waiting for mount name=%.*s\n", + dentry->d_name.len, dentry->d_name.name)); - /* If this is an unused directory that isn't a mount point, - bitch at the daemon and fix it in user space */ - spin_lock(&dcache_lock); - if (S_ISDIR(dentry->d_inode->i_mode) && - !d_mountpoint(dentry) && - list_empty(&dentry->d_subdirs)) { - DPRINTK(("try_to_fill_entry: mounting existing dir\n")); - spin_unlock(&dcache_lock); - return autofs4_wait(sbi, &dentry->d_name, NFY_MOUNT) == 0; + dentry->d_flags |= DCACHE_AUTOFS_PENDING; + status = autofs4_wait(sbi, dentry, NFY_MOUNT); + + DPRINTK(("try_to_fill_entry: mount done status=%d\n", status)); + + if (status) { + dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; + return 0; + } + /* also for chdir or chroot so subsequent path walks work properly */ + } else if (dentry == current->fs->pwd || dentry == current->fs->root) { + struct vfsmount *mnt; + + DPRINTK(("try_to_fill_entry: waiting for mount name=%.*s\n", + dentry->d_name.len, dentry->d_name.name)); + + dentry->d_flags |= DCACHE_AUTOFS_PENDING; + status = autofs4_wait(sbi, dentry, NFY_MOUNT); + + DPRINTK(("try_to_fill_entry: mount done status=%d\n", status)); + + if ( status ) { + dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; + return 0; + } + + if (dentry == current->fs->pwd) { + mnt = lookup_mnt(current->fs->pwdmnt, dentry); + set_fs_pwd(current->fs, mnt, mnt->mnt_root); + } else { + mnt = lookup_mnt(current->fs->rootmnt, dentry); + set_fs_root(current->fs, mnt, mnt->mnt_root); + } + mntput(mnt); } - spin_unlock(&dcache_lock); /* We don't update the usages for the autofs daemon itself, this is necessary for recursive autofs mounts */ @@ -135,25 +373,25 @@ static int try_to_fill_dentry(struct den return 1; } - /* * Revalidate is called on every cache lookup. Some of those * cache lookups may actually happen while the dentry is not * yet completely filled in, and revalidate has to delay such * lookups.. */ -static int autofs4_root_revalidate(struct dentry * dentry, struct nameidata *nd) +static int autofs4_revalidate(struct dentry * dentry, struct nameidata *nd) { struct inode * dir = dentry->d_parent->d_inode; struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); int oz_mode = autofs4_oz_mode(sbi); + int flags = nd ? nd->flags : 0; + int status = 1; /* Pending dentry */ if (autofs4_ispending(dentry)) { - if (autofs4_oz_mode(sbi)) - return 1; - else - return try_to_fill_dentry(dentry, dir->i_sb, sbi); + if (!oz_mode) + status = try_to_fill_dentry(dentry, dir->i_sb, sbi, flags); + return status; } /* Negative dentry.. invalidate if "old" */ @@ -168,10 +406,9 @@ static int autofs4_root_revalidate(struc DPRINTK(("autofs4_root_revalidate: dentry=%p %.*s, emptydir\n", dentry, dentry->d_name.len, dentry->d_name.name)); spin_unlock(&dcache_lock); - if (oz_mode) - return 1; - else - return try_to_fill_dentry(dentry, dir->i_sb, sbi); + if (!oz_mode) + status = try_to_fill_dentry(dentry, dir->i_sb, sbi, flags); + return status; } spin_unlock(&dcache_lock); @@ -182,16 +419,6 @@ static int autofs4_root_revalidate(struc return 1; } -static int autofs4_revalidate(struct dentry *dentry, struct nameidata *nd) -{ - struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); - - if (!autofs4_oz_mode(sbi)) - autofs4_update_usage(dentry); - - return 1; -} - static void autofs4_dentry_release(struct dentry *de) { struct autofs_info *inf; @@ -211,7 +438,7 @@ static void autofs4_dentry_release(struc /* For dentries of directories in the root dir */ static struct dentry_operations autofs4_root_dentry_operations = { - .d_revalidate = autofs4_root_revalidate, + .d_revalidate = autofs4_revalidate, .d_release = autofs4_dentry_release, }; @@ -223,11 +450,10 @@ static struct dentry_operations autofs4_ /* Lookups in non-root dirs never find anything - if it's there, it's already in the dcache */ -/* SMP-safe */ static struct dentry *autofs4_dir_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) { #if 0 - DPRINTK(("autofs_dir_lookup: ignoring lookup of %.*s/%.*s\n", + DPRINTK(("autofs4_dir_lookup: iignoring lookup of %.*s/%.*s\n", dentry->d_parent->d_name.len, dentry->d_parent->d_name.name, dentry->d_name.len, dentry->d_name.name)); #endif @@ -415,8 +641,6 @@ static int autofs4_dir_rmdir(struct inod return 0; } - - static int autofs4_dir_mkdir(struct inode *dir, struct dentry *dentry, int mode) { struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); @@ -475,6 +699,12 @@ static inline int autofs4_get_protover(s return put_user(sbi->version, p); } +/* Return protocol sub version */ +static inline int autofs4_get_protosubver(struct autofs_sb_info *sbi, int *p) +{ + return put_user(sbi->sub_version, p); +} + /* Identify autofs4_dentries - this is so we can tell if there's an extra dentry refcount or not. We only hold a refcount on the dentry if its non-negative (ie, d_inode != NULL) @@ -516,6 +746,8 @@ static int autofs4_root_ioctl(struct ino return 0; case AUTOFS_IOC_PROTOVER: /* Get protocol version */ return autofs4_get_protover(sbi, (int *)arg); + case AUTOFS_IOC_PROTOSUBVER: /* Get protocol sub version */ + return autofs4_get_protosubver(sbi, (int *)arg); case AUTOFS_IOC_SETTIMEOUT: return autofs4_get_set_timeout(sbi,(unsigned long *)arg); diff -puN fs/autofs4/waitq.c~5-autofs4-2.6.0-readdir-20040405 fs/autofs4/waitq.c --- 25/fs/autofs4/waitq.c~5-autofs4-2.6.0-readdir-20040405 Tue Apr 6 15:50:40 2004 +++ 25-akpm/fs/autofs4/waitq.c Tue Apr 6 15:50:40 2004 @@ -3,6 +3,7 @@ * linux/fs/autofs/waitq.c * * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved + * Copyright 2001-2003 Ian Kent * * This file is part of the Linux kernel and is made available under * the terms of the GNU General Public License, version 2, or at your @@ -126,25 +127,64 @@ static void autofs4_notify_daemon(struct autofs4_catatonic_mode(sbi); } -int autofs4_wait(struct autofs_sb_info *sbi, struct qstr *name, +static int autofs4_getpath(struct autofs_sb_info *sbi, + struct dentry *dentry, char **name) +{ + struct dentry *root = sbi->sb->s_root; + struct dentry *tmp; + char *buf = *name; + char *p; + int len = 0; + + spin_lock(&dcache_lock); + for (tmp = dentry ; tmp != root ; tmp = tmp->d_parent) + len += tmp->d_name.len + 1; + + if (--len > NAME_MAX) { + spin_unlock(&dcache_lock); + return 0; + } + + *(buf + len) = '\0'; + p = buf + len - dentry->d_name.len; + strncpy(p, dentry->d_name.name, dentry->d_name.len); + + for (tmp = dentry->d_parent; tmp != root ; tmp = tmp->d_parent) { + *(--p) = '/'; + p -= tmp->d_name.len; + strncpy(p, tmp->d_name.name, tmp->d_name.len); + } + spin_unlock(&dcache_lock); + + return len; +} + +int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, enum autofs_notify notify) { struct autofs_wait_queue *wq; - int status; + char *name; + int len, status; /* In catatonic mode, we don't wait for nobody */ if ( sbi->catatonic ) return -ENOENT; - /* We shouldn't be able to get here, but just in case */ - if ( name->len > NAME_MAX ) + name = kmalloc(NAME_MAX + 1, GFP_KERNEL); + if (!name) + return -ENOMEM; + + len = autofs4_getpath(sbi, dentry, &name); + if (!len) { + kfree(name); return -ENOENT; + } spin_lock(&waitq_lock); - for ( wq = sbi->queues ; wq ; wq = wq->next ) { - if ( wq->hash == name->hash && - wq->len == name->len && - wq->name && !memcmp(wq->name,name->name,name->len) ) + for (wq = sbi->queues ; wq ; wq = wq->next) { + if (wq->hash == dentry->d_name.hash && + wq->len == len && + wq->name && !memcmp(wq->name, name, len)) break; } spin_unlock(&waitq_lock); @@ -152,12 +192,8 @@ int autofs4_wait(struct autofs_sb_info * if ( !wq ) { /* Create a new wait queue */ wq = kmalloc(sizeof(struct autofs_wait_queue),GFP_KERNEL); - if ( !wq ) - return -ENOMEM; - - wq->name = kmalloc(name->len,GFP_KERNEL); - if ( !wq->name ) { - kfree(wq); + if ( !wq ) { + kfree(name); return -ENOMEM; } @@ -169,10 +205,10 @@ int autofs4_wait(struct autofs_sb_info * sbi->queues = wq; spin_unlock(&waitq_lock); init_waitqueue_head(&wq->queue); - wq->hash = name->hash; - wq->len = name->len; + wq->hash = dentry->d_name.hash; + wq->name = name; + wq->len = len; wq->status = -EINTR; /* Status return if interrupted */ - memcpy(wq->name, name->name, name->len); DPRINTK(("autofs4_wait: new wait id = 0x%08lx, name = %.*s, nfy=%d\n", (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify)); diff -puN fs/open.c~5-autofs4-2.6.0-readdir-20040405 fs/open.c --- 25/fs/open.c~5-autofs4-2.6.0-readdir-20040405 Tue Apr 6 15:50:40 2004 +++ 25-akpm/fs/open.c Tue Apr 6 15:50:40 2004 @@ -517,6 +517,8 @@ asmlinkage long sys_chdir(const char __u { struct nameidata nd; int error; + struct vfsmount *old_mnt = mntget(current->fs->pwdmnt); + struct dentry *old_dentry = dget(current->fs->pwd); error = __user_walk(filename, LOOKUP_FOLLOW|LOOKUP_DIRECTORY, &nd); if (error) @@ -528,7 +530,27 @@ asmlinkage long sys_chdir(const char __u set_fs_pwd(current->fs, nd.mnt, nd.dentry); + /* + * if we chdir to an autofs4 mount point we must get in early + * for subsequent path_walks to work properly. + */ + if (nd.dentry->d_op && nd.dentry->d_op->d_revalidate) { + int res; + + res = nd.dentry->d_op->d_revalidate(nd.dentry, &nd); + if (res) { + error = permission(current->fs->pwd->d_inode, MAY_EXEC, &nd); + if (!error) + goto dput_and_out; + } else + error = -ENOENT; + + set_fs_pwd(current->fs, old_mnt, old_dentry); + } + dput_and_out: + mntput(old_mnt); + dput(old_dentry); path_release(&nd); out: return error; @@ -568,6 +590,8 @@ asmlinkage long sys_chroot(const char __ { struct nameidata nd; int error; + struct vfsmount *old_mnt = mntget(current->fs->rootmnt); + struct dentry *old_dentry = dget(current->fs->root); error = __user_walk(filename, LOOKUP_FOLLOW | LOOKUP_DIRECTORY | LOOKUP_NOALT, &nd); if (error) @@ -582,9 +606,31 @@ asmlinkage long sys_chroot(const char __ goto dput_and_out; set_fs_root(current->fs, nd.mnt, nd.dentry); + + /* + * if we chroot to an autofs4 mount point we must get in early + * for subsequent path_walks to work properly. + */ + if (nd.dentry->d_op && nd.dentry->d_op->d_revalidate) { + int res; + + res = nd.dentry->d_op->d_revalidate(nd.dentry, &nd); + if (res) { + error = permission(current->fs->pwd->d_inode, MAY_EXEC, &nd); + if (!error) + goto valid; + } else + error = -ENOENT; + + set_fs_root(current->fs, old_mnt, old_dentry); + goto dput_and_out; + } +valid: set_fs_altroot(); error = 0; dput_and_out: + mntput(old_mnt); + dput(old_dentry); path_release(&nd); out: return error; diff -puN include/linux/auto_fs4.h~5-autofs4-2.6.0-readdir-20040405 include/linux/auto_fs4.h --- 25/include/linux/auto_fs4.h~5-autofs4-2.6.0-readdir-20040405 Tue Apr 6 15:50:40 2004 +++ 25-akpm/include/linux/auto_fs4.h Tue Apr 6 15:50:40 2004 @@ -23,6 +23,8 @@ #define AUTOFS_MIN_PROTO_VERSION 3 #define AUTOFS_MAX_PROTO_VERSION 4 +#define AUTOFS_PROTO_SUBVERSION 5 + /* Mask for expire behaviour */ #define AUTOFS_EXP_IMMEDIATE 1 #define AUTOFS_EXP_LEAVES 2 @@ -46,6 +48,7 @@ union autofs_packet_union { }; #define AUTOFS_IOC_EXPIRE_MULTI _IOW(0x93,0x66,int) +#define AUTOFS_IOC_PROTOSUBVER _IOR(0x93,0x67,int) #endif /* _LINUX_AUTO_FS4_H */ _