File: /usr/src/linux/fs/adfs/dir_fplus.c

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