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

1     /*
2      *  linux/fs/adfs/super.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/module.h>
12     #include <linux/errno.h>
13     #include <linux/fs.h>
14     #include <linux/adfs_fs.h>
15     #include <linux/slab.h>
16     #include <linux/sched.h>
17     #include <linux/stat.h>
18     #include <linux/string.h>
19     #include <linux/locks.h>
20     #include <linux/init.h>
21     
22     #include <asm/bitops.h>
23     #include <asm/uaccess.h>
24     #include <asm/system.h>
25     
26     #include <stdarg.h>
27     
28     #include "adfs.h"
29     #include "dir_f.h"
30     #include "dir_fplus.h"
31     
32     void __adfs_error(struct super_block *sb, const char *function, const char *fmt, ...)
33     {
34     	char error_buf[128];
35     	va_list args;
36     
37     	va_start(args, fmt);
38     	vsprintf(error_buf, fmt, args);
39     	va_end(args);
40     
41     	printk(KERN_CRIT "ADFS-fs error (device %s)%s%s: %s\n",
42     		kdevname(sb->s_dev), function ? ": " : "",
43     		function ? function : "", error_buf);
44     }
45     
46     static int adfs_checkdiscrecord(struct adfs_discrecord *dr)
47     {
48     	int i;
49     
50     	/* sector size must be 256, 512 or 1024 bytes */
51     	if (dr->log2secsize != 8 &&
52     	    dr->log2secsize != 9 &&
53     	    dr->log2secsize != 10)
54     		return 1;
55     
56     	/* idlen must be at least log2secsize + 3 */
57     	if (dr->idlen < dr->log2secsize + 3)
58     		return 1;
59     
60     	/* we cannot have such a large disc that we
61     	 * are unable to represent sector offsets in
62     	 * 32 bits.  This works out at 2.0 TB.
63     	 */
64     	if (dr->disc_size_high >> dr->log2secsize)
65     		return 1;
66     
67     	/*
68     	 * The following checks are not required for F+
69     	 * stage 1.
70     	 */
71     #if 0
72     	/* idlen must be smaller be no greater than 15 */
73     	if (dr->idlen > 15)
74     		return 1;
75     
76     	/* nzones must be less than 128 for the root
77     	 * directory to be addressable
78     	 */
79     	if (dr->nzones >= 128 && dr->nzones_high == 0)
80     		return 1;
81     
82     	/* root must be of the form 0x2.. */
83     	if ((le32_to_cpu(dr->root) & 0xffffff00) != 0x00000200)
84     		return 1;
85     #else
86     	/*
87     	 * Stage 2 F+ does not require the following check
88     	 */
89     #if 0
90     	/* idlen must be no greater than 16 v2 [1.0] */
91     	if (dr->idlen > 16)
92     		return 1;
93     
94     	/* we can't handle F+ discs yet */
95     	if (dr->format_version || dr->root_size)
96     		return 1;
97     
98     #else
99     	/* idlen must be no greater than 19 v2 [1.0] */
100     	if (dr->idlen > 19)
101     		return 1;
102     #endif
103     #endif
104     
105     	/* reserved bytes should be zero */
106     	for (i = 0; i < sizeof(dr->unused52); i++)
107     		if (dr->unused52[i] != 0)
108     			return 1;
109     
110     	return 0;
111     }
112     
113     static unsigned char adfs_calczonecheck(struct super_block *sb, unsigned char *map)
114     {
115     	unsigned int v0, v1, v2, v3;
116     	int i;
117     
118     	v0 = v1 = v2 = v3 = 0;
119     	for (i = sb->s_blocksize - 4; i; i -= 4) {
120     		v0 += map[i]     + (v3 >> 8);
121     		v3 &= 0xff;
122     		v1 += map[i + 1] + (v0 >> 8);
123     		v0 &= 0xff;
124     		v2 += map[i + 2] + (v1 >> 8);
125     		v1 &= 0xff;
126     		v3 += map[i + 3] + (v2 >> 8);
127     		v2 &= 0xff;
128     	}
129     	v0 +=           v3 >> 8;
130     	v1 += map[1] + (v0 >> 8);
131     	v2 += map[2] + (v1 >> 8);
132     	v3 += map[3] + (v2 >> 8);
133     
134     	return v0 ^ v1 ^ v2 ^ v3;
135     }
136     
137     static int adfs_checkmap(struct super_block *sb, struct adfs_discmap *dm)
138     {
139     	unsigned char crosscheck = 0, zonecheck = 1;
140     	int i;
141     
142     	for (i = 0; i < sb->u.adfs_sb.s_map_size; i++) {
143     		unsigned char *map;
144     
145     		map = dm[i].dm_bh->b_data;
146     
147     		if (adfs_calczonecheck(sb, map) != map[0]) {
148     			adfs_error(sb, "zone %d fails zonecheck", i);
149     			zonecheck = 0;
150     		}
151     		crosscheck ^= map[3];
152     	}
153     	if (crosscheck != 0xff)
154     		adfs_error(sb, "crosscheck != 0xff");
155     	return crosscheck == 0xff && zonecheck;
156     }
157     
158     static void adfs_put_super(struct super_block *sb)
159     {
160     	int i;
161     
162     	for (i = 0; i < sb->u.adfs_sb.s_map_size; i++)
163     		brelse(sb->u.adfs_sb.s_map[i].dm_bh);
164     	kfree(sb->u.adfs_sb.s_map);
165     }
166     
167     static int parse_options(struct super_block *sb, char *options)
168     {
169     	char *value, *opt;
170     
171     	if (!options)
172     		return 0;
173     
174     	for (opt = strtok(options, ","); opt != NULL; opt = strtok(NULL, ",")) {
175     		value = strchr(opt, '=');
176     		if (value)
177     			*value++ = '\0';
178     
179     		if (!strcmp(opt, "uid")) {	/* owner of all files */
180     			if (!value || !*value)
181     				return -EINVAL;
182     			sb->u.adfs_sb.s_uid = simple_strtoul(value, &value, 0);
183     			if (*value)
184     				return -EINVAL;
185     		} else
186     		if (!strcmp(opt, "gid")) {	/* group owner of all files */
187     			if (!value || !*value)
188     				return -EINVAL;
189     			sb->u.adfs_sb.s_gid = simple_strtoul(value, &value, 0);
190     			if (*value)
191     				return -EINVAL;
192     		} else
193     		if (!strcmp(opt, "ownmask")) {	/* owner permission mask */
194     			if (!value || !*value)
195     				return -EINVAL;
196     			sb->u.adfs_sb.s_owner_mask = simple_strtoul(value, &value, 8);
197     			if (*value)
198     				return -EINVAL;
199     		} else
200     		if (!strcmp(opt, "othmask")) {	/* others permission mask */
201     			if (!value || !*value)
202     				return -EINVAL;
203     			sb->u.adfs_sb.s_other_mask = simple_strtoul(value, &value, 8);
204     			if (*value)
205     				return -EINVAL;
206     		} else {			/* eh? say again. */
207     			printk("ADFS-fs: unrecognised mount option %s\n", opt);
208     			return -EINVAL;
209     		}
210     	}
211     	return 0;
212     }
213     
214     static int adfs_remount(struct super_block *sb, int *flags, char *data)
215     {
216     	return parse_options(sb, data);
217     }
218     
219     static int adfs_statfs(struct super_block *sb, struct statfs *buf)
220     {
221     	struct adfs_sb_info *asb = &sb->u.adfs_sb;
222     
223     	buf->f_type    = ADFS_SUPER_MAGIC;
224     	buf->f_namelen = asb->s_namelen;
225     	buf->f_bsize   = sb->s_blocksize;
226     	buf->f_blocks  = asb->s_size;
227     	buf->f_files   = asb->s_ids_per_zone * asb->s_map_size;
228     	buf->f_bavail  =
229     	buf->f_bfree   = adfs_map_free(sb);
230     	buf->f_ffree   = buf->f_bfree * buf->f_files / buf->f_blocks;
231     
232     	return 0;
233     }
234     
235     static struct super_operations adfs_sops = {
236     	write_inode:	adfs_write_inode,
237     	put_super:	adfs_put_super,
238     	statfs:		adfs_statfs,
239     	remount_fs:	adfs_remount,
240     };
241     
242     static struct adfs_discmap *adfs_read_map(struct super_block *sb, struct adfs_discrecord *dr)
243     {
244     	struct adfs_discmap *dm;
245     	unsigned int map_addr, zone_size, nzones;
246     	int i, zone;
247     
248     	nzones    = sb->u.adfs_sb.s_map_size;
249     	zone_size = (8 << dr->log2secsize) - le16_to_cpu(dr->zone_spare);
250     	map_addr  = (nzones >> 1) * zone_size -
251     		     ((nzones > 1) ? ADFS_DR_SIZE_BITS : 0);
252     	map_addr  = signed_asl(map_addr, sb->u.adfs_sb.s_map2blk);
253     
254     	sb->u.adfs_sb.s_ids_per_zone = zone_size / (sb->u.adfs_sb.s_idlen + 1);
255     
256     	dm = kmalloc(nzones * sizeof(*dm), GFP_KERNEL);
257     	if (dm == NULL) {
258     		adfs_error(sb, "not enough memory");
259     		return NULL;
260     	}
261     
262     	for (zone = 0; zone < nzones; zone++, map_addr++) {
263     		dm[zone].dm_startbit = 0;
264     		dm[zone].dm_endbit   = zone_size;
265     		dm[zone].dm_startblk = zone * zone_size - ADFS_DR_SIZE_BITS;
266     		dm[zone].dm_bh       = bread(sb->s_dev, map_addr, sb->s_blocksize);
267     
268     		if (!dm[zone].dm_bh) {
269     			adfs_error(sb, "unable to read map");
270     			goto error_free;
271     		}
272     	}
273     
274     	/* adjust the limits for the first and last map zones */
275     	i = zone - 1;
276     	dm[0].dm_startblk = 0;
277     	dm[0].dm_startbit = ADFS_DR_SIZE_BITS;
278     	dm[i].dm_endbit   = (dr->disc_size_high << (32 - dr->log2bpmb)) +
279     			    (dr->disc_size >> dr->log2bpmb) +
280     			    (ADFS_DR_SIZE_BITS - i * zone_size);
281     
282     	if (adfs_checkmap(sb, dm))
283     		return dm;
284     
285     	adfs_error(sb, NULL, "map corrupted");
286     
287     error_free:
288     	while (--zone >= 0)
289     		brelse(dm[zone].dm_bh);
290     
291     	kfree(dm);
292     	return NULL;
293     }
294     
295     static inline unsigned long adfs_discsize(struct adfs_discrecord *dr, int block_bits)
296     {
297     	unsigned long discsize;
298     
299     	discsize  = le32_to_cpu(dr->disc_size_high) << (32 - block_bits);
300     	discsize |= le32_to_cpu(dr->disc_size) >> block_bits;
301     
302     	return discsize;
303     }
304     
305     struct super_block *adfs_read_super(struct super_block *sb, void *data, int silent)
306     {
307     	struct adfs_discrecord *dr;
308     	struct buffer_head *bh;
309     	struct object_info root_obj;
310     	unsigned char *b_data;
311     	kdev_t dev = sb->s_dev;
312     
313     	/* set default options */
314     	sb->u.adfs_sb.s_uid = 0;
315     	sb->u.adfs_sb.s_gid = 0;
316     	sb->u.adfs_sb.s_owner_mask = S_IRWXU;
317     	sb->u.adfs_sb.s_other_mask = S_IRWXG | S_IRWXO;
318     
319     	if (parse_options(sb, data))
320     		goto error;
321     
322     	set_blocksize(dev, BLOCK_SIZE);
323     	if (!(bh = bread(dev, ADFS_DISCRECORD / BLOCK_SIZE, BLOCK_SIZE))) {
324     		adfs_error(sb, "unable to read superblock");
325     		goto error;
326     	}
327     
328     	b_data = bh->b_data + (ADFS_DISCRECORD % BLOCK_SIZE);
329     
330     	if (adfs_checkbblk(b_data)) {
331     		if (!silent)
332     			printk("VFS: Can't find an adfs filesystem on dev "
333     				"%s.\n", kdevname(dev));
334     		goto error_free_bh;
335     	}
336     
337     	dr = (struct adfs_discrecord *)(b_data + ADFS_DR_OFFSET);
338     
339     	/*
340     	 * Do some sanity checks on the ADFS disc record
341     	 */
342     	if (adfs_checkdiscrecord(dr)) {
343     		if (!silent)
344     			printk("VPS: Can't find an adfs filesystem on dev "
345     				"%s.\n", kdevname(dev));
346     		goto error_free_bh;
347     	}
348     
349     	sb->s_blocksize_bits = dr->log2secsize;
350     	sb->s_blocksize = 1 << sb->s_blocksize_bits;
351     	if (sb->s_blocksize != BLOCK_SIZE &&
352     	    (sb->s_blocksize == 512 || sb->s_blocksize == 1024 ||
353     	     sb->s_blocksize == 2048 || sb->s_blocksize == 4096)) {
354     
355     		brelse(bh);
356     		set_blocksize(dev, sb->s_blocksize);
357     		bh = bread(dev, ADFS_DISCRECORD / sb->s_blocksize, sb->s_blocksize);
358     		if (!bh) {
359     			adfs_error(sb, "couldn't read superblock on "
360     				"2nd try.");
361     			goto error;
362     		}
363     		b_data = bh->b_data + (ADFS_DISCRECORD % sb->s_blocksize);
364     		if (adfs_checkbblk(b_data)) {
365     			adfs_error(sb, "disc record mismatch, very weird!");
366     			goto error_free_bh;
367     		}
368     		dr = (struct adfs_discrecord *)(b_data + ADFS_DR_OFFSET);
369     	}
370     	if (sb->s_blocksize != bh->b_size) {
371     		if (!silent)
372     			printk(KERN_ERR "VFS: Unsupported blocksize on dev "
373     				"%s.\n", kdevname(dev));
374     		goto error_free_bh;
375     	}
376     
377     	/*
378     	 * blocksize on this device should now be set to the ADFS log2secsize
379     	 */
380     
381     	sb->s_magic		 = ADFS_SUPER_MAGIC;
382     	sb->u.adfs_sb.s_idlen	 = dr->idlen;
383     	sb->u.adfs_sb.s_map_size = dr->nzones | (dr->nzones_high << 8);
384     	sb->u.adfs_sb.s_map2blk	 = dr->log2bpmb - dr->log2secsize;
385     	sb->u.adfs_sb.s_size     = adfs_discsize(dr, sb->s_blocksize_bits);
386     	sb->u.adfs_sb.s_version  = dr->format_version;
387     	sb->u.adfs_sb.s_log2sharesize = dr->log2sharesize;
388     	
389     	sb->u.adfs_sb.s_map = adfs_read_map(sb, dr);
390     	if (!sb->u.adfs_sb.s_map)
391     		goto error_free_bh;
392     
393     	brelse(bh);
394     
395     	/*
396     	 * set up enough so that we can read an inode
397     	 */
398     	sb->s_op = &adfs_sops;
399     
400     	dr = (struct adfs_discrecord *)(sb->u.adfs_sb.s_map[0].dm_bh->b_data + 4);
401     
402     	root_obj.parent_id = root_obj.file_id = le32_to_cpu(dr->root);
403     	root_obj.name_len  = 0;
404     	root_obj.loadaddr  = 0;
405     	root_obj.execaddr  = 0;
406     	root_obj.size	   = ADFS_NEWDIR_SIZE;
407     	root_obj.attr	   = ADFS_NDA_DIRECTORY   | ADFS_NDA_OWNER_READ |
408     			     ADFS_NDA_OWNER_WRITE | ADFS_NDA_PUBLIC_READ;
409     
410     	/*
411     	 * If this is a F+ disk with variable length directories,
412     	 * get the root_size from the disc record.
413     	 */
414     	if (sb->u.adfs_sb.s_version) {
415     		root_obj.size = dr->root_size;
416     		sb->u.adfs_sb.s_dir     = &adfs_fplus_dir_ops;
417     		sb->u.adfs_sb.s_namelen = ADFS_FPLUS_NAME_LEN;
418     	} else {
419     		sb->u.adfs_sb.s_dir     = &adfs_f_dir_ops;
420     		sb->u.adfs_sb.s_namelen = ADFS_F_NAME_LEN;
421     	}
422     
423     	sb->s_root = d_alloc_root(adfs_iget(sb, &root_obj));
424     	if (!sb->s_root) {
425     		int i;
426     
427     		for (i = 0; i < sb->u.adfs_sb.s_map_size; i++)
428     			brelse(sb->u.adfs_sb.s_map[i].dm_bh);
429     		kfree(sb->u.adfs_sb.s_map);
430     		adfs_error(sb, "get root inode failed\n");
431     		goto error;
432     	} else
433     		sb->s_root->d_op = &adfs_dentry_operations;
434     	return sb;
435     
436     error_free_bh:
437     	brelse(bh);
438     error:
439     	return NULL;
440     }
441     
442     static DECLARE_FSTYPE_DEV(adfs_fs_type, "adfs", adfs_read_super);
443     
444     static int __init init_adfs_fs(void)
445     {
446     	return register_filesystem(&adfs_fs_type);
447     }
448     
449     static void __exit exit_adfs_fs(void)
450     {
451     	unregister_filesystem(&adfs_fs_type);
452     }
453     
454     EXPORT_NO_SYMBOLS;
455     
456     module_init(init_adfs_fs)
457     module_exit(exit_adfs_fs)
458