super.c 8.2 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10
/*
 * super.c
 *
 * Copyright (c) 1999 Al Smith
 *
 * Portions derived from work (c) 1995,1996 Christian Vogelgsang.
 */

#include <linux/init.h>
#include <linux/module.h>
11
#include <linux/exportfs.h>
L
Linus Torvalds 已提交
12 13 14 15
#include <linux/slab.h>
#include <linux/buffer_head.h>
#include <linux/vfs.h>

16 17 18 19
#include "efs.h"
#include <linux/efs_vh.h>
#include <linux/efs_fs_sb.h>

20
static int efs_statfs(struct dentry *dentry, struct kstatfs *buf);
L
Linus Torvalds 已提交
21 22
static int efs_fill_super(struct super_block *s, void *d, int silent);

A
Al Viro 已提交
23 24
static struct dentry *efs_mount(struct file_system_type *fs_type,
	int flags, const char *dev_name, void *data)
L
Linus Torvalds 已提交
25
{
A
Al Viro 已提交
26
	return mount_bdev(fs_type, flags, dev_name, data, efs_fill_super);
L
Linus Torvalds 已提交
27 28
}

A
Al Viro 已提交
29 30 31 32 33 34 35
static void efs_kill_sb(struct super_block *s)
{
	struct efs_sb_info *sbi = SUPER_INFO(s);
	kill_block_super(s);
	kfree(sbi);
}

L
Linus Torvalds 已提交
36 37 38
static struct file_system_type efs_fs_type = {
	.owner		= THIS_MODULE,
	.name		= "efs",
A
Al Viro 已提交
39
	.mount		= efs_mount,
A
Al Viro 已提交
40
	.kill_sb	= efs_kill_sb,
L
Linus Torvalds 已提交
41 42
	.fs_flags	= FS_REQUIRES_DEV,
};
43
MODULE_ALIAS_FS("efs");
L
Linus Torvalds 已提交
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64

static struct pt_types sgi_pt_types[] = {
	{0x00,		"SGI vh"},
	{0x01,		"SGI trkrepl"},
	{0x02,		"SGI secrepl"},
	{0x03,		"SGI raw"},
	{0x04,		"SGI bsd"},
	{SGI_SYSV,	"SGI sysv"},
	{0x06,		"SGI vol"},
	{SGI_EFS,	"SGI efs"},
	{0x08,		"SGI lv"},
	{0x09,		"SGI rlv"},
	{0x0A,		"SGI xfs"},
	{0x0B,		"SGI xfslog"},
	{0x0C,		"SGI xlv"},
	{0x82,		"Linux swap"},
	{0x83,		"Linux native"},
	{0,		NULL}
};


65
static struct kmem_cache * efs_inode_cachep;
L
Linus Torvalds 已提交
66 67 68 69

static struct inode *efs_alloc_inode(struct super_block *sb)
{
	struct efs_inode_info *ei;
F
Firo Yang 已提交
70
	ei = kmem_cache_alloc(efs_inode_cachep, GFP_KERNEL);
L
Linus Torvalds 已提交
71 72 73 74 75
	if (!ei)
		return NULL;
	return &ei->vfs_inode;
}

N
Nick Piggin 已提交
76
static void efs_i_callback(struct rcu_head *head)
L
Linus Torvalds 已提交
77
{
N
Nick Piggin 已提交
78
	struct inode *inode = container_of(head, struct inode, i_rcu);
L
Linus Torvalds 已提交
79 80 81
	kmem_cache_free(efs_inode_cachep, INODE_INFO(inode));
}

N
Nick Piggin 已提交
82 83 84 85 86
static void efs_destroy_inode(struct inode *inode)
{
	call_rcu(&inode->i_rcu, efs_i_callback);
}

87
static void init_once(void *foo)
L
Linus Torvalds 已提交
88 89 90
{
	struct efs_inode_info *ei = (struct efs_inode_info *) foo;

C
Christoph Lameter 已提交
91
	inode_init_once(&ei->vfs_inode);
L
Linus Torvalds 已提交
92
}
93

94
static int __init init_inodecache(void)
L
Linus Torvalds 已提交
95 96
{
	efs_inode_cachep = kmem_cache_create("efs_inode_cache",
97 98 99
				sizeof(struct efs_inode_info), 0,
				SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD|
				SLAB_ACCOUNT, init_once);
L
Linus Torvalds 已提交
100 101 102 103 104 105 106
	if (efs_inode_cachep == NULL)
		return -ENOMEM;
	return 0;
}

static void destroy_inodecache(void)
{
107 108 109 110 111
	/*
	 * Make sure all delayed rcu free inodes are flushed before we
	 * destroy cache.
	 */
	rcu_barrier();
112
	kmem_cache_destroy(efs_inode_cachep);
L
Linus Torvalds 已提交
113 114 115 116
}

static int efs_remount(struct super_block *sb, int *flags, char *data)
{
117
	sync_filesystem(sb);
L
Linus Torvalds 已提交
118 119 120 121
	*flags |= MS_RDONLY;
	return 0;
}

122
static const struct super_operations efs_superblock_operations = {
L
Linus Torvalds 已提交
123 124 125 126 127 128
	.alloc_inode	= efs_alloc_inode,
	.destroy_inode	= efs_destroy_inode,
	.statfs		= efs_statfs,
	.remount_fs	= efs_remount,
};

129
static const struct export_operations efs_export_ops = {
C
Christoph Hellwig 已提交
130 131
	.fh_to_dentry	= efs_fh_to_dentry,
	.fh_to_parent	= efs_fh_to_parent,
L
Linus Torvalds 已提交
132 133 134 135 136
	.get_parent	= efs_get_parent,
};

static int __init init_efs_fs(void) {
	int err;
137
	pr_info(EFS_VERSION" - http://aeschi.ch.eu.org/efs/\n");
L
Linus Torvalds 已提交
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
	err = init_inodecache();
	if (err)
		goto out1;
	err = register_filesystem(&efs_fs_type);
	if (err)
		goto out;
	return 0;
out:
	destroy_inodecache();
out1:
	return err;
}

static void __exit exit_efs_fs(void) {
	unregister_filesystem(&efs_fs_type);
	destroy_inodecache();
}

module_init(init_efs_fs)
module_exit(exit_efs_fs)

static efs_block_t efs_validate_vh(struct volume_header *vh) {
	int		i;
	__be32		cs, *ui;
	int		csum;
	efs_block_t	sblock = 0; /* shuts up gcc */
	struct pt_types	*pt_entry;
	int		pt_type, slice = -1;

	if (be32_to_cpu(vh->vh_magic) != VHMAGIC) {
		/*
		 * assume that we're dealing with a partition and allow
		 * read_super() to try and detect a valid superblock
		 * on the next block.
		 */
		return 0;
	}

	ui = ((__be32 *) (vh + 1)) - 1;
	for(csum = 0; ui >= ((__be32 *) vh);) {
		cs = *ui--;
		csum += be32_to_cpu(cs);
	}
	if (csum) {
182
		pr_warn("SGI disklabel: checksum bad, label corrupted\n");
L
Linus Torvalds 已提交
183 184 185 186
		return 0;
	}

#ifdef DEBUG
187
	pr_debug("bf: \"%16s\"\n", vh->vh_bootfile);
L
Linus Torvalds 已提交
188 189 190 191 192 193 194 195 196 197 198

	for(i = 0; i < NVDIR; i++) {
		int	j;
		char	name[VDNAMESIZE+1];

		for(j = 0; j < VDNAMESIZE; j++) {
			name[j] = vh->vh_vd[i].vd_name[j];
		}
		name[j] = (char) 0;

		if (name[0]) {
199 200
			pr_debug("vh: %8s block: 0x%08x size: 0x%08x\n",
				name, (int) be32_to_cpu(vh->vh_vd[i].vd_lbn),
L
Linus Torvalds 已提交
201 202 203 204 205 206 207 208 209 210 211 212
				(int) be32_to_cpu(vh->vh_vd[i].vd_nbytes));
		}
	}
#endif

	for(i = 0; i < NPARTAB; i++) {
		pt_type = (int) be32_to_cpu(vh->vh_pt[i].pt_type);
		for(pt_entry = sgi_pt_types; pt_entry->pt_name; pt_entry++) {
			if (pt_type == pt_entry->pt_type) break;
		}
#ifdef DEBUG
		if (be32_to_cpu(vh->vh_pt[i].pt_nblks)) {
213 214 215 216 217
			pr_debug("pt %2d: start: %08d size: %08d type: 0x%02x (%s)\n",
				 i, (int)be32_to_cpu(vh->vh_pt[i].pt_firstlbn),
				 (int)be32_to_cpu(vh->vh_pt[i].pt_nblks),
				 pt_type, (pt_entry->pt_name) ?
				 pt_entry->pt_name : "unknown");
L
Linus Torvalds 已提交
218 219 220 221 222 223 224 225 226
		}
#endif
		if (IS_EFS(pt_type)) {
			sblock = be32_to_cpu(vh->vh_pt[i].pt_firstlbn);
			slice = i;
		}
	}

	if (slice == -1) {
227
		pr_notice("partition table contained no EFS partitions\n");
L
Linus Torvalds 已提交
228 229
#ifdef DEBUG
	} else {
230
		pr_info("using slice %d (type %s, offset 0x%x)\n", slice,
L
Linus Torvalds 已提交
231 232 233 234
			(pt_entry->pt_name) ? pt_entry->pt_name : "unknown",
			sblock);
#endif
	}
235
	return sblock;
L
Linus Torvalds 已提交
236 237 238 239
}

static int efs_validate_super(struct efs_sb_info *sb, struct efs_super *super) {

240 241
	if (!IS_EFS_MAGIC(be32_to_cpu(super->fs_magic)))
		return -1;
L
Linus Torvalds 已提交
242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260

	sb->fs_magic     = be32_to_cpu(super->fs_magic);
	sb->total_blocks = be32_to_cpu(super->fs_size);
	sb->first_block  = be32_to_cpu(super->fs_firstcg);
	sb->group_size   = be32_to_cpu(super->fs_cgfsize);
	sb->data_free    = be32_to_cpu(super->fs_tfree);
	sb->inode_free   = be32_to_cpu(super->fs_tinode);
	sb->inode_blocks = be16_to_cpu(super->fs_cgisize);
	sb->total_groups = be16_to_cpu(super->fs_ncg);
    
	return 0;    
}

static int efs_fill_super(struct super_block *s, void *d, int silent)
{
	struct efs_sb_info *sb;
	struct buffer_head *bh;
	struct inode *root;

261
 	sb = kzalloc(sizeof(struct efs_sb_info), GFP_KERNEL);
L
Linus Torvalds 已提交
262 263 264 265 266 267
	if (!sb)
		return -ENOMEM;
	s->s_fs_info = sb;
 
	s->s_magic		= EFS_SUPER_MAGIC;
	if (!sb_set_blocksize(s, EFS_BLOCKSIZE)) {
268
		pr_err("device does not support %d byte blocks\n",
L
Linus Torvalds 已提交
269
			EFS_BLOCKSIZE);
A
Al Viro 已提交
270
		return -EINVAL;
L
Linus Torvalds 已提交
271 272 273 274 275 276
	}
  
	/* read the vh (volume header) block */
	bh = sb_bread(s, 0);

	if (!bh) {
277
		pr_err("cannot read volume header\n");
H
Heloise 已提交
278
		return -EIO;
L
Linus Torvalds 已提交
279 280 281 282 283 284 285 286 287 288 289
	}

	/*
	 * if this returns zero then we didn't find any partition table.
	 * this isn't (yet) an error - just assume for the moment that
	 * the device is valid and go on to search for a superblock.
	 */
	sb->fs_start = efs_validate_vh((struct volume_header *) bh->b_data);
	brelse(bh);

	if (sb->fs_start == -1) {
A
Al Viro 已提交
290
		return -EINVAL;
L
Linus Torvalds 已提交
291 292 293 294
	}

	bh = sb_bread(s, sb->fs_start + EFS_SUPER);
	if (!bh) {
295
		pr_err("cannot read superblock\n");
H
Heloise 已提交
296
		return -EIO;
L
Linus Torvalds 已提交
297 298 299 300
	}
		
	if (efs_validate_super(sb, (struct efs_super *) bh->b_data)) {
#ifdef DEBUG
301 302
		pr_warn("invalid superblock at block %u\n",
			sb->fs_start + EFS_SUPER);
L
Linus Torvalds 已提交
303 304
#endif
		brelse(bh);
A
Al Viro 已提交
305
		return -EINVAL;
L
Linus Torvalds 已提交
306 307 308 309 310
	}
	brelse(bh);

	if (!(s->s_flags & MS_RDONLY)) {
#ifdef DEBUG
311
		pr_info("forcing read-only mode\n");
L
Linus Torvalds 已提交
312 313 314 315 316
#endif
		s->s_flags |= MS_RDONLY;
	}
	s->s_op   = &efs_superblock_operations;
	s->s_export_op = &efs_export_ops;
317 318
	root = efs_iget(s, EFS_ROOTINODE);
	if (IS_ERR(root)) {
319
		pr_err("get root inode failed\n");
A
Al Viro 已提交
320
		return PTR_ERR(root);
321 322
	}

323
	s->s_root = d_make_root(root);
L
Linus Torvalds 已提交
324
	if (!(s->s_root)) {
325
		pr_err("get root dentry failed\n");
A
Al Viro 已提交
326
		return -ENOMEM;
L
Linus Torvalds 已提交
327 328 329 330 331
	}

	return 0;
}

332
static int efs_statfs(struct dentry *dentry, struct kstatfs *buf) {
C
Coly Li 已提交
333 334 335
	struct super_block *sb = dentry->d_sb;
	struct efs_sb_info *sbi = SUPER_INFO(sb);
	u64 id = huge_encode_dev(sb->s_bdev->bd_dev);
L
Linus Torvalds 已提交
336 337 338

	buf->f_type    = EFS_SUPER_MAGIC;	/* efs magic number */
	buf->f_bsize   = EFS_BLOCKSIZE;		/* blocksize */
C
Coly Li 已提交
339 340 341 342 343 344
	buf->f_blocks  = sbi->total_groups *	/* total data blocks */
			(sbi->group_size - sbi->inode_blocks);
	buf->f_bfree   = sbi->data_free;	/* free data blocks */
	buf->f_bavail  = sbi->data_free;	/* free blocks for non-root */
	buf->f_files   = sbi->total_groups *	/* total inodes */
			sbi->inode_blocks *
L
Linus Torvalds 已提交
345
			(EFS_BLOCKSIZE / sizeof(struct efs_dinode));
C
Coly Li 已提交
346 347 348
	buf->f_ffree   = sbi->inode_free;	/* free inodes */
	buf->f_fsid.val[0] = (u32)id;
	buf->f_fsid.val[1] = (u32)(id >> 32);
L
Linus Torvalds 已提交
349 350 351 352 353
	buf->f_namelen = EFS_MAXNAMELEN;	/* max filename length */

	return 0;
}