remarkable-linux/fs/sysv/super.c
Eric W. Biederman fa7614ddd6 fs: Readd the fs module aliases.
I had assumed that the only use of module aliases for filesystems
prior to "fs: Limit sys_mount to only request filesystem modules."
was in request_module.  It turns out I was wrong.  At least mkinitcpio
in Arch linux uses these aliases.

So readd the preexising aliases, to keep from breaking userspace.

Userspace eventually will have to follow and use the same aliases the
kernel does.  So at some point we may be delete these aliases without
problems.  However that day is not today.

Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com>
2013-03-12 18:55:21 -07:00

593 lines
15 KiB
C

/*
* linux/fs/sysv/inode.c
*
* minix/inode.c
* Copyright (C) 1991, 1992 Linus Torvalds
*
* xenix/inode.c
* Copyright (C) 1992 Doug Evans
*
* coh/inode.c
* Copyright (C) 1993 Pascal Haible, Bruno Haible
*
* sysv/inode.c
* Copyright (C) 1993 Paul B. Monday
*
* sysv/inode.c
* Copyright (C) 1993 Bruno Haible
* Copyright (C) 1997, 1998 Krzysztof G. Baranowski
*
* This file contains code for read/parsing the superblock.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/buffer_head.h>
#include "sysv.h"
/*
* The following functions try to recognize specific filesystems.
*
* We recognize:
* - Xenix FS by its magic number.
* - SystemV FS by its magic number.
* - Coherent FS by its funny fname/fpack field.
* - SCO AFS by s_nfree == 0xffff
* - V7 FS has no distinguishing features.
*
* We discriminate among SystemV4 and SystemV2 FS by the assumption that
* the time stamp is not < 01-01-1980.
*/
enum {
JAN_1_1980 = (10*365 + 2) * 24 * 60 * 60
};
static void detected_xenix(struct sysv_sb_info *sbi, unsigned *max_links)
{
struct buffer_head *bh1 = sbi->s_bh1;
struct buffer_head *bh2 = sbi->s_bh2;
struct xenix_super_block * sbd1;
struct xenix_super_block * sbd2;
if (bh1 != bh2)
sbd1 = sbd2 = (struct xenix_super_block *) bh1->b_data;
else {
/* block size = 512, so bh1 != bh2 */
sbd1 = (struct xenix_super_block *) bh1->b_data;
sbd2 = (struct xenix_super_block *) (bh2->b_data - 512);
}
*max_links = XENIX_LINK_MAX;
sbi->s_fic_size = XENIX_NICINOD;
sbi->s_flc_size = XENIX_NICFREE;
sbi->s_sbd1 = (char *)sbd1;
sbi->s_sbd2 = (char *)sbd2;
sbi->s_sb_fic_count = &sbd1->s_ninode;
sbi->s_sb_fic_inodes = &sbd1->s_inode[0];
sbi->s_sb_total_free_inodes = &sbd2->s_tinode;
sbi->s_bcache_count = &sbd1->s_nfree;
sbi->s_bcache = &sbd1->s_free[0];
sbi->s_free_blocks = &sbd2->s_tfree;
sbi->s_sb_time = &sbd2->s_time;
sbi->s_firstdatazone = fs16_to_cpu(sbi, sbd1->s_isize);
sbi->s_nzones = fs32_to_cpu(sbi, sbd1->s_fsize);
}
static void detected_sysv4(struct sysv_sb_info *sbi, unsigned *max_links)
{
struct sysv4_super_block * sbd;
struct buffer_head *bh1 = sbi->s_bh1;
struct buffer_head *bh2 = sbi->s_bh2;
if (bh1 == bh2)
sbd = (struct sysv4_super_block *) (bh1->b_data + BLOCK_SIZE/2);
else
sbd = (struct sysv4_super_block *) bh2->b_data;
*max_links = SYSV_LINK_MAX;
sbi->s_fic_size = SYSV_NICINOD;
sbi->s_flc_size = SYSV_NICFREE;
sbi->s_sbd1 = (char *)sbd;
sbi->s_sbd2 = (char *)sbd;
sbi->s_sb_fic_count = &sbd->s_ninode;
sbi->s_sb_fic_inodes = &sbd->s_inode[0];
sbi->s_sb_total_free_inodes = &sbd->s_tinode;
sbi->s_bcache_count = &sbd->s_nfree;
sbi->s_bcache = &sbd->s_free[0];
sbi->s_free_blocks = &sbd->s_tfree;
sbi->s_sb_time = &sbd->s_time;
sbi->s_sb_state = &sbd->s_state;
sbi->s_firstdatazone = fs16_to_cpu(sbi, sbd->s_isize);
sbi->s_nzones = fs32_to_cpu(sbi, sbd->s_fsize);
}
static void detected_sysv2(struct sysv_sb_info *sbi, unsigned *max_links)
{
struct sysv2_super_block *sbd;
struct buffer_head *bh1 = sbi->s_bh1;
struct buffer_head *bh2 = sbi->s_bh2;
if (bh1 == bh2)
sbd = (struct sysv2_super_block *) (bh1->b_data + BLOCK_SIZE/2);
else
sbd = (struct sysv2_super_block *) bh2->b_data;
*max_links = SYSV_LINK_MAX;
sbi->s_fic_size = SYSV_NICINOD;
sbi->s_flc_size = SYSV_NICFREE;
sbi->s_sbd1 = (char *)sbd;
sbi->s_sbd2 = (char *)sbd;
sbi->s_sb_fic_count = &sbd->s_ninode;
sbi->s_sb_fic_inodes = &sbd->s_inode[0];
sbi->s_sb_total_free_inodes = &sbd->s_tinode;
sbi->s_bcache_count = &sbd->s_nfree;
sbi->s_bcache = &sbd->s_free[0];
sbi->s_free_blocks = &sbd->s_tfree;
sbi->s_sb_time = &sbd->s_time;
sbi->s_sb_state = &sbd->s_state;
sbi->s_firstdatazone = fs16_to_cpu(sbi, sbd->s_isize);
sbi->s_nzones = fs32_to_cpu(sbi, sbd->s_fsize);
}
static void detected_coherent(struct sysv_sb_info *sbi, unsigned *max_links)
{
struct coh_super_block * sbd;
struct buffer_head *bh1 = sbi->s_bh1;
sbd = (struct coh_super_block *) bh1->b_data;
*max_links = COH_LINK_MAX;
sbi->s_fic_size = COH_NICINOD;
sbi->s_flc_size = COH_NICFREE;
sbi->s_sbd1 = (char *)sbd;
sbi->s_sbd2 = (char *)sbd;
sbi->s_sb_fic_count = &sbd->s_ninode;
sbi->s_sb_fic_inodes = &sbd->s_inode[0];
sbi->s_sb_total_free_inodes = &sbd->s_tinode;
sbi->s_bcache_count = &sbd->s_nfree;
sbi->s_bcache = &sbd->s_free[0];
sbi->s_free_blocks = &sbd->s_tfree;
sbi->s_sb_time = &sbd->s_time;
sbi->s_firstdatazone = fs16_to_cpu(sbi, sbd->s_isize);
sbi->s_nzones = fs32_to_cpu(sbi, sbd->s_fsize);
}
static void detected_v7(struct sysv_sb_info *sbi, unsigned *max_links)
{
struct buffer_head *bh2 = sbi->s_bh2;
struct v7_super_block *sbd = (struct v7_super_block *)bh2->b_data;
*max_links = V7_LINK_MAX;
sbi->s_fic_size = V7_NICINOD;
sbi->s_flc_size = V7_NICFREE;
sbi->s_sbd1 = (char *)sbd;
sbi->s_sbd2 = (char *)sbd;
sbi->s_sb_fic_count = &sbd->s_ninode;
sbi->s_sb_fic_inodes = &sbd->s_inode[0];
sbi->s_sb_total_free_inodes = &sbd->s_tinode;
sbi->s_bcache_count = &sbd->s_nfree;
sbi->s_bcache = &sbd->s_free[0];
sbi->s_free_blocks = &sbd->s_tfree;
sbi->s_sb_time = &sbd->s_time;
sbi->s_firstdatazone = fs16_to_cpu(sbi, sbd->s_isize);
sbi->s_nzones = fs32_to_cpu(sbi, sbd->s_fsize);
}
static int detect_xenix(struct sysv_sb_info *sbi, struct buffer_head *bh)
{
struct xenix_super_block *sbd = (struct xenix_super_block *)bh->b_data;
if (*(__le32 *)&sbd->s_magic == cpu_to_le32(0x2b5544))
sbi->s_bytesex = BYTESEX_LE;
else if (*(__be32 *)&sbd->s_magic == cpu_to_be32(0x2b5544))
sbi->s_bytesex = BYTESEX_BE;
else
return 0;
switch (fs32_to_cpu(sbi, sbd->s_type)) {
case 1:
sbi->s_type = FSTYPE_XENIX;
return 1;
case 2:
sbi->s_type = FSTYPE_XENIX;
return 2;
default:
return 0;
}
}
static int detect_sysv(struct sysv_sb_info *sbi, struct buffer_head *bh)
{
struct super_block *sb = sbi->s_sb;
/* All relevant fields are at the same offsets in R2 and R4 */
struct sysv4_super_block * sbd;
u32 type;
sbd = (struct sysv4_super_block *) (bh->b_data + BLOCK_SIZE/2);
if (*(__le32 *)&sbd->s_magic == cpu_to_le32(0xfd187e20))
sbi->s_bytesex = BYTESEX_LE;
else if (*(__be32 *)&sbd->s_magic == cpu_to_be32(0xfd187e20))
sbi->s_bytesex = BYTESEX_BE;
else
return 0;
type = fs32_to_cpu(sbi, sbd->s_type);
if (fs16_to_cpu(sbi, sbd->s_nfree) == 0xffff) {
sbi->s_type = FSTYPE_AFS;
sbi->s_forced_ro = 1;
if (!(sb->s_flags & MS_RDONLY)) {
printk("SysV FS: SCO EAFS on %s detected, "
"forcing read-only mode.\n",
sb->s_id);
}
return type;
}
if (fs32_to_cpu(sbi, sbd->s_time) < JAN_1_1980) {
/* this is likely to happen on SystemV2 FS */
if (type > 3 || type < 1)
return 0;
sbi->s_type = FSTYPE_SYSV2;
return type;
}
if ((type > 3 || type < 1) && (type > 0x30 || type < 0x10))
return 0;
/* On Interactive Unix (ISC) Version 4.0/3.x s_type field = 0x10,
0x20 or 0x30 indicates that symbolic links and the 14-character
filename limit is gone. Due to lack of information about this
feature read-only mode seems to be a reasonable approach... -KGB */
if (type >= 0x10) {
printk("SysV FS: can't handle long file names on %s, "
"forcing read-only mode.\n", sb->s_id);
sbi->s_forced_ro = 1;
}
sbi->s_type = FSTYPE_SYSV4;
return type >= 0x10 ? type >> 4 : type;
}
static int detect_coherent(struct sysv_sb_info *sbi, struct buffer_head *bh)
{
struct coh_super_block * sbd;
sbd = (struct coh_super_block *) (bh->b_data + BLOCK_SIZE/2);
if ((memcmp(sbd->s_fname,"noname",6) && memcmp(sbd->s_fname,"xxxxx ",6))
|| (memcmp(sbd->s_fpack,"nopack",6) && memcmp(sbd->s_fpack,"xxxxx\n",6)))
return 0;
sbi->s_bytesex = BYTESEX_PDP;
sbi->s_type = FSTYPE_COH;
return 1;
}
static int detect_sysv_odd(struct sysv_sb_info *sbi, struct buffer_head *bh)
{
int size = detect_sysv(sbi, bh);
return size>2 ? 0 : size;
}
static struct {
int block;
int (*test)(struct sysv_sb_info *, struct buffer_head *);
} flavours[] = {
{1, detect_xenix},
{0, detect_sysv},
{0, detect_coherent},
{9, detect_sysv_odd},
{15,detect_sysv_odd},
{18,detect_sysv},
};
static char *flavour_names[] = {
[FSTYPE_XENIX] = "Xenix",
[FSTYPE_SYSV4] = "SystemV",
[FSTYPE_SYSV2] = "SystemV Release 2",
[FSTYPE_COH] = "Coherent",
[FSTYPE_V7] = "V7",
[FSTYPE_AFS] = "AFS",
};
static void (*flavour_setup[])(struct sysv_sb_info *, unsigned *) = {
[FSTYPE_XENIX] = detected_xenix,
[FSTYPE_SYSV4] = detected_sysv4,
[FSTYPE_SYSV2] = detected_sysv2,
[FSTYPE_COH] = detected_coherent,
[FSTYPE_V7] = detected_v7,
[FSTYPE_AFS] = detected_sysv4,
};
static int complete_read_super(struct super_block *sb, int silent, int size)
{
struct sysv_sb_info *sbi = SYSV_SB(sb);
struct inode *root_inode;
char *found = flavour_names[sbi->s_type];
u_char n_bits = size+8;
int bsize = 1 << n_bits;
int bsize_4 = bsize >> 2;
sbi->s_firstinodezone = 2;
flavour_setup[sbi->s_type](sbi, &sb->s_max_links);
sbi->s_truncate = 1;
sbi->s_ndatazones = sbi->s_nzones - sbi->s_firstdatazone;
sbi->s_inodes_per_block = bsize >> 6;
sbi->s_inodes_per_block_1 = (bsize >> 6)-1;
sbi->s_inodes_per_block_bits = n_bits-6;
sbi->s_ind_per_block = bsize_4;
sbi->s_ind_per_block_2 = bsize_4*bsize_4;
sbi->s_toobig_block = 10 + bsize_4 * (1 + bsize_4 * (1 + bsize_4));
sbi->s_ind_per_block_bits = n_bits-2;
sbi->s_ninodes = (sbi->s_firstdatazone - sbi->s_firstinodezone)
<< sbi->s_inodes_per_block_bits;
if (!silent)
printk("VFS: Found a %s FS (block size = %ld) on device %s\n",
found, sb->s_blocksize, sb->s_id);
sb->s_magic = SYSV_MAGIC_BASE + sbi->s_type;
/* set up enough so that it can read an inode */
sb->s_op = &sysv_sops;
if (sbi->s_forced_ro)
sb->s_flags |= MS_RDONLY;
if (sbi->s_truncate)
sb->s_d_op = &sysv_dentry_operations;
root_inode = sysv_iget(sb, SYSV_ROOT_INO);
if (IS_ERR(root_inode)) {
printk("SysV FS: get root inode failed\n");
return 0;
}
sb->s_root = d_make_root(root_inode);
if (!sb->s_root) {
printk("SysV FS: get root dentry failed\n");
return 0;
}
return 1;
}
static int sysv_fill_super(struct super_block *sb, void *data, int silent)
{
struct buffer_head *bh1, *bh = NULL;
struct sysv_sb_info *sbi;
unsigned long blocknr;
int size = 0, i;
BUILD_BUG_ON(1024 != sizeof (struct xenix_super_block));
BUILD_BUG_ON(512 != sizeof (struct sysv4_super_block));
BUILD_BUG_ON(512 != sizeof (struct sysv2_super_block));
BUILD_BUG_ON(500 != sizeof (struct coh_super_block));
BUILD_BUG_ON(64 != sizeof (struct sysv_inode));
sbi = kzalloc(sizeof(struct sysv_sb_info), GFP_KERNEL);
if (!sbi)
return -ENOMEM;
sbi->s_sb = sb;
sbi->s_block_base = 0;
mutex_init(&sbi->s_lock);
sb->s_fs_info = sbi;
sb_set_blocksize(sb, BLOCK_SIZE);
for (i = 0; i < ARRAY_SIZE(flavours) && !size; i++) {
brelse(bh);
bh = sb_bread(sb, flavours[i].block);
if (!bh)
continue;
size = flavours[i].test(SYSV_SB(sb), bh);
}
if (!size)
goto Eunknown;
switch (size) {
case 1:
blocknr = bh->b_blocknr << 1;
brelse(bh);
sb_set_blocksize(sb, 512);
bh1 = sb_bread(sb, blocknr);
bh = sb_bread(sb, blocknr + 1);
break;
case 2:
bh1 = bh;
break;
case 3:
blocknr = bh->b_blocknr >> 1;
brelse(bh);
sb_set_blocksize(sb, 2048);
bh1 = bh = sb_bread(sb, blocknr);
break;
default:
goto Ebadsize;
}
if (bh && bh1) {
sbi->s_bh1 = bh1;
sbi->s_bh2 = bh;
if (complete_read_super(sb, silent, size))
return 0;
}
brelse(bh1);
brelse(bh);
sb_set_blocksize(sb, BLOCK_SIZE);
printk("oldfs: cannot read superblock\n");
failed:
kfree(sbi);
return -EINVAL;
Eunknown:
brelse(bh);
if (!silent)
printk("VFS: unable to find oldfs superblock on device %s\n",
sb->s_id);
goto failed;
Ebadsize:
brelse(bh);
if (!silent)
printk("VFS: oldfs: unsupported block size (%dKb)\n",
1<<(size-2));
goto failed;
}
static int v7_sanity_check(struct super_block *sb, struct buffer_head *bh)
{
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 */
bh2 = sb_bread(sb, 2);
if (bh2 == 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;
}
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");
if (64 != sizeof (struct sysv_inode))
panic("sysv fs: bad i-node size");
sbi = kzalloc(sizeof(struct sysv_sb_info), GFP_KERNEL);
if (!sbi)
return -ENOMEM;
sbi->s_sb = sb;
sbi->s_block_base = 0;
sbi->s_type = FSTYPE_V7;
sb->s_fs_info = sbi;
sb_set_blocksize(sb, 512);
if ((bh = sb_bread(sb, 1)) == NULL) {
if (!silent)
printk("VFS: unable to read V7 FS superblock on "
"device %s.\n", sb->s_id);
goto failed;
}
/* 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;
if (complete_read_super(sb, silent, 1))
return 0;
failed:
printk(KERN_ERR "VFS: could not find a valid V7 on %s.\n",
sb->s_id);
brelse(bh);
kfree(sbi);
return -EINVAL;
}
/* Every kernel module contains stuff like this. */
static struct dentry *sysv_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void *data)
{
return mount_bdev(fs_type, flags, dev_name, data, sysv_fill_super);
}
static struct dentry *v7_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void *data)
{
return mount_bdev(fs_type, flags, dev_name, data, v7_fill_super);
}
static struct file_system_type sysv_fs_type = {
.owner = THIS_MODULE,
.name = "sysv",
.mount = sysv_mount,
.kill_sb = kill_block_super,
.fs_flags = FS_REQUIRES_DEV,
};
MODULE_ALIAS_FS("sysv");
static struct file_system_type v7_fs_type = {
.owner = THIS_MODULE,
.name = "v7",
.mount = v7_mount,
.kill_sb = kill_block_super,
.fs_flags = FS_REQUIRES_DEV,
};
MODULE_ALIAS_FS("v7");
MODULE_ALIAS("v7");
static int __init init_sysv_fs(void)
{
int error;
error = sysv_init_icache();
if (error)
goto out;
error = register_filesystem(&sysv_fs_type);
if (error)
goto destroy_icache;
error = register_filesystem(&v7_fs_type);
if (error)
goto unregister;
return 0;
unregister:
unregister_filesystem(&sysv_fs_type);
destroy_icache:
sysv_destroy_icache();
out:
return error;
}
static void __exit exit_sysv_fs(void)
{
unregister_filesystem(&sysv_fs_type);
unregister_filesystem(&v7_fs_type);
sysv_destroy_icache();
}
module_init(init_sysv_fs)
module_exit(exit_sysv_fs)
MODULE_LICENSE("GPL");