xref: /openbmc/linux/fs/adfs/dir_fplus.c (revision 587065dc)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  *  linux/fs/adfs/dir_fplus.c
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  *  Copyright (C) 1997-1999 Russell King
61da177e4SLinus Torvalds  */
71da177e4SLinus Torvalds #include "adfs.h"
81da177e4SLinus Torvalds #include "dir_fplus.h"
91da177e4SLinus Torvalds 
100db35a02SRussell King /* Return the byte offset to directory entry pos */
adfs_fplus_offset(const struct adfs_bigdirheader * h,unsigned int pos)110db35a02SRussell King static unsigned int adfs_fplus_offset(const struct adfs_bigdirheader *h,
120db35a02SRussell King 				      unsigned int pos)
130db35a02SRussell King {
140db35a02SRussell King 	return offsetof(struct adfs_bigdirheader, bigdirname) +
150db35a02SRussell King 	       ALIGN(le32_to_cpu(h->bigdirnamelen), 4) +
160db35a02SRussell King 	       pos * sizeof(struct adfs_bigdirentry);
170db35a02SRussell King }
180db35a02SRussell King 
adfs_fplus_validate_header(const struct adfs_bigdirheader * h)196674ecabSRussell King static int adfs_fplus_validate_header(const struct adfs_bigdirheader *h)
206674ecabSRussell King {
216674ecabSRussell King 	unsigned int size = le32_to_cpu(h->bigdirsize);
22aa3d4e01SRussell King 	unsigned int len;
236674ecabSRussell King 
246674ecabSRussell King 	if (h->bigdirversion[0] != 0 || h->bigdirversion[1] != 0 ||
256674ecabSRussell King 	    h->bigdirversion[2] != 0 ||
266674ecabSRussell King 	    h->bigdirstartname != cpu_to_le32(BIGDIRSTARTNAME) ||
27aa3d4e01SRussell King 	    !size || size & 2047 || size > SZ_4M)
28aa3d4e01SRussell King 		return -EIO;
29aa3d4e01SRussell King 
30aa3d4e01SRussell King 	size -= sizeof(struct adfs_bigdirtail) +
31aa3d4e01SRussell King 		offsetof(struct adfs_bigdirheader, bigdirname);
32aa3d4e01SRussell King 
33aa3d4e01SRussell King 	/* Check that bigdirnamelen fits within the directory */
34aa3d4e01SRussell King 	len = ALIGN(le32_to_cpu(h->bigdirnamelen), 4);
35aa3d4e01SRussell King 	if (len > size)
36aa3d4e01SRussell King 		return -EIO;
37aa3d4e01SRussell King 
38aa3d4e01SRussell King 	size -= len;
39aa3d4e01SRussell King 
40aa3d4e01SRussell King 	/* Check that bigdirnamesize fits within the directory */
41aa3d4e01SRussell King 	len = le32_to_cpu(h->bigdirnamesize);
42aa3d4e01SRussell King 	if (len > size)
43aa3d4e01SRussell King 		return -EIO;
44aa3d4e01SRussell King 
45aa3d4e01SRussell King 	size -= len;
46aa3d4e01SRussell King 
47aa3d4e01SRussell King 	/*
48aa3d4e01SRussell King 	 * Avoid division, we know that absolute maximum number of entries
49aa3d4e01SRussell King 	 * can not be so large to cause overflow of the multiplication below.
50aa3d4e01SRussell King 	 */
51aa3d4e01SRussell King 	len = le32_to_cpu(h->bigdirentries);
52aa3d4e01SRussell King 	if (len > SZ_4M / sizeof(struct adfs_bigdirentry) ||
53aa3d4e01SRussell King 	    len * sizeof(struct adfs_bigdirentry) > size)
546674ecabSRussell King 		return -EIO;
556674ecabSRussell King 
566674ecabSRussell King 	return 0;
576674ecabSRussell King }
586674ecabSRussell King 
adfs_fplus_validate_tail(const struct adfs_bigdirheader * h,const struct adfs_bigdirtail * t)596674ecabSRussell King static int adfs_fplus_validate_tail(const struct adfs_bigdirheader *h,
606674ecabSRussell King 				    const struct adfs_bigdirtail *t)
616674ecabSRussell King {
626674ecabSRussell King 	if (t->bigdirendname != cpu_to_le32(BIGDIRENDNAME) ||
636674ecabSRussell King 	    t->bigdirendmasseq != h->startmasseq ||
646674ecabSRussell King 	    t->reserved[0] != 0 || t->reserved[1] != 0)
656674ecabSRussell King 		return -EIO;
666674ecabSRussell King 
676674ecabSRussell King 	return 0;
686674ecabSRussell King }
696674ecabSRussell King 
adfs_fplus_checkbyte(struct adfs_dir * dir)70d79288b4SRussell King static u8 adfs_fplus_checkbyte(struct adfs_dir *dir)
71d79288b4SRussell King {
72d79288b4SRussell King 	struct adfs_bigdirheader *h = dir->bighead;
73d79288b4SRussell King 	struct adfs_bigdirtail *t = dir->bigtail;
74d79288b4SRussell King 	unsigned int end, bs, bi, i;
75d79288b4SRussell King 	__le32 *bp;
76d79288b4SRussell King 	u32 dircheck;
77d79288b4SRussell King 
78d79288b4SRussell King 	end = adfs_fplus_offset(h, le32_to_cpu(h->bigdirentries)) +
79d79288b4SRussell King 		le32_to_cpu(h->bigdirnamesize);
80d79288b4SRussell King 
81d79288b4SRussell King 	/* Accumulate the contents of the header, entries and names */
82d79288b4SRussell King 	for (dircheck = 0, bi = 0; end; bi++) {
83d79288b4SRussell King 		bp = (void *)dir->bhs[bi]->b_data;
84d79288b4SRussell King 		bs = dir->bhs[bi]->b_size;
85d79288b4SRussell King 		if (bs > end)
86d79288b4SRussell King 			bs = end;
87d79288b4SRussell King 
88d79288b4SRussell King 		for (i = 0; i < bs; i += sizeof(u32))
89d79288b4SRussell King 			dircheck = ror32(dircheck, 13) ^ le32_to_cpup(bp++);
90d79288b4SRussell King 
91d79288b4SRussell King 		end -= bs;
92d79288b4SRussell King 	}
93d79288b4SRussell King 
94d79288b4SRussell King 	/* Accumulate the contents of the tail except for the check byte */
95d79288b4SRussell King 	dircheck = ror32(dircheck, 13) ^ le32_to_cpu(t->bigdirendname);
96d79288b4SRussell King 	dircheck = ror32(dircheck, 13) ^ t->bigdirendmasseq;
97d79288b4SRussell King 	dircheck = ror32(dircheck, 13) ^ t->reserved[0];
98d79288b4SRussell King 	dircheck = ror32(dircheck, 13) ^ t->reserved[1];
99d79288b4SRussell King 
100d79288b4SRussell King 	return dircheck ^ dircheck >> 8 ^ dircheck >> 16 ^ dircheck >> 24;
101d79288b4SRussell King }
102d79288b4SRussell King 
adfs_fplus_read(struct super_block * sb,u32 indaddr,unsigned int size,struct adfs_dir * dir)103419a6e5eSRussell King static int adfs_fplus_read(struct super_block *sb, u32 indaddr,
104419a6e5eSRussell King 			   unsigned int size, struct adfs_dir *dir)
1051da177e4SLinus Torvalds {
1061da177e4SLinus Torvalds 	struct adfs_bigdirheader *h;
1071da177e4SLinus Torvalds 	struct adfs_bigdirtail *t;
108419a6e5eSRussell King 	unsigned int dirsize;
109419a6e5eSRussell King 	int ret;
1101da177e4SLinus Torvalds 
111419a6e5eSRussell King 	/* Read first buffer */
112419a6e5eSRussell King 	ret = adfs_dir_read_buffers(sb, indaddr, sb->s_blocksize, dir);
113419a6e5eSRussell King 	if (ret)
114419a6e5eSRussell King 		return ret;
1151da177e4SLinus Torvalds 
116016936b3SRussell King 	dir->bighead = h = (void *)dir->bhs[0]->b_data;
117587065dcSDan Carpenter 	ret = adfs_fplus_validate_header(h);
118587065dcSDan Carpenter 	if (ret) {
1196674ecabSRussell King 		adfs_error(sb, "dir %06x has malformed header", indaddr);
1206674ecabSRussell King 		goto out;
1216674ecabSRussell King 	}
1226674ecabSRussell King 
123419a6e5eSRussell King 	dirsize = le32_to_cpu(h->bigdirsize);
124a464152fSRussell King 	if (size && dirsize != size) {
125ceb3b106SRussell King 		adfs_msg(sb, KERN_WARNING,
126419a6e5eSRussell King 			 "dir %06x header size %X does not match directory size %X",
127419a6e5eSRussell King 			 indaddr, dirsize, size);
1281da177e4SLinus Torvalds 	}
1291da177e4SLinus Torvalds 
130419a6e5eSRussell King 	/* Read remaining buffers */
131419a6e5eSRussell King 	ret = adfs_dir_read_buffers(sb, indaddr, dirsize, dir);
132419a6e5eSRussell King 	if (ret)
133419a6e5eSRussell King 		return ret;
1342f09719aSStuart Swales 
135016936b3SRussell King 	dir->bigtail = t = (struct adfs_bigdirtail *)
136419a6e5eSRussell King 		(dir->bhs[dir->nr_buffers - 1]->b_data + (sb->s_blocksize - 8));
1371da177e4SLinus Torvalds 
1386674ecabSRussell King 	ret = adfs_fplus_validate_tail(h, t);
1396674ecabSRussell King 	if (ret) {
140419a6e5eSRussell King 		adfs_error(sb, "dir %06x has malformed tail", indaddr);
1411da177e4SLinus Torvalds 		goto out;
1422f09719aSStuart Swales 	}
1431da177e4SLinus Torvalds 
144d79288b4SRussell King 	if (adfs_fplus_checkbyte(dir) != t->bigdircheckbyte) {
145d79288b4SRussell King 		adfs_error(sb, "dir %06x checkbyte mismatch\n", indaddr);
146d79288b4SRussell King 		goto out;
147d79288b4SRussell King 	}
148d79288b4SRussell King 
1491da177e4SLinus Torvalds 	dir->parent_id = le32_to_cpu(h->bigdirparent);
1501da177e4SLinus Torvalds 	return 0;
1512f09719aSStuart Swales 
1521da177e4SLinus Torvalds out:
1531dd9f5baSRussell King 	adfs_dir_relse(dir);
1542f09719aSStuart Swales 
1551da177e4SLinus Torvalds 	return ret;
1561da177e4SLinus Torvalds }
1571da177e4SLinus Torvalds 
1581da177e4SLinus Torvalds static int
adfs_fplus_setpos(struct adfs_dir * dir,unsigned int fpos)1591da177e4SLinus Torvalds adfs_fplus_setpos(struct adfs_dir *dir, unsigned int fpos)
1601da177e4SLinus Torvalds {
1611da177e4SLinus Torvalds 	int ret = -ENOENT;
1621da177e4SLinus Torvalds 
163016936b3SRussell King 	if (fpos <= le32_to_cpu(dir->bighead->bigdirentries)) {
1641da177e4SLinus Torvalds 		dir->pos = fpos;
1651da177e4SLinus Torvalds 		ret = 0;
1661da177e4SLinus Torvalds 	}
1671da177e4SLinus Torvalds 
1681da177e4SLinus Torvalds 	return ret;
1691da177e4SLinus Torvalds }
1701da177e4SLinus Torvalds 
1711da177e4SLinus Torvalds static int
adfs_fplus_getnext(struct adfs_dir * dir,struct object_info * obj)1721da177e4SLinus Torvalds adfs_fplus_getnext(struct adfs_dir *dir, struct object_info *obj)
1731da177e4SLinus Torvalds {
174016936b3SRussell King 	struct adfs_bigdirheader *h = dir->bighead;
1751da177e4SLinus Torvalds 	struct adfs_bigdirentry bde;
1761da177e4SLinus Torvalds 	unsigned int offset;
177a317120bSRussell King 	int ret;
1781da177e4SLinus Torvalds 
1791da177e4SLinus Torvalds 	if (dir->pos >= le32_to_cpu(h->bigdirentries))
180a317120bSRussell King 		return -ENOENT;
1811da177e4SLinus Torvalds 
1820db35a02SRussell King 	offset = adfs_fplus_offset(h, dir->pos);
1831da177e4SLinus Torvalds 
184a317120bSRussell King 	ret = adfs_dir_copyfrom(&bde, dir, offset,
185a317120bSRussell King 				sizeof(struct adfs_bigdirentry));
186a317120bSRussell King 	if (ret)
187a317120bSRussell King 		return ret;
1881da177e4SLinus Torvalds 
1891da177e4SLinus Torvalds 	obj->loadaddr = le32_to_cpu(bde.bigdirload);
1901da177e4SLinus Torvalds 	obj->execaddr = le32_to_cpu(bde.bigdirexec);
1911da177e4SLinus Torvalds 	obj->size     = le32_to_cpu(bde.bigdirlen);
1925ed70bb4SRussell King 	obj->indaddr  = le32_to_cpu(bde.bigdirindaddr);
1931da177e4SLinus Torvalds 	obj->attr     = le32_to_cpu(bde.bigdirattr);
1941da177e4SLinus Torvalds 	obj->name_len = le32_to_cpu(bde.bigdirobnamelen);
1951da177e4SLinus Torvalds 
1960db35a02SRussell King 	offset = adfs_fplus_offset(h, le32_to_cpu(h->bigdirentries));
1971da177e4SLinus Torvalds 	offset += le32_to_cpu(bde.bigdirobnameptr);
1981da177e4SLinus Torvalds 
199a317120bSRussell King 	ret = adfs_dir_copyfrom(obj->name, dir, offset, obj->name_len);
200a317120bSRussell King 	if (ret)
201a317120bSRussell King 		return ret;
202a317120bSRussell King 
203411c49bcSRussell King 	adfs_object_fixup(dir, obj);
204da23ef05SStuart Swales 
2051da177e4SLinus Torvalds 	dir->pos += 1;
206a317120bSRussell King 
207a317120bSRussell King 	return 0;
2081da177e4SLinus Torvalds }
2091da177e4SLinus Torvalds 
adfs_fplus_iterate(struct adfs_dir * dir,struct dir_context * ctx)2104287e4deSRussell King static int adfs_fplus_iterate(struct adfs_dir *dir, struct dir_context *ctx)
2114287e4deSRussell King {
2124287e4deSRussell King 	struct object_info obj;
2134287e4deSRussell King 
2144287e4deSRussell King 	if ((ctx->pos - 2) >> 32)
2154287e4deSRussell King 		return 0;
2164287e4deSRussell King 
2174287e4deSRussell King 	if (adfs_fplus_setpos(dir, ctx->pos - 2))
2184287e4deSRussell King 		return 0;
2194287e4deSRussell King 
2204287e4deSRussell King 	while (!adfs_fplus_getnext(dir, &obj)) {
2214287e4deSRussell King 		if (!dir_emit(ctx, obj.name, obj.name_len,
2224287e4deSRussell King 			      obj.indaddr, DT_UNKNOWN))
2234287e4deSRussell King 			break;
2244287e4deSRussell King 		ctx->pos++;
2254287e4deSRussell King 	}
2264287e4deSRussell King 
2274287e4deSRussell King 	return 0;
2284287e4deSRussell King }
2294287e4deSRussell King 
adfs_fplus_update(struct adfs_dir * dir,struct object_info * obj)230a464152fSRussell King static int adfs_fplus_update(struct adfs_dir *dir, struct object_info *obj)
231a464152fSRussell King {
232a464152fSRussell King 	struct adfs_bigdirheader *h = dir->bighead;
233a464152fSRussell King 	struct adfs_bigdirentry bde;
234a464152fSRussell King 	int offset, end, ret;
235a464152fSRussell King 
236a464152fSRussell King 	offset = adfs_fplus_offset(h, 0) - sizeof(bde);
237a464152fSRussell King 	end = adfs_fplus_offset(h, le32_to_cpu(h->bigdirentries));
238a464152fSRussell King 
239a464152fSRussell King 	do {
240a464152fSRussell King 		offset += sizeof(bde);
241a464152fSRussell King 		if (offset >= end) {
242a464152fSRussell King 			adfs_error(dir->sb, "unable to locate entry to update");
243a464152fSRussell King 			return -ENOENT;
244a464152fSRussell King 		}
245a464152fSRussell King 		ret = adfs_dir_copyfrom(&bde, dir, offset, sizeof(bde));
246a464152fSRussell King 		if (ret) {
247a464152fSRussell King 			adfs_error(dir->sb, "error reading directory entry");
248a464152fSRussell King 			return -ENOENT;
249a464152fSRussell King 		}
250a464152fSRussell King 	} while (le32_to_cpu(bde.bigdirindaddr) != obj->indaddr);
251a464152fSRussell King 
252a464152fSRussell King 	bde.bigdirload    = cpu_to_le32(obj->loadaddr);
253a464152fSRussell King 	bde.bigdirexec    = cpu_to_le32(obj->execaddr);
254a464152fSRussell King 	bde.bigdirlen     = cpu_to_le32(obj->size);
255a464152fSRussell King 	bde.bigdirindaddr = cpu_to_le32(obj->indaddr);
256a464152fSRussell King 	bde.bigdirattr    = cpu_to_le32(obj->attr);
257a464152fSRussell King 
258a464152fSRussell King 	return adfs_dir_copyto(dir, offset, &bde, sizeof(bde));
259a464152fSRussell King }
260a464152fSRussell King 
adfs_fplus_commit(struct adfs_dir * dir)261a464152fSRussell King static int adfs_fplus_commit(struct adfs_dir *dir)
262a464152fSRussell King {
263a464152fSRussell King 	int ret;
264a464152fSRussell King 
265a464152fSRussell King 	/* Increment directory sequence number */
266a464152fSRussell King 	dir->bighead->startmasseq += 1;
267a464152fSRussell King 	dir->bigtail->bigdirendmasseq += 1;
268a464152fSRussell King 
269a464152fSRussell King 	/* Update directory check byte */
270a464152fSRussell King 	dir->bigtail->bigdircheckbyte = adfs_fplus_checkbyte(dir);
271a464152fSRussell King 
272a464152fSRussell King 	/* Make sure the directory still validates correctly */
273a464152fSRussell King 	ret = adfs_fplus_validate_header(dir->bighead);
274a464152fSRussell King 	if (ret == 0)
275a464152fSRussell King 		ret = adfs_fplus_validate_tail(dir->bighead, dir->bigtail);
276a464152fSRussell King 
277a464152fSRussell King 	return ret;
278a464152fSRussell King }
279a464152fSRussell King 
2800125f504SJulia Lawall const struct adfs_dir_ops adfs_fplus_dir_ops = {
2811da177e4SLinus Torvalds 	.read		= adfs_fplus_read,
2824287e4deSRussell King 	.iterate	= adfs_fplus_iterate,
2831da177e4SLinus Torvalds 	.setpos		= adfs_fplus_setpos,
2841da177e4SLinus Torvalds 	.getnext	= adfs_fplus_getnext,
285a464152fSRussell King 	.update		= adfs_fplus_update,
286a464152fSRussell King 	.commit		= adfs_fplus_commit,
2871da177e4SLinus Torvalds };
288