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