11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * linux/fs/ext2/dir.c 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * Copyright (C) 1992, 1993, 1994, 1995 51da177e4SLinus Torvalds * Remy Card (card@masi.ibp.fr) 61da177e4SLinus Torvalds * Laboratoire MASI - Institut Blaise Pascal 71da177e4SLinus Torvalds * Universite Pierre et Marie Curie (Paris VI) 81da177e4SLinus Torvalds * 91da177e4SLinus Torvalds * from 101da177e4SLinus Torvalds * 111da177e4SLinus Torvalds * linux/fs/minix/dir.c 121da177e4SLinus Torvalds * 131da177e4SLinus Torvalds * Copyright (C) 1991, 1992 Linus Torvalds 141da177e4SLinus Torvalds * 151da177e4SLinus Torvalds * ext2 directory handling functions 161da177e4SLinus Torvalds * 171da177e4SLinus Torvalds * Big-endian to little-endian byte-swapping/bitmaps by 181da177e4SLinus Torvalds * David S. Miller (davem@caip.rutgers.edu), 1995 191da177e4SLinus Torvalds * 201da177e4SLinus Torvalds * All code that works with directory layout had been switched to pagecache 211da177e4SLinus Torvalds * and moved here. AV 221da177e4SLinus Torvalds */ 231da177e4SLinus Torvalds 241da177e4SLinus Torvalds #include "ext2.h" 25f34fb6ecSNick Piggin #include <linux/buffer_head.h> 261da177e4SLinus Torvalds #include <linux/pagemap.h> 27f34fb6ecSNick Piggin #include <linux/swap.h> 281da177e4SLinus Torvalds 291da177e4SLinus Torvalds typedef struct ext2_dir_entry_2 ext2_dirent; 301da177e4SLinus Torvalds 3189910cccSJan Kara static inline unsigned ext2_rec_len_from_disk(__le16 dlen) 3289910cccSJan Kara { 3389910cccSJan Kara unsigned len = le16_to_cpu(dlen); 3489910cccSJan Kara 3589910cccSJan Kara if (len == EXT2_MAX_REC_LEN) 3689910cccSJan Kara return 1 << 16; 3789910cccSJan Kara return len; 3889910cccSJan Kara } 3989910cccSJan Kara 4089910cccSJan Kara static inline __le16 ext2_rec_len_to_disk(unsigned len) 4189910cccSJan Kara { 4289910cccSJan Kara if (len == (1 << 16)) 4389910cccSJan Kara return cpu_to_le16(EXT2_MAX_REC_LEN); 442c11619aSJulia Lawall else 452c11619aSJulia Lawall BUG_ON(len > (1 << 16)); 4689910cccSJan Kara return cpu_to_le16(len); 4789910cccSJan Kara } 4889910cccSJan Kara 491da177e4SLinus Torvalds /* 501da177e4SLinus Torvalds * ext2 uses block-sized chunks. Arguably, sector-sized ones would be 511da177e4SLinus Torvalds * more robust, but we have what we have 521da177e4SLinus Torvalds */ 531da177e4SLinus Torvalds static inline unsigned ext2_chunk_size(struct inode *inode) 541da177e4SLinus Torvalds { 551da177e4SLinus Torvalds return inode->i_sb->s_blocksize; 561da177e4SLinus Torvalds } 571da177e4SLinus Torvalds 581da177e4SLinus Torvalds static inline void ext2_put_page(struct page *page) 591da177e4SLinus Torvalds { 601da177e4SLinus Torvalds kunmap(page); 611da177e4SLinus Torvalds page_cache_release(page); 621da177e4SLinus Torvalds } 631da177e4SLinus Torvalds 641da177e4SLinus Torvalds static inline unsigned long dir_pages(struct inode *inode) 651da177e4SLinus Torvalds { 661da177e4SLinus Torvalds return (inode->i_size+PAGE_CACHE_SIZE-1)>>PAGE_CACHE_SHIFT; 671da177e4SLinus Torvalds } 681da177e4SLinus Torvalds 691da177e4SLinus Torvalds /* 701da177e4SLinus Torvalds * Return the offset into page `page_nr' of the last valid 711da177e4SLinus Torvalds * byte in that page, plus one. 721da177e4SLinus Torvalds */ 731da177e4SLinus Torvalds static unsigned 741da177e4SLinus Torvalds ext2_last_byte(struct inode *inode, unsigned long page_nr) 751da177e4SLinus Torvalds { 761da177e4SLinus Torvalds unsigned last_byte = inode->i_size; 771da177e4SLinus Torvalds 781da177e4SLinus Torvalds last_byte -= page_nr << PAGE_CACHE_SHIFT; 791da177e4SLinus Torvalds if (last_byte > PAGE_CACHE_SIZE) 801da177e4SLinus Torvalds last_byte = PAGE_CACHE_SIZE; 811da177e4SLinus Torvalds return last_byte; 821da177e4SLinus Torvalds } 831da177e4SLinus Torvalds 84f34fb6ecSNick Piggin static int ext2_commit_chunk(struct page *page, loff_t pos, unsigned len) 851da177e4SLinus Torvalds { 86f34fb6ecSNick Piggin struct address_space *mapping = page->mapping; 87f34fb6ecSNick Piggin struct inode *dir = mapping->host; 881da177e4SLinus Torvalds int err = 0; 89f34fb6ecSNick Piggin 901da177e4SLinus Torvalds dir->i_version++; 91f34fb6ecSNick Piggin block_write_end(NULL, mapping, pos, len, len, page, NULL); 92f34fb6ecSNick Piggin 93f34fb6ecSNick Piggin if (pos+len > dir->i_size) { 94f34fb6ecSNick Piggin i_size_write(dir, pos+len); 95f34fb6ecSNick Piggin mark_inode_dirty(dir); 96f34fb6ecSNick Piggin } 97f34fb6ecSNick Piggin 986b7021efSJan Kara if (IS_DIRSYNC(dir)) { 991da177e4SLinus Torvalds err = write_one_page(page, 1); 1006b7021efSJan Kara if (!err) 1016b7021efSJan Kara err = ext2_sync_inode(dir); 1026b7021efSJan Kara } else { 1031da177e4SLinus Torvalds unlock_page(page); 1046b7021efSJan Kara } 105f34fb6ecSNick Piggin 1061da177e4SLinus Torvalds return err; 1071da177e4SLinus Torvalds } 1081da177e4SLinus Torvalds 109bd39597cSEric Sandeen static void ext2_check_page(struct page *page, int quiet) 1101da177e4SLinus Torvalds { 1111da177e4SLinus Torvalds struct inode *dir = page->mapping->host; 1121da177e4SLinus Torvalds struct super_block *sb = dir->i_sb; 1131da177e4SLinus Torvalds unsigned chunk_size = ext2_chunk_size(dir); 1141da177e4SLinus Torvalds char *kaddr = page_address(page); 1151da177e4SLinus Torvalds u32 max_inumber = le32_to_cpu(EXT2_SB(sb)->s_es->s_inodes_count); 1161da177e4SLinus Torvalds unsigned offs, rec_len; 1171da177e4SLinus Torvalds unsigned limit = PAGE_CACHE_SIZE; 1181da177e4SLinus Torvalds ext2_dirent *p; 1191da177e4SLinus Torvalds char *error; 1201da177e4SLinus Torvalds 1211da177e4SLinus Torvalds if ((dir->i_size >> PAGE_CACHE_SHIFT) == page->index) { 1221da177e4SLinus Torvalds limit = dir->i_size & ~PAGE_CACHE_MASK; 1231da177e4SLinus Torvalds if (limit & (chunk_size - 1)) 1241da177e4SLinus Torvalds goto Ebadsize; 1251da177e4SLinus Torvalds if (!limit) 1261da177e4SLinus Torvalds goto out; 1271da177e4SLinus Torvalds } 1281da177e4SLinus Torvalds for (offs = 0; offs <= limit - EXT2_DIR_REC_LEN(1); offs += rec_len) { 1291da177e4SLinus Torvalds p = (ext2_dirent *)(kaddr + offs); 13089910cccSJan Kara rec_len = ext2_rec_len_from_disk(p->rec_len); 1311da177e4SLinus Torvalds 1321da177e4SLinus Torvalds if (rec_len < EXT2_DIR_REC_LEN(1)) 1331da177e4SLinus Torvalds goto Eshort; 1341da177e4SLinus Torvalds if (rec_len & 3) 1351da177e4SLinus Torvalds goto Ealign; 1361da177e4SLinus Torvalds if (rec_len < EXT2_DIR_REC_LEN(p->name_len)) 1371da177e4SLinus Torvalds goto Enamelen; 1381da177e4SLinus Torvalds if (((offs + rec_len - 1) ^ offs) & ~(chunk_size-1)) 1391da177e4SLinus Torvalds goto Espan; 1401da177e4SLinus Torvalds if (le32_to_cpu(p->inode) > max_inumber) 1411da177e4SLinus Torvalds goto Einumber; 1421da177e4SLinus Torvalds } 1431da177e4SLinus Torvalds if (offs != limit) 1441da177e4SLinus Torvalds goto Eend; 1451da177e4SLinus Torvalds out: 1461da177e4SLinus Torvalds SetPageChecked(page); 1471da177e4SLinus Torvalds return; 1481da177e4SLinus Torvalds 1491da177e4SLinus Torvalds /* Too bad, we had an error */ 1501da177e4SLinus Torvalds 1511da177e4SLinus Torvalds Ebadsize: 152bd39597cSEric Sandeen if (!quiet) 153bd39597cSEric Sandeen ext2_error(sb, __func__, 154bd39597cSEric Sandeen "size of directory #%lu is not a multiple " 155bd39597cSEric Sandeen "of chunk size", dir->i_ino); 1561da177e4SLinus Torvalds goto fail; 1571da177e4SLinus Torvalds Eshort: 1581da177e4SLinus Torvalds error = "rec_len is smaller than minimal"; 1591da177e4SLinus Torvalds goto bad_entry; 1601da177e4SLinus Torvalds Ealign: 1611da177e4SLinus Torvalds error = "unaligned directory entry"; 1621da177e4SLinus Torvalds goto bad_entry; 1631da177e4SLinus Torvalds Enamelen: 1641da177e4SLinus Torvalds error = "rec_len is too small for name_len"; 1651da177e4SLinus Torvalds goto bad_entry; 1661da177e4SLinus Torvalds Espan: 1671da177e4SLinus Torvalds error = "directory entry across blocks"; 1681da177e4SLinus Torvalds goto bad_entry; 1691da177e4SLinus Torvalds Einumber: 1701da177e4SLinus Torvalds error = "inode out of bounds"; 1711da177e4SLinus Torvalds bad_entry: 172bd39597cSEric Sandeen if (!quiet) 173bd39597cSEric Sandeen ext2_error(sb, __func__, "bad entry in directory #%lu: : %s - " 1741da177e4SLinus Torvalds "offset=%lu, inode=%lu, rec_len=%d, name_len=%d", 1751da177e4SLinus Torvalds dir->i_ino, error, (page->index<<PAGE_CACHE_SHIFT)+offs, 1761da177e4SLinus Torvalds (unsigned long) le32_to_cpu(p->inode), 1771da177e4SLinus Torvalds rec_len, p->name_len); 1781da177e4SLinus Torvalds goto fail; 1791da177e4SLinus Torvalds Eend: 180bd39597cSEric Sandeen if (!quiet) { 1811da177e4SLinus Torvalds p = (ext2_dirent *)(kaddr + offs); 1821da177e4SLinus Torvalds ext2_error(sb, "ext2_check_page", 1831da177e4SLinus Torvalds "entry in directory #%lu spans the page boundary" 1841da177e4SLinus Torvalds "offset=%lu, inode=%lu", 1851da177e4SLinus Torvalds dir->i_ino, (page->index<<PAGE_CACHE_SHIFT)+offs, 1861da177e4SLinus Torvalds (unsigned long) le32_to_cpu(p->inode)); 187bd39597cSEric Sandeen } 1881da177e4SLinus Torvalds fail: 1891da177e4SLinus Torvalds SetPageChecked(page); 1901da177e4SLinus Torvalds SetPageError(page); 1911da177e4SLinus Torvalds } 1921da177e4SLinus Torvalds 193bd39597cSEric Sandeen static struct page * ext2_get_page(struct inode *dir, unsigned long n, 194bd39597cSEric Sandeen int quiet) 1951da177e4SLinus Torvalds { 1961da177e4SLinus Torvalds struct address_space *mapping = dir->i_mapping; 197090d2b18SPekka Enberg struct page *page = read_mapping_page(mapping, n, NULL); 1981da177e4SLinus Torvalds if (!IS_ERR(page)) { 1991da177e4SLinus Torvalds kmap(page); 2001da177e4SLinus Torvalds if (!PageChecked(page)) 201bd39597cSEric Sandeen ext2_check_page(page, quiet); 2021da177e4SLinus Torvalds if (PageError(page)) 2031da177e4SLinus Torvalds goto fail; 2041da177e4SLinus Torvalds } 2051da177e4SLinus Torvalds return page; 2061da177e4SLinus Torvalds 2071da177e4SLinus Torvalds fail: 2081da177e4SLinus Torvalds ext2_put_page(page); 2091da177e4SLinus Torvalds return ERR_PTR(-EIO); 2101da177e4SLinus Torvalds } 2111da177e4SLinus Torvalds 2121da177e4SLinus Torvalds /* 2131da177e4SLinus Torvalds * NOTE! unlike strncmp, ext2_match returns 1 for success, 0 for failure. 2141da177e4SLinus Torvalds * 2151da177e4SLinus Torvalds * len <= EXT2_NAME_LEN and de != NULL are guaranteed by caller. 2161da177e4SLinus Torvalds */ 2171da177e4SLinus Torvalds static inline int ext2_match (int len, const char * const name, 2181da177e4SLinus Torvalds struct ext2_dir_entry_2 * de) 2191da177e4SLinus Torvalds { 2201da177e4SLinus Torvalds if (len != de->name_len) 2211da177e4SLinus Torvalds return 0; 2221da177e4SLinus Torvalds if (!de->inode) 2231da177e4SLinus Torvalds return 0; 2241da177e4SLinus Torvalds return !memcmp(name, de->name, len); 2251da177e4SLinus Torvalds } 2261da177e4SLinus Torvalds 2271da177e4SLinus Torvalds /* 2281da177e4SLinus Torvalds * p is at least 6 bytes before the end of page 2291da177e4SLinus Torvalds */ 2301da177e4SLinus Torvalds static inline ext2_dirent *ext2_next_entry(ext2_dirent *p) 2311da177e4SLinus Torvalds { 23289910cccSJan Kara return (ext2_dirent *)((char *)p + 23389910cccSJan Kara ext2_rec_len_from_disk(p->rec_len)); 2341da177e4SLinus Torvalds } 2351da177e4SLinus Torvalds 2361da177e4SLinus Torvalds static inline unsigned 2371da177e4SLinus Torvalds ext2_validate_entry(char *base, unsigned offset, unsigned mask) 2381da177e4SLinus Torvalds { 2391da177e4SLinus Torvalds ext2_dirent *de = (ext2_dirent*)(base + offset); 2401da177e4SLinus Torvalds ext2_dirent *p = (ext2_dirent*)(base + (offset&mask)); 2411da177e4SLinus Torvalds while ((char*)p < (char*)de) { 2421da177e4SLinus Torvalds if (p->rec_len == 0) 2431da177e4SLinus Torvalds break; 2441da177e4SLinus Torvalds p = ext2_next_entry(p); 2451da177e4SLinus Torvalds } 2461da177e4SLinus Torvalds return (char *)p - base; 2471da177e4SLinus Torvalds } 2481da177e4SLinus Torvalds 2491da177e4SLinus Torvalds static unsigned char ext2_filetype_table[EXT2_FT_MAX] = { 2501da177e4SLinus Torvalds [EXT2_FT_UNKNOWN] = DT_UNKNOWN, 2511da177e4SLinus Torvalds [EXT2_FT_REG_FILE] = DT_REG, 2521da177e4SLinus Torvalds [EXT2_FT_DIR] = DT_DIR, 2531da177e4SLinus Torvalds [EXT2_FT_CHRDEV] = DT_CHR, 2541da177e4SLinus Torvalds [EXT2_FT_BLKDEV] = DT_BLK, 2551da177e4SLinus Torvalds [EXT2_FT_FIFO] = DT_FIFO, 2561da177e4SLinus Torvalds [EXT2_FT_SOCK] = DT_SOCK, 2571da177e4SLinus Torvalds [EXT2_FT_SYMLINK] = DT_LNK, 2581da177e4SLinus Torvalds }; 2591da177e4SLinus Torvalds 2601da177e4SLinus Torvalds #define S_SHIFT 12 2611da177e4SLinus Torvalds static unsigned char ext2_type_by_mode[S_IFMT >> S_SHIFT] = { 2621da177e4SLinus Torvalds [S_IFREG >> S_SHIFT] = EXT2_FT_REG_FILE, 2631da177e4SLinus Torvalds [S_IFDIR >> S_SHIFT] = EXT2_FT_DIR, 2641da177e4SLinus Torvalds [S_IFCHR >> S_SHIFT] = EXT2_FT_CHRDEV, 2651da177e4SLinus Torvalds [S_IFBLK >> S_SHIFT] = EXT2_FT_BLKDEV, 2661da177e4SLinus Torvalds [S_IFIFO >> S_SHIFT] = EXT2_FT_FIFO, 2671da177e4SLinus Torvalds [S_IFSOCK >> S_SHIFT] = EXT2_FT_SOCK, 2681da177e4SLinus Torvalds [S_IFLNK >> S_SHIFT] = EXT2_FT_SYMLINK, 2691da177e4SLinus Torvalds }; 2701da177e4SLinus Torvalds 2711da177e4SLinus Torvalds static inline void ext2_set_de_type(ext2_dirent *de, struct inode *inode) 2721da177e4SLinus Torvalds { 2731da177e4SLinus Torvalds mode_t mode = inode->i_mode; 2741da177e4SLinus Torvalds if (EXT2_HAS_INCOMPAT_FEATURE(inode->i_sb, EXT2_FEATURE_INCOMPAT_FILETYPE)) 2751da177e4SLinus Torvalds de->file_type = ext2_type_by_mode[(mode & S_IFMT)>>S_SHIFT]; 2761da177e4SLinus Torvalds else 2771da177e4SLinus Torvalds de->file_type = 0; 2781da177e4SLinus Torvalds } 2791da177e4SLinus Torvalds 2801da177e4SLinus Torvalds static int 2811da177e4SLinus Torvalds ext2_readdir (struct file * filp, void * dirent, filldir_t filldir) 2821da177e4SLinus Torvalds { 2831da177e4SLinus Torvalds loff_t pos = filp->f_pos; 284c29c6934SJosef "Jeff" Sipek struct inode *inode = filp->f_path.dentry->d_inode; 2851da177e4SLinus Torvalds struct super_block *sb = inode->i_sb; 2861da177e4SLinus Torvalds unsigned int offset = pos & ~PAGE_CACHE_MASK; 2871da177e4SLinus Torvalds unsigned long n = pos >> PAGE_CACHE_SHIFT; 2881da177e4SLinus Torvalds unsigned long npages = dir_pages(inode); 2891da177e4SLinus Torvalds unsigned chunk_mask = ~(ext2_chunk_size(inode)-1); 2901da177e4SLinus Torvalds unsigned char *types = NULL; 2912d7f2ea9SAl Viro int need_revalidate = filp->f_version != inode->i_version; 2921da177e4SLinus Torvalds 2931da177e4SLinus Torvalds if (pos > inode->i_size - EXT2_DIR_REC_LEN(1)) 2942d7f2ea9SAl Viro return 0; 2951da177e4SLinus Torvalds 2961da177e4SLinus Torvalds if (EXT2_HAS_INCOMPAT_FEATURE(sb, EXT2_FEATURE_INCOMPAT_FILETYPE)) 2971da177e4SLinus Torvalds types = ext2_filetype_table; 2981da177e4SLinus Torvalds 2991da177e4SLinus Torvalds for ( ; n < npages; n++, offset = 0) { 3001da177e4SLinus Torvalds char *kaddr, *limit; 3011da177e4SLinus Torvalds ext2_dirent *de; 302bd39597cSEric Sandeen struct page *page = ext2_get_page(inode, n, 0); 3031da177e4SLinus Torvalds 3041da177e4SLinus Torvalds if (IS_ERR(page)) { 305605afd60SHarvey Harrison ext2_error(sb, __func__, 3061da177e4SLinus Torvalds "bad page in #%lu", 3071da177e4SLinus Torvalds inode->i_ino); 3081da177e4SLinus Torvalds filp->f_pos += PAGE_CACHE_SIZE - offset; 309bbff2860SAkinobu Mita return PTR_ERR(page); 3101da177e4SLinus Torvalds } 3111da177e4SLinus Torvalds kaddr = page_address(page); 3122d7f2ea9SAl Viro if (unlikely(need_revalidate)) { 3132d7f2ea9SAl Viro if (offset) { 3141da177e4SLinus Torvalds offset = ext2_validate_entry(kaddr, offset, chunk_mask); 3152d7f2ea9SAl Viro filp->f_pos = (n<<PAGE_CACHE_SHIFT) + offset; 3162d7f2ea9SAl Viro } 3172d7f2ea9SAl Viro filp->f_version = inode->i_version; 3181da177e4SLinus Torvalds need_revalidate = 0; 3191da177e4SLinus Torvalds } 3201da177e4SLinus Torvalds de = (ext2_dirent *)(kaddr+offset); 3211da177e4SLinus Torvalds limit = kaddr + ext2_last_byte(inode, n) - EXT2_DIR_REC_LEN(1); 3221da177e4SLinus Torvalds for ( ;(char*)de <= limit; de = ext2_next_entry(de)) { 3231da177e4SLinus Torvalds if (de->rec_len == 0) { 324605afd60SHarvey Harrison ext2_error(sb, __func__, 3251da177e4SLinus Torvalds "zero-length directory entry"); 3261da177e4SLinus Torvalds ext2_put_page(page); 3272d7f2ea9SAl Viro return -EIO; 3281da177e4SLinus Torvalds } 3291da177e4SLinus Torvalds if (de->inode) { 3301da177e4SLinus Torvalds int over; 3311da177e4SLinus Torvalds unsigned char d_type = DT_UNKNOWN; 3321da177e4SLinus Torvalds 3331da177e4SLinus Torvalds if (types && de->file_type < EXT2_FT_MAX) 3341da177e4SLinus Torvalds d_type = types[de->file_type]; 3351da177e4SLinus Torvalds 3361da177e4SLinus Torvalds offset = (char *)de - kaddr; 3371da177e4SLinus Torvalds over = filldir(dirent, de->name, de->name_len, 3381da177e4SLinus Torvalds (n<<PAGE_CACHE_SHIFT) | offset, 3391da177e4SLinus Torvalds le32_to_cpu(de->inode), d_type); 3401da177e4SLinus Torvalds if (over) { 3411da177e4SLinus Torvalds ext2_put_page(page); 3422d7f2ea9SAl Viro return 0; 3431da177e4SLinus Torvalds } 3441da177e4SLinus Torvalds } 34589910cccSJan Kara filp->f_pos += ext2_rec_len_from_disk(de->rec_len); 3461da177e4SLinus Torvalds } 3471da177e4SLinus Torvalds ext2_put_page(page); 3481da177e4SLinus Torvalds } 3492d7f2ea9SAl Viro return 0; 3501da177e4SLinus Torvalds } 3511da177e4SLinus Torvalds 3521da177e4SLinus Torvalds /* 3531da177e4SLinus Torvalds * ext2_find_entry() 3541da177e4SLinus Torvalds * 3551da177e4SLinus Torvalds * finds an entry in the specified directory with the wanted name. It 3561da177e4SLinus Torvalds * returns the page in which the entry was found, and the entry itself 3571da177e4SLinus Torvalds * (as a parameter - res_dir). Page is returned mapped and unlocked. 3581da177e4SLinus Torvalds * Entry is guaranteed to be valid. 3591da177e4SLinus Torvalds */ 3601da177e4SLinus Torvalds struct ext2_dir_entry_2 *ext2_find_entry (struct inode * dir, 361a9885444SAl Viro struct qstr *child, struct page ** res_page) 3621da177e4SLinus Torvalds { 363a9885444SAl Viro const char *name = child->name; 364a9885444SAl Viro int namelen = child->len; 3651da177e4SLinus Torvalds unsigned reclen = EXT2_DIR_REC_LEN(namelen); 3661da177e4SLinus Torvalds unsigned long start, n; 3671da177e4SLinus Torvalds unsigned long npages = dir_pages(dir); 3681da177e4SLinus Torvalds struct page *page = NULL; 3691da177e4SLinus Torvalds struct ext2_inode_info *ei = EXT2_I(dir); 3701da177e4SLinus Torvalds ext2_dirent * de; 371bd39597cSEric Sandeen int dir_has_error = 0; 3721da177e4SLinus Torvalds 3731da177e4SLinus Torvalds if (npages == 0) 3741da177e4SLinus Torvalds goto out; 3751da177e4SLinus Torvalds 3761da177e4SLinus Torvalds /* OFFSET_CACHE */ 3771da177e4SLinus Torvalds *res_page = NULL; 3781da177e4SLinus Torvalds 3791da177e4SLinus Torvalds start = ei->i_dir_start_lookup; 3801da177e4SLinus Torvalds if (start >= npages) 3811da177e4SLinus Torvalds start = 0; 3821da177e4SLinus Torvalds n = start; 3831da177e4SLinus Torvalds do { 3841da177e4SLinus Torvalds char *kaddr; 385bd39597cSEric Sandeen page = ext2_get_page(dir, n, dir_has_error); 3861da177e4SLinus Torvalds if (!IS_ERR(page)) { 3871da177e4SLinus Torvalds kaddr = page_address(page); 3881da177e4SLinus Torvalds de = (ext2_dirent *) kaddr; 3891da177e4SLinus Torvalds kaddr += ext2_last_byte(dir, n) - reclen; 3901da177e4SLinus Torvalds while ((char *) de <= kaddr) { 3911da177e4SLinus Torvalds if (de->rec_len == 0) { 392605afd60SHarvey Harrison ext2_error(dir->i_sb, __func__, 3931da177e4SLinus Torvalds "zero-length directory entry"); 3941da177e4SLinus Torvalds ext2_put_page(page); 3951da177e4SLinus Torvalds goto out; 3961da177e4SLinus Torvalds } 3971da177e4SLinus Torvalds if (ext2_match (namelen, name, de)) 3981da177e4SLinus Torvalds goto found; 3991da177e4SLinus Torvalds de = ext2_next_entry(de); 4001da177e4SLinus Torvalds } 4011da177e4SLinus Torvalds ext2_put_page(page); 402bd39597cSEric Sandeen } else 403bd39597cSEric Sandeen dir_has_error = 1; 404bd39597cSEric Sandeen 4051da177e4SLinus Torvalds if (++n >= npages) 4061da177e4SLinus Torvalds n = 0; 407d8adb9ceSEric Sandeen /* next page is past the blocks we've got */ 408d8adb9ceSEric Sandeen if (unlikely(n > (dir->i_blocks >> (PAGE_CACHE_SHIFT - 9)))) { 409605afd60SHarvey Harrison ext2_error(dir->i_sb, __func__, 410d8adb9ceSEric Sandeen "dir %lu size %lld exceeds block count %llu", 411d8adb9ceSEric Sandeen dir->i_ino, dir->i_size, 412d8adb9ceSEric Sandeen (unsigned long long)dir->i_blocks); 413d8adb9ceSEric Sandeen goto out; 414d8adb9ceSEric Sandeen } 4151da177e4SLinus Torvalds } while (n != start); 4161da177e4SLinus Torvalds out: 4171da177e4SLinus Torvalds return NULL; 4181da177e4SLinus Torvalds 4191da177e4SLinus Torvalds found: 4201da177e4SLinus Torvalds *res_page = page; 4211da177e4SLinus Torvalds ei->i_dir_start_lookup = n; 4221da177e4SLinus Torvalds return de; 4231da177e4SLinus Torvalds } 4241da177e4SLinus Torvalds 4251da177e4SLinus Torvalds struct ext2_dir_entry_2 * ext2_dotdot (struct inode *dir, struct page **p) 4261da177e4SLinus Torvalds { 427bd39597cSEric Sandeen struct page *page = ext2_get_page(dir, 0, 0); 4281da177e4SLinus Torvalds ext2_dirent *de = NULL; 4291da177e4SLinus Torvalds 4301da177e4SLinus Torvalds if (!IS_ERR(page)) { 4311da177e4SLinus Torvalds de = ext2_next_entry((ext2_dirent *) page_address(page)); 4321da177e4SLinus Torvalds *p = page; 4331da177e4SLinus Torvalds } 4341da177e4SLinus Torvalds return de; 4351da177e4SLinus Torvalds } 4361da177e4SLinus Torvalds 437a9885444SAl Viro ino_t ext2_inode_by_name(struct inode *dir, struct qstr *child) 4381da177e4SLinus Torvalds { 4391da177e4SLinus Torvalds ino_t res = 0; 4401da177e4SLinus Torvalds struct ext2_dir_entry_2 *de; 4411da177e4SLinus Torvalds struct page *page; 4421da177e4SLinus Torvalds 443a9885444SAl Viro de = ext2_find_entry (dir, child, &page); 4441da177e4SLinus Torvalds if (de) { 4451da177e4SLinus Torvalds res = le32_to_cpu(de->inode); 4467d93a1a5SEvgeniy Dushistov ext2_put_page(page); 4471da177e4SLinus Torvalds } 4481da177e4SLinus Torvalds return res; 4491da177e4SLinus Torvalds } 4501da177e4SLinus Torvalds 4511da177e4SLinus Torvalds /* Releases the page */ 4521da177e4SLinus Torvalds void ext2_set_link(struct inode *dir, struct ext2_dir_entry_2 *de, 4531da177e4SLinus Torvalds struct page *page, struct inode *inode) 4541da177e4SLinus Torvalds { 455f34fb6ecSNick Piggin loff_t pos = page_offset(page) + 456f34fb6ecSNick Piggin (char *) de - (char *) page_address(page); 45789910cccSJan Kara unsigned len = ext2_rec_len_from_disk(de->rec_len); 4581da177e4SLinus Torvalds int err; 4591da177e4SLinus Torvalds 4601da177e4SLinus Torvalds lock_page(page); 461f34fb6ecSNick Piggin err = __ext2_write_begin(NULL, page->mapping, pos, len, 462f34fb6ecSNick Piggin AOP_FLAG_UNINTERRUPTIBLE, &page, NULL); 463309be53dSEric Sesterhenn BUG_ON(err); 4641da177e4SLinus Torvalds de->inode = cpu_to_le32(inode->i_ino); 4651da177e4SLinus Torvalds ext2_set_de_type(de, inode); 466f34fb6ecSNick Piggin err = ext2_commit_chunk(page, pos, len); 4671da177e4SLinus Torvalds ext2_put_page(page); 4681da177e4SLinus Torvalds dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC; 4691da177e4SLinus Torvalds EXT2_I(dir)->i_flags &= ~EXT2_BTREE_FL; 4701da177e4SLinus Torvalds mark_inode_dirty(dir); 4711da177e4SLinus Torvalds } 4721da177e4SLinus Torvalds 4731da177e4SLinus Torvalds /* 4741da177e4SLinus Torvalds * Parent is locked. 4751da177e4SLinus Torvalds */ 4761da177e4SLinus Torvalds int ext2_add_link (struct dentry *dentry, struct inode *inode) 4771da177e4SLinus Torvalds { 4781da177e4SLinus Torvalds struct inode *dir = dentry->d_parent->d_inode; 4791da177e4SLinus Torvalds const char *name = dentry->d_name.name; 4801da177e4SLinus Torvalds int namelen = dentry->d_name.len; 4811da177e4SLinus Torvalds unsigned chunk_size = ext2_chunk_size(dir); 4821da177e4SLinus Torvalds unsigned reclen = EXT2_DIR_REC_LEN(namelen); 4831da177e4SLinus Torvalds unsigned short rec_len, name_len; 4841da177e4SLinus Torvalds struct page *page = NULL; 4851da177e4SLinus Torvalds ext2_dirent * de; 4861da177e4SLinus Torvalds unsigned long npages = dir_pages(dir); 4871da177e4SLinus Torvalds unsigned long n; 4881da177e4SLinus Torvalds char *kaddr; 489f34fb6ecSNick Piggin loff_t pos; 4901da177e4SLinus Torvalds int err; 4911da177e4SLinus Torvalds 4921da177e4SLinus Torvalds /* 4931da177e4SLinus Torvalds * We take care of directory expansion in the same loop. 4941da177e4SLinus Torvalds * This code plays outside i_size, so it locks the page 4951da177e4SLinus Torvalds * to protect that region. 4961da177e4SLinus Torvalds */ 4971da177e4SLinus Torvalds for (n = 0; n <= npages; n++) { 4981da177e4SLinus Torvalds char *dir_end; 4991da177e4SLinus Torvalds 500bd39597cSEric Sandeen page = ext2_get_page(dir, n, 0); 5011da177e4SLinus Torvalds err = PTR_ERR(page); 5021da177e4SLinus Torvalds if (IS_ERR(page)) 5031da177e4SLinus Torvalds goto out; 5041da177e4SLinus Torvalds lock_page(page); 5051da177e4SLinus Torvalds kaddr = page_address(page); 5061da177e4SLinus Torvalds dir_end = kaddr + ext2_last_byte(dir, n); 5071da177e4SLinus Torvalds de = (ext2_dirent *)kaddr; 5081da177e4SLinus Torvalds kaddr += PAGE_CACHE_SIZE - reclen; 5091da177e4SLinus Torvalds while ((char *)de <= kaddr) { 5101da177e4SLinus Torvalds if ((char *)de == dir_end) { 5111da177e4SLinus Torvalds /* We hit i_size */ 5121da177e4SLinus Torvalds name_len = 0; 5131da177e4SLinus Torvalds rec_len = chunk_size; 51489910cccSJan Kara de->rec_len = ext2_rec_len_to_disk(chunk_size); 5151da177e4SLinus Torvalds de->inode = 0; 5161da177e4SLinus Torvalds goto got_it; 5171da177e4SLinus Torvalds } 5181da177e4SLinus Torvalds if (de->rec_len == 0) { 519605afd60SHarvey Harrison ext2_error(dir->i_sb, __func__, 5201da177e4SLinus Torvalds "zero-length directory entry"); 5211da177e4SLinus Torvalds err = -EIO; 5221da177e4SLinus Torvalds goto out_unlock; 5231da177e4SLinus Torvalds } 5241da177e4SLinus Torvalds err = -EEXIST; 5251da177e4SLinus Torvalds if (ext2_match (namelen, name, de)) 5261da177e4SLinus Torvalds goto out_unlock; 5271da177e4SLinus Torvalds name_len = EXT2_DIR_REC_LEN(de->name_len); 52889910cccSJan Kara rec_len = ext2_rec_len_from_disk(de->rec_len); 5291da177e4SLinus Torvalds if (!de->inode && rec_len >= reclen) 5301da177e4SLinus Torvalds goto got_it; 5311da177e4SLinus Torvalds if (rec_len >= name_len + reclen) 5321da177e4SLinus Torvalds goto got_it; 5331da177e4SLinus Torvalds de = (ext2_dirent *) ((char *) de + rec_len); 5341da177e4SLinus Torvalds } 5351da177e4SLinus Torvalds unlock_page(page); 5361da177e4SLinus Torvalds ext2_put_page(page); 5371da177e4SLinus Torvalds } 5381da177e4SLinus Torvalds BUG(); 5391da177e4SLinus Torvalds return -EINVAL; 5401da177e4SLinus Torvalds 5411da177e4SLinus Torvalds got_it: 542f34fb6ecSNick Piggin pos = page_offset(page) + 543f34fb6ecSNick Piggin (char*)de - (char*)page_address(page); 544f34fb6ecSNick Piggin err = __ext2_write_begin(NULL, page->mapping, pos, rec_len, 0, 545f34fb6ecSNick Piggin &page, NULL); 5461da177e4SLinus Torvalds if (err) 5471da177e4SLinus Torvalds goto out_unlock; 5481da177e4SLinus Torvalds if (de->inode) { 5491da177e4SLinus Torvalds ext2_dirent *de1 = (ext2_dirent *) ((char *) de + name_len); 55089910cccSJan Kara de1->rec_len = ext2_rec_len_to_disk(rec_len - name_len); 55189910cccSJan Kara de->rec_len = ext2_rec_len_to_disk(name_len); 5521da177e4SLinus Torvalds de = de1; 5531da177e4SLinus Torvalds } 5541da177e4SLinus Torvalds de->name_len = namelen; 5551da177e4SLinus Torvalds memcpy(de->name, name, namelen); 5561da177e4SLinus Torvalds de->inode = cpu_to_le32(inode->i_ino); 5571da177e4SLinus Torvalds ext2_set_de_type (de, inode); 558f34fb6ecSNick Piggin err = ext2_commit_chunk(page, pos, rec_len); 5591da177e4SLinus Torvalds dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC; 5601da177e4SLinus Torvalds EXT2_I(dir)->i_flags &= ~EXT2_BTREE_FL; 5611da177e4SLinus Torvalds mark_inode_dirty(dir); 5621da177e4SLinus Torvalds /* OFFSET_CACHE */ 5631da177e4SLinus Torvalds out_put: 5641da177e4SLinus Torvalds ext2_put_page(page); 5651da177e4SLinus Torvalds out: 5661da177e4SLinus Torvalds return err; 5671da177e4SLinus Torvalds out_unlock: 5681da177e4SLinus Torvalds unlock_page(page); 5691da177e4SLinus Torvalds goto out_put; 5701da177e4SLinus Torvalds } 5711da177e4SLinus Torvalds 5721da177e4SLinus Torvalds /* 5731da177e4SLinus Torvalds * ext2_delete_entry deletes a directory entry by merging it with the 5741da177e4SLinus Torvalds * previous entry. Page is up-to-date. Releases the page. 5751da177e4SLinus Torvalds */ 5761da177e4SLinus Torvalds int ext2_delete_entry (struct ext2_dir_entry_2 * dir, struct page * page ) 5771da177e4SLinus Torvalds { 5781da177e4SLinus Torvalds struct address_space *mapping = page->mapping; 5791da177e4SLinus Torvalds struct inode *inode = mapping->host; 5801da177e4SLinus Torvalds char *kaddr = page_address(page); 5811da177e4SLinus Torvalds unsigned from = ((char*)dir - kaddr) & ~(ext2_chunk_size(inode)-1); 58289910cccSJan Kara unsigned to = ((char *)dir - kaddr) + 58389910cccSJan Kara ext2_rec_len_from_disk(dir->rec_len); 584f34fb6ecSNick Piggin loff_t pos; 5851da177e4SLinus Torvalds ext2_dirent * pde = NULL; 5861da177e4SLinus Torvalds ext2_dirent * de = (ext2_dirent *) (kaddr + from); 5871da177e4SLinus Torvalds int err; 5881da177e4SLinus Torvalds 5891da177e4SLinus Torvalds while ((char*)de < (char*)dir) { 5901da177e4SLinus Torvalds if (de->rec_len == 0) { 591605afd60SHarvey Harrison ext2_error(inode->i_sb, __func__, 5921da177e4SLinus Torvalds "zero-length directory entry"); 5931da177e4SLinus Torvalds err = -EIO; 5941da177e4SLinus Torvalds goto out; 5951da177e4SLinus Torvalds } 5961da177e4SLinus Torvalds pde = de; 5971da177e4SLinus Torvalds de = ext2_next_entry(de); 5981da177e4SLinus Torvalds } 5991da177e4SLinus Torvalds if (pde) 6001da177e4SLinus Torvalds from = (char*)pde - (char*)page_address(page); 601f34fb6ecSNick Piggin pos = page_offset(page) + from; 6021da177e4SLinus Torvalds lock_page(page); 603f34fb6ecSNick Piggin err = __ext2_write_begin(NULL, page->mapping, pos, to - from, 0, 604f34fb6ecSNick Piggin &page, NULL); 605309be53dSEric Sesterhenn BUG_ON(err); 6061da177e4SLinus Torvalds if (pde) 60789910cccSJan Kara pde->rec_len = ext2_rec_len_to_disk(to - from); 6081da177e4SLinus Torvalds dir->inode = 0; 609f34fb6ecSNick Piggin err = ext2_commit_chunk(page, pos, to - from); 6101da177e4SLinus Torvalds inode->i_ctime = inode->i_mtime = CURRENT_TIME_SEC; 6111da177e4SLinus Torvalds EXT2_I(inode)->i_flags &= ~EXT2_BTREE_FL; 6121da177e4SLinus Torvalds mark_inode_dirty(inode); 6131da177e4SLinus Torvalds out: 6141da177e4SLinus Torvalds ext2_put_page(page); 6151da177e4SLinus Torvalds return err; 6161da177e4SLinus Torvalds } 6171da177e4SLinus Torvalds 6181da177e4SLinus Torvalds /* 6191da177e4SLinus Torvalds * Set the first fragment of directory. 6201da177e4SLinus Torvalds */ 6211da177e4SLinus Torvalds int ext2_make_empty(struct inode *inode, struct inode *parent) 6221da177e4SLinus Torvalds { 6231da177e4SLinus Torvalds struct address_space *mapping = inode->i_mapping; 6241da177e4SLinus Torvalds struct page *page = grab_cache_page(mapping, 0); 6251da177e4SLinus Torvalds unsigned chunk_size = ext2_chunk_size(inode); 6261da177e4SLinus Torvalds struct ext2_dir_entry_2 * de; 6271da177e4SLinus Torvalds int err; 6281da177e4SLinus Torvalds void *kaddr; 6291da177e4SLinus Torvalds 6301da177e4SLinus Torvalds if (!page) 6311da177e4SLinus Torvalds return -ENOMEM; 632f34fb6ecSNick Piggin 633f34fb6ecSNick Piggin err = __ext2_write_begin(NULL, page->mapping, 0, chunk_size, 0, 634f34fb6ecSNick Piggin &page, NULL); 6351da177e4SLinus Torvalds if (err) { 6361da177e4SLinus Torvalds unlock_page(page); 6371da177e4SLinus Torvalds goto fail; 6381da177e4SLinus Torvalds } 6391da177e4SLinus Torvalds kaddr = kmap_atomic(page, KM_USER0); 6401da177e4SLinus Torvalds memset(kaddr, 0, chunk_size); 6411da177e4SLinus Torvalds de = (struct ext2_dir_entry_2 *)kaddr; 6421da177e4SLinus Torvalds de->name_len = 1; 64389910cccSJan Kara de->rec_len = ext2_rec_len_to_disk(EXT2_DIR_REC_LEN(1)); 6441da177e4SLinus Torvalds memcpy (de->name, ".\0\0", 4); 6451da177e4SLinus Torvalds de->inode = cpu_to_le32(inode->i_ino); 6461da177e4SLinus Torvalds ext2_set_de_type (de, inode); 6471da177e4SLinus Torvalds 6481da177e4SLinus Torvalds de = (struct ext2_dir_entry_2 *)(kaddr + EXT2_DIR_REC_LEN(1)); 6491da177e4SLinus Torvalds de->name_len = 2; 65089910cccSJan Kara de->rec_len = ext2_rec_len_to_disk(chunk_size - EXT2_DIR_REC_LEN(1)); 6511da177e4SLinus Torvalds de->inode = cpu_to_le32(parent->i_ino); 6521da177e4SLinus Torvalds memcpy (de->name, "..\0", 4); 6531da177e4SLinus Torvalds ext2_set_de_type (de, inode); 6541da177e4SLinus Torvalds kunmap_atomic(kaddr, KM_USER0); 6551da177e4SLinus Torvalds err = ext2_commit_chunk(page, 0, chunk_size); 6561da177e4SLinus Torvalds fail: 6571da177e4SLinus Torvalds page_cache_release(page); 6581da177e4SLinus Torvalds return err; 6591da177e4SLinus Torvalds } 6601da177e4SLinus Torvalds 6611da177e4SLinus Torvalds /* 6621da177e4SLinus Torvalds * routine to check that the specified directory is empty (for rmdir) 6631da177e4SLinus Torvalds */ 6641da177e4SLinus Torvalds int ext2_empty_dir (struct inode * inode) 6651da177e4SLinus Torvalds { 6661da177e4SLinus Torvalds struct page *page = NULL; 6671da177e4SLinus Torvalds unsigned long i, npages = dir_pages(inode); 668bd39597cSEric Sandeen int dir_has_error = 0; 6691da177e4SLinus Torvalds 6701da177e4SLinus Torvalds for (i = 0; i < npages; i++) { 6711da177e4SLinus Torvalds char *kaddr; 6721da177e4SLinus Torvalds ext2_dirent * de; 673bd39597cSEric Sandeen page = ext2_get_page(inode, i, dir_has_error); 6741da177e4SLinus Torvalds 675bd39597cSEric Sandeen if (IS_ERR(page)) { 676bd39597cSEric Sandeen dir_has_error = 1; 6771da177e4SLinus Torvalds continue; 678bd39597cSEric Sandeen } 6791da177e4SLinus Torvalds 6801da177e4SLinus Torvalds kaddr = page_address(page); 6811da177e4SLinus Torvalds de = (ext2_dirent *)kaddr; 6821da177e4SLinus Torvalds kaddr += ext2_last_byte(inode, i) - EXT2_DIR_REC_LEN(1); 6831da177e4SLinus Torvalds 6841da177e4SLinus Torvalds while ((char *)de <= kaddr) { 6851da177e4SLinus Torvalds if (de->rec_len == 0) { 686605afd60SHarvey Harrison ext2_error(inode->i_sb, __func__, 6871da177e4SLinus Torvalds "zero-length directory entry"); 6881da177e4SLinus Torvalds printk("kaddr=%p, de=%p\n", kaddr, de); 6891da177e4SLinus Torvalds goto not_empty; 6901da177e4SLinus Torvalds } 6911da177e4SLinus Torvalds if (de->inode != 0) { 6921da177e4SLinus Torvalds /* check for . and .. */ 6931da177e4SLinus Torvalds if (de->name[0] != '.') 6941da177e4SLinus Torvalds goto not_empty; 6951da177e4SLinus Torvalds if (de->name_len > 2) 6961da177e4SLinus Torvalds goto not_empty; 6971da177e4SLinus Torvalds if (de->name_len < 2) { 6981da177e4SLinus Torvalds if (de->inode != 6991da177e4SLinus Torvalds cpu_to_le32(inode->i_ino)) 7001da177e4SLinus Torvalds goto not_empty; 7011da177e4SLinus Torvalds } else if (de->name[1] != '.') 7021da177e4SLinus Torvalds goto not_empty; 7031da177e4SLinus Torvalds } 7041da177e4SLinus Torvalds de = ext2_next_entry(de); 7051da177e4SLinus Torvalds } 7061da177e4SLinus Torvalds ext2_put_page(page); 7071da177e4SLinus Torvalds } 7081da177e4SLinus Torvalds return 1; 7091da177e4SLinus Torvalds 7101da177e4SLinus Torvalds not_empty: 7111da177e4SLinus Torvalds ext2_put_page(page); 7121da177e4SLinus Torvalds return 0; 7131da177e4SLinus Torvalds } 7141da177e4SLinus Torvalds 7154b6f5d20SArjan van de Ven const struct file_operations ext2_dir_operations = { 7161da177e4SLinus Torvalds .llseek = generic_file_llseek, 7171da177e4SLinus Torvalds .read = generic_read_dir, 7181da177e4SLinus Torvalds .readdir = ext2_readdir, 71914f9f7b2SAndi Kleen .unlocked_ioctl = ext2_ioctl, 720e322ff07SDavid Howells #ifdef CONFIG_COMPAT 721e322ff07SDavid Howells .compat_ioctl = ext2_compat_ioctl, 722e322ff07SDavid Howells #endif 7231da177e4SLinus Torvalds .fsync = ext2_sync_file, 7241da177e4SLinus Torvalds }; 725