Don't pass inode to ->d_hash() and ->d_compare()
[cascardo/linux.git] / fs / ncpfs / dir.c
index 8163260..3be0474 100644 (file)
 
 #include "ncp_fs.h"
 
-static void ncp_read_volume_list(struct file *, void *, filldir_t,
+static void ncp_read_volume_list(struct file *, struct dir_context *,
                                struct ncp_cache_control *);
-static void ncp_do_readdir(struct file *, void *, filldir_t,
+static void ncp_do_readdir(struct file *, struct dir_context *,
                                struct ncp_cache_control *);
 
-static int ncp_readdir(struct file *, void *, filldir_t);
+static int ncp_readdir(struct file *, struct dir_context *);
 
 static int ncp_create(struct inode *, struct dentry *, umode_t, bool);
 static struct dentry *ncp_lookup(struct inode *, struct dentry *, unsigned int);
@@ -49,7 +49,7 @@ const struct file_operations ncp_dir_operations =
 {
        .llseek         = generic_file_llseek,
        .read           = generic_read_dir,
-       .readdir        = ncp_readdir,
+       .iterate        = ncp_readdir,
        .unlocked_ioctl = ncp_ioctl,
 #ifdef CONFIG_COMPAT
        .compat_ioctl   = ncp_compat_ioctl,
@@ -73,10 +73,8 @@ const struct inode_operations ncp_dir_inode_operations =
  * Dentry operations routines
  */
 static int ncp_lookup_validate(struct dentry *, unsigned int);
-static int ncp_hash_dentry(const struct dentry *, const struct inode *,
-               struct qstr *);
-static int ncp_compare_dentry(const struct dentry *, const struct inode *,
-               const struct dentry *, const struct inode *,
+static int ncp_hash_dentry(const struct dentry *, struct qstr *);
+static int ncp_compare_dentry(const struct dentry *, const struct dentry *,
                unsigned int, const char *, const struct qstr *);
 static int ncp_delete_dentry(const struct dentry *);
 
@@ -119,11 +117,19 @@ static inline int ncp_case_sensitive(const struct inode *i)
 /*
  * Note: leave the hash unchanged if the directory
  * is case-sensitive.
+ *
+ * Accessing the parent inode can be racy under RCU pathwalking.
+ * Use ACCESS_ONCE() to make sure we use _one_ particular inode,
+ * the callers will handle races.
  */
 static int 
-ncp_hash_dentry(const struct dentry *dentry, const struct inode *inode,
-               struct qstr *this)
+ncp_hash_dentry(const struct dentry *dentry, struct qstr *this)
 {
+       struct inode *inode = ACCESS_ONCE(dentry->d_inode);
+
+       if (!inode)
+               return 0;
+
        if (!ncp_case_sensitive(inode)) {
                struct super_block *sb = dentry->d_sb;
                struct nls_table *t;
@@ -140,14 +146,24 @@ ncp_hash_dentry(const struct dentry *dentry, const struct inode *inode,
        return 0;
 }
 
+/*
+ * Accessing the parent inode can be racy under RCU pathwalking.
+ * Use ACCESS_ONCE() to make sure we use _one_ particular inode,
+ * the callers will handle races.
+ */
 static int
-ncp_compare_dentry(const struct dentry *parent, const struct inode *pinode,
-               const struct dentry *dentry, const struct inode *inode,
+ncp_compare_dentry(const struct dentry *parent, const struct dentry *dentry,
                unsigned int len, const char *str, const struct qstr *name)
 {
+       struct inode *pinode;
+
        if (len != name->len)
                return 1;
 
+       pinode = ACCESS_ONCE(parent->d_inode);
+       if (!pinode)
+               return 1;
+
        if (ncp_case_sensitive(pinode))
                return strncmp(str, name->name, len);
 
@@ -424,9 +440,9 @@ static time_t ncp_obtain_mtime(struct dentry *dentry)
        return ncp_date_dos2unix(i.modifyTime, i.modifyDate);
 }
 
-static int ncp_readdir(struct file *filp, void *dirent, filldir_t filldir)
+static int ncp_readdir(struct file *file, struct dir_context *ctx)
 {
-       struct dentry *dentry = filp->f_path.dentry;
+       struct dentry *dentry = file->f_path.dentry;
        struct inode *inode = dentry->d_inode;
        struct page *page = NULL;
        struct ncp_server *server = NCP_SERVER(inode);
@@ -440,7 +456,7 @@ static int ncp_readdir(struct file *filp, void *dirent, filldir_t filldir)
 
        DDPRINTK("ncp_readdir: reading %s/%s, pos=%d\n",
                dentry->d_parent->d_name.name, dentry->d_name.name,
-               (int) filp->f_pos);
+               (int) ctx->pos);
 
        result = -EIO;
        /* Do not generate '.' and '..' when server is dead. */
@@ -448,16 +464,8 @@ static int ncp_readdir(struct file *filp, void *dirent, filldir_t filldir)
                goto out;
 
        result = 0;
-       if (filp->f_pos == 0) {
-               if (filldir(dirent, ".", 1, 0, inode->i_ino, DT_DIR))
-                       goto out;
-               filp->f_pos = 1;
-       }
-       if (filp->f_pos == 1) {
-               if (filldir(dirent, "..", 2, 1, parent_ino(dentry), DT_DIR))
-                       goto out;
-               filp->f_pos = 2;
-       }
+       if (!dir_emit_dots(file, ctx))
+               goto out;
 
        page = grab_cache_page(&inode->i_data, 0);
        if (!page)
@@ -469,7 +477,7 @@ static int ncp_readdir(struct file *filp, void *dirent, filldir_t filldir)
        if (!PageUptodate(page) || !ctl.head.eof)
                goto init_cache;
 
-       if (filp->f_pos == 2) {
+       if (ctx->pos == 2) {
                if (jiffies - ctl.head.time >= NCP_MAX_AGE(server))
                        goto init_cache;
 
@@ -479,10 +487,10 @@ static int ncp_readdir(struct file *filp, void *dirent, filldir_t filldir)
                        goto init_cache;
        }
 
-       if (filp->f_pos > ctl.head.end)
+       if (ctx->pos > ctl.head.end)
                goto finished;
 
-       ctl.fpos = filp->f_pos + (NCP_DIRCACHE_START - 2);
+       ctl.fpos = ctx->pos + (NCP_DIRCACHE_START - 2);
        ctl.ofs  = ctl.fpos / NCP_DIRCACHE_SIZE;
        ctl.idx  = ctl.fpos % NCP_DIRCACHE_SIZE;
 
@@ -497,21 +505,21 @@ static int ncp_readdir(struct file *filp, void *dirent, filldir_t filldir)
                }
                while (ctl.idx < NCP_DIRCACHE_SIZE) {
                        struct dentry *dent;
-                       int res;
+                       bool over;
 
                        dent = ncp_dget_fpos(ctl.cache->dentry[ctl.idx],
-                                               dentry, filp->f_pos);
+                                               dentry, ctx->pos);
                        if (!dent)
                                goto invalid_cache;
-                       res = filldir(dirent, dent->d_name.name,
-                                       dent->d_name.len, filp->f_pos,
+                       over = !dir_emit(ctx, dent->d_name.name,
+                                       dent->d_name.len,
                                        dent->d_inode->i_ino, DT_UNKNOWN);
                        dput(dent);
-                       if (res)
+                       if (over)
                                goto finished;
-                       filp->f_pos += 1;
+                       ctx->pos += 1;
                        ctl.idx += 1;
-                       if (filp->f_pos > ctl.head.end)
+                       if (ctx->pos > ctl.head.end)
                                goto finished;
                }
                if (ctl.page) {
@@ -548,9 +556,9 @@ init_cache:
        ctl.valid  = 1;
 read_really:
        if (ncp_is_server_root(inode)) {
-               ncp_read_volume_list(filp, dirent, filldir, &ctl);
+               ncp_read_volume_list(file, ctx, &ctl);
        } else {
-               ncp_do_readdir(filp, dirent, filldir, &ctl);
+               ncp_do_readdir(file, ctx, &ctl);
        }
        ctl.head.end = ctl.fpos - 1;
        ctl.head.eof = ctl.valid;
@@ -573,11 +581,11 @@ out:
 }
 
 static int
-ncp_fill_cache(struct file *filp, void *dirent, filldir_t filldir,
+ncp_fill_cache(struct file *file, struct dir_context *ctx,
                struct ncp_cache_control *ctrl, struct ncp_entry_info *entry,
                int inval_childs)
 {
-       struct dentry *newdent, *dentry = filp->f_path.dentry;
+       struct dentry *newdent, *dentry = file->f_path.dentry;
        struct inode *dir = dentry->d_inode;
        struct ncp_cache_control ctl = *ctrl;
        struct qstr qname;
@@ -666,15 +674,13 @@ ncp_fill_cache(struct file *filp, void *dirent, filldir_t filldir,
 end_advance:
        if (!valid)
                ctl.valid = 0;
-       if (!ctl.filled && (ctl.fpos == filp->f_pos)) {
-               if (!ino)
-                       ino = find_inode_number(dentry, &qname);
+       if (!ctl.filled && (ctl.fpos == ctx->pos)) {
                if (!ino)
                        ino = iunique(dir->i_sb, 2);
-               ctl.filled = filldir(dirent, qname.name, qname.len,
-                                    filp->f_pos, ino, DT_UNKNOWN);
+               ctl.filled = !dir_emit(ctx, qname.name, qname.len,
+                                    ino, DT_UNKNOWN);
                if (!ctl.filled)
-                       filp->f_pos += 1;
+                       ctx->pos += 1;
        }
        ctl.fpos += 1;
        ctl.idx  += 1;
@@ -683,10 +689,10 @@ end_advance:
 }
 
 static void
-ncp_read_volume_list(struct file *filp, void *dirent, filldir_t filldir,
+ncp_read_volume_list(struct file *file, struct dir_context *ctx,
                        struct ncp_cache_control *ctl)
 {
-       struct dentry *dentry = filp->f_path.dentry;
+       struct dentry *dentry = file->f_path.dentry;
        struct inode *inode = dentry->d_inode;
        struct ncp_server *server = NCP_SERVER(inode);
        struct ncp_volume_info info;
@@ -694,7 +700,7 @@ ncp_read_volume_list(struct file *filp, void *dirent, filldir_t filldir,
        int i;
 
        DPRINTK("ncp_read_volume_list: pos=%ld\n",
-                       (unsigned long) filp->f_pos);
+                       (unsigned long) ctx->pos);
 
        for (i = 0; i < NCP_NUMBER_OF_VOLUMES; i++) {
                int inval_dentry;
@@ -715,16 +721,16 @@ ncp_read_volume_list(struct file *filp, void *dirent, filldir_t filldir,
                }
                inval_dentry = ncp_update_known_namespace(server, entry.i.volNumber, NULL);
                entry.volume = entry.i.volNumber;
-               if (!ncp_fill_cache(filp, dirent, filldir, ctl, &entry, inval_dentry))
+               if (!ncp_fill_cache(file, ctx, ctl, &entry, inval_dentry))
                        return;
        }
 }
 
 static void
-ncp_do_readdir(struct file *filp, void *dirent, filldir_t filldir,
+ncp_do_readdir(struct file *file, struct dir_context *ctx,
                                                struct ncp_cache_control *ctl)
 {
-       struct dentry *dentry = filp->f_path.dentry;
+       struct dentry *dentry = file->f_path.dentry;
        struct inode *dir = dentry->d_inode;
        struct ncp_server *server = NCP_SERVER(dir);
        struct nw_search_sequence seq;
@@ -736,7 +742,7 @@ ncp_do_readdir(struct file *filp, void *dirent, filldir_t filldir,
 
        DPRINTK("ncp_do_readdir: %s/%s, fpos=%ld\n",
                dentry->d_parent->d_name.name, dentry->d_name.name,
-               (unsigned long) filp->f_pos);
+               (unsigned long) ctx->pos);
        PPRINTK("ncp_do_readdir: init %s, volnum=%d, dirent=%u\n",
                dentry->d_name.name, NCP_FINFO(dir)->volNumber,
                NCP_FINFO(dir)->dirEntNum);
@@ -778,7 +784,7 @@ ncp_do_readdir(struct file *filp, void *dirent, filldir_t filldir,
                        rpl += onerpl;
                        rpls -= onerpl;
                        entry.volume = entry.i.volNumber;
-                       if (!ncp_fill_cache(filp, dirent, filldir, ctl, &entry, 0))
+                       if (!ncp_fill_cache(file, ctx, ctl, &entry, 0))
                                break;
                }
        } while (more);
@@ -1029,15 +1035,6 @@ static int ncp_rmdir(struct inode *dir, struct dentry *dentry)
        DPRINTK("ncp_rmdir: removing %s/%s\n",
                dentry->d_parent->d_name.name, dentry->d_name.name);
 
-       /*
-        * fail with EBUSY if there are still references to this
-        * directory.
-        */
-       dentry_unhash(dentry);
-       error = -EBUSY;
-       if (!d_unhashed(dentry))
-               goto out;
-
        len = sizeof(__name);
        error = ncp_io2vol(server, __name, &len, dentry->d_name.name,
                           dentry->d_name.len, !ncp_preserve_case(dir));
@@ -1140,17 +1137,6 @@ static int ncp_rename(struct inode *old_dir, struct dentry *old_dentry,
                old_dentry->d_parent->d_name.name, old_dentry->d_name.name,
                new_dentry->d_parent->d_name.name, new_dentry->d_name.name);
 
-       if (new_dentry->d_inode && S_ISDIR(new_dentry->d_inode->i_mode)) {
-               /*
-                * fail with EBUSY if there are still references to this
-                * directory.
-                */
-               dentry_unhash(new_dentry);
-               error = -EBUSY;
-               if (!d_unhashed(new_dentry))
-                       goto out;
-       }
-
        ncp_age_dentry(server, old_dentry);
        ncp_age_dentry(server, new_dentry);