super.c 15.1 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/*
 *  linux/fs/affs/inode.c
 *
 *  (c) 1996  Hans-Joachim Widmaier - Rewritten
 *
 *  (C) 1993  Ray Burr - Modified for Amiga FFS filesystem.
 *
 *  (C) 1992  Eric Youngdale Modified for ISO 9660 filesystem.
 *
 *  (C) 1991  Linus Torvalds - minix filesystem
 */

#include <linux/module.h>
#include <linux/init.h>
#include <linux/statfs.h>
#include <linux/parser.h>
17
#include <linux/magic.h>
Alexey Dobriyan's avatar
Alexey Dobriyan committed
18
#include <linux/sched.h>
19
#include <linux/slab.h>
20
#include <linux/writeback.h>
Linus Torvalds's avatar
Linus Torvalds committed
21
22
#include "affs.h"

23
static int affs_statfs(struct dentry *dentry, struct kstatfs *buf);
Linus Torvalds's avatar
Linus Torvalds committed
24
25
static int affs_remount (struct super_block *sb, int *flags, char *data);

Christoph Hellwig's avatar
Christoph Hellwig committed
26
static void
Artem Bityutskiy's avatar
Artem Bityutskiy committed
27
affs_commit_super(struct super_block *sb, int wait)
Christoph Hellwig's avatar
Christoph Hellwig committed
28
29
30
31
32
{
	struct affs_sb_info *sbi = AFFS_SB(sb);
	struct buffer_head *bh = sbi->s_root_bh;
	struct affs_root_tail *tail = AFFS_ROOT_TAIL(sb, bh);

Artem Bityutskiy's avatar
Artem Bityutskiy committed
33
	lock_buffer(bh);
Christoph Hellwig's avatar
Christoph Hellwig committed
34
35
	secs_to_datestamp(get_seconds(), &tail->disk_change);
	affs_fix_checksum(sb, bh);
Artem Bityutskiy's avatar
Artem Bityutskiy committed
36
37
	unlock_buffer(bh);

Christoph Hellwig's avatar
Christoph Hellwig committed
38
	mark_buffer_dirty(bh);
39
40
	if (wait)
		sync_dirty_buffer(bh);
Christoph Hellwig's avatar
Christoph Hellwig committed
41
42
}

Linus Torvalds's avatar
Linus Torvalds committed
43
44
45
46
static void
affs_put_super(struct super_block *sb)
{
	struct affs_sb_info *sbi = AFFS_SB(sb);
Fabian Frederick's avatar
Fabian Frederick committed
47
	pr_debug("%s()\n", __func__);
Linus Torvalds's avatar
Linus Torvalds committed
48

49
	cancel_delayed_work_sync(&sbi->sb_work);
Linus Torvalds's avatar
Linus Torvalds committed
50
51
}

Christoph Hellwig's avatar
Christoph Hellwig committed
52
53
54
static int
affs_sync_fs(struct super_block *sb, int wait)
{
Artem Bityutskiy's avatar
Artem Bityutskiy committed
55
	affs_commit_super(sb, wait);
Christoph Hellwig's avatar
Christoph Hellwig committed
56
57
58
	return 0;
}

59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
static void flush_superblock(struct work_struct *work)
{
	struct affs_sb_info *sbi;
	struct super_block *sb;

	sbi = container_of(work, struct affs_sb_info, sb_work.work);
	sb = sbi->sb;

	spin_lock(&sbi->work_lock);
	sbi->work_queued = 0;
	spin_unlock(&sbi->work_lock);

	affs_commit_super(sb, 1);
}

void affs_mark_sb_dirty(struct super_block *sb)
{
	struct affs_sb_info *sbi = AFFS_SB(sb);
	unsigned long delay;

	if (sb->s_flags & MS_RDONLY)
	       return;

	spin_lock(&sbi->work_lock);
	if (!sbi->work_queued) {
	       delay = msecs_to_jiffies(dirty_writeback_interval * 10);
	       queue_delayed_work(system_long_wq, &sbi->sb_work, delay);
	       sbi->work_queued = 1;
	}
	spin_unlock(&sbi->work_lock);
}

91
static struct kmem_cache * affs_inode_cachep;
Linus Torvalds's avatar
Linus Torvalds committed
92
93
94

static struct inode *affs_alloc_inode(struct super_block *sb)
{
95
96
97
98
	struct affs_inode_info *i;

	i = kmem_cache_alloc(affs_inode_cachep, GFP_KERNEL);
	if (!i)
Linus Torvalds's avatar
Linus Torvalds committed
99
		return NULL;
100
101
102
103
104
105
106

	i->vfs_inode.i_version = 1;
	i->i_lc = NULL;
	i->i_ext_bh = NULL;
	i->i_pa_cnt = 0;

	return &i->vfs_inode;
Linus Torvalds's avatar
Linus Torvalds committed
107
108
}

Nick Piggin's avatar
Nick Piggin committed
109
static void affs_i_callback(struct rcu_head *head)
Linus Torvalds's avatar
Linus Torvalds committed
110
{
Nick Piggin's avatar
Nick Piggin committed
111
	struct inode *inode = container_of(head, struct inode, i_rcu);
Linus Torvalds's avatar
Linus Torvalds committed
112
113
114
	kmem_cache_free(affs_inode_cachep, AFFS_I(inode));
}

Nick Piggin's avatar
Nick Piggin committed
115
116
117
118
119
static void affs_destroy_inode(struct inode *inode)
{
	call_rcu(&inode->i_rcu, affs_i_callback);
}

120
static void init_once(void *foo)
Linus Torvalds's avatar
Linus Torvalds committed
121
122
123
{
	struct affs_inode_info *ei = (struct affs_inode_info *) foo;

124
125
	sema_init(&ei->i_link_lock, 1);
	sema_init(&ei->i_ext_lock, 1);
126
	inode_init_once(&ei->vfs_inode);
Linus Torvalds's avatar
Linus Torvalds committed
127
128
}

129
static int __init init_inodecache(void)
Linus Torvalds's avatar
Linus Torvalds committed
130
131
132
{
	affs_inode_cachep = kmem_cache_create("affs_inode_cache",
					     sizeof(struct affs_inode_info),
133
134
					     0, (SLAB_RECLAIM_ACCOUNT|
						SLAB_MEM_SPREAD),
135
					     init_once);
Linus Torvalds's avatar
Linus Torvalds committed
136
137
138
139
140
141
142
	if (affs_inode_cachep == NULL)
		return -ENOMEM;
	return 0;
}

static void destroy_inodecache(void)
{
143
144
145
146
147
	/*
	 * Make sure all delayed rcu free inodes are flushed before we
	 * destroy cache.
	 */
	rcu_barrier();
148
	kmem_cache_destroy(affs_inode_cachep);
Linus Torvalds's avatar
Linus Torvalds committed
149
150
}

151
static const struct super_operations affs_sops = {
Linus Torvalds's avatar
Linus Torvalds committed
152
153
154
	.alloc_inode	= affs_alloc_inode,
	.destroy_inode	= affs_destroy_inode,
	.write_inode	= affs_write_inode,
Al Viro's avatar
Al Viro committed
155
	.evict_inode	= affs_evict_inode,
Linus Torvalds's avatar
Linus Torvalds committed
156
	.put_super	= affs_put_super,
Christoph Hellwig's avatar
Christoph Hellwig committed
157
	.sync_fs	= affs_sync_fs,
Linus Torvalds's avatar
Linus Torvalds committed
158
159
	.statfs		= affs_statfs,
	.remount_fs	= affs_remount,
Miklos Szeredi's avatar
Miklos Szeredi committed
160
	.show_options	= generic_show_options,
Linus Torvalds's avatar
Linus Torvalds committed
161
162
163
};

enum {
164
	Opt_bs, Opt_mode, Opt_mufs, Opt_notruncate, Opt_prefix, Opt_protect,
Linus Torvalds's avatar
Linus Torvalds committed
165
166
167
168
	Opt_reserved, Opt_root, Opt_setgid, Opt_setuid,
	Opt_verbose, Opt_volume, Opt_ignore, Opt_err,
};

169
static const match_table_t tokens = {
Linus Torvalds's avatar
Linus Torvalds committed
170
171
172
	{Opt_bs, "bs=%u"},
	{Opt_mode, "mode=%o"},
	{Opt_mufs, "mufs"},
173
	{Opt_notruncate, "nofilenametruncate"},
Linus Torvalds's avatar
Linus Torvalds committed
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
	{Opt_prefix, "prefix=%s"},
	{Opt_protect, "protect"},
	{Opt_reserved, "reserved=%u"},
	{Opt_root, "root=%u"},
	{Opt_setgid, "setgid=%u"},
	{Opt_setuid, "setuid=%u"},
	{Opt_verbose, "verbose"},
	{Opt_volume, "volume=%s"},
	{Opt_ignore, "grpquota"},
	{Opt_ignore, "noquota"},
	{Opt_ignore, "quota"},
	{Opt_ignore, "usrquota"},
	{Opt_err, NULL},
};

static int
190
parse_options(char *options, kuid_t *uid, kgid_t *gid, int *mode, int *reserved, s32 *root,
Linus Torvalds's avatar
Linus Torvalds committed
191
192
193
194
195
196
197
		int *blocksize, char **prefix, char *volume, unsigned long *mount_opts)
{
	char *p;
	substring_t args[MAX_OPT_ARGS];

	/* Fill in defaults */

198
199
	*uid        = current_uid();
	*gid        = current_gid();
Linus Torvalds's avatar
Linus Torvalds committed
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
	*reserved   = 2;
	*root       = -1;
	*blocksize  = -1;
	volume[0]   = ':';
	volume[1]   = 0;
	*mount_opts = 0;
	if (!options)
		return 1;

	while ((p = strsep(&options, ",")) != NULL) {
		int token, n, option;
		if (!*p)
			continue;

		token = match_token(p, tokens, args);
		switch (token) {
		case Opt_bs:
			if (match_int(&args[0], &n))
Al Viro's avatar
Al Viro committed
218
				return 0;
Linus Torvalds's avatar
Linus Torvalds committed
219
220
			if (n != 512 && n != 1024 && n != 2048
			    && n != 4096) {
221
				pr_warn("Invalid blocksize (512, 1024, 2048, 4096 allowed)\n");
Linus Torvalds's avatar
Linus Torvalds committed
222
223
224
225
226
227
				return 0;
			}
			*blocksize = n;
			break;
		case Opt_mode:
			if (match_octal(&args[0], &option))
Al Viro's avatar
Al Viro committed
228
				return 0;
Linus Torvalds's avatar
Linus Torvalds committed
229
230
231
232
233
234
			*mode = option & 0777;
			*mount_opts |= SF_SETMODE;
			break;
		case Opt_mufs:
			*mount_opts |= SF_MUFS;
			break;
235
236
237
		case Opt_notruncate:
			*mount_opts |= SF_NO_TRUNCATE;
			break;
Linus Torvalds's avatar
Linus Torvalds committed
238
239
240
241
242
243
244
245
246
247
248
		case Opt_prefix:
			*prefix = match_strdup(&args[0]);
			if (!*prefix)
				return 0;
			*mount_opts |= SF_PREFIX;
			break;
		case Opt_protect:
			*mount_opts |= SF_IMMUTABLE;
			break;
		case Opt_reserved:
			if (match_int(&args[0], reserved))
Al Viro's avatar
Al Viro committed
249
				return 0;
Linus Torvalds's avatar
Linus Torvalds committed
250
251
252
			break;
		case Opt_root:
			if (match_int(&args[0], root))
Al Viro's avatar
Al Viro committed
253
				return 0;
Linus Torvalds's avatar
Linus Torvalds committed
254
255
256
			break;
		case Opt_setgid:
			if (match_int(&args[0], &option))
Al Viro's avatar
Al Viro committed
257
				return 0;
258
259
260
			*gid = make_kgid(current_user_ns(), option);
			if (!gid_valid(*gid))
				return 0;
Linus Torvalds's avatar
Linus Torvalds committed
261
262
263
264
			*mount_opts |= SF_SETGID;
			break;
		case Opt_setuid:
			if (match_int(&args[0], &option))
Al Viro's avatar
Al Viro committed
265
				return 0;
266
267
268
			*uid = make_kuid(current_user_ns(), option);
			if (!uid_valid(*uid))
				return 0;
Linus Torvalds's avatar
Linus Torvalds committed
269
270
271
272
273
274
275
			*mount_opts |= SF_SETUID;
			break;
		case Opt_verbose:
			*mount_opts |= SF_VERBOSE;
			break;
		case Opt_volume: {
			char *vol = match_strdup(&args[0]);
276
277
			if (!vol)
				return 0;
Linus Torvalds's avatar
Linus Torvalds committed
278
279
280
281
282
283
284
285
			strlcpy(volume, vol, 32);
			kfree(vol);
			break;
		}
		case Opt_ignore:
		 	/* Silently ignore the quota options */
			break;
		default:
286
287
			pr_warn("Unrecognized mount option \"%s\" or missing value\n",
				p);
Linus Torvalds's avatar
Linus Torvalds committed
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
			return 0;
		}
	}
	return 1;
}

/* This function definitely needs to be split up. Some fine day I'll
 * hopefully have the guts to do so. Until then: sorry for the mess.
 */

static int affs_fill_super(struct super_block *sb, void *data, int silent)
{
	struct affs_sb_info	*sbi;
	struct buffer_head	*root_bh = NULL;
	struct buffer_head	*boot_bh;
	struct inode		*root_inode = NULL;
	s32			 root_block;
	int			 size, blocksize;
	u32			 chksum;
	int			 num_bm;
	int			 i, j;
309
310
	kuid_t			 uid;
	kgid_t			 gid;
Linus Torvalds's avatar
Linus Torvalds committed
311
312
313
	int			 reserved;
	unsigned long		 mount_flags;
	int			 tmp_flags;	/* fix remount prototype... */
Al Viro's avatar
Al Viro committed
314
	u8			 sig[4];
315
	int			 ret;
Linus Torvalds's avatar
Linus Torvalds committed
316

Miklos Szeredi's avatar
Miklos Szeredi committed
317
318
	save_mount_options(sb, data);

Fabian Frederick's avatar
Fabian Frederick committed
319
	pr_debug("read_super(%s)\n", data ? (const char *)data : "no options");
Linus Torvalds's avatar
Linus Torvalds committed
320
321
322
323
324

	sb->s_magic             = AFFS_SUPER_MAGIC;
	sb->s_op                = &affs_sops;
	sb->s_flags |= MS_NODIRATIME;

325
	sbi = kzalloc(sizeof(struct affs_sb_info), GFP_KERNEL);
Linus Torvalds's avatar
Linus Torvalds committed
326
327
	if (!sbi)
		return -ENOMEM;
Jan Blunck's avatar
Jan Blunck committed
328

Linus Torvalds's avatar
Linus Torvalds committed
329
	sb->s_fs_info = sbi;
330
	sbi->sb = sb;
331
	mutex_init(&sbi->s_bmlock);
332
	spin_lock_init(&sbi->symlink_lock);
333
334
	spin_lock_init(&sbi->work_lock);
	INIT_DELAYED_WORK(&sbi->sb_work, flush_superblock);
Linus Torvalds's avatar
Linus Torvalds committed
335
336
337
338

	if (!parse_options(data,&uid,&gid,&i,&reserved,&root_block,
				&blocksize,&sbi->s_prefix,
				sbi->s_volume, &mount_flags)) {
339
		pr_err("Error parsing options\n");
Linus Torvalds's avatar
Linus Torvalds committed
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
		return -EINVAL;
	}
	/* N.B. after this point s_prefix must be released */

	sbi->s_flags   = mount_flags;
	sbi->s_mode    = i;
	sbi->s_uid     = uid;
	sbi->s_gid     = gid;
	sbi->s_reserved= reserved;

	/* Get the size of the device in 512-byte blocks.
	 * If we later see that the partition uses bigger
	 * blocks, we will have to change it.
	 */

	size = sb->s_bdev->bd_inode->i_size >> 9;
Fabian Frederick's avatar
Fabian Frederick committed
356
	pr_debug("initial blocksize=%d, #blocks=%d\n", 512, size);
Linus Torvalds's avatar
Linus Torvalds committed
357
358
359
360
361
362
363
364
365
366

	affs_set_blocksize(sb, PAGE_SIZE);
	/* Try to find root block. Its location depends on the block size. */

	i = 512;
	j = 4096;
	if (blocksize > 0) {
		i = j = blocksize;
		size = size / (blocksize / 512);
	}
367
	for (blocksize = i; blocksize <= j; blocksize <<= 1, size >>= 1) {
Linus Torvalds's avatar
Linus Torvalds committed
368
369
370
		sbi->s_root_block = root_block;
		if (root_block < 0)
			sbi->s_root_block = (reserved + size - 1) / 2;
Fabian Frederick's avatar
Fabian Frederick committed
371
		pr_debug("setting blocksize to %d\n", blocksize);
Linus Torvalds's avatar
Linus Torvalds committed
372
373
374
375
376
377
378
379
380
381
382
383
384
385
		affs_set_blocksize(sb, blocksize);
		sbi->s_partition_size = size;

		/* The root block location that was calculated above is not
		 * correct if the partition size is an odd number of 512-
		 * byte blocks, which will be rounded down to a number of
		 * 1024-byte blocks, and if there were an even number of
		 * reserved blocks. Ideally, all partition checkers should
		 * report the real number of blocks of the real blocksize,
		 * but since this just cannot be done, we have to try to
		 * find the root block anyways. In the above case, it is one
		 * block behind the calculated one. So we check this one, too.
		 */
		for (num_bm = 0; num_bm < 2; num_bm++) {
Fabian Frederick's avatar
Fabian Frederick committed
386
			pr_debug("Dev %s, trying root=%u, bs=%d, "
Linus Torvalds's avatar
Linus Torvalds committed
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
				"size=%d, reserved=%d\n",
				sb->s_id,
				sbi->s_root_block + num_bm,
				blocksize, size, reserved);
			root_bh = affs_bread(sb, sbi->s_root_block + num_bm);
			if (!root_bh)
				continue;
			if (!affs_checksum_block(sb, root_bh) &&
			    be32_to_cpu(AFFS_ROOT_HEAD(root_bh)->ptype) == T_SHORT &&
			    be32_to_cpu(AFFS_ROOT_TAIL(sb, root_bh)->stype) == ST_ROOT) {
				sbi->s_hashsize    = blocksize / 4 - 56;
				sbi->s_root_block += num_bm;
				goto got_root;
			}
			affs_brelse(root_bh);
			root_bh = NULL;
		}
	}
	if (!silent)
406
		pr_err("No valid root block on device %s\n", sb->s_id);
407
	return -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
408
409
410

	/* N.B. after this point bh must be released */
got_root:
411
412
	/* Keep super block in cache */
	sbi->s_root_bh = root_bh;
Linus Torvalds's avatar
Linus Torvalds committed
413
414
415
416
417
	root_block = sbi->s_root_block;

	/* Find out which kind of FS we have */
	boot_bh = sb_bread(sb, 0);
	if (!boot_bh) {
418
		pr_err("Cannot read boot block\n");
419
		return -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
420
	}
Al Viro's avatar
Al Viro committed
421
	memcpy(sig, boot_bh->b_data, 4);
Linus Torvalds's avatar
Linus Torvalds committed
422
	brelse(boot_bh);
Al Viro's avatar
Al Viro committed
423
	chksum = be32_to_cpu(*(__be32 *)sig);
Linus Torvalds's avatar
Linus Torvalds committed
424
425
426
427
428
429
430

	/* Dircache filesystems are compatible with non-dircache ones
	 * when reading. As long as they aren't supported, writing is
	 * not recommended.
	 */
	if ((chksum == FS_DCFFS || chksum == MUFS_DCFFS || chksum == FS_DCOFS
	     || chksum == MUFS_DCOFS) && !(sb->s_flags & MS_RDONLY)) {
431
		pr_notice("Dircache FS - mounting %s read only\n", sb->s_id);
Linus Torvalds's avatar
Linus Torvalds committed
432
433
434
		sb->s_flags |= MS_RDONLY;
	}
	switch (chksum) {
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
	case MUFS_FS:
	case MUFS_INTLFFS:
	case MUFS_DCFFS:
		sbi->s_flags |= SF_MUFS;
		/* fall thru */
	case FS_INTLFFS:
	case FS_DCFFS:
		sbi->s_flags |= SF_INTL;
		break;
	case MUFS_FFS:
		sbi->s_flags |= SF_MUFS;
		break;
	case FS_FFS:
		break;
	case MUFS_OFS:
		sbi->s_flags |= SF_MUFS;
		/* fall thru */
	case FS_OFS:
		sbi->s_flags |= SF_OFS;
		sb->s_flags |= MS_NOEXEC;
		break;
	case MUFS_DCOFS:
	case MUFS_INTLOFS:
		sbi->s_flags |= SF_MUFS;
	case FS_DCOFS:
	case FS_INTLOFS:
		sbi->s_flags |= SF_INTL | SF_OFS;
		sb->s_flags |= MS_NOEXEC;
		break;
	default:
		pr_err("Unknown filesystem on device %s: %08X\n",
		       sb->s_id, chksum);
		return -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
468
469
470
	}

	if (mount_flags & SF_VERBOSE) {
Al Viro's avatar
Al Viro committed
471
		u8 len = AFFS_ROOT_TAIL(sb, root_bh)->disk_name[0];
472
		pr_notice("Mounting volume \"%.*s\": Type=%.3s\\%c, Blocksize=%d\n",
Al Viro's avatar
Al Viro committed
473
			len > 31 ? 31 : len,
Linus Torvalds's avatar
Linus Torvalds committed
474
			AFFS_ROOT_TAIL(sb, root_bh)->disk_name + 1,
Al Viro's avatar
Al Viro committed
475
			sig, sig[3] + '0', blocksize);
Linus Torvalds's avatar
Linus Torvalds committed
476
477
478
479
480
481
482
483
484
	}

	sb->s_flags |= MS_NODEV | MS_NOSUID;

	sbi->s_data_blksize = sb->s_blocksize;
	if (sbi->s_flags & SF_OFS)
		sbi->s_data_blksize -= 24;

	tmp_flags = sb->s_flags;
485
486
487
	ret = affs_init_bitmap(sb, &tmp_flags);
	if (ret)
		return ret;
Linus Torvalds's avatar
Linus Torvalds committed
488
489
490
491
	sb->s_flags = tmp_flags;

	/* set up enough so that it can read an inode */

492
	root_inode = affs_iget(sb, root_block);
493
494
	if (IS_ERR(root_inode))
		return PTR_ERR(root_inode);
495

Al Viro's avatar
Al Viro committed
496
497
498
499
500
	if (AFFS_SB(sb)->s_flags & SF_INTL)
		sb->s_d_op = &affs_intl_dentry_operations;
	else
		sb->s_d_op = &affs_dentry_operations;

501
	sb->s_root = d_make_root(root_inode);
Linus Torvalds's avatar
Linus Torvalds committed
502
	if (!sb->s_root) {
503
		pr_err("AFFS: Get root inode failed\n");
504
		return -ENOMEM;
Linus Torvalds's avatar
Linus Torvalds committed
505
506
	}

Fabian Frederick's avatar
Fabian Frederick committed
507
	pr_debug("s_flags=%lX\n", sb->s_flags);
Linus Torvalds's avatar
Linus Torvalds committed
508
509
510
511
512
513
514
515
	return 0;
}

static int
affs_remount(struct super_block *sb, int *flags, char *data)
{
	struct affs_sb_info	*sbi = AFFS_SB(sb);
	int			 blocksize;
516
517
	kuid_t			 uid;
	kgid_t			 gid;
Linus Torvalds's avatar
Linus Torvalds committed
518
519
520
521
522
	int			 mode;
	int			 reserved;
	int			 root_block;
	unsigned long		 mount_flags;
	int			 res = 0;
Miklos Szeredi's avatar
Miklos Szeredi committed
523
	char			*new_opts = kstrdup(data, GFP_KERNEL);
524
525
	char			 volume[32];
	char			*prefix = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
526

Fabian Frederick's avatar
Fabian Frederick committed
527
	pr_debug("%s(flags=0x%x,opts=\"%s\")\n", __func__, *flags, data);
Linus Torvalds's avatar
Linus Torvalds committed
528

529
	sync_filesystem(sb);
Linus Torvalds's avatar
Linus Torvalds committed
530
531
	*flags |= MS_NODIRATIME;

532
	memcpy(volume, sbi->s_volume, 32);
Miklos Szeredi's avatar
Miklos Szeredi committed
533
	if (!parse_options(data, &uid, &gid, &mode, &reserved, &root_block,
534
			   &blocksize, &prefix, volume,
Miklos Szeredi's avatar
Miklos Szeredi committed
535
			   &mount_flags)) {
536
		kfree(prefix);
Miklos Szeredi's avatar
Miklos Szeredi committed
537
		kfree(new_opts);
Linus Torvalds's avatar
Linus Torvalds committed
538
		return -EINVAL;
Miklos Szeredi's avatar
Miklos Szeredi committed
539
	}
Jan Blunck's avatar
Jan Blunck committed
540

541
	flush_delayed_work(&sbi->sb_work);
542
	replace_mount_options(sb, new_opts);
Miklos Szeredi's avatar
Miklos Szeredi committed
543

Linus Torvalds's avatar
Linus Torvalds committed
544
545
546
547
	sbi->s_flags = mount_flags;
	sbi->s_mode  = mode;
	sbi->s_uid   = uid;
	sbi->s_gid   = gid;
548
549
550
551
552
553
554
555
	/* protect against readers */
	spin_lock(&sbi->symlink_lock);
	if (prefix) {
		kfree(sbi->s_prefix);
		sbi->s_prefix = prefix;
	}
	memcpy(sbi->s_volume, volume, 32);
	spin_unlock(&sbi->symlink_lock);
Linus Torvalds's avatar
Linus Torvalds committed
556

Jan Blunck's avatar
Jan Blunck committed
557
	if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY))
Linus Torvalds's avatar
Linus Torvalds committed
558
		return 0;
Jan Blunck's avatar
Jan Blunck committed
559

560
	if (*flags & MS_RDONLY)
Linus Torvalds's avatar
Linus Torvalds committed
561
		affs_free_bitmap(sb);
562
	else
Linus Torvalds's avatar
Linus Torvalds committed
563
564
565
566
567
568
		res = affs_init_bitmap(sb, flags);

	return res;
}

static int
569
affs_statfs(struct dentry *dentry, struct kstatfs *buf)
Linus Torvalds's avatar
Linus Torvalds committed
570
{
571
	struct super_block *sb = dentry->d_sb;
Linus Torvalds's avatar
Linus Torvalds committed
572
	int		 free;
Coly Li's avatar
Coly Li committed
573
	u64		 id = huge_encode_dev(sb->s_bdev->bd_dev);
Linus Torvalds's avatar
Linus Torvalds committed
574

Fabian Frederick's avatar
Fabian Frederick committed
575
576
577
	pr_debug("%s() partsize=%d, reserved=%d\n",
		 __func__, AFFS_SB(sb)->s_partition_size,
		 AFFS_SB(sb)->s_reserved);
Linus Torvalds's avatar
Linus Torvalds committed
578
579
580
581
582
583
584

	free          = affs_count_free_blocks(sb);
	buf->f_type    = AFFS_SUPER_MAGIC;
	buf->f_bsize   = sb->s_blocksize;
	buf->f_blocks  = AFFS_SB(sb)->s_partition_size - AFFS_SB(sb)->s_reserved;
	buf->f_bfree   = free;
	buf->f_bavail  = free;
Coly Li's avatar
Coly Li committed
585
586
	buf->f_fsid.val[0] = (u32)id;
	buf->f_fsid.val[1] = (u32)(id >> 32);
587
	buf->f_namelen = AFFSNAMEMAX;
Linus Torvalds's avatar
Linus Torvalds committed
588
589
590
	return 0;
}

Al Viro's avatar
Al Viro committed
591
592
static struct dentry *affs_mount(struct file_system_type *fs_type,
	int flags, const char *dev_name, void *data)
Linus Torvalds's avatar
Linus Torvalds committed
593
{
Al Viro's avatar
Al Viro committed
594
	return mount_bdev(fs_type, flags, dev_name, data, affs_fill_super);
Linus Torvalds's avatar
Linus Torvalds committed
595
596
}

597
598
599
600
601
602
603
604
static void affs_kill_sb(struct super_block *sb)
{
	struct affs_sb_info *sbi = AFFS_SB(sb);
	kill_block_super(sb);
	if (sbi) {
		affs_free_bitmap(sb);
		affs_brelse(sbi->s_root_bh);
		kfree(sbi->s_prefix);
605
		mutex_destroy(&sbi->s_bmlock);
606
607
608
609
		kfree(sbi);
	}
}

Linus Torvalds's avatar
Linus Torvalds committed
610
611
612
static struct file_system_type affs_fs_type = {
	.owner		= THIS_MODULE,
	.name		= "affs",
Al Viro's avatar
Al Viro committed
613
	.mount		= affs_mount,
614
	.kill_sb	= affs_kill_sb,
Linus Torvalds's avatar
Linus Torvalds committed
615
616
	.fs_flags	= FS_REQUIRES_DEV,
};
617
MODULE_ALIAS_FS("affs");
Linus Torvalds's avatar
Linus Torvalds committed
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644

static int __init init_affs_fs(void)
{
	int err = init_inodecache();
	if (err)
		goto out1;
	err = register_filesystem(&affs_fs_type);
	if (err)
		goto out;
	return 0;
out:
	destroy_inodecache();
out1:
	return err;
}

static void __exit exit_affs_fs(void)
{
	unregister_filesystem(&affs_fs_type);
	destroy_inodecache();
}

MODULE_DESCRIPTION("Amiga filesystem support for Linux");
MODULE_LICENSE("GPL");

module_init(init_affs_fs)
module_exit(exit_affs_fs)