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_fplus.c (7249B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 *  linux/fs/adfs/dir_fplus.c
      4 *
      5 *  Copyright (C) 1997-1999 Russell King
      6 */
      7#include "adfs.h"
      8#include "dir_fplus.h"
      9
     10/* Return the byte offset to directory entry pos */
     11static unsigned int adfs_fplus_offset(const struct adfs_bigdirheader *h,
     12				      unsigned int pos)
     13{
     14	return offsetof(struct adfs_bigdirheader, bigdirname) +
     15	       ALIGN(le32_to_cpu(h->bigdirnamelen), 4) +
     16	       pos * sizeof(struct adfs_bigdirentry);
     17}
     18
     19static int adfs_fplus_validate_header(const struct adfs_bigdirheader *h)
     20{
     21	unsigned int size = le32_to_cpu(h->bigdirsize);
     22	unsigned int len;
     23
     24	if (h->bigdirversion[0] != 0 || h->bigdirversion[1] != 0 ||
     25	    h->bigdirversion[2] != 0 ||
     26	    h->bigdirstartname != cpu_to_le32(BIGDIRSTARTNAME) ||
     27	    !size || size & 2047 || size > SZ_4M)
     28		return -EIO;
     29
     30	size -= sizeof(struct adfs_bigdirtail) +
     31		offsetof(struct adfs_bigdirheader, bigdirname);
     32
     33	/* Check that bigdirnamelen fits within the directory */
     34	len = ALIGN(le32_to_cpu(h->bigdirnamelen), 4);
     35	if (len > size)
     36		return -EIO;
     37
     38	size -= len;
     39
     40	/* Check that bigdirnamesize fits within the directory */
     41	len = le32_to_cpu(h->bigdirnamesize);
     42	if (len > size)
     43		return -EIO;
     44
     45	size -= len;
     46
     47	/*
     48	 * Avoid division, we know that absolute maximum number of entries
     49	 * can not be so large to cause overflow of the multiplication below.
     50	 */
     51	len = le32_to_cpu(h->bigdirentries);
     52	if (len > SZ_4M / sizeof(struct adfs_bigdirentry) ||
     53	    len * sizeof(struct adfs_bigdirentry) > size)
     54		return -EIO;
     55
     56	return 0;
     57}
     58
     59static int adfs_fplus_validate_tail(const struct adfs_bigdirheader *h,
     60				    const struct adfs_bigdirtail *t)
     61{
     62	if (t->bigdirendname != cpu_to_le32(BIGDIRENDNAME) ||
     63	    t->bigdirendmasseq != h->startmasseq ||
     64	    t->reserved[0] != 0 || t->reserved[1] != 0)
     65		return -EIO;
     66
     67	return 0;
     68}
     69
     70static u8 adfs_fplus_checkbyte(struct adfs_dir *dir)
     71{
     72	struct adfs_bigdirheader *h = dir->bighead;
     73	struct adfs_bigdirtail *t = dir->bigtail;
     74	unsigned int end, bs, bi, i;
     75	__le32 *bp;
     76	u32 dircheck;
     77
     78	end = adfs_fplus_offset(h, le32_to_cpu(h->bigdirentries)) +
     79		le32_to_cpu(h->bigdirnamesize);
     80
     81	/* Accumulate the contents of the header, entries and names */
     82	for (dircheck = 0, bi = 0; end; bi++) {
     83		bp = (void *)dir->bhs[bi]->b_data;
     84		bs = dir->bhs[bi]->b_size;
     85		if (bs > end)
     86			bs = end;
     87
     88		for (i = 0; i < bs; i += sizeof(u32))
     89			dircheck = ror32(dircheck, 13) ^ le32_to_cpup(bp++);
     90
     91		end -= bs;
     92	}
     93
     94	/* Accumulate the contents of the tail except for the check byte */
     95	dircheck = ror32(dircheck, 13) ^ le32_to_cpu(t->bigdirendname);
     96	dircheck = ror32(dircheck, 13) ^ t->bigdirendmasseq;
     97	dircheck = ror32(dircheck, 13) ^ t->reserved[0];
     98	dircheck = ror32(dircheck, 13) ^ t->reserved[1];
     99
    100	return dircheck ^ dircheck >> 8 ^ dircheck >> 16 ^ dircheck >> 24;
    101}
    102
    103static int adfs_fplus_read(struct super_block *sb, u32 indaddr,
    104			   unsigned int size, struct adfs_dir *dir)
    105{
    106	struct adfs_bigdirheader *h;
    107	struct adfs_bigdirtail *t;
    108	unsigned int dirsize;
    109	int ret;
    110
    111	/* Read first buffer */
    112	ret = adfs_dir_read_buffers(sb, indaddr, sb->s_blocksize, dir);
    113	if (ret)
    114		return ret;
    115
    116	dir->bighead = h = (void *)dir->bhs[0]->b_data;
    117	ret = adfs_fplus_validate_header(h);
    118	if (ret) {
    119		adfs_error(sb, "dir %06x has malformed header", indaddr);
    120		goto out;
    121	}
    122
    123	dirsize = le32_to_cpu(h->bigdirsize);
    124	if (size && dirsize != size) {
    125		adfs_msg(sb, KERN_WARNING,
    126			 "dir %06x header size %X does not match directory size %X",
    127			 indaddr, dirsize, size);
    128	}
    129
    130	/* Read remaining buffers */
    131	ret = adfs_dir_read_buffers(sb, indaddr, dirsize, dir);
    132	if (ret)
    133		return ret;
    134
    135	dir->bigtail = t = (struct adfs_bigdirtail *)
    136		(dir->bhs[dir->nr_buffers - 1]->b_data + (sb->s_blocksize - 8));
    137
    138	ret = adfs_fplus_validate_tail(h, t);
    139	if (ret) {
    140		adfs_error(sb, "dir %06x has malformed tail", indaddr);
    141		goto out;
    142	}
    143
    144	if (adfs_fplus_checkbyte(dir) != t->bigdircheckbyte) {
    145		adfs_error(sb, "dir %06x checkbyte mismatch\n", indaddr);
    146		goto out;
    147	}
    148
    149	dir->parent_id = le32_to_cpu(h->bigdirparent);
    150	return 0;
    151
    152out:
    153	adfs_dir_relse(dir);
    154
    155	return ret;
    156}
    157
    158static int
    159adfs_fplus_setpos(struct adfs_dir *dir, unsigned int fpos)
    160{
    161	int ret = -ENOENT;
    162
    163	if (fpos <= le32_to_cpu(dir->bighead->bigdirentries)) {
    164		dir->pos = fpos;
    165		ret = 0;
    166	}
    167
    168	return ret;
    169}
    170
    171static int
    172adfs_fplus_getnext(struct adfs_dir *dir, struct object_info *obj)
    173{
    174	struct adfs_bigdirheader *h = dir->bighead;
    175	struct adfs_bigdirentry bde;
    176	unsigned int offset;
    177	int ret;
    178
    179	if (dir->pos >= le32_to_cpu(h->bigdirentries))
    180		return -ENOENT;
    181
    182	offset = adfs_fplus_offset(h, dir->pos);
    183
    184	ret = adfs_dir_copyfrom(&bde, dir, offset,
    185				sizeof(struct adfs_bigdirentry));
    186	if (ret)
    187		return ret;
    188
    189	obj->loadaddr = le32_to_cpu(bde.bigdirload);
    190	obj->execaddr = le32_to_cpu(bde.bigdirexec);
    191	obj->size     = le32_to_cpu(bde.bigdirlen);
    192	obj->indaddr  = le32_to_cpu(bde.bigdirindaddr);
    193	obj->attr     = le32_to_cpu(bde.bigdirattr);
    194	obj->name_len = le32_to_cpu(bde.bigdirobnamelen);
    195
    196	offset = adfs_fplus_offset(h, le32_to_cpu(h->bigdirentries));
    197	offset += le32_to_cpu(bde.bigdirobnameptr);
    198
    199	ret = adfs_dir_copyfrom(obj->name, dir, offset, obj->name_len);
    200	if (ret)
    201		return ret;
    202
    203	adfs_object_fixup(dir, obj);
    204
    205	dir->pos += 1;
    206
    207	return 0;
    208}
    209
    210static int adfs_fplus_iterate(struct adfs_dir *dir, struct dir_context *ctx)
    211{
    212	struct object_info obj;
    213
    214	if ((ctx->pos - 2) >> 32)
    215		return 0;
    216
    217	if (adfs_fplus_setpos(dir, ctx->pos - 2))
    218		return 0;
    219
    220	while (!adfs_fplus_getnext(dir, &obj)) {
    221		if (!dir_emit(ctx, obj.name, obj.name_len,
    222			      obj.indaddr, DT_UNKNOWN))
    223			break;
    224		ctx->pos++;
    225	}
    226
    227	return 0;
    228}
    229
    230static int adfs_fplus_update(struct adfs_dir *dir, struct object_info *obj)
    231{
    232	struct adfs_bigdirheader *h = dir->bighead;
    233	struct adfs_bigdirentry bde;
    234	int offset, end, ret;
    235
    236	offset = adfs_fplus_offset(h, 0) - sizeof(bde);
    237	end = adfs_fplus_offset(h, le32_to_cpu(h->bigdirentries));
    238
    239	do {
    240		offset += sizeof(bde);
    241		if (offset >= end) {
    242			adfs_error(dir->sb, "unable to locate entry to update");
    243			return -ENOENT;
    244		}
    245		ret = adfs_dir_copyfrom(&bde, dir, offset, sizeof(bde));
    246		if (ret) {
    247			adfs_error(dir->sb, "error reading directory entry");
    248			return -ENOENT;
    249		}
    250	} while (le32_to_cpu(bde.bigdirindaddr) != obj->indaddr);
    251
    252	bde.bigdirload    = cpu_to_le32(obj->loadaddr);
    253	bde.bigdirexec    = cpu_to_le32(obj->execaddr);
    254	bde.bigdirlen     = cpu_to_le32(obj->size);
    255	bde.bigdirindaddr = cpu_to_le32(obj->indaddr);
    256	bde.bigdirattr    = cpu_to_le32(obj->attr);
    257
    258	return adfs_dir_copyto(dir, offset, &bde, sizeof(bde));
    259}
    260
    261static int adfs_fplus_commit(struct adfs_dir *dir)
    262{
    263	int ret;
    264
    265	/* Increment directory sequence number */
    266	dir->bighead->startmasseq += 1;
    267	dir->bigtail->bigdirendmasseq += 1;
    268
    269	/* Update directory check byte */
    270	dir->bigtail->bigdircheckbyte = adfs_fplus_checkbyte(dir);
    271
    272	/* Make sure the directory still validates correctly */
    273	ret = adfs_fplus_validate_header(dir->bighead);
    274	if (ret == 0)
    275		ret = adfs_fplus_validate_tail(dir->bighead, dir->bigtail);
    276
    277	return ret;
    278}
    279
    280const struct adfs_dir_ops adfs_fplus_dir_ops = {
    281	.read		= adfs_fplus_read,
    282	.iterate	= adfs_fplus_iterate,
    283	.setpos		= adfs_fplus_setpos,
    284	.getnext	= adfs_fplus_getnext,
    285	.update		= adfs_fplus_update,
    286	.commit		= adfs_fplus_commit,
    287};