*
* Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved
* Copyright 1999-2000 Jeremy Fitzhardinge <jeremy@goop.org>
- * Copyright 2001-2003 Ian Kent <raven@themaw.net>
+ * Copyright 2001-2006 Ian Kent <raven@themaw.net>
*
* 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
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 *);
-struct file_operations autofs4_root_operations = {
+const struct file_operations autofs4_root_operations = {
.open = dcache_dir_open,
.release = dcache_dir_close,
.read = generic_read_dir,
.ioctl = autofs4_root_ioctl,
};
-struct file_operations autofs4_dir_operations = {
+const 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 = {
+struct inode_operations autofs4_indirect_root_inode_operations = {
.lookup = autofs4_lookup,
.unlink = autofs4_dir_unlink,
.symlink = autofs4_dir_symlink,
.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,
nd.flags = LOOKUP_DIRECTORY;
ret = (dentry->d_op->d_revalidate)(dentry, &nd);
- if (!ret) {
+ if (ret <= 0) {
+ if (ret < 0)
+ status = ret;
dcache_dir_close(inode, file);
goto out;
}
*/
status = d_invalidate(dentry);
if (status != -EBUSY)
- return 0;
+ return -ENOENT;
}
DPRINTK("dentry=%p %.*s ino=%p",
DPRINTK("mount done status=%d", status);
if (status && dentry->d_inode)
- return 0; /* Try to get the kernel to invalidate this dentry */
+ return status; /* Try to get the kernel to invalidate this dentry */
/* Turn this into a real negative dentry? */
if (status == -ENOENT) {
spin_lock(&dentry->d_lock);
dentry->d_flags &= ~DCACHE_AUTOFS_PENDING;
spin_unlock(&dentry->d_lock);
- return 0;
+ return status;
} else if (status) {
/* Return a negative dentry, but leave it "pending" */
- return 0;
+ return status;
}
/* Trigger mount for path component or follow link */
} else if (flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) ||
spin_lock(&dentry->d_lock);
dentry->d_flags &= ~DCACHE_AUTOFS_PENDING;
spin_unlock(&dentry->d_lock);
- return 0;
+ return status;
}
}
spin_lock(&dentry->d_lock);
dentry->d_flags &= ~DCACHE_AUTOFS_PENDING;
spin_unlock(&dentry->d_lock);
- return 1;
+ 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) && __simple_empty(dentry)) {
+ 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);
}
/*
/* Pending dentry */
if (autofs4_ispending(dentry)) {
- if (!oz_mode)
- status = try_to_fill_dentry(dentry, flags);
+ /* 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;
+
return status;
}
spin_lock(&dcache_lock);
if (S_ISDIR(dentry->d_inode->i_mode) &&
!d_mountpoint(dentry) &&
- simple_empty_nolock(dentry)) {
+ __simple_empty(dentry)) {
DPRINTK("dentry=%p %.*s, emptydir",
dentry, dentry->d_name.len, dentry->d_name.name);
spin_unlock(&dcache_lock);
- if (!oz_mode)
- status = try_to_fill_dentry(dentry, flags);
+ /* 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;
+
return status;
}
spin_unlock(&dcache_lock);
return 1;
}
-static void autofs4_dentry_release(struct dentry *de)
+void autofs4_dentry_release(struct dentry *de)
{
struct autofs_info *inf;
{
int status = 0;
- if (may_umount(mnt) == 0)
+ if (may_umount(mnt))
status = 1;
DPRINTK("returning %d", status);