diff options
-rw-r--r-- | fs/dcache.c | 102 | ||||
-rw-r--r-- | include/linux/dcache.h | 1 |
2 files changed, 103 insertions, 0 deletions
diff --git a/fs/dcache.c b/fs/dcache.c index f2584d22cb45..101663d15e9f 100644 --- a/fs/dcache.c +++ b/fs/dcache.c | |||
@@ -1220,6 +1220,107 @@ struct dentry *d_splice_alias(struct inode *inode, struct dentry *dentry) | |||
1220 | return new; | 1220 | return new; |
1221 | } | 1221 | } |
1222 | 1222 | ||
1223 | /** | ||
1224 | * d_add_ci - lookup or allocate new dentry with case-exact name | ||
1225 | * @inode: the inode case-insensitive lookup has found | ||
1226 | * @dentry: the negative dentry that was passed to the parent's lookup func | ||
1227 | * @name: the case-exact name to be associated with the returned dentry | ||
1228 | * | ||
1229 | * This is to avoid filling the dcache with case-insensitive names to the | ||
1230 | * same inode, only the actual correct case is stored in the dcache for | ||
1231 | * case-insensitive filesystems. | ||
1232 | * | ||
1233 | * For a case-insensitive lookup match and if the the case-exact dentry | ||
1234 | * already exists in in the dcache, use it and return it. | ||
1235 | * | ||
1236 | * If no entry exists with the exact case name, allocate new dentry with | ||
1237 | * the exact case, and return the spliced entry. | ||
1238 | */ | ||
1239 | struct dentry *d_add_ci(struct inode *inode, struct dentry *dentry, | ||
1240 | struct qstr *name) | ||
1241 | { | ||
1242 | int error; | ||
1243 | struct dentry *found; | ||
1244 | struct dentry *new; | ||
1245 | |||
1246 | /* Does a dentry matching the name exist already? */ | ||
1247 | found = d_hash_and_lookup(dentry->d_parent, name); | ||
1248 | /* If not, create it now and return */ | ||
1249 | if (!found) { | ||
1250 | new = d_alloc(dentry->d_parent, name); | ||
1251 | if (!new) { | ||
1252 | error = -ENOMEM; | ||
1253 | goto err_out; | ||
1254 | } | ||
1255 | found = d_splice_alias(inode, new); | ||
1256 | if (found) { | ||
1257 | dput(new); | ||
1258 | return found; | ||
1259 | } | ||
1260 | return new; | ||
1261 | } | ||
1262 | /* Matching dentry exists, check if it is negative. */ | ||
1263 | if (found->d_inode) { | ||
1264 | if (unlikely(found->d_inode != inode)) { | ||
1265 | /* This can't happen because bad inodes are unhashed. */ | ||
1266 | BUG_ON(!is_bad_inode(inode)); | ||
1267 | BUG_ON(!is_bad_inode(found->d_inode)); | ||
1268 | } | ||
1269 | /* | ||
1270 | * Already have the inode and the dentry attached, decrement | ||
1271 | * the reference count to balance the iget() done | ||
1272 | * earlier on. We found the dentry using d_lookup() so it | ||
1273 | * cannot be disconnected and thus we do not need to worry | ||
1274 | * about any NFS/disconnectedness issues here. | ||
1275 | */ | ||
1276 | iput(inode); | ||
1277 | return found; | ||
1278 | } | ||
1279 | /* | ||
1280 | * Negative dentry: instantiate it unless the inode is a directory and | ||
1281 | * has a 'disconnected' dentry (i.e. IS_ROOT and DCACHE_DISCONNECTED), | ||
1282 | * in which case d_move() that in place of the found dentry. | ||
1283 | */ | ||
1284 | if (!S_ISDIR(inode->i_mode)) { | ||
1285 | /* Not a directory; everything is easy. */ | ||
1286 | d_instantiate(found, inode); | ||
1287 | return found; | ||
1288 | } | ||
1289 | spin_lock(&dcache_lock); | ||
1290 | if (list_empty(&inode->i_dentry)) { | ||
1291 | /* | ||
1292 | * Directory without a 'disconnected' dentry; we need to do | ||
1293 | * d_instantiate() by hand because it takes dcache_lock which | ||
1294 | * we already hold. | ||
1295 | */ | ||
1296 | list_add(&found->d_alias, &inode->i_dentry); | ||
1297 | found->d_inode = inode; | ||
1298 | spin_unlock(&dcache_lock); | ||
1299 | security_d_instantiate(found, inode); | ||
1300 | return found; | ||
1301 | } | ||
1302 | /* | ||
1303 | * Directory with a 'disconnected' dentry; get a reference to the | ||
1304 | * 'disconnected' dentry. | ||
1305 | */ | ||
1306 | new = list_entry(inode->i_dentry.next, struct dentry, d_alias); | ||
1307 | dget_locked(new); | ||
1308 | spin_unlock(&dcache_lock); | ||
1309 | /* Do security vodoo. */ | ||
1310 | security_d_instantiate(found, inode); | ||
1311 | /* Move new in place of found. */ | ||
1312 | d_move(new, found); | ||
1313 | /* Balance the iget() we did above. */ | ||
1314 | iput(inode); | ||
1315 | /* Throw away found. */ | ||
1316 | dput(found); | ||
1317 | /* Use new as the actual dentry. */ | ||
1318 | return new; | ||
1319 | |||
1320 | err_out: | ||
1321 | iput(inode); | ||
1322 | return ERR_PTR(error); | ||
1323 | } | ||
1223 | 1324 | ||
1224 | /** | 1325 | /** |
1225 | * d_lookup - search for a dentry | 1326 | * d_lookup - search for a dentry |
@@ -2254,6 +2355,7 @@ EXPORT_SYMBOL(d_path); | |||
2254 | EXPORT_SYMBOL(d_prune_aliases); | 2355 | EXPORT_SYMBOL(d_prune_aliases); |
2255 | EXPORT_SYMBOL(d_rehash); | 2356 | EXPORT_SYMBOL(d_rehash); |
2256 | EXPORT_SYMBOL(d_splice_alias); | 2357 | EXPORT_SYMBOL(d_splice_alias); |
2358 | EXPORT_SYMBOL(d_add_ci); | ||
2257 | EXPORT_SYMBOL(d_validate); | 2359 | EXPORT_SYMBOL(d_validate); |
2258 | EXPORT_SYMBOL(dget_locked); | 2360 | EXPORT_SYMBOL(dget_locked); |
2259 | EXPORT_SYMBOL(dput); | 2361 | EXPORT_SYMBOL(dput); |
diff --git a/include/linux/dcache.h b/include/linux/dcache.h index 98202c672fde..07aa198f19ed 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h | |||
@@ -230,6 +230,7 @@ extern void d_delete(struct dentry *); | |||
230 | extern struct dentry * d_alloc(struct dentry *, const struct qstr *); | 230 | extern struct dentry * d_alloc(struct dentry *, const struct qstr *); |
231 | extern struct dentry * d_alloc_anon(struct inode *); | 231 | extern struct dentry * d_alloc_anon(struct inode *); |
232 | extern struct dentry * d_splice_alias(struct inode *, struct dentry *); | 232 | extern struct dentry * d_splice_alias(struct inode *, struct dentry *); |
233 | extern struct dentry * d_add_ci(struct inode *, struct dentry *, struct qstr *); | ||
233 | extern void shrink_dcache_sb(struct super_block *); | 234 | extern void shrink_dcache_sb(struct super_block *); |
234 | extern void shrink_dcache_parent(struct dentry *); | 235 | extern void shrink_dcache_parent(struct dentry *); |
235 | extern void shrink_dcache_for_umount(struct super_block *); | 236 | extern void shrink_dcache_for_umount(struct super_block *); |