vsprintf: factor out skip_space code in a separate function
[safe/jmp/linux-2.6] / fs / autofs4 / root.c
index dbb70d5..b96a3c5 100644 (file)
@@ -25,25 +25,27 @@ 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 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 *);
 
+#define TRIGGER_FLAGS   (LOOKUP_CONTINUE | LOOKUP_DIRECTORY)
+#define TRIGGER_INTENTS (LOOKUP_OPEN | LOOKUP_CREATE)
+
 const struct file_operations autofs4_root_operations = {
        .open           = dcache_dir_open,
        .release        = dcache_dir_close,
        .read           = generic_read_dir,
-       .readdir        = autofs4_root_readdir,
+       .readdir        = dcache_readdir,
+       .llseek         = dcache_dir_lseek,
        .ioctl          = autofs4_root_ioctl,
 };
 
 const struct file_operations autofs4_dir_operations = {
        .open           = autofs4_dir_open,
-       .release        = autofs4_dir_close,
+       .release        = dcache_dir_close,
        .read           = generic_read_dir,
-       .readdir        = autofs4_dir_readdir,
+       .readdir        = dcache_readdir,
+       .llseek         = dcache_dir_lseek,
 };
 
 const struct inode_operations autofs4_indirect_root_inode_operations = {
@@ -70,42 +72,10 @@ const struct inode_operations autofs4_dir_inode_operations = {
        .rmdir          = autofs4_dir_rmdir,
 };
 
-static int autofs4_root_readdir(struct file *file, void *dirent,
-                               filldir_t filldir)
-{
-       struct autofs_sb_info *sbi = autofs4_sbi(file->f_path.dentry->d_sb);
-       int oz_mode = autofs4_oz_mode(sbi);
-
-       DPRINTK("called, filp->f_pos = %lld", file->f_pos);
-
-       /*
-        * Don't set reghost flag if:
-        * 1) f_pos is larger than zero -- we've already been here.
-        * 2) we haven't even enabled reghosting in the 1st place.
-        * 3) this is the daemon doing a readdir
-        */
-       if (oz_mode && file->f_pos == 0 && sbi->reghost_enabled)
-               sbi->needs_reghost = 1;
-
-       DPRINTK("needs_reghost = %d", sbi->needs_reghost);
-
-       return dcache_readdir(file, dirent, filldir);
-}
-
 static int autofs4_dir_open(struct inode *inode, struct file *file)
 {
        struct dentry *dentry = file->f_path.dentry;
-       struct vfsmount *mnt = file->f_path.mnt;
        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);
@@ -113,129 +83,24 @@ static int autofs4_dir_open(struct inode *inode, struct file *file)
        if (autofs4_oz_mode(sbi))
                goto out;
 
-       if (autofs4_ispending(dentry)) {
-               DPRINTK("dentry busy");
-               dcache_dir_close(inode, file);
-               status = -EBUSY;
-               goto out;
-       }
-
-       status = -ENOENT;
-       if (!d_mountpoint(dentry) && dentry->d_op && dentry->d_op->d_revalidate) {
-               struct nameidata nd;
-               int empty, ret;
-
-               /* In case there are stale directory dentrys from a failed mount */
-               spin_lock(&dcache_lock);
-               empty = list_empty(&dentry->d_subdirs);
+       /*
+        * An empty directory in an autofs file system is always a
+        * mount point. The daemon must have failed to mount this
+        * during lookup so it doesn't exist. This can happen, for
+        * example, if user space returns an incorrect status for a
+        * mount request. Otherwise we're doing a readdir on the
+        * autofs file system so just let the libfs routines handle
+        * it.
+        */
+       spin_lock(&dcache_lock);
+       if (!d_mountpoint(dentry) && __simple_empty(dentry)) {
                spin_unlock(&dcache_lock);
-
-               if (!empty)
-                       d_invalidate(dentry);
-
-               nd.flags = LOOKUP_DIRECTORY;
-               ret = (dentry->d_op->d_revalidate)(dentry, &nd);
-
-               if (ret <= 0) {
-                       if (ret < 0)
-                               status = ret;
-                       dcache_dir_close(inode, file);
-                       goto out;
-               }
+               return -ENOENT;
        }
+       spin_unlock(&dcache_lock);
 
-       if (d_mountpoint(dentry)) {
-               struct file *fp = NULL;
-               struct path fp_path = { .dentry = dentry, .mnt = mnt };
-
-               path_get(&fp_path);
-
-               if (!autofs4_follow_mount(&fp_path.mnt, &fp_path.dentry)) {
-                       path_put(&fp_path);
-                       dcache_dir_close(inode, file);
-                       goto out;
-               }
-
-               fp = dentry_open(fp_path.dentry, fp_path.mnt, file->f_flags);
-               status = PTR_ERR(fp);
-               if (IS_ERR(fp)) {
-                       dcache_dir_close(inode, file);
-                       goto out;
-               }
-               cursor->d_fsdata = fp;
-       }
-       return 0;
-out:
-       return status;
-}
-
-static int autofs4_dir_close(struct inode *inode, struct file *file)
-{
-       struct dentry *dentry = file->f_path.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);
-
-       if (autofs4_oz_mode(sbi))
-               goto out;
-
-       if (autofs4_ispending(dentry)) {
-               DPRINTK("dentry busy");
-               status = -EBUSY;
-               goto out;
-       }
-
-       if (d_mountpoint(dentry)) {
-               struct file *fp = cursor->d_fsdata;
-               if (!fp) {
-                       status = -ENOENT;
-                       goto out;
-               }
-               filp_close(fp, current->files);
-       }
-out:
-       dcache_dir_close(inode, file);
-       return status;
-}
-
-static int autofs4_dir_readdir(struct file *file, void *dirent, filldir_t filldir)
-{
-       struct dentry *dentry = file->f_path.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",
-               file, dentry, dentry->d_name.len, dentry->d_name.name);
-
-       if (autofs4_oz_mode(sbi))
-               goto out;
-
-       if (autofs4_ispending(dentry)) {
-               DPRINTK("dentry busy");
-               return -EBUSY;
-       }
-
-       if (d_mountpoint(dentry)) {
-               struct file *fp = cursor->d_fsdata;
-
-               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 dcache_readdir(file, dirent, filldir);
+       return dcache_dir_open(inode, file);
 }
 
 static int try_to_fill_dentry(struct dentry *dentry, int flags)
@@ -244,27 +109,6 @@ static int try_to_fill_dentry(struct dentry *dentry, int flags)
        struct autofs_info *ino = autofs4_dentry_ino(dentry);
        int status;
 
-       /* 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)) {
-               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
-                * time during the walk.
-                */
-               status = d_invalidate(dentry);
-               if (status != -EBUSY)
-                       return -EAGAIN;
-       }
-
        DPRINTK("dentry=%p %.*s ino=%p",
                 dentry, dentry->d_name.len, dentry->d_name.name, dentry->d_inode);
 
@@ -291,7 +135,8 @@ static int try_to_fill_dentry(struct dentry *dentry, int flags)
                        return status;
                }
        /* Trigger mount for path component or follow link */
-       } else if (flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) ||
+       } else if (dentry->d_flags & DCACHE_AUTOFS_PENDING ||
+                       flags & (TRIGGER_FLAGS | TRIGGER_INTENTS) ||
                        current->link_count) {
                DPRINTK("waiting for mount name=%.*s",
                        dentry->d_name.len, dentry->d_name.name);
@@ -334,51 +179,62 @@ static void *autofs4_follow_link(struct dentry *dentry, struct nameidata *nd)
        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)
+       /*
+        * For an expire of a covered direct or offset mount we need
+        * to break out of follow_down() at the autofs mount trigger
+        * (d_mounted--), so we can see the expiring flag, and manage
+        * the blocking and following here until the expire is completed.
+        */
+       if (oz_mode) {
+               spin_lock(&sbi->fs_lock);
+               if (ino->flags & AUTOFS_INF_EXPIRING) {
+                       spin_unlock(&sbi->fs_lock);
+                       /* Follow down to our covering mount. */
+                       if (!follow_down(&nd->path))
+                               goto done;
+                       goto follow;
+               }
+               spin_unlock(&sbi->fs_lock);
                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);
+       /* If an expire request is pending everyone must wait. */
+       autofs4_expire_wait(dentry);
 
-               DPRINTK("request done status=%d", status);
-       }
+       /* We trigger a mount for almost all flags */
+       lookup_type = nd->flags & (TRIGGER_FLAGS | TRIGGER_INTENTS);
+       if (!(lookup_type || dentry->d_flags & DCACHE_AUTOFS_PENDING))
+               goto follow;
 
        /*
-        * 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.
+        * 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)) {
+       if (dentry->d_flags & DCACHE_AUTOFS_PENDING ||
+           (!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->path.mnt,
-                                                 &nd->path.dentry)) {
-                               status = -ENOENT;
-                               goto out_error;
-                       }
-               }
-
-               goto done;
+               goto follow;
        }
        spin_unlock(&dcache_lock);
+follow:
+       /*
+        * If there is no root mount it must be an autofs
+        * multi-mount with no root offset so we don't need
+        * to follow it.
+        */
+       if (d_mountpoint(dentry)) {
+               if (!autofs4_follow_mount(&nd->path)) {
+                       status = -ENOENT;
+                       goto out_error;
+               }
+       }
 
 done:
        return NULL;
@@ -403,12 +259,23 @@ static int autofs4_revalidate(struct dentry *dentry, struct nameidata *nd)
        int status = 1;
 
        /* Pending dentry */
+       spin_lock(&sbi->fs_lock);
        if (autofs4_ispending(dentry)) {
                /* The daemon never causes a mount to trigger */
+               spin_unlock(&sbi->fs_lock);
+
                if (oz_mode)
                        return 1;
 
                /*
+                * If the directory has gone away due to an expire
+                * we have been called as ->d_revalidate() and so
+                * we need to return false and proceed to ->lookup().
+                */
+               if (autofs4_expire_wait(dentry) == -EAGAIN)
+                       return 0;
+
+               /*
                 * A zero status is success otherwise we have a
                 * negative error code.
                 */
@@ -416,17 +283,9 @@ static int autofs4_revalidate(struct dentry *dentry, struct nameidata *nd)
                if (status == 0)
                        return 1;
 
-               /*
-                * A status of EAGAIN here means that the dentry has gone
-                * away while waiting for an expire to complete. If we are
-                * racing with expire lookup will wait for it so this must
-                * be a revalidate and we need to send it to lookup.
-                */
-               if (status == -EAGAIN)
-                       return 0;
-
                return status;
        }
+       spin_unlock(&sbi->fs_lock);
 
        /* Negative dentry.. invalidate if "old" */
        if (dentry->d_inode == NULL)
@@ -440,6 +299,7 @@ static int autofs4_revalidate(struct dentry *dentry, struct nameidata *nd)
                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;
@@ -488,13 +348,13 @@ void autofs4_dentry_release(struct dentry *de)
 }
 
 /* For dentries of directories in the root dir */
-static struct dentry_operations autofs4_root_dentry_operations = {
+static const struct dentry_operations autofs4_root_dentry_operations = {
        .d_revalidate   = autofs4_revalidate,
        .d_release      = autofs4_dentry_release,
 };
 
 /* For other dentries */
-static struct dentry_operations autofs4_dentry_operations = {
+static const struct dentry_operations autofs4_dentry_operations = {
        .d_revalidate   = autofs4_revalidate,
        .d_release      = autofs4_dentry_release,
 };
@@ -624,28 +484,6 @@ static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, s
        DPRINTK("pid = %u, pgrp = %u, catatonic = %d, oz_mode = %d",
                 current->pid, task_pgrp_nr(current), sbi->catatonic, oz_mode);
 
-       expiring = autofs4_lookup_expiring(sbi, dentry->d_parent, &dentry->d_name);
-       if (expiring) {
-               /*
-                * If we are racing with expire the request might not
-                * be quite complete but the directory has been removed
-                * so it must have been successful, so just wait for it.
-                */
-               ino = autofs4_dentry_ino(expiring);
-               while (ino && (ino->flags & AUTOFS_INF_EXPIRING)) {
-                       DPRINTK("wait for incomplete expire %p name=%.*s",
-                               expiring, expiring->d_name.len,
-                               expiring->d_name.name);
-                       autofs4_wait(sbi, expiring, NFY_NONE);
-                       DPRINTK("request completed");
-               }
-               spin_lock(&sbi->lookup_lock);
-               if (!list_empty(&ino->expiring))
-                       list_del_init(&ino->expiring);
-               spin_unlock(&sbi->lookup_lock);
-               dput(expiring);
-       }
-
        unhashed = autofs4_lookup_active(sbi, dentry->d_parent, &dentry->d_name);
        if (unhashed)
                dentry = unhashed;
@@ -683,14 +521,30 @@ static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, s
        }
 
        if (!oz_mode) {
+               mutex_unlock(&dir->i_mutex);
+               expiring = autofs4_lookup_expiring(sbi,
+                                                  dentry->d_parent,
+                                                  &dentry->d_name);
+               if (expiring) {
+                       /*
+                        * If we are racing with expire the request might not
+                        * be quite complete but the directory has been removed
+                        * so it must have been successful, so just wait for it.
+                        */
+                       ino = autofs4_dentry_ino(expiring);
+                       autofs4_expire_wait(expiring);
+                       spin_lock(&sbi->lookup_lock);
+                       if (!list_empty(&ino->expiring))
+                               list_del_init(&ino->expiring);
+                       spin_unlock(&sbi->lookup_lock);
+                       dput(expiring);
+               }
+
                spin_lock(&dentry->d_lock);
                dentry->d_flags |= DCACHE_AUTOFS_PENDING;
                spin_unlock(&dentry->d_lock);
-       }
-
-       if (dentry->d_op && dentry->d_op->d_revalidate) {
-               mutex_unlock(&dir->i_mutex);
-               (dentry->d_op->d_revalidate)(dentry, nd);
+               if (dentry->d_op && dentry->d_op->d_revalidate)
+                       (dentry->d_op->d_revalidate)(dentry, nd);
                mutex_lock(&dir->i_mutex);
        }
 
@@ -775,6 +629,7 @@ static int autofs4_dir_symlink(struct inode *dir,
                list_del_init(&ino->active);
        spin_unlock(&sbi->lookup_lock);
 
+       ino->size = strlen(symname);
        cp = kmalloc(ino->size + 1, GFP_KERNEL);
        if (!cp) {
                if (!dentry->d_fsdata)
@@ -806,7 +661,6 @@ static int autofs4_dir_symlink(struct inode *dir,
                atomic_inc(&p_ino->count);
        ino->inode = inode;
 
-       ino->size = strlen(symname);
        ino->u.symlink = cp;
        dir->i_mtime = CURRENT_TIME;
 
@@ -984,44 +838,6 @@ static inline int autofs4_get_protosubver(struct autofs_sb_info *sbi, int __user
 }
 
 /*
- * Tells the daemon whether we need to reghost or not. Also, clears
- * the reghost_needed flag.
- */
-static inline int autofs4_ask_reghost(struct autofs_sb_info *sbi, int __user *p)
-{
-       int status;
-
-       DPRINTK("returning %d", sbi->needs_reghost);
-
-       status = put_user(sbi->needs_reghost, p);
-       if (status)
-               return status;
-
-       sbi->needs_reghost = 0;
-       return 0;
-}
-
-/*
- * Enable / Disable reghosting ioctl() operation
- */
-static inline int autofs4_toggle_reghost(struct autofs_sb_info *sbi, int __user *p)
-{
-       int status;
-       int val;
-
-       status = get_user(val, p);
-
-       DPRINTK("reghost = %d", val);
-
-       if (status)
-               return status;
-
-       /* turn on/off reghosting, with the val */
-       sbi->reghost_enabled = val;
-       return 0;
-}
-
-/*
 * Tells the daemon whether it can umount the autofs mount.
 */
 static inline int autofs4_ask_umount(struct vfsmount *mnt, int __user *p)
@@ -1085,11 +901,6 @@ static int autofs4_root_ioctl(struct inode *inode, struct file *filp,
        case AUTOFS_IOC_SETTIMEOUT:
                return autofs4_get_set_timeout(sbi, p);
 
-       case AUTOFS_IOC_TOGGLEREGHOST:
-               return autofs4_toggle_reghost(sbi, p);
-       case AUTOFS_IOC_ASKREGHOST:
-               return autofs4_ask_reghost(sbi, p);
-
        case AUTOFS_IOC_ASKUMOUNT:
                return autofs4_ask_umount(filp->f_path.mnt, p);