162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * linux/fs/ext2/dir.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 1992, 1993, 1994, 1995 662306a36Sopenharmony_ci * Remy Card (card@masi.ibp.fr) 762306a36Sopenharmony_ci * Laboratoire MASI - Institut Blaise Pascal 862306a36Sopenharmony_ci * Universite Pierre et Marie Curie (Paris VI) 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * from 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * linux/fs/minix/dir.c 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * Copyright (C) 1991, 1992 Linus Torvalds 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci * ext2 directory handling functions 1762306a36Sopenharmony_ci * 1862306a36Sopenharmony_ci * Big-endian to little-endian byte-swapping/bitmaps by 1962306a36Sopenharmony_ci * David S. Miller (davem@caip.rutgers.edu), 1995 2062306a36Sopenharmony_ci * 2162306a36Sopenharmony_ci * All code that works with directory layout had been switched to pagecache 2262306a36Sopenharmony_ci * and moved here. AV 2362306a36Sopenharmony_ci */ 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include "ext2.h" 2662306a36Sopenharmony_ci#include <linux/buffer_head.h> 2762306a36Sopenharmony_ci#include <linux/pagemap.h> 2862306a36Sopenharmony_ci#include <linux/swap.h> 2962306a36Sopenharmony_ci#include <linux/iversion.h> 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_citypedef struct ext2_dir_entry_2 ext2_dirent; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/* 3462306a36Sopenharmony_ci * Tests against MAX_REC_LEN etc were put in place for 64k block 3562306a36Sopenharmony_ci * sizes; if that is not possible on this arch, we can skip 3662306a36Sopenharmony_ci * those tests and speed things up. 3762306a36Sopenharmony_ci */ 3862306a36Sopenharmony_cistatic inline unsigned ext2_rec_len_from_disk(__le16 dlen) 3962306a36Sopenharmony_ci{ 4062306a36Sopenharmony_ci unsigned len = le16_to_cpu(dlen); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#if (PAGE_SIZE >= 65536) 4362306a36Sopenharmony_ci if (len == EXT2_MAX_REC_LEN) 4462306a36Sopenharmony_ci return 1 << 16; 4562306a36Sopenharmony_ci#endif 4662306a36Sopenharmony_ci return len; 4762306a36Sopenharmony_ci} 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic inline __le16 ext2_rec_len_to_disk(unsigned len) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci#if (PAGE_SIZE >= 65536) 5262306a36Sopenharmony_ci if (len == (1 << 16)) 5362306a36Sopenharmony_ci return cpu_to_le16(EXT2_MAX_REC_LEN); 5462306a36Sopenharmony_ci else 5562306a36Sopenharmony_ci BUG_ON(len > (1 << 16)); 5662306a36Sopenharmony_ci#endif 5762306a36Sopenharmony_ci return cpu_to_le16(len); 5862306a36Sopenharmony_ci} 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci/* 6162306a36Sopenharmony_ci * ext2 uses block-sized chunks. Arguably, sector-sized ones would be 6262306a36Sopenharmony_ci * more robust, but we have what we have 6362306a36Sopenharmony_ci */ 6462306a36Sopenharmony_cistatic inline unsigned ext2_chunk_size(struct inode *inode) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci return inode->i_sb->s_blocksize; 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci/* 7062306a36Sopenharmony_ci * Return the offset into page `page_nr' of the last valid 7162306a36Sopenharmony_ci * byte in that page, plus one. 7262306a36Sopenharmony_ci */ 7362306a36Sopenharmony_cistatic unsigned 7462306a36Sopenharmony_ciext2_last_byte(struct inode *inode, unsigned long page_nr) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci unsigned last_byte = inode->i_size; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci last_byte -= page_nr << PAGE_SHIFT; 7962306a36Sopenharmony_ci if (last_byte > PAGE_SIZE) 8062306a36Sopenharmony_ci last_byte = PAGE_SIZE; 8162306a36Sopenharmony_ci return last_byte; 8262306a36Sopenharmony_ci} 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic void ext2_commit_chunk(struct page *page, loff_t pos, unsigned len) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci struct address_space *mapping = page->mapping; 8762306a36Sopenharmony_ci struct inode *dir = mapping->host; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci inode_inc_iversion(dir); 9062306a36Sopenharmony_ci block_write_end(NULL, mapping, pos, len, len, page, NULL); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci if (pos+len > dir->i_size) { 9362306a36Sopenharmony_ci i_size_write(dir, pos+len); 9462306a36Sopenharmony_ci mark_inode_dirty(dir); 9562306a36Sopenharmony_ci } 9662306a36Sopenharmony_ci unlock_page(page); 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic bool ext2_check_page(struct page *page, int quiet, char *kaddr) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci struct inode *dir = page->mapping->host; 10262306a36Sopenharmony_ci struct super_block *sb = dir->i_sb; 10362306a36Sopenharmony_ci unsigned chunk_size = ext2_chunk_size(dir); 10462306a36Sopenharmony_ci u32 max_inumber = le32_to_cpu(EXT2_SB(sb)->s_es->s_inodes_count); 10562306a36Sopenharmony_ci unsigned offs, rec_len; 10662306a36Sopenharmony_ci unsigned limit = PAGE_SIZE; 10762306a36Sopenharmony_ci ext2_dirent *p; 10862306a36Sopenharmony_ci char *error; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci if ((dir->i_size >> PAGE_SHIFT) == page->index) { 11162306a36Sopenharmony_ci limit = dir->i_size & ~PAGE_MASK; 11262306a36Sopenharmony_ci if (limit & (chunk_size - 1)) 11362306a36Sopenharmony_ci goto Ebadsize; 11462306a36Sopenharmony_ci if (!limit) 11562306a36Sopenharmony_ci goto out; 11662306a36Sopenharmony_ci } 11762306a36Sopenharmony_ci for (offs = 0; offs <= limit - EXT2_DIR_REC_LEN(1); offs += rec_len) { 11862306a36Sopenharmony_ci p = (ext2_dirent *)(kaddr + offs); 11962306a36Sopenharmony_ci rec_len = ext2_rec_len_from_disk(p->rec_len); 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci if (unlikely(rec_len < EXT2_DIR_REC_LEN(1))) 12262306a36Sopenharmony_ci goto Eshort; 12362306a36Sopenharmony_ci if (unlikely(rec_len & 3)) 12462306a36Sopenharmony_ci goto Ealign; 12562306a36Sopenharmony_ci if (unlikely(rec_len < EXT2_DIR_REC_LEN(p->name_len))) 12662306a36Sopenharmony_ci goto Enamelen; 12762306a36Sopenharmony_ci if (unlikely(((offs + rec_len - 1) ^ offs) & ~(chunk_size-1))) 12862306a36Sopenharmony_ci goto Espan; 12962306a36Sopenharmony_ci if (unlikely(le32_to_cpu(p->inode) > max_inumber)) 13062306a36Sopenharmony_ci goto Einumber; 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci if (offs != limit) 13362306a36Sopenharmony_ci goto Eend; 13462306a36Sopenharmony_ciout: 13562306a36Sopenharmony_ci SetPageChecked(page); 13662306a36Sopenharmony_ci return true; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci /* Too bad, we had an error */ 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ciEbadsize: 14162306a36Sopenharmony_ci if (!quiet) 14262306a36Sopenharmony_ci ext2_error(sb, __func__, 14362306a36Sopenharmony_ci "size of directory #%lu is not a multiple " 14462306a36Sopenharmony_ci "of chunk size", dir->i_ino); 14562306a36Sopenharmony_ci goto fail; 14662306a36Sopenharmony_ciEshort: 14762306a36Sopenharmony_ci error = "rec_len is smaller than minimal"; 14862306a36Sopenharmony_ci goto bad_entry; 14962306a36Sopenharmony_ciEalign: 15062306a36Sopenharmony_ci error = "unaligned directory entry"; 15162306a36Sopenharmony_ci goto bad_entry; 15262306a36Sopenharmony_ciEnamelen: 15362306a36Sopenharmony_ci error = "rec_len is too small for name_len"; 15462306a36Sopenharmony_ci goto bad_entry; 15562306a36Sopenharmony_ciEspan: 15662306a36Sopenharmony_ci error = "directory entry across blocks"; 15762306a36Sopenharmony_ci goto bad_entry; 15862306a36Sopenharmony_ciEinumber: 15962306a36Sopenharmony_ci error = "inode out of bounds"; 16062306a36Sopenharmony_cibad_entry: 16162306a36Sopenharmony_ci if (!quiet) 16262306a36Sopenharmony_ci ext2_error(sb, __func__, "bad entry in directory #%lu: : %s - " 16362306a36Sopenharmony_ci "offset=%lu, inode=%lu, rec_len=%d, name_len=%d", 16462306a36Sopenharmony_ci dir->i_ino, error, (page->index<<PAGE_SHIFT)+offs, 16562306a36Sopenharmony_ci (unsigned long) le32_to_cpu(p->inode), 16662306a36Sopenharmony_ci rec_len, p->name_len); 16762306a36Sopenharmony_ci goto fail; 16862306a36Sopenharmony_ciEend: 16962306a36Sopenharmony_ci if (!quiet) { 17062306a36Sopenharmony_ci p = (ext2_dirent *)(kaddr + offs); 17162306a36Sopenharmony_ci ext2_error(sb, "ext2_check_page", 17262306a36Sopenharmony_ci "entry in directory #%lu spans the page boundary" 17362306a36Sopenharmony_ci "offset=%lu, inode=%lu", 17462306a36Sopenharmony_ci dir->i_ino, (page->index<<PAGE_SHIFT)+offs, 17562306a36Sopenharmony_ci (unsigned long) le32_to_cpu(p->inode)); 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_cifail: 17862306a36Sopenharmony_ci SetPageError(page); 17962306a36Sopenharmony_ci return false; 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci/* 18362306a36Sopenharmony_ci * Calls to ext2_get_page()/ext2_put_page() must be nested according to the 18462306a36Sopenharmony_ci * rules documented in kmap_local_page()/kunmap_local(). 18562306a36Sopenharmony_ci * 18662306a36Sopenharmony_ci * NOTE: ext2_find_entry() and ext2_dotdot() act as a call to ext2_get_page() 18762306a36Sopenharmony_ci * and should be treated as a call to ext2_get_page() for nesting purposes. 18862306a36Sopenharmony_ci */ 18962306a36Sopenharmony_cistatic void *ext2_get_page(struct inode *dir, unsigned long n, 19062306a36Sopenharmony_ci int quiet, struct page **page) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci struct address_space *mapping = dir->i_mapping; 19362306a36Sopenharmony_ci struct folio *folio = read_mapping_folio(mapping, n, NULL); 19462306a36Sopenharmony_ci void *page_addr; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci if (IS_ERR(folio)) 19762306a36Sopenharmony_ci return ERR_CAST(folio); 19862306a36Sopenharmony_ci page_addr = kmap_local_folio(folio, n & (folio_nr_pages(folio) - 1)); 19962306a36Sopenharmony_ci if (unlikely(!folio_test_checked(folio))) { 20062306a36Sopenharmony_ci if (!ext2_check_page(&folio->page, quiet, page_addr)) 20162306a36Sopenharmony_ci goto fail; 20262306a36Sopenharmony_ci } 20362306a36Sopenharmony_ci *page = &folio->page; 20462306a36Sopenharmony_ci return page_addr; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cifail: 20762306a36Sopenharmony_ci ext2_put_page(&folio->page, page_addr); 20862306a36Sopenharmony_ci return ERR_PTR(-EIO); 20962306a36Sopenharmony_ci} 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci/* 21262306a36Sopenharmony_ci * NOTE! unlike strncmp, ext2_match returns 1 for success, 0 for failure. 21362306a36Sopenharmony_ci * 21462306a36Sopenharmony_ci * len <= EXT2_NAME_LEN and de != NULL are guaranteed by caller. 21562306a36Sopenharmony_ci */ 21662306a36Sopenharmony_cistatic inline int ext2_match (int len, const char * const name, 21762306a36Sopenharmony_ci struct ext2_dir_entry_2 * de) 21862306a36Sopenharmony_ci{ 21962306a36Sopenharmony_ci if (len != de->name_len) 22062306a36Sopenharmony_ci return 0; 22162306a36Sopenharmony_ci if (!de->inode) 22262306a36Sopenharmony_ci return 0; 22362306a36Sopenharmony_ci return !memcmp(name, de->name, len); 22462306a36Sopenharmony_ci} 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci/* 22762306a36Sopenharmony_ci * p is at least 6 bytes before the end of page 22862306a36Sopenharmony_ci */ 22962306a36Sopenharmony_cistatic inline ext2_dirent *ext2_next_entry(ext2_dirent *p) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci return (ext2_dirent *)((char *)p + 23262306a36Sopenharmony_ci ext2_rec_len_from_disk(p->rec_len)); 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_cistatic inline unsigned 23662306a36Sopenharmony_ciext2_validate_entry(char *base, unsigned offset, unsigned mask) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci ext2_dirent *de = (ext2_dirent*)(base + offset); 23962306a36Sopenharmony_ci ext2_dirent *p = (ext2_dirent*)(base + (offset&mask)); 24062306a36Sopenharmony_ci while ((char*)p < (char*)de) { 24162306a36Sopenharmony_ci if (p->rec_len == 0) 24262306a36Sopenharmony_ci break; 24362306a36Sopenharmony_ci p = ext2_next_entry(p); 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci return offset_in_page(p); 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_cistatic inline void ext2_set_de_type(ext2_dirent *de, struct inode *inode) 24962306a36Sopenharmony_ci{ 25062306a36Sopenharmony_ci if (EXT2_HAS_INCOMPAT_FEATURE(inode->i_sb, EXT2_FEATURE_INCOMPAT_FILETYPE)) 25162306a36Sopenharmony_ci de->file_type = fs_umode_to_ftype(inode->i_mode); 25262306a36Sopenharmony_ci else 25362306a36Sopenharmony_ci de->file_type = 0; 25462306a36Sopenharmony_ci} 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_cistatic int 25762306a36Sopenharmony_ciext2_readdir(struct file *file, struct dir_context *ctx) 25862306a36Sopenharmony_ci{ 25962306a36Sopenharmony_ci loff_t pos = ctx->pos; 26062306a36Sopenharmony_ci struct inode *inode = file_inode(file); 26162306a36Sopenharmony_ci struct super_block *sb = inode->i_sb; 26262306a36Sopenharmony_ci unsigned int offset = pos & ~PAGE_MASK; 26362306a36Sopenharmony_ci unsigned long n = pos >> PAGE_SHIFT; 26462306a36Sopenharmony_ci unsigned long npages = dir_pages(inode); 26562306a36Sopenharmony_ci unsigned chunk_mask = ~(ext2_chunk_size(inode)-1); 26662306a36Sopenharmony_ci bool need_revalidate = !inode_eq_iversion(inode, file->f_version); 26762306a36Sopenharmony_ci bool has_filetype; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci if (pos > inode->i_size - EXT2_DIR_REC_LEN(1)) 27062306a36Sopenharmony_ci return 0; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci has_filetype = 27362306a36Sopenharmony_ci EXT2_HAS_INCOMPAT_FEATURE(sb, EXT2_FEATURE_INCOMPAT_FILETYPE); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci for ( ; n < npages; n++, offset = 0) { 27662306a36Sopenharmony_ci ext2_dirent *de; 27762306a36Sopenharmony_ci struct page *page; 27862306a36Sopenharmony_ci char *kaddr = ext2_get_page(inode, n, 0, &page); 27962306a36Sopenharmony_ci char *limit; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci if (IS_ERR(kaddr)) { 28262306a36Sopenharmony_ci ext2_error(sb, __func__, 28362306a36Sopenharmony_ci "bad page in #%lu", 28462306a36Sopenharmony_ci inode->i_ino); 28562306a36Sopenharmony_ci ctx->pos += PAGE_SIZE - offset; 28662306a36Sopenharmony_ci return PTR_ERR(kaddr); 28762306a36Sopenharmony_ci } 28862306a36Sopenharmony_ci if (unlikely(need_revalidate)) { 28962306a36Sopenharmony_ci if (offset) { 29062306a36Sopenharmony_ci offset = ext2_validate_entry(kaddr, offset, chunk_mask); 29162306a36Sopenharmony_ci ctx->pos = (n<<PAGE_SHIFT) + offset; 29262306a36Sopenharmony_ci } 29362306a36Sopenharmony_ci file->f_version = inode_query_iversion(inode); 29462306a36Sopenharmony_ci need_revalidate = false; 29562306a36Sopenharmony_ci } 29662306a36Sopenharmony_ci de = (ext2_dirent *)(kaddr+offset); 29762306a36Sopenharmony_ci limit = kaddr + ext2_last_byte(inode, n) - EXT2_DIR_REC_LEN(1); 29862306a36Sopenharmony_ci for ( ;(char*)de <= limit; de = ext2_next_entry(de)) { 29962306a36Sopenharmony_ci if (de->rec_len == 0) { 30062306a36Sopenharmony_ci ext2_error(sb, __func__, 30162306a36Sopenharmony_ci "zero-length directory entry"); 30262306a36Sopenharmony_ci ext2_put_page(page, de); 30362306a36Sopenharmony_ci return -EIO; 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci if (de->inode) { 30662306a36Sopenharmony_ci unsigned char d_type = DT_UNKNOWN; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci if (has_filetype) 30962306a36Sopenharmony_ci d_type = fs_ftype_to_dtype(de->file_type); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci if (!dir_emit(ctx, de->name, de->name_len, 31262306a36Sopenharmony_ci le32_to_cpu(de->inode), 31362306a36Sopenharmony_ci d_type)) { 31462306a36Sopenharmony_ci ext2_put_page(page, de); 31562306a36Sopenharmony_ci return 0; 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci } 31862306a36Sopenharmony_ci ctx->pos += ext2_rec_len_from_disk(de->rec_len); 31962306a36Sopenharmony_ci } 32062306a36Sopenharmony_ci ext2_put_page(page, kaddr); 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci return 0; 32362306a36Sopenharmony_ci} 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci/* 32662306a36Sopenharmony_ci * ext2_find_entry() 32762306a36Sopenharmony_ci * 32862306a36Sopenharmony_ci * finds an entry in the specified directory with the wanted name. It 32962306a36Sopenharmony_ci * returns the page in which the entry was found (as a parameter - res_page), 33062306a36Sopenharmony_ci * and the entry itself. Page is returned mapped and unlocked. 33162306a36Sopenharmony_ci * Entry is guaranteed to be valid. 33262306a36Sopenharmony_ci * 33362306a36Sopenharmony_ci * On Success ext2_put_page() should be called on *res_page. 33462306a36Sopenharmony_ci * 33562306a36Sopenharmony_ci * NOTE: Calls to ext2_get_page()/ext2_put_page() must be nested according to 33662306a36Sopenharmony_ci * the rules documented in kmap_local_page()/kunmap_local(). 33762306a36Sopenharmony_ci * 33862306a36Sopenharmony_ci * ext2_find_entry() and ext2_dotdot() act as a call to ext2_get_page() and 33962306a36Sopenharmony_ci * should be treated as a call to ext2_get_page() for nesting purposes. 34062306a36Sopenharmony_ci */ 34162306a36Sopenharmony_cistruct ext2_dir_entry_2 *ext2_find_entry (struct inode *dir, 34262306a36Sopenharmony_ci const struct qstr *child, struct page **res_page) 34362306a36Sopenharmony_ci{ 34462306a36Sopenharmony_ci const char *name = child->name; 34562306a36Sopenharmony_ci int namelen = child->len; 34662306a36Sopenharmony_ci unsigned reclen = EXT2_DIR_REC_LEN(namelen); 34762306a36Sopenharmony_ci unsigned long start, n; 34862306a36Sopenharmony_ci unsigned long npages = dir_pages(dir); 34962306a36Sopenharmony_ci struct page *page = NULL; 35062306a36Sopenharmony_ci struct ext2_inode_info *ei = EXT2_I(dir); 35162306a36Sopenharmony_ci ext2_dirent * de; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci if (npages == 0) 35462306a36Sopenharmony_ci goto out; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci /* OFFSET_CACHE */ 35762306a36Sopenharmony_ci *res_page = NULL; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci start = ei->i_dir_start_lookup; 36062306a36Sopenharmony_ci if (start >= npages) 36162306a36Sopenharmony_ci start = 0; 36262306a36Sopenharmony_ci n = start; 36362306a36Sopenharmony_ci do { 36462306a36Sopenharmony_ci char *kaddr = ext2_get_page(dir, n, 0, &page); 36562306a36Sopenharmony_ci if (IS_ERR(kaddr)) 36662306a36Sopenharmony_ci return ERR_CAST(kaddr); 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci de = (ext2_dirent *) kaddr; 36962306a36Sopenharmony_ci kaddr += ext2_last_byte(dir, n) - reclen; 37062306a36Sopenharmony_ci while ((char *) de <= kaddr) { 37162306a36Sopenharmony_ci if (de->rec_len == 0) { 37262306a36Sopenharmony_ci ext2_error(dir->i_sb, __func__, 37362306a36Sopenharmony_ci "zero-length directory entry"); 37462306a36Sopenharmony_ci ext2_put_page(page, de); 37562306a36Sopenharmony_ci goto out; 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci if (ext2_match(namelen, name, de)) 37862306a36Sopenharmony_ci goto found; 37962306a36Sopenharmony_ci de = ext2_next_entry(de); 38062306a36Sopenharmony_ci } 38162306a36Sopenharmony_ci ext2_put_page(page, kaddr); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci if (++n >= npages) 38462306a36Sopenharmony_ci n = 0; 38562306a36Sopenharmony_ci /* next page is past the blocks we've got */ 38662306a36Sopenharmony_ci if (unlikely(n > (dir->i_blocks >> (PAGE_SHIFT - 9)))) { 38762306a36Sopenharmony_ci ext2_error(dir->i_sb, __func__, 38862306a36Sopenharmony_ci "dir %lu size %lld exceeds block count %llu", 38962306a36Sopenharmony_ci dir->i_ino, dir->i_size, 39062306a36Sopenharmony_ci (unsigned long long)dir->i_blocks); 39162306a36Sopenharmony_ci goto out; 39262306a36Sopenharmony_ci } 39362306a36Sopenharmony_ci } while (n != start); 39462306a36Sopenharmony_ciout: 39562306a36Sopenharmony_ci return ERR_PTR(-ENOENT); 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_cifound: 39862306a36Sopenharmony_ci *res_page = page; 39962306a36Sopenharmony_ci ei->i_dir_start_lookup = n; 40062306a36Sopenharmony_ci return de; 40162306a36Sopenharmony_ci} 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci/* 40462306a36Sopenharmony_ci * Return the '..' directory entry and the page in which the entry was found 40562306a36Sopenharmony_ci * (as a parameter - p). 40662306a36Sopenharmony_ci * 40762306a36Sopenharmony_ci * On Success ext2_put_page() should be called on *p. 40862306a36Sopenharmony_ci * 40962306a36Sopenharmony_ci * NOTE: Calls to ext2_get_page()/ext2_put_page() must be nested according to 41062306a36Sopenharmony_ci * the rules documented in kmap_local_page()/kunmap_local(). 41162306a36Sopenharmony_ci * 41262306a36Sopenharmony_ci * ext2_find_entry() and ext2_dotdot() act as a call to ext2_get_page() and 41362306a36Sopenharmony_ci * should be treated as a call to ext2_get_page() for nesting purposes. 41462306a36Sopenharmony_ci */ 41562306a36Sopenharmony_cistruct ext2_dir_entry_2 *ext2_dotdot(struct inode *dir, struct page **p) 41662306a36Sopenharmony_ci{ 41762306a36Sopenharmony_ci ext2_dirent *de = ext2_get_page(dir, 0, 0, p); 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci if (!IS_ERR(de)) 42062306a36Sopenharmony_ci return ext2_next_entry(de); 42162306a36Sopenharmony_ci return NULL; 42262306a36Sopenharmony_ci} 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ciint ext2_inode_by_name(struct inode *dir, const struct qstr *child, ino_t *ino) 42562306a36Sopenharmony_ci{ 42662306a36Sopenharmony_ci struct ext2_dir_entry_2 *de; 42762306a36Sopenharmony_ci struct page *page; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci de = ext2_find_entry(dir, child, &page); 43062306a36Sopenharmony_ci if (IS_ERR(de)) 43162306a36Sopenharmony_ci return PTR_ERR(de); 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci *ino = le32_to_cpu(de->inode); 43462306a36Sopenharmony_ci ext2_put_page(page, de); 43562306a36Sopenharmony_ci return 0; 43662306a36Sopenharmony_ci} 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_cistatic int ext2_prepare_chunk(struct page *page, loff_t pos, unsigned len) 43962306a36Sopenharmony_ci{ 44062306a36Sopenharmony_ci return __block_write_begin(page, pos, len, ext2_get_block); 44162306a36Sopenharmony_ci} 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_cistatic int ext2_handle_dirsync(struct inode *dir) 44562306a36Sopenharmony_ci{ 44662306a36Sopenharmony_ci int err; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci err = filemap_write_and_wait(dir->i_mapping); 44962306a36Sopenharmony_ci if (!err) 45062306a36Sopenharmony_ci err = sync_inode_metadata(dir, 1); 45162306a36Sopenharmony_ci return err; 45262306a36Sopenharmony_ci} 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ciint ext2_set_link(struct inode *dir, struct ext2_dir_entry_2 *de, 45562306a36Sopenharmony_ci struct page *page, struct inode *inode, bool update_times) 45662306a36Sopenharmony_ci{ 45762306a36Sopenharmony_ci loff_t pos = page_offset(page) + offset_in_page(de); 45862306a36Sopenharmony_ci unsigned len = ext2_rec_len_from_disk(de->rec_len); 45962306a36Sopenharmony_ci int err; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci lock_page(page); 46262306a36Sopenharmony_ci err = ext2_prepare_chunk(page, pos, len); 46362306a36Sopenharmony_ci if (err) { 46462306a36Sopenharmony_ci unlock_page(page); 46562306a36Sopenharmony_ci return err; 46662306a36Sopenharmony_ci } 46762306a36Sopenharmony_ci de->inode = cpu_to_le32(inode->i_ino); 46862306a36Sopenharmony_ci ext2_set_de_type(de, inode); 46962306a36Sopenharmony_ci ext2_commit_chunk(page, pos, len); 47062306a36Sopenharmony_ci if (update_times) 47162306a36Sopenharmony_ci dir->i_mtime = inode_set_ctime_current(dir); 47262306a36Sopenharmony_ci EXT2_I(dir)->i_flags &= ~EXT2_BTREE_FL; 47362306a36Sopenharmony_ci mark_inode_dirty(dir); 47462306a36Sopenharmony_ci return ext2_handle_dirsync(dir); 47562306a36Sopenharmony_ci} 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci/* 47862306a36Sopenharmony_ci * Parent is locked. 47962306a36Sopenharmony_ci */ 48062306a36Sopenharmony_ciint ext2_add_link (struct dentry *dentry, struct inode *inode) 48162306a36Sopenharmony_ci{ 48262306a36Sopenharmony_ci struct inode *dir = d_inode(dentry->d_parent); 48362306a36Sopenharmony_ci const char *name = dentry->d_name.name; 48462306a36Sopenharmony_ci int namelen = dentry->d_name.len; 48562306a36Sopenharmony_ci unsigned chunk_size = ext2_chunk_size(dir); 48662306a36Sopenharmony_ci unsigned reclen = EXT2_DIR_REC_LEN(namelen); 48762306a36Sopenharmony_ci unsigned short rec_len, name_len; 48862306a36Sopenharmony_ci struct page *page = NULL; 48962306a36Sopenharmony_ci ext2_dirent * de; 49062306a36Sopenharmony_ci unsigned long npages = dir_pages(dir); 49162306a36Sopenharmony_ci unsigned long n; 49262306a36Sopenharmony_ci loff_t pos; 49362306a36Sopenharmony_ci int err; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci /* 49662306a36Sopenharmony_ci * We take care of directory expansion in the same loop. 49762306a36Sopenharmony_ci * This code plays outside i_size, so it locks the page 49862306a36Sopenharmony_ci * to protect that region. 49962306a36Sopenharmony_ci */ 50062306a36Sopenharmony_ci for (n = 0; n <= npages; n++) { 50162306a36Sopenharmony_ci char *kaddr = ext2_get_page(dir, n, 0, &page); 50262306a36Sopenharmony_ci char *dir_end; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci if (IS_ERR(kaddr)) 50562306a36Sopenharmony_ci return PTR_ERR(kaddr); 50662306a36Sopenharmony_ci lock_page(page); 50762306a36Sopenharmony_ci dir_end = kaddr + ext2_last_byte(dir, n); 50862306a36Sopenharmony_ci de = (ext2_dirent *)kaddr; 50962306a36Sopenharmony_ci kaddr += PAGE_SIZE - reclen; 51062306a36Sopenharmony_ci while ((char *)de <= kaddr) { 51162306a36Sopenharmony_ci if ((char *)de == dir_end) { 51262306a36Sopenharmony_ci /* We hit i_size */ 51362306a36Sopenharmony_ci name_len = 0; 51462306a36Sopenharmony_ci rec_len = chunk_size; 51562306a36Sopenharmony_ci de->rec_len = ext2_rec_len_to_disk(chunk_size); 51662306a36Sopenharmony_ci de->inode = 0; 51762306a36Sopenharmony_ci goto got_it; 51862306a36Sopenharmony_ci } 51962306a36Sopenharmony_ci if (de->rec_len == 0) { 52062306a36Sopenharmony_ci ext2_error(dir->i_sb, __func__, 52162306a36Sopenharmony_ci "zero-length directory entry"); 52262306a36Sopenharmony_ci err = -EIO; 52362306a36Sopenharmony_ci goto out_unlock; 52462306a36Sopenharmony_ci } 52562306a36Sopenharmony_ci err = -EEXIST; 52662306a36Sopenharmony_ci if (ext2_match (namelen, name, de)) 52762306a36Sopenharmony_ci goto out_unlock; 52862306a36Sopenharmony_ci name_len = EXT2_DIR_REC_LEN(de->name_len); 52962306a36Sopenharmony_ci rec_len = ext2_rec_len_from_disk(de->rec_len); 53062306a36Sopenharmony_ci if (!de->inode && rec_len >= reclen) 53162306a36Sopenharmony_ci goto got_it; 53262306a36Sopenharmony_ci if (rec_len >= name_len + reclen) 53362306a36Sopenharmony_ci goto got_it; 53462306a36Sopenharmony_ci de = (ext2_dirent *) ((char *) de + rec_len); 53562306a36Sopenharmony_ci } 53662306a36Sopenharmony_ci unlock_page(page); 53762306a36Sopenharmony_ci ext2_put_page(page, kaddr); 53862306a36Sopenharmony_ci } 53962306a36Sopenharmony_ci BUG(); 54062306a36Sopenharmony_ci return -EINVAL; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_cigot_it: 54362306a36Sopenharmony_ci pos = page_offset(page) + offset_in_page(de); 54462306a36Sopenharmony_ci err = ext2_prepare_chunk(page, pos, rec_len); 54562306a36Sopenharmony_ci if (err) 54662306a36Sopenharmony_ci goto out_unlock; 54762306a36Sopenharmony_ci if (de->inode) { 54862306a36Sopenharmony_ci ext2_dirent *de1 = (ext2_dirent *) ((char *) de + name_len); 54962306a36Sopenharmony_ci de1->rec_len = ext2_rec_len_to_disk(rec_len - name_len); 55062306a36Sopenharmony_ci de->rec_len = ext2_rec_len_to_disk(name_len); 55162306a36Sopenharmony_ci de = de1; 55262306a36Sopenharmony_ci } 55362306a36Sopenharmony_ci de->name_len = namelen; 55462306a36Sopenharmony_ci memcpy(de->name, name, namelen); 55562306a36Sopenharmony_ci de->inode = cpu_to_le32(inode->i_ino); 55662306a36Sopenharmony_ci ext2_set_de_type (de, inode); 55762306a36Sopenharmony_ci ext2_commit_chunk(page, pos, rec_len); 55862306a36Sopenharmony_ci dir->i_mtime = inode_set_ctime_current(dir); 55962306a36Sopenharmony_ci EXT2_I(dir)->i_flags &= ~EXT2_BTREE_FL; 56062306a36Sopenharmony_ci mark_inode_dirty(dir); 56162306a36Sopenharmony_ci err = ext2_handle_dirsync(dir); 56262306a36Sopenharmony_ci /* OFFSET_CACHE */ 56362306a36Sopenharmony_ciout_put: 56462306a36Sopenharmony_ci ext2_put_page(page, de); 56562306a36Sopenharmony_ci return err; 56662306a36Sopenharmony_ciout_unlock: 56762306a36Sopenharmony_ci unlock_page(page); 56862306a36Sopenharmony_ci goto out_put; 56962306a36Sopenharmony_ci} 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci/* 57262306a36Sopenharmony_ci * ext2_delete_entry deletes a directory entry by merging it with the 57362306a36Sopenharmony_ci * previous entry. Page is up-to-date. 57462306a36Sopenharmony_ci */ 57562306a36Sopenharmony_ciint ext2_delete_entry(struct ext2_dir_entry_2 *dir, struct page *page) 57662306a36Sopenharmony_ci{ 57762306a36Sopenharmony_ci struct inode *inode = page->mapping->host; 57862306a36Sopenharmony_ci char *kaddr = (char *)((unsigned long)dir & PAGE_MASK); 57962306a36Sopenharmony_ci unsigned from = offset_in_page(dir) & ~(ext2_chunk_size(inode)-1); 58062306a36Sopenharmony_ci unsigned to = offset_in_page(dir) + 58162306a36Sopenharmony_ci ext2_rec_len_from_disk(dir->rec_len); 58262306a36Sopenharmony_ci loff_t pos; 58362306a36Sopenharmony_ci ext2_dirent *pde = NULL; 58462306a36Sopenharmony_ci ext2_dirent *de = (ext2_dirent *)(kaddr + from); 58562306a36Sopenharmony_ci int err; 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci while ((char*)de < (char*)dir) { 58862306a36Sopenharmony_ci if (de->rec_len == 0) { 58962306a36Sopenharmony_ci ext2_error(inode->i_sb, __func__, 59062306a36Sopenharmony_ci "zero-length directory entry"); 59162306a36Sopenharmony_ci return -EIO; 59262306a36Sopenharmony_ci } 59362306a36Sopenharmony_ci pde = de; 59462306a36Sopenharmony_ci de = ext2_next_entry(de); 59562306a36Sopenharmony_ci } 59662306a36Sopenharmony_ci if (pde) 59762306a36Sopenharmony_ci from = offset_in_page(pde); 59862306a36Sopenharmony_ci pos = page_offset(page) + from; 59962306a36Sopenharmony_ci lock_page(page); 60062306a36Sopenharmony_ci err = ext2_prepare_chunk(page, pos, to - from); 60162306a36Sopenharmony_ci if (err) { 60262306a36Sopenharmony_ci unlock_page(page); 60362306a36Sopenharmony_ci return err; 60462306a36Sopenharmony_ci } 60562306a36Sopenharmony_ci if (pde) 60662306a36Sopenharmony_ci pde->rec_len = ext2_rec_len_to_disk(to - from); 60762306a36Sopenharmony_ci dir->inode = 0; 60862306a36Sopenharmony_ci ext2_commit_chunk(page, pos, to - from); 60962306a36Sopenharmony_ci inode->i_mtime = inode_set_ctime_current(inode); 61062306a36Sopenharmony_ci EXT2_I(inode)->i_flags &= ~EXT2_BTREE_FL; 61162306a36Sopenharmony_ci mark_inode_dirty(inode); 61262306a36Sopenharmony_ci return ext2_handle_dirsync(inode); 61362306a36Sopenharmony_ci} 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci/* 61662306a36Sopenharmony_ci * Set the first fragment of directory. 61762306a36Sopenharmony_ci */ 61862306a36Sopenharmony_ciint ext2_make_empty(struct inode *inode, struct inode *parent) 61962306a36Sopenharmony_ci{ 62062306a36Sopenharmony_ci struct page *page = grab_cache_page(inode->i_mapping, 0); 62162306a36Sopenharmony_ci unsigned chunk_size = ext2_chunk_size(inode); 62262306a36Sopenharmony_ci struct ext2_dir_entry_2 * de; 62362306a36Sopenharmony_ci int err; 62462306a36Sopenharmony_ci void *kaddr; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci if (!page) 62762306a36Sopenharmony_ci return -ENOMEM; 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci err = ext2_prepare_chunk(page, 0, chunk_size); 63062306a36Sopenharmony_ci if (err) { 63162306a36Sopenharmony_ci unlock_page(page); 63262306a36Sopenharmony_ci goto fail; 63362306a36Sopenharmony_ci } 63462306a36Sopenharmony_ci kaddr = kmap_local_page(page); 63562306a36Sopenharmony_ci memset(kaddr, 0, chunk_size); 63662306a36Sopenharmony_ci de = (struct ext2_dir_entry_2 *)kaddr; 63762306a36Sopenharmony_ci de->name_len = 1; 63862306a36Sopenharmony_ci de->rec_len = ext2_rec_len_to_disk(EXT2_DIR_REC_LEN(1)); 63962306a36Sopenharmony_ci memcpy (de->name, ".\0\0", 4); 64062306a36Sopenharmony_ci de->inode = cpu_to_le32(inode->i_ino); 64162306a36Sopenharmony_ci ext2_set_de_type (de, inode); 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci de = (struct ext2_dir_entry_2 *)(kaddr + EXT2_DIR_REC_LEN(1)); 64462306a36Sopenharmony_ci de->name_len = 2; 64562306a36Sopenharmony_ci de->rec_len = ext2_rec_len_to_disk(chunk_size - EXT2_DIR_REC_LEN(1)); 64662306a36Sopenharmony_ci de->inode = cpu_to_le32(parent->i_ino); 64762306a36Sopenharmony_ci memcpy (de->name, "..\0", 4); 64862306a36Sopenharmony_ci ext2_set_de_type (de, inode); 64962306a36Sopenharmony_ci kunmap_local(kaddr); 65062306a36Sopenharmony_ci ext2_commit_chunk(page, 0, chunk_size); 65162306a36Sopenharmony_ci err = ext2_handle_dirsync(inode); 65262306a36Sopenharmony_cifail: 65362306a36Sopenharmony_ci put_page(page); 65462306a36Sopenharmony_ci return err; 65562306a36Sopenharmony_ci} 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci/* 65862306a36Sopenharmony_ci * routine to check that the specified directory is empty (for rmdir) 65962306a36Sopenharmony_ci */ 66062306a36Sopenharmony_ciint ext2_empty_dir (struct inode * inode) 66162306a36Sopenharmony_ci{ 66262306a36Sopenharmony_ci struct page *page; 66362306a36Sopenharmony_ci char *kaddr; 66462306a36Sopenharmony_ci unsigned long i, npages = dir_pages(inode); 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci for (i = 0; i < npages; i++) { 66762306a36Sopenharmony_ci ext2_dirent *de; 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci kaddr = ext2_get_page(inode, i, 0, &page); 67062306a36Sopenharmony_ci if (IS_ERR(kaddr)) 67162306a36Sopenharmony_ci return 0; 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci de = (ext2_dirent *)kaddr; 67462306a36Sopenharmony_ci kaddr += ext2_last_byte(inode, i) - EXT2_DIR_REC_LEN(1); 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci while ((char *)de <= kaddr) { 67762306a36Sopenharmony_ci if (de->rec_len == 0) { 67862306a36Sopenharmony_ci ext2_error(inode->i_sb, __func__, 67962306a36Sopenharmony_ci "zero-length directory entry"); 68062306a36Sopenharmony_ci printk("kaddr=%p, de=%p\n", kaddr, de); 68162306a36Sopenharmony_ci goto not_empty; 68262306a36Sopenharmony_ci } 68362306a36Sopenharmony_ci if (de->inode != 0) { 68462306a36Sopenharmony_ci /* check for . and .. */ 68562306a36Sopenharmony_ci if (de->name[0] != '.') 68662306a36Sopenharmony_ci goto not_empty; 68762306a36Sopenharmony_ci if (de->name_len > 2) 68862306a36Sopenharmony_ci goto not_empty; 68962306a36Sopenharmony_ci if (de->name_len < 2) { 69062306a36Sopenharmony_ci if (de->inode != 69162306a36Sopenharmony_ci cpu_to_le32(inode->i_ino)) 69262306a36Sopenharmony_ci goto not_empty; 69362306a36Sopenharmony_ci } else if (de->name[1] != '.') 69462306a36Sopenharmony_ci goto not_empty; 69562306a36Sopenharmony_ci } 69662306a36Sopenharmony_ci de = ext2_next_entry(de); 69762306a36Sopenharmony_ci } 69862306a36Sopenharmony_ci ext2_put_page(page, kaddr); 69962306a36Sopenharmony_ci } 70062306a36Sopenharmony_ci return 1; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_cinot_empty: 70362306a36Sopenharmony_ci ext2_put_page(page, kaddr); 70462306a36Sopenharmony_ci return 0; 70562306a36Sopenharmony_ci} 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ciconst struct file_operations ext2_dir_operations = { 70862306a36Sopenharmony_ci .llseek = generic_file_llseek, 70962306a36Sopenharmony_ci .read = generic_read_dir, 71062306a36Sopenharmony_ci .iterate_shared = ext2_readdir, 71162306a36Sopenharmony_ci .unlocked_ioctl = ext2_ioctl, 71262306a36Sopenharmony_ci#ifdef CONFIG_COMPAT 71362306a36Sopenharmony_ci .compat_ioctl = ext2_compat_ioctl, 71462306a36Sopenharmony_ci#endif 71562306a36Sopenharmony_ci .fsync = ext2_fsync, 71662306a36Sopenharmony_ci}; 717