Prev: [PATCH 1/3] [fs/sysv] Add v7 alias
Next: Bad relocation warnings whileBuilding a CONFIG_RELOCATABLE kernel with CONFIG_ISERIES enabled
From: Lubomir Rintel on 19 Jul 2010 07:20 A mount-time option was added that makes it possible to override the endianness and an attempt is made to autodetect it (which seems easy, given the disk addresses are 3-byte. No attempt is made to detect big-endian filesystems -- were there any? Tested with PDP-11 v7 filesystems and PC-IX maintenance floppy. Cc: Christoph Hellwig <hch(a)lst.de> Signed-off-by: Lubomir Rintel <lkundrak(a)v3.sk> --- fs/sysv/super.c | 116 +++++++++++++++++++++++++++++++++++++---------- include/linux/sysv_fs.h | 6 +- 2 files changed, 95 insertions(+), 27 deletions(-) diff --git a/fs/sysv/super.c b/fs/sysv/super.c index 17ac83d..9cd5be0 100644 --- a/fs/sysv/super.c +++ b/fs/sysv/super.c @@ -24,6 +24,7 @@ #include <linux/init.h> #include <linux/slab.h> #include <linux/buffer_head.h> +#include <linux/parser.h> #include "sysv.h" /* @@ -435,12 +436,55 @@ Ebadsize: goto failed; } -static int v7_fill_super(struct super_block *sb, void *data, int silent) +static int v7_sanity_check(struct super_block *sb, struct buffer_head *bh) { - struct sysv_sb_info *sbi; - struct buffer_head *bh, *bh2 = NULL; struct v7_super_block *v7sb; struct sysv_inode *v7i; + struct buffer_head *bh2; + struct sysv_sb_info *sbi; + + sbi = sb->s_fs_info; + + /* plausibility check on superblock */ + v7sb = (struct v7_super_block *) bh->b_data; + if (fs16_to_cpu(sbi, v7sb->s_nfree) > V7_NICFREE || + fs16_to_cpu(sbi, v7sb->s_ninode) > V7_NICINOD || + fs32_to_cpu(sbi, v7sb->s_fsize) > V7_MAXSIZE) + return 0; + + /* plausibility check on root inode: it is a directory, + with a nonzero size that is a multiple of 16 */ + if ((bh2 = sb_bread(sb, 2)) == NULL) { + return 0; + } + + v7i = (struct sysv_inode *)(bh2->b_data + 64); + if ((fs16_to_cpu(sbi, v7i->i_mode) & ~0777) != S_IFDIR || + (fs32_to_cpu(sbi, v7i->i_size) == 0) || + (fs32_to_cpu(sbi, v7i->i_size) & 017) || + (fs32_to_cpu(sbi, v7i->i_size) > V7_NFILES * + sizeof (struct sysv_dir_entry))) { + brelse(bh2); + return 0; + } + + brelse(bh2); + return 1; +} + +enum { Opt_err, Opt_bytesex_pdp, Opt_bytesex_le, Opt_bytesex_be }; + +static const match_table_t v7_tokens = { + {Opt_bytesex_pdp, "bytesex=pdp"}, + {Opt_bytesex_le, "bytesex=le"}, + {Opt_bytesex_be, "bytesex=be"}, + {Opt_err, NULL} +}; + +static int v7_fill_super(struct super_block *sb, void *data, int silent) +{ + struct sysv_sb_info *sbi; + struct buffer_head *bh; if (440 != sizeof (struct v7_super_block)) panic("V7 FS: bad super-block size"); @@ -454,7 +498,6 @@ static int v7_fill_super(struct super_block *sb, void *data, int silent) sbi->s_sb = sb; sbi->s_block_base = 0; sbi->s_type = FSTYPE_V7; - sbi->s_bytesex = BYTESEX_PDP; sb->s_fs_info = sbi; sb_set_blocksize(sb, 512); @@ -466,26 +509,51 @@ static int v7_fill_super(struct super_block *sb, void *data, int silent) goto failed; } - /* plausibility check on superblock */ - v7sb = (struct v7_super_block *) bh->b_data; - if (fs16_to_cpu(sbi, v7sb->s_nfree) > V7_NICFREE || - fs16_to_cpu(sbi, v7sb->s_ninode) > V7_NICINOD || - fs32_to_cpu(sbi, v7sb->s_fsize) > V7_MAXSIZE) - return 0; + if (data) { + substring_t args[MAX_OPT_ARGS]; + char *p; + + while ((p = strsep ((char **)&data, ",")) != NULL) { + int token; + if (!*p) + continue; + token = match_token(p, v7_tokens, args); + switch (token) { + case Opt_bytesex_pdp: + sbi->s_bytesex = BYTESEX_PDP; + break; + case Opt_bytesex_le: + sbi->s_bytesex = BYTESEX_LE; + break; + case Opt_bytesex_be: + sbi->s_bytesex = BYTESEX_BE; + break; + default: + /* An unrecognized option */ + goto failed; + } + } + } - /* plausibility check on root inode: it is a directory, - with a nonzero size that is a multiple of 16 */ - if ((bh2 = sb_bread(sb, 2)) == NULL) - goto failed; - v7i = (struct sysv_inode *)(bh2->b_data + 64); - if ((fs16_to_cpu(sbi, v7i->i_mode) & ~0777) != S_IFDIR || - (fs32_to_cpu(sbi, v7i->i_size) == 0) || - (fs32_to_cpu(sbi, v7i->i_size) & 017) - (fs32_to_cpu(sbi, v7i->i_size) > V7_NFILES * - sizeof (struct sysv_dir_entry))) { - goto failed; - brelse(bh2); - bh2 = NULL; + /* Endianness overridden by a mount option */ + if (sbi->s_bytesex) { + if (!v7_sanity_check (sb, bh)) + goto failed; + goto detected; + } + + /* Try PDP-11 UNIX */ + sbi->s_bytesex = BYTESEX_PDP; + if (v7_sanity_check (sb, bh)) + goto detected; + + /* Try PC/IX, v7/x86 */ + sbi->s_bytesex = BYTESEX_LE; + if (v7_sanity_check (sb, bh)) + goto detected; + + goto failed; +detected: sbi->s_bh1 = bh; sbi->s_bh2 = bh; @@ -493,7 +561,7 @@ static int v7_fill_super(struct super_block *sb, void *data, int silent) return 0; failed: - brelse(bh2); + printk("VFS: could not find a valid V7 on %s.\n", sb->s_id); brelse(bh); kfree(sbi); return -EINVAL; diff --git a/include/linux/sysv_fs.h b/include/linux/sysv_fs.h index 0a7a232..e47d6d9 100644 --- a/include/linux/sysv_fs.h +++ b/include/linux/sysv_fs.h @@ -155,9 +155,9 @@ struct v7_super_block { * directory. Thus, if we see anything higher, we just probably got the * endiannes wrong. */ #define V7_NFILES 1024 -/* Indirect blocks hold just three-byte addresses, therefore if see a file - * system whose length has the most significant byte non-zero something is - * most likely wrong (not a filesystem, bad bytesex). */ +/* The disk addresses are three-byte (despite direct block addresses being + * aligned word-wise in inode). If the most significant byte is non-zero, + * something is most likely wrong (not a filesystem, bad bytesex). */ #define V7_MAXSIZE 0x00ffffff /* Coherent super-block data on disk */ -- 1.6.5.2 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo(a)vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/ |