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 (10107B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * OMFS (as used by RIO Karma) directory operations.
      4 * Copyright (C) 2005 Bob Copeland <me@bobcopeland.com>
      5 */
      6
      7#include <linux/fs.h>
      8#include <linux/ctype.h>
      9#include <linux/buffer_head.h>
     10#include "omfs.h"
     11
     12static int omfs_hash(const char *name, int namelen, int mod)
     13{
     14	int i, hash = 0;
     15	for (i = 0; i < namelen; i++)
     16		hash ^= tolower(name[i]) << (i % 24);
     17	return hash % mod;
     18}
     19
     20/*
     21 * Finds the bucket for a given name and reads the containing block;
     22 * *ofs is set to the offset of the first list entry.
     23 */
     24static struct buffer_head *omfs_get_bucket(struct inode *dir,
     25		const char *name, int namelen, int *ofs)
     26{
     27	int nbuckets = (dir->i_size - OMFS_DIR_START)/8;
     28	int bucket = omfs_hash(name, namelen, nbuckets);
     29
     30	*ofs = OMFS_DIR_START + bucket * 8;
     31	return omfs_bread(dir->i_sb, dir->i_ino);
     32}
     33
     34static struct buffer_head *omfs_scan_list(struct inode *dir, u64 block,
     35				const char *name, int namelen,
     36				u64 *prev_block)
     37{
     38	struct buffer_head *bh;
     39	struct omfs_inode *oi;
     40	int err = -ENOENT;
     41	*prev_block = ~0;
     42
     43	while (block != ~0) {
     44		bh = omfs_bread(dir->i_sb, block);
     45		if (!bh) {
     46			err = -EIO;
     47			goto err;
     48		}
     49
     50		oi = (struct omfs_inode *) bh->b_data;
     51		if (omfs_is_bad(OMFS_SB(dir->i_sb), &oi->i_head, block)) {
     52			brelse(bh);
     53			goto err;
     54		}
     55
     56		if (strncmp(oi->i_name, name, namelen) == 0)
     57			return bh;
     58
     59		*prev_block = block;
     60		block = be64_to_cpu(oi->i_sibling);
     61		brelse(bh);
     62	}
     63err:
     64	return ERR_PTR(err);
     65}
     66
     67static struct buffer_head *omfs_find_entry(struct inode *dir,
     68					   const char *name, int namelen)
     69{
     70	struct buffer_head *bh;
     71	int ofs;
     72	u64 block, dummy;
     73
     74	bh = omfs_get_bucket(dir, name, namelen, &ofs);
     75	if (!bh)
     76		return ERR_PTR(-EIO);
     77
     78	block = be64_to_cpu(*((__be64 *) &bh->b_data[ofs]));
     79	brelse(bh);
     80
     81	return omfs_scan_list(dir, block, name, namelen, &dummy);
     82}
     83
     84int omfs_make_empty(struct inode *inode, struct super_block *sb)
     85{
     86	struct omfs_sb_info *sbi = OMFS_SB(sb);
     87	struct buffer_head *bh;
     88	struct omfs_inode *oi;
     89
     90	bh = omfs_bread(sb, inode->i_ino);
     91	if (!bh)
     92		return -ENOMEM;
     93
     94	memset(bh->b_data, 0, sizeof(struct omfs_inode));
     95
     96	if (S_ISDIR(inode->i_mode)) {
     97		memset(&bh->b_data[OMFS_DIR_START], 0xff,
     98			sbi->s_sys_blocksize - OMFS_DIR_START);
     99	} else
    100		omfs_make_empty_table(bh, OMFS_EXTENT_START);
    101
    102	oi = (struct omfs_inode *) bh->b_data;
    103	oi->i_head.h_self = cpu_to_be64(inode->i_ino);
    104	oi->i_sibling = ~cpu_to_be64(0ULL);
    105
    106	mark_buffer_dirty(bh);
    107	brelse(bh);
    108	return 0;
    109}
    110
    111static int omfs_add_link(struct dentry *dentry, struct inode *inode)
    112{
    113	struct inode *dir = d_inode(dentry->d_parent);
    114	const char *name = dentry->d_name.name;
    115	int namelen = dentry->d_name.len;
    116	struct omfs_inode *oi;
    117	struct buffer_head *bh;
    118	u64 block;
    119	__be64 *entry;
    120	int ofs;
    121
    122	/* just prepend to head of queue in proper bucket */
    123	bh = omfs_get_bucket(dir, name, namelen, &ofs);
    124	if (!bh)
    125		goto out;
    126
    127	entry = (__be64 *) &bh->b_data[ofs];
    128	block = be64_to_cpu(*entry);
    129	*entry = cpu_to_be64(inode->i_ino);
    130	mark_buffer_dirty(bh);
    131	brelse(bh);
    132
    133	/* now set the sibling and parent pointers on the new inode */
    134	bh = omfs_bread(dir->i_sb, inode->i_ino);
    135	if (!bh)
    136		goto out;
    137
    138	oi = (struct omfs_inode *) bh->b_data;
    139	memcpy(oi->i_name, name, namelen);
    140	memset(oi->i_name + namelen, 0, OMFS_NAMELEN - namelen);
    141	oi->i_sibling = cpu_to_be64(block);
    142	oi->i_parent = cpu_to_be64(dir->i_ino);
    143	mark_buffer_dirty(bh);
    144	brelse(bh);
    145
    146	dir->i_ctime = current_time(dir);
    147
    148	/* mark affected inodes dirty to rebuild checksums */
    149	mark_inode_dirty(dir);
    150	mark_inode_dirty(inode);
    151	return 0;
    152out:
    153	return -ENOMEM;
    154}
    155
    156static int omfs_delete_entry(struct dentry *dentry)
    157{
    158	struct inode *dir = d_inode(dentry->d_parent);
    159	struct inode *dirty;
    160	const char *name = dentry->d_name.name;
    161	int namelen = dentry->d_name.len;
    162	struct omfs_inode *oi;
    163	struct buffer_head *bh, *bh2;
    164	__be64 *entry, next;
    165	u64 block, prev;
    166	int ofs;
    167	int err = -ENOMEM;
    168
    169	/* delete the proper node in the bucket's linked list */
    170	bh = omfs_get_bucket(dir, name, namelen, &ofs);
    171	if (!bh)
    172		goto out;
    173
    174	entry = (__be64 *) &bh->b_data[ofs];
    175	block = be64_to_cpu(*entry);
    176
    177	bh2 = omfs_scan_list(dir, block, name, namelen, &prev);
    178	if (IS_ERR(bh2)) {
    179		err = PTR_ERR(bh2);
    180		goto out_free_bh;
    181	}
    182
    183	oi = (struct omfs_inode *) bh2->b_data;
    184	next = oi->i_sibling;
    185	brelse(bh2);
    186
    187	if (prev != ~0) {
    188		/* found in middle of list, get list ptr */
    189		brelse(bh);
    190		bh = omfs_bread(dir->i_sb, prev);
    191		if (!bh)
    192			goto out;
    193
    194		oi = (struct omfs_inode *) bh->b_data;
    195		entry = &oi->i_sibling;
    196	}
    197
    198	*entry = next;
    199	mark_buffer_dirty(bh);
    200
    201	if (prev != ~0) {
    202		dirty = omfs_iget(dir->i_sb, prev);
    203		if (!IS_ERR(dirty)) {
    204			mark_inode_dirty(dirty);
    205			iput(dirty);
    206		}
    207	}
    208
    209	err = 0;
    210out_free_bh:
    211	brelse(bh);
    212out:
    213	return err;
    214}
    215
    216static int omfs_dir_is_empty(struct inode *inode)
    217{
    218	int nbuckets = (inode->i_size - OMFS_DIR_START) / 8;
    219	struct buffer_head *bh;
    220	u64 *ptr;
    221	int i;
    222
    223	bh = omfs_bread(inode->i_sb, inode->i_ino);
    224
    225	if (!bh)
    226		return 0;
    227
    228	ptr = (u64 *) &bh->b_data[OMFS_DIR_START];
    229
    230	for (i = 0; i < nbuckets; i++, ptr++)
    231		if (*ptr != ~0)
    232			break;
    233
    234	brelse(bh);
    235	return *ptr != ~0;
    236}
    237
    238static int omfs_remove(struct inode *dir, struct dentry *dentry)
    239{
    240	struct inode *inode = d_inode(dentry);
    241	int ret;
    242
    243
    244	if (S_ISDIR(inode->i_mode) &&
    245	    !omfs_dir_is_empty(inode))
    246		return -ENOTEMPTY;
    247
    248	ret = omfs_delete_entry(dentry);
    249	if (ret)
    250		return ret;
    251	
    252	clear_nlink(inode);
    253	mark_inode_dirty(inode);
    254	mark_inode_dirty(dir);
    255	return 0;
    256}
    257
    258static int omfs_add_node(struct inode *dir, struct dentry *dentry, umode_t mode)
    259{
    260	int err;
    261	struct inode *inode = omfs_new_inode(dir, mode);
    262
    263	if (IS_ERR(inode))
    264		return PTR_ERR(inode);
    265
    266	err = omfs_make_empty(inode, dir->i_sb);
    267	if (err)
    268		goto out_free_inode;
    269
    270	err = omfs_add_link(dentry, inode);
    271	if (err)
    272		goto out_free_inode;
    273
    274	d_instantiate(dentry, inode);
    275	return 0;
    276
    277out_free_inode:
    278	iput(inode);
    279	return err;
    280}
    281
    282static int omfs_mkdir(struct user_namespace *mnt_userns, struct inode *dir,
    283		      struct dentry *dentry, umode_t mode)
    284{
    285	return omfs_add_node(dir, dentry, mode | S_IFDIR);
    286}
    287
    288static int omfs_create(struct user_namespace *mnt_userns, struct inode *dir,
    289		       struct dentry *dentry, umode_t mode, bool excl)
    290{
    291	return omfs_add_node(dir, dentry, mode | S_IFREG);
    292}
    293
    294static struct dentry *omfs_lookup(struct inode *dir, struct dentry *dentry,
    295				  unsigned int flags)
    296{
    297	struct buffer_head *bh;
    298	struct inode *inode = NULL;
    299
    300	if (dentry->d_name.len > OMFS_NAMELEN)
    301		return ERR_PTR(-ENAMETOOLONG);
    302
    303	bh = omfs_find_entry(dir, dentry->d_name.name, dentry->d_name.len);
    304	if (!IS_ERR(bh)) {
    305		struct omfs_inode *oi = (struct omfs_inode *)bh->b_data;
    306		ino_t ino = be64_to_cpu(oi->i_head.h_self);
    307		brelse(bh);
    308		inode = omfs_iget(dir->i_sb, ino);
    309	} else if (bh != ERR_PTR(-ENOENT)) {
    310		inode = ERR_CAST(bh);
    311	}
    312	return d_splice_alias(inode, dentry);
    313}
    314
    315/* sanity check block's self pointer */
    316int omfs_is_bad(struct omfs_sb_info *sbi, struct omfs_header *header,
    317	u64 fsblock)
    318{
    319	int is_bad;
    320	u64 ino = be64_to_cpu(header->h_self);
    321	is_bad = ((ino != fsblock) || (ino < sbi->s_root_ino) ||
    322		(ino > sbi->s_num_blocks));
    323
    324	if (is_bad)
    325		printk(KERN_WARNING "omfs: bad hash chain detected\n");
    326
    327	return is_bad;
    328}
    329
    330static bool omfs_fill_chain(struct inode *dir, struct dir_context *ctx,
    331		u64 fsblock, int hindex)
    332{
    333	/* follow chain in this bucket */
    334	while (fsblock != ~0) {
    335		struct buffer_head *bh = omfs_bread(dir->i_sb, fsblock);
    336		struct omfs_inode *oi;
    337		u64 self;
    338		unsigned char d_type;
    339
    340		if (!bh)
    341			return true;
    342
    343		oi = (struct omfs_inode *) bh->b_data;
    344		if (omfs_is_bad(OMFS_SB(dir->i_sb), &oi->i_head, fsblock)) {
    345			brelse(bh);
    346			return true;
    347		}
    348
    349		self = fsblock;
    350		fsblock = be64_to_cpu(oi->i_sibling);
    351
    352		/* skip visited nodes */
    353		if (hindex) {
    354			hindex--;
    355			brelse(bh);
    356			continue;
    357		}
    358
    359		d_type = (oi->i_type == OMFS_DIR) ? DT_DIR : DT_REG;
    360
    361		if (!dir_emit(ctx, oi->i_name,
    362			      strnlen(oi->i_name, OMFS_NAMELEN),
    363			      self, d_type)) {
    364			brelse(bh);
    365			return false;
    366		}
    367		brelse(bh);
    368		ctx->pos++;
    369	}
    370	return true;
    371}
    372
    373static int omfs_rename(struct user_namespace *mnt_userns, struct inode *old_dir,
    374		       struct dentry *old_dentry, struct inode *new_dir,
    375		       struct dentry *new_dentry, unsigned int flags)
    376{
    377	struct inode *new_inode = d_inode(new_dentry);
    378	struct inode *old_inode = d_inode(old_dentry);
    379	int err;
    380
    381	if (flags & ~RENAME_NOREPLACE)
    382		return -EINVAL;
    383
    384	if (new_inode) {
    385		/* overwriting existing file/dir */
    386		err = omfs_remove(new_dir, new_dentry);
    387		if (err)
    388			goto out;
    389	}
    390
    391	/* since omfs locates files by name, we need to unlink _before_
    392	 * adding the new link or we won't find the old one */
    393	err = omfs_delete_entry(old_dentry);
    394	if (err)
    395		goto out;
    396
    397	mark_inode_dirty(old_dir);
    398	err = omfs_add_link(new_dentry, old_inode);
    399	if (err)
    400		goto out;
    401
    402	old_inode->i_ctime = current_time(old_inode);
    403	mark_inode_dirty(old_inode);
    404out:
    405	return err;
    406}
    407
    408static int omfs_readdir(struct file *file, struct dir_context *ctx)
    409{
    410	struct inode *dir = file_inode(file);
    411	struct buffer_head *bh;
    412	__be64 *p;
    413	unsigned int hchain, hindex;
    414	int nbuckets;
    415
    416	if (ctx->pos >> 32)
    417		return -EINVAL;
    418
    419	if (ctx->pos < 1 << 20) {
    420		if (!dir_emit_dots(file, ctx))
    421			return 0;
    422		ctx->pos = 1 << 20;
    423	}
    424
    425	nbuckets = (dir->i_size - OMFS_DIR_START) / 8;
    426
    427	/* high 12 bits store bucket + 1 and low 20 bits store hash index */
    428	hchain = (ctx->pos >> 20) - 1;
    429	hindex = ctx->pos & 0xfffff;
    430
    431	bh = omfs_bread(dir->i_sb, dir->i_ino);
    432	if (!bh)
    433		return -EINVAL;
    434
    435	p = (__be64 *)(bh->b_data + OMFS_DIR_START) + hchain;
    436
    437	for (; hchain < nbuckets; hchain++) {
    438		__u64 fsblock = be64_to_cpu(*p++);
    439		if (!omfs_fill_chain(dir, ctx, fsblock, hindex))
    440			break;
    441		hindex = 0;
    442		ctx->pos = (hchain+2) << 20;
    443	}
    444	brelse(bh);
    445	return 0;
    446}
    447
    448const struct inode_operations omfs_dir_inops = {
    449	.lookup = omfs_lookup,
    450	.mkdir = omfs_mkdir,
    451	.rename = omfs_rename,
    452	.create = omfs_create,
    453	.unlink = omfs_remove,
    454	.rmdir = omfs_remove,
    455};
    456
    457const struct file_operations omfs_dir_operations = {
    458	.read = generic_read_dir,
    459	.iterate_shared = omfs_readdir,
    460	.llseek = generic_file_llseek,
    461};