cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

dir.c (8782B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 *	fs/bfs/dir.c
      4 *	BFS directory operations.
      5 *	Copyright (C) 1999-2018  Tigran Aivazian <aivazian.tigran@gmail.com>
      6 *  Made endianness-clean by Andrew Stribblehill <ads@wompom.org> 2005
      7 */
      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 DEBUG
     17
     18#ifdef DEBUG
     19#define dprintf(x...)	printf(x)
     20#else
     21#define dprintf(x...)
     22#endif
     23
     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 user_namespace *mnt_userns, 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(&init_user_ns, inode, dir, mode);
    100	inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(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->i_ctime = current_time(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	dir->i_ctime = dir->i_mtime = current_time(dir);
    191	mark_inode_dirty(dir);
    192	inode->i_ctime = dir->i_ctime;
    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 user_namespace *mnt_userns, 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	old_dir->i_ctime = old_dir->i_mtime = current_time(old_dir);
    244	mark_inode_dirty(old_dir);
    245	if (new_inode) {
    246		new_inode->i_ctime = current_time(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	if (!namelen)
    279		return -ENOENT;
    280	if (namelen > BFS_NAMELEN)
    281		return -ENAMETOOLONG;
    282
    283	sblock = BFS_I(dir)->i_sblock;
    284	eblock = BFS_I(dir)->i_eblock;
    285	for (block = sblock; block <= eblock; block++) {
    286		bh = sb_bread(dir->i_sb, block);
    287		if (!bh)
    288			return -EIO;
    289		for (off = 0; off < BFS_BSIZE; off += BFS_DIRENT_SIZE) {
    290			de = (struct bfs_dirent *)(bh->b_data + off);
    291			if (!de->ino) {
    292				pos = (block - sblock) * BFS_BSIZE + off;
    293				if (pos >= dir->i_size) {
    294					dir->i_size += BFS_DIRENT_SIZE;
    295					dir->i_ctime = current_time(dir);
    296				}
    297				dir->i_mtime = current_time(dir);
    298				mark_inode_dirty(dir);
    299				de->ino = cpu_to_le16((u16)ino);
    300				for (i = 0; i < BFS_NAMELEN; i++)
    301					de->name[i] =
    302						(i < namelen) ? name[i] : 0;
    303				mark_buffer_dirty_inode(bh, dir);
    304				brelse(bh);
    305				return 0;
    306			}
    307		}
    308		brelse(bh);
    309	}
    310	return -ENOSPC;
    311}
    312
    313static inline int bfs_namecmp(int len, const unsigned char *name,
    314							const char *buffer)
    315{
    316	if ((len < BFS_NAMELEN) && buffer[len])
    317		return 0;
    318	return !memcmp(name, buffer, len);
    319}
    320
    321static struct buffer_head *bfs_find_entry(struct inode *dir,
    322			const struct qstr *child,
    323			struct bfs_dirent **res_dir)
    324{
    325	unsigned long block = 0, offset = 0;
    326	struct buffer_head *bh = NULL;
    327	struct bfs_dirent *de;
    328	const unsigned char *name = child->name;
    329	int namelen = child->len;
    330
    331	*res_dir = NULL;
    332	if (namelen > BFS_NAMELEN)
    333		return NULL;
    334
    335	while (block * BFS_BSIZE + offset < dir->i_size) {
    336		if (!bh) {
    337			bh = sb_bread(dir->i_sb, BFS_I(dir)->i_sblock + block);
    338			if (!bh) {
    339				block++;
    340				continue;
    341			}
    342		}
    343		de = (struct bfs_dirent *)(bh->b_data + offset);
    344		offset += BFS_DIRENT_SIZE;
    345		if (le16_to_cpu(de->ino) &&
    346				bfs_namecmp(namelen, name, de->name)) {
    347			*res_dir = de;
    348			return bh;
    349		}
    350		if (offset < bh->b_size)
    351			continue;
    352		brelse(bh);
    353		bh = NULL;
    354		offset = 0;
    355		block++;
    356	}
    357	brelse(bh);
    358	return NULL;
    359}