fs: name case update method
Nick Piggin [Fri, 7 Jan 2011 06:49:26 +0000 (17:49 +1100)]
smpfs and ncpfs want to update a live dentry name in-place. Rather than
have them open code the locking, provide a documented dcache API.

Signed-off-by: Nick Piggin <npiggin@kernel.dk>

drivers/staging/smbfs/cache.c
fs/dcache.c
fs/ncpfs/dir.c
include/linux/dcache.h

index dbb9865..dbd2e1d 100644 (file)
@@ -145,8 +145,8 @@ smb_fill_cache(struct file *filp, void *dirent, filldir_t filldir,
                        goto end_advance;
        } else {
                hashed = 1;
-               memcpy((char *) newdent->d_name.name, qname->name,
-                      newdent->d_name.len);
+               /* dir i_mutex is locked because we're in readdir */
+               dentry_update_name_case(newdent, qname);
        }
 
        if (!newdent->d_inode) {
index 6ee6bc4..814e5f4 100644 (file)
@@ -1589,6 +1589,33 @@ void d_rehash(struct dentry * entry)
 }
 EXPORT_SYMBOL(d_rehash);
 
+/**
+ * dentry_update_name_case - update case insensitive dentry with a new name
+ * @dentry: dentry to be updated
+ * @name: new name
+ *
+ * Update a case insensitive dentry with new case of name.
+ *
+ * dentry must have been returned by d_lookup with name @name. Old and new
+ * name lengths must match (ie. no d_compare which allows mismatched name
+ * lengths).
+ *
+ * Parent inode i_mutex must be held over d_lookup and into this call (to
+ * keep renames and concurrent inserts, and readdir(2) away).
+ */
+void dentry_update_name_case(struct dentry *dentry, struct qstr *name)
+{
+       BUG_ON(!mutex_is_locked(&dentry->d_inode->i_mutex));
+       BUG_ON(dentry->d_name.len != name->len); /* d_lookup gives this */
+
+       spin_lock(&dcache_lock);
+       spin_lock(&dentry->d_lock);
+       memcpy((unsigned char *)dentry->d_name.name, name->name, name->len);
+       spin_unlock(&dentry->d_lock);
+       spin_unlock(&dcache_lock);
+}
+EXPORT_SYMBOL(dentry_update_name_case);
+
 /*
  * When switching names, the actual string doesn't strictly have to
  * be preserved in the target - because we're dropping the target
index d6e6453..e80ea4e 100644 (file)
@@ -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) {
@@ -657,7 +634,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);
        }
index cbfc956..6cdf499 100644 (file)
@@ -290,6 +290,8 @@ static inline struct dentry *d_add_unique(struct dentry *entry, struct inode *in
        return res;
 }
 
+extern void dentry_update_name_case(struct dentry *, struct qstr *);
+
 /* used for rename() and baskets */
 extern void d_move(struct dentry *, struct dentry *);
 extern struct dentry *d_ancestor(struct dentry *, struct dentry *);