brintos

brintos / linux-shallow public Read only

0
0
Text · 8.5 KiB · c375e22 Raw
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