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

1     /*
2      *  linux/fs/adfs/dir_f.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      *  E and F format directory handling
11      */
12     #include <linux/version.h>
13     #include <linux/errno.h>
14     #include <linux/fs.h>
15     #include <linux/adfs_fs.h>
16     #include <linux/sched.h>
17     #include <linux/stat.h>
18     #include <linux/spinlock.h>
19     
20     #include "adfs.h"
21     #include "dir_f.h"
22     
23     static void adfs_f_free(struct adfs_dir *dir);
24     
25     /*
26      * Read an (unaligned) value of length 1..4 bytes
27      */
28     static inline unsigned int adfs_readval(unsigned char *p, int len)
29     {
30     	unsigned int val = 0;
31     
32     	switch (len) {
33     	case 4:		val |= p[3] << 24;
34     	case 3:		val |= p[2] << 16;
35     	case 2:		val |= p[1] << 8;
36     	default:	val |= p[0];
37     	}
38     	return val;
39     }
40     
41     static inline void adfs_writeval(unsigned char *p, int len, unsigned int val)
42     {
43     	switch (len) {
44     	case 4:		p[3] = val >> 24;
45     	case 3:		p[2] = val >> 16;
46     	case 2:		p[1] = val >> 8;
47     	default:	p[0] = val;
48     	}
49     }
50     
51     static inline int adfs_readname(char *buf, char *ptr, int maxlen)
52     {
53     	char *old_buf = buf;
54     
55     	while (*ptr >= ' ' && maxlen--) {
56     		if (*ptr == '/')
57     			*buf++ = '.';
58     		else
59     			*buf++ = *ptr;
60     		ptr++;
61     	}
62     	*buf = '\0';
63     
64     	return buf - old_buf;
65     }
66     
67     static inline void adfs_writename(char *to, char *from, int maxlen)
68     {
69     	int i;
70     
71     	for (i = 0; i < maxlen; i++) {
72     		if (from[i] == '\0')
73     			break;
74     		if (from[i] == '.')
75     			to[i] = '/';
76     		else
77     			to[i] = from[i];
78     	}
79     
80     	for (; i < maxlen; i++)
81     		to[i] = '\0';
82     }
83     
84     #define ror13(v) ((v >> 13) | (v << 19))
85     
86     #define dir_u8(idx)				\
87     	({ int _buf = idx >> blocksize_bits;	\
88     	   int _off = idx - (_buf << blocksize_bits);\
89     	  *(u8 *)(bh[_buf]->b_data + _off);	\
90     	})
91     
92     #define dir_u32(idx)				\
93     	({ int _buf = idx >> blocksize_bits;	\
94     	   int _off = idx - (_buf << blocksize_bits);\
95     	  *(u32 *)(bh[_buf]->b_data + _off);	\
96     	})
97     
98     #define bufoff(_bh,_idx)			\
99     	({ int _buf = _idx >> blocksize_bits;	\
100     	   int _off = _idx - (_buf << blocksize_bits);\
101     	  (u8 *)(_bh[_buf]->b_data + _off);	\
102     	})
103     
104     /*
105      * There are some algorithms that are nice in
106      * assembler, but a bitch in C...  This is one
107      * of them.
108      */
109     static u8
110     adfs_dir_checkbyte(const struct adfs_dir *dir)
111     {
112     	struct buffer_head * const *bh = dir->bh;
113     	const int blocksize_bits = dir->sb->s_blocksize_bits;
114     	union { u32 *ptr32; u8 *ptr8; } ptr, end;
115     	u32 dircheck = 0;
116     	int last = 5 - 26;
117     	int i = 0;
118     
119     	/*
120     	 * Accumulate each word up to the last whole
121     	 * word of the last directory entry.  This
122     	 * can spread across several buffer heads.
123     	 */
124     	do {
125     		last += 26;
126     		do {
127     			dircheck = cpu_to_le32(dir_u32(i)) ^ ror13(dircheck);
128     
129     			i += sizeof(u32);
130     		} while (i < (last & ~3));
131     	} while (dir_u8(last) != 0);
132     
133     	/*
134     	 * Accumulate the last few bytes.  These
135     	 * bytes will be within the same bh.
136     	 */
137     	if (i != last) {
138     		ptr.ptr8 = bufoff(bh, i);
139     		end.ptr8 = ptr.ptr8 + last - i;
140     
141     		do
142     			dircheck = *ptr.ptr8++ ^ ror13(dircheck);
143     		while (ptr.ptr8 < end.ptr8);
144     	}
145     
146     	/*
147     	 * The directory tail is in the final bh
148     	 * Note that contary to the RISC OS PRMs,
149     	 * the first few bytes are NOT included
150     	 * in the check.  All bytes are in the
151     	 * same bh.
152     	 */
153     	ptr.ptr8 = bufoff(bh, 2008);
154     	end.ptr8 = ptr.ptr8 + 36;
155     
156     	do {
157     		unsigned int v = *ptr.ptr32++;
158     		dircheck = cpu_to_le32(v) ^ ror13(dircheck);
159     	} while (ptr.ptr32 < end.ptr32);
160     
161     	return (dircheck ^ (dircheck >> 8) ^ (dircheck >> 16) ^ (dircheck >> 24)) & 0xff;
162     }
163     
164     /*
165      * Read and check that a directory is valid
166      */
167     int
168     adfs_dir_read(struct super_block *sb, unsigned long object_id,
169     	      unsigned int size, struct adfs_dir *dir)
170     {
171     	const unsigned int blocksize_bits = sb->s_blocksize_bits;
172     	int blk = 0;
173     
174     	/*
175     	 * Directories which are not a multiple of 2048 bytes
176     	 * are considered bad v2 [3.6]
177     	 */
178     	if (size & 2047)
179     		goto bad_dir;
180     
181     	size >>= blocksize_bits;
182     
183     	dir->nr_buffers = 0;
184     	dir->sb = sb;
185     
186     	for (blk = 0; blk < size; blk++) {
187     		int phys;
188     
189     		phys = __adfs_block_map(sb, object_id, blk);
190     		if (!phys) {
191     			adfs_error(sb, "dir object %lX has a hole at offset %d",
192     				   object_id, blk);
193     			goto release_buffers;
194     		}
195     
196     		dir->bh[blk] = bread(sb->s_dev, phys, sb->s_blocksize);
197     		if (!dir->bh[blk])
198     			goto release_buffers;
199     	}
200     
201     	memcpy(&dir->dirhead, bufoff(dir->bh, 0), sizeof(dir->dirhead));
202     	memcpy(&dir->dirtail, bufoff(dir->bh, 2007), sizeof(dir->dirtail));
203     
204     	if (dir->dirhead.startmasseq != dir->dirtail.new.endmasseq ||
205     	    memcmp(&dir->dirhead.startname, &dir->dirtail.new.endname, 4))
206     		goto bad_dir;
207     
208     	if (memcmp(&dir->dirhead.startname, "Nick", 4) &&
209     	    memcmp(&dir->dirhead.startname, "Hugo", 4))
210     		goto bad_dir;
211     
212     	if (adfs_dir_checkbyte(dir) != dir->dirtail.new.dircheckbyte)
213     		goto bad_dir;
214     
215     	dir->nr_buffers = blk;
216     
217     	return 0;
218     
219     bad_dir:
220     	adfs_error(sb, "corrupted directory fragment %lX",
221     		   object_id);
222     release_buffers:
223     	for (blk -= 1; blk >= 0; blk -= 1)
224     		brelse(dir->bh[blk]);
225     
226     	dir->sb = NULL;
227     
228     	return -EIO;
229     }
230     
231     /*
232      * convert a disk-based directory entry to a Linux ADFS directory entry
233      */
234     static inline void
235     adfs_dir2obj(struct object_info *obj, struct adfs_direntry *de)
236     {
237     	obj->name_len =	adfs_readname(obj->name, de->dirobname, ADFS_F_NAME_LEN);
238     	obj->file_id  = adfs_readval(de->dirinddiscadd, 3);
239     	obj->loadaddr = adfs_readval(de->dirload, 4);
240     	obj->execaddr = adfs_readval(de->direxec, 4);
241     	obj->size     = adfs_readval(de->dirlen,  4);
242     	obj->attr     = de->newdiratts;
243     }
244     
245     /*
246      * convert a Linux ADFS directory entry to a disk-based directory entry
247      */
248     static inline void
249     adfs_obj2dir(struct adfs_direntry *de, struct object_info *obj)
250     {
251     	adfs_writeval(de->dirinddiscadd, 3, obj->file_id);
252     	adfs_writeval(de->dirload, 4, obj->loadaddr);
253     	adfs_writeval(de->direxec, 4, obj->execaddr);
254     	adfs_writeval(de->dirlen,  4, obj->size);
255     	de->newdiratts = obj->attr;
256     }
257     
258     /*
259      * get a directory entry.  Note that the caller is responsible
260      * for holding the relevent locks.
261      */
262     int
263     __adfs_dir_get(struct adfs_dir *dir, int pos, struct object_info *obj)
264     {
265     	struct super_block *sb = dir->sb;
266     	struct adfs_direntry de;
267     	int thissize, buffer, offset;
268     
269     	buffer = pos >> sb->s_blocksize_bits;
270     
271     	if (buffer > dir->nr_buffers)
272     		return -EINVAL;
273     
274     	offset = pos & (sb->s_blocksize - 1);
275     	thissize = sb->s_blocksize - offset;
276     	if (thissize > 26)
277     		thissize = 26;
278     
279     	memcpy(&de, dir->bh[buffer]->b_data + offset, thissize);
280     	if (thissize != 26)
281     		memcpy(((char *)&de) + thissize, dir->bh[buffer + 1]->b_data,
282     		       26 - thissize);
283     
284     	if (!de.dirobname[0])
285     		return -ENOENT;
286     
287     	adfs_dir2obj(obj, &de);
288     
289     	return 0;
290     }
291     
292     int
293     __adfs_dir_put(struct adfs_dir *dir, int pos, struct object_info *obj)
294     {
295     	struct super_block *sb = dir->sb;
296     	struct adfs_direntry de;
297     	int thissize, buffer, offset;
298     
299     	buffer = pos >> sb->s_blocksize_bits;
300     
301     	if (buffer > dir->nr_buffers)
302     		return -EINVAL;
303     
304     	offset = pos & (sb->s_blocksize - 1);
305     	thissize = sb->s_blocksize - offset;
306     	if (thissize > 26)
307     		thissize = 26;
308     
309     	/*
310     	 * Get the entry in total
311     	 */
312     	memcpy(&de, dir->bh[buffer]->b_data + offset, thissize);
313     	if (thissize != 26)
314     		memcpy(((char *)&de) + thissize, dir->bh[buffer + 1]->b_data,
315     		       26 - thissize);
316     
317     	/*
318     	 * update it
319     	 */
320     	adfs_obj2dir(&de, obj);
321     
322     	/*
323     	 * Put the new entry back
324     	 */
325     	memcpy(dir->bh[buffer]->b_data + offset, &de, thissize);
326     	if (thissize != 26)
327     		memcpy(dir->bh[buffer + 1]->b_data, ((char *)&de) + thissize,
328     		       26 - thissize);
329     
330     	return 0;
331     }
332     
333     /*
334      * the caller is responsible for holding the necessary
335      * locks.
336      */
337     static int
338     adfs_dir_find_entry(struct adfs_dir *dir, unsigned long object_id)
339     {
340     	int pos, ret;
341     
342     	ret = -ENOENT;
343     
344     	for (pos = 5; pos < ADFS_NUM_DIR_ENTRIES * 26 + 5; pos += 26) {
345     		struct object_info obj;
346     
347     		if (!__adfs_dir_get(dir, pos, &obj))
348     			break;
349     
350     		if (obj.file_id == object_id) {
351     			ret = pos;
352     			break;
353     		}
354     	}
355     
356     	return ret;
357     }
358     
359     static int
360     adfs_f_read(struct super_block *sb, unsigned int id, unsigned int sz, struct adfs_dir *dir)
361     {
362     	int ret;
363     
364     	if (sz != ADFS_NEWDIR_SIZE)
365     		return -EIO;
366     
367     	ret = adfs_dir_read(sb, id, sz, dir);
368     	if (ret)
369     		adfs_error(sb, "unable to read directory");
370     	else
371     		dir->parent_id = adfs_readval(dir->dirtail.new.dirparent, 3);
372     
373     	return ret;
374     }
375     
376     static int
377     adfs_f_setpos(struct adfs_dir *dir, unsigned int fpos)
378     {
379     	if (fpos >= ADFS_NUM_DIR_ENTRIES)
380     		return -ENOENT;
381     
382     	dir->pos = 5 + fpos * 26;
383     	return 0;
384     }
385     
386     static int
387     adfs_f_getnext(struct adfs_dir *dir, struct object_info *obj)
388     {
389     	unsigned int ret;
390     
391     	ret = __adfs_dir_get(dir, dir->pos, obj);
392     	if (ret == 0)
393     		dir->pos += 26;
394     
395     	return ret;
396     }
397     
398     static int
399     adfs_f_update(struct adfs_dir *dir, struct object_info *obj)
400     {
401     	struct super_block *sb = dir->sb;
402     	int ret, i;
403     
404     	ret = adfs_dir_find_entry(dir, obj->file_id);
405     	if (ret < 0) {
406     		adfs_error(dir->sb, "unable to locate entry to update");
407     		goto out;
408     	}
409     
410     	__adfs_dir_put(dir, ret, obj);
411      
412     	/*
413     	 * Increment directory sequence number
414     	 */
415     	dir->bh[0]->b_data[0] += 1;
416     	dir->bh[dir->nr_buffers - 1]->b_data[sb->s_blocksize - 6] += 1;
417     
418     	ret = adfs_dir_checkbyte(dir);
419     	/*
420     	 * Update directory check byte
421     	 */
422     	dir->bh[dir->nr_buffers - 1]->b_data[sb->s_blocksize - 1] = ret;
423     
424     #if 1
425     	{
426     	const unsigned int blocksize_bits = sb->s_blocksize_bits;
427     
428     	memcpy(&dir->dirhead, bufoff(dir->bh, 0), sizeof(dir->dirhead));
429     	memcpy(&dir->dirtail, bufoff(dir->bh, 2007), sizeof(dir->dirtail));
430     
431     	if (dir->dirhead.startmasseq != dir->dirtail.new.endmasseq ||
432     	    memcmp(&dir->dirhead.startname, &dir->dirtail.new.endname, 4))
433     		goto bad_dir;
434     
435     	if (memcmp(&dir->dirhead.startname, "Nick", 4) &&
436     	    memcmp(&dir->dirhead.startname, "Hugo", 4))
437     		goto bad_dir;
438     
439     	if (adfs_dir_checkbyte(dir) != dir->dirtail.new.dircheckbyte)
440     		goto bad_dir;
441     	}
442     #endif
443     	for (i = dir->nr_buffers - 1; i >= 0; i--)
444     		mark_buffer_dirty(dir->bh[i]);
445     
446     	ret = 0;
447     out:
448     	return ret;
449     #if 1
450     bad_dir:
451     	adfs_error(dir->sb, "whoops!  I broke a directory!");
452     	return -EIO;
453     #endif
454     }
455     
456     static void
457     adfs_f_free(struct adfs_dir *dir)
458     {
459     	int i;
460     
461     	for (i = dir->nr_buffers - 1; i >= 0; i--) {
462     		brelse(dir->bh[i]);
463     		dir->bh[i] = NULL;
464     	}
465     
466     	dir->nr_buffers = 0;
467     	dir->sb = NULL;
468     }
469     
470     struct adfs_dir_ops adfs_f_dir_ops = {
471     	adfs_f_read,
472     	adfs_f_setpos,
473     	adfs_f_getnext,
474     	adfs_f_update,
475     	NULL,
476     	NULL,
477     	adfs_f_free
478     };
479