forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
594 lines
15 KiB
594 lines
15 KiB
// SPDX-License-Identifier: GPL-2.0-only |
|
/* |
|
* 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_rdonly(sb)) { |
|
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_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 |= SB_RDONLY; |
|
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->s_time_min = 0; |
|
sb->s_time_max = U32_MAX; |
|
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; |
|
mutex_init(&sbi->s_lock); |
|
sb->s_fs_info = sbi; |
|
sb->s_time_min = 0; |
|
sb->s_time_max = U32_MAX; |
|
|
|
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");
|
|
|