inode.c 8.42 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
/*
 *  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 allocating/freeing inodes and for read/writing
 *  the superblock.
 */

#include <linux/fs.h>
#include <linux/sysv_fs.h>
#include <linux/locks.h>
#include <linux/smp_lock.h>
#include <linux/highuid.h>
Linus Torvalds's avatar
Linus Torvalds committed
29
#include <linux/slab.h>
Linus Torvalds's avatar
Linus Torvalds committed
30
#include <linux/init.h>
Linus Torvalds's avatar
Linus Torvalds committed
31 32 33 34 35
#include <asm/byteorder.h>

/* This is only called on sync() and umount(), when s_dirt=1. */
static void sysv_write_super(struct super_block *sb)
{
36
	lock_kernel();
Linus Torvalds's avatar
Linus Torvalds committed
37
	if (!(sb->s_flags & MS_RDONLY)) {
Linus Torvalds's avatar
Linus Torvalds committed
38 39 40 41
		/* If we are going to write out the super block,
		   then attach current time stamp.
		   But if the filesystem was marked clean, keep it clean. */
		unsigned long time = CURRENT_TIME;
Linus Torvalds's avatar
Linus Torvalds committed
42
		unsigned long old_time = fs32_to_cpu(sb, *sb->sv_sb_time);
Linus Torvalds's avatar
Linus Torvalds committed
43
		if (sb->sv_type == FSTYPE_SYSV4)
Linus Torvalds's avatar
Linus Torvalds committed
44 45 46
			if (*sb->sv_sb_state == cpu_to_fs32(sb, 0x7c269d38 - old_time))
				*sb->sv_sb_state = cpu_to_fs32(sb, 0x7c269d38 - time);
		*sb->sv_sb_time = cpu_to_fs32(sb, time);
Linus Torvalds's avatar
Linus Torvalds committed
47 48 49
		mark_buffer_dirty(sb->sv_bh2);
	}
	sb->s_dirt = 0;
50
	unlock_kernel();
Linus Torvalds's avatar
Linus Torvalds committed
51 52 53 54
}

static void sysv_put_super(struct super_block *sb)
{
Linus Torvalds's avatar
Linus Torvalds committed
55 56 57 58 59 60 61
	if (!(sb->s_flags & MS_RDONLY)) {
		/* XXX ext2 also updates the state here */
		mark_buffer_dirty(sb->sv_bh1);
		if (sb->sv_bh1 != sb->sv_bh2)
			mark_buffer_dirty(sb->sv_bh2);
	}

Linus Torvalds's avatar
Linus Torvalds committed
62
	brelse(sb->sv_bh1);
Linus Torvalds's avatar
Linus Torvalds committed
63 64
	if (sb->sv_bh1 != sb->sv_bh2)
		brelse(sb->sv_bh2);
Linus Torvalds's avatar
Linus Torvalds committed
65 66 67 68
}

static int sysv_statfs(struct super_block *sb, struct statfs *buf)
{
Linus Torvalds's avatar
Linus Torvalds committed
69 70 71 72 73 74
	buf->f_type = sb->s_magic;
	buf->f_bsize = sb->s_blocksize;
	buf->f_blocks = sb->sv_ndatazones;
	buf->f_bavail = buf->f_bfree = sysv_count_free_blocks(sb);
	buf->f_files = sb->sv_ninodes;
	buf->f_ffree = sysv_count_free_inodes(sb);
Linus Torvalds's avatar
Linus Torvalds committed
75 76 77 78
	buf->f_namelen = SYSV_NAMELEN;
	return 0;
}

Linus Torvalds's avatar
Linus Torvalds committed
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
/* 
 * NXI <-> N0XI for PDP, XIN <-> XIN0 for le32, NIX <-> 0NIX for be32
 */
static inline void read3byte(struct super_block *sb,
	unsigned char * from, unsigned char * to)
{
	if (sb->sv_bytesex == BYTESEX_PDP) {
		to[0] = from[0];
		to[1] = 0;
		to[2] = from[1];
		to[3] = from[2];
	} else if (sb->sv_bytesex == BYTESEX_LE) {
		to[0] = from[0];
		to[1] = from[1];
		to[2] = from[2];
		to[3] = 0;
Linus Torvalds's avatar
Linus Torvalds committed
95
	} else {
Linus Torvalds's avatar
Linus Torvalds committed
96 97 98 99
		to[0] = 0;
		to[1] = from[0];
		to[2] = from[1];
		to[3] = from[2];
Linus Torvalds's avatar
Linus Torvalds committed
100 101 102
	}
}

Linus Torvalds's avatar
Linus Torvalds committed
103 104
static inline void write3byte(struct super_block *sb,
	unsigned char * from, unsigned char * to)
Linus Torvalds's avatar
Linus Torvalds committed
105
{
Linus Torvalds's avatar
Linus Torvalds committed
106 107 108 109 110 111 112 113
	if (sb->sv_bytesex == BYTESEX_PDP) {
		to[0] = from[0];
		to[1] = from[2];
		to[2] = from[3];
	} else if (sb->sv_bytesex == BYTESEX_LE) {
		to[0] = from[0];
		to[1] = from[1];
		to[2] = from[2];
Linus Torvalds's avatar
Linus Torvalds committed
114
	} else {
Linus Torvalds's avatar
Linus Torvalds committed
115 116 117
		to[0] = from[1];
		to[1] = from[2];
		to[2] = from[3];
Linus Torvalds's avatar
Linus Torvalds committed
118 119 120
	}
}

Linus Torvalds's avatar
Linus Torvalds committed
121
static struct inode_operations sysv_symlink_inode_operations = {
Linus Torvalds's avatar
Linus Torvalds committed
122 123 124 125
	readlink:	page_readlink,
	follow_link:	page_follow_link,
};

Linus Torvalds's avatar
Linus Torvalds committed
126 127 128 129 130 131 132 133 134 135 136
void sysv_set_inode(struct inode *inode, dev_t rdev)
{
	if (S_ISREG(inode->i_mode)) {
		inode->i_op = &sysv_file_inode_operations;
		inode->i_fop = &sysv_file_operations;
		inode->i_mapping->a_ops = &sysv_aops;
	} else if (S_ISDIR(inode->i_mode)) {
		inode->i_op = &sysv_dir_inode_operations;
		inode->i_fop = &sysv_dir_operations;
		inode->i_mapping->a_ops = &sysv_aops;
	} else if (S_ISLNK(inode->i_mode)) {
Linus Torvalds's avatar
Linus Torvalds committed
137 138 139 140 141
		if (inode->i_blocks) {
			inode->i_op = &sysv_symlink_inode_operations;
			inode->i_mapping->a_ops = &sysv_aops;
		} else
			inode->i_op = &sysv_fast_symlink_inode_operations;
Linus Torvalds's avatar
Linus Torvalds committed
142 143 144 145
	} else
		init_special_inode(inode, inode->i_mode, rdev);
}

Linus Torvalds's avatar
Linus Torvalds committed
146 147 148 149 150
static void sysv_read_inode(struct inode *inode)
{
	struct super_block * sb = inode->i_sb;
	struct buffer_head * bh;
	struct sysv_inode * raw_inode;
Linus Torvalds's avatar
Linus Torvalds committed
151
	struct sysv_inode_info * si;
Linus Torvalds's avatar
Linus Torvalds committed
152
	unsigned int block, ino;
Linus Torvalds's avatar
Linus Torvalds committed
153
	dev_t rdev = 0;
Linus Torvalds's avatar
Linus Torvalds committed
154 155 156

	ino = inode->i_ino;
	if (!ino || ino > sb->sv_ninodes) {
Linus Torvalds's avatar
Linus Torvalds committed
157 158
		printk("Bad inode number on dev %s: %d is out of range\n",
		       inode->i_sb->s_id, ino);
Linus Torvalds's avatar
Linus Torvalds committed
159
		goto bad_inode;
Linus Torvalds's avatar
Linus Torvalds committed
160
	}
Linus Torvalds's avatar
Linus Torvalds committed
161 162 163
	raw_inode = sysv_raw_inode(sb, ino, &bh);
	if (!raw_inode) {
		printk("Major problem: unable to read inode from dev %s\n",
Linus Torvalds's avatar
Linus Torvalds committed
164
		       inode->i_sb->s_id);
Linus Torvalds's avatar
Linus Torvalds committed
165
		goto bad_inode;
Linus Torvalds's avatar
Linus Torvalds committed
166 167
	}
	/* SystemV FS: kludge permissions if ino==SYSV_ROOT_INO ?? */
Linus Torvalds's avatar
Linus Torvalds committed
168
	inode->i_mode = fs16_to_cpu(sb, raw_inode->i_mode);
Linus Torvalds's avatar
Linus Torvalds committed
169 170 171 172 173 174 175
	inode->i_uid = (uid_t)fs16_to_cpu(sb, raw_inode->i_uid);
	inode->i_gid = (gid_t)fs16_to_cpu(sb, raw_inode->i_gid);
	inode->i_nlink = fs16_to_cpu(sb, raw_inode->i_nlink);
	inode->i_size = fs32_to_cpu(sb, raw_inode->i_size);
	inode->i_atime = fs32_to_cpu(sb, raw_inode->i_atime);
	inode->i_mtime = fs32_to_cpu(sb, raw_inode->i_mtime);
	inode->i_ctime = fs32_to_cpu(sb, raw_inode->i_ctime);
Linus Torvalds's avatar
Linus Torvalds committed
176
	inode->i_blocks = inode->i_blksize = 0;
Linus Torvalds's avatar
Linus Torvalds committed
177 178

	si = SYSV_I(inode);
Linus Torvalds's avatar
Linus Torvalds committed
179 180
	for (block = 0; block < 10+1+1+1; block++)
		read3byte(sb, &raw_inode->i_a.i_addb[3*block],
Linus Torvalds's avatar
Linus Torvalds committed
181
			(unsigned char*)&si->i_data[block]);
Linus Torvalds's avatar
Linus Torvalds committed
182
	brelse(bh);
Linus Torvalds's avatar
Linus Torvalds committed
183
	if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
Linus Torvalds's avatar
Linus Torvalds committed
184 185
		rdev = (u16)fs32_to_cpu(sb, si->i_data[0]);
	si->i_dir_start_lookup = 0;
Linus Torvalds's avatar
Linus Torvalds committed
186
	sysv_set_inode(inode, rdev);
Linus Torvalds's avatar
Linus Torvalds committed
187 188 189 190 191
	return;

bad_inode:
	make_bad_inode(inode);
	return;
Linus Torvalds's avatar
Linus Torvalds committed
192 193 194 195 196 197 198
}

static struct buffer_head * sysv_update_inode(struct inode * inode)
{
	struct super_block * sb = inode->i_sb;
	struct buffer_head * bh;
	struct sysv_inode * raw_inode;
Linus Torvalds's avatar
Linus Torvalds committed
199
	struct sysv_inode_info * si;
Linus Torvalds's avatar
Linus Torvalds committed
200 201 202 203
	unsigned int ino, block;

	ino = inode->i_ino;
	if (!ino || ino > sb->sv_ninodes) {
Linus Torvalds's avatar
Linus Torvalds committed
204
		printk("Bad inode number on dev %s: %d is out of range\n",
Linus Torvalds's avatar
Linus Torvalds committed
205
		       inode->i_sb->s_id, ino);
Linus Torvalds's avatar
Linus Torvalds committed
206 207
		return 0;
	}
Linus Torvalds's avatar
Linus Torvalds committed
208 209
	raw_inode = sysv_raw_inode(sb, ino, &bh);
	if (!raw_inode) {
Linus Torvalds's avatar
Linus Torvalds committed
210 211 212
		printk("unable to read i-node block\n");
		return 0;
	}
Linus Torvalds's avatar
Linus Torvalds committed
213 214

	raw_inode->i_mode = cpu_to_fs16(sb, inode->i_mode);
Linus Torvalds's avatar
Linus Torvalds committed
215 216 217 218 219 220 221
	raw_inode->i_uid = cpu_to_fs16(sb, fs_high2lowuid(inode->i_uid));
	raw_inode->i_gid = cpu_to_fs16(sb, fs_high2lowgid(inode->i_gid));
	raw_inode->i_nlink = cpu_to_fs16(sb, inode->i_nlink);
	raw_inode->i_size = cpu_to_fs32(sb, inode->i_size);
	raw_inode->i_atime = cpu_to_fs32(sb, inode->i_atime);
	raw_inode->i_mtime = cpu_to_fs32(sb, inode->i_mtime);
	raw_inode->i_ctime = cpu_to_fs32(sb, inode->i_ctime);
Linus Torvalds's avatar
Linus Torvalds committed
222 223

	si = SYSV_I(inode);
Linus Torvalds's avatar
Linus Torvalds committed
224
	if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
Linus Torvalds's avatar
Linus Torvalds committed
225
		si->i_data[0] = cpu_to_fs32(sb, kdev_t_to_nr(inode->i_rdev));
Linus Torvalds's avatar
Linus Torvalds committed
226
	for (block = 0; block < 10+1+1+1; block++)
Linus Torvalds's avatar
Linus Torvalds committed
227
		write3byte(sb, (unsigned char*)&si->i_data[block],
Linus Torvalds's avatar
Linus Torvalds committed
228
			&raw_inode->i_a.i_addb[3*block]);
Linus Torvalds's avatar
Linus Torvalds committed
229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250
	mark_buffer_dirty(bh);
	return bh;
}

void sysv_write_inode(struct inode * inode, int wait)
{
	struct buffer_head *bh;
	lock_kernel();
	bh = sysv_update_inode(inode);
	brelse(bh);
	unlock_kernel();
}

int sysv_sync_inode(struct inode * inode)
{
        int err = 0;
        struct buffer_head *bh;

        bh = sysv_update_inode(inode);
        if (bh && buffer_dirty(bh)) {
                ll_rw_block(WRITE, 1, &bh);
                wait_on_buffer(bh);
Linus Torvalds's avatar
Linus Torvalds committed
251 252
                if (buffer_req(bh) && !buffer_uptodate(bh)) {
                        printk ("IO error syncing sysv inode [%s:%08lx]\n",
Linus Torvalds's avatar
Linus Torvalds committed
253
                                inode->i_sb->s_id, inode->i_ino);
Linus Torvalds's avatar
Linus Torvalds committed
254 255 256 257 258 259 260 261 262
                        err = -1;
                }
        }
        else if (!bh)
                err = -1;
        brelse (bh);
        return err;
}

Linus Torvalds's avatar
Linus Torvalds committed
263
static void sysv_delete_inode(struct inode *inode)
Linus Torvalds's avatar
Linus Torvalds committed
264
{
Linus Torvalds's avatar
Linus Torvalds committed
265 266
	inode->i_size = 0;
	sysv_truncate(inode);
267
	lock_kernel();
Linus Torvalds's avatar
Linus Torvalds committed
268 269
	sysv_free_inode(inode);
	unlock_kernel();
Linus Torvalds's avatar
Linus Torvalds committed
270 271
}

Linus Torvalds's avatar
Linus Torvalds committed
272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297
static kmem_cache_t *sysv_inode_cachep;

static struct inode *sysv_alloc_inode(struct super_block *sb)
{
	struct sysv_inode_info *si;

	si = kmem_cache_alloc(sysv_inode_cachep, SLAB_KERNEL);
	if (!si)
		return NULL;
	return &si->vfs_inode;
}

static void sysv_destroy_inode(struct inode *inode)
{
	kmem_cache_free(sysv_inode_cachep, SYSV_I(inode));
}

static void init_once(void *p, kmem_cache_t *cachep, unsigned long flags)
{
	struct sysv_inode_info *si = (struct sysv_inode_info *)p;

	if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
			SLAB_CTOR_CONSTRUCTOR)
		inode_init_once(&si->vfs_inode);
}

Linus Torvalds's avatar
Linus Torvalds committed
298
struct super_operations sysv_sops = {
Linus Torvalds's avatar
Linus Torvalds committed
299 300
	alloc_inode:	sysv_alloc_inode,
	destroy_inode:	sysv_destroy_inode,
Linus Torvalds's avatar
Linus Torvalds committed
301 302 303 304 305 306 307
	read_inode:	sysv_read_inode,
	write_inode:	sysv_write_inode,
	delete_inode:	sysv_delete_inode,
	put_super:	sysv_put_super,
	write_super:	sysv_write_super,
	statfs:		sysv_statfs,
};
Linus Torvalds's avatar
Linus Torvalds committed
308 309 310 311 312 313 314 315 316 317 318 319 320 321 322

int __init sysv_init_icache(void)
{
	sysv_inode_cachep = kmem_cache_create("sysv_inode_cache",
			sizeof(struct sysv_inode_info), 0,
			SLAB_HWCACHE_ALIGN, init_once, NULL);
	if (!sysv_inode_cachep)
		return -ENOMEM;
	return 0;
}

void sysv_destroy_icache(void)
{
	kmem_cache_destroy(sysv_inode_cachep);
}