Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  *  linux/fs/adfs/dir_fplus.c
0004  *
0005  *  Copyright (C) 1997-1999 Russell King
0006  */
0007 #include "adfs.h"
0008 #include "dir_fplus.h"
0009 
0010 /* Return the byte offset to directory entry pos */
0011 static unsigned int adfs_fplus_offset(const struct adfs_bigdirheader *h,
0012                       unsigned int pos)
0013 {
0014     return offsetof(struct adfs_bigdirheader, bigdirname) +
0015            ALIGN(le32_to_cpu(h->bigdirnamelen), 4) +
0016            pos * sizeof(struct adfs_bigdirentry);
0017 }
0018 
0019 static int adfs_fplus_validate_header(const struct adfs_bigdirheader *h)
0020 {
0021     unsigned int size = le32_to_cpu(h->bigdirsize);
0022     unsigned int len;
0023 
0024     if (h->bigdirversion[0] != 0 || h->bigdirversion[1] != 0 ||
0025         h->bigdirversion[2] != 0 ||
0026         h->bigdirstartname != cpu_to_le32(BIGDIRSTARTNAME) ||
0027         !size || size & 2047 || size > SZ_4M)
0028         return -EIO;
0029 
0030     size -= sizeof(struct adfs_bigdirtail) +
0031         offsetof(struct adfs_bigdirheader, bigdirname);
0032 
0033     /* Check that bigdirnamelen fits within the directory */
0034     len = ALIGN(le32_to_cpu(h->bigdirnamelen), 4);
0035     if (len > size)
0036         return -EIO;
0037 
0038     size -= len;
0039 
0040     /* Check that bigdirnamesize fits within the directory */
0041     len = le32_to_cpu(h->bigdirnamesize);
0042     if (len > size)
0043         return -EIO;
0044 
0045     size -= len;
0046 
0047     /*
0048      * Avoid division, we know that absolute maximum number of entries
0049      * can not be so large to cause overflow of the multiplication below.
0050      */
0051     len = le32_to_cpu(h->bigdirentries);
0052     if (len > SZ_4M / sizeof(struct adfs_bigdirentry) ||
0053         len * sizeof(struct adfs_bigdirentry) > size)
0054         return -EIO;
0055 
0056     return 0;
0057 }
0058 
0059 static int adfs_fplus_validate_tail(const struct adfs_bigdirheader *h,
0060                     const struct adfs_bigdirtail *t)
0061 {
0062     if (t->bigdirendname != cpu_to_le32(BIGDIRENDNAME) ||
0063         t->bigdirendmasseq != h->startmasseq ||
0064         t->reserved[0] != 0 || t->reserved[1] != 0)
0065         return -EIO;
0066 
0067     return 0;
0068 }
0069 
0070 static u8 adfs_fplus_checkbyte(struct adfs_dir *dir)
0071 {
0072     struct adfs_bigdirheader *h = dir->bighead;
0073     struct adfs_bigdirtail *t = dir->bigtail;
0074     unsigned int end, bs, bi, i;
0075     __le32 *bp;
0076     u32 dircheck;
0077 
0078     end = adfs_fplus_offset(h, le32_to_cpu(h->bigdirentries)) +
0079         le32_to_cpu(h->bigdirnamesize);
0080 
0081     /* Accumulate the contents of the header, entries and names */
0082     for (dircheck = 0, bi = 0; end; bi++) {
0083         bp = (void *)dir->bhs[bi]->b_data;
0084         bs = dir->bhs[bi]->b_size;
0085         if (bs > end)
0086             bs = end;
0087 
0088         for (i = 0; i < bs; i += sizeof(u32))
0089             dircheck = ror32(dircheck, 13) ^ le32_to_cpup(bp++);
0090 
0091         end -= bs;
0092     }
0093 
0094     /* Accumulate the contents of the tail except for the check byte */
0095     dircheck = ror32(dircheck, 13) ^ le32_to_cpu(t->bigdirendname);
0096     dircheck = ror32(dircheck, 13) ^ t->bigdirendmasseq;
0097     dircheck = ror32(dircheck, 13) ^ t->reserved[0];
0098     dircheck = ror32(dircheck, 13) ^ t->reserved[1];
0099 
0100     return dircheck ^ dircheck >> 8 ^ dircheck >> 16 ^ dircheck >> 24;
0101 }
0102 
0103 static int adfs_fplus_read(struct super_block *sb, u32 indaddr,
0104                unsigned int size, struct adfs_dir *dir)
0105 {
0106     struct adfs_bigdirheader *h;
0107     struct adfs_bigdirtail *t;
0108     unsigned int dirsize;
0109     int ret;
0110 
0111     /* Read first buffer */
0112     ret = adfs_dir_read_buffers(sb, indaddr, sb->s_blocksize, dir);
0113     if (ret)
0114         return ret;
0115 
0116     dir->bighead = h = (void *)dir->bhs[0]->b_data;
0117     ret = adfs_fplus_validate_header(h);
0118     if (ret) {
0119         adfs_error(sb, "dir %06x has malformed header", indaddr);
0120         goto out;
0121     }
0122 
0123     dirsize = le32_to_cpu(h->bigdirsize);
0124     if (size && dirsize != size) {
0125         adfs_msg(sb, KERN_WARNING,
0126              "dir %06x header size %X does not match directory size %X",
0127              indaddr, dirsize, size);
0128     }
0129 
0130     /* Read remaining buffers */
0131     ret = adfs_dir_read_buffers(sb, indaddr, dirsize, dir);
0132     if (ret)
0133         return ret;
0134 
0135     dir->bigtail = t = (struct adfs_bigdirtail *)
0136         (dir->bhs[dir->nr_buffers - 1]->b_data + (sb->s_blocksize - 8));
0137 
0138     ret = adfs_fplus_validate_tail(h, t);
0139     if (ret) {
0140         adfs_error(sb, "dir %06x has malformed tail", indaddr);
0141         goto out;
0142     }
0143 
0144     if (adfs_fplus_checkbyte(dir) != t->bigdircheckbyte) {
0145         adfs_error(sb, "dir %06x checkbyte mismatch\n", indaddr);
0146         goto out;
0147     }
0148 
0149     dir->parent_id = le32_to_cpu(h->bigdirparent);
0150     return 0;
0151 
0152 out:
0153     adfs_dir_relse(dir);
0154 
0155     return ret;
0156 }
0157 
0158 static int
0159 adfs_fplus_setpos(struct adfs_dir *dir, unsigned int fpos)
0160 {
0161     int ret = -ENOENT;
0162 
0163     if (fpos <= le32_to_cpu(dir->bighead->bigdirentries)) {
0164         dir->pos = fpos;
0165         ret = 0;
0166     }
0167 
0168     return ret;
0169 }
0170 
0171 static int
0172 adfs_fplus_getnext(struct adfs_dir *dir, struct object_info *obj)
0173 {
0174     struct adfs_bigdirheader *h = dir->bighead;
0175     struct adfs_bigdirentry bde;
0176     unsigned int offset;
0177     int ret;
0178 
0179     if (dir->pos >= le32_to_cpu(h->bigdirentries))
0180         return -ENOENT;
0181 
0182     offset = adfs_fplus_offset(h, dir->pos);
0183 
0184     ret = adfs_dir_copyfrom(&bde, dir, offset,
0185                 sizeof(struct adfs_bigdirentry));
0186     if (ret)
0187         return ret;
0188 
0189     obj->loadaddr = le32_to_cpu(bde.bigdirload);
0190     obj->execaddr = le32_to_cpu(bde.bigdirexec);
0191     obj->size     = le32_to_cpu(bde.bigdirlen);
0192     obj->indaddr  = le32_to_cpu(bde.bigdirindaddr);
0193     obj->attr     = le32_to_cpu(bde.bigdirattr);
0194     obj->name_len = le32_to_cpu(bde.bigdirobnamelen);
0195 
0196     offset = adfs_fplus_offset(h, le32_to_cpu(h->bigdirentries));
0197     offset += le32_to_cpu(bde.bigdirobnameptr);
0198 
0199     ret = adfs_dir_copyfrom(obj->name, dir, offset, obj->name_len);
0200     if (ret)
0201         return ret;
0202 
0203     adfs_object_fixup(dir, obj);
0204 
0205     dir->pos += 1;
0206 
0207     return 0;
0208 }
0209 
0210 static int adfs_fplus_iterate(struct adfs_dir *dir, struct dir_context *ctx)
0211 {
0212     struct object_info obj;
0213 
0214     if ((ctx->pos - 2) >> 32)
0215         return 0;
0216 
0217     if (adfs_fplus_setpos(dir, ctx->pos - 2))
0218         return 0;
0219 
0220     while (!adfs_fplus_getnext(dir, &obj)) {
0221         if (!dir_emit(ctx, obj.name, obj.name_len,
0222                   obj.indaddr, DT_UNKNOWN))
0223             break;
0224         ctx->pos++;
0225     }
0226 
0227     return 0;
0228 }
0229 
0230 static int adfs_fplus_update(struct adfs_dir *dir, struct object_info *obj)
0231 {
0232     struct adfs_bigdirheader *h = dir->bighead;
0233     struct adfs_bigdirentry bde;
0234     int offset, end, ret;
0235 
0236     offset = adfs_fplus_offset(h, 0) - sizeof(bde);
0237     end = adfs_fplus_offset(h, le32_to_cpu(h->bigdirentries));
0238 
0239     do {
0240         offset += sizeof(bde);
0241         if (offset >= end) {
0242             adfs_error(dir->sb, "unable to locate entry to update");
0243             return -ENOENT;
0244         }
0245         ret = adfs_dir_copyfrom(&bde, dir, offset, sizeof(bde));
0246         if (ret) {
0247             adfs_error(dir->sb, "error reading directory entry");
0248             return -ENOENT;
0249         }
0250     } while (le32_to_cpu(bde.bigdirindaddr) != obj->indaddr);
0251 
0252     bde.bigdirload    = cpu_to_le32(obj->loadaddr);
0253     bde.bigdirexec    = cpu_to_le32(obj->execaddr);
0254     bde.bigdirlen     = cpu_to_le32(obj->size);
0255     bde.bigdirindaddr = cpu_to_le32(obj->indaddr);
0256     bde.bigdirattr    = cpu_to_le32(obj->attr);
0257 
0258     return adfs_dir_copyto(dir, offset, &bde, sizeof(bde));
0259 }
0260 
0261 static int adfs_fplus_commit(struct adfs_dir *dir)
0262 {
0263     int ret;
0264 
0265     /* Increment directory sequence number */
0266     dir->bighead->startmasseq += 1;
0267     dir->bigtail->bigdirendmasseq += 1;
0268 
0269     /* Update directory check byte */
0270     dir->bigtail->bigdircheckbyte = adfs_fplus_checkbyte(dir);
0271 
0272     /* Make sure the directory still validates correctly */
0273     ret = adfs_fplus_validate_header(dir->bighead);
0274     if (ret == 0)
0275         ret = adfs_fplus_validate_tail(dir->bighead, dir->bigtail);
0276 
0277     return ret;
0278 }
0279 
0280 const struct adfs_dir_ops adfs_fplus_dir_ops = {
0281     .read       = adfs_fplus_read,
0282     .iterate    = adfs_fplus_iterate,
0283     .setpos     = adfs_fplus_setpos,
0284     .getnext    = adfs_fplus_getnext,
0285     .update     = adfs_fplus_update,
0286     .commit     = adfs_fplus_commit,
0287 };