18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * linux/fs/ufs/ufs_dir.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 1996 68c2ecf20Sopenharmony_ci * Adrian Rodriguez (adrian@franklins-tower.rutgers.edu) 78c2ecf20Sopenharmony_ci * Laboratory for Computer Science Research Computing Facility 88c2ecf20Sopenharmony_ci * Rutgers, The State University of New Jersey 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * swab support by Francois-Rene Rideau <fare@tunes.org> 19970406 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * 4.4BSD (FreeBSD) support added on February 1st 1998 by 138c2ecf20Sopenharmony_ci * Niels Kristian Bech Jensen <nkbj@image.dk> partially based 148c2ecf20Sopenharmony_ci * on code by Martin von Loewis <martin@mira.isdn.cs.tu-berlin.de>. 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * Migration to usage of "page cache" on May 2006 by 178c2ecf20Sopenharmony_ci * Evgeniy Dushistov <dushistov@mail.ru> based on ext2 code base. 188c2ecf20Sopenharmony_ci */ 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <linux/time.h> 218c2ecf20Sopenharmony_ci#include <linux/fs.h> 228c2ecf20Sopenharmony_ci#include <linux/swap.h> 238c2ecf20Sopenharmony_ci#include <linux/iversion.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include "ufs_fs.h" 268c2ecf20Sopenharmony_ci#include "ufs.h" 278c2ecf20Sopenharmony_ci#include "swab.h" 288c2ecf20Sopenharmony_ci#include "util.h" 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci/* 318c2ecf20Sopenharmony_ci * NOTE! unlike strncmp, ufs_match returns 1 for success, 0 for failure. 328c2ecf20Sopenharmony_ci * 338c2ecf20Sopenharmony_ci * len <= UFS_MAXNAMLEN and de != NULL are guaranteed by caller. 348c2ecf20Sopenharmony_ci */ 358c2ecf20Sopenharmony_cistatic inline int ufs_match(struct super_block *sb, int len, 368c2ecf20Sopenharmony_ci const unsigned char *name, struct ufs_dir_entry *de) 378c2ecf20Sopenharmony_ci{ 388c2ecf20Sopenharmony_ci if (len != ufs_get_de_namlen(sb, de)) 398c2ecf20Sopenharmony_ci return 0; 408c2ecf20Sopenharmony_ci if (!de->d_ino) 418c2ecf20Sopenharmony_ci return 0; 428c2ecf20Sopenharmony_ci return !memcmp(name, de->d_name, len); 438c2ecf20Sopenharmony_ci} 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistatic int ufs_commit_chunk(struct page *page, loff_t pos, unsigned len) 468c2ecf20Sopenharmony_ci{ 478c2ecf20Sopenharmony_ci struct address_space *mapping = page->mapping; 488c2ecf20Sopenharmony_ci struct inode *dir = mapping->host; 498c2ecf20Sopenharmony_ci int err = 0; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci inode_inc_iversion(dir); 528c2ecf20Sopenharmony_ci block_write_end(NULL, mapping, pos, len, len, page, NULL); 538c2ecf20Sopenharmony_ci if (pos+len > dir->i_size) { 548c2ecf20Sopenharmony_ci i_size_write(dir, pos+len); 558c2ecf20Sopenharmony_ci mark_inode_dirty(dir); 568c2ecf20Sopenharmony_ci } 578c2ecf20Sopenharmony_ci if (IS_DIRSYNC(dir)) 588c2ecf20Sopenharmony_ci err = write_one_page(page); 598c2ecf20Sopenharmony_ci else 608c2ecf20Sopenharmony_ci unlock_page(page); 618c2ecf20Sopenharmony_ci return err; 628c2ecf20Sopenharmony_ci} 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic inline void ufs_put_page(struct page *page) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci kunmap(page); 678c2ecf20Sopenharmony_ci put_page(page); 688c2ecf20Sopenharmony_ci} 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ciino_t ufs_inode_by_name(struct inode *dir, const struct qstr *qstr) 718c2ecf20Sopenharmony_ci{ 728c2ecf20Sopenharmony_ci ino_t res = 0; 738c2ecf20Sopenharmony_ci struct ufs_dir_entry *de; 748c2ecf20Sopenharmony_ci struct page *page; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci de = ufs_find_entry(dir, qstr, &page); 778c2ecf20Sopenharmony_ci if (de) { 788c2ecf20Sopenharmony_ci res = fs32_to_cpu(dir->i_sb, de->d_ino); 798c2ecf20Sopenharmony_ci ufs_put_page(page); 808c2ecf20Sopenharmony_ci } 818c2ecf20Sopenharmony_ci return res; 828c2ecf20Sopenharmony_ci} 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci/* Releases the page */ 868c2ecf20Sopenharmony_civoid ufs_set_link(struct inode *dir, struct ufs_dir_entry *de, 878c2ecf20Sopenharmony_ci struct page *page, struct inode *inode, 888c2ecf20Sopenharmony_ci bool update_times) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci loff_t pos = page_offset(page) + 918c2ecf20Sopenharmony_ci (char *) de - (char *) page_address(page); 928c2ecf20Sopenharmony_ci unsigned len = fs16_to_cpu(dir->i_sb, de->d_reclen); 938c2ecf20Sopenharmony_ci int err; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci lock_page(page); 968c2ecf20Sopenharmony_ci err = ufs_prepare_chunk(page, pos, len); 978c2ecf20Sopenharmony_ci BUG_ON(err); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci de->d_ino = cpu_to_fs32(dir->i_sb, inode->i_ino); 1008c2ecf20Sopenharmony_ci ufs_set_de_type(dir->i_sb, de, inode->i_mode); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci err = ufs_commit_chunk(page, pos, len); 1038c2ecf20Sopenharmony_ci ufs_put_page(page); 1048c2ecf20Sopenharmony_ci if (update_times) 1058c2ecf20Sopenharmony_ci dir->i_mtime = dir->i_ctime = current_time(dir); 1068c2ecf20Sopenharmony_ci mark_inode_dirty(dir); 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_cistatic bool ufs_check_page(struct page *page) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci struct inode *dir = page->mapping->host; 1138c2ecf20Sopenharmony_ci struct super_block *sb = dir->i_sb; 1148c2ecf20Sopenharmony_ci char *kaddr = page_address(page); 1158c2ecf20Sopenharmony_ci unsigned offs, rec_len; 1168c2ecf20Sopenharmony_ci unsigned limit = PAGE_SIZE; 1178c2ecf20Sopenharmony_ci const unsigned chunk_mask = UFS_SB(sb)->s_uspi->s_dirblksize - 1; 1188c2ecf20Sopenharmony_ci struct ufs_dir_entry *p; 1198c2ecf20Sopenharmony_ci char *error; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci if ((dir->i_size >> PAGE_SHIFT) == page->index) { 1228c2ecf20Sopenharmony_ci limit = dir->i_size & ~PAGE_MASK; 1238c2ecf20Sopenharmony_ci if (limit & chunk_mask) 1248c2ecf20Sopenharmony_ci goto Ebadsize; 1258c2ecf20Sopenharmony_ci if (!limit) 1268c2ecf20Sopenharmony_ci goto out; 1278c2ecf20Sopenharmony_ci } 1288c2ecf20Sopenharmony_ci for (offs = 0; offs <= limit - UFS_DIR_REC_LEN(1); offs += rec_len) { 1298c2ecf20Sopenharmony_ci p = (struct ufs_dir_entry *)(kaddr + offs); 1308c2ecf20Sopenharmony_ci rec_len = fs16_to_cpu(sb, p->d_reclen); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci if (rec_len < UFS_DIR_REC_LEN(1)) 1338c2ecf20Sopenharmony_ci goto Eshort; 1348c2ecf20Sopenharmony_ci if (rec_len & 3) 1358c2ecf20Sopenharmony_ci goto Ealign; 1368c2ecf20Sopenharmony_ci if (rec_len < UFS_DIR_REC_LEN(ufs_get_de_namlen(sb, p))) 1378c2ecf20Sopenharmony_ci goto Enamelen; 1388c2ecf20Sopenharmony_ci if (((offs + rec_len - 1) ^ offs) & ~chunk_mask) 1398c2ecf20Sopenharmony_ci goto Espan; 1408c2ecf20Sopenharmony_ci if (fs32_to_cpu(sb, p->d_ino) > (UFS_SB(sb)->s_uspi->s_ipg * 1418c2ecf20Sopenharmony_ci UFS_SB(sb)->s_uspi->s_ncg)) 1428c2ecf20Sopenharmony_ci goto Einumber; 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_ci if (offs != limit) 1458c2ecf20Sopenharmony_ci goto Eend; 1468c2ecf20Sopenharmony_ciout: 1478c2ecf20Sopenharmony_ci SetPageChecked(page); 1488c2ecf20Sopenharmony_ci return true; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci /* Too bad, we had an error */ 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ciEbadsize: 1538c2ecf20Sopenharmony_ci ufs_error(sb, "ufs_check_page", 1548c2ecf20Sopenharmony_ci "size of directory #%lu is not a multiple of chunk size", 1558c2ecf20Sopenharmony_ci dir->i_ino 1568c2ecf20Sopenharmony_ci ); 1578c2ecf20Sopenharmony_ci goto fail; 1588c2ecf20Sopenharmony_ciEshort: 1598c2ecf20Sopenharmony_ci error = "rec_len is smaller than minimal"; 1608c2ecf20Sopenharmony_ci goto bad_entry; 1618c2ecf20Sopenharmony_ciEalign: 1628c2ecf20Sopenharmony_ci error = "unaligned directory entry"; 1638c2ecf20Sopenharmony_ci goto bad_entry; 1648c2ecf20Sopenharmony_ciEnamelen: 1658c2ecf20Sopenharmony_ci error = "rec_len is too small for name_len"; 1668c2ecf20Sopenharmony_ci goto bad_entry; 1678c2ecf20Sopenharmony_ciEspan: 1688c2ecf20Sopenharmony_ci error = "directory entry across blocks"; 1698c2ecf20Sopenharmony_ci goto bad_entry; 1708c2ecf20Sopenharmony_ciEinumber: 1718c2ecf20Sopenharmony_ci error = "inode out of bounds"; 1728c2ecf20Sopenharmony_cibad_entry: 1738c2ecf20Sopenharmony_ci ufs_error (sb, "ufs_check_page", "bad entry in directory #%lu: %s - " 1748c2ecf20Sopenharmony_ci "offset=%lu, rec_len=%d, name_len=%d", 1758c2ecf20Sopenharmony_ci dir->i_ino, error, (page->index<<PAGE_SHIFT)+offs, 1768c2ecf20Sopenharmony_ci rec_len, ufs_get_de_namlen(sb, p)); 1778c2ecf20Sopenharmony_ci goto fail; 1788c2ecf20Sopenharmony_ciEend: 1798c2ecf20Sopenharmony_ci p = (struct ufs_dir_entry *)(kaddr + offs); 1808c2ecf20Sopenharmony_ci ufs_error(sb, __func__, 1818c2ecf20Sopenharmony_ci "entry in directory #%lu spans the page boundary" 1828c2ecf20Sopenharmony_ci "offset=%lu", 1838c2ecf20Sopenharmony_ci dir->i_ino, (page->index<<PAGE_SHIFT)+offs); 1848c2ecf20Sopenharmony_cifail: 1858c2ecf20Sopenharmony_ci SetPageError(page); 1868c2ecf20Sopenharmony_ci return false; 1878c2ecf20Sopenharmony_ci} 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_cistatic struct page *ufs_get_page(struct inode *dir, unsigned long n) 1908c2ecf20Sopenharmony_ci{ 1918c2ecf20Sopenharmony_ci struct address_space *mapping = dir->i_mapping; 1928c2ecf20Sopenharmony_ci struct page *page = read_mapping_page(mapping, n, NULL); 1938c2ecf20Sopenharmony_ci if (!IS_ERR(page)) { 1948c2ecf20Sopenharmony_ci kmap(page); 1958c2ecf20Sopenharmony_ci if (unlikely(!PageChecked(page))) { 1968c2ecf20Sopenharmony_ci if (PageError(page) || !ufs_check_page(page)) 1978c2ecf20Sopenharmony_ci goto fail; 1988c2ecf20Sopenharmony_ci } 1998c2ecf20Sopenharmony_ci } 2008c2ecf20Sopenharmony_ci return page; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_cifail: 2038c2ecf20Sopenharmony_ci ufs_put_page(page); 2048c2ecf20Sopenharmony_ci return ERR_PTR(-EIO); 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci/* 2088c2ecf20Sopenharmony_ci * Return the offset into page `page_nr' of the last valid 2098c2ecf20Sopenharmony_ci * byte in that page, plus one. 2108c2ecf20Sopenharmony_ci */ 2118c2ecf20Sopenharmony_cistatic unsigned 2128c2ecf20Sopenharmony_ciufs_last_byte(struct inode *inode, unsigned long page_nr) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci unsigned last_byte = inode->i_size; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci last_byte -= page_nr << PAGE_SHIFT; 2178c2ecf20Sopenharmony_ci if (last_byte > PAGE_SIZE) 2188c2ecf20Sopenharmony_ci last_byte = PAGE_SIZE; 2198c2ecf20Sopenharmony_ci return last_byte; 2208c2ecf20Sopenharmony_ci} 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_cistatic inline struct ufs_dir_entry * 2238c2ecf20Sopenharmony_ciufs_next_entry(struct super_block *sb, struct ufs_dir_entry *p) 2248c2ecf20Sopenharmony_ci{ 2258c2ecf20Sopenharmony_ci return (struct ufs_dir_entry *)((char *)p + 2268c2ecf20Sopenharmony_ci fs16_to_cpu(sb, p->d_reclen)); 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cistruct ufs_dir_entry *ufs_dotdot(struct inode *dir, struct page **p) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci struct page *page = ufs_get_page(dir, 0); 2328c2ecf20Sopenharmony_ci struct ufs_dir_entry *de = NULL; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci if (!IS_ERR(page)) { 2358c2ecf20Sopenharmony_ci de = ufs_next_entry(dir->i_sb, 2368c2ecf20Sopenharmony_ci (struct ufs_dir_entry *)page_address(page)); 2378c2ecf20Sopenharmony_ci *p = page; 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci return de; 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci/* 2438c2ecf20Sopenharmony_ci * ufs_find_entry() 2448c2ecf20Sopenharmony_ci * 2458c2ecf20Sopenharmony_ci * finds an entry in the specified directory with the wanted name. It 2468c2ecf20Sopenharmony_ci * returns the page in which the entry was found, and the entry itself 2478c2ecf20Sopenharmony_ci * (as a parameter - res_dir). Page is returned mapped and unlocked. 2488c2ecf20Sopenharmony_ci * Entry is guaranteed to be valid. 2498c2ecf20Sopenharmony_ci */ 2508c2ecf20Sopenharmony_cistruct ufs_dir_entry *ufs_find_entry(struct inode *dir, const struct qstr *qstr, 2518c2ecf20Sopenharmony_ci struct page **res_page) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci struct super_block *sb = dir->i_sb; 2548c2ecf20Sopenharmony_ci const unsigned char *name = qstr->name; 2558c2ecf20Sopenharmony_ci int namelen = qstr->len; 2568c2ecf20Sopenharmony_ci unsigned reclen = UFS_DIR_REC_LEN(namelen); 2578c2ecf20Sopenharmony_ci unsigned long start, n; 2588c2ecf20Sopenharmony_ci unsigned long npages = dir_pages(dir); 2598c2ecf20Sopenharmony_ci struct page *page = NULL; 2608c2ecf20Sopenharmony_ci struct ufs_inode_info *ui = UFS_I(dir); 2618c2ecf20Sopenharmony_ci struct ufs_dir_entry *de; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci UFSD("ENTER, dir_ino %lu, name %s, namlen %u\n", dir->i_ino, name, namelen); 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci if (npages == 0 || namelen > UFS_MAXNAMLEN) 2668c2ecf20Sopenharmony_ci goto out; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci /* OFFSET_CACHE */ 2698c2ecf20Sopenharmony_ci *res_page = NULL; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci start = ui->i_dir_start_lookup; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci if (start >= npages) 2748c2ecf20Sopenharmony_ci start = 0; 2758c2ecf20Sopenharmony_ci n = start; 2768c2ecf20Sopenharmony_ci do { 2778c2ecf20Sopenharmony_ci char *kaddr; 2788c2ecf20Sopenharmony_ci page = ufs_get_page(dir, n); 2798c2ecf20Sopenharmony_ci if (!IS_ERR(page)) { 2808c2ecf20Sopenharmony_ci kaddr = page_address(page); 2818c2ecf20Sopenharmony_ci de = (struct ufs_dir_entry *) kaddr; 2828c2ecf20Sopenharmony_ci kaddr += ufs_last_byte(dir, n) - reclen; 2838c2ecf20Sopenharmony_ci while ((char *) de <= kaddr) { 2848c2ecf20Sopenharmony_ci if (ufs_match(sb, namelen, name, de)) 2858c2ecf20Sopenharmony_ci goto found; 2868c2ecf20Sopenharmony_ci de = ufs_next_entry(sb, de); 2878c2ecf20Sopenharmony_ci } 2888c2ecf20Sopenharmony_ci ufs_put_page(page); 2898c2ecf20Sopenharmony_ci } 2908c2ecf20Sopenharmony_ci if (++n >= npages) 2918c2ecf20Sopenharmony_ci n = 0; 2928c2ecf20Sopenharmony_ci } while (n != start); 2938c2ecf20Sopenharmony_ciout: 2948c2ecf20Sopenharmony_ci return NULL; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_cifound: 2978c2ecf20Sopenharmony_ci *res_page = page; 2988c2ecf20Sopenharmony_ci ui->i_dir_start_lookup = n; 2998c2ecf20Sopenharmony_ci return de; 3008c2ecf20Sopenharmony_ci} 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci/* 3038c2ecf20Sopenharmony_ci * Parent is locked. 3048c2ecf20Sopenharmony_ci */ 3058c2ecf20Sopenharmony_ciint ufs_add_link(struct dentry *dentry, struct inode *inode) 3068c2ecf20Sopenharmony_ci{ 3078c2ecf20Sopenharmony_ci struct inode *dir = d_inode(dentry->d_parent); 3088c2ecf20Sopenharmony_ci const unsigned char *name = dentry->d_name.name; 3098c2ecf20Sopenharmony_ci int namelen = dentry->d_name.len; 3108c2ecf20Sopenharmony_ci struct super_block *sb = dir->i_sb; 3118c2ecf20Sopenharmony_ci unsigned reclen = UFS_DIR_REC_LEN(namelen); 3128c2ecf20Sopenharmony_ci const unsigned int chunk_size = UFS_SB(sb)->s_uspi->s_dirblksize; 3138c2ecf20Sopenharmony_ci unsigned short rec_len, name_len; 3148c2ecf20Sopenharmony_ci struct page *page = NULL; 3158c2ecf20Sopenharmony_ci struct ufs_dir_entry *de; 3168c2ecf20Sopenharmony_ci unsigned long npages = dir_pages(dir); 3178c2ecf20Sopenharmony_ci unsigned long n; 3188c2ecf20Sopenharmony_ci char *kaddr; 3198c2ecf20Sopenharmony_ci loff_t pos; 3208c2ecf20Sopenharmony_ci int err; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci UFSD("ENTER, name %s, namelen %u\n", name, namelen); 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci /* 3258c2ecf20Sopenharmony_ci * We take care of directory expansion in the same loop. 3268c2ecf20Sopenharmony_ci * This code plays outside i_size, so it locks the page 3278c2ecf20Sopenharmony_ci * to protect that region. 3288c2ecf20Sopenharmony_ci */ 3298c2ecf20Sopenharmony_ci for (n = 0; n <= npages; n++) { 3308c2ecf20Sopenharmony_ci char *dir_end; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci page = ufs_get_page(dir, n); 3338c2ecf20Sopenharmony_ci err = PTR_ERR(page); 3348c2ecf20Sopenharmony_ci if (IS_ERR(page)) 3358c2ecf20Sopenharmony_ci goto out; 3368c2ecf20Sopenharmony_ci lock_page(page); 3378c2ecf20Sopenharmony_ci kaddr = page_address(page); 3388c2ecf20Sopenharmony_ci dir_end = kaddr + ufs_last_byte(dir, n); 3398c2ecf20Sopenharmony_ci de = (struct ufs_dir_entry *)kaddr; 3408c2ecf20Sopenharmony_ci kaddr += PAGE_SIZE - reclen; 3418c2ecf20Sopenharmony_ci while ((char *)de <= kaddr) { 3428c2ecf20Sopenharmony_ci if ((char *)de == dir_end) { 3438c2ecf20Sopenharmony_ci /* We hit i_size */ 3448c2ecf20Sopenharmony_ci name_len = 0; 3458c2ecf20Sopenharmony_ci rec_len = chunk_size; 3468c2ecf20Sopenharmony_ci de->d_reclen = cpu_to_fs16(sb, chunk_size); 3478c2ecf20Sopenharmony_ci de->d_ino = 0; 3488c2ecf20Sopenharmony_ci goto got_it; 3498c2ecf20Sopenharmony_ci } 3508c2ecf20Sopenharmony_ci if (de->d_reclen == 0) { 3518c2ecf20Sopenharmony_ci ufs_error(dir->i_sb, __func__, 3528c2ecf20Sopenharmony_ci "zero-length directory entry"); 3538c2ecf20Sopenharmony_ci err = -EIO; 3548c2ecf20Sopenharmony_ci goto out_unlock; 3558c2ecf20Sopenharmony_ci } 3568c2ecf20Sopenharmony_ci err = -EEXIST; 3578c2ecf20Sopenharmony_ci if (ufs_match(sb, namelen, name, de)) 3588c2ecf20Sopenharmony_ci goto out_unlock; 3598c2ecf20Sopenharmony_ci name_len = UFS_DIR_REC_LEN(ufs_get_de_namlen(sb, de)); 3608c2ecf20Sopenharmony_ci rec_len = fs16_to_cpu(sb, de->d_reclen); 3618c2ecf20Sopenharmony_ci if (!de->d_ino && rec_len >= reclen) 3628c2ecf20Sopenharmony_ci goto got_it; 3638c2ecf20Sopenharmony_ci if (rec_len >= name_len + reclen) 3648c2ecf20Sopenharmony_ci goto got_it; 3658c2ecf20Sopenharmony_ci de = (struct ufs_dir_entry *) ((char *) de + rec_len); 3668c2ecf20Sopenharmony_ci } 3678c2ecf20Sopenharmony_ci unlock_page(page); 3688c2ecf20Sopenharmony_ci ufs_put_page(page); 3698c2ecf20Sopenharmony_ci } 3708c2ecf20Sopenharmony_ci BUG(); 3718c2ecf20Sopenharmony_ci return -EINVAL; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_cigot_it: 3748c2ecf20Sopenharmony_ci pos = page_offset(page) + 3758c2ecf20Sopenharmony_ci (char*)de - (char*)page_address(page); 3768c2ecf20Sopenharmony_ci err = ufs_prepare_chunk(page, pos, rec_len); 3778c2ecf20Sopenharmony_ci if (err) 3788c2ecf20Sopenharmony_ci goto out_unlock; 3798c2ecf20Sopenharmony_ci if (de->d_ino) { 3808c2ecf20Sopenharmony_ci struct ufs_dir_entry *de1 = 3818c2ecf20Sopenharmony_ci (struct ufs_dir_entry *) ((char *) de + name_len); 3828c2ecf20Sopenharmony_ci de1->d_reclen = cpu_to_fs16(sb, rec_len - name_len); 3838c2ecf20Sopenharmony_ci de->d_reclen = cpu_to_fs16(sb, name_len); 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci de = de1; 3868c2ecf20Sopenharmony_ci } 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci ufs_set_de_namlen(sb, de, namelen); 3898c2ecf20Sopenharmony_ci memcpy(de->d_name, name, namelen + 1); 3908c2ecf20Sopenharmony_ci de->d_ino = cpu_to_fs32(sb, inode->i_ino); 3918c2ecf20Sopenharmony_ci ufs_set_de_type(sb, de, inode->i_mode); 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci err = ufs_commit_chunk(page, pos, rec_len); 3948c2ecf20Sopenharmony_ci dir->i_mtime = dir->i_ctime = current_time(dir); 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci mark_inode_dirty(dir); 3978c2ecf20Sopenharmony_ci /* OFFSET_CACHE */ 3988c2ecf20Sopenharmony_ciout_put: 3998c2ecf20Sopenharmony_ci ufs_put_page(page); 4008c2ecf20Sopenharmony_ciout: 4018c2ecf20Sopenharmony_ci return err; 4028c2ecf20Sopenharmony_ciout_unlock: 4038c2ecf20Sopenharmony_ci unlock_page(page); 4048c2ecf20Sopenharmony_ci goto out_put; 4058c2ecf20Sopenharmony_ci} 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_cistatic inline unsigned 4088c2ecf20Sopenharmony_ciufs_validate_entry(struct super_block *sb, char *base, 4098c2ecf20Sopenharmony_ci unsigned offset, unsigned mask) 4108c2ecf20Sopenharmony_ci{ 4118c2ecf20Sopenharmony_ci struct ufs_dir_entry *de = (struct ufs_dir_entry*)(base + offset); 4128c2ecf20Sopenharmony_ci struct ufs_dir_entry *p = (struct ufs_dir_entry*)(base + (offset&mask)); 4138c2ecf20Sopenharmony_ci while ((char*)p < (char*)de) 4148c2ecf20Sopenharmony_ci p = ufs_next_entry(sb, p); 4158c2ecf20Sopenharmony_ci return (char *)p - base; 4168c2ecf20Sopenharmony_ci} 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci/* 4208c2ecf20Sopenharmony_ci * This is blatantly stolen from ext2fs 4218c2ecf20Sopenharmony_ci */ 4228c2ecf20Sopenharmony_cistatic int 4238c2ecf20Sopenharmony_ciufs_readdir(struct file *file, struct dir_context *ctx) 4248c2ecf20Sopenharmony_ci{ 4258c2ecf20Sopenharmony_ci loff_t pos = ctx->pos; 4268c2ecf20Sopenharmony_ci struct inode *inode = file_inode(file); 4278c2ecf20Sopenharmony_ci struct super_block *sb = inode->i_sb; 4288c2ecf20Sopenharmony_ci unsigned int offset = pos & ~PAGE_MASK; 4298c2ecf20Sopenharmony_ci unsigned long n = pos >> PAGE_SHIFT; 4308c2ecf20Sopenharmony_ci unsigned long npages = dir_pages(inode); 4318c2ecf20Sopenharmony_ci unsigned chunk_mask = ~(UFS_SB(sb)->s_uspi->s_dirblksize - 1); 4328c2ecf20Sopenharmony_ci bool need_revalidate = !inode_eq_iversion(inode, file->f_version); 4338c2ecf20Sopenharmony_ci unsigned flags = UFS_SB(sb)->s_flags; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci UFSD("BEGIN\n"); 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci if (pos > inode->i_size - UFS_DIR_REC_LEN(1)) 4388c2ecf20Sopenharmony_ci return 0; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci for ( ; n < npages; n++, offset = 0) { 4418c2ecf20Sopenharmony_ci char *kaddr, *limit; 4428c2ecf20Sopenharmony_ci struct ufs_dir_entry *de; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci struct page *page = ufs_get_page(inode, n); 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci if (IS_ERR(page)) { 4478c2ecf20Sopenharmony_ci ufs_error(sb, __func__, 4488c2ecf20Sopenharmony_ci "bad page in #%lu", 4498c2ecf20Sopenharmony_ci inode->i_ino); 4508c2ecf20Sopenharmony_ci ctx->pos += PAGE_SIZE - offset; 4518c2ecf20Sopenharmony_ci return -EIO; 4528c2ecf20Sopenharmony_ci } 4538c2ecf20Sopenharmony_ci kaddr = page_address(page); 4548c2ecf20Sopenharmony_ci if (unlikely(need_revalidate)) { 4558c2ecf20Sopenharmony_ci if (offset) { 4568c2ecf20Sopenharmony_ci offset = ufs_validate_entry(sb, kaddr, offset, chunk_mask); 4578c2ecf20Sopenharmony_ci ctx->pos = (n<<PAGE_SHIFT) + offset; 4588c2ecf20Sopenharmony_ci } 4598c2ecf20Sopenharmony_ci file->f_version = inode_query_iversion(inode); 4608c2ecf20Sopenharmony_ci need_revalidate = false; 4618c2ecf20Sopenharmony_ci } 4628c2ecf20Sopenharmony_ci de = (struct ufs_dir_entry *)(kaddr+offset); 4638c2ecf20Sopenharmony_ci limit = kaddr + ufs_last_byte(inode, n) - UFS_DIR_REC_LEN(1); 4648c2ecf20Sopenharmony_ci for ( ;(char*)de <= limit; de = ufs_next_entry(sb, de)) { 4658c2ecf20Sopenharmony_ci if (de->d_ino) { 4668c2ecf20Sopenharmony_ci unsigned char d_type = DT_UNKNOWN; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci UFSD("filldir(%s,%u)\n", de->d_name, 4698c2ecf20Sopenharmony_ci fs32_to_cpu(sb, de->d_ino)); 4708c2ecf20Sopenharmony_ci UFSD("namlen %u\n", ufs_get_de_namlen(sb, de)); 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci if ((flags & UFS_DE_MASK) == UFS_DE_44BSD) 4738c2ecf20Sopenharmony_ci d_type = de->d_u.d_44.d_type; 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci if (!dir_emit(ctx, de->d_name, 4768c2ecf20Sopenharmony_ci ufs_get_de_namlen(sb, de), 4778c2ecf20Sopenharmony_ci fs32_to_cpu(sb, de->d_ino), 4788c2ecf20Sopenharmony_ci d_type)) { 4798c2ecf20Sopenharmony_ci ufs_put_page(page); 4808c2ecf20Sopenharmony_ci return 0; 4818c2ecf20Sopenharmony_ci } 4828c2ecf20Sopenharmony_ci } 4838c2ecf20Sopenharmony_ci ctx->pos += fs16_to_cpu(sb, de->d_reclen); 4848c2ecf20Sopenharmony_ci } 4858c2ecf20Sopenharmony_ci ufs_put_page(page); 4868c2ecf20Sopenharmony_ci } 4878c2ecf20Sopenharmony_ci return 0; 4888c2ecf20Sopenharmony_ci} 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci/* 4928c2ecf20Sopenharmony_ci * ufs_delete_entry deletes a directory entry by merging it with the 4938c2ecf20Sopenharmony_ci * previous entry. 4948c2ecf20Sopenharmony_ci */ 4958c2ecf20Sopenharmony_ciint ufs_delete_entry(struct inode *inode, struct ufs_dir_entry *dir, 4968c2ecf20Sopenharmony_ci struct page * page) 4978c2ecf20Sopenharmony_ci{ 4988c2ecf20Sopenharmony_ci struct super_block *sb = inode->i_sb; 4998c2ecf20Sopenharmony_ci char *kaddr = page_address(page); 5008c2ecf20Sopenharmony_ci unsigned from = ((char*)dir - kaddr) & ~(UFS_SB(sb)->s_uspi->s_dirblksize - 1); 5018c2ecf20Sopenharmony_ci unsigned to = ((char*)dir - kaddr) + fs16_to_cpu(sb, dir->d_reclen); 5028c2ecf20Sopenharmony_ci loff_t pos; 5038c2ecf20Sopenharmony_ci struct ufs_dir_entry *pde = NULL; 5048c2ecf20Sopenharmony_ci struct ufs_dir_entry *de = (struct ufs_dir_entry *) (kaddr + from); 5058c2ecf20Sopenharmony_ci int err; 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci UFSD("ENTER\n"); 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci UFSD("ino %u, reclen %u, namlen %u, name %s\n", 5108c2ecf20Sopenharmony_ci fs32_to_cpu(sb, de->d_ino), 5118c2ecf20Sopenharmony_ci fs16_to_cpu(sb, de->d_reclen), 5128c2ecf20Sopenharmony_ci ufs_get_de_namlen(sb, de), de->d_name); 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci while ((char*)de < (char*)dir) { 5158c2ecf20Sopenharmony_ci if (de->d_reclen == 0) { 5168c2ecf20Sopenharmony_ci ufs_error(inode->i_sb, __func__, 5178c2ecf20Sopenharmony_ci "zero-length directory entry"); 5188c2ecf20Sopenharmony_ci err = -EIO; 5198c2ecf20Sopenharmony_ci goto out; 5208c2ecf20Sopenharmony_ci } 5218c2ecf20Sopenharmony_ci pde = de; 5228c2ecf20Sopenharmony_ci de = ufs_next_entry(sb, de); 5238c2ecf20Sopenharmony_ci } 5248c2ecf20Sopenharmony_ci if (pde) 5258c2ecf20Sopenharmony_ci from = (char*)pde - (char*)page_address(page); 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci pos = page_offset(page) + from; 5288c2ecf20Sopenharmony_ci lock_page(page); 5298c2ecf20Sopenharmony_ci err = ufs_prepare_chunk(page, pos, to - from); 5308c2ecf20Sopenharmony_ci BUG_ON(err); 5318c2ecf20Sopenharmony_ci if (pde) 5328c2ecf20Sopenharmony_ci pde->d_reclen = cpu_to_fs16(sb, to - from); 5338c2ecf20Sopenharmony_ci dir->d_ino = 0; 5348c2ecf20Sopenharmony_ci err = ufs_commit_chunk(page, pos, to - from); 5358c2ecf20Sopenharmony_ci inode->i_ctime = inode->i_mtime = current_time(inode); 5368c2ecf20Sopenharmony_ci mark_inode_dirty(inode); 5378c2ecf20Sopenharmony_ciout: 5388c2ecf20Sopenharmony_ci ufs_put_page(page); 5398c2ecf20Sopenharmony_ci UFSD("EXIT\n"); 5408c2ecf20Sopenharmony_ci return err; 5418c2ecf20Sopenharmony_ci} 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ciint ufs_make_empty(struct inode * inode, struct inode *dir) 5448c2ecf20Sopenharmony_ci{ 5458c2ecf20Sopenharmony_ci struct super_block * sb = dir->i_sb; 5468c2ecf20Sopenharmony_ci struct address_space *mapping = inode->i_mapping; 5478c2ecf20Sopenharmony_ci struct page *page = grab_cache_page(mapping, 0); 5488c2ecf20Sopenharmony_ci const unsigned int chunk_size = UFS_SB(sb)->s_uspi->s_dirblksize; 5498c2ecf20Sopenharmony_ci struct ufs_dir_entry * de; 5508c2ecf20Sopenharmony_ci char *base; 5518c2ecf20Sopenharmony_ci int err; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci if (!page) 5548c2ecf20Sopenharmony_ci return -ENOMEM; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci err = ufs_prepare_chunk(page, 0, chunk_size); 5578c2ecf20Sopenharmony_ci if (err) { 5588c2ecf20Sopenharmony_ci unlock_page(page); 5598c2ecf20Sopenharmony_ci goto fail; 5608c2ecf20Sopenharmony_ci } 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci kmap(page); 5638c2ecf20Sopenharmony_ci base = (char*)page_address(page); 5648c2ecf20Sopenharmony_ci memset(base, 0, PAGE_SIZE); 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci de = (struct ufs_dir_entry *) base; 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci de->d_ino = cpu_to_fs32(sb, inode->i_ino); 5698c2ecf20Sopenharmony_ci ufs_set_de_type(sb, de, inode->i_mode); 5708c2ecf20Sopenharmony_ci ufs_set_de_namlen(sb, de, 1); 5718c2ecf20Sopenharmony_ci de->d_reclen = cpu_to_fs16(sb, UFS_DIR_REC_LEN(1)); 5728c2ecf20Sopenharmony_ci strcpy (de->d_name, "."); 5738c2ecf20Sopenharmony_ci de = (struct ufs_dir_entry *) 5748c2ecf20Sopenharmony_ci ((char *)de + fs16_to_cpu(sb, de->d_reclen)); 5758c2ecf20Sopenharmony_ci de->d_ino = cpu_to_fs32(sb, dir->i_ino); 5768c2ecf20Sopenharmony_ci ufs_set_de_type(sb, de, dir->i_mode); 5778c2ecf20Sopenharmony_ci de->d_reclen = cpu_to_fs16(sb, chunk_size - UFS_DIR_REC_LEN(1)); 5788c2ecf20Sopenharmony_ci ufs_set_de_namlen(sb, de, 2); 5798c2ecf20Sopenharmony_ci strcpy (de->d_name, ".."); 5808c2ecf20Sopenharmony_ci kunmap(page); 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci err = ufs_commit_chunk(page, 0, chunk_size); 5838c2ecf20Sopenharmony_cifail: 5848c2ecf20Sopenharmony_ci put_page(page); 5858c2ecf20Sopenharmony_ci return err; 5868c2ecf20Sopenharmony_ci} 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci/* 5898c2ecf20Sopenharmony_ci * routine to check that the specified directory is empty (for rmdir) 5908c2ecf20Sopenharmony_ci */ 5918c2ecf20Sopenharmony_ciint ufs_empty_dir(struct inode * inode) 5928c2ecf20Sopenharmony_ci{ 5938c2ecf20Sopenharmony_ci struct super_block *sb = inode->i_sb; 5948c2ecf20Sopenharmony_ci struct page *page = NULL; 5958c2ecf20Sopenharmony_ci unsigned long i, npages = dir_pages(inode); 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci for (i = 0; i < npages; i++) { 5988c2ecf20Sopenharmony_ci char *kaddr; 5998c2ecf20Sopenharmony_ci struct ufs_dir_entry *de; 6008c2ecf20Sopenharmony_ci page = ufs_get_page(inode, i); 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci if (IS_ERR(page)) 6038c2ecf20Sopenharmony_ci continue; 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci kaddr = page_address(page); 6068c2ecf20Sopenharmony_ci de = (struct ufs_dir_entry *)kaddr; 6078c2ecf20Sopenharmony_ci kaddr += ufs_last_byte(inode, i) - UFS_DIR_REC_LEN(1); 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci while ((char *)de <= kaddr) { 6108c2ecf20Sopenharmony_ci if (de->d_reclen == 0) { 6118c2ecf20Sopenharmony_ci ufs_error(inode->i_sb, __func__, 6128c2ecf20Sopenharmony_ci "zero-length directory entry: " 6138c2ecf20Sopenharmony_ci "kaddr=%p, de=%p\n", kaddr, de); 6148c2ecf20Sopenharmony_ci goto not_empty; 6158c2ecf20Sopenharmony_ci } 6168c2ecf20Sopenharmony_ci if (de->d_ino) { 6178c2ecf20Sopenharmony_ci u16 namelen=ufs_get_de_namlen(sb, de); 6188c2ecf20Sopenharmony_ci /* check for . and .. */ 6198c2ecf20Sopenharmony_ci if (de->d_name[0] != '.') 6208c2ecf20Sopenharmony_ci goto not_empty; 6218c2ecf20Sopenharmony_ci if (namelen > 2) 6228c2ecf20Sopenharmony_ci goto not_empty; 6238c2ecf20Sopenharmony_ci if (namelen < 2) { 6248c2ecf20Sopenharmony_ci if (inode->i_ino != 6258c2ecf20Sopenharmony_ci fs32_to_cpu(sb, de->d_ino)) 6268c2ecf20Sopenharmony_ci goto not_empty; 6278c2ecf20Sopenharmony_ci } else if (de->d_name[1] != '.') 6288c2ecf20Sopenharmony_ci goto not_empty; 6298c2ecf20Sopenharmony_ci } 6308c2ecf20Sopenharmony_ci de = ufs_next_entry(sb, de); 6318c2ecf20Sopenharmony_ci } 6328c2ecf20Sopenharmony_ci ufs_put_page(page); 6338c2ecf20Sopenharmony_ci } 6348c2ecf20Sopenharmony_ci return 1; 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_cinot_empty: 6378c2ecf20Sopenharmony_ci ufs_put_page(page); 6388c2ecf20Sopenharmony_ci return 0; 6398c2ecf20Sopenharmony_ci} 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ciconst struct file_operations ufs_dir_operations = { 6428c2ecf20Sopenharmony_ci .read = generic_read_dir, 6438c2ecf20Sopenharmony_ci .iterate_shared = ufs_readdir, 6448c2ecf20Sopenharmony_ci .fsync = generic_file_fsync, 6458c2ecf20Sopenharmony_ci .llseek = generic_file_llseek, 6468c2ecf20Sopenharmony_ci}; 647