~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/fs/adfs/dir_fplus.c

Version: ~ [ linux-5.14-rc1 ] ~ [ linux-5.13.1 ] ~ [ linux-5.12.16 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.49 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.131 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.197 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.239 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.275 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.275 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.18.140 ] ~ [ linux-3.16.85 ] ~ [ linux-3.14.79 ] ~ [ linux-3.12.74 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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 */
 11 static 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 
 19 static 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 
 59 static 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 
 70 static 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 
103 static 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 
152 out:
153         adfs_dir_relse(dir);
154 
155         return ret;
156 }
157 
158 static int
159 adfs_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 
171 static int
172 adfs_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 
210 static 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 
230 static 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 
261 static 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 
280 const 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 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp