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 "adfs.h"
  12#include "dir_fplus.h"
  13
  14static int
  15adfs_fplus_read(struct super_block *sb, unsigned int id, unsigned int sz, struct adfs_dir *dir)
  16{
  17        struct adfs_bigdirheader *h;
  18        struct adfs_bigdirtail *t;
  19        unsigned long block;
  20        unsigned int blk, size;
  21        int i, ret = -EIO;
  22
  23        dir->nr_buffers = 0;
  24
  25        block = __adfs_block_map(sb, id, 0);
  26        if (!block) {
  27                adfs_error(sb, "dir object %X has a hole at offset 0", id);
  28                goto out;
  29        }
  30
  31        dir->bh[0] = sb_bread(sb, block);
  32        if (!dir->bh[0])
  33                goto out;
  34        dir->nr_buffers += 1;
  35
  36        h = (struct adfs_bigdirheader *)dir->bh[0]->b_data;
  37        size = le32_to_cpu(h->bigdirsize);
  38        if (size != sz) {
  39                printk(KERN_WARNING "adfs: adfs_fplus_read: directory header size\n"
  40                                " does not match directory size\n");
  41        }
  42
  43        if (h->bigdirversion[0] != 0 || h->bigdirversion[1] != 0 ||
  44            h->bigdirversion[2] != 0 || size & 2047 ||
  45            h->bigdirstartname != cpu_to_le32(BIGDIRSTARTNAME))
  46                goto out;
  47
  48        size >>= sb->s_blocksize_bits;
  49        for (blk = 1; blk < size; blk++) {
  50                block = __adfs_block_map(sb, id, blk);
  51                if (!block) {
  52                        adfs_error(sb, "dir object %X has a hole at offset %d", id, blk);
  53                        goto out;
  54                }
  55
  56                dir->bh[blk] = sb_bread(sb, block);
  57                if (!dir->bh[blk])
  58                        goto out;
  59                dir->nr_buffers = blk;
  60        }
  61
  62        t = (struct adfs_bigdirtail *)(dir->bh[size - 1]->b_data + (sb->s_blocksize - 8));
  63
  64        if (t->bigdirendname != cpu_to_le32(BIGDIRENDNAME) ||
  65            t->bigdirendmasseq != h->startmasseq ||
  66            t->reserved[0] != 0 || t->reserved[1] != 0)
  67                goto out;
  68
  69        dir->parent_id = le32_to_cpu(h->bigdirparent);
  70        dir->sb = sb;
  71        return 0;
  72out:
  73        for (i = 0; i < dir->nr_buffers; i++)
  74                brelse(dir->bh[i]);
  75        dir->sb = NULL;
  76        return ret;
  77}
  78
  79static int
  80adfs_fplus_setpos(struct adfs_dir *dir, unsigned int fpos)
  81{
  82        struct adfs_bigdirheader *h = (struct adfs_bigdirheader *)dir->bh[0]->b_data;
  83        int ret = -ENOENT;
  84
  85        if (fpos <= le32_to_cpu(h->bigdirentries)) {
  86                dir->pos = fpos;
  87                ret = 0;
  88        }
  89
  90        return ret;
  91}
  92
  93static void
  94dir_memcpy(struct adfs_dir *dir, unsigned int offset, void *to, int len)
  95{
  96        struct super_block *sb = dir->sb;
  97        unsigned int buffer, partial, remainder;
  98
  99        buffer = offset >> sb->s_blocksize_bits;
 100        offset &= sb->s_blocksize - 1;
 101
 102        partial = sb->s_blocksize - offset;
 103
 104        if (partial >= len)
 105                memcpy(to, dir->bh[buffer]->b_data + offset, len);
 106        else {
 107                char *c = (char *)to;
 108
 109                remainder = len - partial;
 110
 111                memcpy(c, dir->bh[buffer]->b_data + offset, partial);
 112                memcpy(c + partial, dir->bh[buffer + 1]->b_data, remainder);
 113        }
 114}
 115
 116static int
 117adfs_fplus_getnext(struct adfs_dir *dir, struct object_info *obj)
 118{
 119        struct adfs_bigdirheader *h = (struct adfs_bigdirheader *)dir->bh[0]->b_data;
 120        struct adfs_bigdirentry bde;
 121        unsigned int offset;
 122        int i, ret = -ENOENT;
 123
 124        if (dir->pos >= le32_to_cpu(h->bigdirentries))
 125                goto out;
 126
 127        offset = offsetof(struct adfs_bigdirheader, bigdirname);
 128        offset += ((le32_to_cpu(h->bigdirnamelen) + 4) & ~3);
 129        offset += dir->pos * sizeof(struct adfs_bigdirentry);
 130
 131        dir_memcpy(dir, offset, &bde, sizeof(struct adfs_bigdirentry));
 132
 133        obj->loadaddr = le32_to_cpu(bde.bigdirload);
 134        obj->execaddr = le32_to_cpu(bde.bigdirexec);
 135        obj->size     = le32_to_cpu(bde.bigdirlen);
 136        obj->file_id  = le32_to_cpu(bde.bigdirindaddr);
 137        obj->attr     = le32_to_cpu(bde.bigdirattr);
 138        obj->name_len = le32_to_cpu(bde.bigdirobnamelen);
 139
 140        offset = offsetof(struct adfs_bigdirheader, bigdirname);
 141        offset += ((le32_to_cpu(h->bigdirnamelen) + 4) & ~3);
 142        offset += le32_to_cpu(h->bigdirentries) * sizeof(struct adfs_bigdirentry);
 143        offset += le32_to_cpu(bde.bigdirobnameptr);
 144
 145        dir_memcpy(dir, offset, obj->name, obj->name_len);
 146        for (i = 0; i < obj->name_len; i++)
 147                if (obj->name[i] == '/')
 148                        obj->name[i] = '.';
 149
 150        dir->pos += 1;
 151        ret = 0;
 152out:
 153        return ret;
 154}
 155
 156static int
 157adfs_fplus_sync(struct adfs_dir *dir)
 158{
 159        int err = 0;
 160        int i;
 161
 162        for (i = dir->nr_buffers - 1; i >= 0; i--) {
 163                struct buffer_head *bh = dir->bh[i];
 164                sync_dirty_buffer(bh);
 165                if (buffer_req(bh) && !buffer_uptodate(bh))
 166                        err = -EIO;
 167        }
 168
 169        return err;
 170}
 171
 172static void
 173adfs_fplus_free(struct adfs_dir *dir)
 174{
 175        int i;
 176
 177        for (i = 0; i < dir->nr_buffers; i++)
 178                brelse(dir->bh[i]);
 179        dir->sb = NULL;
 180}
 181
 182struct adfs_dir_ops adfs_fplus_dir_ops = {
 183        .read           = adfs_fplus_read,
 184        .setpos         = adfs_fplus_setpos,
 185        .getnext        = adfs_fplus_getnext,
 186        .sync           = adfs_fplus_sync,
 187        .free           = adfs_fplus_free
 188};
 189