11da177e4SLinus Torvalds /* dir.c: AFS filesystem directory handling 21da177e4SLinus Torvalds * 31da177e4SLinus Torvalds * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. 41da177e4SLinus Torvalds * Written by David Howells (dhowells@redhat.com) 51da177e4SLinus Torvalds * 61da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or 71da177e4SLinus Torvalds * modify it under the terms of the GNU General Public License 81da177e4SLinus Torvalds * as published by the Free Software Foundation; either version 91da177e4SLinus Torvalds * 2 of the License, or (at your option) any later version. 101da177e4SLinus Torvalds */ 111da177e4SLinus Torvalds 121da177e4SLinus Torvalds #include <linux/kernel.h> 131da177e4SLinus Torvalds #include <linux/module.h> 141da177e4SLinus Torvalds #include <linux/init.h> 151da177e4SLinus Torvalds #include <linux/slab.h> 161da177e4SLinus Torvalds #include <linux/fs.h> 171da177e4SLinus Torvalds #include <linux/pagemap.h> 1800d3b7a4SDavid Howells #include <linux/ctype.h> 191da177e4SLinus Torvalds #include "internal.h" 201da177e4SLinus Torvalds 211da177e4SLinus Torvalds static struct dentry *afs_dir_lookup(struct inode *dir, struct dentry *dentry, 221da177e4SLinus Torvalds struct nameidata *nd); 231da177e4SLinus Torvalds static int afs_dir_open(struct inode *inode, struct file *file); 241da177e4SLinus Torvalds static int afs_dir_readdir(struct file *file, void *dirent, filldir_t filldir); 251da177e4SLinus Torvalds static int afs_d_revalidate(struct dentry *dentry, struct nameidata *nd); 261da177e4SLinus Torvalds static int afs_d_delete(struct dentry *dentry); 271da177e4SLinus Torvalds static int afs_dir_lookup_filldir(void *_cookie, const char *name, int nlen, 28afefdbb2SDavid Howells loff_t fpos, u64 ino, unsigned dtype); 291da177e4SLinus Torvalds 304b6f5d20SArjan van de Ven const struct file_operations afs_dir_file_operations = { 311da177e4SLinus Torvalds .open = afs_dir_open, 3200d3b7a4SDavid Howells .release = afs_release, 331da177e4SLinus Torvalds .readdir = afs_dir_readdir, 341da177e4SLinus Torvalds }; 351da177e4SLinus Torvalds 36754661f1SArjan van de Ven const struct inode_operations afs_dir_inode_operations = { 371da177e4SLinus Torvalds .lookup = afs_dir_lookup, 3800d3b7a4SDavid Howells .permission = afs_permission, 391da177e4SLinus Torvalds .getattr = afs_inode_getattr, 401da177e4SLinus Torvalds #if 0 /* TODO */ 411da177e4SLinus Torvalds .create = afs_dir_create, 421da177e4SLinus Torvalds .link = afs_dir_link, 431da177e4SLinus Torvalds .unlink = afs_dir_unlink, 441da177e4SLinus Torvalds .symlink = afs_dir_symlink, 451da177e4SLinus Torvalds .mkdir = afs_dir_mkdir, 461da177e4SLinus Torvalds .rmdir = afs_dir_rmdir, 471da177e4SLinus Torvalds .mknod = afs_dir_mknod, 481da177e4SLinus Torvalds .rename = afs_dir_rename, 491da177e4SLinus Torvalds #endif 501da177e4SLinus Torvalds }; 511da177e4SLinus Torvalds 521da177e4SLinus Torvalds static struct dentry_operations afs_fs_dentry_operations = { 531da177e4SLinus Torvalds .d_revalidate = afs_d_revalidate, 541da177e4SLinus Torvalds .d_delete = afs_d_delete, 551da177e4SLinus Torvalds }; 561da177e4SLinus Torvalds 571da177e4SLinus Torvalds #define AFS_DIR_HASHTBL_SIZE 128 581da177e4SLinus Torvalds #define AFS_DIR_DIRENT_SIZE 32 591da177e4SLinus Torvalds #define AFS_DIRENT_PER_BLOCK 64 601da177e4SLinus Torvalds 611da177e4SLinus Torvalds union afs_dirent { 621da177e4SLinus Torvalds struct { 631da177e4SLinus Torvalds uint8_t valid; 641da177e4SLinus Torvalds uint8_t unused[1]; 651da177e4SLinus Torvalds __be16 hash_next; 661da177e4SLinus Torvalds __be32 vnode; 671da177e4SLinus Torvalds __be32 unique; 681da177e4SLinus Torvalds uint8_t name[16]; 691da177e4SLinus Torvalds uint8_t overflow[4]; /* if any char of the name (inc 701da177e4SLinus Torvalds * NUL) reaches here, consume 711da177e4SLinus Torvalds * the next dirent too */ 721da177e4SLinus Torvalds } u; 731da177e4SLinus Torvalds uint8_t extended_name[32]; 741da177e4SLinus Torvalds }; 751da177e4SLinus Torvalds 761da177e4SLinus Torvalds /* AFS directory page header (one at the beginning of every 2048-byte chunk) */ 771da177e4SLinus Torvalds struct afs_dir_pagehdr { 781da177e4SLinus Torvalds __be16 npages; 791da177e4SLinus Torvalds __be16 magic; 801da177e4SLinus Torvalds #define AFS_DIR_MAGIC htons(1234) 811da177e4SLinus Torvalds uint8_t nentries; 821da177e4SLinus Torvalds uint8_t bitmap[8]; 831da177e4SLinus Torvalds uint8_t pad[19]; 841da177e4SLinus Torvalds }; 851da177e4SLinus Torvalds 861da177e4SLinus Torvalds /* directory block layout */ 871da177e4SLinus Torvalds union afs_dir_block { 881da177e4SLinus Torvalds 891da177e4SLinus Torvalds struct afs_dir_pagehdr pagehdr; 901da177e4SLinus Torvalds 911da177e4SLinus Torvalds struct { 921da177e4SLinus Torvalds struct afs_dir_pagehdr pagehdr; 931da177e4SLinus Torvalds uint8_t alloc_ctrs[128]; 941da177e4SLinus Torvalds /* dir hash table */ 951da177e4SLinus Torvalds uint16_t hashtable[AFS_DIR_HASHTBL_SIZE]; 961da177e4SLinus Torvalds } hdr; 971da177e4SLinus Torvalds 981da177e4SLinus Torvalds union afs_dirent dirents[AFS_DIRENT_PER_BLOCK]; 991da177e4SLinus Torvalds }; 1001da177e4SLinus Torvalds 1011da177e4SLinus Torvalds /* layout on a linux VM page */ 1021da177e4SLinus Torvalds struct afs_dir_page { 1031da177e4SLinus Torvalds union afs_dir_block blocks[PAGE_SIZE / sizeof(union afs_dir_block)]; 1041da177e4SLinus Torvalds }; 1051da177e4SLinus Torvalds 1061da177e4SLinus Torvalds struct afs_dir_lookup_cookie { 1071da177e4SLinus Torvalds struct afs_fid fid; 1081da177e4SLinus Torvalds const char *name; 1091da177e4SLinus Torvalds size_t nlen; 1101da177e4SLinus Torvalds int found; 1111da177e4SLinus Torvalds }; 1121da177e4SLinus Torvalds 1131da177e4SLinus Torvalds /* 1141da177e4SLinus Torvalds * check that a directory page is valid 1151da177e4SLinus Torvalds */ 1161da177e4SLinus Torvalds static inline void afs_dir_check_page(struct inode *dir, struct page *page) 1171da177e4SLinus Torvalds { 1181da177e4SLinus Torvalds struct afs_dir_page *dbuf; 1191da177e4SLinus Torvalds loff_t latter; 1201da177e4SLinus Torvalds int tmp, qty; 1211da177e4SLinus Torvalds 1221da177e4SLinus Torvalds #if 0 1231da177e4SLinus Torvalds /* check the page count */ 1241da177e4SLinus Torvalds qty = desc.size / sizeof(dbuf->blocks[0]); 1251da177e4SLinus Torvalds if (qty == 0) 1261da177e4SLinus Torvalds goto error; 1271da177e4SLinus Torvalds 1281da177e4SLinus Torvalds if (page->index == 0 && qty != ntohs(dbuf->blocks[0].pagehdr.npages)) { 1291da177e4SLinus Torvalds printk("kAFS: %s(%lu): wrong number of dir blocks %d!=%hu\n", 13008e0e7c8SDavid Howells __FUNCTION__, dir->i_ino, qty, 13108e0e7c8SDavid Howells ntohs(dbuf->blocks[0].pagehdr.npages)); 1321da177e4SLinus Torvalds goto error; 1331da177e4SLinus Torvalds } 1341da177e4SLinus Torvalds #endif 1351da177e4SLinus Torvalds 1361da177e4SLinus Torvalds /* determine how many magic numbers there should be in this page */ 13754b21a79SAndrew Morton latter = dir->i_size - page_offset(page); 1381da177e4SLinus Torvalds if (latter >= PAGE_SIZE) 1391da177e4SLinus Torvalds qty = PAGE_SIZE; 1401da177e4SLinus Torvalds else 1411da177e4SLinus Torvalds qty = latter; 1421da177e4SLinus Torvalds qty /= sizeof(union afs_dir_block); 1431da177e4SLinus Torvalds 1441da177e4SLinus Torvalds /* check them */ 1451da177e4SLinus Torvalds dbuf = page_address(page); 1461da177e4SLinus Torvalds for (tmp = 0; tmp < qty; tmp++) { 1471da177e4SLinus Torvalds if (dbuf->blocks[tmp].pagehdr.magic != AFS_DIR_MAGIC) { 1481da177e4SLinus Torvalds printk("kAFS: %s(%lu): bad magic %d/%d is %04hx\n", 1491da177e4SLinus Torvalds __FUNCTION__, dir->i_ino, tmp, qty, 1501da177e4SLinus Torvalds ntohs(dbuf->blocks[tmp].pagehdr.magic)); 1511da177e4SLinus Torvalds goto error; 1521da177e4SLinus Torvalds } 1531da177e4SLinus Torvalds } 1541da177e4SLinus Torvalds 1551da177e4SLinus Torvalds SetPageChecked(page); 1561da177e4SLinus Torvalds return; 1571da177e4SLinus Torvalds 1581da177e4SLinus Torvalds error: 1591da177e4SLinus Torvalds SetPageChecked(page); 1601da177e4SLinus Torvalds SetPageError(page); 161ec26815aSDavid Howells } 1621da177e4SLinus Torvalds 1631da177e4SLinus Torvalds /* 1641da177e4SLinus Torvalds * discard a page cached in the pagecache 1651da177e4SLinus Torvalds */ 1661da177e4SLinus Torvalds static inline void afs_dir_put_page(struct page *page) 1671da177e4SLinus Torvalds { 1681da177e4SLinus Torvalds kunmap(page); 1691da177e4SLinus Torvalds page_cache_release(page); 170ec26815aSDavid Howells } 1711da177e4SLinus Torvalds 1721da177e4SLinus Torvalds /* 1731da177e4SLinus Torvalds * get a page into the pagecache 1741da177e4SLinus Torvalds */ 17500d3b7a4SDavid Howells static struct page *afs_dir_get_page(struct inode *dir, unsigned long index, 17600d3b7a4SDavid Howells struct key *key) 1771da177e4SLinus Torvalds { 1781da177e4SLinus Torvalds struct page *page; 17900d3b7a4SDavid Howells struct file file = { 18000d3b7a4SDavid Howells .private_data = key, 18100d3b7a4SDavid Howells }; 1821da177e4SLinus Torvalds 1831da177e4SLinus Torvalds _enter("{%lu},%lu", dir->i_ino, index); 1841da177e4SLinus Torvalds 18500d3b7a4SDavid Howells page = read_mapping_page(dir->i_mapping, index, &file); 1861da177e4SLinus Torvalds if (!IS_ERR(page)) { 1871da177e4SLinus Torvalds wait_on_page_locked(page); 1881da177e4SLinus Torvalds kmap(page); 1891da177e4SLinus Torvalds if (!PageUptodate(page)) 1901da177e4SLinus Torvalds goto fail; 1911da177e4SLinus Torvalds if (!PageChecked(page)) 1921da177e4SLinus Torvalds afs_dir_check_page(dir, page); 1931da177e4SLinus Torvalds if (PageError(page)) 1941da177e4SLinus Torvalds goto fail; 1951da177e4SLinus Torvalds } 1961da177e4SLinus Torvalds return page; 1971da177e4SLinus Torvalds 1981da177e4SLinus Torvalds fail: 1991da177e4SLinus Torvalds afs_dir_put_page(page); 20008e0e7c8SDavid Howells _leave(" = -EIO"); 2011da177e4SLinus Torvalds return ERR_PTR(-EIO); 202ec26815aSDavid Howells } 2031da177e4SLinus Torvalds 2041da177e4SLinus Torvalds /* 2051da177e4SLinus Torvalds * open an AFS directory file 2061da177e4SLinus Torvalds */ 2071da177e4SLinus Torvalds static int afs_dir_open(struct inode *inode, struct file *file) 2081da177e4SLinus Torvalds { 2091da177e4SLinus Torvalds _enter("{%lu}", inode->i_ino); 2101da177e4SLinus Torvalds 2112ecd05aeSAlexey Dobriyan BUILD_BUG_ON(sizeof(union afs_dir_block) != 2048); 2122ecd05aeSAlexey Dobriyan BUILD_BUG_ON(sizeof(union afs_dirent) != 32); 2131da177e4SLinus Torvalds 21408e0e7c8SDavid Howells if (test_bit(AFS_VNODE_DELETED, &AFS_FS_I(inode)->flags)) 2151da177e4SLinus Torvalds return -ENOENT; 2161da177e4SLinus Torvalds 21700d3b7a4SDavid Howells return afs_open(inode, file); 218ec26815aSDavid Howells } 2191da177e4SLinus Torvalds 2201da177e4SLinus Torvalds /* 2211da177e4SLinus Torvalds * deal with one block in an AFS directory 2221da177e4SLinus Torvalds */ 2231da177e4SLinus Torvalds static int afs_dir_iterate_block(unsigned *fpos, 2241da177e4SLinus Torvalds union afs_dir_block *block, 2251da177e4SLinus Torvalds unsigned blkoff, 2261da177e4SLinus Torvalds void *cookie, 2271da177e4SLinus Torvalds filldir_t filldir) 2281da177e4SLinus Torvalds { 2291da177e4SLinus Torvalds union afs_dirent *dire; 2301da177e4SLinus Torvalds unsigned offset, next, curr; 2311da177e4SLinus Torvalds size_t nlen; 2321da177e4SLinus Torvalds int tmp, ret; 2331da177e4SLinus Torvalds 2341da177e4SLinus Torvalds _enter("%u,%x,%p,,",*fpos,blkoff,block); 2351da177e4SLinus Torvalds 2361da177e4SLinus Torvalds curr = (*fpos - blkoff) / sizeof(union afs_dirent); 2371da177e4SLinus Torvalds 2381da177e4SLinus Torvalds /* walk through the block, an entry at a time */ 2391da177e4SLinus Torvalds for (offset = AFS_DIRENT_PER_BLOCK - block->pagehdr.nentries; 2401da177e4SLinus Torvalds offset < AFS_DIRENT_PER_BLOCK; 2411da177e4SLinus Torvalds offset = next 2421da177e4SLinus Torvalds ) { 2431da177e4SLinus Torvalds next = offset + 1; 2441da177e4SLinus Torvalds 2451da177e4SLinus Torvalds /* skip entries marked unused in the bitmap */ 2461da177e4SLinus Torvalds if (!(block->pagehdr.bitmap[offset / 8] & 2471da177e4SLinus Torvalds (1 << (offset % 8)))) { 24808e0e7c8SDavid Howells _debug("ENT[%Zu.%u]: unused", 2491da177e4SLinus Torvalds blkoff / sizeof(union afs_dir_block), offset); 2501da177e4SLinus Torvalds if (offset >= curr) 2511da177e4SLinus Torvalds *fpos = blkoff + 2521da177e4SLinus Torvalds next * sizeof(union afs_dirent); 2531da177e4SLinus Torvalds continue; 2541da177e4SLinus Torvalds } 2551da177e4SLinus Torvalds 2561da177e4SLinus Torvalds /* got a valid entry */ 2571da177e4SLinus Torvalds dire = &block->dirents[offset]; 2581da177e4SLinus Torvalds nlen = strnlen(dire->u.name, 2591da177e4SLinus Torvalds sizeof(*block) - 2601da177e4SLinus Torvalds offset * sizeof(union afs_dirent)); 2611da177e4SLinus Torvalds 26208e0e7c8SDavid Howells _debug("ENT[%Zu.%u]: %s %Zu \"%s\"", 2631da177e4SLinus Torvalds blkoff / sizeof(union afs_dir_block), offset, 2641da177e4SLinus Torvalds (offset < curr ? "skip" : "fill"), 2651da177e4SLinus Torvalds nlen, dire->u.name); 2661da177e4SLinus Torvalds 2671da177e4SLinus Torvalds /* work out where the next possible entry is */ 2681da177e4SLinus Torvalds for (tmp = nlen; tmp > 15; tmp -= sizeof(union afs_dirent)) { 2691da177e4SLinus Torvalds if (next >= AFS_DIRENT_PER_BLOCK) { 2701da177e4SLinus Torvalds _debug("ENT[%Zu.%u]:" 2711da177e4SLinus Torvalds " %u travelled beyond end dir block" 27208e0e7c8SDavid Howells " (len %u/%Zu)", 2731da177e4SLinus Torvalds blkoff / sizeof(union afs_dir_block), 2741da177e4SLinus Torvalds offset, next, tmp, nlen); 2751da177e4SLinus Torvalds return -EIO; 2761da177e4SLinus Torvalds } 2771da177e4SLinus Torvalds if (!(block->pagehdr.bitmap[next / 8] & 2781da177e4SLinus Torvalds (1 << (next % 8)))) { 2791da177e4SLinus Torvalds _debug("ENT[%Zu.%u]:" 28008e0e7c8SDavid Howells " %u unmarked extension (len %u/%Zu)", 2811da177e4SLinus Torvalds blkoff / sizeof(union afs_dir_block), 2821da177e4SLinus Torvalds offset, next, tmp, nlen); 2831da177e4SLinus Torvalds return -EIO; 2841da177e4SLinus Torvalds } 2851da177e4SLinus Torvalds 28608e0e7c8SDavid Howells _debug("ENT[%Zu.%u]: ext %u/%Zu", 2871da177e4SLinus Torvalds blkoff / sizeof(union afs_dir_block), 2881da177e4SLinus Torvalds next, tmp, nlen); 2891da177e4SLinus Torvalds next++; 2901da177e4SLinus Torvalds } 2911da177e4SLinus Torvalds 2921da177e4SLinus Torvalds /* skip if starts before the current position */ 2931da177e4SLinus Torvalds if (offset < curr) 2941da177e4SLinus Torvalds continue; 2951da177e4SLinus Torvalds 2961da177e4SLinus Torvalds /* found the next entry */ 2971da177e4SLinus Torvalds ret = filldir(cookie, 2981da177e4SLinus Torvalds dire->u.name, 2991da177e4SLinus Torvalds nlen, 3001da177e4SLinus Torvalds blkoff + offset * sizeof(union afs_dirent), 3011da177e4SLinus Torvalds ntohl(dire->u.vnode), 3021da177e4SLinus Torvalds filldir == afs_dir_lookup_filldir ? 3031da177e4SLinus Torvalds ntohl(dire->u.unique) : DT_UNKNOWN); 3041da177e4SLinus Torvalds if (ret < 0) { 3051da177e4SLinus Torvalds _leave(" = 0 [full]"); 3061da177e4SLinus Torvalds return 0; 3071da177e4SLinus Torvalds } 3081da177e4SLinus Torvalds 3091da177e4SLinus Torvalds *fpos = blkoff + next * sizeof(union afs_dirent); 3101da177e4SLinus Torvalds } 3111da177e4SLinus Torvalds 3121da177e4SLinus Torvalds _leave(" = 1 [more]"); 3131da177e4SLinus Torvalds return 1; 314ec26815aSDavid Howells } 3151da177e4SLinus Torvalds 3161da177e4SLinus Torvalds /* 31708e0e7c8SDavid Howells * iterate through the data blob that lists the contents of an AFS directory 3181da177e4SLinus Torvalds */ 3191da177e4SLinus Torvalds static int afs_dir_iterate(struct inode *dir, unsigned *fpos, void *cookie, 32000d3b7a4SDavid Howells filldir_t filldir, struct key *key) 3211da177e4SLinus Torvalds { 3221da177e4SLinus Torvalds union afs_dir_block *dblock; 3231da177e4SLinus Torvalds struct afs_dir_page *dbuf; 3241da177e4SLinus Torvalds struct page *page; 3251da177e4SLinus Torvalds unsigned blkoff, limit; 3261da177e4SLinus Torvalds int ret; 3271da177e4SLinus Torvalds 3281da177e4SLinus Torvalds _enter("{%lu},%u,,", dir->i_ino, *fpos); 3291da177e4SLinus Torvalds 33008e0e7c8SDavid Howells if (test_bit(AFS_VNODE_DELETED, &AFS_FS_I(dir)->flags)) { 3311da177e4SLinus Torvalds _leave(" = -ESTALE"); 3321da177e4SLinus Torvalds return -ESTALE; 3331da177e4SLinus Torvalds } 3341da177e4SLinus Torvalds 3351da177e4SLinus Torvalds /* round the file position up to the next entry boundary */ 3361da177e4SLinus Torvalds *fpos += sizeof(union afs_dirent) - 1; 3371da177e4SLinus Torvalds *fpos &= ~(sizeof(union afs_dirent) - 1); 3381da177e4SLinus Torvalds 3391da177e4SLinus Torvalds /* walk through the blocks in sequence */ 3401da177e4SLinus Torvalds ret = 0; 3411da177e4SLinus Torvalds while (*fpos < dir->i_size) { 3421da177e4SLinus Torvalds blkoff = *fpos & ~(sizeof(union afs_dir_block) - 1); 3431da177e4SLinus Torvalds 3441da177e4SLinus Torvalds /* fetch the appropriate page from the directory */ 34500d3b7a4SDavid Howells page = afs_dir_get_page(dir, blkoff / PAGE_SIZE, key); 3461da177e4SLinus Torvalds if (IS_ERR(page)) { 3471da177e4SLinus Torvalds ret = PTR_ERR(page); 3481da177e4SLinus Torvalds break; 3491da177e4SLinus Torvalds } 3501da177e4SLinus Torvalds 3511da177e4SLinus Torvalds limit = blkoff & ~(PAGE_SIZE - 1); 3521da177e4SLinus Torvalds 3531da177e4SLinus Torvalds dbuf = page_address(page); 3541da177e4SLinus Torvalds 3551da177e4SLinus Torvalds /* deal with the individual blocks stashed on this page */ 3561da177e4SLinus Torvalds do { 3571da177e4SLinus Torvalds dblock = &dbuf->blocks[(blkoff % PAGE_SIZE) / 3581da177e4SLinus Torvalds sizeof(union afs_dir_block)]; 3591da177e4SLinus Torvalds ret = afs_dir_iterate_block(fpos, dblock, blkoff, 3601da177e4SLinus Torvalds cookie, filldir); 3611da177e4SLinus Torvalds if (ret != 1) { 3621da177e4SLinus Torvalds afs_dir_put_page(page); 3631da177e4SLinus Torvalds goto out; 3641da177e4SLinus Torvalds } 3651da177e4SLinus Torvalds 3661da177e4SLinus Torvalds blkoff += sizeof(union afs_dir_block); 3671da177e4SLinus Torvalds 3681da177e4SLinus Torvalds } while (*fpos < dir->i_size && blkoff < limit); 3691da177e4SLinus Torvalds 3701da177e4SLinus Torvalds afs_dir_put_page(page); 3711da177e4SLinus Torvalds ret = 0; 3721da177e4SLinus Torvalds } 3731da177e4SLinus Torvalds 3741da177e4SLinus Torvalds out: 3751da177e4SLinus Torvalds _leave(" = %d", ret); 3761da177e4SLinus Torvalds return ret; 377ec26815aSDavid Howells } 3781da177e4SLinus Torvalds 3791da177e4SLinus Torvalds /* 3801da177e4SLinus Torvalds * read an AFS directory 3811da177e4SLinus Torvalds */ 3821da177e4SLinus Torvalds static int afs_dir_readdir(struct file *file, void *cookie, filldir_t filldir) 3831da177e4SLinus Torvalds { 3841da177e4SLinus Torvalds unsigned fpos; 3851da177e4SLinus Torvalds int ret; 3861da177e4SLinus Torvalds 38708e0e7c8SDavid Howells _enter("{%Ld,{%lu}}", 38808e0e7c8SDavid Howells file->f_pos, file->f_path.dentry->d_inode->i_ino); 3891da177e4SLinus Torvalds 39000d3b7a4SDavid Howells ASSERT(file->private_data != NULL); 39100d3b7a4SDavid Howells 3921da177e4SLinus Torvalds fpos = file->f_pos; 39308e0e7c8SDavid Howells ret = afs_dir_iterate(file->f_path.dentry->d_inode, &fpos, 39400d3b7a4SDavid Howells cookie, filldir, file->private_data); 3951da177e4SLinus Torvalds file->f_pos = fpos; 3961da177e4SLinus Torvalds 3971da177e4SLinus Torvalds _leave(" = %d", ret); 3981da177e4SLinus Torvalds return ret; 399ec26815aSDavid Howells } 4001da177e4SLinus Torvalds 4011da177e4SLinus Torvalds /* 4021da177e4SLinus Torvalds * search the directory for a name 4031da177e4SLinus Torvalds * - if afs_dir_iterate_block() spots this function, it'll pass the FID 4041da177e4SLinus Torvalds * uniquifier through dtype 4051da177e4SLinus Torvalds */ 4061da177e4SLinus Torvalds static int afs_dir_lookup_filldir(void *_cookie, const char *name, int nlen, 407afefdbb2SDavid Howells loff_t fpos, u64 ino, unsigned dtype) 4081da177e4SLinus Torvalds { 4091da177e4SLinus Torvalds struct afs_dir_lookup_cookie *cookie = _cookie; 4101da177e4SLinus Torvalds 41108e0e7c8SDavid Howells _enter("{%s,%Zu},%s,%u,,%llu,%u", 4121da177e4SLinus Torvalds cookie->name, cookie->nlen, name, nlen, ino, dtype); 4131da177e4SLinus Torvalds 41408e0e7c8SDavid Howells /* insanity checks first */ 41508e0e7c8SDavid Howells BUILD_BUG_ON(sizeof(union afs_dir_block) != 2048); 41608e0e7c8SDavid Howells BUILD_BUG_ON(sizeof(union afs_dirent) != 32); 41708e0e7c8SDavid Howells 4181da177e4SLinus Torvalds if (cookie->nlen != nlen || memcmp(cookie->name, name, nlen) != 0) { 4191da177e4SLinus Torvalds _leave(" = 0 [no]"); 4201da177e4SLinus Torvalds return 0; 4211da177e4SLinus Torvalds } 4221da177e4SLinus Torvalds 4231da177e4SLinus Torvalds cookie->fid.vnode = ino; 4241da177e4SLinus Torvalds cookie->fid.unique = dtype; 4251da177e4SLinus Torvalds cookie->found = 1; 4261da177e4SLinus Torvalds 4271da177e4SLinus Torvalds _leave(" = -1 [found]"); 4281da177e4SLinus Torvalds return -1; 429ec26815aSDavid Howells } 4301da177e4SLinus Torvalds 4311da177e4SLinus Torvalds /* 43208e0e7c8SDavid Howells * do a lookup in a directory 4331da177e4SLinus Torvalds */ 43408e0e7c8SDavid Howells static int afs_do_lookup(struct inode *dir, struct dentry *dentry, 43500d3b7a4SDavid Howells struct afs_fid *fid, struct key *key) 4361da177e4SLinus Torvalds { 4371da177e4SLinus Torvalds struct afs_dir_lookup_cookie cookie; 4381da177e4SLinus Torvalds struct afs_super_info *as; 4391da177e4SLinus Torvalds unsigned fpos; 4401da177e4SLinus Torvalds int ret; 4411da177e4SLinus Torvalds 44208e0e7c8SDavid Howells _enter("{%lu},%p{%s},", dir->i_ino, dentry, dentry->d_name.name); 4431da177e4SLinus Torvalds 4441da177e4SLinus Torvalds as = dir->i_sb->s_fs_info; 4451da177e4SLinus Torvalds 4461da177e4SLinus Torvalds /* search the directory */ 4471da177e4SLinus Torvalds cookie.name = dentry->d_name.name; 4481da177e4SLinus Torvalds cookie.nlen = dentry->d_name.len; 4491da177e4SLinus Torvalds cookie.fid.vid = as->volume->vid; 4501da177e4SLinus Torvalds cookie.found = 0; 4511da177e4SLinus Torvalds 4521da177e4SLinus Torvalds fpos = 0; 45300d3b7a4SDavid Howells ret = afs_dir_iterate(dir, &fpos, &cookie, afs_dir_lookup_filldir, 45400d3b7a4SDavid Howells key); 4551da177e4SLinus Torvalds if (ret < 0) { 45608e0e7c8SDavid Howells _leave(" = %d [iter]", ret); 45708e0e7c8SDavid Howells return ret; 4581da177e4SLinus Torvalds } 4591da177e4SLinus Torvalds 4601da177e4SLinus Torvalds ret = -ENOENT; 4611da177e4SLinus Torvalds if (!cookie.found) { 46208e0e7c8SDavid Howells _leave(" = -ENOENT [not found]"); 46308e0e7c8SDavid Howells return -ENOENT; 46408e0e7c8SDavid Howells } 46508e0e7c8SDavid Howells 46608e0e7c8SDavid Howells *fid = cookie.fid; 46708e0e7c8SDavid Howells _leave(" = 0 { vn=%u u=%u }", fid->vnode, fid->unique); 46808e0e7c8SDavid Howells return 0; 46908e0e7c8SDavid Howells } 47008e0e7c8SDavid Howells 47108e0e7c8SDavid Howells /* 47208e0e7c8SDavid Howells * look up an entry in a directory 47308e0e7c8SDavid Howells */ 47408e0e7c8SDavid Howells static struct dentry *afs_dir_lookup(struct inode *dir, struct dentry *dentry, 47508e0e7c8SDavid Howells struct nameidata *nd) 47608e0e7c8SDavid Howells { 47708e0e7c8SDavid Howells struct afs_vnode *vnode; 47808e0e7c8SDavid Howells struct afs_fid fid; 47908e0e7c8SDavid Howells struct inode *inode; 48000d3b7a4SDavid Howells struct key *key; 48108e0e7c8SDavid Howells int ret; 48208e0e7c8SDavid Howells 48308e0e7c8SDavid Howells _enter("{%lu},%p{%s}", dir->i_ino, dentry, dentry->d_name.name); 48408e0e7c8SDavid Howells 48508e0e7c8SDavid Howells if (dentry->d_name.len > 255) { 48608e0e7c8SDavid Howells _leave(" = -ENAMETOOLONG"); 48708e0e7c8SDavid Howells return ERR_PTR(-ENAMETOOLONG); 48808e0e7c8SDavid Howells } 48908e0e7c8SDavid Howells 49008e0e7c8SDavid Howells vnode = AFS_FS_I(dir); 49108e0e7c8SDavid Howells if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) { 49208e0e7c8SDavid Howells _leave(" = -ESTALE"); 49308e0e7c8SDavid Howells return ERR_PTR(-ESTALE); 49408e0e7c8SDavid Howells } 49508e0e7c8SDavid Howells 49600d3b7a4SDavid Howells key = afs_request_key(vnode->volume->cell); 49700d3b7a4SDavid Howells if (IS_ERR(key)) { 49800d3b7a4SDavid Howells _leave(" = %ld [key]", PTR_ERR(key)); 49900d3b7a4SDavid Howells return ERR_PTR(PTR_ERR(key)); 50000d3b7a4SDavid Howells } 50100d3b7a4SDavid Howells 50200d3b7a4SDavid Howells ret = afs_do_lookup(dir, dentry, &fid, key); 50308e0e7c8SDavid Howells if (ret < 0) { 50400d3b7a4SDavid Howells key_put(key); 50508e0e7c8SDavid Howells _leave(" = %d [do]", ret); 5061da177e4SLinus Torvalds return ERR_PTR(ret); 5071da177e4SLinus Torvalds } 5081da177e4SLinus Torvalds 5091da177e4SLinus Torvalds /* instantiate the dentry */ 51000d3b7a4SDavid Howells inode = afs_iget(dir->i_sb, key, &fid); 51100d3b7a4SDavid Howells key_put(key); 51208e0e7c8SDavid Howells if (IS_ERR(inode)) { 51308e0e7c8SDavid Howells _leave(" = %ld", PTR_ERR(inode)); 51408e0e7c8SDavid Howells return ERR_PTR(PTR_ERR(inode)); 5151da177e4SLinus Torvalds } 5161da177e4SLinus Torvalds 5171da177e4SLinus Torvalds dentry->d_op = &afs_fs_dentry_operations; 5181da177e4SLinus Torvalds 5191da177e4SLinus Torvalds d_add(dentry, inode); 5201da177e4SLinus Torvalds _leave(" = 0 { vn=%u u=%u } -> { ino=%lu v=%lu }", 52108e0e7c8SDavid Howells fid.vnode, 52208e0e7c8SDavid Howells fid.unique, 5231da177e4SLinus Torvalds dentry->d_inode->i_ino, 5241da177e4SLinus Torvalds dentry->d_inode->i_version); 5251da177e4SLinus Torvalds 5261da177e4SLinus Torvalds return NULL; 527ec26815aSDavid Howells } 5281da177e4SLinus Torvalds 5291da177e4SLinus Torvalds /* 53008e0e7c8SDavid Howells * propagate changed and modified flags on a directory to all the children of 53108e0e7c8SDavid Howells * that directory as they may indicate that the ACL on the dir has changed, 53208e0e7c8SDavid Howells * potentially rendering the child inaccessible or that a file has been deleted 53308e0e7c8SDavid Howells * or renamed 53408e0e7c8SDavid Howells */ 53508e0e7c8SDavid Howells static void afs_propagate_dir_changes(struct dentry *dir) 53608e0e7c8SDavid Howells { 53708e0e7c8SDavid Howells struct dentry *child; 53808e0e7c8SDavid Howells bool c, m; 53908e0e7c8SDavid Howells 54008e0e7c8SDavid Howells c = test_bit(AFS_VNODE_CHANGED, &AFS_FS_I(dir->d_inode)->flags); 54108e0e7c8SDavid Howells m = test_bit(AFS_VNODE_MODIFIED, &AFS_FS_I(dir->d_inode)->flags); 54208e0e7c8SDavid Howells 54308e0e7c8SDavid Howells _enter("{%d,%d}", c, m); 54408e0e7c8SDavid Howells 54508e0e7c8SDavid Howells spin_lock(&dir->d_lock); 54608e0e7c8SDavid Howells 54708e0e7c8SDavid Howells list_for_each_entry(child, &dir->d_subdirs, d_u.d_child) { 54808e0e7c8SDavid Howells if (child->d_inode) { 54908e0e7c8SDavid Howells struct afs_vnode *vnode; 55008e0e7c8SDavid Howells 55108e0e7c8SDavid Howells _debug("tag %s", child->d_name.name); 55208e0e7c8SDavid Howells vnode = AFS_FS_I(child->d_inode); 55308e0e7c8SDavid Howells if (c) 55408e0e7c8SDavid Howells set_bit(AFS_VNODE_DIR_CHANGED, &vnode->flags); 55508e0e7c8SDavid Howells if (m) 55608e0e7c8SDavid Howells set_bit(AFS_VNODE_DIR_MODIFIED, &vnode->flags); 55708e0e7c8SDavid Howells } 55808e0e7c8SDavid Howells } 55908e0e7c8SDavid Howells 56008e0e7c8SDavid Howells spin_unlock(&dir->d_lock); 56108e0e7c8SDavid Howells } 56208e0e7c8SDavid Howells 56308e0e7c8SDavid Howells /* 5641da177e4SLinus Torvalds * check that a dentry lookup hit has found a valid entry 5651da177e4SLinus Torvalds * - NOTE! the hit can be a negative hit too, so we can't assume we have an 5661da177e4SLinus Torvalds * inode 56708e0e7c8SDavid Howells * - there are several things we need to check 56808e0e7c8SDavid Howells * - parent dir data changes (rm, rmdir, rename, mkdir, create, link, 56908e0e7c8SDavid Howells * symlink) 57008e0e7c8SDavid Howells * - parent dir metadata changed (security changes) 57108e0e7c8SDavid Howells * - dentry data changed (write, truncate) 57208e0e7c8SDavid Howells * - dentry metadata changed (security changes) 5731da177e4SLinus Torvalds */ 5741da177e4SLinus Torvalds static int afs_d_revalidate(struct dentry *dentry, struct nameidata *nd) 5751da177e4SLinus Torvalds { 57608e0e7c8SDavid Howells struct afs_vnode *vnode; 57708e0e7c8SDavid Howells struct afs_fid fid; 5781da177e4SLinus Torvalds struct dentry *parent; 5791da177e4SLinus Torvalds struct inode *inode, *dir; 58000d3b7a4SDavid Howells struct key *key; 5811da177e4SLinus Torvalds int ret; 5821da177e4SLinus Torvalds 58308e0e7c8SDavid Howells vnode = AFS_FS_I(dentry->d_inode); 58408e0e7c8SDavid Howells 58508e0e7c8SDavid Howells _enter("{sb=%p n=%s fl=%lx},", 58608e0e7c8SDavid Howells dentry->d_sb, dentry->d_name.name, vnode->flags); 5871da177e4SLinus Torvalds 58800d3b7a4SDavid Howells key = afs_request_key(vnode->volume->cell); 58900d3b7a4SDavid Howells if (IS_ERR(key)) 59000d3b7a4SDavid Howells key = NULL; 59100d3b7a4SDavid Howells 5921da177e4SLinus Torvalds /* lock down the parent dentry so we can peer at it */ 59308e0e7c8SDavid Howells parent = dget_parent(dentry); 5941da177e4SLinus Torvalds 5951da177e4SLinus Torvalds dir = parent->d_inode; 5961da177e4SLinus Torvalds inode = dentry->d_inode; 5971da177e4SLinus Torvalds 5981da177e4SLinus Torvalds /* handle a negative dentry */ 5991da177e4SLinus Torvalds if (!inode) 6001da177e4SLinus Torvalds goto out_bad; 6011da177e4SLinus Torvalds 6021da177e4SLinus Torvalds /* handle a bad inode */ 6031da177e4SLinus Torvalds if (is_bad_inode(inode)) { 6041da177e4SLinus Torvalds printk("kAFS: afs_d_revalidate: %s/%s has bad inode\n", 60508e0e7c8SDavid Howells parent->d_name.name, dentry->d_name.name); 6061da177e4SLinus Torvalds goto out_bad; 6071da177e4SLinus Torvalds } 6081da177e4SLinus Torvalds 60908e0e7c8SDavid Howells /* check that this dirent still exists if the directory's contents were 61008e0e7c8SDavid Howells * modified */ 61108e0e7c8SDavid Howells if (test_bit(AFS_VNODE_DELETED, &AFS_FS_I(dir)->flags)) { 6121da177e4SLinus Torvalds _debug("%s: parent dir deleted", dentry->d_name.name); 6131da177e4SLinus Torvalds goto out_bad; 6141da177e4SLinus Torvalds } 6151da177e4SLinus Torvalds 61608e0e7c8SDavid Howells if (test_and_clear_bit(AFS_VNODE_DIR_MODIFIED, &vnode->flags)) { 61708e0e7c8SDavid Howells /* rm/rmdir/rename may have occurred */ 61808e0e7c8SDavid Howells _debug("dir modified"); 6191da177e4SLinus Torvalds 6201da177e4SLinus Torvalds /* search the directory for this vnode */ 62100d3b7a4SDavid Howells ret = afs_do_lookup(dir, dentry, &fid, key); 62208e0e7c8SDavid Howells if (ret == -ENOENT) { 62308e0e7c8SDavid Howells _debug("%s: dirent not found", dentry->d_name.name); 62408e0e7c8SDavid Howells goto not_found; 62508e0e7c8SDavid Howells } 6261da177e4SLinus Torvalds if (ret < 0) { 6271da177e4SLinus Torvalds _debug("failed to iterate dir %s: %d", 6281da177e4SLinus Torvalds parent->d_name.name, ret); 6291da177e4SLinus Torvalds goto out_bad; 6301da177e4SLinus Torvalds } 6311da177e4SLinus Torvalds 6321da177e4SLinus Torvalds /* if the vnode ID has changed, then the dirent points to a 6331da177e4SLinus Torvalds * different file */ 63408e0e7c8SDavid Howells if (fid.vnode != vnode->fid.vnode) { 63508e0e7c8SDavid Howells _debug("%s: dirent changed [%u != %u]", 63608e0e7c8SDavid Howells dentry->d_name.name, fid.vnode, 63708e0e7c8SDavid Howells vnode->fid.vnode); 6381da177e4SLinus Torvalds goto not_found; 6391da177e4SLinus Torvalds } 6401da177e4SLinus Torvalds 6411da177e4SLinus Torvalds /* if the vnode ID uniqifier has changed, then the file has 6421da177e4SLinus Torvalds * been deleted */ 64308e0e7c8SDavid Howells if (fid.unique != vnode->fid.unique) { 6441da177e4SLinus Torvalds _debug("%s: file deleted (uq %u -> %u I:%lu)", 64508e0e7c8SDavid Howells dentry->d_name.name, fid.unique, 64608e0e7c8SDavid Howells vnode->fid.unique, inode->i_version); 64708e0e7c8SDavid Howells spin_lock(&vnode->lock); 64808e0e7c8SDavid Howells set_bit(AFS_VNODE_DELETED, &vnode->flags); 64908e0e7c8SDavid Howells spin_unlock(&vnode->lock); 6501da177e4SLinus Torvalds invalidate_remote_inode(inode); 6511da177e4SLinus Torvalds goto out_bad; 6521da177e4SLinus Torvalds } 6531da177e4SLinus Torvalds } 6541da177e4SLinus Torvalds 65508e0e7c8SDavid Howells /* if the directory's metadata were changed then the security may be 65608e0e7c8SDavid Howells * different and we may no longer have access */ 65708e0e7c8SDavid Howells mutex_lock(&vnode->cb_broken_lock); 65808e0e7c8SDavid Howells 65908e0e7c8SDavid Howells if (test_and_clear_bit(AFS_VNODE_DIR_CHANGED, &vnode->flags) || 66008e0e7c8SDavid Howells test_bit(AFS_VNODE_CB_BROKEN, &vnode->flags)) { 66108e0e7c8SDavid Howells _debug("%s: changed", dentry->d_name.name); 66208e0e7c8SDavid Howells set_bit(AFS_VNODE_CB_BROKEN, &vnode->flags); 66300d3b7a4SDavid Howells if (afs_vnode_fetch_status(vnode, NULL, key) < 0) { 66408e0e7c8SDavid Howells mutex_unlock(&vnode->cb_broken_lock); 66508e0e7c8SDavid Howells goto out_bad; 66608e0e7c8SDavid Howells } 66708e0e7c8SDavid Howells } 66808e0e7c8SDavid Howells 66908e0e7c8SDavid Howells if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) { 67008e0e7c8SDavid Howells _debug("%s: file already deleted", dentry->d_name.name); 67108e0e7c8SDavid Howells mutex_unlock(&vnode->cb_broken_lock); 67208e0e7c8SDavid Howells goto out_bad; 67308e0e7c8SDavid Howells } 67408e0e7c8SDavid Howells 67508e0e7c8SDavid Howells /* if the vnode's data version number changed then its contents are 67608e0e7c8SDavid Howells * different */ 67708e0e7c8SDavid Howells if (test_and_clear_bit(AFS_VNODE_ZAP_DATA, &vnode->flags)) { 67808e0e7c8SDavid Howells _debug("zap data"); 67908e0e7c8SDavid Howells invalidate_remote_inode(inode); 68008e0e7c8SDavid Howells } 68108e0e7c8SDavid Howells 68208e0e7c8SDavid Howells if (S_ISDIR(inode->i_mode) && 68308e0e7c8SDavid Howells (test_bit(AFS_VNODE_CHANGED, &vnode->flags) || 68408e0e7c8SDavid Howells test_bit(AFS_VNODE_MODIFIED, &vnode->flags))) 68508e0e7c8SDavid Howells afs_propagate_dir_changes(dentry); 68608e0e7c8SDavid Howells 68708e0e7c8SDavid Howells clear_bit(AFS_VNODE_CHANGED, &vnode->flags); 68808e0e7c8SDavid Howells clear_bit(AFS_VNODE_MODIFIED, &vnode->flags); 68908e0e7c8SDavid Howells mutex_unlock(&vnode->cb_broken_lock); 69008e0e7c8SDavid Howells 6911da177e4SLinus Torvalds out_valid: 6921da177e4SLinus Torvalds dput(parent); 69300d3b7a4SDavid Howells key_put(key); 6941da177e4SLinus Torvalds _leave(" = 1 [valid]"); 6951da177e4SLinus Torvalds return 1; 6961da177e4SLinus Torvalds 6971da177e4SLinus Torvalds /* the dirent, if it exists, now points to a different vnode */ 6981da177e4SLinus Torvalds not_found: 6991da177e4SLinus Torvalds spin_lock(&dentry->d_lock); 7001da177e4SLinus Torvalds dentry->d_flags |= DCACHE_NFSFS_RENAMED; 7011da177e4SLinus Torvalds spin_unlock(&dentry->d_lock); 7021da177e4SLinus Torvalds 7031da177e4SLinus Torvalds out_bad: 7041da177e4SLinus Torvalds if (inode) { 7051da177e4SLinus Torvalds /* don't unhash if we have submounts */ 7061da177e4SLinus Torvalds if (have_submounts(dentry)) 7071da177e4SLinus Torvalds goto out_valid; 7081da177e4SLinus Torvalds } 7091da177e4SLinus Torvalds 7101da177e4SLinus Torvalds _debug("dropping dentry %s/%s", 71108e0e7c8SDavid Howells parent->d_name.name, dentry->d_name.name); 71208e0e7c8SDavid Howells shrink_dcache_parent(dentry); 7131da177e4SLinus Torvalds d_drop(dentry); 7141da177e4SLinus Torvalds dput(parent); 71500d3b7a4SDavid Howells key_put(key); 7161da177e4SLinus Torvalds 7171da177e4SLinus Torvalds _leave(" = 0 [bad]"); 7181da177e4SLinus Torvalds return 0; 719ec26815aSDavid Howells } 7201da177e4SLinus Torvalds 7211da177e4SLinus Torvalds /* 7221da177e4SLinus Torvalds * allow the VFS to enquire as to whether a dentry should be unhashed (mustn't 7231da177e4SLinus Torvalds * sleep) 7241da177e4SLinus Torvalds * - called from dput() when d_count is going to 0. 7251da177e4SLinus Torvalds * - return 1 to request dentry be unhashed, 0 otherwise 7261da177e4SLinus Torvalds */ 7271da177e4SLinus Torvalds static int afs_d_delete(struct dentry *dentry) 7281da177e4SLinus Torvalds { 7291da177e4SLinus Torvalds _enter("%s", dentry->d_name.name); 7301da177e4SLinus Torvalds 7311da177e4SLinus Torvalds if (dentry->d_flags & DCACHE_NFSFS_RENAMED) 7321da177e4SLinus Torvalds goto zap; 7331da177e4SLinus Torvalds 73408e0e7c8SDavid Howells if (dentry->d_inode && 73508e0e7c8SDavid Howells test_bit(AFS_VNODE_DELETED, &AFS_FS_I(dentry->d_inode)->flags)) 7361da177e4SLinus Torvalds goto zap; 7371da177e4SLinus Torvalds 7381da177e4SLinus Torvalds _leave(" = 0 [keep]"); 7391da177e4SLinus Torvalds return 0; 7401da177e4SLinus Torvalds 7411da177e4SLinus Torvalds zap: 7421da177e4SLinus Torvalds _leave(" = 1 [zap]"); 7431da177e4SLinus Torvalds return 1; 744ec26815aSDavid Howells } 745