diff options
Diffstat (limited to 'fs/dcache.c')
| -rw-r--r-- | fs/dcache.c | 164 | 
1 files changed, 150 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 | */ | 
| 831 | struct dentry *d_instantiate_unique(struct dentry *entry, struct inode *inode) | 831 | static 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); | 
| 860 | do_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 | |||
| 865 | struct 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 | |||
| 867 | EXPORT_SYMBOL(d_instantiate_unique); | 885 | EXPORT_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 | ||
| 1256 | static 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 | ||
| 1245 | void d_rehash(struct dentry * entry) | 1268 | void 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 | */ | ||
| 1414 | static 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 | */ | ||
| 1450 | struct 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 | |||
| 1493 | found_lock: | ||
| 1494 | spin_lock(&actual->d_lock); | ||
| 1495 | found: | ||
| 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 */ | ||
| 1509 | connect_mountpoint: | ||
| 1510 | __d_materialise_dentry(dentry, alias); | ||
| 1511 | |||
| 1512 | /* Replace the candidate dentry with the alias in the tree */ | ||
| 1513 | replace_with_alias: | ||
| 1514 | __d_drop(alias); | ||
| 1515 | actual = alias; | ||
| 1516 | goto found; | ||
| 1517 | |||
| 1518 | shouldnt_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); | |||
| 1784 | EXPORT_SYMBOL(d_invalidate); | 1919 | EXPORT_SYMBOL(d_invalidate); | 
| 1785 | EXPORT_SYMBOL(d_lookup); | 1920 | EXPORT_SYMBOL(d_lookup); | 
| 1786 | EXPORT_SYMBOL(d_move); | 1921 | EXPORT_SYMBOL(d_move); | 
| 1922 | EXPORT_SYMBOL_GPL(d_materialise_unique); | ||
| 1787 | EXPORT_SYMBOL(d_path); | 1923 | EXPORT_SYMBOL(d_path); | 
| 1788 | EXPORT_SYMBOL(d_prune_aliases); | 1924 | EXPORT_SYMBOL(d_prune_aliases); | 
| 1789 | EXPORT_SYMBOL(d_rehash); | 1925 | EXPORT_SYMBOL(d_rehash); | 
