aboutsummaryrefslogtreecommitdiffstats
path: root/fs/adfs/super.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2019-07-19 14:33:22 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2019-07-19 14:33:22 -0400
commitd2fbf4b6d585e40f2369675148777abce3abd0e7 (patch)
tree0749aa1da31af21d752585dc7a2f31715d434ee3 /fs/adfs/super.c
parent933a90bf4f3505f8ec83bda21a3c7d70d7c2b426 (diff)
parentb4ed8f75c82876342b3399942427392ba5f3bbb5 (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.c121
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
23void __adfs_error(struct super_block *sb, const char *function, const char *fmt, ...) 24void __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
40void 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
37static int adfs_checkdiscrecord(struct adfs_discrecord *dr) 52static 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
155static int parse_options(struct super_block *sb, char *options) 176static 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
210static int adfs_remount(struct super_block *sb, int *flags, char *data) 232static 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
217static int adfs_statfs(struct dentry *dentry, struct kstatfs *buf) 248static 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
347static 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
357static int adfs_fill_super(struct super_block *sb, void *data, int silent) 378static 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
524error_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);
508error_free_bh: 529error_free_bh:
509 brelse(bh); 530 brelse(bh);
510error: 531error: