linux/fs/adfs/dir_fplus.c
<<
>>
Prefs
   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 <linux/buffer_head.h>
   8#include <linux/slab.h>
   9#include "adfs.h"
  10#include "dir_fplus.h"
  11
  12static int
  13adfs_fplus_read(struct super_block *sb, unsigned int id, unsigned int sz, struct adfs_dir *dir)
  14{
  15        struct adfs_bigdirheader *h;
  16        struct adfs_bigdirtail *t;
  17        unsigned long block;
  18        unsigned int blk, size;
  19        int i, ret = -EIO;
  20
  21        dir->nr_buffers = 0;
  22
  23        /* start off using fixed bh set - only alloc for big dirs */
  24        dir->bh_fplus = &dir->bh[0];
  25
  26        block = __adfs_block_map(sb, id, 0);
  27        if (!block) {
  28                adfs_error(sb, "dir object %X has a hole at offset 0", id);
  29                goto out;
  30        }
  31
  32        dir->bh_fplus[0] = sb_bread(sb, block);
  33        if (!dir->bh_fplus[0])
  34                goto out;
  35        dir->nr_buffers += 1;
  36
  37        h = (struct adfs_bigdirheader *)dir->bh_fplus[0]->b_data;
  38        size = le32_to_cpu(h->bigdirsize);
  39        if (size != sz) {
  40                printk(KERN_WARNING "adfs: adfs_fplus_read:"
  41                                        " directory header size %X\n"
  42                                        " does not match directory size %X\n",
  43                                        size, sz);
  44        }
  45
  46        if (h->bigdirversion[0] != 0 || h->bigdirversion[1] != 0 ||
  47            h->bigdirversion[2] != 0 || size & 2047 ||
  48            h->bigdirstartname != cpu_to_le32(BIGDIRSTARTNAME)) {
  49                printk(KERN_WARNING "adfs: dir object %X has"
  50                                        " malformed dir header\n", id);
  51                goto out;
  52        }
  53
  54        size >>= sb->s_blocksize_bits;
  55        if (size > ARRAY_SIZE(dir->bh)) {
  56                /* this directory is too big for fixed bh set, must allocate */
  57                struct buffer_head **bh_fplus =
  58                        kcalloc(size, sizeof(struct buffer_head *),
  59                                GFP_KERNEL);
  60                if (!bh_fplus) {
  61                        ret = -ENOMEM;
  62                        adfs_error(sb, "not enough memory for"
  63                                        " dir object %X (%d blocks)", id, size);
  64                        goto out;
  65                }
  66                dir->bh_fplus = bh_fplus;
  67                /* copy over the pointer to the block that we've already read */
  68                dir->bh_fplus[0] = dir->bh[0];
  69        }
  70
  71        for (blk = 1; blk < size; blk++) {
  72                block = __adfs_block_map(sb, id, blk);
  73                if (!block) {
  74                        adfs_error(sb, "dir object %X has a hole at offset %d", id, blk);
  75                        goto out;
  76                }
  77
  78                dir->bh_fplus[blk] = sb_bread(sb, block);
  79                if (!dir->bh_fplus[blk]) {
  80                        adfs_error(sb,  "dir object %x failed read for offset %d, mapped block %lX",
  81                                   id, blk, block);
  82                        goto out;
  83                }
  84
  85                dir->nr_buffers += 1;
  86        }
  87
  88        t = (struct adfs_bigdirtail *)
  89                (dir->bh_fplus[size - 1]->b_data + (sb->s_blocksize - 8));
  90
  91        if (t->bigdirendname != cpu_to_le32(BIGDIRENDNAME) ||
  92            t->bigdirendmasseq != h->startmasseq ||
  93            t->reserved[0] != 0 || t->reserved[1] != 0) {
  94                printk(KERN_WARNING "adfs: dir object %X has "
  95                                        "malformed dir end\n", id);
  96                goto out;
  97        }
  98
  99        dir->parent_id = le32_to_cpu(h->bigdirparent);
 100        dir->sb = sb;
 101        return 0;
 102
 103out:
 104        if (dir->bh_fplus) {
 105                for (i = 0; i < dir->nr_buffers; i++)
 106                        brelse(dir->bh_fplus[i]);
 107
 108                if (&dir->bh[0] != dir->bh_fplus)
 109                        kfree(dir->bh_fplus);
 110
 111                dir->bh_fplus = NULL;
 112        }
 113
 114        dir->nr_buffers = 0;
 115        dir->sb = NULL;
 116        return ret;
 117}
 118
 119static int
 120adfs_fplus_setpos(struct adfs_dir *dir, unsigned int fpos)
 121{
 122        struct adfs_bigdirheader *h =
 123                (struct adfs_bigdirheader *) dir->bh_fplus[0]->b_data;
 124        int ret = -ENOENT;
 125
 126        if (fpos <= le32_to_cpu(h->bigdirentries)) {
 127                dir->pos = fpos;
 128                ret = 0;
 129        }
 130
 131        return ret;
 132}
 133
 134static void
 135dir_memcpy(struct adfs_dir *dir, unsigned int offset, void *to, int len)
 136{
 137        struct super_block *sb = dir->sb;
 138        unsigned int buffer, partial, remainder;
 139
 140        buffer = offset >> sb->s_blocksize_bits;
 141        offset &= sb->s_blocksize - 1;
 142
 143        partial = sb->s_blocksize - offset;
 144
 145        if (partial >= len)
 146                memcpy(to, dir->bh_fplus[buffer]->b_data + offset, len);
 147        else {
 148                char *c = (char *)to;
 149
 150                remainder = len - partial;
 151
 152                memcpy(c,
 153                        dir->bh_fplus[buffer]->b_data + offset,
 154                        partial);
 155
 156                memcpy(c + partial,
 157                        dir->bh_fplus[buffer + 1]->b_data,
 158                        remainder);
 159        }
 160}
 161
 162static int
 163adfs_fplus_getnext(struct adfs_dir *dir, struct object_info *obj)
 164{
 165        struct adfs_bigdirheader *h =
 166                (struct adfs_bigdirheader *) dir->bh_fplus[0]->b_data;
 167        struct adfs_bigdirentry bde;
 168        unsigned int offset;
 169        int ret = -ENOENT;
 170
 171        if (dir->pos >= le32_to_cpu(h->bigdirentries))
 172                goto out;
 173
 174        offset = offsetof(struct adfs_bigdirheader, bigdirname);
 175        offset += ((le32_to_cpu(h->bigdirnamelen) + 4) & ~3);
 176        offset += dir->pos * sizeof(struct adfs_bigdirentry);
 177
 178        dir_memcpy(dir, offset, &bde, sizeof(struct adfs_bigdirentry));
 179
 180        obj->loadaddr = le32_to_cpu(bde.bigdirload);
 181        obj->execaddr = le32_to_cpu(bde.bigdirexec);
 182        obj->size     = le32_to_cpu(bde.bigdirlen);
 183        obj->file_id  = le32_to_cpu(bde.bigdirindaddr);
 184        obj->attr     = le32_to_cpu(bde.bigdirattr);
 185        obj->name_len = le32_to_cpu(bde.bigdirobnamelen);
 186
 187        offset = offsetof(struct adfs_bigdirheader, bigdirname);
 188        offset += ((le32_to_cpu(h->bigdirnamelen) + 4) & ~3);
 189        offset += le32_to_cpu(h->bigdirentries) * sizeof(struct adfs_bigdirentry);
 190        offset += le32_to_cpu(bde.bigdirobnameptr);
 191
 192        dir_memcpy(dir, offset, obj->name, obj->name_len);
 193        adfs_object_fixup(dir, obj);
 194
 195        dir->pos += 1;
 196        ret = 0;
 197out:
 198        return ret;
 199}
 200
 201static int
 202adfs_fplus_sync(struct adfs_dir *dir)
 203{
 204        int err = 0;
 205        int i;
 206
 207        for (i = dir->nr_buffers - 1; i >= 0; i--) {
 208                struct buffer_head *bh = dir->bh_fplus[i];
 209                sync_dirty_buffer(bh);
 210                if (buffer_req(bh) && !buffer_uptodate(bh))
 211                        err = -EIO;
 212        }
 213
 214        return err;
 215}
 216
 217static void
 218adfs_fplus_free(struct adfs_dir *dir)
 219{
 220        int i;
 221
 222        if (dir->bh_fplus) {
 223                for (i = 0; i < dir->nr_buffers; i++)
 224                        brelse(dir->bh_fplus[i]);
 225
 226                if (&dir->bh[0] != dir->bh_fplus)
 227                        kfree(dir->bh_fplus);
 228
 229                dir->bh_fplus = NULL;
 230        }
 231
 232        dir->nr_buffers = 0;
 233        dir->sb = NULL;
 234}
 235
 236const struct adfs_dir_ops adfs_fplus_dir_ops = {
 237        .read           = adfs_fplus_read,
 238        .setpos         = adfs_fplus_setpos,
 239        .getnext        = adfs_fplus_getnext,
 240        .sync           = adfs_fplus_sync,
 241        .free           = adfs_fplus_free
 242};
 243