inode.c 11.3 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1
2
3
4
5
/*
 *	fs/bfs/inode.c
 *	BFS superblock and inode operations.
 *	Copyright (C) 1999,2000 Tigran Aivazian <tigran@veritas.com>
 *	From fs/minix, Copyright (C) 1991, 1992 Linus Torvalds.
6
7
 *
 *      Made endianness-clean by Andrew Stribblehill <ads@wompom.org>, 2005.
Linus Torvalds's avatar
Linus Torvalds committed
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
 */

#include <linux/module.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/smp_lock.h>
#include <linux/buffer_head.h>
#include <linux/vfs.h>
#include <asm/uaccess.h>
#include "bfs.h"

MODULE_AUTHOR("Tigran A. Aivazian <tigran@veritas.com>");
MODULE_DESCRIPTION("SCO UnixWare BFS filesystem for Linux");
MODULE_LICENSE("GPL");

#undef DEBUG

#ifdef DEBUG
#define dprintf(x...)	printf(x)
#else
#define dprintf(x...)
#endif

void dump_imap(const char *prefix, struct super_block * s);

static void bfs_read_inode(struct inode * inode)
{
	unsigned long ino = inode->i_ino;
	struct bfs_inode * di;
	struct buffer_head * bh;
	int block, off;

	if (ino < BFS_ROOT_INO || ino > BFS_SB(inode->i_sb)->si_lasti) {
		printf("Bad inode number %s:%08lx\n", inode->i_sb->s_id, ino);
		make_bad_inode(inode);
		return;
	}

	block = (ino - BFS_ROOT_INO)/BFS_INODES_PER_BLOCK + 1;
	bh = sb_bread(inode->i_sb, block);
	if (!bh) {
		printf("Unable to read inode %s:%08lx\n", inode->i_sb->s_id, ino);
		make_bad_inode(inode);
		return;
	}

	off = (ino - BFS_ROOT_INO) % BFS_INODES_PER_BLOCK;
	di = (struct bfs_inode *)bh->b_data + off;

59
60
	inode->i_mode = 0x0000FFFF &  le32_to_cpu(di->i_mode);
	if (le32_to_cpu(di->i_vtype) == BFS_VDIR) {
Linus Torvalds's avatar
Linus Torvalds committed
61
62
63
		inode->i_mode |= S_IFDIR;
		inode->i_op = &bfs_dir_inops;
		inode->i_fop = &bfs_dir_operations;
64
	} else if (le32_to_cpu(di->i_vtype) == BFS_VREG) {
Linus Torvalds's avatar
Linus Torvalds committed
65
66
67
68
69
70
		inode->i_mode |= S_IFREG;
		inode->i_op = &bfs_file_inops;
		inode->i_fop = &bfs_file_operations;
		inode->i_mapping->a_ops = &bfs_aops;
	}

71
72
73
74
75
	BFS_I(inode)->i_sblock =  le32_to_cpu(di->i_sblock);
	BFS_I(inode)->i_eblock =  le32_to_cpu(di->i_eblock);
	inode->i_uid =  le32_to_cpu(di->i_uid);
	inode->i_gid =  le32_to_cpu(di->i_gid);
	inode->i_nlink =  le32_to_cpu(di->i_nlink);
Linus Torvalds's avatar
Linus Torvalds committed
76
77
	inode->i_size = BFS_FILESIZE(di);
	inode->i_blocks = BFS_FILEBLOCKS(di);
78
        if (inode->i_size || inode->i_blocks) dprintf("Registered inode with %lld size, %ld blocks\n", inode->i_size, inode->i_blocks);
Linus Torvalds's avatar
Linus Torvalds committed
79
	inode->i_blksize = PAGE_SIZE;
80
81
82
	inode->i_atime.tv_sec =  le32_to_cpu(di->i_atime);
	inode->i_mtime.tv_sec =  le32_to_cpu(di->i_mtime);
	inode->i_ctime.tv_sec =  le32_to_cpu(di->i_ctime);
Linus Torvalds's avatar
Linus Torvalds committed
83
84
85
	inode->i_atime.tv_nsec = 0;
	inode->i_mtime.tv_nsec = 0;
	inode->i_ctime.tv_nsec = 0;
86
	BFS_I(inode)->i_dsk_ino = le16_to_cpu(di->i_ino); /* can be 0 so we store a copy */
Linus Torvalds's avatar
Linus Torvalds committed
87
88
89
90
91
92

	brelse(bh);
}

static int bfs_write_inode(struct inode * inode, int unused)
{
93
94
	unsigned int ino = (u16)inode->i_ino;
        unsigned long i_sblock;
Linus Torvalds's avatar
Linus Torvalds committed
95
96
97
98
	struct bfs_inode * di;
	struct buffer_head * bh;
	int block, off;

99
100
        dprintf("ino=%08x\n", ino);

Linus Torvalds's avatar
Linus Torvalds committed
101
	if (ino < BFS_ROOT_INO || ino > BFS_SB(inode->i_sb)->si_lasti) {
102
		printf("Bad inode number %s:%08x\n", inode->i_sb->s_id, ino);
Linus Torvalds's avatar
Linus Torvalds committed
103
104
105
106
107
108
109
		return -EIO;
	}

	lock_kernel();
	block = (ino - BFS_ROOT_INO)/BFS_INODES_PER_BLOCK + 1;
	bh = sb_bread(inode->i_sb, block);
	if (!bh) {
110
		printf("Unable to read inode %s:%08x\n", inode->i_sb->s_id, ino);
Linus Torvalds's avatar
Linus Torvalds committed
111
112
113
114
115
116
117
		unlock_kernel();
		return -EIO;
	}

	off = (ino - BFS_ROOT_INO)%BFS_INODES_PER_BLOCK;
	di = (struct bfs_inode *)bh->b_data + off;

118
119
	if (ino == BFS_ROOT_INO)
		di->i_vtype = cpu_to_le32(BFS_VDIR);
Linus Torvalds's avatar
Linus Torvalds committed
120
	else
121
122
123
124
125
126
127
128
129
130
131
132
133
134
		di->i_vtype = cpu_to_le32(BFS_VREG);

	di->i_ino = cpu_to_le16(ino);
	di->i_mode = cpu_to_le32(inode->i_mode);
	di->i_uid = cpu_to_le32(inode->i_uid);
	di->i_gid = cpu_to_le32(inode->i_gid);
	di->i_nlink = cpu_to_le32(inode->i_nlink);
	di->i_atime = cpu_to_le32(inode->i_atime.tv_sec);
	di->i_mtime = cpu_to_le32(inode->i_mtime.tv_sec);
	di->i_ctime = cpu_to_le32(inode->i_ctime.tv_sec);
        i_sblock = BFS_I(inode)->i_sblock;
	di->i_sblock = cpu_to_le32(i_sblock);
	di->i_eblock = cpu_to_le32(BFS_I(inode)->i_eblock);
	di->i_eoffset = cpu_to_le32(i_sblock * BFS_BSIZE + inode->i_size - 1);
Linus Torvalds's avatar
Linus Torvalds committed
135
136

	mark_buffer_dirty(bh);
137
        dprintf("Written ino=%d into %d:%d\n",le16_to_cpu(di->i_ino),block,off);
Linus Torvalds's avatar
Linus Torvalds committed
138
139
140
141
142
143
144
145
146
147
148
149
150
	brelse(bh);
	unlock_kernel();
	return 0;
}

static void bfs_delete_inode(struct inode * inode)
{
	unsigned long ino = inode->i_ino;
	struct bfs_inode * di;
	struct buffer_head * bh;
	int block, off;
	struct super_block * s = inode->i_sb;
	struct bfs_sb_info * info = BFS_SB(s);
151
	struct bfs_inode_info * bi = BFS_I(inode);
Linus Torvalds's avatar
Linus Torvalds committed
152

153
	dprintf("ino=%08lx\n", ino);
Linus Torvalds's avatar
Linus Torvalds committed
154

155
156
	truncate_inode_pages(&inode->i_data, 0);

157
158
	if (ino < BFS_ROOT_INO || ino > info->si_lasti) {
		printf("invalid ino=%08lx\n", ino);
Linus Torvalds's avatar
Linus Torvalds committed
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
		return;
	}
	
	inode->i_size = 0;
	inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC;
	lock_kernel();
	mark_inode_dirty(inode);
	block = (ino - BFS_ROOT_INO)/BFS_INODES_PER_BLOCK + 1;
	bh = sb_bread(s, block);
	if (!bh) {
		printf("Unable to read inode %s:%08lx\n", inode->i_sb->s_id, ino);
		unlock_kernel();
		return;
	}
	off = (ino - BFS_ROOT_INO)%BFS_INODES_PER_BLOCK;
174
175
176
	di = (struct bfs_inode *) bh->b_data + off;
        if (bi->i_dsk_ino) {
		info->si_freeb += 1 + bi->i_eblock - bi->i_sblock;
Linus Torvalds's avatar
Linus Torvalds committed
177
		info->si_freei++;
178
		clear_bit(ino, info->si_imap);
Linus Torvalds's avatar
Linus Torvalds committed
179
		dump_imap("delete_inode", s);
180
        }
Linus Torvalds's avatar
Linus Torvalds committed
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
	di->i_ino = 0;
	di->i_sblock = 0;
	mark_buffer_dirty(bh);
	brelse(bh);

	/* if this was the last file, make the previous 
	   block "last files last block" even if there is no real file there,
	   saves us 1 gap */
	if (info->si_lf_eblk == BFS_I(inode)->i_eblock) {
		info->si_lf_eblk = BFS_I(inode)->i_sblock - 1;
		mark_buffer_dirty(info->si_sbh);
	}
	unlock_kernel();
	clear_inode(inode);
}

static void bfs_put_super(struct super_block *s)
{
	struct bfs_sb_info *info = BFS_SB(s);
	brelse(info->si_sbh);
	kfree(info->si_imap);
	kfree(info);
	s->s_fs_info = NULL;
}

206
static int bfs_statfs(struct dentry *dentry, struct kstatfs *buf)
Linus Torvalds's avatar
Linus Torvalds committed
207
{
208
	struct super_block *s = dentry->d_sb;
Linus Torvalds's avatar
Linus Torvalds committed
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
	struct bfs_sb_info *info = BFS_SB(s);
	u64 id = huge_encode_dev(s->s_bdev->bd_dev);
	buf->f_type = BFS_MAGIC;
	buf->f_bsize = s->s_blocksize;
	buf->f_blocks = info->si_blocks;
	buf->f_bfree = buf->f_bavail = info->si_freeb;
	buf->f_files = info->si_lasti + 1 - BFS_ROOT_INO;
	buf->f_ffree = info->si_freei;
	buf->f_fsid.val[0] = (u32)id;
	buf->f_fsid.val[1] = (u32)(id >> 32);
	buf->f_namelen = BFS_NAMELEN;
	return 0;
}

static void bfs_write_super(struct super_block *s)
{
	lock_kernel();
	if (!(s->s_flags & MS_RDONLY))
		mark_buffer_dirty(BFS_SB(s)->si_sbh);
	s->s_dirt = 0;
	unlock_kernel();
}

static kmem_cache_t * bfs_inode_cachep;

static struct inode *bfs_alloc_inode(struct super_block *sb)
{
	struct bfs_inode_info *bi;
	bi = kmem_cache_alloc(bfs_inode_cachep, SLAB_KERNEL);
	if (!bi)
		return NULL;
	return &bi->vfs_inode;
}

static void bfs_destroy_inode(struct inode *inode)
{
	kmem_cache_free(bfs_inode_cachep, BFS_I(inode));
}

static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags)
{
	struct bfs_inode_info *bi = foo;

	if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
	    SLAB_CTOR_CONSTRUCTOR)
		inode_init_once(&bi->vfs_inode);
}
 
static int init_inodecache(void)
{
	bfs_inode_cachep = kmem_cache_create("bfs_inode_cache",
					     sizeof(struct bfs_inode_info),
261
262
					     0, (SLAB_RECLAIM_ACCOUNT|
						SLAB_MEM_SPREAD),
Linus Torvalds's avatar
Linus Torvalds committed
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
					     init_once, NULL);
	if (bfs_inode_cachep == NULL)
		return -ENOMEM;
	return 0;
}

static void destroy_inodecache(void)
{
	if (kmem_cache_destroy(bfs_inode_cachep))
		printk(KERN_INFO "bfs_inode_cache: not all structures were freed\n");
}

static struct super_operations bfs_sops = {
	.alloc_inode	= bfs_alloc_inode,
	.destroy_inode	= bfs_destroy_inode,
	.read_inode	= bfs_read_inode,
	.write_inode	= bfs_write_inode,
	.delete_inode	= bfs_delete_inode,
	.put_super	= bfs_put_super,
	.write_super	= bfs_write_super,
	.statfs		= bfs_statfs,
};

void dump_imap(const char *prefix, struct super_block * s)
{
288
#ifdef DEBUG
Linus Torvalds's avatar
Linus Torvalds committed
289
290
291
292
293
294
	int i;
	char *tmpbuf = (char *)get_zeroed_page(GFP_KERNEL);

	if (!tmpbuf)
		return;
	for (i=BFS_SB(s)->si_lasti; i>=0; i--) {
295
		if (i > PAGE_SIZE-100) break;
Linus Torvalds's avatar
Linus Torvalds committed
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
		if (test_bit(i, BFS_SB(s)->si_imap))
			strcat(tmpbuf, "1");
		else
			strcat(tmpbuf, "0");
	}
	printk(KERN_ERR "BFS-fs: %s: lasti=%08lx <%s>\n", prefix, BFS_SB(s)->si_lasti, tmpbuf);
	free_page((unsigned long)tmpbuf);
#endif
}

static int bfs_fill_super(struct super_block *s, void *data, int silent)
{
	struct buffer_head * bh;
	struct bfs_super_block * bfs_sb;
	struct inode * inode;
311
	unsigned i, imap_len;
Linus Torvalds's avatar
Linus Torvalds committed
312
313
314
315
316
317
318
319
320
321
322
323
324
325
	struct bfs_sb_info * info;

	info = kmalloc(sizeof(*info), GFP_KERNEL);
	if (!info)
		return -ENOMEM;
	s->s_fs_info = info;
	memset(info, 0, sizeof(*info));

	sb_set_blocksize(s, BFS_BSIZE);

	bh = sb_bread(s, 0);
	if(!bh)
		goto out;
	bfs_sb = (struct bfs_super_block *)bh->b_data;
326
	if (le32_to_cpu(bfs_sb->s_magic) != BFS_MAGIC) {
Linus Torvalds's avatar
Linus Torvalds committed
327
328
		if (!silent)
			printf("No BFS filesystem on %s (magic=%08x)\n", 
329
				s->s_id,  le32_to_cpu(bfs_sb->s_magic));
Linus Torvalds's avatar
Linus Torvalds committed
330
331
332
333
334
335
336
		goto out;
	}
	if (BFS_UNCLEAN(bfs_sb, s) && !silent)
		printf("%s is unclean, continuing\n", s->s_id);

	s->s_magic = BFS_MAGIC;
	info->si_sbh = bh;
337
	info->si_lasti = (le32_to_cpu(bfs_sb->s_start) - BFS_BSIZE)/sizeof(struct bfs_inode)
Linus Torvalds's avatar
Linus Torvalds committed
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
			+ BFS_ROOT_INO - 1;

	imap_len = info->si_lasti/8 + 1;
	info->si_imap = kmalloc(imap_len, GFP_KERNEL);
	if (!info->si_imap)
		goto out;
	memset(info->si_imap, 0, imap_len);
	for (i=0; i<BFS_ROOT_INO; i++) 
		set_bit(i, info->si_imap);

	s->s_op = &bfs_sops;
	inode = iget(s, BFS_ROOT_INO);
	if (!inode) {
		kfree(info->si_imap);
		goto out;
	}
	s->s_root = d_alloc_root(inode);
	if (!s->s_root) {
		iput(inode);
		kfree(info->si_imap);
		goto out;
	}

361
	info->si_blocks = (le32_to_cpu(bfs_sb->s_end) + 1)>>BFS_BSIZE_BITS; /* for statfs(2) */
362
	info->si_freeb = (le32_to_cpu(bfs_sb->s_end) + 1 -  le32_to_cpu(bfs_sb->s_start))>>BFS_BSIZE_BITS;
Linus Torvalds's avatar
Linus Torvalds committed
363
364
365
366
	info->si_freei = 0;
	info->si_lf_eblk = 0;
	info->si_lf_sblk = 0;
	info->si_lf_ioff = 0;
Al Viro's avatar
Al Viro committed
367
	bh = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
368
	for (i=BFS_ROOT_INO; i<=info->si_lasti; i++) {
Al Viro's avatar
Al Viro committed
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
		struct bfs_inode *di;
		int block = (i - BFS_ROOT_INO)/BFS_INODES_PER_BLOCK + 1;
		int off = (i - BFS_ROOT_INO) % BFS_INODES_PER_BLOCK;
		unsigned long sblock, eblock;

		if (!off) {
			brelse(bh);
			bh = sb_bread(s, block);
		}

		if (!bh)
			continue;

		di = (struct bfs_inode *)bh->b_data + off;

		if (!di->i_ino) {
Linus Torvalds's avatar
Linus Torvalds committed
385
			info->si_freei++;
Al Viro's avatar
Al Viro committed
386
387
388
389
390
391
392
393
394
395
396
			continue;
		}
		set_bit(i, info->si_imap);
		info->si_freeb -= BFS_FILEBLOCKS(di);

		sblock =  le32_to_cpu(di->i_sblock);
		eblock =  le32_to_cpu(di->i_eblock);
		if (eblock > info->si_lf_eblk) {
			info->si_lf_eblk = eblock;
			info->si_lf_sblk = sblock;
			info->si_lf_ioff = BFS_INO2OFF(i);
Linus Torvalds's avatar
Linus Torvalds committed
397
398
		}
	}
Al Viro's avatar
Al Viro committed
399
	brelse(bh);
Linus Torvalds's avatar
Linus Torvalds committed
400
	if (!(s->s_flags & MS_RDONLY)) {
Al Viro's avatar
Al Viro committed
401
		mark_buffer_dirty(info->si_sbh);
Linus Torvalds's avatar
Linus Torvalds committed
402
403
404
405
406
407
408
409
410
411
412
413
		s->s_dirt = 1;
	} 
	dump_imap("read_super", s);
	return 0;

out:
	brelse(bh);
	kfree(info);
	s->s_fs_info = NULL;
	return -EINVAL;
}

414
415
static int bfs_get_sb(struct file_system_type *fs_type,
	int flags, const char *dev_name, void *data, struct vfsmount *mnt)
Linus Torvalds's avatar
Linus Torvalds committed
416
{
417
	return get_sb_bdev(fs_type, flags, dev_name, data, bfs_fill_super, mnt);
Linus Torvalds's avatar
Linus Torvalds committed
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
}

static struct file_system_type bfs_fs_type = {
	.owner		= THIS_MODULE,
	.name		= "bfs",
	.get_sb		= bfs_get_sb,
	.kill_sb	= kill_block_super,
	.fs_flags	= FS_REQUIRES_DEV,
};

static int __init init_bfs_fs(void)
{
	int err = init_inodecache();
	if (err)
		goto out1;
        err = register_filesystem(&bfs_fs_type);
	if (err)
		goto out;
	return 0;
out:
	destroy_inodecache();
out1:
	return err;
}

static void __exit exit_bfs_fs(void)
{
	unregister_filesystem(&bfs_fs_type);
	destroy_inodecache();
}

module_init(init_bfs_fs)
module_exit(exit_bfs_fs)