Blame view

fs/adfs/dir_fplus.c 6.24 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
  /*
   *  linux/fs/adfs/dir_fplus.c
   *
   *  Copyright (C) 1997-1999 Russell King
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License version 2 as
   * published by the Free Software Foundation.
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
10
  #include <linux/buffer_head.h>
2f09719af   Stuart Swales   adfs: fix E+/F+ d...
11
  #include <linux/slab.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
12
13
14
15
16
17
18
19
20
21
22
23
24
  #include "adfs.h"
  #include "dir_fplus.h"
  
  static int
  adfs_fplus_read(struct super_block *sb, unsigned int id, unsigned int sz, struct adfs_dir *dir)
  {
  	struct adfs_bigdirheader *h;
  	struct adfs_bigdirtail *t;
  	unsigned long block;
  	unsigned int blk, size;
  	int i, ret = -EIO;
  
  	dir->nr_buffers = 0;
2f09719af   Stuart Swales   adfs: fix E+/F+ d...
25
26
  	/* start off using fixed bh set - only alloc for big dirs */
  	dir->bh_fplus = &dir->bh[0];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
27
28
29
30
31
  	block = __adfs_block_map(sb, id, 0);
  	if (!block) {
  		adfs_error(sb, "dir object %X has a hole at offset 0", id);
  		goto out;
  	}
2f09719af   Stuart Swales   adfs: fix E+/F+ d...
32
33
  	dir->bh_fplus[0] = sb_bread(sb, block);
  	if (!dir->bh_fplus[0])
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
34
35
  		goto out;
  	dir->nr_buffers += 1;
2f09719af   Stuart Swales   adfs: fix E+/F+ d...
36
  	h = (struct adfs_bigdirheader *)dir->bh_fplus[0]->b_data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
37
38
  	size = le32_to_cpu(h->bigdirsize);
  	if (size != sz) {
2f09719af   Stuart Swales   adfs: fix E+/F+ d...
39
40
41
42
43
44
  		printk(KERN_WARNING "adfs: adfs_fplus_read:"
  					" directory header size %X
  "
  					" does not match directory size %X
  ",
  					size, sz);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
45
46
47
48
  	}
  
  	if (h->bigdirversion[0] != 0 || h->bigdirversion[1] != 0 ||
  	    h->bigdirversion[2] != 0 || size & 2047 ||
2f09719af   Stuart Swales   adfs: fix E+/F+ d...
49
50
51
52
  	    h->bigdirstartname != cpu_to_le32(BIGDIRSTARTNAME)) {
  		printk(KERN_WARNING "adfs: dir object %X has"
  					" malformed dir header
  ", id);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
53
  		goto out;
2f09719af   Stuart Swales   adfs: fix E+/F+ d...
54
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
55
56
  
  	size >>= sb->s_blocksize_bits;
2f09719af   Stuart Swales   adfs: fix E+/F+ d...
57
58
59
60
61
62
63
64
65
66
67
68
69
70
  	if (size > sizeof(dir->bh)/sizeof(dir->bh[0])) {
  		/* this directory is too big for fixed bh set, must allocate */
  		struct buffer_head **bh_fplus =
  			kzalloc(size * sizeof(struct buffer_head *),
  				GFP_KERNEL);
  		if (!bh_fplus) {
  			adfs_error(sb, "not enough memory for"
  					" dir object %X (%d blocks)", id, size);
  			goto out;
  		}
  		dir->bh_fplus = bh_fplus;
  		/* copy over the pointer to the block that we've already read */
  		dir->bh_fplus[0] = dir->bh[0];
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
71
72
73
74
75
76
  	for (blk = 1; blk < size; blk++) {
  		block = __adfs_block_map(sb, id, blk);
  		if (!block) {
  			adfs_error(sb, "dir object %X has a hole at offset %d", id, blk);
  			goto out;
  		}
2f09719af   Stuart Swales   adfs: fix E+/F+ d...
77
78
79
80
81
  		dir->bh_fplus[blk] = sb_bread(sb, block);
  		if (!dir->bh_fplus[blk]) {
  			adfs_error(sb,	"dir object %X failed read for"
  					" offset %d, mapped block %X",
  					id, blk, block);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
82
  			goto out;
2f09719af   Stuart Swales   adfs: fix E+/F+ d...
83
84
85
  		}
  
  		dir->nr_buffers += 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
86
  	}
2f09719af   Stuart Swales   adfs: fix E+/F+ d...
87
88
  	t = (struct adfs_bigdirtail *)
  		(dir->bh_fplus[size - 1]->b_data + (sb->s_blocksize - 8));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
89
90
91
  
  	if (t->bigdirendname != cpu_to_le32(BIGDIRENDNAME) ||
  	    t->bigdirendmasseq != h->startmasseq ||
2f09719af   Stuart Swales   adfs: fix E+/F+ d...
92
93
94
95
  	    t->reserved[0] != 0 || t->reserved[1] != 0) {
  		printk(KERN_WARNING "adfs: dir object %X has "
  					"malformed dir end
  ", id);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
96
  		goto out;
2f09719af   Stuart Swales   adfs: fix E+/F+ d...
97
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
98
99
100
101
  
  	dir->parent_id = le32_to_cpu(h->bigdirparent);
  	dir->sb = sb;
  	return 0;
2f09719af   Stuart Swales   adfs: fix E+/F+ d...
102

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
103
  out:
2f09719af   Stuart Swales   adfs: fix E+/F+ d...
104
105
106
107
108
109
110
111
112
113
114
  	if (dir->bh_fplus) {
  		for (i = 0; i < dir->nr_buffers; i++)
  			brelse(dir->bh_fplus[i]);
  
  		if (&dir->bh[0] != dir->bh_fplus)
  			kfree(dir->bh_fplus);
  
  		dir->bh_fplus = NULL;
  	}
  
  	dir->nr_buffers = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
115
116
117
118
119
120
121
  	dir->sb = NULL;
  	return ret;
  }
  
  static int
  adfs_fplus_setpos(struct adfs_dir *dir, unsigned int fpos)
  {
2f09719af   Stuart Swales   adfs: fix E+/F+ d...
122
123
  	struct adfs_bigdirheader *h =
  		(struct adfs_bigdirheader *) dir->bh_fplus[0]->b_data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
  	int ret = -ENOENT;
  
  	if (fpos <= le32_to_cpu(h->bigdirentries)) {
  		dir->pos = fpos;
  		ret = 0;
  	}
  
  	return ret;
  }
  
  static void
  dir_memcpy(struct adfs_dir *dir, unsigned int offset, void *to, int len)
  {
  	struct super_block *sb = dir->sb;
  	unsigned int buffer, partial, remainder;
  
  	buffer = offset >> sb->s_blocksize_bits;
  	offset &= sb->s_blocksize - 1;
  
  	partial = sb->s_blocksize - offset;
  
  	if (partial >= len)
2f09719af   Stuart Swales   adfs: fix E+/F+ d...
146
  		memcpy(to, dir->bh_fplus[buffer]->b_data + offset, len);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
147
148
149
150
  	else {
  		char *c = (char *)to;
  
  		remainder = len - partial;
2f09719af   Stuart Swales   adfs: fix E+/F+ d...
151
152
153
154
155
156
157
  		memcpy(c,
  			dir->bh_fplus[buffer]->b_data + offset,
  			partial);
  
  		memcpy(c + partial,
  			dir->bh_fplus[buffer + 1]->b_data,
  			remainder);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
158
159
160
161
162
163
  	}
  }
  
  static int
  adfs_fplus_getnext(struct adfs_dir *dir, struct object_info *obj)
  {
2f09719af   Stuart Swales   adfs: fix E+/F+ d...
164
165
  	struct adfs_bigdirheader *h =
  		(struct adfs_bigdirheader *) dir->bh_fplus[0]->b_data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
  	struct adfs_bigdirentry bde;
  	unsigned int offset;
  	int i, ret = -ENOENT;
  
  	if (dir->pos >= le32_to_cpu(h->bigdirentries))
  		goto out;
  
  	offset = offsetof(struct adfs_bigdirheader, bigdirname);
  	offset += ((le32_to_cpu(h->bigdirnamelen) + 4) & ~3);
  	offset += dir->pos * sizeof(struct adfs_bigdirentry);
  
  	dir_memcpy(dir, offset, &bde, sizeof(struct adfs_bigdirentry));
  
  	obj->loadaddr = le32_to_cpu(bde.bigdirload);
  	obj->execaddr = le32_to_cpu(bde.bigdirexec);
  	obj->size     = le32_to_cpu(bde.bigdirlen);
  	obj->file_id  = le32_to_cpu(bde.bigdirindaddr);
  	obj->attr     = le32_to_cpu(bde.bigdirattr);
  	obj->name_len = le32_to_cpu(bde.bigdirobnamelen);
  
  	offset = offsetof(struct adfs_bigdirheader, bigdirname);
  	offset += ((le32_to_cpu(h->bigdirnamelen) + 4) & ~3);
  	offset += le32_to_cpu(h->bigdirentries) * sizeof(struct adfs_bigdirentry);
  	offset += le32_to_cpu(bde.bigdirobnameptr);
  
  	dir_memcpy(dir, offset, obj->name, obj->name_len);
  	for (i = 0; i < obj->name_len; i++)
  		if (obj->name[i] == '/')
  			obj->name[i] = '.';
da23ef054   Stuart Swales   adfs: add hexadec...
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
  	obj->filetype = -1;
  
  	/*
  	 * object is a file and is filetyped and timestamped?
  	 * RISC OS 12-bit filetype is stored in load_address[19:8]
  	 */
  	if ((0 == (obj->attr & ADFS_NDA_DIRECTORY)) &&
  		(0xfff00000 == (0xfff00000 & obj->loadaddr))) {
  		obj->filetype = (__u16) ((0x000fff00 & obj->loadaddr) >> 8);
  
  		/* optionally append the ,xyz hex filetype suffix */
  		if (ADFS_SB(dir->sb)->s_ftsuffix)
  			obj->name_len +=
  				append_filetype_suffix(
  					&obj->name[obj->name_len],
  					obj->filetype);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
212
213
214
215
216
  	dir->pos += 1;
  	ret = 0;
  out:
  	return ret;
  }
ffdc9064f   Al Viro   repair adfs ->wri...
217
218
219
220
221
222
223
  static int
  adfs_fplus_sync(struct adfs_dir *dir)
  {
  	int err = 0;
  	int i;
  
  	for (i = dir->nr_buffers - 1; i >= 0; i--) {
2f09719af   Stuart Swales   adfs: fix E+/F+ d...
224
  		struct buffer_head *bh = dir->bh_fplus[i];
ffdc9064f   Al Viro   repair adfs ->wri...
225
226
227
228
229
230
231
  		sync_dirty_buffer(bh);
  		if (buffer_req(bh) && !buffer_uptodate(bh))
  			err = -EIO;
  	}
  
  	return err;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
232
233
234
235
  static void
  adfs_fplus_free(struct adfs_dir *dir)
  {
  	int i;
2f09719af   Stuart Swales   adfs: fix E+/F+ d...
236
237
238
239
240
241
242
243
244
245
246
  	if (dir->bh_fplus) {
  		for (i = 0; i < dir->nr_buffers; i++)
  			brelse(dir->bh_fplus[i]);
  
  		if (&dir->bh[0] != dir->bh_fplus)
  			kfree(dir->bh_fplus);
  
  		dir->bh_fplus = NULL;
  	}
  
  	dir->nr_buffers = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
247
248
249
250
251
252
253
  	dir->sb = NULL;
  }
  
  struct adfs_dir_ops adfs_fplus_dir_ops = {
  	.read		= adfs_fplus_read,
  	.setpos		= adfs_fplus_setpos,
  	.getnext	= adfs_fplus_getnext,
ffdc9064f   Al Viro   repair adfs ->wri...
254
  	.sync		= adfs_fplus_sync,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
255
256
  	.free		= adfs_fplus_free
  };