Files
laptops-kernel/fs/bfs/dir.c
T

355 lines
8.5 KiB
C
Raw Normal View History

// SPDX-License-Identifier: GPL-2.0
2005-04-16 15:20:36 -07:00
/*
* fs/bfs/dir.c
* BFS directory operations.
* Copyright (C) 1999-2018 Tigran Aivazian <aivazian.tigran@gmail.com>
* Made endianness-clean by Andrew Stribblehill <ads@wompom.org> 2005
2005-04-16 15:20:36 -07:00
*/
#include <linux/time.h>
#include <linux/string.h>
#include <linux/fs.h>
#include <linux/buffer_head.h>
#include <linux/sched.h>
#include "bfs.h"
#undef DEBUG
#ifdef DEBUG
#define dprintf(x...) printf(x)
#else
#define dprintf(x...)
#endif
static int bfs_add_entry(struct inode *dir, const struct qstr *child, int ino);
2007-11-14 16:59:47 -08:00
static struct buffer_head *bfs_find_entry(struct inode *dir,
const struct qstr *child,
2007-11-14 16:59:47 -08:00
struct bfs_dirent **res_dir);
2005-04-16 15:20:36 -07:00
2013-05-16 13:41:48 -04:00
static int bfs_readdir(struct file *f, struct dir_context *ctx)
2005-04-16 15:20:36 -07:00
{
2013-01-23 17:07:38 -05:00
struct inode *dir = file_inode(f);
2007-11-14 16:59:47 -08:00
struct buffer_head *bh;
struct bfs_dirent *de;
2005-04-16 15:20:36 -07:00
unsigned int offset;
int block;
2013-05-16 13:41:48 -04:00
if (ctx->pos & (BFS_DIRENT_SIZE - 1)) {
2007-11-14 16:59:47 -08:00
printf("Bad f_pos=%08lx for %s:%08lx\n",
2013-05-16 13:41:48 -04:00
(unsigned long)ctx->pos,
2007-11-14 16:59:47 -08:00
dir->i_sb->s_id, dir->i_ino);
2013-05-16 13:41:48 -04:00
return -EINVAL;
2005-04-16 15:20:36 -07:00
}
2013-05-16 13:41:48 -04:00
while (ctx->pos < dir->i_size) {
offset = ctx->pos & (BFS_BSIZE - 1);
block = BFS_I(dir)->i_sblock + (ctx->pos >> BFS_BSIZE_BITS);
2005-04-16 15:20:36 -07:00
bh = sb_bread(dir->i_sb, block);
if (!bh) {
2013-05-16 13:41:48 -04:00
ctx->pos += BFS_BSIZE - offset;
2005-04-16 15:20:36 -07:00
continue;
}
do {
de = (struct bfs_dirent *)(bh->b_data + offset);
if (de->ino) {
int size = strnlen(de->name, BFS_NAMELEN);
2013-05-16 13:41:48 -04:00
if (!dir_emit(ctx, de->name, size,
2007-11-14 16:59:47 -08:00
le16_to_cpu(de->ino),
2013-05-16 13:41:48 -04:00
DT_UNKNOWN)) {
2005-04-16 15:20:36 -07:00
brelse(bh);
return 0;
}
}
offset += BFS_DIRENT_SIZE;
2013-05-16 13:41:48 -04:00
ctx->pos += BFS_DIRENT_SIZE;
} while ((offset < BFS_BSIZE) && (ctx->pos < dir->i_size));
2005-04-16 15:20:36 -07:00
brelse(bh);
}
2013-05-16 13:41:48 -04:00
return 0;
2005-04-16 15:20:36 -07:00
}
const struct file_operations bfs_dir_operations = {
2005-04-16 15:20:36 -07:00
.read = generic_read_dir,
2016-04-30 22:37:34 -04:00
.iterate_shared = bfs_readdir,
2010-05-26 17:53:41 +02:00
.fsync = generic_file_fsync,
2008-09-03 21:53:01 +02:00
.llseek = generic_file_llseek,
2005-04-16 15:20:36 -07:00
};
2023-01-13 12:49:13 +01:00
static int bfs_create(struct mnt_idmap *idmap, struct inode *dir,
2021-01-21 14:19:43 +01:00
struct dentry *dentry, umode_t mode, bool excl)
2005-04-16 15:20:36 -07:00
{
int err;
2007-11-14 16:59:47 -08:00
struct inode *inode;
struct super_block *s = dir->i_sb;
struct bfs_sb_info *info = BFS_SB(s);
2005-04-16 15:20:36 -07:00
unsigned long ino;
inode = new_inode(s);
if (!inode)
2015-04-16 12:48:32 -07:00
return -ENOMEM;
2008-07-25 19:44:54 -07:00
mutex_lock(&info->bfs_lock);
ino = find_first_zero_bit(info->si_imap, info->si_lasti + 1);
2005-04-16 15:20:36 -07:00
if (ino > info->si_lasti) {
2008-07-25 19:44:54 -07:00
mutex_unlock(&info->bfs_lock);
2005-04-16 15:20:36 -07:00
iput(inode);
return -ENOSPC;
}
2007-11-14 16:59:47 -08:00
set_bit(ino, info->si_imap);
2005-04-16 15:20:36 -07:00
info->si_freei--;
2023-01-13 12:49:25 +01:00
inode_init_owner(&nop_mnt_idmap, inode, dir, mode);
2023-10-04 14:52:07 -04:00
simple_inode_init_ts(inode);
inode->i_blocks = 0;
2005-04-16 15:20:36 -07:00
inode->i_op = &bfs_file_inops;
inode->i_fop = &bfs_file_operations;
inode->i_mapping->a_ops = &bfs_aops;
inode->i_ino = ino;
2005-10-04 17:43:06 +01:00
BFS_I(inode)->i_dsk_ino = ino;
2005-04-16 15:20:36 -07:00
BFS_I(inode)->i_sblock = 0;
BFS_I(inode)->i_eblock = 0;
insert_inode_hash(inode);
mark_inode_dirty(inode);
2014-08-08 14:22:29 -07:00
bfs_dump_imap("create", s);
2005-04-16 15:20:36 -07:00
err = bfs_add_entry(dir, &dentry->d_name, inode->i_ino);
2005-04-16 15:20:36 -07:00
if (err) {
inode_dec_link_count(inode);
2008-07-25 19:44:54 -07:00
mutex_unlock(&info->bfs_lock);
2008-09-13 02:33:12 -07:00
iput(inode);
2005-04-16 15:20:36 -07:00
return err;
}
2008-07-25 19:44:54 -07:00
mutex_unlock(&info->bfs_lock);
2005-04-16 15:20:36 -07:00
d_instantiate(dentry, inode);
return 0;
}
2007-11-14 16:59:47 -08:00
static struct dentry *bfs_lookup(struct inode *dir, struct dentry *dentry,
2012-06-10 17:13:09 -04:00
unsigned int flags)
2005-04-16 15:20:36 -07:00
{
2007-11-14 16:59:47 -08:00
struct inode *inode = NULL;
struct buffer_head *bh;
struct bfs_dirent *de;
2008-07-25 19:44:54 -07:00
struct bfs_sb_info *info = BFS_SB(dir->i_sb);
2005-04-16 15:20:36 -07:00
if (dentry->d_name.len > BFS_NAMELEN)
return ERR_PTR(-ENAMETOOLONG);
2008-07-25 19:44:54 -07:00
mutex_lock(&info->bfs_lock);
bh = bfs_find_entry(dir, &dentry->d_name, &de);
2005-04-16 15:20:36 -07:00
if (bh) {
unsigned long ino = (unsigned long)le16_to_cpu(de->ino);
2005-04-16 15:20:36 -07:00
brelse(bh);
inode = bfs_iget(dir->i_sb, ino);
2005-04-16 15:20:36 -07:00
}
2008-07-25 19:44:54 -07:00
mutex_unlock(&info->bfs_lock);
2018-04-30 19:05:17 -04:00
return d_splice_alias(inode, dentry);
2005-04-16 15:20:36 -07:00
}
2007-11-14 16:59:47 -08:00
static int bfs_link(struct dentry *old, struct inode *dir,
struct dentry *new)
2005-04-16 15:20:36 -07:00
{
struct inode *inode = d_inode(old);
2008-07-25 19:44:54 -07:00
struct bfs_sb_info *info = BFS_SB(inode->i_sb);
2005-04-16 15:20:36 -07:00
int err;
2008-07-25 19:44:54 -07:00
mutex_lock(&info->bfs_lock);
err = bfs_add_entry(dir, &new->d_name, inode->i_ino);
2005-04-16 15:20:36 -07:00
if (err) {
2008-07-25 19:44:54 -07:00
mutex_unlock(&info->bfs_lock);
2005-04-16 15:20:36 -07:00
return err;
}
inc_nlink(inode);
2023-07-05 15:00:53 -04:00
inode_set_ctime_current(inode);
2005-04-16 15:20:36 -07:00
mark_inode_dirty(inode);
2010-10-23 11:11:40 -04:00
ihold(inode);
2005-04-16 15:20:36 -07:00
d_instantiate(new, inode);
2008-07-25 19:44:54 -07:00
mutex_unlock(&info->bfs_lock);
2005-04-16 15:20:36 -07:00
return 0;
}
2007-11-14 16:59:47 -08:00
static int bfs_unlink(struct inode *dir, struct dentry *dentry)
2005-04-16 15:20:36 -07:00
{
int error = -ENOENT;
struct inode *inode = d_inode(dentry);
2007-11-14 16:59:47 -08:00
struct buffer_head *bh;
struct bfs_dirent *de;
2008-07-25 19:44:54 -07:00
struct bfs_sb_info *info = BFS_SB(inode->i_sb);
2005-04-16 15:20:36 -07:00
2008-07-25 19:44:54 -07:00
mutex_lock(&info->bfs_lock);
bh = bfs_find_entry(dir, &dentry->d_name, &de);
2007-11-14 16:59:47 -08:00
if (!bh || (le16_to_cpu(de->ino) != inode->i_ino))
2005-04-16 15:20:36 -07:00
goto out_brelse;
if (!inode->i_nlink) {
2007-11-14 16:59:47 -08:00
printf("unlinking non-existent file %s:%lu (nlink=%d)\n",
inode->i_sb->s_id, inode->i_ino,
inode->i_nlink);
2011-10-28 14:13:29 +02:00
set_nlink(inode, 1);
2005-04-16 15:20:36 -07:00
}
de->ino = 0;
mark_buffer_dirty_inode(bh, dir);
2023-10-04 14:52:07 -04:00
inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir));
2005-04-16 15:20:36 -07:00
mark_inode_dirty(dir);
2023-07-05 15:00:53 -04:00
inode_set_ctime_to_ts(inode, inode_get_ctime(dir));
inode_dec_link_count(inode);
2005-04-16 15:20:36 -07:00
error = 0;
out_brelse:
brelse(bh);
2008-07-25 19:44:54 -07:00
mutex_unlock(&info->bfs_lock);
2005-04-16 15:20:36 -07:00
return error;
}
2023-01-13 12:49:17 +01:00
static int bfs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
2021-01-21 14:19:43 +01:00
struct dentry *old_dentry, struct inode *new_dir,
struct dentry *new_dentry, unsigned int flags)
2005-04-16 15:20:36 -07:00
{
2007-11-14 16:59:47 -08:00
struct inode *old_inode, *new_inode;
struct buffer_head *old_bh, *new_bh;
struct bfs_dirent *old_de, *new_de;
2008-07-25 19:44:54 -07:00
struct bfs_sb_info *info;
2005-04-16 15:20:36 -07:00
int error = -ENOENT;
if (flags & ~RENAME_NOREPLACE)
return -EINVAL;
2005-04-16 15:20:36 -07:00
old_bh = new_bh = NULL;
old_inode = d_inode(old_dentry);
2005-04-16 15:20:36 -07:00
if (S_ISDIR(old_inode->i_mode))
return -EINVAL;
2008-07-25 19:44:54 -07:00
info = BFS_SB(old_inode->i_sb);
mutex_lock(&info->bfs_lock);
old_bh = bfs_find_entry(old_dir, &old_dentry->d_name, &old_de);
2005-04-16 15:20:36 -07:00
2007-11-14 16:59:47 -08:00
if (!old_bh || (le16_to_cpu(old_de->ino) != old_inode->i_ino))
2005-04-16 15:20:36 -07:00
goto end_rename;
error = -EPERM;
new_inode = d_inode(new_dentry);
new_bh = bfs_find_entry(new_dir, &new_dentry->d_name, &new_de);
2005-04-16 15:20:36 -07:00
if (new_bh && !new_inode) {
brelse(new_bh);
new_bh = NULL;
}
if (!new_bh) {
error = bfs_add_entry(new_dir, &new_dentry->d_name,
2007-11-14 16:59:47 -08:00
old_inode->i_ino);
2005-04-16 15:20:36 -07:00
if (error)
goto end_rename;
}
old_de->ino = 0;
2023-10-04 14:52:07 -04:00
inode_set_mtime_to_ts(old_dir, inode_set_ctime_current(old_dir));
2005-04-16 15:20:36 -07:00
mark_inode_dirty(old_dir);
if (new_inode) {
2023-07-05 15:00:53 -04:00
inode_set_ctime_current(new_inode);
inode_dec_link_count(new_inode);
2005-04-16 15:20:36 -07:00
}
mark_buffer_dirty_inode(old_bh, old_dir);
2005-04-16 15:20:36 -07:00
error = 0;
end_rename:
2008-07-25 19:44:54 -07:00
mutex_unlock(&info->bfs_lock);
2005-04-16 15:20:36 -07:00
brelse(old_bh);
brelse(new_bh);
return error;
}
const struct inode_operations bfs_dir_inops = {
2005-04-16 15:20:36 -07:00
.create = bfs_create,
.lookup = bfs_lookup,
.link = bfs_link,
.unlink = bfs_unlink,
.rename = bfs_rename,
};
static int bfs_add_entry(struct inode *dir, const struct qstr *child, int ino)
2005-04-16 15:20:36 -07:00
{
const unsigned char *name = child->name;
int namelen = child->len;
2007-11-14 16:59:47 -08:00
struct buffer_head *bh;
struct bfs_dirent *de;
int block, sblock, eblock, off, pos;
2005-04-16 15:20:36 -07:00
int i;
dprintf("name=%s, namelen=%d\n", name, namelen);
sblock = BFS_I(dir)->i_sblock;
eblock = BFS_I(dir)->i_eblock;
2007-11-14 16:59:47 -08:00
for (block = sblock; block <= eblock; block++) {
2005-04-16 15:20:36 -07:00
bh = sb_bread(dir->i_sb, block);
2007-11-14 16:59:47 -08:00
if (!bh)
2015-04-16 12:48:32 -07:00
return -EIO;
2007-11-14 16:59:47 -08:00
for (off = 0; off < BFS_BSIZE; off += BFS_DIRENT_SIZE) {
2005-04-16 15:20:36 -07:00
de = (struct bfs_dirent *)(bh->b_data + off);
if (!de->ino) {
2007-11-14 16:59:47 -08:00
pos = (block - sblock) * BFS_BSIZE + off;
if (pos >= dir->i_size) {
2005-04-16 15:20:36 -07:00
dir->i_size += BFS_DIRENT_SIZE;
2023-07-05 15:00:53 -04:00
inode_set_ctime_current(dir);
2005-04-16 15:20:36 -07:00
}
2023-10-04 14:52:07 -04:00
inode_set_mtime_to_ts(dir,
inode_set_ctime_current(dir));
2005-04-16 15:20:36 -07:00
mark_inode_dirty(dir);
de->ino = cpu_to_le16((u16)ino);
2007-11-14 16:59:47 -08:00
for (i = 0; i < BFS_NAMELEN; i++)
de->name[i] =
(i < namelen) ? name[i] : 0;
mark_buffer_dirty_inode(bh, dir);
2005-04-16 15:20:36 -07:00
brelse(bh);
return 0;
}
}
brelse(bh);
}
return -ENOSPC;
}
2007-11-14 16:59:47 -08:00
static inline int bfs_namecmp(int len, const unsigned char *name,
const char *buffer)
2005-04-16 15:20:36 -07:00
{
2007-11-14 16:59:47 -08:00
if ((len < BFS_NAMELEN) && buffer[len])
2005-04-16 15:20:36 -07:00
return 0;
return !memcmp(name, buffer, len);
}
2007-11-14 16:59:47 -08:00
static struct buffer_head *bfs_find_entry(struct inode *dir,
const struct qstr *child,
2007-11-14 16:59:47 -08:00
struct bfs_dirent **res_dir)
2005-04-16 15:20:36 -07:00
{
2007-11-14 16:59:47 -08:00
unsigned long block = 0, offset = 0;
struct buffer_head *bh = NULL;
struct bfs_dirent *de;
const unsigned char *name = child->name;
int namelen = child->len;
2005-04-16 15:20:36 -07:00
*res_dir = NULL;
if (namelen > BFS_NAMELEN)
return NULL;
2007-11-14 16:59:47 -08:00
2005-04-16 15:20:36 -07:00
while (block * BFS_BSIZE + offset < dir->i_size) {
if (!bh) {
bh = sb_bread(dir->i_sb, BFS_I(dir)->i_sblock + block);
if (!bh) {
block++;
continue;
}
}
de = (struct bfs_dirent *)(bh->b_data + offset);
offset += BFS_DIRENT_SIZE;
2007-11-14 16:59:47 -08:00
if (le16_to_cpu(de->ino) &&
bfs_namecmp(namelen, name, de->name)) {
2005-04-16 15:20:36 -07:00
*res_dir = de;
return bh;
}
if (offset < bh->b_size)
continue;
brelse(bh);
bh = NULL;
offset = 0;
block++;
}
brelse(bh);
return NULL;
}