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