162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * linux/fs/ufs/ufs_dir.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 1996 662306a36Sopenharmony_ci * Adrian Rodriguez (adrian@franklins-tower.rutgers.edu) 762306a36Sopenharmony_ci * Laboratory for Computer Science Research Computing Facility 862306a36Sopenharmony_ci * Rutgers, The State University of New Jersey 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * swab support by Francois-Rene Rideau <fare@tunes.org> 19970406 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * 4.4BSD (FreeBSD) support added on February 1st 1998 by 1362306a36Sopenharmony_ci * Niels Kristian Bech Jensen <nkbj@image.dk> partially based 1462306a36Sopenharmony_ci * on code by Martin von Loewis <martin@mira.isdn.cs.tu-berlin.de>. 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci * Migration to usage of "page cache" on May 2006 by 1762306a36Sopenharmony_ci * Evgeniy Dushistov <dushistov@mail.ru> based on ext2 code base. 1862306a36Sopenharmony_ci */ 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include <linux/time.h> 2162306a36Sopenharmony_ci#include <linux/fs.h> 2262306a36Sopenharmony_ci#include <linux/swap.h> 2362306a36Sopenharmony_ci#include <linux/iversion.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include "ufs_fs.h" 2662306a36Sopenharmony_ci#include "ufs.h" 2762306a36Sopenharmony_ci#include "swab.h" 2862306a36Sopenharmony_ci#include "util.h" 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci/* 3162306a36Sopenharmony_ci * NOTE! unlike strncmp, ufs_match returns 1 for success, 0 for failure. 3262306a36Sopenharmony_ci * 3362306a36Sopenharmony_ci * len <= UFS_MAXNAMLEN and de != NULL are guaranteed by caller. 3462306a36Sopenharmony_ci */ 3562306a36Sopenharmony_cistatic inline int ufs_match(struct super_block *sb, int len, 3662306a36Sopenharmony_ci const unsigned char *name, struct ufs_dir_entry *de) 3762306a36Sopenharmony_ci{ 3862306a36Sopenharmony_ci if (len != ufs_get_de_namlen(sb, de)) 3962306a36Sopenharmony_ci return 0; 4062306a36Sopenharmony_ci if (!de->d_ino) 4162306a36Sopenharmony_ci return 0; 4262306a36Sopenharmony_ci return !memcmp(name, de->d_name, len); 4362306a36Sopenharmony_ci} 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic void ufs_commit_chunk(struct page *page, loff_t pos, unsigned len) 4662306a36Sopenharmony_ci{ 4762306a36Sopenharmony_ci struct address_space *mapping = page->mapping; 4862306a36Sopenharmony_ci struct inode *dir = mapping->host; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci inode_inc_iversion(dir); 5162306a36Sopenharmony_ci block_write_end(NULL, mapping, pos, len, len, page, NULL); 5262306a36Sopenharmony_ci if (pos+len > dir->i_size) { 5362306a36Sopenharmony_ci i_size_write(dir, pos+len); 5462306a36Sopenharmony_ci mark_inode_dirty(dir); 5562306a36Sopenharmony_ci } 5662306a36Sopenharmony_ci unlock_page(page); 5762306a36Sopenharmony_ci} 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic int ufs_handle_dirsync(struct inode *dir) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci int err; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci err = filemap_write_and_wait(dir->i_mapping); 6462306a36Sopenharmony_ci if (!err) 6562306a36Sopenharmony_ci err = sync_inode_metadata(dir, 1); 6662306a36Sopenharmony_ci return err; 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistatic inline void ufs_put_page(struct page *page) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci kunmap(page); 7262306a36Sopenharmony_ci put_page(page); 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ciino_t ufs_inode_by_name(struct inode *dir, const struct qstr *qstr) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci ino_t res = 0; 7862306a36Sopenharmony_ci struct ufs_dir_entry *de; 7962306a36Sopenharmony_ci struct page *page; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci de = ufs_find_entry(dir, qstr, &page); 8262306a36Sopenharmony_ci if (de) { 8362306a36Sopenharmony_ci res = fs32_to_cpu(dir->i_sb, de->d_ino); 8462306a36Sopenharmony_ci ufs_put_page(page); 8562306a36Sopenharmony_ci } 8662306a36Sopenharmony_ci return res; 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci/* Releases the page */ 9162306a36Sopenharmony_civoid ufs_set_link(struct inode *dir, struct ufs_dir_entry *de, 9262306a36Sopenharmony_ci struct page *page, struct inode *inode, 9362306a36Sopenharmony_ci bool update_times) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci loff_t pos = page_offset(page) + 9662306a36Sopenharmony_ci (char *) de - (char *) page_address(page); 9762306a36Sopenharmony_ci unsigned len = fs16_to_cpu(dir->i_sb, de->d_reclen); 9862306a36Sopenharmony_ci int err; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci lock_page(page); 10162306a36Sopenharmony_ci err = ufs_prepare_chunk(page, pos, len); 10262306a36Sopenharmony_ci BUG_ON(err); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci de->d_ino = cpu_to_fs32(dir->i_sb, inode->i_ino); 10562306a36Sopenharmony_ci ufs_set_de_type(dir->i_sb, de, inode->i_mode); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci ufs_commit_chunk(page, pos, len); 10862306a36Sopenharmony_ci ufs_put_page(page); 10962306a36Sopenharmony_ci if (update_times) 11062306a36Sopenharmony_ci dir->i_mtime = inode_set_ctime_current(dir); 11162306a36Sopenharmony_ci mark_inode_dirty(dir); 11262306a36Sopenharmony_ci ufs_handle_dirsync(dir); 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic bool ufs_check_page(struct page *page) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci struct inode *dir = page->mapping->host; 11962306a36Sopenharmony_ci struct super_block *sb = dir->i_sb; 12062306a36Sopenharmony_ci char *kaddr = page_address(page); 12162306a36Sopenharmony_ci unsigned offs, rec_len; 12262306a36Sopenharmony_ci unsigned limit = PAGE_SIZE; 12362306a36Sopenharmony_ci const unsigned chunk_mask = UFS_SB(sb)->s_uspi->s_dirblksize - 1; 12462306a36Sopenharmony_ci struct ufs_dir_entry *p; 12562306a36Sopenharmony_ci char *error; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci if ((dir->i_size >> PAGE_SHIFT) == page->index) { 12862306a36Sopenharmony_ci limit = dir->i_size & ~PAGE_MASK; 12962306a36Sopenharmony_ci if (limit & chunk_mask) 13062306a36Sopenharmony_ci goto Ebadsize; 13162306a36Sopenharmony_ci if (!limit) 13262306a36Sopenharmony_ci goto out; 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci for (offs = 0; offs <= limit - UFS_DIR_REC_LEN(1); offs += rec_len) { 13562306a36Sopenharmony_ci p = (struct ufs_dir_entry *)(kaddr + offs); 13662306a36Sopenharmony_ci rec_len = fs16_to_cpu(sb, p->d_reclen); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci if (rec_len < UFS_DIR_REC_LEN(1)) 13962306a36Sopenharmony_ci goto Eshort; 14062306a36Sopenharmony_ci if (rec_len & 3) 14162306a36Sopenharmony_ci goto Ealign; 14262306a36Sopenharmony_ci if (rec_len < UFS_DIR_REC_LEN(ufs_get_de_namlen(sb, p))) 14362306a36Sopenharmony_ci goto Enamelen; 14462306a36Sopenharmony_ci if (((offs + rec_len - 1) ^ offs) & ~chunk_mask) 14562306a36Sopenharmony_ci goto Espan; 14662306a36Sopenharmony_ci if (fs32_to_cpu(sb, p->d_ino) > (UFS_SB(sb)->s_uspi->s_ipg * 14762306a36Sopenharmony_ci UFS_SB(sb)->s_uspi->s_ncg)) 14862306a36Sopenharmony_ci goto Einumber; 14962306a36Sopenharmony_ci } 15062306a36Sopenharmony_ci if (offs != limit) 15162306a36Sopenharmony_ci goto Eend; 15262306a36Sopenharmony_ciout: 15362306a36Sopenharmony_ci SetPageChecked(page); 15462306a36Sopenharmony_ci return true; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci /* Too bad, we had an error */ 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ciEbadsize: 15962306a36Sopenharmony_ci ufs_error(sb, "ufs_check_page", 16062306a36Sopenharmony_ci "size of directory #%lu is not a multiple of chunk size", 16162306a36Sopenharmony_ci dir->i_ino 16262306a36Sopenharmony_ci ); 16362306a36Sopenharmony_ci goto fail; 16462306a36Sopenharmony_ciEshort: 16562306a36Sopenharmony_ci error = "rec_len is smaller than minimal"; 16662306a36Sopenharmony_ci goto bad_entry; 16762306a36Sopenharmony_ciEalign: 16862306a36Sopenharmony_ci error = "unaligned directory entry"; 16962306a36Sopenharmony_ci goto bad_entry; 17062306a36Sopenharmony_ciEnamelen: 17162306a36Sopenharmony_ci error = "rec_len is too small for name_len"; 17262306a36Sopenharmony_ci goto bad_entry; 17362306a36Sopenharmony_ciEspan: 17462306a36Sopenharmony_ci error = "directory entry across blocks"; 17562306a36Sopenharmony_ci goto bad_entry; 17662306a36Sopenharmony_ciEinumber: 17762306a36Sopenharmony_ci error = "inode out of bounds"; 17862306a36Sopenharmony_cibad_entry: 17962306a36Sopenharmony_ci ufs_error (sb, "ufs_check_page", "bad entry in directory #%lu: %s - " 18062306a36Sopenharmony_ci "offset=%lu, rec_len=%d, name_len=%d", 18162306a36Sopenharmony_ci dir->i_ino, error, (page->index<<PAGE_SHIFT)+offs, 18262306a36Sopenharmony_ci rec_len, ufs_get_de_namlen(sb, p)); 18362306a36Sopenharmony_ci goto fail; 18462306a36Sopenharmony_ciEend: 18562306a36Sopenharmony_ci p = (struct ufs_dir_entry *)(kaddr + offs); 18662306a36Sopenharmony_ci ufs_error(sb, __func__, 18762306a36Sopenharmony_ci "entry in directory #%lu spans the page boundary" 18862306a36Sopenharmony_ci "offset=%lu", 18962306a36Sopenharmony_ci dir->i_ino, (page->index<<PAGE_SHIFT)+offs); 19062306a36Sopenharmony_cifail: 19162306a36Sopenharmony_ci SetPageError(page); 19262306a36Sopenharmony_ci return false; 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_cistatic struct page *ufs_get_page(struct inode *dir, unsigned long n) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci struct address_space *mapping = dir->i_mapping; 19862306a36Sopenharmony_ci struct page *page = read_mapping_page(mapping, n, NULL); 19962306a36Sopenharmony_ci if (!IS_ERR(page)) { 20062306a36Sopenharmony_ci kmap(page); 20162306a36Sopenharmony_ci if (unlikely(!PageChecked(page))) { 20262306a36Sopenharmony_ci if (!ufs_check_page(page)) 20362306a36Sopenharmony_ci goto fail; 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci return page; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_cifail: 20962306a36Sopenharmony_ci ufs_put_page(page); 21062306a36Sopenharmony_ci return ERR_PTR(-EIO); 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci/* 21462306a36Sopenharmony_ci * Return the offset into page `page_nr' of the last valid 21562306a36Sopenharmony_ci * byte in that page, plus one. 21662306a36Sopenharmony_ci */ 21762306a36Sopenharmony_cistatic unsigned 21862306a36Sopenharmony_ciufs_last_byte(struct inode *inode, unsigned long page_nr) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci unsigned last_byte = inode->i_size; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci last_byte -= page_nr << PAGE_SHIFT; 22362306a36Sopenharmony_ci if (last_byte > PAGE_SIZE) 22462306a36Sopenharmony_ci last_byte = PAGE_SIZE; 22562306a36Sopenharmony_ci return last_byte; 22662306a36Sopenharmony_ci} 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_cistatic inline struct ufs_dir_entry * 22962306a36Sopenharmony_ciufs_next_entry(struct super_block *sb, struct ufs_dir_entry *p) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci return (struct ufs_dir_entry *)((char *)p + 23262306a36Sopenharmony_ci fs16_to_cpu(sb, p->d_reclen)); 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_cistruct ufs_dir_entry *ufs_dotdot(struct inode *dir, struct page **p) 23662306a36Sopenharmony_ci{ 23762306a36Sopenharmony_ci struct page *page = ufs_get_page(dir, 0); 23862306a36Sopenharmony_ci struct ufs_dir_entry *de = NULL; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci if (!IS_ERR(page)) { 24162306a36Sopenharmony_ci de = ufs_next_entry(dir->i_sb, 24262306a36Sopenharmony_ci (struct ufs_dir_entry *)page_address(page)); 24362306a36Sopenharmony_ci *p = page; 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci return de; 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci/* 24962306a36Sopenharmony_ci * ufs_find_entry() 25062306a36Sopenharmony_ci * 25162306a36Sopenharmony_ci * finds an entry in the specified directory with the wanted name. It 25262306a36Sopenharmony_ci * returns the page in which the entry was found, and the entry itself 25362306a36Sopenharmony_ci * (as a parameter - res_dir). Page is returned mapped and unlocked. 25462306a36Sopenharmony_ci * Entry is guaranteed to be valid. 25562306a36Sopenharmony_ci */ 25662306a36Sopenharmony_cistruct ufs_dir_entry *ufs_find_entry(struct inode *dir, const struct qstr *qstr, 25762306a36Sopenharmony_ci struct page **res_page) 25862306a36Sopenharmony_ci{ 25962306a36Sopenharmony_ci struct super_block *sb = dir->i_sb; 26062306a36Sopenharmony_ci const unsigned char *name = qstr->name; 26162306a36Sopenharmony_ci int namelen = qstr->len; 26262306a36Sopenharmony_ci unsigned reclen = UFS_DIR_REC_LEN(namelen); 26362306a36Sopenharmony_ci unsigned long start, n; 26462306a36Sopenharmony_ci unsigned long npages = dir_pages(dir); 26562306a36Sopenharmony_ci struct page *page = NULL; 26662306a36Sopenharmony_ci struct ufs_inode_info *ui = UFS_I(dir); 26762306a36Sopenharmony_ci struct ufs_dir_entry *de; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci UFSD("ENTER, dir_ino %lu, name %s, namlen %u\n", dir->i_ino, name, namelen); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci if (npages == 0 || namelen > UFS_MAXNAMLEN) 27262306a36Sopenharmony_ci goto out; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci /* OFFSET_CACHE */ 27562306a36Sopenharmony_ci *res_page = NULL; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci start = ui->i_dir_start_lookup; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci if (start >= npages) 28062306a36Sopenharmony_ci start = 0; 28162306a36Sopenharmony_ci n = start; 28262306a36Sopenharmony_ci do { 28362306a36Sopenharmony_ci char *kaddr; 28462306a36Sopenharmony_ci page = ufs_get_page(dir, n); 28562306a36Sopenharmony_ci if (!IS_ERR(page)) { 28662306a36Sopenharmony_ci kaddr = page_address(page); 28762306a36Sopenharmony_ci de = (struct ufs_dir_entry *) kaddr; 28862306a36Sopenharmony_ci kaddr += ufs_last_byte(dir, n) - reclen; 28962306a36Sopenharmony_ci while ((char *) de <= kaddr) { 29062306a36Sopenharmony_ci if (ufs_match(sb, namelen, name, de)) 29162306a36Sopenharmony_ci goto found; 29262306a36Sopenharmony_ci de = ufs_next_entry(sb, de); 29362306a36Sopenharmony_ci } 29462306a36Sopenharmony_ci ufs_put_page(page); 29562306a36Sopenharmony_ci } 29662306a36Sopenharmony_ci if (++n >= npages) 29762306a36Sopenharmony_ci n = 0; 29862306a36Sopenharmony_ci } while (n != start); 29962306a36Sopenharmony_ciout: 30062306a36Sopenharmony_ci return NULL; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_cifound: 30362306a36Sopenharmony_ci *res_page = page; 30462306a36Sopenharmony_ci ui->i_dir_start_lookup = n; 30562306a36Sopenharmony_ci return de; 30662306a36Sopenharmony_ci} 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci/* 30962306a36Sopenharmony_ci * Parent is locked. 31062306a36Sopenharmony_ci */ 31162306a36Sopenharmony_ciint ufs_add_link(struct dentry *dentry, struct inode *inode) 31262306a36Sopenharmony_ci{ 31362306a36Sopenharmony_ci struct inode *dir = d_inode(dentry->d_parent); 31462306a36Sopenharmony_ci const unsigned char *name = dentry->d_name.name; 31562306a36Sopenharmony_ci int namelen = dentry->d_name.len; 31662306a36Sopenharmony_ci struct super_block *sb = dir->i_sb; 31762306a36Sopenharmony_ci unsigned reclen = UFS_DIR_REC_LEN(namelen); 31862306a36Sopenharmony_ci const unsigned int chunk_size = UFS_SB(sb)->s_uspi->s_dirblksize; 31962306a36Sopenharmony_ci unsigned short rec_len, name_len; 32062306a36Sopenharmony_ci struct page *page = NULL; 32162306a36Sopenharmony_ci struct ufs_dir_entry *de; 32262306a36Sopenharmony_ci unsigned long npages = dir_pages(dir); 32362306a36Sopenharmony_ci unsigned long n; 32462306a36Sopenharmony_ci char *kaddr; 32562306a36Sopenharmony_ci loff_t pos; 32662306a36Sopenharmony_ci int err; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci UFSD("ENTER, name %s, namelen %u\n", name, namelen); 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci /* 33162306a36Sopenharmony_ci * We take care of directory expansion in the same loop. 33262306a36Sopenharmony_ci * This code plays outside i_size, so it locks the page 33362306a36Sopenharmony_ci * to protect that region. 33462306a36Sopenharmony_ci */ 33562306a36Sopenharmony_ci for (n = 0; n <= npages; n++) { 33662306a36Sopenharmony_ci char *dir_end; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci page = ufs_get_page(dir, n); 33962306a36Sopenharmony_ci err = PTR_ERR(page); 34062306a36Sopenharmony_ci if (IS_ERR(page)) 34162306a36Sopenharmony_ci goto out; 34262306a36Sopenharmony_ci lock_page(page); 34362306a36Sopenharmony_ci kaddr = page_address(page); 34462306a36Sopenharmony_ci dir_end = kaddr + ufs_last_byte(dir, n); 34562306a36Sopenharmony_ci de = (struct ufs_dir_entry *)kaddr; 34662306a36Sopenharmony_ci kaddr += PAGE_SIZE - reclen; 34762306a36Sopenharmony_ci while ((char *)de <= kaddr) { 34862306a36Sopenharmony_ci if ((char *)de == dir_end) { 34962306a36Sopenharmony_ci /* We hit i_size */ 35062306a36Sopenharmony_ci name_len = 0; 35162306a36Sopenharmony_ci rec_len = chunk_size; 35262306a36Sopenharmony_ci de->d_reclen = cpu_to_fs16(sb, chunk_size); 35362306a36Sopenharmony_ci de->d_ino = 0; 35462306a36Sopenharmony_ci goto got_it; 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci if (de->d_reclen == 0) { 35762306a36Sopenharmony_ci ufs_error(dir->i_sb, __func__, 35862306a36Sopenharmony_ci "zero-length directory entry"); 35962306a36Sopenharmony_ci err = -EIO; 36062306a36Sopenharmony_ci goto out_unlock; 36162306a36Sopenharmony_ci } 36262306a36Sopenharmony_ci err = -EEXIST; 36362306a36Sopenharmony_ci if (ufs_match(sb, namelen, name, de)) 36462306a36Sopenharmony_ci goto out_unlock; 36562306a36Sopenharmony_ci name_len = UFS_DIR_REC_LEN(ufs_get_de_namlen(sb, de)); 36662306a36Sopenharmony_ci rec_len = fs16_to_cpu(sb, de->d_reclen); 36762306a36Sopenharmony_ci if (!de->d_ino && rec_len >= reclen) 36862306a36Sopenharmony_ci goto got_it; 36962306a36Sopenharmony_ci if (rec_len >= name_len + reclen) 37062306a36Sopenharmony_ci goto got_it; 37162306a36Sopenharmony_ci de = (struct ufs_dir_entry *) ((char *) de + rec_len); 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci unlock_page(page); 37462306a36Sopenharmony_ci ufs_put_page(page); 37562306a36Sopenharmony_ci } 37662306a36Sopenharmony_ci BUG(); 37762306a36Sopenharmony_ci return -EINVAL; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_cigot_it: 38062306a36Sopenharmony_ci pos = page_offset(page) + 38162306a36Sopenharmony_ci (char*)de - (char*)page_address(page); 38262306a36Sopenharmony_ci err = ufs_prepare_chunk(page, pos, rec_len); 38362306a36Sopenharmony_ci if (err) 38462306a36Sopenharmony_ci goto out_unlock; 38562306a36Sopenharmony_ci if (de->d_ino) { 38662306a36Sopenharmony_ci struct ufs_dir_entry *de1 = 38762306a36Sopenharmony_ci (struct ufs_dir_entry *) ((char *) de + name_len); 38862306a36Sopenharmony_ci de1->d_reclen = cpu_to_fs16(sb, rec_len - name_len); 38962306a36Sopenharmony_ci de->d_reclen = cpu_to_fs16(sb, name_len); 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci de = de1; 39262306a36Sopenharmony_ci } 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci ufs_set_de_namlen(sb, de, namelen); 39562306a36Sopenharmony_ci memcpy(de->d_name, name, namelen + 1); 39662306a36Sopenharmony_ci de->d_ino = cpu_to_fs32(sb, inode->i_ino); 39762306a36Sopenharmony_ci ufs_set_de_type(sb, de, inode->i_mode); 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci ufs_commit_chunk(page, pos, rec_len); 40062306a36Sopenharmony_ci dir->i_mtime = inode_set_ctime_current(dir); 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci mark_inode_dirty(dir); 40362306a36Sopenharmony_ci err = ufs_handle_dirsync(dir); 40462306a36Sopenharmony_ci /* OFFSET_CACHE */ 40562306a36Sopenharmony_ciout_put: 40662306a36Sopenharmony_ci ufs_put_page(page); 40762306a36Sopenharmony_ciout: 40862306a36Sopenharmony_ci return err; 40962306a36Sopenharmony_ciout_unlock: 41062306a36Sopenharmony_ci unlock_page(page); 41162306a36Sopenharmony_ci goto out_put; 41262306a36Sopenharmony_ci} 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_cistatic inline unsigned 41562306a36Sopenharmony_ciufs_validate_entry(struct super_block *sb, char *base, 41662306a36Sopenharmony_ci unsigned offset, unsigned mask) 41762306a36Sopenharmony_ci{ 41862306a36Sopenharmony_ci struct ufs_dir_entry *de = (struct ufs_dir_entry*)(base + offset); 41962306a36Sopenharmony_ci struct ufs_dir_entry *p = (struct ufs_dir_entry*)(base + (offset&mask)); 42062306a36Sopenharmony_ci while ((char*)p < (char*)de) 42162306a36Sopenharmony_ci p = ufs_next_entry(sb, p); 42262306a36Sopenharmony_ci return (char *)p - base; 42362306a36Sopenharmony_ci} 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci/* 42762306a36Sopenharmony_ci * This is blatantly stolen from ext2fs 42862306a36Sopenharmony_ci */ 42962306a36Sopenharmony_cistatic int 43062306a36Sopenharmony_ciufs_readdir(struct file *file, struct dir_context *ctx) 43162306a36Sopenharmony_ci{ 43262306a36Sopenharmony_ci loff_t pos = ctx->pos; 43362306a36Sopenharmony_ci struct inode *inode = file_inode(file); 43462306a36Sopenharmony_ci struct super_block *sb = inode->i_sb; 43562306a36Sopenharmony_ci unsigned int offset = pos & ~PAGE_MASK; 43662306a36Sopenharmony_ci unsigned long n = pos >> PAGE_SHIFT; 43762306a36Sopenharmony_ci unsigned long npages = dir_pages(inode); 43862306a36Sopenharmony_ci unsigned chunk_mask = ~(UFS_SB(sb)->s_uspi->s_dirblksize - 1); 43962306a36Sopenharmony_ci bool need_revalidate = !inode_eq_iversion(inode, file->f_version); 44062306a36Sopenharmony_ci unsigned flags = UFS_SB(sb)->s_flags; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci UFSD("BEGIN\n"); 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci if (pos > inode->i_size - UFS_DIR_REC_LEN(1)) 44562306a36Sopenharmony_ci return 0; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci for ( ; n < npages; n++, offset = 0) { 44862306a36Sopenharmony_ci char *kaddr, *limit; 44962306a36Sopenharmony_ci struct ufs_dir_entry *de; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci struct page *page = ufs_get_page(inode, n); 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci if (IS_ERR(page)) { 45462306a36Sopenharmony_ci ufs_error(sb, __func__, 45562306a36Sopenharmony_ci "bad page in #%lu", 45662306a36Sopenharmony_ci inode->i_ino); 45762306a36Sopenharmony_ci ctx->pos += PAGE_SIZE - offset; 45862306a36Sopenharmony_ci return -EIO; 45962306a36Sopenharmony_ci } 46062306a36Sopenharmony_ci kaddr = page_address(page); 46162306a36Sopenharmony_ci if (unlikely(need_revalidate)) { 46262306a36Sopenharmony_ci if (offset) { 46362306a36Sopenharmony_ci offset = ufs_validate_entry(sb, kaddr, offset, chunk_mask); 46462306a36Sopenharmony_ci ctx->pos = (n<<PAGE_SHIFT) + offset; 46562306a36Sopenharmony_ci } 46662306a36Sopenharmony_ci file->f_version = inode_query_iversion(inode); 46762306a36Sopenharmony_ci need_revalidate = false; 46862306a36Sopenharmony_ci } 46962306a36Sopenharmony_ci de = (struct ufs_dir_entry *)(kaddr+offset); 47062306a36Sopenharmony_ci limit = kaddr + ufs_last_byte(inode, n) - UFS_DIR_REC_LEN(1); 47162306a36Sopenharmony_ci for ( ;(char*)de <= limit; de = ufs_next_entry(sb, de)) { 47262306a36Sopenharmony_ci if (de->d_ino) { 47362306a36Sopenharmony_ci unsigned char d_type = DT_UNKNOWN; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci UFSD("filldir(%s,%u)\n", de->d_name, 47662306a36Sopenharmony_ci fs32_to_cpu(sb, de->d_ino)); 47762306a36Sopenharmony_ci UFSD("namlen %u\n", ufs_get_de_namlen(sb, de)); 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci if ((flags & UFS_DE_MASK) == UFS_DE_44BSD) 48062306a36Sopenharmony_ci d_type = de->d_u.d_44.d_type; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci if (!dir_emit(ctx, de->d_name, 48362306a36Sopenharmony_ci ufs_get_de_namlen(sb, de), 48462306a36Sopenharmony_ci fs32_to_cpu(sb, de->d_ino), 48562306a36Sopenharmony_ci d_type)) { 48662306a36Sopenharmony_ci ufs_put_page(page); 48762306a36Sopenharmony_ci return 0; 48862306a36Sopenharmony_ci } 48962306a36Sopenharmony_ci } 49062306a36Sopenharmony_ci ctx->pos += fs16_to_cpu(sb, de->d_reclen); 49162306a36Sopenharmony_ci } 49262306a36Sopenharmony_ci ufs_put_page(page); 49362306a36Sopenharmony_ci } 49462306a36Sopenharmony_ci return 0; 49562306a36Sopenharmony_ci} 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci/* 49962306a36Sopenharmony_ci * ufs_delete_entry deletes a directory entry by merging it with the 50062306a36Sopenharmony_ci * previous entry. 50162306a36Sopenharmony_ci */ 50262306a36Sopenharmony_ciint ufs_delete_entry(struct inode *inode, struct ufs_dir_entry *dir, 50362306a36Sopenharmony_ci struct page * page) 50462306a36Sopenharmony_ci{ 50562306a36Sopenharmony_ci struct super_block *sb = inode->i_sb; 50662306a36Sopenharmony_ci char *kaddr = page_address(page); 50762306a36Sopenharmony_ci unsigned from = ((char*)dir - kaddr) & ~(UFS_SB(sb)->s_uspi->s_dirblksize - 1); 50862306a36Sopenharmony_ci unsigned to = ((char*)dir - kaddr) + fs16_to_cpu(sb, dir->d_reclen); 50962306a36Sopenharmony_ci loff_t pos; 51062306a36Sopenharmony_ci struct ufs_dir_entry *pde = NULL; 51162306a36Sopenharmony_ci struct ufs_dir_entry *de = (struct ufs_dir_entry *) (kaddr + from); 51262306a36Sopenharmony_ci int err; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci UFSD("ENTER\n"); 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci UFSD("ino %u, reclen %u, namlen %u, name %s\n", 51762306a36Sopenharmony_ci fs32_to_cpu(sb, de->d_ino), 51862306a36Sopenharmony_ci fs16_to_cpu(sb, de->d_reclen), 51962306a36Sopenharmony_ci ufs_get_de_namlen(sb, de), de->d_name); 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci while ((char*)de < (char*)dir) { 52262306a36Sopenharmony_ci if (de->d_reclen == 0) { 52362306a36Sopenharmony_ci ufs_error(inode->i_sb, __func__, 52462306a36Sopenharmony_ci "zero-length directory entry"); 52562306a36Sopenharmony_ci err = -EIO; 52662306a36Sopenharmony_ci goto out; 52762306a36Sopenharmony_ci } 52862306a36Sopenharmony_ci pde = de; 52962306a36Sopenharmony_ci de = ufs_next_entry(sb, de); 53062306a36Sopenharmony_ci } 53162306a36Sopenharmony_ci if (pde) 53262306a36Sopenharmony_ci from = (char*)pde - (char*)page_address(page); 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci pos = page_offset(page) + from; 53562306a36Sopenharmony_ci lock_page(page); 53662306a36Sopenharmony_ci err = ufs_prepare_chunk(page, pos, to - from); 53762306a36Sopenharmony_ci BUG_ON(err); 53862306a36Sopenharmony_ci if (pde) 53962306a36Sopenharmony_ci pde->d_reclen = cpu_to_fs16(sb, to - from); 54062306a36Sopenharmony_ci dir->d_ino = 0; 54162306a36Sopenharmony_ci ufs_commit_chunk(page, pos, to - from); 54262306a36Sopenharmony_ci inode->i_mtime = inode_set_ctime_current(inode); 54362306a36Sopenharmony_ci mark_inode_dirty(inode); 54462306a36Sopenharmony_ci err = ufs_handle_dirsync(inode); 54562306a36Sopenharmony_ciout: 54662306a36Sopenharmony_ci ufs_put_page(page); 54762306a36Sopenharmony_ci UFSD("EXIT\n"); 54862306a36Sopenharmony_ci return err; 54962306a36Sopenharmony_ci} 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ciint ufs_make_empty(struct inode * inode, struct inode *dir) 55262306a36Sopenharmony_ci{ 55362306a36Sopenharmony_ci struct super_block * sb = dir->i_sb; 55462306a36Sopenharmony_ci struct address_space *mapping = inode->i_mapping; 55562306a36Sopenharmony_ci struct page *page = grab_cache_page(mapping, 0); 55662306a36Sopenharmony_ci const unsigned int chunk_size = UFS_SB(sb)->s_uspi->s_dirblksize; 55762306a36Sopenharmony_ci struct ufs_dir_entry * de; 55862306a36Sopenharmony_ci char *base; 55962306a36Sopenharmony_ci int err; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci if (!page) 56262306a36Sopenharmony_ci return -ENOMEM; 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci err = ufs_prepare_chunk(page, 0, chunk_size); 56562306a36Sopenharmony_ci if (err) { 56662306a36Sopenharmony_ci unlock_page(page); 56762306a36Sopenharmony_ci goto fail; 56862306a36Sopenharmony_ci } 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci kmap(page); 57162306a36Sopenharmony_ci base = (char*)page_address(page); 57262306a36Sopenharmony_ci memset(base, 0, PAGE_SIZE); 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci de = (struct ufs_dir_entry *) base; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci de->d_ino = cpu_to_fs32(sb, inode->i_ino); 57762306a36Sopenharmony_ci ufs_set_de_type(sb, de, inode->i_mode); 57862306a36Sopenharmony_ci ufs_set_de_namlen(sb, de, 1); 57962306a36Sopenharmony_ci de->d_reclen = cpu_to_fs16(sb, UFS_DIR_REC_LEN(1)); 58062306a36Sopenharmony_ci strcpy (de->d_name, "."); 58162306a36Sopenharmony_ci de = (struct ufs_dir_entry *) 58262306a36Sopenharmony_ci ((char *)de + fs16_to_cpu(sb, de->d_reclen)); 58362306a36Sopenharmony_ci de->d_ino = cpu_to_fs32(sb, dir->i_ino); 58462306a36Sopenharmony_ci ufs_set_de_type(sb, de, dir->i_mode); 58562306a36Sopenharmony_ci de->d_reclen = cpu_to_fs16(sb, chunk_size - UFS_DIR_REC_LEN(1)); 58662306a36Sopenharmony_ci ufs_set_de_namlen(sb, de, 2); 58762306a36Sopenharmony_ci strcpy (de->d_name, ".."); 58862306a36Sopenharmony_ci kunmap(page); 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci ufs_commit_chunk(page, 0, chunk_size); 59162306a36Sopenharmony_ci err = ufs_handle_dirsync(inode); 59262306a36Sopenharmony_cifail: 59362306a36Sopenharmony_ci put_page(page); 59462306a36Sopenharmony_ci return err; 59562306a36Sopenharmony_ci} 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci/* 59862306a36Sopenharmony_ci * routine to check that the specified directory is empty (for rmdir) 59962306a36Sopenharmony_ci */ 60062306a36Sopenharmony_ciint ufs_empty_dir(struct inode * inode) 60162306a36Sopenharmony_ci{ 60262306a36Sopenharmony_ci struct super_block *sb = inode->i_sb; 60362306a36Sopenharmony_ci struct page *page = NULL; 60462306a36Sopenharmony_ci unsigned long i, npages = dir_pages(inode); 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci for (i = 0; i < npages; i++) { 60762306a36Sopenharmony_ci char *kaddr; 60862306a36Sopenharmony_ci struct ufs_dir_entry *de; 60962306a36Sopenharmony_ci page = ufs_get_page(inode, i); 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci if (IS_ERR(page)) 61262306a36Sopenharmony_ci continue; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci kaddr = page_address(page); 61562306a36Sopenharmony_ci de = (struct ufs_dir_entry *)kaddr; 61662306a36Sopenharmony_ci kaddr += ufs_last_byte(inode, i) - UFS_DIR_REC_LEN(1); 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci while ((char *)de <= kaddr) { 61962306a36Sopenharmony_ci if (de->d_reclen == 0) { 62062306a36Sopenharmony_ci ufs_error(inode->i_sb, __func__, 62162306a36Sopenharmony_ci "zero-length directory entry: " 62262306a36Sopenharmony_ci "kaddr=%p, de=%p\n", kaddr, de); 62362306a36Sopenharmony_ci goto not_empty; 62462306a36Sopenharmony_ci } 62562306a36Sopenharmony_ci if (de->d_ino) { 62662306a36Sopenharmony_ci u16 namelen=ufs_get_de_namlen(sb, de); 62762306a36Sopenharmony_ci /* check for . and .. */ 62862306a36Sopenharmony_ci if (de->d_name[0] != '.') 62962306a36Sopenharmony_ci goto not_empty; 63062306a36Sopenharmony_ci if (namelen > 2) 63162306a36Sopenharmony_ci goto not_empty; 63262306a36Sopenharmony_ci if (namelen < 2) { 63362306a36Sopenharmony_ci if (inode->i_ino != 63462306a36Sopenharmony_ci fs32_to_cpu(sb, de->d_ino)) 63562306a36Sopenharmony_ci goto not_empty; 63662306a36Sopenharmony_ci } else if (de->d_name[1] != '.') 63762306a36Sopenharmony_ci goto not_empty; 63862306a36Sopenharmony_ci } 63962306a36Sopenharmony_ci de = ufs_next_entry(sb, de); 64062306a36Sopenharmony_ci } 64162306a36Sopenharmony_ci ufs_put_page(page); 64262306a36Sopenharmony_ci } 64362306a36Sopenharmony_ci return 1; 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_cinot_empty: 64662306a36Sopenharmony_ci ufs_put_page(page); 64762306a36Sopenharmony_ci return 0; 64862306a36Sopenharmony_ci} 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ciconst struct file_operations ufs_dir_operations = { 65162306a36Sopenharmony_ci .read = generic_read_dir, 65262306a36Sopenharmony_ci .iterate_shared = ufs_readdir, 65362306a36Sopenharmony_ci .fsync = generic_file_fsync, 65462306a36Sopenharmony_ci .llseek = generic_file_llseek, 65562306a36Sopenharmony_ci}; 656