xref: /linux/fs/adfs/dir_fplus.c (revision 419a6e5e82ca0bdba0cc3624d969b65ae49d959b)
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 static int adfs_fplus_read(struct super_block *sb, u32 indaddr,
11 			   unsigned int size, struct adfs_dir *dir)
12 {
13 	struct adfs_bigdirheader *h;
14 	struct adfs_bigdirtail *t;
15 	unsigned int dirsize;
16 	int ret;
17 
18 	/* Read first buffer */
19 	ret = adfs_dir_read_buffers(sb, indaddr, sb->s_blocksize, dir);
20 	if (ret)
21 		return ret;
22 
23 	h = (struct adfs_bigdirheader *)dir->bhs[0]->b_data;
24 	dirsize = le32_to_cpu(h->bigdirsize);
25 	if (dirsize != size) {
26 		adfs_msg(sb, KERN_WARNING,
27 			 "dir %06x header size %X does not match directory size %X",
28 			 indaddr, dirsize, size);
29 	}
30 
31 	if (h->bigdirversion[0] != 0 || h->bigdirversion[1] != 0 ||
32 	    h->bigdirversion[2] != 0 || size & 2047 ||
33 	    h->bigdirstartname != cpu_to_le32(BIGDIRSTARTNAME)) {
34 		adfs_error(sb, "dir %06x has malformed header", indaddr);
35 		goto out;
36 	}
37 
38 	/* Read remaining buffers */
39 	ret = adfs_dir_read_buffers(sb, indaddr, dirsize, dir);
40 	if (ret)
41 		return ret;
42 
43 	t = (struct adfs_bigdirtail *)
44 		(dir->bhs[dir->nr_buffers - 1]->b_data + (sb->s_blocksize - 8));
45 
46 	if (t->bigdirendname != cpu_to_le32(BIGDIRENDNAME) ||
47 	    t->bigdirendmasseq != h->startmasseq ||
48 	    t->reserved[0] != 0 || t->reserved[1] != 0) {
49 		adfs_error(sb, "dir %06x has malformed tail", indaddr);
50 		goto out;
51 	}
52 
53 	dir->parent_id = le32_to_cpu(h->bigdirparent);
54 	return 0;
55 
56 out:
57 	adfs_dir_relse(dir);
58 
59 	return ret;
60 }
61 
62 static int
63 adfs_fplus_setpos(struct adfs_dir *dir, unsigned int fpos)
64 {
65 	struct adfs_bigdirheader *h =
66 		(struct adfs_bigdirheader *) dir->bhs[0]->b_data;
67 	int ret = -ENOENT;
68 
69 	if (fpos <= le32_to_cpu(h->bigdirentries)) {
70 		dir->pos = fpos;
71 		ret = 0;
72 	}
73 
74 	return ret;
75 }
76 
77 static int
78 adfs_fplus_getnext(struct adfs_dir *dir, struct object_info *obj)
79 {
80 	struct adfs_bigdirheader *h =
81 		(struct adfs_bigdirheader *) dir->bhs[0]->b_data;
82 	struct adfs_bigdirentry bde;
83 	unsigned int offset;
84 	int ret;
85 
86 	if (dir->pos >= le32_to_cpu(h->bigdirentries))
87 		return -ENOENT;
88 
89 	offset = offsetof(struct adfs_bigdirheader, bigdirname);
90 	offset += ((le32_to_cpu(h->bigdirnamelen) + 4) & ~3);
91 	offset += dir->pos * sizeof(struct adfs_bigdirentry);
92 
93 	ret = adfs_dir_copyfrom(&bde, dir, offset,
94 				sizeof(struct adfs_bigdirentry));
95 	if (ret)
96 		return ret;
97 
98 	obj->loadaddr = le32_to_cpu(bde.bigdirload);
99 	obj->execaddr = le32_to_cpu(bde.bigdirexec);
100 	obj->size     = le32_to_cpu(bde.bigdirlen);
101 	obj->indaddr  = le32_to_cpu(bde.bigdirindaddr);
102 	obj->attr     = le32_to_cpu(bde.bigdirattr);
103 	obj->name_len = le32_to_cpu(bde.bigdirobnamelen);
104 
105 	offset = offsetof(struct adfs_bigdirheader, bigdirname);
106 	offset += ((le32_to_cpu(h->bigdirnamelen) + 4) & ~3);
107 	offset += le32_to_cpu(h->bigdirentries) * sizeof(struct adfs_bigdirentry);
108 	offset += le32_to_cpu(bde.bigdirobnameptr);
109 
110 	ret = adfs_dir_copyfrom(obj->name, dir, offset, obj->name_len);
111 	if (ret)
112 		return ret;
113 
114 	adfs_object_fixup(dir, obj);
115 
116 	dir->pos += 1;
117 
118 	return 0;
119 }
120 
121 const struct adfs_dir_ops adfs_fplus_dir_ops = {
122 	.read		= adfs_fplus_read,
123 	.setpos		= adfs_fplus_setpos,
124 	.getnext	= adfs_fplus_getnext,
125 };
126