X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;ds=sidebyside;f=fs%2Fautofs4%2Froot.c;fp=fs%2Fautofs4%2Froot.c;h=62d8d4acb8bbbe75ba0965214808c259c4b1b292;hb=64ba3f394c830ec48a1c31b53dcae312c56f1604;hp=3d3590cc647db6afd968cec5104eb3db3953f0b7;hpb=be1e6109ac94a859551f8e1774eb9a8469fe055c;p=linux-2.6.git diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c index 3d3590cc6..62d8d4acb 100644 --- a/fs/autofs4/root.c +++ b/fs/autofs4/root.c @@ -4,7 +4,7 @@ * * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved * Copyright 1999-2000 Jeremy Fitzhardinge - * Copyright 2001-2006 Ian Kent + * 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 @@ -30,9 +30,9 @@ 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 int autofs4_root_readdir(struct file * filp, void * dirent, filldir_t filldir); static struct dentry *autofs4_lookup(struct inode *,struct dentry *, struct nameidata *); -static void *autofs4_follow_link(struct dentry *, struct nameidata *); +static int autofs4_dcache_readdir(struct file *, void *, filldir_t); -const struct file_operations autofs4_root_operations = { +struct file_operations autofs4_root_operations = { .open = dcache_dir_open, .release = dcache_dir_close, .read = generic_read_dir, @@ -40,14 +40,14 @@ const struct file_operations autofs4_root_operations = { .ioctl = autofs4_root_ioctl, }; -const struct file_operations autofs4_dir_operations = { +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_indirect_root_inode_operations = { +struct inode_operations autofs4_root_inode_operations = { .lookup = autofs4_lookup, .unlink = autofs4_dir_unlink, .symlink = autofs4_dir_symlink, @@ -55,14 +55,6 @@ struct inode_operations autofs4_indirect_root_inode_operations = { .rmdir = autofs4_dir_rmdir, }; -struct inode_operations autofs4_direct_root_inode_operations = { - .lookup = autofs4_lookup, - .unlink = autofs4_dir_unlink, - .mkdir = autofs4_dir_mkdir, - .rmdir = autofs4_dir_rmdir, - .follow_link = autofs4_follow_link, -}; - struct inode_operations autofs4_dir_inode_operations = { .lookup = autofs4_lookup, .unlink = autofs4_dir_unlink, @@ -90,7 +82,87 @@ static int autofs4_root_readdir(struct file *file, void *dirent, DPRINTK("needs_reghost = %d", sbi->needs_reghost); - return dcache_readdir(file, dirent, filldir); + return autofs4_dcache_readdir(file, dirent, filldir); +} + +/* Update usage from here to top of tree, so that scan of + top-level directories will give a useful result */ +static void autofs4_update_usage(struct vfsmount *mnt, struct dentry *dentry) +{ + struct dentry *top = dentry->d_sb->s_root; + + spin_lock(&dcache_lock); + for(; dentry != top; dentry = dentry->d_parent) { + struct autofs_info *ino = autofs4_dentry_ino(dentry); + + if (ino) { + touch_atime(mnt, dentry); + ino->last_used = jiffies; + } + } + spin_unlock(&dcache_lock); +} + +/* + * 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_u.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) @@ -98,16 +170,8 @@ 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); - struct dentry *cursor; int status; - status = dcache_dir_open(inode, file); - if (status) - goto out; - - cursor = file->private_data; - cursor->d_fsdata = NULL; - DPRINTK("file=%p dentry=%p %.*s", file, dentry, dentry->d_name.len, dentry->d_name.name); @@ -116,15 +180,12 @@ static int autofs4_dir_open(struct inode *inode, struct file *file) if (autofs4_ispending(dentry)) { DPRINTK("dentry busy"); - dcache_dir_close(inode, file); - status = -EBUSY; - goto out; + return -EBUSY; } - status = -ENOENT; if (!d_mountpoint(dentry) && dentry->d_op && dentry->d_op->d_revalidate) { struct nameidata nd; - int empty, ret; + int empty; /* In case there are stale directory dentrys from a failed mount */ spin_lock(&dcache_lock); @@ -134,15 +195,13 @@ static int autofs4_dir_open(struct inode *inode, struct file *file) if (!empty) d_invalidate(dentry); + nd.dentry = dentry; + nd.mnt = mnt; nd.flags = LOOKUP_DIRECTORY; - ret = (dentry->d_op->d_revalidate)(dentry, &nd); + status = (dentry->d_op->d_revalidate)(dentry, &nd); - if (ret <= 0) { - if (ret < 0) - status = ret; - dcache_dir_close(inode, file); - goto out; - } + if (!status) + return -ENOENT; } if (d_mountpoint(dentry)) { @@ -153,29 +212,25 @@ static int autofs4_dir_open(struct inode *inode, struct file *file) if (!autofs4_follow_mount(&fp_mnt, &fp_dentry)) { dput(fp_dentry); mntput(fp_mnt); - dcache_dir_close(inode, file); - goto out; + return -ENOENT; } fp = dentry_open(fp_dentry, fp_mnt, file->f_flags); status = PTR_ERR(fp); if (IS_ERR(fp)) { - dcache_dir_close(inode, file); - goto out; + file->private_data = NULL; + return status; } - cursor->d_fsdata = fp; + file->private_data = fp; } - return 0; out: - return status; + 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); - struct dentry *cursor = file->private_data; - int status = 0; DPRINTK("file=%p dentry=%p %.*s", file, dentry, dentry->d_name.len, dentry->d_name.name); @@ -185,28 +240,26 @@ static int autofs4_dir_close(struct inode *inode, struct file *file) if (autofs4_ispending(dentry)) { DPRINTK("dentry busy"); - status = -EBUSY; - goto out; + return -EBUSY; } if (d_mountpoint(dentry)) { - struct file *fp = cursor->d_fsdata; - if (!fp) { - status = -ENOENT; - goto out; - } + struct file *fp = file->private_data; + + if (!fp) + return -ENOENT; + filp_close(fp, current->files); + file->private_data = NULL; } out: - dcache_dir_close(inode, file); - return status; + 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); - struct dentry *cursor = file->private_data; int status; DPRINTK("file=%p dentry=%p %.*s", @@ -221,7 +274,7 @@ static int autofs4_dir_readdir(struct file *file, void *dirent, filldir_t filldi } if (d_mountpoint(dentry)) { - struct file *fp = cursor->d_fsdata; + struct file *fp = file->private_data; if (!fp) return -ENOENT; @@ -236,26 +289,27 @@ static int autofs4_dir_readdir(struct file *file, void *dirent, filldir_t filldi return status; } out: - return dcache_readdir(file, dirent, filldir); + return autofs4_dcache_readdir(file, dirent, filldir); } -static int try_to_fill_dentry(struct dentry *dentry, int flags) +static int try_to_fill_dentry(struct vfsmount *mnt, struct dentry *dentry, int flags) { - struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); - struct autofs_info *ino = autofs4_dentry_ino(dentry); + struct super_block *sb = mnt->mnt_sb; + struct autofs_sb_info *sbi = autofs4_sbi(sb); + struct autofs_info *de_info = autofs4_dentry_ino(dentry); int status = 0; /* Block on any pending expiry here; invalidate the dentry when expiration is done to trigger mount request with a new dentry */ - if (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { + if (de_info && (de_info->flags & AUTOFS_INF_EXPIRING)) { DPRINTK("waiting for expire %p name=%.*s", dentry, dentry->d_name.len, dentry->d_name.name); status = autofs4_wait(sbi, dentry, NFY_NONE); - + DPRINTK("expire done status=%d", status); - + /* * If the directory still exists the mount request must * continue otherwise it can't be followed at the right @@ -263,36 +317,34 @@ static int try_to_fill_dentry(struct dentry *dentry, int flags) */ status = d_invalidate(dentry); if (status != -EBUSY) - return -ENOENT; + return 0; } DPRINTK("dentry=%p %.*s ino=%p", dentry, dentry->d_name.len, dentry->d_name.name, dentry->d_inode); - /* - * Wait for a pending mount, triggering one if there - * isn't one already - */ + /* Wait for a pending mount, triggering one if there isn't one already */ if (dentry->d_inode == NULL) { DPRINTK("waiting for mount name=%.*s", dentry->d_name.len, dentry->d_name.name); status = autofs4_wait(sbi, dentry, NFY_MOUNT); - + DPRINTK("mount done status=%d", status); if (status && dentry->d_inode) - return status; /* Try to get the kernel to invalidate this dentry */ - + return 0; /* Try to get the kernel to invalidate this dentry */ + /* Turn this into a real negative dentry? */ if (status == -ENOENT) { + dentry->d_time = jiffies + AUTOFS_NEGATIVE_TIMEOUT; spin_lock(&dentry->d_lock); dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; spin_unlock(&dentry->d_lock); - return status; + return 1; } else if (status) { /* Return a negative dentry, but leave it "pending" */ - return status; + return 1; } /* Trigger mount for path component or follow link */ } else if (flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) || @@ -311,83 +363,19 @@ static int try_to_fill_dentry(struct dentry *dentry, int flags) spin_lock(&dentry->d_lock); dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; spin_unlock(&dentry->d_lock); - return status; + return 0; } } - /* Initialize expiry counter after successful mount */ - if (ino) - ino->last_used = jiffies; + /* We don't update the usages for the autofs daemon itself, this + is necessary for recursive autofs mounts */ + if (!autofs4_oz_mode(sbi)) + autofs4_update_usage(mnt, dentry); spin_lock(&dentry->d_lock); dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; spin_unlock(&dentry->d_lock); - return status; -} - -/* For autofs direct mounts the follow link triggers the mount */ -static void *autofs4_follow_link(struct dentry *dentry, struct nameidata *nd) -{ - struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); - struct autofs_info *ino = autofs4_dentry_ino(dentry); - int oz_mode = autofs4_oz_mode(sbi); - unsigned int lookup_type; - int status; - - DPRINTK("dentry=%p %.*s oz_mode=%d nd->flags=%d", - dentry, dentry->d_name.len, dentry->d_name.name, oz_mode, - nd->flags); - - /* If it's our master or we shouldn't trigger a mount we're done */ - lookup_type = nd->flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY); - if (oz_mode || !lookup_type) - goto done; - - /* If an expire request is pending wait for it. */ - if (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { - DPRINTK("waiting for active request %p name=%.*s", - dentry, dentry->d_name.len, dentry->d_name.name); - - status = autofs4_wait(sbi, dentry, NFY_NONE); - - DPRINTK("request done status=%d", status); - } - - /* - * If the dentry contains directories then it is an - * autofs multi-mount with no root mount offset. So - * don't try to mount it again. - */ - spin_lock(&dcache_lock); - if (!d_mountpoint(dentry) && list_empty(&dentry->d_subdirs)) { - spin_unlock(&dcache_lock); - - status = try_to_fill_dentry(dentry, 0); - if (status) - goto out_error; - - /* - * The mount succeeded but if there is no root mount - * it must be an autofs multi-mount with no root offset - * so we don't need to follow the mount. - */ - if (d_mountpoint(dentry)) { - if (!autofs4_follow_mount(&nd->mnt, &nd->dentry)) { - status = -ENOENT; - goto out_error; - } - } - - goto done; - } - spin_unlock(&dcache_lock); - -done: - return NULL; - -out_error: - path_release(nd); - return ERR_PTR(status); + return 1; } /* @@ -396,63 +384,47 @@ out_error: * yet completely filled in, and revalidate has to delay such * lookups.. */ -static int autofs4_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 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; + int status = 1; /* Pending dentry */ if (autofs4_ispending(dentry)) { - /* The daemon never causes a mount to trigger */ - if (oz_mode) - return 1; - - /* - * A zero status is success otherwise we have a - * negative error code. - */ - status = try_to_fill_dentry(dentry, flags); - if (status == 0) - return 1; - + if (!oz_mode) + status = try_to_fill_dentry(nd->mnt, dentry, flags); return status; } /* Negative dentry.. invalidate if "old" */ if (dentry->d_inode == NULL) - return 0; + return (dentry->d_time - jiffies <= AUTOFS_NEGATIVE_TIMEOUT); /* Check for a non-mountpoint directory with no contents */ spin_lock(&dcache_lock); if (S_ISDIR(dentry->d_inode->i_mode) && !d_mountpoint(dentry) && - __simple_empty(dentry)) { + list_empty(&dentry->d_subdirs)) { DPRINTK("dentry=%p %.*s, emptydir", dentry, dentry->d_name.len, dentry->d_name.name); spin_unlock(&dcache_lock); - /* The daemon never causes a mount to trigger */ - if (oz_mode) - return 1; - - /* - * A zero status is success otherwise we have a - * negative error code. - */ - status = try_to_fill_dentry(dentry, flags); - if (status == 0) - return 1; - + if (!oz_mode) + status = try_to_fill_dentry(nd->mnt, dentry, flags); return status; } spin_unlock(&dcache_lock); + /* Update the usage list */ + if (!oz_mode) + autofs4_update_usage(nd->mnt, dentry); + return 1; } -void autofs4_dentry_release(struct dentry *de) +static void autofs4_dentry_release(struct dentry *de) { struct autofs_info *inf; @@ -490,13 +462,12 @@ static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, s DPRINTK("name = %.*s", dentry->d_name.len, dentry->d_name.name); - /* File name too long to exist */ if (dentry->d_name.len > NAME_MAX) - return ERR_PTR(-ENAMETOOLONG); + return ERR_PTR(-ENAMETOOLONG);/* File name too long to exist */ sbi = autofs4_sbi(dir->i_sb); - oz_mode = autofs4_oz_mode(sbi); + oz_mode = autofs4_oz_mode(sbi); DPRINTK("pid = %u, pgrp = %u, catatonic = %d, oz_mode = %d", current->pid, process_group(current), sbi->catatonic, oz_mode); @@ -548,7 +519,7 @@ static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, s * doesn't do the right thing for all system calls, but it should * be OK for the operations we permit from an autofs. */ - if (dentry->d_inode && d_unhashed(dentry)) + if ( dentry->d_inode && d_unhashed(dentry) ) return ERR_PTR(-ENOENT); return NULL; @@ -560,7 +531,6 @@ static int autofs4_dir_symlink(struct inode *dir, { struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); struct autofs_info *ino = autofs4_dentry_ino(dentry); - struct autofs_info *p_ino; struct inode *inode; char *cp; @@ -594,10 +564,6 @@ static int autofs4_dir_symlink(struct inode *dir, dentry->d_fsdata = ino; ino->dentry = dget(dentry); - atomic_inc(&ino->count); - p_ino = autofs4_dentry_ino(dentry->d_parent); - if (p_ino && dentry->d_parent != dentry) - atomic_inc(&p_ino->count); ino->inode = inode; dir->i_mtime = CURRENT_TIME; @@ -624,17 +590,11 @@ static int autofs4_dir_unlink(struct inode *dir, struct dentry *dentry) { struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); struct autofs_info *ino = autofs4_dentry_ino(dentry); - struct autofs_info *p_ino; /* This allows root to remove symlinks */ if ( !autofs4_oz_mode(sbi) && !capable(CAP_SYS_ADMIN) ) return -EACCES; - if (atomic_dec_and_test(&ino->count)) { - p_ino = autofs4_dentry_ino(dentry->d_parent); - if (p_ino && dentry->d_parent != dentry) - atomic_dec(&p_ino->count); - } dput(ino->dentry); dentry->d_inode->i_size = 0; @@ -651,7 +611,6 @@ static int autofs4_dir_rmdir(struct inode *dir, struct dentry *dentry) { struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); struct autofs_info *ino = autofs4_dentry_ino(dentry); - struct autofs_info *p_ino; if (!autofs4_oz_mode(sbi)) return -EACCES; @@ -666,12 +625,8 @@ static int autofs4_dir_rmdir(struct inode *dir, struct dentry *dentry) spin_unlock(&dentry->d_lock); spin_unlock(&dcache_lock); - if (atomic_dec_and_test(&ino->count)) { - p_ino = autofs4_dentry_ino(dentry->d_parent); - if (p_ino && dentry->d_parent != dentry) - atomic_dec(&p_ino->count); - } dput(ino->dentry); + dentry->d_inode->i_size = 0; dentry->d_inode->i_nlink = 0; @@ -685,7 +640,6 @@ static int autofs4_dir_mkdir(struct inode *dir, struct dentry *dentry, int mode) { struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); struct autofs_info *ino = autofs4_dentry_ino(dentry); - struct autofs_info *p_ino; struct inode *inode; if ( !autofs4_oz_mode(sbi) ) @@ -708,10 +662,6 @@ static int autofs4_dir_mkdir(struct inode *dir, struct dentry *dentry, int mode) dentry->d_fsdata = ino; ino->dentry = dget(dentry); - atomic_inc(&ino->count); - p_ino = autofs4_dentry_ino(dentry->d_parent); - if (p_ino && dentry->d_parent != dentry) - atomic_inc(&p_ino->count); ino->inode = inode; dir->i_nlink++; dir->i_mtime = CURRENT_TIME; @@ -795,7 +745,7 @@ static inline int autofs4_ask_umount(struct vfsmount *mnt, int __user *p) { int status = 0; - if (may_umount(mnt)) + if (may_umount(mnt) == 0) status = 1; DPRINTK("returning %d", status);