356 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * fs/bfs/dir.c4 * BFS directory operations.5 * Copyright (C) 1999-2018 Tigran Aivazian <aivazian.tigran@gmail.com>6 * Made endianness-clean by Andrew Stribblehill <ads@wompom.org> 20057 */8 9#include <linux/time.h>10#include <linux/string.h>11#include <linux/fs.h>12#include <linux/buffer_head.h>13#include <linux/sched.h>14#include "bfs.h"15 16#undef DEBUG17 18#ifdef DEBUG19#define dprintf(x...) printf(x)20#else21#define dprintf(x...)22#endif23 24static int bfs_add_entry(struct inode *dir, const struct qstr *child, int ino);25static struct buffer_head *bfs_find_entry(struct inode *dir,26 const struct qstr *child,27 struct bfs_dirent **res_dir);28 29static int bfs_readdir(struct file *f, struct dir_context *ctx)30{31 struct inode *dir = file_inode(f);32 struct buffer_head *bh;33 struct bfs_dirent *de;34 unsigned int offset;35 int block;36 37 if (ctx->pos & (BFS_DIRENT_SIZE - 1)) {38 printf("Bad f_pos=%08lx for %s:%08lx\n",39 (unsigned long)ctx->pos,40 dir->i_sb->s_id, dir->i_ino);41 return -EINVAL;42 }43 44 while (ctx->pos < dir->i_size) {45 offset = ctx->pos & (BFS_BSIZE - 1);46 block = BFS_I(dir)->i_sblock + (ctx->pos >> BFS_BSIZE_BITS);47 bh = sb_bread(dir->i_sb, block);48 if (!bh) {49 ctx->pos += BFS_BSIZE - offset;50 continue;51 }52 do {53 de = (struct bfs_dirent *)(bh->b_data + offset);54 if (de->ino) {55 int size = strnlen(de->name, BFS_NAMELEN);56 if (!dir_emit(ctx, de->name, size,57 le16_to_cpu(de->ino),58 DT_UNKNOWN)) {59 brelse(bh);60 return 0;61 }62 }63 offset += BFS_DIRENT_SIZE;64 ctx->pos += BFS_DIRENT_SIZE;65 } while ((offset < BFS_BSIZE) && (ctx->pos < dir->i_size));66 brelse(bh);67 }68 return 0;69}70 71const struct file_operations bfs_dir_operations = {72 .read = generic_read_dir,73 .iterate_shared = bfs_readdir,74 .fsync = generic_file_fsync,75 .llseek = generic_file_llseek,76};77 78static int bfs_create(struct mnt_idmap *idmap, struct inode *dir,79 struct dentry *dentry, umode_t mode, bool excl)80{81 int err;82 struct inode *inode;83 struct super_block *s = dir->i_sb;84 struct bfs_sb_info *info = BFS_SB(s);85 unsigned long ino;86 87 inode = new_inode(s);88 if (!inode)89 return -ENOMEM;90 mutex_lock(&info->bfs_lock);91 ino = find_first_zero_bit(info->si_imap, info->si_lasti + 1);92 if (ino > info->si_lasti) {93 mutex_unlock(&info->bfs_lock);94 iput(inode);95 return -ENOSPC;96 }97 set_bit(ino, info->si_imap);98 info->si_freei--;99 inode_init_owner(&nop_mnt_idmap, inode, dir, mode);100 simple_inode_init_ts(inode);101 inode->i_blocks = 0;102 inode->i_op = &bfs_file_inops;103 inode->i_fop = &bfs_file_operations;104 inode->i_mapping->a_ops = &bfs_aops;105 inode->i_ino = ino;106 BFS_I(inode)->i_dsk_ino = ino;107 BFS_I(inode)->i_sblock = 0;108 BFS_I(inode)->i_eblock = 0;109 insert_inode_hash(inode);110 mark_inode_dirty(inode);111 bfs_dump_imap("create", s);112 113 err = bfs_add_entry(dir, &dentry->d_name, inode->i_ino);114 if (err) {115 inode_dec_link_count(inode);116 mutex_unlock(&info->bfs_lock);117 iput(inode);118 return err;119 }120 mutex_unlock(&info->bfs_lock);121 d_instantiate(dentry, inode);122 return 0;123}124 125static struct dentry *bfs_lookup(struct inode *dir, struct dentry *dentry,126 unsigned int flags)127{128 struct inode *inode = NULL;129 struct buffer_head *bh;130 struct bfs_dirent *de;131 struct bfs_sb_info *info = BFS_SB(dir->i_sb);132 133 if (dentry->d_name.len > BFS_NAMELEN)134 return ERR_PTR(-ENAMETOOLONG);135 136 mutex_lock(&info->bfs_lock);137 bh = bfs_find_entry(dir, &dentry->d_name, &de);138 if (bh) {139 unsigned long ino = (unsigned long)le16_to_cpu(de->ino);140 brelse(bh);141 inode = bfs_iget(dir->i_sb, ino);142 }143 mutex_unlock(&info->bfs_lock);144 return d_splice_alias(inode, dentry);145}146 147static int bfs_link(struct dentry *old, struct inode *dir,148 struct dentry *new)149{150 struct inode *inode = d_inode(old);151 struct bfs_sb_info *info = BFS_SB(inode->i_sb);152 int err;153 154 mutex_lock(&info->bfs_lock);155 err = bfs_add_entry(dir, &new->d_name, inode->i_ino);156 if (err) {157 mutex_unlock(&info->bfs_lock);158 return err;159 }160 inc_nlink(inode);161 inode_set_ctime_current(inode);162 mark_inode_dirty(inode);163 ihold(inode);164 d_instantiate(new, inode);165 mutex_unlock(&info->bfs_lock);166 return 0;167}168 169static int bfs_unlink(struct inode *dir, struct dentry *dentry)170{171 int error = -ENOENT;172 struct inode *inode = d_inode(dentry);173 struct buffer_head *bh;174 struct bfs_dirent *de;175 struct bfs_sb_info *info = BFS_SB(inode->i_sb);176 177 mutex_lock(&info->bfs_lock);178 bh = bfs_find_entry(dir, &dentry->d_name, &de);179 if (!bh || (le16_to_cpu(de->ino) != inode->i_ino))180 goto out_brelse;181 182 if (!inode->i_nlink) {183 printf("unlinking non-existent file %s:%lu (nlink=%d)\n",184 inode->i_sb->s_id, inode->i_ino,185 inode->i_nlink);186 set_nlink(inode, 1);187 }188 de->ino = 0;189 mark_buffer_dirty_inode(bh, dir);190 inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir));191 mark_inode_dirty(dir);192 inode_set_ctime_to_ts(inode, inode_get_ctime(dir));193 inode_dec_link_count(inode);194 error = 0;195 196out_brelse:197 brelse(bh);198 mutex_unlock(&info->bfs_lock);199 return error;200}201 202static int bfs_rename(struct mnt_idmap *idmap, struct inode *old_dir,203 struct dentry *old_dentry, struct inode *new_dir,204 struct dentry *new_dentry, unsigned int flags)205{206 struct inode *old_inode, *new_inode;207 struct buffer_head *old_bh, *new_bh;208 struct bfs_dirent *old_de, *new_de;209 struct bfs_sb_info *info;210 int error = -ENOENT;211 212 if (flags & ~RENAME_NOREPLACE)213 return -EINVAL;214 215 old_bh = new_bh = NULL;216 old_inode = d_inode(old_dentry);217 if (S_ISDIR(old_inode->i_mode))218 return -EINVAL;219 220 info = BFS_SB(old_inode->i_sb);221 222 mutex_lock(&info->bfs_lock);223 old_bh = bfs_find_entry(old_dir, &old_dentry->d_name, &old_de);224 225 if (!old_bh || (le16_to_cpu(old_de->ino) != old_inode->i_ino))226 goto end_rename;227 228 error = -EPERM;229 new_inode = d_inode(new_dentry);230 new_bh = bfs_find_entry(new_dir, &new_dentry->d_name, &new_de);231 232 if (new_bh && !new_inode) {233 brelse(new_bh);234 new_bh = NULL;235 }236 if (!new_bh) {237 error = bfs_add_entry(new_dir, &new_dentry->d_name,238 old_inode->i_ino);239 if (error)240 goto end_rename;241 }242 old_de->ino = 0;243 inode_set_mtime_to_ts(old_dir, inode_set_ctime_current(old_dir));244 mark_inode_dirty(old_dir);245 if (new_inode) {246 inode_set_ctime_current(new_inode);247 inode_dec_link_count(new_inode);248 }249 mark_buffer_dirty_inode(old_bh, old_dir);250 error = 0;251 252end_rename:253 mutex_unlock(&info->bfs_lock);254 brelse(old_bh);255 brelse(new_bh);256 return error;257}258 259const struct inode_operations bfs_dir_inops = {260 .create = bfs_create,261 .lookup = bfs_lookup,262 .link = bfs_link,263 .unlink = bfs_unlink,264 .rename = bfs_rename,265};266 267static int bfs_add_entry(struct inode *dir, const struct qstr *child, int ino)268{269 const unsigned char *name = child->name;270 int namelen = child->len;271 struct buffer_head *bh;272 struct bfs_dirent *de;273 int block, sblock, eblock, off, pos;274 int i;275 276 dprintf("name=%s, namelen=%d\n", name, namelen);277 278 sblock = BFS_I(dir)->i_sblock;279 eblock = BFS_I(dir)->i_eblock;280 for (block = sblock; block <= eblock; block++) {281 bh = sb_bread(dir->i_sb, block);282 if (!bh)283 return -EIO;284 for (off = 0; off < BFS_BSIZE; off += BFS_DIRENT_SIZE) {285 de = (struct bfs_dirent *)(bh->b_data + off);286 if (!de->ino) {287 pos = (block - sblock) * BFS_BSIZE + off;288 if (pos >= dir->i_size) {289 dir->i_size += BFS_DIRENT_SIZE;290 inode_set_ctime_current(dir);291 }292 inode_set_mtime_to_ts(dir,293 inode_set_ctime_current(dir));294 mark_inode_dirty(dir);295 de->ino = cpu_to_le16((u16)ino);296 for (i = 0; i < BFS_NAMELEN; i++)297 de->name[i] =298 (i < namelen) ? name[i] : 0;299 mark_buffer_dirty_inode(bh, dir);300 brelse(bh);301 return 0;302 }303 }304 brelse(bh);305 }306 return -ENOSPC;307}308 309static inline int bfs_namecmp(int len, const unsigned char *name,310 const char *buffer)311{312 if ((len < BFS_NAMELEN) && buffer[len])313 return 0;314 return !memcmp(name, buffer, len);315}316 317static struct buffer_head *bfs_find_entry(struct inode *dir,318 const struct qstr *child,319 struct bfs_dirent **res_dir)320{321 unsigned long block = 0, offset = 0;322 struct buffer_head *bh = NULL;323 struct bfs_dirent *de;324 const unsigned char *name = child->name;325 int namelen = child->len;326 327 *res_dir = NULL;328 if (namelen > BFS_NAMELEN)329 return NULL;330 331 while (block * BFS_BSIZE + offset < dir->i_size) {332 if (!bh) {333 bh = sb_bread(dir->i_sb, BFS_I(dir)->i_sblock + block);334 if (!bh) {335 block++;336 continue;337 }338 }339 de = (struct bfs_dirent *)(bh->b_data + offset);340 offset += BFS_DIRENT_SIZE;341 if (le16_to_cpu(de->ino) &&342 bfs_namecmp(namelen, name, de->name)) {343 *res_dir = de;344 return bh;345 }346 if (offset < bh->b_size)347 continue;348 brelse(bh);349 bh = NULL;350 offset = 0;351 block++;352 }353 brelse(bh);354 return NULL;355}356