]> git.proxmox.com Git - mirror_ubuntu-hirsute-kernel.git/blame - fs/adfs/dir_fplus.c
fs/adfs: bigdir: directory validation strengthening
[mirror_ubuntu-hirsute-kernel.git] / fs / adfs / dir_fplus.c
CommitLineData
d2912cb1 1// SPDX-License-Identifier: GPL-2.0-only
1da177e4
LT
2/*
3 * linux/fs/adfs/dir_fplus.c
4 *
5 * Copyright (C) 1997-1999 Russell King
1da177e4 6 */
1da177e4
LT
7#include "adfs.h"
8#include "dir_fplus.h"
9
0db35a02
RK
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
6674ecab
RK
19static int adfs_fplus_validate_header(const struct adfs_bigdirheader *h)
20{
21 unsigned int size = le32_to_cpu(h->bigdirsize);
aa3d4e01 22 unsigned int len;
6674ecab
RK
23
24 if (h->bigdirversion[0] != 0 || h->bigdirversion[1] != 0 ||
25 h->bigdirversion[2] != 0 ||
26 h->bigdirstartname != cpu_to_le32(BIGDIRSTARTNAME) ||
aa3d4e01
RK
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)
6674ecab
RK
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
419a6e5e
RK
70static int adfs_fplus_read(struct super_block *sb, u32 indaddr,
71 unsigned int size, struct adfs_dir *dir)
1da177e4
LT
72{
73 struct adfs_bigdirheader *h;
74 struct adfs_bigdirtail *t;
419a6e5e
RK
75 unsigned int dirsize;
76 int ret;
1da177e4 77
419a6e5e
RK
78 /* Read first buffer */
79 ret = adfs_dir_read_buffers(sb, indaddr, sb->s_blocksize, dir);
80 if (ret)
81 return ret;
1da177e4 82
016936b3 83 dir->bighead = h = (void *)dir->bhs[0]->b_data;
6674ecab
RK
84 if (adfs_fplus_validate_header(h)) {
85 adfs_error(sb, "dir %06x has malformed header", indaddr);
86 goto out;
87 }
88
419a6e5e
RK
89 dirsize = le32_to_cpu(h->bigdirsize);
90 if (dirsize != size) {
ceb3b106 91 adfs_msg(sb, KERN_WARNING,
419a6e5e
RK
92 "dir %06x header size %X does not match directory size %X",
93 indaddr, dirsize, size);
1da177e4
LT
94 }
95
419a6e5e
RK
96 /* Read remaining buffers */
97 ret = adfs_dir_read_buffers(sb, indaddr, dirsize, dir);
98 if (ret)
99 return ret;
1da177e4 100
016936b3 101 dir->bigtail = t = (struct adfs_bigdirtail *)
419a6e5e 102 (dir->bhs[dir->nr_buffers - 1]->b_data + (sb->s_blocksize - 8));
1da177e4 103
6674ecab
RK
104 ret = adfs_fplus_validate_tail(h, t);
105 if (ret) {
419a6e5e 106 adfs_error(sb, "dir %06x has malformed tail", indaddr);
1da177e4 107 goto out;
2f09719a 108 }
1da177e4
LT
109
110 dir->parent_id = le32_to_cpu(h->bigdirparent);
1da177e4 111 return 0;
2f09719a 112
1da177e4 113out:
1dd9f5ba 114 adfs_dir_relse(dir);
2f09719a 115
1da177e4
LT
116 return ret;
117}
118
119static int
120adfs_fplus_setpos(struct adfs_dir *dir, unsigned int fpos)
121{
1da177e4
LT
122 int ret = -ENOENT;
123
016936b3 124 if (fpos <= le32_to_cpu(dir->bighead->bigdirentries)) {
1da177e4
LT
125 dir->pos = fpos;
126 ret = 0;
127 }
128
129 return ret;
130}
131
1da177e4
LT
132static int
133adfs_fplus_getnext(struct adfs_dir *dir, struct object_info *obj)
134{
016936b3 135 struct adfs_bigdirheader *h = dir->bighead;
1da177e4
LT
136 struct adfs_bigdirentry bde;
137 unsigned int offset;
a317120b 138 int ret;
1da177e4
LT
139
140 if (dir->pos >= le32_to_cpu(h->bigdirentries))
a317120b 141 return -ENOENT;
1da177e4 142
0db35a02 143 offset = adfs_fplus_offset(h, dir->pos);
1da177e4 144
a317120b
RK
145 ret = adfs_dir_copyfrom(&bde, dir, offset,
146 sizeof(struct adfs_bigdirentry));
147 if (ret)
148 return ret;
1da177e4
LT
149
150 obj->loadaddr = le32_to_cpu(bde.bigdirload);
151 obj->execaddr = le32_to_cpu(bde.bigdirexec);
152 obj->size = le32_to_cpu(bde.bigdirlen);
5ed70bb4 153 obj->indaddr = le32_to_cpu(bde.bigdirindaddr);
1da177e4
LT
154 obj->attr = le32_to_cpu(bde.bigdirattr);
155 obj->name_len = le32_to_cpu(bde.bigdirobnamelen);
156
0db35a02 157 offset = adfs_fplus_offset(h, le32_to_cpu(h->bigdirentries));
1da177e4
LT
158 offset += le32_to_cpu(bde.bigdirobnameptr);
159
a317120b
RK
160 ret = adfs_dir_copyfrom(obj->name, dir, offset, obj->name_len);
161 if (ret)
162 return ret;
163
411c49bc 164 adfs_object_fixup(dir, obj);
da23ef05 165
1da177e4 166 dir->pos += 1;
a317120b
RK
167
168 return 0;
1da177e4
LT
169}
170
4287e4de
RK
171static int adfs_fplus_iterate(struct adfs_dir *dir, struct dir_context *ctx)
172{
173 struct object_info obj;
174
175 if ((ctx->pos - 2) >> 32)
176 return 0;
177
178 if (adfs_fplus_setpos(dir, ctx->pos - 2))
179 return 0;
180
181 while (!adfs_fplus_getnext(dir, &obj)) {
182 if (!dir_emit(ctx, obj.name, obj.name_len,
183 obj.indaddr, DT_UNKNOWN))
184 break;
185 ctx->pos++;
186 }
187
188 return 0;
189}
190
0125f504 191const struct adfs_dir_ops adfs_fplus_dir_ops = {
1da177e4 192 .read = adfs_fplus_read,
4287e4de 193 .iterate = adfs_fplus_iterate,
1da177e4
LT
194 .setpos = adfs_fplus_setpos,
195 .getnext = adfs_fplus_getnext,
1da177e4 196};