diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2019-07-19 14:33:22 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2019-07-19 14:33:22 -0400 |
commit | d2fbf4b6d585e40f2369675148777abce3abd0e7 (patch) | |
tree | 0749aa1da31af21d752585dc7a2f31715d434ee3 /fs/adfs/super.c | |
parent | 933a90bf4f3505f8ec83bda21a3c7d70d7c2b426 (diff) | |
parent | b4ed8f75c82876342b3399942427392ba5f3bbb5 (diff) |
Merge branch 'work.adfs' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
Pull adfs updates from Al Viro:
"More ADFS patches from Russell King"
* 'work.adfs' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs:
fs/adfs: add time stamp and file type helpers
fs/adfs: super: limit idlen according to directory type
fs/adfs: super: fix use-after-free bug
fs/adfs: super: safely update options on remount
fs/adfs: super: correct superblock flags
fs/adfs: clean up indirect disc addresses and fragment IDs
fs/adfs: clean up error message printing
fs/adfs: use %pV for error messages
fs/adfs: use format_version from disc_record
fs/adfs: add helper to get filesystem size
fs/adfs: add helper to get discrecord from map
fs/adfs: correct disc record structure
Diffstat (limited to 'fs/adfs/super.c')
-rw-r--r-- | fs/adfs/super.c | 121 |
1 files changed, 71 insertions, 50 deletions
diff --git a/fs/adfs/super.c b/fs/adfs/super.c index ffb669f9bba7..65b04ebb51c3 100644 --- a/fs/adfs/super.c +++ b/fs/adfs/super.c | |||
@@ -6,7 +6,6 @@ | |||
6 | */ | 6 | */ |
7 | #include <linux/module.h> | 7 | #include <linux/module.h> |
8 | #include <linux/init.h> | 8 | #include <linux/init.h> |
9 | #include <linux/buffer_head.h> | ||
10 | #include <linux/parser.h> | 9 | #include <linux/parser.h> |
11 | #include <linux/mount.h> | 10 | #include <linux/mount.h> |
12 | #include <linux/seq_file.h> | 11 | #include <linux/seq_file.h> |
@@ -17,25 +16,42 @@ | |||
17 | #include "dir_f.h" | 16 | #include "dir_f.h" |
18 | #include "dir_fplus.h" | 17 | #include "dir_fplus.h" |
19 | 18 | ||
19 | #define ADFS_SB_FLAGS SB_NOATIME | ||
20 | |||
20 | #define ADFS_DEFAULT_OWNER_MASK S_IRWXU | 21 | #define ADFS_DEFAULT_OWNER_MASK S_IRWXU |
21 | #define ADFS_DEFAULT_OTHER_MASK (S_IRWXG | S_IRWXO) | 22 | #define ADFS_DEFAULT_OTHER_MASK (S_IRWXG | S_IRWXO) |
22 | 23 | ||
23 | void __adfs_error(struct super_block *sb, const char *function, const char *fmt, ...) | 24 | void __adfs_error(struct super_block *sb, const char *function, const char *fmt, ...) |
24 | { | 25 | { |
25 | char error_buf[128]; | 26 | struct va_format vaf; |
26 | va_list args; | 27 | va_list args; |
27 | 28 | ||
28 | va_start(args, fmt); | 29 | va_start(args, fmt); |
29 | vsnprintf(error_buf, sizeof(error_buf), fmt, args); | 30 | vaf.fmt = fmt; |
30 | va_end(args); | 31 | vaf.va = &args; |
31 | 32 | ||
32 | printk(KERN_CRIT "ADFS-fs error (device %s)%s%s: %s\n", | 33 | printk(KERN_CRIT "ADFS-fs error (device %s)%s%s: %pV\n", |
33 | sb->s_id, function ? ": " : "", | 34 | sb->s_id, function ? ": " : "", |
34 | function ? function : "", error_buf); | 35 | function ? function : "", &vaf); |
36 | |||
37 | va_end(args); | ||
38 | } | ||
39 | |||
40 | void adfs_msg(struct super_block *sb, const char *pfx, const char *fmt, ...) | ||
41 | { | ||
42 | struct va_format vaf; | ||
43 | va_list args; | ||
44 | |||
45 | va_start(args, fmt); | ||
46 | vaf.fmt = fmt; | ||
47 | vaf.va = &args; | ||
48 | printk("%sADFS-fs (%s): %pV\n", pfx, sb->s_id, &vaf); | ||
49 | va_end(args); | ||
35 | } | 50 | } |
36 | 51 | ||
37 | static int adfs_checkdiscrecord(struct adfs_discrecord *dr) | 52 | static int adfs_checkdiscrecord(struct adfs_discrecord *dr) |
38 | { | 53 | { |
54 | unsigned int max_idlen; | ||
39 | int i; | 55 | int i; |
40 | 56 | ||
41 | /* sector size must be 256, 512 or 1024 bytes */ | 57 | /* sector size must be 256, 512 or 1024 bytes */ |
@@ -55,8 +71,13 @@ static int adfs_checkdiscrecord(struct adfs_discrecord *dr) | |||
55 | if (le32_to_cpu(dr->disc_size_high) >> dr->log2secsize) | 71 | if (le32_to_cpu(dr->disc_size_high) >> dr->log2secsize) |
56 | return 1; | 72 | return 1; |
57 | 73 | ||
58 | /* idlen must be no greater than 19 v2 [1.0] */ | 74 | /* |
59 | if (dr->idlen > 19) | 75 | * Maximum idlen is limited to 16 bits for new directories by |
76 | * the three-byte storage of an indirect disc address. For | ||
77 | * big directories, idlen must be no greater than 19 v2 [1.0] | ||
78 | */ | ||
79 | max_idlen = dr->format_version ? 19 : 16; | ||
80 | if (dr->idlen > max_idlen) | ||
60 | return 1; | 81 | return 1; |
61 | 82 | ||
62 | /* reserved bytes should be zero */ | 83 | /* reserved bytes should be zero */ |
@@ -152,10 +173,10 @@ static const match_table_t tokens = { | |||
152 | {Opt_err, NULL} | 173 | {Opt_err, NULL} |
153 | }; | 174 | }; |
154 | 175 | ||
155 | static int parse_options(struct super_block *sb, char *options) | 176 | static int parse_options(struct super_block *sb, struct adfs_sb_info *asb, |
177 | char *options) | ||
156 | { | 178 | { |
157 | char *p; | 179 | char *p; |
158 | struct adfs_sb_info *asb = ADFS_SB(sb); | ||
159 | int option; | 180 | int option; |
160 | 181 | ||
161 | if (!options) | 182 | if (!options) |
@@ -199,8 +220,9 @@ static int parse_options(struct super_block *sb, char *options) | |||
199 | asb->s_ftsuffix = option; | 220 | asb->s_ftsuffix = option; |
200 | break; | 221 | break; |
201 | default: | 222 | default: |
202 | printk("ADFS-fs: unrecognised mount option \"%s\" " | 223 | adfs_msg(sb, KERN_ERR, |
203 | "or missing value\n", p); | 224 | "unrecognised mount option \"%s\" or missing value", |
225 | p); | ||
204 | return -EINVAL; | 226 | return -EINVAL; |
205 | } | 227 | } |
206 | } | 228 | } |
@@ -209,21 +231,31 @@ static int parse_options(struct super_block *sb, char *options) | |||
209 | 231 | ||
210 | static int adfs_remount(struct super_block *sb, int *flags, char *data) | 232 | static int adfs_remount(struct super_block *sb, int *flags, char *data) |
211 | { | 233 | { |
234 | struct adfs_sb_info temp_asb; | ||
235 | int ret; | ||
236 | |||
212 | sync_filesystem(sb); | 237 | sync_filesystem(sb); |
213 | *flags |= SB_NODIRATIME; | 238 | *flags |= ADFS_SB_FLAGS; |
214 | return parse_options(sb, data); | 239 | |
240 | temp_asb = *ADFS_SB(sb); | ||
241 | ret = parse_options(sb, &temp_asb, data); | ||
242 | if (ret == 0) | ||
243 | *ADFS_SB(sb) = temp_asb; | ||
244 | |||
245 | return ret; | ||
215 | } | 246 | } |
216 | 247 | ||
217 | static int adfs_statfs(struct dentry *dentry, struct kstatfs *buf) | 248 | static int adfs_statfs(struct dentry *dentry, struct kstatfs *buf) |
218 | { | 249 | { |
219 | struct super_block *sb = dentry->d_sb; | 250 | struct super_block *sb = dentry->d_sb; |
220 | struct adfs_sb_info *sbi = ADFS_SB(sb); | 251 | struct adfs_sb_info *sbi = ADFS_SB(sb); |
252 | struct adfs_discrecord *dr = adfs_map_discrecord(sbi->s_map); | ||
221 | u64 id = huge_encode_dev(sb->s_bdev->bd_dev); | 253 | u64 id = huge_encode_dev(sb->s_bdev->bd_dev); |
222 | 254 | ||
223 | buf->f_type = ADFS_SUPER_MAGIC; | 255 | buf->f_type = ADFS_SUPER_MAGIC; |
224 | buf->f_namelen = sbi->s_namelen; | 256 | buf->f_namelen = sbi->s_namelen; |
225 | buf->f_bsize = sb->s_blocksize; | 257 | buf->f_bsize = sb->s_blocksize; |
226 | buf->f_blocks = sbi->s_size; | 258 | buf->f_blocks = adfs_disc_size(dr) >> sb->s_blocksize_bits; |
227 | buf->f_files = sbi->s_ids_per_zone * sbi->s_map_size; | 259 | buf->f_files = sbi->s_ids_per_zone * sbi->s_map_size; |
228 | buf->f_bavail = | 260 | buf->f_bavail = |
229 | buf->f_bfree = adfs_map_free(sb); | 261 | buf->f_bfree = adfs_map_free(sb); |
@@ -327,8 +359,7 @@ static struct adfs_discmap *adfs_read_map(struct super_block *sb, struct adfs_di | |||
327 | i = zone - 1; | 359 | i = zone - 1; |
328 | dm[0].dm_startblk = 0; | 360 | dm[0].dm_startblk = 0; |
329 | dm[0].dm_startbit = ADFS_DR_SIZE_BITS; | 361 | dm[0].dm_startbit = ADFS_DR_SIZE_BITS; |
330 | dm[i].dm_endbit = (le32_to_cpu(dr->disc_size_high) << (32 - dr->log2bpmb)) + | 362 | dm[i].dm_endbit = (adfs_disc_size(dr) >> dr->log2bpmb) + |
331 | (le32_to_cpu(dr->disc_size) >> dr->log2bpmb) + | ||
332 | (ADFS_DR_SIZE_BITS - i * zone_size); | 363 | (ADFS_DR_SIZE_BITS - i * zone_size); |
333 | 364 | ||
334 | if (adfs_checkmap(sb, dm)) | 365 | if (adfs_checkmap(sb, dm)) |
@@ -344,27 +375,18 @@ error_free: | |||
344 | return ERR_PTR(-EIO); | 375 | return ERR_PTR(-EIO); |
345 | } | 376 | } |
346 | 377 | ||
347 | static inline unsigned long adfs_discsize(struct adfs_discrecord *dr, int block_bits) | ||
348 | { | ||
349 | unsigned long discsize; | ||
350 | |||
351 | discsize = le32_to_cpu(dr->disc_size_high) << (32 - block_bits); | ||
352 | discsize |= le32_to_cpu(dr->disc_size) >> block_bits; | ||
353 | |||
354 | return discsize; | ||
355 | } | ||
356 | |||
357 | static int adfs_fill_super(struct super_block *sb, void *data, int silent) | 378 | static int adfs_fill_super(struct super_block *sb, void *data, int silent) |
358 | { | 379 | { |
359 | struct adfs_discrecord *dr; | 380 | struct adfs_discrecord *dr; |
360 | struct buffer_head *bh; | 381 | struct buffer_head *bh; |
361 | struct object_info root_obj; | 382 | struct object_info root_obj; |
362 | unsigned char *b_data; | 383 | unsigned char *b_data; |
384 | unsigned int blocksize; | ||
363 | struct adfs_sb_info *asb; | 385 | struct adfs_sb_info *asb; |
364 | struct inode *root; | 386 | struct inode *root; |
365 | int ret = -EINVAL; | 387 | int ret = -EINVAL; |
366 | 388 | ||
367 | sb->s_flags |= SB_NODIRATIME; | 389 | sb->s_flags |= ADFS_SB_FLAGS; |
368 | 390 | ||
369 | asb = kzalloc(sizeof(*asb), GFP_KERNEL); | 391 | asb = kzalloc(sizeof(*asb), GFP_KERNEL); |
370 | if (!asb) | 392 | if (!asb) |
@@ -378,12 +400,12 @@ static int adfs_fill_super(struct super_block *sb, void *data, int silent) | |||
378 | asb->s_other_mask = ADFS_DEFAULT_OTHER_MASK; | 400 | asb->s_other_mask = ADFS_DEFAULT_OTHER_MASK; |
379 | asb->s_ftsuffix = 0; | 401 | asb->s_ftsuffix = 0; |
380 | 402 | ||
381 | if (parse_options(sb, data)) | 403 | if (parse_options(sb, asb, data)) |
382 | goto error; | 404 | goto error; |
383 | 405 | ||
384 | sb_set_blocksize(sb, BLOCK_SIZE); | 406 | sb_set_blocksize(sb, BLOCK_SIZE); |
385 | if (!(bh = sb_bread(sb, ADFS_DISCRECORD / BLOCK_SIZE))) { | 407 | if (!(bh = sb_bread(sb, ADFS_DISCRECORD / BLOCK_SIZE))) { |
386 | adfs_error(sb, "unable to read superblock"); | 408 | adfs_msg(sb, KERN_ERR, "error: unable to read superblock"); |
387 | ret = -EIO; | 409 | ret = -EIO; |
388 | goto error; | 410 | goto error; |
389 | } | 411 | } |
@@ -391,11 +413,8 @@ static int adfs_fill_super(struct super_block *sb, void *data, int silent) | |||
391 | b_data = bh->b_data + (ADFS_DISCRECORD % BLOCK_SIZE); | 413 | b_data = bh->b_data + (ADFS_DISCRECORD % BLOCK_SIZE); |
392 | 414 | ||
393 | if (adfs_checkbblk(b_data)) { | 415 | if (adfs_checkbblk(b_data)) { |
394 | if (!silent) | ||
395 | printk("VFS: Can't find an adfs filesystem on dev " | ||
396 | "%s.\n", sb->s_id); | ||
397 | ret = -EINVAL; | 416 | ret = -EINVAL; |
398 | goto error_free_bh; | 417 | goto error_badfs; |
399 | } | 418 | } |
400 | 419 | ||
401 | dr = (struct adfs_discrecord *)(b_data + ADFS_DR_OFFSET); | 420 | dr = (struct adfs_discrecord *)(b_data + ADFS_DR_OFFSET); |
@@ -404,33 +423,33 @@ static int adfs_fill_super(struct super_block *sb, void *data, int silent) | |||
404 | * Do some sanity checks on the ADFS disc record | 423 | * Do some sanity checks on the ADFS disc record |
405 | */ | 424 | */ |
406 | if (adfs_checkdiscrecord(dr)) { | 425 | if (adfs_checkdiscrecord(dr)) { |
407 | if (!silent) | ||
408 | printk("VPS: Can't find an adfs filesystem on dev " | ||
409 | "%s.\n", sb->s_id); | ||
410 | ret = -EINVAL; | 426 | ret = -EINVAL; |
411 | goto error_free_bh; | 427 | goto error_badfs; |
412 | } | 428 | } |
413 | 429 | ||
430 | blocksize = 1 << dr->log2secsize; | ||
414 | brelse(bh); | 431 | brelse(bh); |
415 | if (sb_set_blocksize(sb, 1 << dr->log2secsize)) { | 432 | |
433 | if (sb_set_blocksize(sb, blocksize)) { | ||
416 | bh = sb_bread(sb, ADFS_DISCRECORD / sb->s_blocksize); | 434 | bh = sb_bread(sb, ADFS_DISCRECORD / sb->s_blocksize); |
417 | if (!bh) { | 435 | if (!bh) { |
418 | adfs_error(sb, "couldn't read superblock on " | 436 | adfs_msg(sb, KERN_ERR, |
419 | "2nd try."); | 437 | "error: couldn't read superblock on 2nd try."); |
420 | ret = -EIO; | 438 | ret = -EIO; |
421 | goto error; | 439 | goto error; |
422 | } | 440 | } |
423 | b_data = bh->b_data + (ADFS_DISCRECORD % sb->s_blocksize); | 441 | b_data = bh->b_data + (ADFS_DISCRECORD % sb->s_blocksize); |
424 | if (adfs_checkbblk(b_data)) { | 442 | if (adfs_checkbblk(b_data)) { |
425 | adfs_error(sb, "disc record mismatch, very weird!"); | 443 | adfs_msg(sb, KERN_ERR, |
444 | "error: disc record mismatch, very weird!"); | ||
426 | ret = -EINVAL; | 445 | ret = -EINVAL; |
427 | goto error_free_bh; | 446 | goto error_free_bh; |
428 | } | 447 | } |
429 | dr = (struct adfs_discrecord *)(b_data + ADFS_DR_OFFSET); | 448 | dr = (struct adfs_discrecord *)(b_data + ADFS_DR_OFFSET); |
430 | } else { | 449 | } else { |
431 | if (!silent) | 450 | if (!silent) |
432 | printk(KERN_ERR "VFS: Unsupported blocksize on dev " | 451 | adfs_msg(sb, KERN_ERR, |
433 | "%s.\n", sb->s_id); | 452 | "error: unsupported blocksize"); |
434 | ret = -EINVAL; | 453 | ret = -EINVAL; |
435 | goto error; | 454 | goto error; |
436 | } | 455 | } |
@@ -443,8 +462,6 @@ static int adfs_fill_super(struct super_block *sb, void *data, int silent) | |||
443 | asb->s_idlen = dr->idlen; | 462 | asb->s_idlen = dr->idlen; |
444 | asb->s_map_size = dr->nzones | (dr->nzones_high << 8); | 463 | asb->s_map_size = dr->nzones | (dr->nzones_high << 8); |
445 | asb->s_map2blk = dr->log2bpmb - dr->log2secsize; | 464 | asb->s_map2blk = dr->log2bpmb - dr->log2secsize; |
446 | asb->s_size = adfs_discsize(dr, sb->s_blocksize_bits); | ||
447 | asb->s_version = dr->format_version; | ||
448 | asb->s_log2sharesize = dr->log2sharesize; | 465 | asb->s_log2sharesize = dr->log2sharesize; |
449 | 466 | ||
450 | asb->s_map = adfs_read_map(sb, dr); | 467 | asb->s_map = adfs_read_map(sb, dr); |
@@ -460,9 +477,9 @@ static int adfs_fill_super(struct super_block *sb, void *data, int silent) | |||
460 | */ | 477 | */ |
461 | sb->s_op = &adfs_sops; | 478 | sb->s_op = &adfs_sops; |
462 | 479 | ||
463 | dr = (struct adfs_discrecord *)(asb->s_map[0].dm_bh->b_data + 4); | 480 | dr = adfs_map_discrecord(asb->s_map); |
464 | 481 | ||
465 | root_obj.parent_id = root_obj.file_id = le32_to_cpu(dr->root); | 482 | root_obj.parent_id = root_obj.indaddr = le32_to_cpu(dr->root); |
466 | root_obj.name_len = 0; | 483 | root_obj.name_len = 0; |
467 | /* Set root object date as 01 Jan 1987 00:00:00 */ | 484 | /* Set root object date as 01 Jan 1987 00:00:00 */ |
468 | root_obj.loadaddr = 0xfff0003f; | 485 | root_obj.loadaddr = 0xfff0003f; |
@@ -470,13 +487,12 @@ static int adfs_fill_super(struct super_block *sb, void *data, int silent) | |||
470 | root_obj.size = ADFS_NEWDIR_SIZE; | 487 | root_obj.size = ADFS_NEWDIR_SIZE; |
471 | root_obj.attr = ADFS_NDA_DIRECTORY | ADFS_NDA_OWNER_READ | | 488 | root_obj.attr = ADFS_NDA_DIRECTORY | ADFS_NDA_OWNER_READ | |
472 | ADFS_NDA_OWNER_WRITE | ADFS_NDA_PUBLIC_READ; | 489 | ADFS_NDA_OWNER_WRITE | ADFS_NDA_PUBLIC_READ; |
473 | root_obj.filetype = -1; | ||
474 | 490 | ||
475 | /* | 491 | /* |
476 | * If this is a F+ disk with variable length directories, | 492 | * If this is a F+ disk with variable length directories, |
477 | * get the root_size from the disc record. | 493 | * get the root_size from the disc record. |
478 | */ | 494 | */ |
479 | if (asb->s_version) { | 495 | if (dr->format_version) { |
480 | root_obj.size = le32_to_cpu(dr->root_size); | 496 | root_obj.size = le32_to_cpu(dr->root_size); |
481 | asb->s_dir = &adfs_fplus_dir_ops; | 497 | asb->s_dir = &adfs_fplus_dir_ops; |
482 | asb->s_namelen = ADFS_FPLUS_NAME_LEN; | 498 | asb->s_namelen = ADFS_FPLUS_NAME_LEN; |
@@ -505,6 +521,11 @@ static int adfs_fill_super(struct super_block *sb, void *data, int silent) | |||
505 | } | 521 | } |
506 | return 0; | 522 | return 0; |
507 | 523 | ||
524 | error_badfs: | ||
525 | if (!silent) | ||
526 | adfs_msg(sb, KERN_ERR, | ||
527 | "error: can't find an ADFS filesystem on dev %s.", | ||
528 | sb->s_id); | ||
508 | error_free_bh: | 529 | error_free_bh: |
509 | brelse(bh); | 530 | brelse(bh); |
510 | error: | 531 | error: |