Blame view

fs/hpfs/super.c 19.4 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
12
13
  /*
   *  linux/fs/hpfs/super.c
   *
   *  Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
   *
   *  mounting, unmounting, error handling
   */
  
  #include "hpfs_fn.h"
  #include <linux/module.h>
  #include <linux/parser.h>
  #include <linux/init.h>
  #include <linux/statfs.h>
e18fa700c   Jeff Garzik   Move several *_SU...
14
  #include <linux/magic.h>
e8edc6e03   Alexey Dobriyan   Detach sched.h fr...
15
  #include <linux/sched.h>
f4c54fcf3   Akinobu Mita   hpfs: use bitmap_...
16
  #include <linux/bitmap.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
17
  #include <linux/slab.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
18
19
  
  /* Mark the filesystem dirty, so that chkdsk checks it when os/2 booted */
dab4c82a6   Mikulas Patocka   HPFS: Fix a bug t...
20
  static void mark_dirty(struct super_block *s, int remount)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
21
  {
dab4c82a6   Mikulas Patocka   HPFS: Fix a bug t...
22
  	if (hpfs_sb(s)->sb_chkdsk && (remount || !(s->s_flags & MS_RDONLY))) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
23
24
25
26
27
28
  		struct buffer_head *bh;
  		struct hpfs_spare_block *sb;
  		if ((sb = hpfs_map_sector(s, 17, &bh, 0))) {
  			sb->dirty = 1;
  			sb->old_wrote = 0;
  			mark_buffer_dirty(bh);
f73976818   Mikulas Patocka   HPFS: When markin...
29
  			sync_dirty_buffer(bh);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
30
31
32
33
34
35
36
37
38
39
40
41
42
  			brelse(bh);
  		}
  	}
  }
  
  /* Mark the filesystem clean (mark it dirty for chkdsk if chkdsk==2 or if there
     were errors) */
  
  static void unmark_dirty(struct super_block *s)
  {
  	struct buffer_head *bh;
  	struct hpfs_spare_block *sb;
  	if (s->s_flags & MS_RDONLY) return;
f73976818   Mikulas Patocka   HPFS: When markin...
43
  	sync_blockdev(s->s_bdev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
44
45
46
47
  	if ((sb = hpfs_map_sector(s, 17, &bh, 0))) {
  		sb->dirty = hpfs_sb(s)->sb_chkdsk > 1 - hpfs_sb(s)->sb_was_error;
  		sb->old_wrote = hpfs_sb(s)->sb_chkdsk >= 2 && !hpfs_sb(s)->sb_was_error;
  		mark_buffer_dirty(bh);
f73976818   Mikulas Patocka   HPFS: When markin...
48
  		sync_dirty_buffer(bh);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
49
50
51
52
53
  		brelse(bh);
  	}
  }
  
  /* Filesystem error... */
352d94d04   Alexey Dobriyan   [PATCH] hpfs: bri...
54
  void hpfs_error(struct super_block *s, const char *fmt, ...)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
55
  {
a28e4b2b1   Joe Perches   hpfs: hpfs_error:...
56
  	struct va_format vaf;
352d94d04   Alexey Dobriyan   [PATCH] hpfs: bri...
57
58
59
  	va_list args;
  
  	va_start(args, fmt);
a28e4b2b1   Joe Perches   hpfs: hpfs_error:...
60
61
62
63
64
  
  	vaf.fmt = fmt;
  	vaf.va = &args;
  
  	pr_err("filesystem error: %pV", &vaf);
352d94d04   Alexey Dobriyan   [PATCH] hpfs: bri...
65
  	va_end(args);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
66
67
  	if (!hpfs_sb(s)->sb_was_error) {
  		if (hpfs_sb(s)->sb_err == 2) {
b7cb1ce22   Fabian Frederick   fs/hpfs: convert ...
68
69
  			pr_cont("; crashing the system because you wanted it
  ");
dab4c82a6   Mikulas Patocka   HPFS: Fix a bug t...
70
  			mark_dirty(s, 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
71
72
  			panic("HPFS panic");
  		} else if (hpfs_sb(s)->sb_err == 1) {
b7cb1ce22   Fabian Frederick   fs/hpfs: convert ...
73
74
75
  			if (s->s_flags & MS_RDONLY)
  				pr_cont("; already mounted read-only
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
76
  			else {
b7cb1ce22   Fabian Frederick   fs/hpfs: convert ...
77
78
  				pr_cont("; remounting read-only
  ");
dab4c82a6   Mikulas Patocka   HPFS: Fix a bug t...
79
  				mark_dirty(s, 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
80
81
  				s->s_flags |= MS_RDONLY;
  			}
b7cb1ce22   Fabian Frederick   fs/hpfs: convert ...
82
83
84
85
86
87
88
89
90
  		} else if (s->s_flags & MS_RDONLY)
  				pr_cont("; going on - but anything won't be destroyed because it's read-only
  ");
  		else
  			pr_cont("; corrupted filesystem mounted read/write - your computer will explode within 20 seconds ... but you wanted it so!
  ");
  	} else
  		pr_cont("
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
  	hpfs_sb(s)->sb_was_error = 1;
  }
  
  /* 
   * A little trick to detect cycles in many hpfs structures and don't let the
   * kernel crash on corrupted filesystem. When first called, set c2 to 0.
   *
   * BTW. chkdsk doesn't detect cycles correctly. When I had 2 lost directories
   * nested each in other, chkdsk locked up happilly.
   */
  
  int hpfs_stop_cycles(struct super_block *s, int key, int *c1, int *c2,
  		char *msg)
  {
  	if (*c2 && *c1 == key) {
  		hpfs_error(s, "cycle detected on key %08x in %s", key, msg);
  		return 1;
  	}
  	(*c2)++;
  	if (!((*c2 - 1) & *c2)) *c1 = key;
  	return 0;
  }
30687e0a4   Al Viro   hpfs: make freein...
113
  static void free_sbi(struct hpfs_sb_info *sbi)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
114
  {
30687e0a4   Al Viro   hpfs: make freein...
115
116
117
118
  	kfree(sbi->sb_cp_table);
  	kfree(sbi->sb_bmp_dir);
  	kfree(sbi);
  }
6cfd01484   Christoph Hellwig   push BKL down int...
119

30687e0a4   Al Viro   hpfs: make freein...
120
121
122
123
124
125
126
  static void lazy_free_sbi(struct rcu_head *rcu)
  {
  	free_sbi(container_of(rcu, struct hpfs_sb_info, rcu));
  }
  
  static void hpfs_put_super(struct super_block *s)
  {
7dd29d8d8   Mikulas Patocka   HPFS: Introduce a...
127
128
129
  	hpfs_lock(s);
  	unmark_dirty(s);
  	hpfs_unlock(s);
30687e0a4   Al Viro   hpfs: make freein...
130
  	call_rcu(&hpfs_sb(s)->rcu, lazy_free_sbi);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
131
  }
2cbe5c76f   Mikulas Patocka   hpfs: remember fr...
132
  static unsigned hpfs_count_one_bitmap(struct super_block *s, secno secno)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
133
134
  {
  	struct quad_buffer_head qbh;
f4c54fcf3   Akinobu Mita   hpfs: use bitmap_...
135
136
  	unsigned long *bits;
  	unsigned count;
275f495db   Mikulas Patocka   hpfs: implement p...
137
  	bits = hpfs_map_4sectors(s, secno, &qbh, 0);
f4c54fcf3   Akinobu Mita   hpfs: use bitmap_...
138
  	if (!bits)
2cbe5c76f   Mikulas Patocka   hpfs: remember fr...
139
  		return (unsigned)-1;
f4c54fcf3   Akinobu Mita   hpfs: use bitmap_...
140
  	count = bitmap_weight(bits, 2048 * BITS_PER_BYTE);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
141
142
143
144
145
146
147
148
149
  	hpfs_brelse4(&qbh);
  	return count;
  }
  
  static unsigned count_bitmaps(struct super_block *s)
  {
  	unsigned n, count, n_bands;
  	n_bands = (hpfs_sb(s)->sb_fs_size + 0x3fff) >> 14;
  	count = 0;
275f495db   Mikulas Patocka   hpfs: implement p...
150
151
152
153
  	for (n = 0; n < COUNT_RD_AHEAD; n++) {
  		hpfs_prefetch_bitmap(s, n);
  	}
  	for (n = 0; n < n_bands; n++) {
2cbe5c76f   Mikulas Patocka   hpfs: remember fr...
154
  		unsigned c;
275f495db   Mikulas Patocka   hpfs: implement p...
155
  		hpfs_prefetch_bitmap(s, n + COUNT_RD_AHEAD);
2cbe5c76f   Mikulas Patocka   hpfs: remember fr...
156
157
158
  		c = hpfs_count_one_bitmap(s, le32_to_cpu(hpfs_sb(s)->sb_bmp_dir[n]));
  		if (c != (unsigned)-1)
  			count += c;
275f495db   Mikulas Patocka   hpfs: implement p...
159
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
160
161
  	return count;
  }
2cbe5c76f   Mikulas Patocka   hpfs: remember fr...
162
163
164
165
166
167
168
169
170
171
172
  unsigned hpfs_get_free_dnodes(struct super_block *s)
  {
  	struct hpfs_sb_info *sbi = hpfs_sb(s);
  	if (sbi->sb_n_free_dnodes == (unsigned)-1) {
  		unsigned c = hpfs_count_one_bitmap(s, sbi->sb_dmap);
  		if (c == (unsigned)-1)
  			return 0;
  		sbi->sb_n_free_dnodes = c;
  	}
  	return sbi->sb_n_free_dnodes;
  }
726c33422   David Howells   [PATCH] VFS: Perm...
173
  static int hpfs_statfs(struct dentry *dentry, struct kstatfs *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
174
  {
726c33422   David Howells   [PATCH] VFS: Perm...
175
  	struct super_block *s = dentry->d_sb;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
176
  	struct hpfs_sb_info *sbi = hpfs_sb(s);
604d295c2   Coly Li   fs/hpfs: return f...
177
  	u64 id = huge_encode_dev(s->s_bdev->bd_dev);
2cbe5c76f   Mikulas Patocka   hpfs: remember fr...
178

9a311b96c   Arnd Bergmann   hpfs: remove the BKL
179
  	hpfs_lock(s);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
180

2cbe5c76f   Mikulas Patocka   hpfs: remember fr...
181
  	if (sbi->sb_n_free == (unsigned)-1)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
182
  		sbi->sb_n_free = count_bitmaps(s);
2cbe5c76f   Mikulas Patocka   hpfs: remember fr...
183

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
184
185
186
187
188
189
  	buf->f_type = s->s_magic;
  	buf->f_bsize = 512;
  	buf->f_blocks = sbi->sb_fs_size;
  	buf->f_bfree = sbi->sb_n_free;
  	buf->f_bavail = sbi->sb_n_free;
  	buf->f_files = sbi->sb_dirband_size / 4;
2cbe5c76f   Mikulas Patocka   hpfs: remember fr...
190
  	buf->f_ffree = hpfs_get_free_dnodes(s);
604d295c2   Coly Li   fs/hpfs: return f...
191
192
  	buf->f_fsid.val[0] = (u32)id;
  	buf->f_fsid.val[1] = (u32)(id >> 32);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
193
  	buf->f_namelen = 254;
9a311b96c   Arnd Bergmann   hpfs: remove the BKL
194
  	hpfs_unlock(s);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
195
196
197
  
  	return 0;
  }
a27b5b97d   Mikulas Patocka   hpfs: add fstrim ...
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
  
  long hpfs_ioctl(struct file *file, unsigned cmd, unsigned long arg)
  {
  	switch (cmd) {
  		case FITRIM: {
  			struct fstrim_range range;
  			secno n_trimmed;
  			int r;
  			if (!capable(CAP_SYS_ADMIN))
  				return -EPERM;
  			if (copy_from_user(&range, (struct fstrim_range __user *)arg, sizeof(range)))
  				return -EFAULT;
  			r = hpfs_trim_fs(file_inode(file)->i_sb, range.start >> 9, (range.start + range.len) >> 9, (range.minlen + 511) >> 9, &n_trimmed);
  			if (r)
  				return r;
  			range.len = (u64)n_trimmed << 9;
  			if (copy_to_user((struct fstrim_range __user *)arg, &range, sizeof(range)))
  				return -EFAULT;
  			return 0;
  		}
  		default: {
  			return -ENOIOCTLCMD;
  		}
  	}
  }
e18b890bb   Christoph Lameter   [PATCH] slab: rem...
223
  static struct kmem_cache * hpfs_inode_cachep;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
224
225
226
227
  
  static struct inode *hpfs_alloc_inode(struct super_block *sb)
  {
  	struct hpfs_inode_info *ei;
d7b04097c   Firo Yang   hpfs: Remove unes...
228
  	ei = kmem_cache_alloc(hpfs_inode_cachep, GFP_NOFS);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
229
230
231
232
233
  	if (!ei)
  		return NULL;
  	ei->vfs_inode.i_version = 1;
  	return &ei->vfs_inode;
  }
fa0d7e3de   Nick Piggin   fs: icache RCU fr...
234
  static void hpfs_i_callback(struct rcu_head *head)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
235
  {
fa0d7e3de   Nick Piggin   fs: icache RCU fr...
236
  	struct inode *inode = container_of(head, struct inode, i_rcu);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
237
238
  	kmem_cache_free(hpfs_inode_cachep, hpfs_i(inode));
  }
fa0d7e3de   Nick Piggin   fs: icache RCU fr...
239
240
241
242
  static void hpfs_destroy_inode(struct inode *inode)
  {
  	call_rcu(&inode->i_rcu, hpfs_i_callback);
  }
51cc50685   Alexey Dobriyan   SL*B: drop kmem c...
243
  static void init_once(void *foo)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
244
245
  {
  	struct hpfs_inode_info *ei = (struct hpfs_inode_info *) foo;
a35afb830   Christoph Lameter   Remove SLAB_CTOR_...
246
  	inode_init_once(&ei->vfs_inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
247
  }
20c2df83d   Paul Mundt   mm: Remove slab d...
248

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
249
250
251
252
  static int init_inodecache(void)
  {
  	hpfs_inode_cachep = kmem_cache_create("hpfs_inode_cache",
  					     sizeof(struct hpfs_inode_info),
fffb60f93   Paul Jackson   [PATCH] cpuset me...
253
254
  					     0, (SLAB_RECLAIM_ACCOUNT|
  						SLAB_MEM_SPREAD),
20c2df83d   Paul Mundt   mm: Remove slab d...
255
  					     init_once);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
256
257
258
259
260
261
262
  	if (hpfs_inode_cachep == NULL)
  		return -ENOMEM;
  	return 0;
  }
  
  static void destroy_inodecache(void)
  {
8c0a85377   Kirill A. Shutemov   fs: push rcu_barr...
263
264
265
266
267
  	/*
  	 * Make sure all delayed rcu free inodes are flushed before we
  	 * destroy cache.
  	 */
  	rcu_barrier();
1a1d92c10   Alexey Dobriyan   [PATCH] Really ig...
268
  	kmem_cache_destroy(hpfs_inode_cachep);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
269
270
271
272
273
274
275
276
277
278
  }
  
  /*
   * A tiny parser for option strings, stolen from dosfs.
   * Stolen again from read-only hpfs.
   * And updated for table-driven option parsing.
   */
  
  enum {
  	Opt_help, Opt_uid, Opt_gid, Opt_umask, Opt_case_lower, Opt_case_asis,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
279
280
281
282
283
284
  	Opt_check_none, Opt_check_normal, Opt_check_strict,
  	Opt_err_cont, Opt_err_ro, Opt_err_panic,
  	Opt_eas_no, Opt_eas_ro, Opt_eas_rw,
  	Opt_chkdsk_no, Opt_chkdsk_errors, Opt_chkdsk_always,
  	Opt_timeshift, Opt_err,
  };
a447c0932   Steven Whitehouse   vfs: Use const fo...
285
  static const match_table_t tokens = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
286
287
288
289
290
291
  	{Opt_help, "help"},
  	{Opt_uid, "uid=%u"},
  	{Opt_gid, "gid=%u"},
  	{Opt_umask, "umask=%o"},
  	{Opt_case_lower, "case=lower"},
  	{Opt_case_asis, "case=asis"},
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
  	{Opt_check_none, "check=none"},
  	{Opt_check_normal, "check=normal"},
  	{Opt_check_strict, "check=strict"},
  	{Opt_err_cont, "errors=continue"},
  	{Opt_err_ro, "errors=remount-ro"},
  	{Opt_err_panic, "errors=panic"},
  	{Opt_eas_no, "eas=no"},
  	{Opt_eas_ro, "eas=ro"},
  	{Opt_eas_rw, "eas=rw"},
  	{Opt_chkdsk_no, "chkdsk=no"},
  	{Opt_chkdsk_errors, "chkdsk=errors"},
  	{Opt_chkdsk_always, "chkdsk=always"},
  	{Opt_timeshift, "timeshift=%d"},
  	{Opt_err, NULL},
  };
0e1a43c71   Eric W. Biederman   userns: Convert h...
307
  static int parse_opts(char *opts, kuid_t *uid, kgid_t *gid, umode_t *umask,
0fe105aa2   Mikulas Patocka   HPFS: Remove CR/L...
308
  		      int *lowercase, int *eas, int *chk, int *errs,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
309
310
311
312
313
314
315
  		      int *chkdsk, int *timeshift)
  {
  	char *p;
  	int option;
  
  	if (!opts)
  		return 1;
b7cb1ce22   Fabian Frederick   fs/hpfs: convert ...
316
317
  	/*pr_info("Parsing opts: '%s'
  ",opts);*/
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
318
319
320
321
322
323
324
325
326
327
328
329
330
331
  
  	while ((p = strsep(&opts, ",")) != NULL) {
  		substring_t args[MAX_OPT_ARGS];
  		int token;
  		if (!*p)
  			continue;
  
  		token = match_token(p, tokens, args);
  		switch (token) {
  		case Opt_help:
  			return 2;
  		case Opt_uid:
  			if (match_int(args, &option))
  				return 0;
0e1a43c71   Eric W. Biederman   userns: Convert h...
332
333
334
  			*uid = make_kuid(current_user_ns(), option);
  			if (!uid_valid(*uid))
  				return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
335
336
337
338
  			break;
  		case Opt_gid:
  			if (match_int(args, &option))
  				return 0;
0e1a43c71   Eric W. Biederman   userns: Convert h...
339
340
341
  			*gid = make_kgid(current_user_ns(), option);
  			if (!gid_valid(*gid))
  				return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
342
343
344
345
346
347
348
349
350
351
352
353
  			break;
  		case Opt_umask:
  			if (match_octal(args, &option))
  				return 0;
  			*umask = option;
  			break;
  		case Opt_case_lower:
  			*lowercase = 1;
  			break;
  		case Opt_case_asis:
  			*lowercase = 0;
  			break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
  		case Opt_check_none:
  			*chk = 0;
  			break;
  		case Opt_check_normal:
  			*chk = 1;
  			break;
  		case Opt_check_strict:
  			*chk = 2;
  			break;
  		case Opt_err_cont:
  			*errs = 0;
  			break;
  		case Opt_err_ro:
  			*errs = 1;
  			break;
  		case Opt_err_panic:
  			*errs = 2;
  			break;
  		case Opt_eas_no:
  			*eas = 0;
  			break;
  		case Opt_eas_ro:
  			*eas = 1;
  			break;
  		case Opt_eas_rw:
  			*eas = 2;
  			break;
  		case Opt_chkdsk_no:
  			*chkdsk = 0;
  			break;
  		case Opt_chkdsk_errors:
  			*chkdsk = 1;
  			break;
  		case Opt_chkdsk_always:
  			*chkdsk = 2;
  			break;
  		case Opt_timeshift:
  		{
  			int m = 1;
  			char *rhs = args[0].from;
  			if (!rhs || !*rhs)
  				return 0;
  			if (*rhs == '-') m = -1;
  			if (*rhs == '+' || *rhs == '-') rhs++;
  			*timeshift = simple_strtoul(rhs, &rhs, 0) * m;
  			if (*rhs)
  				return 0;
  			break;
  		}
  		default:
  			return 0;
  		}
  	}
  	return 1;
  }
  
  static inline void hpfs_help(void)
  {
b7cb1ce22   Fabian Frederick   fs/hpfs: convert ...
412
413
  	pr_info("
  \
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
414
415
416
417
418
419
420
421
422
423
424
425
426
427
  HPFS filesystem options:
  \
        help              do not mount and display this text
  \
        uid=xxx           set uid of files that don't have uid specified in eas
  \
        gid=xxx           set gid of files that don't have gid specified in eas
  \
        umask=xxx         set mode of files that don't have mode specified in eas
  \
        case=lower        lowercase all files
  \
        case=asis         do not lowercase files (default)
  \
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
428
429
430
431
432
433
434
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
        check=none        no fs checks - kernel may crash on corrupted filesystem
  \
        check=normal      do some checks - it should not crash (default)
  \
        check=strict      do extra time-consuming checks, used for debugging
  \
        errors=continue   continue on errors
  \
        errors=remount-ro remount read-only if errors found (default)
  \
        errors=panic      panic on errors
  \
        chkdsk=no         do not mark fs for chkdsking even if there were errors
  \
        chkdsk=errors     mark fs dirty if errors found (default)
  \
        chkdsk=always     always mark fs dirty - used for debugging
  \
        eas=no            ignore extended attributes
  \
        eas=ro            read but do not write extended attributes
  \
        eas=rw            r/w eas => enables chmod, chown, mknod, ln -s (default)
  \
        timeshift=nnn	add nnn seconds to file times
  \
  
  ");
  }
  
  static int hpfs_remount_fs(struct super_block *s, int *flags, char *data)
  {
0e1a43c71   Eric W. Biederman   userns: Convert h...
460
461
  	kuid_t uid;
  	kgid_t gid;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
462
  	umode_t umask;
0fe105aa2   Mikulas Patocka   HPFS: Remove CR/L...
463
  	int lowercase, eas, chk, errs, chkdsk, timeshift;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
464
465
  	int o;
  	struct hpfs_sb_info *sbi = hpfs_sb(s);
6d9c1fd42   Miklos Szeredi   mount options: fi...
466
  	char *new_opts = kstrdup(data, GFP_KERNEL);
ce657611b   Sanidhya Kashyap   hpfs: kstrdup() o...
467

5cb3ec3d6   Mikulas Patocka   hpfs: fix remount...
468
  	if (data && !new_opts)
ce657611b   Sanidhya Kashyap   hpfs: kstrdup() o...
469
  		return -ENOMEM;
02b9984d6   Theodore Ts'o   fs: push sync_fil...
470
  	sync_filesystem(s);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
471
  	*flags |= MS_NOATIME;
ce657611b   Sanidhya Kashyap   hpfs: kstrdup() o...
472

9a311b96c   Arnd Bergmann   hpfs: remove the BKL
473
  	hpfs_lock(s);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
474
475
  	uid = sbi->sb_uid; gid = sbi->sb_gid;
  	umask = 0777 & ~sbi->sb_mode;
0fe105aa2   Mikulas Patocka   HPFS: Remove CR/L...
476
  	lowercase = sbi->sb_lowercase;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
477
478
  	eas = sbi->sb_eas; chk = sbi->sb_chk; chkdsk = sbi->sb_chkdsk;
  	errs = sbi->sb_err; timeshift = sbi->sb_timeshift;
0fe105aa2   Mikulas Patocka   HPFS: Remove CR/L...
479
  	if (!(o = parse_opts(data, &uid, &gid, &umask, &lowercase,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
480
  	    &eas, &chk, &errs, &chkdsk, &timeshift))) {
a19189e55   Fabian Frederick   fs/hpfs: increase...
481
482
  		pr_err("bad mount options.
  ");
6d9c1fd42   Miklos Szeredi   mount options: fi...
483
  		goto out_err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
484
485
486
  	}
  	if (o == 2) {
  		hpfs_help();
6d9c1fd42   Miklos Szeredi   mount options: fi...
487
  		goto out_err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
488
489
  	}
  	if (timeshift != sbi->sb_timeshift) {
a19189e55   Fabian Frederick   fs/hpfs: increase...
490
491
  		pr_err("timeshift can't be changed using remount.
  ");
6d9c1fd42   Miklos Szeredi   mount options: fi...
492
  		goto out_err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
493
494
495
496
497
498
  	}
  
  	unmark_dirty(s);
  
  	sbi->sb_uid = uid; sbi->sb_gid = gid;
  	sbi->sb_mode = 0777 & ~umask;
0fe105aa2   Mikulas Patocka   HPFS: Remove CR/L...
499
  	sbi->sb_lowercase = lowercase;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
500
501
  	sbi->sb_eas = eas; sbi->sb_chk = chk; sbi->sb_chkdsk = chkdsk;
  	sbi->sb_err = errs; sbi->sb_timeshift = timeshift;
dab4c82a6   Mikulas Patocka   HPFS: Fix a bug t...
502
  	if (!(*flags & MS_RDONLY)) mark_dirty(s, 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
503

5cb3ec3d6   Mikulas Patocka   hpfs: fix remount...
504
505
  	if (new_opts)
  		replace_mount_options(s, new_opts);
6d9c1fd42   Miklos Szeredi   mount options: fi...
506

9a311b96c   Arnd Bergmann   hpfs: remove the BKL
507
  	hpfs_unlock(s);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
508
  	return 0;
6d9c1fd42   Miklos Szeredi   mount options: fi...
509
510
  
  out_err:
9a311b96c   Arnd Bergmann   hpfs: remove the BKL
511
  	hpfs_unlock(s);
6d9c1fd42   Miklos Szeredi   mount options: fi...
512
513
  	kfree(new_opts);
  	return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
514
515
516
  }
  
  /* Super operations */
ee9b6d61a   Josef 'Jeff' Sipek   [PATCH] Mark stru...
517
  static const struct super_operations hpfs_sops =
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
518
519
520
  {
  	.alloc_inode	= hpfs_alloc_inode,
  	.destroy_inode	= hpfs_destroy_inode,
ea5440092   Al Viro   switch hpfs to ->...
521
  	.evict_inode	= hpfs_evict_inode,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
522
523
524
  	.put_super	= hpfs_put_super,
  	.statfs		= hpfs_statfs,
  	.remount_fs	= hpfs_remount_fs,
6d9c1fd42   Miklos Szeredi   mount options: fi...
525
  	.show_options	= generic_show_options,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
526
527
528
529
530
531
532
533
534
535
  };
  
  static int hpfs_fill_super(struct super_block *s, void *options, int silent)
  {
  	struct buffer_head *bh0, *bh1, *bh2;
  	struct hpfs_boot_block *bootblock;
  	struct hpfs_super_block *superblock;
  	struct hpfs_spare_block *spareblock;
  	struct hpfs_sb_info *sbi;
  	struct inode *root;
0e1a43c71   Eric W. Biederman   userns: Convert h...
536
537
  	kuid_t uid;
  	kgid_t gid;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
538
  	umode_t umask;
0fe105aa2   Mikulas Patocka   HPFS: Remove CR/L...
539
  	int lowercase, eas, chk, errs, chkdsk, timeshift;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
540
541
542
543
544
545
  
  	dnode_secno root_dno;
  	struct hpfs_dirent *de = NULL;
  	struct quad_buffer_head qbh;
  
  	int o;
6d9c1fd42   Miklos Szeredi   mount options: fi...
546
  	save_mount_options(s, options);
f8314dc60   Panagiotis Issaris   [PATCH] fs: Conve...
547
  	sbi = kzalloc(sizeof(*sbi), GFP_KERNEL);
db7192221   Jan Blunck   BKL: Explicitly a...
548
  	if (!sbi) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
549
  		return -ENOMEM;
db7192221   Jan Blunck   BKL: Explicitly a...
550
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
551
  	s->s_fs_info = sbi;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
552

7dd29d8d8   Mikulas Patocka   HPFS: Introduce a...
553
554
  	mutex_init(&sbi->hpfs_mutex);
  	hpfs_lock(s);
de395b8ac   David Howells   CRED: Wrap task c...
555
556
  	uid = current_uid();
  	gid = current_gid();
ce3b0f8d5   Al Viro   New helper - curr...
557
  	umask = current_umask();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
558
  	lowercase = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
559
560
561
562
563
  	eas = 2;
  	chk = 1;
  	errs = 1;
  	chkdsk = 1;
  	timeshift = 0;
0fe105aa2   Mikulas Patocka   HPFS: Remove CR/L...
564
  	if (!(o = parse_opts(options, &uid, &gid, &umask, &lowercase,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
565
  	    &eas, &chk, &errs, &chkdsk, &timeshift))) {
a19189e55   Fabian Frederick   fs/hpfs: increase...
566
567
  		pr_err("bad mount options.
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
  		goto bail0;
  	}
  	if (o==2) {
  		hpfs_help();
  		goto bail0;
  	}
  
  	/*sbi->sb_mounting = 1;*/
  	sb_set_blocksize(s, 512);
  	sbi->sb_fs_size = -1;
  	if (!(bootblock = hpfs_map_sector(s, 0, &bh0, 0))) goto bail1;
  	if (!(superblock = hpfs_map_sector(s, 16, &bh1, 1))) goto bail2;
  	if (!(spareblock = hpfs_map_sector(s, 17, &bh2, 0))) goto bail3;
  
  	/* Check magics */
0b69760be   Mikulas Patocka   HPFS: Fix endiani...
583
584
585
  	if (/*le16_to_cpu(bootblock->magic) != BB_MAGIC
  	    ||*/ le32_to_cpu(superblock->magic) != SB_MAGIC
  	    || le32_to_cpu(spareblock->magic) != SP_MAGIC) {
b7cb1ce22   Fabian Frederick   fs/hpfs: convert ...
586
  		if (!silent)
a19189e55   Fabian Frederick   fs/hpfs: increase...
587
588
  			pr_err("Bad magic ... probably not HPFS
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
589
590
591
592
593
594
  		goto bail4;
  	}
  
  	/* Check version */
  	if (!(s->s_flags & MS_RDONLY) &&
  	      superblock->funcversion != 2 && superblock->funcversion != 3) {
a19189e55   Fabian Frederick   fs/hpfs: increase...
595
596
  		pr_err("Bad version %d,%d. Mount readonly to go around
  ",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
597
  			(int)superblock->version, (int)superblock->funcversion);
a19189e55   Fabian Frederick   fs/hpfs: increase...
598
599
  		pr_err("please try recent version of HPFS driver at http://artax.karlin.mff.cuni.cz/~mikulas/vyplody/hpfs/index-e.cgi and if it still can't understand this format, contact author - mikulas@artax.karlin.mff.cuni.cz
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
600
601
602
603
604
605
606
607
  		goto bail4;
  	}
  
  	s->s_flags |= MS_NOATIME;
  
  	/* Fill superblock stuff */
  	s->s_magic = HPFS_SUPER_MAGIC;
  	s->s_op = &hpfs_sops;
43d344d77   Al Viro   switch hpfs
608
  	s->s_d_op = &hpfs_dentry_operations;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
609

0b69760be   Mikulas Patocka   HPFS: Fix endiani...
610
611
612
613
614
615
  	sbi->sb_root = le32_to_cpu(superblock->root);
  	sbi->sb_fs_size = le32_to_cpu(superblock->n_sectors);
  	sbi->sb_bitmaps = le32_to_cpu(superblock->bitmaps);
  	sbi->sb_dirband_start = le32_to_cpu(superblock->dir_band_start);
  	sbi->sb_dirband_size = le32_to_cpu(superblock->n_dir_band);
  	sbi->sb_dmap = le32_to_cpu(superblock->dir_band_bitmap);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
616
617
618
619
620
621
  	sbi->sb_uid = uid;
  	sbi->sb_gid = gid;
  	sbi->sb_mode = 0777 & ~umask;
  	sbi->sb_n_free = -1;
  	sbi->sb_n_free_dnodes = -1;
  	sbi->sb_lowercase = lowercase;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
622
623
624
625
626
627
628
629
630
  	sbi->sb_eas = eas;
  	sbi->sb_chk = chk;
  	sbi->sb_chkdsk = chkdsk;
  	sbi->sb_err = errs;
  	sbi->sb_timeshift = timeshift;
  	sbi->sb_was_error = 0;
  	sbi->sb_cp_table = NULL;
  	sbi->sb_c_bitmap = -1;
  	sbi->sb_max_fwd_alloc = 0xffffff;
3ebacb050   Mikulas Patocka   hpfs: better test...
631
632
633
634
635
636
  
  	if (sbi->sb_fs_size >= 0x80000000) {
  		hpfs_error(s, "invalid size in superblock: %08x",
  			(unsigned)sbi->sb_fs_size);
  		goto bail4;
  	}
a64eefaac   Mikulas Patocka   hpfs: support hot...
637
638
  	if (spareblock->n_spares_used)
  		hpfs_load_hotfix_map(s, spareblock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
639
  	/* Load bitmap directory */
0b69760be   Mikulas Patocka   HPFS: Fix endiani...
640
  	if (!(sbi->sb_bmp_dir = hpfs_load_bitmap_directory(s, le32_to_cpu(superblock->bitmaps))))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
641
642
643
644
645
  		goto bail4;
  	
  	/* Check for general fs errors*/
  	if (spareblock->dirty && !spareblock->old_wrote) {
  		if (errs == 2) {
a19189e55   Fabian Frederick   fs/hpfs: increase...
646
647
  			pr_err("Improperly stopped, not mounted
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
648
649
650
651
652
653
654
655
656
657
  			goto bail4;
  		}
  		hpfs_error(s, "improperly stopped");
  	}
  
  	if (!(s->s_flags & MS_RDONLY)) {
  		spareblock->dirty = 1;
  		spareblock->old_wrote = 0;
  		mark_buffer_dirty(bh2);
  	}
0b69760be   Mikulas Patocka   HPFS: Fix endiani...
658
  	if (le32_to_cpu(spareblock->n_dnode_spares) != le32_to_cpu(spareblock->n_dnode_spares_free)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
659
  		if (errs >= 2) {
a19189e55   Fabian Frederick   fs/hpfs: increase...
660
661
  			pr_err("Spare dnodes used, try chkdsk
  ");
dab4c82a6   Mikulas Patocka   HPFS: Fix a bug t...
662
  			mark_dirty(s, 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
663
664
665
  			goto bail4;
  		}
  		hpfs_error(s, "warning: spare dnodes used, try chkdsk");
b7cb1ce22   Fabian Frederick   fs/hpfs: convert ...
666
  		if (errs == 0)
a19189e55   Fabian Frederick   fs/hpfs: increase...
667
668
  			pr_err("Proceeding, but your filesystem could be corrupted if you delete files or directories
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
669
670
671
  	}
  	if (chk) {
  		unsigned a;
0b69760be   Mikulas Patocka   HPFS: Fix endiani...
672
673
  		if (le32_to_cpu(superblock->dir_band_end) - le32_to_cpu(superblock->dir_band_start) + 1 != le32_to_cpu(superblock->n_dir_band) ||
  		    le32_to_cpu(superblock->dir_band_end) < le32_to_cpu(superblock->dir_band_start) || le32_to_cpu(superblock->n_dir_band) > 0x4000) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
674
  			hpfs_error(s, "dir band size mismatch: dir_band_start==%08x, dir_band_end==%08x, n_dir_band==%08x",
0b69760be   Mikulas Patocka   HPFS: Fix endiani...
675
  				le32_to_cpu(superblock->dir_band_start), le32_to_cpu(superblock->dir_band_end), le32_to_cpu(superblock->n_dir_band));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
676
677
678
679
  			goto bail4;
  		}
  		a = sbi->sb_dirband_size;
  		sbi->sb_dirband_size = 0;
0b69760be   Mikulas Patocka   HPFS: Fix endiani...
680
681
682
  		if (hpfs_chk_sectors(s, le32_to_cpu(superblock->dir_band_start), le32_to_cpu(superblock->n_dir_band), "dir_band") ||
  		    hpfs_chk_sectors(s, le32_to_cpu(superblock->dir_band_bitmap), 4, "dir_band_bitmap") ||
  		    hpfs_chk_sectors(s, le32_to_cpu(superblock->bitmaps), 4, "bitmaps")) {
dab4c82a6   Mikulas Patocka   HPFS: Fix a bug t...
683
  			mark_dirty(s, 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
684
685
686
  			goto bail4;
  		}
  		sbi->sb_dirband_size = a;
b7cb1ce22   Fabian Frederick   fs/hpfs: convert ...
687
  	} else
a19189e55   Fabian Frederick   fs/hpfs: increase...
688
689
  		pr_err("You really don't want any checks? You are crazy...
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
690
691
  
  	/* Load code page table */
0b69760be   Mikulas Patocka   HPFS: Fix endiani...
692
693
  	if (le32_to_cpu(spareblock->n_code_pages))
  		if (!(sbi->sb_cp_table = hpfs_load_code_page(s, le32_to_cpu(spareblock->code_page_dir))))
a19189e55   Fabian Frederick   fs/hpfs: increase...
694
695
  			pr_err("code page support is disabled
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
696
697
698
699
700
701
702
703
704
705
706
  
  	brelse(bh2);
  	brelse(bh1);
  	brelse(bh0);
  
  	root = iget_locked(s, sbi->sb_root);
  	if (!root)
  		goto bail0;
  	hpfs_init_inode(root);
  	hpfs_read_inode(root);
  	unlock_new_inode(root);
48fde701a   Al Viro   switch open-coded...
707
708
  	s->s_root = d_make_root(root);
  	if (!s->s_root)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
709
  		goto bail0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
710
711
712
713
714
715
716
717
718
719
720
  
  	/*
  	 * find the root directory's . pointer & finish filling in the inode
  	 */
  
  	root_dno = hpfs_fnode_dno(s, sbi->sb_root);
  	if (root_dno)
  		de = map_dirent(root, root_dno, "\001\001", 2, NULL, &qbh);
  	if (!de)
  		hpfs_error(s, "unable to find root dir");
  	else {
0b69760be   Mikulas Patocka   HPFS: Fix endiani...
721
  		root->i_atime.tv_sec = local_to_gmt(s, le32_to_cpu(de->read_date));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
722
  		root->i_atime.tv_nsec = 0;
0b69760be   Mikulas Patocka   HPFS: Fix endiani...
723
  		root->i_mtime.tv_sec = local_to_gmt(s, le32_to_cpu(de->write_date));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
724
  		root->i_mtime.tv_nsec = 0;
0b69760be   Mikulas Patocka   HPFS: Fix endiani...
725
  		root->i_ctime.tv_sec = local_to_gmt(s, le32_to_cpu(de->creation_date));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
726
  		root->i_ctime.tv_nsec = 0;
de5e2b362   Al Viro   hpfs: endianness ...
727
  		hpfs_i(root)->i_ea_size = le32_to_cpu(de->ea_size);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
728
729
730
731
732
733
734
  		hpfs_i(root)->i_parent_dir = root->i_ino;
  		if (root->i_size == -1)
  			root->i_size = 2048;
  		if (root->i_blocks == -1)
  			root->i_blocks = 5;
  		hpfs_brelse4(&qbh);
  	}
7dd29d8d8   Mikulas Patocka   HPFS: Introduce a...
735
  	hpfs_unlock(s);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
736
737
738
739
740
741
742
  	return 0;
  
  bail4:	brelse(bh2);
  bail3:	brelse(bh1);
  bail2:	brelse(bh0);
  bail1:
  bail0:
7dd29d8d8   Mikulas Patocka   HPFS: Introduce a...
743
  	hpfs_unlock(s);
30687e0a4   Al Viro   hpfs: make freein...
744
  	free_sbi(sbi);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
745
746
  	return -EINVAL;
  }
152a08366   Al Viro   new helper: mount...
747
748
  static struct dentry *hpfs_mount(struct file_system_type *fs_type,
  	int flags, const char *dev_name, void *data)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
749
  {
152a08366   Al Viro   new helper: mount...
750
  	return mount_bdev(fs_type, flags, dev_name, data, hpfs_fill_super);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
751
752
753
754
755
  }
  
  static struct file_system_type hpfs_fs_type = {
  	.owner		= THIS_MODULE,
  	.name		= "hpfs",
152a08366   Al Viro   new helper: mount...
756
  	.mount		= hpfs_mount,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
757
758
759
  	.kill_sb	= kill_block_super,
  	.fs_flags	= FS_REQUIRES_DEV,
  };
3e64fe5b2   Eric W. Biederman   fs: Limit sys_mou...
760
  MODULE_ALIAS_FS("hpfs");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
  
  static int __init init_hpfs_fs(void)
  {
  	int err = init_inodecache();
  	if (err)
  		goto out1;
  	err = register_filesystem(&hpfs_fs_type);
  	if (err)
  		goto out;
  	return 0;
  out:
  	destroy_inodecache();
  out1:
  	return err;
  }
  
  static void __exit exit_hpfs_fs(void)
  {
  	unregister_filesystem(&hpfs_fs_type);
  	destroy_inodecache();
  }
  
  module_init(init_hpfs_fs)
  module_exit(exit_hpfs_fs)
  MODULE_LICENSE("GPL");