]> nv-tegra.nvidia Code Review - linux-3.10.git/blobdiff - fs/ncpfs/dir.c
Merge remote-tracking branch 'origin/dev/sumit-linux-3.10.96' into TOT-merge
[linux-3.10.git] / fs / ncpfs / dir.c
index d6e6453881ceab20ab143664292e548aafa0e1ce..c578ba9949e67053fa7127f165a674b750b7b1a5 100644 (file)
 #include <linux/kernel.h>
 #include <linux/vmalloc.h>
 #include <linux/mm.h>
+#include <linux/namei.h>
 #include <asm/uaccess.h>
 #include <asm/byteorder.h>
 
-#include <linux/ncp_fs.h>
-
-#include "ncplib_kernel.h"
+#include "ncp_fs.h"
 
 static void ncp_read_volume_list(struct file *, void *, filldir_t,
                                struct ncp_cache_control *);
@@ -31,15 +30,15 @@ static void ncp_do_readdir(struct file *, void *, filldir_t,
 
 static int ncp_readdir(struct file *, void *, filldir_t);
 
-static int ncp_create(struct inode *, struct dentry *, int, struct nameidata *);
-static struct dentry *ncp_lookup(struct inode *, struct dentry *, struct nameidata *);
+static int ncp_create(struct inode *, struct dentry *, umode_t, bool);
+static struct dentry *ncp_lookup(struct inode *, struct dentry *, unsigned int);
 static int ncp_unlink(struct inode *, struct dentry *);
-static int ncp_mkdir(struct inode *, struct dentry *, int);
+static int ncp_mkdir(struct inode *, struct dentry *, umode_t);
 static int ncp_rmdir(struct inode *, struct dentry *);
 static int ncp_rename(struct inode *, struct dentry *,
                      struct inode *, struct dentry *);
 static int ncp_mknod(struct inode * dir, struct dentry *dentry,
-                    int mode, dev_t rdev);
+                    umode_t mode, dev_t rdev);
 #if defined(CONFIG_NCPFS_EXTRAS) || defined(CONFIG_NCPFS_NFS_NS)
 extern int ncp_symlink(struct inode *, struct dentry *, const char *);
 #else
@@ -73,12 +72,15 @@ const struct inode_operations ncp_dir_inode_operations =
 /*
  * Dentry operations routines
  */
-static int ncp_lookup_validate(struct dentry *, struct nameidata *);
-static int ncp_hash_dentry(struct dentry *, struct qstr *);
-static int ncp_compare_dentry (struct dentry *, struct qstr *, struct qstr *);
+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 *,
+               unsigned int, const char *, const struct qstr *);
 static int ncp_delete_dentry(const struct dentry *);
 
-static const struct dentry_operations ncp_dentry_operations =
+const struct dentry_operations ncp_dentry_operations =
 {
        .d_revalidate   = ncp_lookup_validate,
        .d_hash         = ncp_hash_dentry,
@@ -86,14 +88,6 @@ static const struct dentry_operations ncp_dentry_operations =
        .d_delete       = ncp_delete_dentry,
 };
 
-const struct dentry_operations ncp_root_dentry_operations =
-{
-       .d_hash         = ncp_hash_dentry,
-       .d_compare      = ncp_compare_dentry,
-       .d_delete       = ncp_delete_dentry,
-};
-
-
 #define ncp_namespace(i)       (NCP_SERVER(i)->name_space[NCP_FINFO(i)->volNumber])
 
 static inline int ncp_preserve_entry_case(struct inode *i, __u32 nscreator)
@@ -113,10 +107,10 @@ static inline int ncp_preserve_entry_case(struct inode *i, __u32 nscreator)
 
 #define ncp_preserve_case(i)   (ncp_namespace(i) != NW_NS_DOS)
 
-static inline int ncp_case_sensitive(struct dentry *dentry)
+static inline int ncp_case_sensitive(const struct inode *i)
 {
 #ifdef CONFIG_NCPFS_NFS_NS
-       return ncp_namespace(dentry->d_inode) == NW_NS_NFS;
+       return ncp_namespace(i) == NW_NS_NFS;
 #else
        return 0;
 #endif /* CONFIG_NCPFS_NFS_NS */
@@ -127,14 +121,16 @@ static inline int ncp_case_sensitive(struct dentry *dentry)
  * is case-sensitive.
  */
 static int 
-ncp_hash_dentry(struct dentry *dentry, struct qstr *this)
+ncp_hash_dentry(const struct dentry *dentry, const struct inode *inode,
+               struct qstr *this)
 {
-       if (!ncp_case_sensitive(dentry)) {
+       if (!ncp_case_sensitive(inode)) {
+               struct super_block *sb = dentry->d_sb;
                struct nls_table *t;
                unsigned long hash;
                int i;
 
-               t = NCP_IO_TABLE(dentry);
+               t = NCP_IO_TABLE(sb);
                hash = init_name_hash();
                for (i=0; i<this->len ; i++)
                        hash = partial_name_hash(ncp_tolower(t, this->name[i]),
@@ -145,15 +141,17 @@ ncp_hash_dentry(struct dentry *dentry, struct qstr *this)
 }
 
 static int
-ncp_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b)
+ncp_compare_dentry(const struct dentry *parent, const struct inode *pinode,
+               const struct dentry *dentry, const struct inode *inode,
+               unsigned int len, const char *str, const struct qstr *name)
 {
-       if (a->len != b->len)
+       if (len != name->len)
                return 1;
 
-       if (ncp_case_sensitive(dentry))
-               return strncmp(a->name, b->name, a->len);
+       if (ncp_case_sensitive(pinode))
+               return strncmp(str, name->name, len);
 
-       return ncp_strnicmp(NCP_IO_TABLE(dentry), a->name, b->name, a->len);
+       return ncp_strnicmp(NCP_IO_TABLE(pinode->i_sb), str, name->name, len);
 }
 
 /*
@@ -292,7 +290,7 @@ leave_me:;
 
 
 static int
-ncp_lookup_validate(struct dentry *dentry, struct nameidata *nd)
+ncp_lookup_validate(struct dentry *dentry, unsigned int flags)
 {
        struct ncp_server *server;
        struct dentry *parent;
@@ -301,6 +299,12 @@ ncp_lookup_validate(struct dentry *dentry, struct nameidata *nd)
        int res, val = 0, len;
        __u8 __name[NCP_MAXPATHLEN + 1];
 
+       if (dentry == dentry->d_sb->s_root)
+               return 1;
+
+       if (flags & LOOKUP_RCU)
+               return -ECHILD;
+
        parent = dget_parent(dentry);
        dir = parent->d_inode;
 
@@ -384,21 +388,21 @@ ncp_dget_fpos(struct dentry *dentry, struct dentry *parent, unsigned long fpos)
        }
 
        /* If a pointer is invalid, we search the dentry. */
-       spin_lock(&dcache_lock);
+       spin_lock(&parent->d_lock);
        next = parent->d_subdirs.next;
        while (next != &parent->d_subdirs) {
-               dent = list_entry(next, struct dentry, d_u.d_child);
+               dent = list_entry(next, struct dentry, d_child);
                if ((unsigned long)dent->d_fsdata == fpos) {
                        if (dent->d_inode)
-                               dget_locked(dent);
+                               dget(dent);
                        else
                                dent = NULL;
-                       spin_unlock(&dcache_lock);
+                       spin_unlock(&parent->d_lock);
                        goto out;
                }
                next = next->next;
        }
-       spin_unlock(&dcache_lock);
+       spin_unlock(&parent->d_lock);
        return NULL;
 
 out:
@@ -589,14 +593,10 @@ ncp_fill_cache(struct file *filp, void *dirent, filldir_t filldir,
                return 1; /* I'm not sure */
 
        qname.name = __name;
-       qname.hash = full_name_hash(qname.name, qname.len);
-
-       if (dentry->d_op && dentry->d_op->d_hash)
-               if (dentry->d_op->d_hash(dentry, &qname) != 0)
-                       goto end_advance;
-
-       newdent = d_lookup(dentry, &qname);
 
+       newdent = d_hash_and_lookup(dentry, &qname);
+       if (unlikely(IS_ERR(newdent)))
+               goto end_advance;
        if (!newdent) {
                newdent = d_alloc(dentry, &qname);
                if (!newdent)
@@ -611,35 +611,12 @@ ncp_fill_cache(struct file *filp, void *dirent, filldir_t filldir,
                        shrink_dcache_parent(newdent);
 
                /*
-                * It is not as dangerous as it looks.  NetWare's OS2 namespace is
-                * case preserving yet case insensitive.  So we update dentry's name
-                * as received from server.  We found dentry via d_lookup with our
-                * hash, so we know that hash does not change, and so replacing name
-                * should be reasonably safe.
+                * NetWare's OS2 namespace is case preserving yet case
+                * insensitive.  So we update dentry's name as received from
+                * server. Parent dir's i_mutex is locked because we're in
+                * readdir.
                 */
-               if (qname.len == newdent->d_name.len &&
-                   memcmp(newdent->d_name.name, qname.name, newdent->d_name.len)) {
-                       struct inode *inode = newdent->d_inode;
-
-                       /*
-                        * Inside ncpfs all uses of d_name are either for debugging,
-                        * or on functions which acquire inode mutex (mknod, creat,
-                        * lookup).  So grab i_mutex here, to be sure.  d_path
-                        * uses dcache_lock when generating path, so we should too.
-                        * And finally d_compare is protected by dentry's d_lock, so
-                        * here we go.
-                        */
-                       if (inode)
-                               mutex_lock(&inode->i_mutex);
-                       spin_lock(&dcache_lock);
-                       spin_lock(&newdent->d_lock);
-                       memcpy((char *) newdent->d_name.name, qname.name,
-                                                               newdent->d_name.len);
-                       spin_unlock(&newdent->d_lock);
-                       spin_unlock(&dcache_lock);
-                       if (inode)
-                               mutex_unlock(&inode->i_mutex);
-               }
+               dentry_update_name_case(newdent, &qname);
        }
 
        if (!newdent->d_inode) {
@@ -649,7 +626,6 @@ ncp_fill_cache(struct file *filp, void *dirent, filldir_t filldir,
                entry->ino = iunique(dir->i_sb, 2);
                inode = ncp_iget(dir->i_sb, entry);
                if (inode) {
-                       newdent->d_op = &ncp_dentry_operations;
                        d_instantiate(newdent, inode);
                        if (!hashed)
                                d_rehash(newdent);
@@ -657,7 +633,7 @@ ncp_fill_cache(struct file *filp, void *dirent, filldir_t filldir,
        } else {
                struct inode *inode = newdent->d_inode;
 
-               mutex_lock(&inode->i_mutex);
+               mutex_lock_nested(&inode->i_mutex, I_MUTEX_CHILD);
                ncp_update_inode2(inode, entry);
                mutex_unlock(&inode->i_mutex);
        }
@@ -856,7 +832,7 @@ out:
        return result;
 }
 
-static struct dentry *ncp_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
+static struct dentry *ncp_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags)
 {
        struct ncp_server *server = NCP_SERVER(dir);
        struct inode *inode = NULL;
@@ -905,7 +881,6 @@ static struct dentry *ncp_lookup(struct inode *dir, struct dentry *dentry, struc
        if (inode) {
                ncp_new_dentry(dentry);
 add_entry:
-               dentry->d_op = &ncp_dentry_operations;
                d_add(dentry, inode);
                error = 0;
        }
@@ -940,7 +915,7 @@ out_close:
        goto out;
 }
 
-int ncp_create_new(struct inode *dir, struct dentry *dentry, int mode,
+int ncp_create_new(struct inode *dir, struct dentry *dentry, umode_t mode,
                   dev_t rdev, __le32 attributes)
 {
        struct ncp_server *server = NCP_SERVER(dir);
@@ -949,7 +924,7 @@ int ncp_create_new(struct inode *dir, struct dentry *dentry, int mode,
        int opmode;
        __u8 __name[NCP_MAXPATHLEN + 1];
        
-       PPRINTK("ncp_create_new: creating %s/%s, mode=%x\n",
+       PPRINTK("ncp_create_new: creating %s/%s, mode=%hx\n",
                dentry->d_parent->d_name.name, dentry->d_name.name, mode);
 
        ncp_age_dentry(server, dentry);
@@ -1000,13 +975,13 @@ out:
        return error;
 }
 
-static int ncp_create(struct inode *dir, struct dentry *dentry, int mode,
-               struct nameidata *nd)
+static int ncp_create(struct inode *dir, struct dentry *dentry, umode_t mode,
+               bool excl)
 {
        return ncp_create_new(dir, dentry, mode, 0, 0);
 }
 
-static int ncp_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+static int ncp_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
 {
        struct ncp_entry_info finfo;
        struct ncp_server *server = NCP_SERVER(dir);
@@ -1054,10 +1029,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);
 
-       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));
@@ -1160,6 +1131,17 @@ 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);
 
@@ -1206,12 +1188,12 @@ out:
 }
 
 static int ncp_mknod(struct inode * dir, struct dentry *dentry,
-                    int mode, dev_t rdev)
+                    umode_t mode, dev_t rdev)
 {
        if (!new_valid_dev(rdev))
                return -EINVAL;
        if (ncp_is_nfs_extras(NCP_SERVER(dir), NCP_FINFO(dir)->volNumber)) {
-               DPRINTK(KERN_DEBUG "ncp_mknod: mode = 0%o\n", mode);
+               DPRINTK(KERN_DEBUG "ncp_mknod: mode = 0%ho\n", mode);
                return ncp_create_new(dir, dentry, mode, rdev, 0);
        }
        return -EPERM; /* Strange, but true */