brintos

brintos / linux-shallow public Read only

0
0
Text · 7.1 KiB · 4a15924 Raw
288 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 *  linux/fs/adfs/dir_fplus.c4 *5 *  Copyright (C) 1997-1999 Russell King6 */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 entries49	 * 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 int159adfs_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 int172adfs_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};288