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


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 *  linux/fs/adfs/dir.c
      4 *
      5 *  Copyright (C) 1999-2000 Russell King
      6 *
      7 *  Common directory handling for ADFS
      8 */
      9#include <linux/slab.h>
     10#include "adfs.h"
     11
     12/*
     13 * For future.  This should probably be per-directory.
     14 */
     15static DECLARE_RWSEM(adfs_dir_rwsem);
     16
     17int adfs_dir_copyfrom(void *dst, struct adfs_dir *dir, unsigned int offset,
     18		      size_t len)
     19{
     20	struct super_block *sb = dir->sb;
     21	unsigned int index, remain;
     22
     23	index = offset >> sb->s_blocksize_bits;
     24	offset &= sb->s_blocksize - 1;
     25	remain = sb->s_blocksize - offset;
     26	if (index + (remain < len) >= dir->nr_buffers)
     27		return -EINVAL;
     28
     29	if (remain < len) {
     30		memcpy(dst, dir->bhs[index]->b_data + offset, remain);
     31		dst += remain;
     32		len -= remain;
     33		index += 1;
     34		offset = 0;
     35	}
     36
     37	memcpy(dst, dir->bhs[index]->b_data + offset, len);
     38
     39	return 0;
     40}
     41
     42int adfs_dir_copyto(struct adfs_dir *dir, unsigned int offset, const void *src,
     43		    size_t len)
     44{
     45	struct super_block *sb = dir->sb;
     46	unsigned int index, remain;
     47
     48	index = offset >> sb->s_blocksize_bits;
     49	offset &= sb->s_blocksize - 1;
     50	remain = sb->s_blocksize - offset;
     51	if (index + (remain < len) >= dir->nr_buffers)
     52		return -EINVAL;
     53
     54	if (remain < len) {
     55		memcpy(dir->bhs[index]->b_data + offset, src, remain);
     56		src += remain;
     57		len -= remain;
     58		index += 1;
     59		offset = 0;
     60	}
     61
     62	memcpy(dir->bhs[index]->b_data + offset, src, len);
     63
     64	return 0;
     65}
     66
     67static void __adfs_dir_cleanup(struct adfs_dir *dir)
     68{
     69	dir->nr_buffers = 0;
     70
     71	if (dir->bhs != dir->bh)
     72		kfree(dir->bhs);
     73	dir->bhs = NULL;
     74	dir->sb = NULL;
     75}
     76
     77void adfs_dir_relse(struct adfs_dir *dir)
     78{
     79	unsigned int i;
     80
     81	for (i = 0; i < dir->nr_buffers; i++)
     82		brelse(dir->bhs[i]);
     83
     84	__adfs_dir_cleanup(dir);
     85}
     86
     87static void adfs_dir_forget(struct adfs_dir *dir)
     88{
     89	unsigned int i;
     90
     91	for (i = 0; i < dir->nr_buffers; i++)
     92		bforget(dir->bhs[i]);
     93
     94	__adfs_dir_cleanup(dir);
     95}
     96
     97int adfs_dir_read_buffers(struct super_block *sb, u32 indaddr,
     98			  unsigned int size, struct adfs_dir *dir)
     99{
    100	struct buffer_head **bhs;
    101	unsigned int i, num;
    102	int block;
    103
    104	num = ALIGN(size, sb->s_blocksize) >> sb->s_blocksize_bits;
    105	if (num > ARRAY_SIZE(dir->bh)) {
    106		/* We only allow one extension */
    107		if (dir->bhs != dir->bh)
    108			return -EINVAL;
    109
    110		bhs = kcalloc(num, sizeof(*bhs), GFP_KERNEL);
    111		if (!bhs)
    112			return -ENOMEM;
    113
    114		if (dir->nr_buffers)
    115			memcpy(bhs, dir->bhs, dir->nr_buffers * sizeof(*bhs));
    116
    117		dir->bhs = bhs;
    118	}
    119
    120	for (i = dir->nr_buffers; i < num; i++) {
    121		block = __adfs_block_map(sb, indaddr, i);
    122		if (!block) {
    123			adfs_error(sb, "dir %06x has a hole at offset %u",
    124				   indaddr, i);
    125			goto error;
    126		}
    127
    128		dir->bhs[i] = sb_bread(sb, block);
    129		if (!dir->bhs[i]) {
    130			adfs_error(sb,
    131				   "dir %06x failed read at offset %u, mapped block 0x%08x",
    132				   indaddr, i, block);
    133			goto error;
    134		}
    135
    136		dir->nr_buffers++;
    137	}
    138	return 0;
    139
    140error:
    141	adfs_dir_relse(dir);
    142
    143	return -EIO;
    144}
    145
    146static int adfs_dir_read(struct super_block *sb, u32 indaddr,
    147			 unsigned int size, struct adfs_dir *dir)
    148{
    149	dir->sb = sb;
    150	dir->bhs = dir->bh;
    151	dir->nr_buffers = 0;
    152
    153	return ADFS_SB(sb)->s_dir->read(sb, indaddr, size, dir);
    154}
    155
    156static int adfs_dir_read_inode(struct super_block *sb, struct inode *inode,
    157			       struct adfs_dir *dir)
    158{
    159	int ret;
    160
    161	ret = adfs_dir_read(sb, ADFS_I(inode)->indaddr, inode->i_size, dir);
    162	if (ret)
    163		return ret;
    164
    165	if (ADFS_I(inode)->parent_id != dir->parent_id) {
    166		adfs_error(sb,
    167			   "parent directory id changed under me! (%06x but got %06x)\n",
    168			   ADFS_I(inode)->parent_id, dir->parent_id);
    169		adfs_dir_relse(dir);
    170		ret = -EIO;
    171	}
    172
    173	return ret;
    174}
    175
    176static void adfs_dir_mark_dirty(struct adfs_dir *dir)
    177{
    178	unsigned int i;
    179
    180	/* Mark the buffers dirty */
    181	for (i = 0; i < dir->nr_buffers; i++)
    182		mark_buffer_dirty(dir->bhs[i]);
    183}
    184
    185static int adfs_dir_sync(struct adfs_dir *dir)
    186{
    187	int err = 0;
    188	int i;
    189
    190	for (i = dir->nr_buffers - 1; i >= 0; i--) {
    191		struct buffer_head *bh = dir->bhs[i];
    192		sync_dirty_buffer(bh);
    193		if (buffer_req(bh) && !buffer_uptodate(bh))
    194			err = -EIO;
    195	}
    196
    197	return err;
    198}
    199
    200void adfs_object_fixup(struct adfs_dir *dir, struct object_info *obj)
    201{
    202	unsigned int dots, i;
    203
    204	/*
    205	 * RISC OS allows the use of '/' in directory entry names, so we need
    206	 * to fix these up.  '/' is typically used for FAT compatibility to
    207	 * represent '.', so do the same conversion here.  In any case, '.'
    208	 * will never be in a RISC OS name since it is used as the pathname
    209	 * separator.  Handle the case where we may generate a '.' or '..'
    210	 * name, replacing the first character with '^' (the RISC OS "parent
    211	 * directory" character.)
    212	 */
    213	for (i = dots = 0; i < obj->name_len; i++)
    214		if (obj->name[i] == '/') {
    215			obj->name[i] = '.';
    216			dots++;
    217		}
    218
    219	if (obj->name_len <= 2 && dots == obj->name_len)
    220		obj->name[0] = '^';
    221
    222	/*
    223	 * If the object is a file, and the user requested the ,xyz hex
    224	 * filetype suffix to the name, check the filetype and append.
    225	 */
    226	if (!(obj->attr & ADFS_NDA_DIRECTORY) && ADFS_SB(dir->sb)->s_ftsuffix) {
    227		u16 filetype = adfs_filetype(obj->loadaddr);
    228
    229		if (filetype != ADFS_FILETYPE_NONE) {
    230			obj->name[obj->name_len++] = ',';
    231			obj->name[obj->name_len++] = hex_asc_lo(filetype >> 8);
    232			obj->name[obj->name_len++] = hex_asc_lo(filetype >> 4);
    233			obj->name[obj->name_len++] = hex_asc_lo(filetype >> 0);
    234		}
    235	}
    236}
    237
    238static int adfs_iterate(struct file *file, struct dir_context *ctx)
    239{
    240	struct inode *inode = file_inode(file);
    241	struct super_block *sb = inode->i_sb;
    242	const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
    243	struct adfs_dir dir;
    244	int ret;
    245
    246	down_read(&adfs_dir_rwsem);
    247	ret = adfs_dir_read_inode(sb, inode, &dir);
    248	if (ret)
    249		goto unlock;
    250
    251	if (ctx->pos == 0) {
    252		if (!dir_emit_dot(file, ctx))
    253			goto unlock_relse;
    254		ctx->pos = 1;
    255	}
    256	if (ctx->pos == 1) {
    257		if (!dir_emit(ctx, "..", 2, dir.parent_id, DT_DIR))
    258			goto unlock_relse;
    259		ctx->pos = 2;
    260	}
    261
    262	ret = ops->iterate(&dir, ctx);
    263
    264unlock_relse:
    265	up_read(&adfs_dir_rwsem);
    266	adfs_dir_relse(&dir);
    267	return ret;
    268
    269unlock:
    270	up_read(&adfs_dir_rwsem);
    271	return ret;
    272}
    273
    274int
    275adfs_dir_update(struct super_block *sb, struct object_info *obj, int wait)
    276{
    277	const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
    278	struct adfs_dir dir;
    279	int ret;
    280
    281	if (!IS_ENABLED(CONFIG_ADFS_FS_RW))
    282		return -EINVAL;
    283
    284	if (!ops->update)
    285		return -EINVAL;
    286
    287	down_write(&adfs_dir_rwsem);
    288	ret = adfs_dir_read(sb, obj->parent_id, 0, &dir);
    289	if (ret)
    290		goto unlock;
    291
    292	ret = ops->update(&dir, obj);
    293	if (ret)
    294		goto forget;
    295
    296	ret = ops->commit(&dir);
    297	if (ret)
    298		goto forget;
    299	up_write(&adfs_dir_rwsem);
    300
    301	adfs_dir_mark_dirty(&dir);
    302
    303	if (wait)
    304		ret = adfs_dir_sync(&dir);
    305
    306	adfs_dir_relse(&dir);
    307	return ret;
    308
    309	/*
    310	 * If the updated failed because the entry wasn't found, we can
    311	 * just release the buffers. If it was any other error, forget
    312	 * the dirtied buffers so they aren't written back to the media.
    313	 */
    314forget:
    315	if (ret == -ENOENT)
    316		adfs_dir_relse(&dir);
    317	else
    318		adfs_dir_forget(&dir);
    319unlock:
    320	up_write(&adfs_dir_rwsem);
    321
    322	return ret;
    323}
    324
    325static unsigned char adfs_tolower(unsigned char c)
    326{
    327	if (c >= 'A' && c <= 'Z')
    328		c += 'a' - 'A';
    329	return c;
    330}
    331
    332static int __adfs_compare(const unsigned char *qstr, u32 qlen,
    333			  const char *str, u32 len)
    334{
    335	u32 i;
    336
    337	if (qlen != len)
    338		return 1;
    339
    340	for (i = 0; i < qlen; i++)
    341		if (adfs_tolower(qstr[i]) != adfs_tolower(str[i]))
    342			return 1;
    343
    344	return 0;
    345}
    346
    347static int adfs_dir_lookup_byname(struct inode *inode, const struct qstr *qstr,
    348				  struct object_info *obj)
    349{
    350	struct super_block *sb = inode->i_sb;
    351	const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
    352	const unsigned char *name;
    353	struct adfs_dir dir;
    354	u32 name_len;
    355	int ret;
    356
    357	down_read(&adfs_dir_rwsem);
    358	ret = adfs_dir_read_inode(sb, inode, &dir);
    359	if (ret)
    360		goto unlock;
    361
    362	ret = ops->setpos(&dir, 0);
    363	if (ret)
    364		goto unlock_relse;
    365
    366	ret = -ENOENT;
    367	name = qstr->name;
    368	name_len = qstr->len;
    369	while (ops->getnext(&dir, obj) == 0) {
    370		if (!__adfs_compare(name, name_len, obj->name, obj->name_len)) {
    371			ret = 0;
    372			break;
    373		}
    374	}
    375	obj->parent_id = ADFS_I(inode)->indaddr;
    376
    377unlock_relse:
    378	up_read(&adfs_dir_rwsem);
    379	adfs_dir_relse(&dir);
    380	return ret;
    381
    382unlock:
    383	up_read(&adfs_dir_rwsem);
    384	return ret;
    385}
    386
    387const struct file_operations adfs_dir_operations = {
    388	.read		= generic_read_dir,
    389	.llseek		= generic_file_llseek,
    390	.iterate_shared	= adfs_iterate,
    391	.fsync		= generic_file_fsync,
    392};
    393
    394static int
    395adfs_hash(const struct dentry *parent, struct qstr *qstr)
    396{
    397	const unsigned char *name;
    398	unsigned long hash;
    399	u32 len;
    400
    401	if (qstr->len > ADFS_SB(parent->d_sb)->s_namelen)
    402		return -ENAMETOOLONG;
    403
    404	len = qstr->len;
    405	name = qstr->name;
    406	hash = init_name_hash(parent);
    407	while (len--)
    408		hash = partial_name_hash(adfs_tolower(*name++), hash);
    409	qstr->hash = end_name_hash(hash);
    410
    411	return 0;
    412}
    413
    414/*
    415 * Compare two names, taking note of the name length
    416 * requirements of the underlying filesystem.
    417 */
    418static int adfs_compare(const struct dentry *dentry, unsigned int len,
    419			const char *str, const struct qstr *qstr)
    420{
    421	return __adfs_compare(qstr->name, qstr->len, str, len);
    422}
    423
    424const struct dentry_operations adfs_dentry_operations = {
    425	.d_hash		= adfs_hash,
    426	.d_compare	= adfs_compare,
    427};
    428
    429static struct dentry *
    430adfs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags)
    431{
    432	struct inode *inode = NULL;
    433	struct object_info obj;
    434	int error;
    435
    436	error = adfs_dir_lookup_byname(dir, &dentry->d_name, &obj);
    437	if (error == 0) {
    438		/*
    439		 * This only returns NULL if get_empty_inode
    440		 * fails.
    441		 */
    442		inode = adfs_iget(dir->i_sb, &obj);
    443		if (!inode)
    444			inode = ERR_PTR(-EACCES);
    445	} else if (error != -ENOENT) {
    446		inode = ERR_PTR(error);
    447	}
    448	return d_splice_alias(inode, dentry);
    449}
    450
    451/*
    452 * directories can handle most operations...
    453 */
    454const struct inode_operations adfs_dir_inode_operations = {
    455	.lookup		= adfs_lookup,
    456	.setattr	= adfs_notify_change,
    457};