162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * fs/bfs/dir.c 462306a36Sopenharmony_ci * BFS directory operations. 562306a36Sopenharmony_ci * Copyright (C) 1999-2018 Tigran Aivazian <aivazian.tigran@gmail.com> 662306a36Sopenharmony_ci * Made endianness-clean by Andrew Stribblehill <ads@wompom.org> 2005 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/time.h> 1062306a36Sopenharmony_ci#include <linux/string.h> 1162306a36Sopenharmony_ci#include <linux/fs.h> 1262306a36Sopenharmony_ci#include <linux/buffer_head.h> 1362306a36Sopenharmony_ci#include <linux/sched.h> 1462306a36Sopenharmony_ci#include "bfs.h" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#undef DEBUG 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#ifdef DEBUG 1962306a36Sopenharmony_ci#define dprintf(x...) printf(x) 2062306a36Sopenharmony_ci#else 2162306a36Sopenharmony_ci#define dprintf(x...) 2262306a36Sopenharmony_ci#endif 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistatic int bfs_add_entry(struct inode *dir, const struct qstr *child, int ino); 2562306a36Sopenharmony_cistatic struct buffer_head *bfs_find_entry(struct inode *dir, 2662306a36Sopenharmony_ci const struct qstr *child, 2762306a36Sopenharmony_ci struct bfs_dirent **res_dir); 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistatic int bfs_readdir(struct file *f, struct dir_context *ctx) 3062306a36Sopenharmony_ci{ 3162306a36Sopenharmony_ci struct inode *dir = file_inode(f); 3262306a36Sopenharmony_ci struct buffer_head *bh; 3362306a36Sopenharmony_ci struct bfs_dirent *de; 3462306a36Sopenharmony_ci unsigned int offset; 3562306a36Sopenharmony_ci int block; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci if (ctx->pos & (BFS_DIRENT_SIZE - 1)) { 3862306a36Sopenharmony_ci printf("Bad f_pos=%08lx for %s:%08lx\n", 3962306a36Sopenharmony_ci (unsigned long)ctx->pos, 4062306a36Sopenharmony_ci dir->i_sb->s_id, dir->i_ino); 4162306a36Sopenharmony_ci return -EINVAL; 4262306a36Sopenharmony_ci } 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci while (ctx->pos < dir->i_size) { 4562306a36Sopenharmony_ci offset = ctx->pos & (BFS_BSIZE - 1); 4662306a36Sopenharmony_ci block = BFS_I(dir)->i_sblock + (ctx->pos >> BFS_BSIZE_BITS); 4762306a36Sopenharmony_ci bh = sb_bread(dir->i_sb, block); 4862306a36Sopenharmony_ci if (!bh) { 4962306a36Sopenharmony_ci ctx->pos += BFS_BSIZE - offset; 5062306a36Sopenharmony_ci continue; 5162306a36Sopenharmony_ci } 5262306a36Sopenharmony_ci do { 5362306a36Sopenharmony_ci de = (struct bfs_dirent *)(bh->b_data + offset); 5462306a36Sopenharmony_ci if (de->ino) { 5562306a36Sopenharmony_ci int size = strnlen(de->name, BFS_NAMELEN); 5662306a36Sopenharmony_ci if (!dir_emit(ctx, de->name, size, 5762306a36Sopenharmony_ci le16_to_cpu(de->ino), 5862306a36Sopenharmony_ci DT_UNKNOWN)) { 5962306a36Sopenharmony_ci brelse(bh); 6062306a36Sopenharmony_ci return 0; 6162306a36Sopenharmony_ci } 6262306a36Sopenharmony_ci } 6362306a36Sopenharmony_ci offset += BFS_DIRENT_SIZE; 6462306a36Sopenharmony_ci ctx->pos += BFS_DIRENT_SIZE; 6562306a36Sopenharmony_ci } while ((offset < BFS_BSIZE) && (ctx->pos < dir->i_size)); 6662306a36Sopenharmony_ci brelse(bh); 6762306a36Sopenharmony_ci } 6862306a36Sopenharmony_ci return 0; 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ciconst struct file_operations bfs_dir_operations = { 7262306a36Sopenharmony_ci .read = generic_read_dir, 7362306a36Sopenharmony_ci .iterate_shared = bfs_readdir, 7462306a36Sopenharmony_ci .fsync = generic_file_fsync, 7562306a36Sopenharmony_ci .llseek = generic_file_llseek, 7662306a36Sopenharmony_ci}; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistatic int bfs_create(struct mnt_idmap *idmap, struct inode *dir, 7962306a36Sopenharmony_ci struct dentry *dentry, umode_t mode, bool excl) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci int err; 8262306a36Sopenharmony_ci struct inode *inode; 8362306a36Sopenharmony_ci struct super_block *s = dir->i_sb; 8462306a36Sopenharmony_ci struct bfs_sb_info *info = BFS_SB(s); 8562306a36Sopenharmony_ci unsigned long ino; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci inode = new_inode(s); 8862306a36Sopenharmony_ci if (!inode) 8962306a36Sopenharmony_ci return -ENOMEM; 9062306a36Sopenharmony_ci mutex_lock(&info->bfs_lock); 9162306a36Sopenharmony_ci ino = find_first_zero_bit(info->si_imap, info->si_lasti + 1); 9262306a36Sopenharmony_ci if (ino > info->si_lasti) { 9362306a36Sopenharmony_ci mutex_unlock(&info->bfs_lock); 9462306a36Sopenharmony_ci iput(inode); 9562306a36Sopenharmony_ci return -ENOSPC; 9662306a36Sopenharmony_ci } 9762306a36Sopenharmony_ci set_bit(ino, info->si_imap); 9862306a36Sopenharmony_ci info->si_freei--; 9962306a36Sopenharmony_ci inode_init_owner(&nop_mnt_idmap, inode, dir, mode); 10062306a36Sopenharmony_ci inode->i_mtime = inode->i_atime = inode_set_ctime_current(inode); 10162306a36Sopenharmony_ci inode->i_blocks = 0; 10262306a36Sopenharmony_ci inode->i_op = &bfs_file_inops; 10362306a36Sopenharmony_ci inode->i_fop = &bfs_file_operations; 10462306a36Sopenharmony_ci inode->i_mapping->a_ops = &bfs_aops; 10562306a36Sopenharmony_ci inode->i_ino = ino; 10662306a36Sopenharmony_ci BFS_I(inode)->i_dsk_ino = ino; 10762306a36Sopenharmony_ci BFS_I(inode)->i_sblock = 0; 10862306a36Sopenharmony_ci BFS_I(inode)->i_eblock = 0; 10962306a36Sopenharmony_ci insert_inode_hash(inode); 11062306a36Sopenharmony_ci mark_inode_dirty(inode); 11162306a36Sopenharmony_ci bfs_dump_imap("create", s); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci err = bfs_add_entry(dir, &dentry->d_name, inode->i_ino); 11462306a36Sopenharmony_ci if (err) { 11562306a36Sopenharmony_ci inode_dec_link_count(inode); 11662306a36Sopenharmony_ci mutex_unlock(&info->bfs_lock); 11762306a36Sopenharmony_ci iput(inode); 11862306a36Sopenharmony_ci return err; 11962306a36Sopenharmony_ci } 12062306a36Sopenharmony_ci mutex_unlock(&info->bfs_lock); 12162306a36Sopenharmony_ci d_instantiate(dentry, inode); 12262306a36Sopenharmony_ci return 0; 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistatic struct dentry *bfs_lookup(struct inode *dir, struct dentry *dentry, 12662306a36Sopenharmony_ci unsigned int flags) 12762306a36Sopenharmony_ci{ 12862306a36Sopenharmony_ci struct inode *inode = NULL; 12962306a36Sopenharmony_ci struct buffer_head *bh; 13062306a36Sopenharmony_ci struct bfs_dirent *de; 13162306a36Sopenharmony_ci struct bfs_sb_info *info = BFS_SB(dir->i_sb); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci if (dentry->d_name.len > BFS_NAMELEN) 13462306a36Sopenharmony_ci return ERR_PTR(-ENAMETOOLONG); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci mutex_lock(&info->bfs_lock); 13762306a36Sopenharmony_ci bh = bfs_find_entry(dir, &dentry->d_name, &de); 13862306a36Sopenharmony_ci if (bh) { 13962306a36Sopenharmony_ci unsigned long ino = (unsigned long)le16_to_cpu(de->ino); 14062306a36Sopenharmony_ci brelse(bh); 14162306a36Sopenharmony_ci inode = bfs_iget(dir->i_sb, ino); 14262306a36Sopenharmony_ci } 14362306a36Sopenharmony_ci mutex_unlock(&info->bfs_lock); 14462306a36Sopenharmony_ci return d_splice_alias(inode, dentry); 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cistatic int bfs_link(struct dentry *old, struct inode *dir, 14862306a36Sopenharmony_ci struct dentry *new) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci struct inode *inode = d_inode(old); 15162306a36Sopenharmony_ci struct bfs_sb_info *info = BFS_SB(inode->i_sb); 15262306a36Sopenharmony_ci int err; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci mutex_lock(&info->bfs_lock); 15562306a36Sopenharmony_ci err = bfs_add_entry(dir, &new->d_name, inode->i_ino); 15662306a36Sopenharmony_ci if (err) { 15762306a36Sopenharmony_ci mutex_unlock(&info->bfs_lock); 15862306a36Sopenharmony_ci return err; 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci inc_nlink(inode); 16162306a36Sopenharmony_ci inode_set_ctime_current(inode); 16262306a36Sopenharmony_ci mark_inode_dirty(inode); 16362306a36Sopenharmony_ci ihold(inode); 16462306a36Sopenharmony_ci d_instantiate(new, inode); 16562306a36Sopenharmony_ci mutex_unlock(&info->bfs_lock); 16662306a36Sopenharmony_ci return 0; 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistatic int bfs_unlink(struct inode *dir, struct dentry *dentry) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci int error = -ENOENT; 17262306a36Sopenharmony_ci struct inode *inode = d_inode(dentry); 17362306a36Sopenharmony_ci struct buffer_head *bh; 17462306a36Sopenharmony_ci struct bfs_dirent *de; 17562306a36Sopenharmony_ci struct bfs_sb_info *info = BFS_SB(inode->i_sb); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci mutex_lock(&info->bfs_lock); 17862306a36Sopenharmony_ci bh = bfs_find_entry(dir, &dentry->d_name, &de); 17962306a36Sopenharmony_ci if (!bh || (le16_to_cpu(de->ino) != inode->i_ino)) 18062306a36Sopenharmony_ci goto out_brelse; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci if (!inode->i_nlink) { 18362306a36Sopenharmony_ci printf("unlinking non-existent file %s:%lu (nlink=%d)\n", 18462306a36Sopenharmony_ci inode->i_sb->s_id, inode->i_ino, 18562306a36Sopenharmony_ci inode->i_nlink); 18662306a36Sopenharmony_ci set_nlink(inode, 1); 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci de->ino = 0; 18962306a36Sopenharmony_ci mark_buffer_dirty_inode(bh, dir); 19062306a36Sopenharmony_ci dir->i_mtime = inode_set_ctime_current(dir); 19162306a36Sopenharmony_ci mark_inode_dirty(dir); 19262306a36Sopenharmony_ci inode_set_ctime_to_ts(inode, inode_get_ctime(dir)); 19362306a36Sopenharmony_ci inode_dec_link_count(inode); 19462306a36Sopenharmony_ci error = 0; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ciout_brelse: 19762306a36Sopenharmony_ci brelse(bh); 19862306a36Sopenharmony_ci mutex_unlock(&info->bfs_lock); 19962306a36Sopenharmony_ci return error; 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_cistatic int bfs_rename(struct mnt_idmap *idmap, struct inode *old_dir, 20362306a36Sopenharmony_ci struct dentry *old_dentry, struct inode *new_dir, 20462306a36Sopenharmony_ci struct dentry *new_dentry, unsigned int flags) 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci struct inode *old_inode, *new_inode; 20762306a36Sopenharmony_ci struct buffer_head *old_bh, *new_bh; 20862306a36Sopenharmony_ci struct bfs_dirent *old_de, *new_de; 20962306a36Sopenharmony_ci struct bfs_sb_info *info; 21062306a36Sopenharmony_ci int error = -ENOENT; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci if (flags & ~RENAME_NOREPLACE) 21362306a36Sopenharmony_ci return -EINVAL; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci old_bh = new_bh = NULL; 21662306a36Sopenharmony_ci old_inode = d_inode(old_dentry); 21762306a36Sopenharmony_ci if (S_ISDIR(old_inode->i_mode)) 21862306a36Sopenharmony_ci return -EINVAL; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci info = BFS_SB(old_inode->i_sb); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci mutex_lock(&info->bfs_lock); 22362306a36Sopenharmony_ci old_bh = bfs_find_entry(old_dir, &old_dentry->d_name, &old_de); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci if (!old_bh || (le16_to_cpu(old_de->ino) != old_inode->i_ino)) 22662306a36Sopenharmony_ci goto end_rename; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci error = -EPERM; 22962306a36Sopenharmony_ci new_inode = d_inode(new_dentry); 23062306a36Sopenharmony_ci new_bh = bfs_find_entry(new_dir, &new_dentry->d_name, &new_de); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci if (new_bh && !new_inode) { 23362306a36Sopenharmony_ci brelse(new_bh); 23462306a36Sopenharmony_ci new_bh = NULL; 23562306a36Sopenharmony_ci } 23662306a36Sopenharmony_ci if (!new_bh) { 23762306a36Sopenharmony_ci error = bfs_add_entry(new_dir, &new_dentry->d_name, 23862306a36Sopenharmony_ci old_inode->i_ino); 23962306a36Sopenharmony_ci if (error) 24062306a36Sopenharmony_ci goto end_rename; 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci old_de->ino = 0; 24362306a36Sopenharmony_ci old_dir->i_mtime = inode_set_ctime_current(old_dir); 24462306a36Sopenharmony_ci mark_inode_dirty(old_dir); 24562306a36Sopenharmony_ci if (new_inode) { 24662306a36Sopenharmony_ci inode_set_ctime_current(new_inode); 24762306a36Sopenharmony_ci inode_dec_link_count(new_inode); 24862306a36Sopenharmony_ci } 24962306a36Sopenharmony_ci mark_buffer_dirty_inode(old_bh, old_dir); 25062306a36Sopenharmony_ci error = 0; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ciend_rename: 25362306a36Sopenharmony_ci mutex_unlock(&info->bfs_lock); 25462306a36Sopenharmony_ci brelse(old_bh); 25562306a36Sopenharmony_ci brelse(new_bh); 25662306a36Sopenharmony_ci return error; 25762306a36Sopenharmony_ci} 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ciconst struct inode_operations bfs_dir_inops = { 26062306a36Sopenharmony_ci .create = bfs_create, 26162306a36Sopenharmony_ci .lookup = bfs_lookup, 26262306a36Sopenharmony_ci .link = bfs_link, 26362306a36Sopenharmony_ci .unlink = bfs_unlink, 26462306a36Sopenharmony_ci .rename = bfs_rename, 26562306a36Sopenharmony_ci}; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_cistatic int bfs_add_entry(struct inode *dir, const struct qstr *child, int ino) 26862306a36Sopenharmony_ci{ 26962306a36Sopenharmony_ci const unsigned char *name = child->name; 27062306a36Sopenharmony_ci int namelen = child->len; 27162306a36Sopenharmony_ci struct buffer_head *bh; 27262306a36Sopenharmony_ci struct bfs_dirent *de; 27362306a36Sopenharmony_ci int block, sblock, eblock, off, pos; 27462306a36Sopenharmony_ci int i; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci dprintf("name=%s, namelen=%d\n", name, namelen); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci if (!namelen) 27962306a36Sopenharmony_ci return -ENOENT; 28062306a36Sopenharmony_ci if (namelen > BFS_NAMELEN) 28162306a36Sopenharmony_ci return -ENAMETOOLONG; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci sblock = BFS_I(dir)->i_sblock; 28462306a36Sopenharmony_ci eblock = BFS_I(dir)->i_eblock; 28562306a36Sopenharmony_ci for (block = sblock; block <= eblock; block++) { 28662306a36Sopenharmony_ci bh = sb_bread(dir->i_sb, block); 28762306a36Sopenharmony_ci if (!bh) 28862306a36Sopenharmony_ci return -EIO; 28962306a36Sopenharmony_ci for (off = 0; off < BFS_BSIZE; off += BFS_DIRENT_SIZE) { 29062306a36Sopenharmony_ci de = (struct bfs_dirent *)(bh->b_data + off); 29162306a36Sopenharmony_ci if (!de->ino) { 29262306a36Sopenharmony_ci pos = (block - sblock) * BFS_BSIZE + off; 29362306a36Sopenharmony_ci if (pos >= dir->i_size) { 29462306a36Sopenharmony_ci dir->i_size += BFS_DIRENT_SIZE; 29562306a36Sopenharmony_ci inode_set_ctime_current(dir); 29662306a36Sopenharmony_ci } 29762306a36Sopenharmony_ci dir->i_mtime = inode_set_ctime_current(dir); 29862306a36Sopenharmony_ci mark_inode_dirty(dir); 29962306a36Sopenharmony_ci de->ino = cpu_to_le16((u16)ino); 30062306a36Sopenharmony_ci for (i = 0; i < BFS_NAMELEN; i++) 30162306a36Sopenharmony_ci de->name[i] = 30262306a36Sopenharmony_ci (i < namelen) ? name[i] : 0; 30362306a36Sopenharmony_ci mark_buffer_dirty_inode(bh, dir); 30462306a36Sopenharmony_ci brelse(bh); 30562306a36Sopenharmony_ci return 0; 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci } 30862306a36Sopenharmony_ci brelse(bh); 30962306a36Sopenharmony_ci } 31062306a36Sopenharmony_ci return -ENOSPC; 31162306a36Sopenharmony_ci} 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_cistatic inline int bfs_namecmp(int len, const unsigned char *name, 31462306a36Sopenharmony_ci const char *buffer) 31562306a36Sopenharmony_ci{ 31662306a36Sopenharmony_ci if ((len < BFS_NAMELEN) && buffer[len]) 31762306a36Sopenharmony_ci return 0; 31862306a36Sopenharmony_ci return !memcmp(name, buffer, len); 31962306a36Sopenharmony_ci} 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_cistatic struct buffer_head *bfs_find_entry(struct inode *dir, 32262306a36Sopenharmony_ci const struct qstr *child, 32362306a36Sopenharmony_ci struct bfs_dirent **res_dir) 32462306a36Sopenharmony_ci{ 32562306a36Sopenharmony_ci unsigned long block = 0, offset = 0; 32662306a36Sopenharmony_ci struct buffer_head *bh = NULL; 32762306a36Sopenharmony_ci struct bfs_dirent *de; 32862306a36Sopenharmony_ci const unsigned char *name = child->name; 32962306a36Sopenharmony_ci int namelen = child->len; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci *res_dir = NULL; 33262306a36Sopenharmony_ci if (namelen > BFS_NAMELEN) 33362306a36Sopenharmony_ci return NULL; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci while (block * BFS_BSIZE + offset < dir->i_size) { 33662306a36Sopenharmony_ci if (!bh) { 33762306a36Sopenharmony_ci bh = sb_bread(dir->i_sb, BFS_I(dir)->i_sblock + block); 33862306a36Sopenharmony_ci if (!bh) { 33962306a36Sopenharmony_ci block++; 34062306a36Sopenharmony_ci continue; 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci } 34362306a36Sopenharmony_ci de = (struct bfs_dirent *)(bh->b_data + offset); 34462306a36Sopenharmony_ci offset += BFS_DIRENT_SIZE; 34562306a36Sopenharmony_ci if (le16_to_cpu(de->ino) && 34662306a36Sopenharmony_ci bfs_namecmp(namelen, name, de->name)) { 34762306a36Sopenharmony_ci *res_dir = de; 34862306a36Sopenharmony_ci return bh; 34962306a36Sopenharmony_ci } 35062306a36Sopenharmony_ci if (offset < bh->b_size) 35162306a36Sopenharmony_ci continue; 35262306a36Sopenharmony_ci brelse(bh); 35362306a36Sopenharmony_ci bh = NULL; 35462306a36Sopenharmony_ci offset = 0; 35562306a36Sopenharmony_ci block++; 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci brelse(bh); 35862306a36Sopenharmony_ci return NULL; 35962306a36Sopenharmony_ci} 360