aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/dcache.c164
-rw-r--r--include/linux/dcache.h1
2 files changed, 151 insertions, 14 deletions
diff --git a/fs/dcache.c b/fs/dcache.c
index 1b4a3a34ec57..17b392a2049e 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -828,17 +828,19 @@ void d_instantiate(struct dentry *entry, struct inode * inode)
828 * (or otherwise set) by the caller to indicate that it is now 828 * (or otherwise set) by the caller to indicate that it is now
829 * in use by the dcache. 829 * in use by the dcache.
830 */ 830 */
831struct dentry *d_instantiate_unique(struct dentry *entry, struct inode *inode) 831static struct dentry *__d_instantiate_unique(struct dentry *entry,
832 struct inode *inode)
832{ 833{
833 struct dentry *alias; 834 struct dentry *alias;
834 int len = entry->d_name.len; 835 int len = entry->d_name.len;
835 const char *name = entry->d_name.name; 836 const char *name = entry->d_name.name;
836 unsigned int hash = entry->d_name.hash; 837 unsigned int hash = entry->d_name.hash;
837 838
838 BUG_ON(!list_empty(&entry->d_alias)); 839 if (!inode) {
839 spin_lock(&dcache_lock); 840 entry->d_inode = NULL;
840 if (!inode) 841 return NULL;
841 goto do_negative; 842 }
843
842 list_for_each_entry(alias, &inode->i_dentry, d_alias) { 844 list_for_each_entry(alias, &inode->i_dentry, d_alias) {
843 struct qstr *qstr = &alias->d_name; 845 struct qstr *qstr = &alias->d_name;
844 846
@@ -851,19 +853,35 @@ struct dentry *d_instantiate_unique(struct dentry *entry, struct inode *inode)
851 if (memcmp(qstr->name, name, len)) 853 if (memcmp(qstr->name, name, len))
852 continue; 854 continue;
853 dget_locked(alias); 855 dget_locked(alias);
854 spin_unlock(&dcache_lock);
855 BUG_ON(!d_unhashed(alias));
856 iput(inode);
857 return alias; 856 return alias;
858 } 857 }
858
859 list_add(&entry->d_alias, &inode->i_dentry); 859 list_add(&entry->d_alias, &inode->i_dentry);
860do_negative:
861 entry->d_inode = inode; 860 entry->d_inode = inode;
862 fsnotify_d_instantiate(entry, inode); 861 fsnotify_d_instantiate(entry, inode);
863 spin_unlock(&dcache_lock);
864 security_d_instantiate(entry, inode);
865 return NULL; 862 return NULL;
866} 863}
864
865struct dentry *d_instantiate_unique(struct dentry *entry, struct inode *inode)
866{
867 struct dentry *result;
868
869 BUG_ON(!list_empty(&entry->d_alias));
870
871 spin_lock(&dcache_lock);
872 result = __d_instantiate_unique(entry, inode);
873 spin_unlock(&dcache_lock);
874
875 if (!result) {
876 security_d_instantiate(entry, inode);
877 return NULL;
878 }
879
880 BUG_ON(!d_unhashed(result));
881 iput(inode);
882 return result;
883}
884
867EXPORT_SYMBOL(d_instantiate_unique); 885EXPORT_SYMBOL(d_instantiate_unique);
868 886
869/** 887/**
@@ -1235,6 +1253,11 @@ static void __d_rehash(struct dentry * entry, struct hlist_head *list)
1235 hlist_add_head_rcu(&entry->d_hash, list); 1253 hlist_add_head_rcu(&entry->d_hash, list);
1236} 1254}
1237 1255
1256static void _d_rehash(struct dentry * entry)
1257{
1258 __d_rehash(entry, d_hash(entry->d_parent, entry->d_name.hash));
1259}
1260
1238/** 1261/**
1239 * d_rehash - add an entry back to the hash 1262 * d_rehash - add an entry back to the hash
1240 * @entry: dentry to add to the hash 1263 * @entry: dentry to add to the hash
@@ -1244,11 +1267,9 @@ static void __d_rehash(struct dentry * entry, struct hlist_head *list)
1244 1267
1245void d_rehash(struct dentry * entry) 1268void d_rehash(struct dentry * entry)
1246{ 1269{
1247 struct hlist_head *list = d_hash(entry->d_parent, entry->d_name.hash);
1248
1249 spin_lock(&dcache_lock); 1270 spin_lock(&dcache_lock);
1250 spin_lock(&entry->d_lock); 1271 spin_lock(&entry->d_lock);
1251 __d_rehash(entry, list); 1272 _d_rehash(entry);
1252 spin_unlock(&entry->d_lock); 1273 spin_unlock(&entry->d_lock);
1253 spin_unlock(&dcache_lock); 1274 spin_unlock(&dcache_lock);
1254} 1275}
@@ -1386,6 +1407,120 @@ already_unhashed:
1386 spin_unlock(&dcache_lock); 1407 spin_unlock(&dcache_lock);
1387} 1408}
1388 1409
1410/*
1411 * Prepare an anonymous dentry for life in the superblock's dentry tree as a
1412 * named dentry in place of the dentry to be replaced.
1413 */
1414static void __d_materialise_dentry(struct dentry *dentry, struct dentry *anon)
1415{
1416 struct dentry *dparent, *aparent;
1417
1418 switch_names(dentry, anon);
1419 do_switch(dentry->d_name.len, anon->d_name.len);
1420 do_switch(dentry->d_name.hash, anon->d_name.hash);
1421
1422 dparent = dentry->d_parent;
1423 aparent = anon->d_parent;
1424
1425 dentry->d_parent = (aparent == anon) ? dentry : aparent;
1426 list_del(&dentry->d_u.d_child);
1427 if (!IS_ROOT(dentry))
1428 list_add(&dentry->d_u.d_child, &dentry->d_parent->d_subdirs);
1429 else
1430 INIT_LIST_HEAD(&dentry->d_u.d_child);
1431
1432 anon->d_parent = (dparent == dentry) ? anon : dparent;
1433 list_del(&anon->d_u.d_child);
1434 if (!IS_ROOT(anon))
1435 list_add(&anon->d_u.d_child, &anon->d_parent->d_subdirs);
1436 else
1437 INIT_LIST_HEAD(&anon->d_u.d_child);
1438
1439 anon->d_flags &= ~DCACHE_DISCONNECTED;
1440}
1441
1442/**
1443 * d_materialise_unique - introduce an inode into the tree
1444 * @dentry: candidate dentry
1445 * @inode: inode to bind to the dentry, to which aliases may be attached
1446 *
1447 * Introduces an dentry into the tree, substituting an extant disconnected
1448 * root directory alias in its place if there is one
1449 */
1450struct dentry *d_materialise_unique(struct dentry *dentry, struct inode *inode)
1451{
1452 struct dentry *alias, *actual;
1453
1454 BUG_ON(!d_unhashed(dentry));
1455
1456 spin_lock(&dcache_lock);
1457
1458 if (!inode) {
1459 actual = dentry;
1460 dentry->d_inode = NULL;
1461 goto found_lock;
1462 }
1463
1464 /* See if a disconnected directory already exists as an anonymous root
1465 * that we should splice into the tree instead */
1466 if (S_ISDIR(inode->i_mode) && (alias = __d_find_alias(inode, 1))) {
1467 spin_lock(&alias->d_lock);
1468
1469 /* Is this a mountpoint that we could splice into our tree? */
1470 if (IS_ROOT(alias))
1471 goto connect_mountpoint;
1472
1473 if (alias->d_name.len == dentry->d_name.len &&
1474 alias->d_parent == dentry->d_parent &&
1475 memcmp(alias->d_name.name,
1476 dentry->d_name.name,
1477 dentry->d_name.len) == 0)
1478 goto replace_with_alias;
1479
1480 spin_unlock(&alias->d_lock);
1481
1482 /* Doh! Seem to be aliasing directories for some reason... */
1483 dput(alias);
1484 }
1485
1486 /* Add a unique reference */
1487 actual = __d_instantiate_unique(dentry, inode);
1488 if (!actual)
1489 actual = dentry;
1490 else if (unlikely(!d_unhashed(actual)))
1491 goto shouldnt_be_hashed;
1492
1493found_lock:
1494 spin_lock(&actual->d_lock);
1495found:
1496 _d_rehash(actual);
1497 spin_unlock(&actual->d_lock);
1498 spin_unlock(&dcache_lock);
1499
1500 if (actual == dentry) {
1501 security_d_instantiate(dentry, inode);
1502 return NULL;
1503 }
1504
1505 iput(inode);
1506 return actual;
1507
1508 /* Convert the anonymous/root alias into an ordinary dentry */
1509connect_mountpoint:
1510 __d_materialise_dentry(dentry, alias);
1511
1512 /* Replace the candidate dentry with the alias in the tree */
1513replace_with_alias:
1514 __d_drop(alias);
1515 actual = alias;
1516 goto found;
1517
1518shouldnt_be_hashed:
1519 spin_unlock(&dcache_lock);
1520 BUG();
1521 goto shouldnt_be_hashed;
1522}
1523
1389/** 1524/**
1390 * d_path - return the path of a dentry 1525 * d_path - return the path of a dentry
1391 * @dentry: dentry to report 1526 * @dentry: dentry to report
@@ -1784,6 +1919,7 @@ EXPORT_SYMBOL(d_instantiate);
1784EXPORT_SYMBOL(d_invalidate); 1919EXPORT_SYMBOL(d_invalidate);
1785EXPORT_SYMBOL(d_lookup); 1920EXPORT_SYMBOL(d_lookup);
1786EXPORT_SYMBOL(d_move); 1921EXPORT_SYMBOL(d_move);
1922EXPORT_SYMBOL_GPL(d_materialise_unique);
1787EXPORT_SYMBOL(d_path); 1923EXPORT_SYMBOL(d_path);
1788EXPORT_SYMBOL(d_prune_aliases); 1924EXPORT_SYMBOL(d_prune_aliases);
1789EXPORT_SYMBOL(d_rehash); 1925EXPORT_SYMBOL(d_rehash);
diff --git a/include/linux/dcache.h b/include/linux/dcache.h
index 471781ffeab1..44605be59409 100644
--- a/include/linux/dcache.h
+++ b/include/linux/dcache.h
@@ -221,6 +221,7 @@ static inline int dname_external(struct dentry *dentry)
221 */ 221 */
222extern void d_instantiate(struct dentry *, struct inode *); 222extern void d_instantiate(struct dentry *, struct inode *);
223extern struct dentry * d_instantiate_unique(struct dentry *, struct inode *); 223extern struct dentry * d_instantiate_unique(struct dentry *, struct inode *);
224extern struct dentry * d_materialise_unique(struct dentry *, struct inode *);
224extern void d_delete(struct dentry *); 225extern void d_delete(struct dentry *);
225 226
226/* allocate/de-allocate */ 227/* allocate/de-allocate */