diff options
author | Nick Piggin <npiggin@kernel.dk> | 2011-01-07 01:49:26 -0500 |
---|---|---|
committer | Nick Piggin <npiggin@kernel.dk> | 2011-01-07 01:50:19 -0500 |
commit | fb2d5b86aff355a27ebfc132d3c99f4a940cc3fe (patch) | |
tree | 7fed12adf54473131e8b86c0c302c443b1d6a846 | |
parent | 2bc334dcc7c77be3700dd443d92a78603f76976b (diff) |
fs: name case update method
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>
-rw-r--r-- | drivers/staging/smbfs/cache.c | 4 | ||||
-rw-r--r-- | fs/dcache.c | 27 | ||||
-rw-r--r-- | fs/ncpfs/dir.c | 35 | ||||
-rw-r--r-- | include/linux/dcache.h | 2 |
4 files changed, 37 insertions, 31 deletions
diff --git a/drivers/staging/smbfs/cache.c b/drivers/staging/smbfs/cache.c index dbb98658148b..dbd2e1df3ba9 100644 --- a/drivers/staging/smbfs/cache.c +++ b/drivers/staging/smbfs/cache.c | |||
@@ -145,8 +145,8 @@ smb_fill_cache(struct file *filp, void *dirent, filldir_t filldir, | |||
145 | goto end_advance; | 145 | goto end_advance; |
146 | } else { | 146 | } else { |
147 | hashed = 1; | 147 | hashed = 1; |
148 | memcpy((char *) newdent->d_name.name, qname->name, | 148 | /* dir i_mutex is locked because we're in readdir */ |
149 | newdent->d_name.len); | 149 | dentry_update_name_case(newdent, qname); |
150 | } | 150 | } |
151 | 151 | ||
152 | if (!newdent->d_inode) { | 152 | if (!newdent->d_inode) { |
diff --git a/fs/dcache.c b/fs/dcache.c index 6ee6bc40cb63..814e5f491e9c 100644 --- a/fs/dcache.c +++ b/fs/dcache.c | |||
@@ -1589,6 +1589,33 @@ void d_rehash(struct dentry * entry) | |||
1589 | } | 1589 | } |
1590 | EXPORT_SYMBOL(d_rehash); | 1590 | EXPORT_SYMBOL(d_rehash); |
1591 | 1591 | ||
1592 | /** | ||
1593 | * dentry_update_name_case - update case insensitive dentry with a new name | ||
1594 | * @dentry: dentry to be updated | ||
1595 | * @name: new name | ||
1596 | * | ||
1597 | * Update a case insensitive dentry with new case of name. | ||
1598 | * | ||
1599 | * dentry must have been returned by d_lookup with name @name. Old and new | ||
1600 | * name lengths must match (ie. no d_compare which allows mismatched name | ||
1601 | * lengths). | ||
1602 | * | ||
1603 | * Parent inode i_mutex must be held over d_lookup and into this call (to | ||
1604 | * keep renames and concurrent inserts, and readdir(2) away). | ||
1605 | */ | ||
1606 | void dentry_update_name_case(struct dentry *dentry, struct qstr *name) | ||
1607 | { | ||
1608 | BUG_ON(!mutex_is_locked(&dentry->d_inode->i_mutex)); | ||
1609 | BUG_ON(dentry->d_name.len != name->len); /* d_lookup gives this */ | ||
1610 | |||
1611 | spin_lock(&dcache_lock); | ||
1612 | spin_lock(&dentry->d_lock); | ||
1613 | memcpy((unsigned char *)dentry->d_name.name, name->name, name->len); | ||
1614 | spin_unlock(&dentry->d_lock); | ||
1615 | spin_unlock(&dcache_lock); | ||
1616 | } | ||
1617 | EXPORT_SYMBOL(dentry_update_name_case); | ||
1618 | |||
1592 | /* | 1619 | /* |
1593 | * When switching names, the actual string doesn't strictly have to | 1620 | * When switching names, the actual string doesn't strictly have to |
1594 | * be preserved in the target - because we're dropping the target | 1621 | * be preserved in the target - because we're dropping the target |
diff --git a/fs/ncpfs/dir.c b/fs/ncpfs/dir.c index d6e6453881ce..e80ea4e37c48 100644 --- a/fs/ncpfs/dir.c +++ b/fs/ncpfs/dir.c | |||
@@ -611,35 +611,12 @@ ncp_fill_cache(struct file *filp, void *dirent, filldir_t filldir, | |||
611 | shrink_dcache_parent(newdent); | 611 | shrink_dcache_parent(newdent); |
612 | 612 | ||
613 | /* | 613 | /* |
614 | * It is not as dangerous as it looks. NetWare's OS2 namespace is | 614 | * NetWare's OS2 namespace is case preserving yet case |
615 | * case preserving yet case insensitive. So we update dentry's name | 615 | * insensitive. So we update dentry's name as received from |
616 | * as received from server. We found dentry via d_lookup with our | 616 | * server. Parent dir's i_mutex is locked because we're in |
617 | * hash, so we know that hash does not change, and so replacing name | 617 | * readdir. |
618 | * should be reasonably safe. | ||
619 | */ | 618 | */ |
620 | if (qname.len == newdent->d_name.len && | 619 | dentry_update_name_case(newdent, &qname); |
621 | memcmp(newdent->d_name.name, qname.name, newdent->d_name.len)) { | ||
622 | struct inode *inode = newdent->d_inode; | ||
623 | |||
624 | /* | ||
625 | * Inside ncpfs all uses of d_name are either for debugging, | ||
626 | * or on functions which acquire inode mutex (mknod, creat, | ||
627 | * lookup). So grab i_mutex here, to be sure. d_path | ||
628 | * uses dcache_lock when generating path, so we should too. | ||
629 | * And finally d_compare is protected by dentry's d_lock, so | ||
630 | * here we go. | ||
631 | */ | ||
632 | if (inode) | ||
633 | mutex_lock(&inode->i_mutex); | ||
634 | spin_lock(&dcache_lock); | ||
635 | spin_lock(&newdent->d_lock); | ||
636 | memcpy((char *) newdent->d_name.name, qname.name, | ||
637 | newdent->d_name.len); | ||
638 | spin_unlock(&newdent->d_lock); | ||
639 | spin_unlock(&dcache_lock); | ||
640 | if (inode) | ||
641 | mutex_unlock(&inode->i_mutex); | ||
642 | } | ||
643 | } | 620 | } |
644 | 621 | ||
645 | if (!newdent->d_inode) { | 622 | if (!newdent->d_inode) { |
@@ -657,7 +634,7 @@ ncp_fill_cache(struct file *filp, void *dirent, filldir_t filldir, | |||
657 | } else { | 634 | } else { |
658 | struct inode *inode = newdent->d_inode; | 635 | struct inode *inode = newdent->d_inode; |
659 | 636 | ||
660 | mutex_lock(&inode->i_mutex); | 637 | mutex_lock_nested(&inode->i_mutex, I_MUTEX_CHILD); |
661 | ncp_update_inode2(inode, entry); | 638 | ncp_update_inode2(inode, entry); |
662 | mutex_unlock(&inode->i_mutex); | 639 | mutex_unlock(&inode->i_mutex); |
663 | } | 640 | } |
diff --git a/include/linux/dcache.h b/include/linux/dcache.h index cbfc9567e4e9..6cdf4995c90a 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h | |||
@@ -290,6 +290,8 @@ static inline struct dentry *d_add_unique(struct dentry *entry, struct inode *in | |||
290 | return res; | 290 | return res; |
291 | } | 291 | } |
292 | 292 | ||
293 | extern void dentry_update_name_case(struct dentry *, struct qstr *); | ||
294 | |||
293 | /* used for rename() and baskets */ | 295 | /* used for rename() and baskets */ |
294 | extern void d_move(struct dentry *, struct dentry *); | 296 | extern void d_move(struct dentry *, struct dentry *); |
295 | extern struct dentry *d_ancestor(struct dentry *, struct dentry *); | 297 | extern struct dentry *d_ancestor(struct dentry *, struct dentry *); |