Blame view

fs/sysv/inode.c 9.59 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
  /*
   *  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.
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
23
24
25
26
27
  #include <linux/highuid.h>
  #include <linux/slab.h>
  #include <linux/init.h>
  #include <linux/buffer_head.h>
  #include <linux/vfs.h>
a9185b41a   Christoph Hellwig   pass writeback_co...
28
  #include <linux/writeback.h>
21acaf8e8   Duane Griffin   sysv: ensure fast...
29
  #include <linux/namei.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
30
31
  #include <asm/byteorder.h>
  #include "sysv.h"
ad43ffdee   Christoph Hellwig   sysv: add ->sync_fs
32
  static int sysv_sync_fs(struct super_block *sb, int wait)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
33
34
35
  {
  	struct sysv_sb_info *sbi = SYSV_SB(sb);
  	unsigned long time = get_seconds(), old_time;
ebc1ac164   Christoph Hellwig   ->write_super loc...
36
  	lock_super(sb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
37
38
39
40
41
42
  
  	/*
  	 * 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.
  	 */
4c9002de3   Al Viro   fix fs/sysv s_dir...
43
  	sb->s_dirt = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
44
45
46
47
48
49
50
  	old_time = fs32_to_cpu(sbi, *sbi->s_sb_time);
  	if (sbi->s_type == FSTYPE_SYSV4) {
  		if (*sbi->s_sb_state == cpu_to_fs32(sbi, 0x7c269d38 - old_time))
  			*sbi->s_sb_state = cpu_to_fs32(sbi, 0x7c269d38 - time);
  		*sbi->s_sb_time = cpu_to_fs32(sbi, time);
  		mark_buffer_dirty(sbi->s_bh2);
  	}
ad43ffdee   Christoph Hellwig   sysv: add ->sync_fs
51

ebc1ac164   Christoph Hellwig   ->write_super loc...
52
  	unlock_super(sb);
ad43ffdee   Christoph Hellwig   sysv: add ->sync_fs
53
54
55
56
57
58
59
60
61
62
  
  	return 0;
  }
  
  static void sysv_write_super(struct super_block *sb)
  {
  	if (!(sb->s_flags & MS_RDONLY))
  		sysv_sync_fs(sb, 1);
  	else
  		sb->s_dirt = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
63
64
65
66
67
  }
  
  static int sysv_remount(struct super_block *sb, int *flags, char *data)
  {
  	struct sysv_sb_info *sbi = SYSV_SB(sb);
bbd6851a3   Al Viro   Push lock_super()...
68
  	lock_super(sb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
69
70
  	if (sbi->s_forced_ro)
  		*flags |= MS_RDONLY;
719f2c879   Artem Bityutskiy   sysv: do not mark...
71
72
  	if (*flags & MS_RDONLY)
  		sysv_write_super(sb);
bbd6851a3   Al Viro   Push lock_super()...
73
  	unlock_super(sb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
74
75
76
77
78
79
  	return 0;
  }
  
  static void sysv_put_super(struct super_block *sb)
  {
  	struct sysv_sb_info *sbi = SYSV_SB(sb);
8c85e1251   Christoph Hellwig   remove ->write_su...
80
81
  	if (sb->s_dirt)
  		sysv_write_super(sb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
82
83
84
85
86
87
88
89
90
91
92
93
94
  	if (!(sb->s_flags & MS_RDONLY)) {
  		/* XXX ext2 also updates the state here */
  		mark_buffer_dirty(sbi->s_bh1);
  		if (sbi->s_bh1 != sbi->s_bh2)
  			mark_buffer_dirty(sbi->s_bh2);
  	}
  
  	brelse(sbi->s_bh1);
  	if (sbi->s_bh1 != sbi->s_bh2)
  		brelse(sbi->s_bh2);
  
  	kfree(sbi);
  }
726c33422   David Howells   [PATCH] VFS: Perm...
95
  static int sysv_statfs(struct dentry *dentry, struct kstatfs *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
96
  {
726c33422   David Howells   [PATCH] VFS: Perm...
97
  	struct super_block *sb = dentry->d_sb;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
98
  	struct sysv_sb_info *sbi = SYSV_SB(sb);
1c5b45411   Coly Li   fs/sysv: return f...
99
  	u64 id = huge_encode_dev(sb->s_bdev->bd_dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
100
101
102
103
104
105
106
107
  
  	buf->f_type = sb->s_magic;
  	buf->f_bsize = sb->s_blocksize;
  	buf->f_blocks = sbi->s_ndatazones;
  	buf->f_bavail = buf->f_bfree = sysv_count_free_blocks(sb);
  	buf->f_files = sbi->s_ninodes;
  	buf->f_ffree = sysv_count_free_inodes(sb);
  	buf->f_namelen = SYSV_NAMELEN;
1c5b45411   Coly Li   fs/sysv: return f...
108
109
  	buf->f_fsid.val[0] = (u32)id;
  	buf->f_fsid.val[1] = (u32)(id >> 32);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
  	return 0;
  }
  
  /* 
   * NXI <-> N0XI for PDP, XIN <-> XIN0 for le32, NIX <-> 0NIX for be32
   */
  static inline void read3byte(struct sysv_sb_info *sbi,
  	unsigned char * from, unsigned char * to)
  {
  	if (sbi->s_bytesex == BYTESEX_PDP) {
  		to[0] = from[0];
  		to[1] = 0;
  		to[2] = from[1];
  		to[3] = from[2];
  	} else if (sbi->s_bytesex == BYTESEX_LE) {
  		to[0] = from[0];
  		to[1] = from[1];
  		to[2] = from[2];
  		to[3] = 0;
  	} else {
  		to[0] = 0;
  		to[1] = from[0];
  		to[2] = from[1];
  		to[3] = from[2];
  	}
  }
  
  static inline void write3byte(struct sysv_sb_info *sbi,
  	unsigned char * from, unsigned char * to)
  {
  	if (sbi->s_bytesex == BYTESEX_PDP) {
  		to[0] = from[0];
  		to[1] = from[2];
  		to[2] = from[3];
  	} else if (sbi->s_bytesex == BYTESEX_LE) {
  		to[0] = from[0];
  		to[1] = from[1];
  		to[2] = from[2];
  	} else {
  		to[0] = from[1];
  		to[1] = from[2];
  		to[2] = from[3];
  	}
  }
c5ef1c42c   Arjan van de Ven   [PATCH] mark stru...
154
  static const struct inode_operations sysv_symlink_inode_operations = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
  	.readlink	= generic_readlink,
  	.follow_link	= page_follow_link_light,
  	.put_link	= page_put_link,
  	.getattr	= sysv_getattr,
  };
  
  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)) {
  		if (inode->i_blocks) {
  			inode->i_op = &sysv_symlink_inode_operations;
  			inode->i_mapping->a_ops = &sysv_aops;
21acaf8e8   Duane Griffin   sysv: ensure fast...
175
  		} else {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
176
  			inode->i_op = &sysv_fast_symlink_inode_operations;
21acaf8e8   Duane Griffin   sysv: ensure fast...
177
178
179
  			nd_terminate_link(SYSV_I(inode)->i_data, inode->i_size,
  				sizeof(SYSV_I(inode)->i_data) - 1);
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
180
181
182
  	} else
  		init_special_inode(inode, inode->i_mode, rdev);
  }
b8e1343f6   David Howells   iget: stop the SY...
183
  struct inode *sysv_iget(struct super_block *sb, unsigned int ino)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
184
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
185
186
187
188
  	struct sysv_sb_info * sbi = SYSV_SB(sb);
  	struct buffer_head * bh;
  	struct sysv_inode * raw_inode;
  	struct sysv_inode_info * si;
b8e1343f6   David Howells   iget: stop the SY...
189
190
  	struct inode *inode;
  	unsigned int block;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
191
192
193
194
  
  	if (!ino || ino > sbi->s_ninodes) {
  		printk("Bad inode number on dev %s: %d is out of range
  ",
b8e1343f6   David Howells   iget: stop the SY...
195
196
  		       sb->s_id, ino);
  		return ERR_PTR(-EIO);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
197
  	}
b8e1343f6   David Howells   iget: stop the SY...
198
199
200
201
202
203
  
  	inode = iget_locked(sb, ino);
  	if (!inode)
  		return ERR_PTR(-ENOMEM);
  	if (!(inode->i_state & I_NEW))
  		return inode;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
204
205
206
207
208
209
210
211
212
213
214
  	raw_inode = sysv_raw_inode(sb, ino, &bh);
  	if (!raw_inode) {
  		printk("Major problem: unable to read inode from dev %s
  ",
  		       inode->i_sb->s_id);
  		goto bad_inode;
  	}
  	/* SystemV FS: kludge permissions if ino==SYSV_ROOT_INO ?? */
  	inode->i_mode = fs16_to_cpu(sbi, raw_inode->i_mode);
  	inode->i_uid = (uid_t)fs16_to_cpu(sbi, raw_inode->i_uid);
  	inode->i_gid = (gid_t)fs16_to_cpu(sbi, raw_inode->i_gid);
bfe868486   Miklos Szeredi   filesystems: add ...
215
  	set_nlink(inode, fs16_to_cpu(sbi, raw_inode->i_nlink));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
216
217
218
219
220
221
222
  	inode->i_size = fs32_to_cpu(sbi, raw_inode->i_size);
  	inode->i_atime.tv_sec = fs32_to_cpu(sbi, raw_inode->i_atime);
  	inode->i_mtime.tv_sec = fs32_to_cpu(sbi, raw_inode->i_mtime);
  	inode->i_ctime.tv_sec = fs32_to_cpu(sbi, raw_inode->i_ctime);
  	inode->i_ctime.tv_nsec = 0;
  	inode->i_atime.tv_nsec = 0;
  	inode->i_mtime.tv_nsec = 0;
ba52de123   Theodore Ts'o   [PATCH] inode-die...
223
  	inode->i_blocks = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
224
225
226
227
228
229
230
231
232
233
234
235
  
  	si = SYSV_I(inode);
  	for (block = 0; block < 10+1+1+1; block++)
  		read3byte(sbi, &raw_inode->i_data[3*block],
  				(u8 *)&si->i_data[block]);
  	brelse(bh);
  	si->i_dir_start_lookup = 0;
  	if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
  		sysv_set_inode(inode,
  			       old_decode_dev(fs32_to_cpu(sbi, si->i_data[0])));
  	else
  		sysv_set_inode(inode, 0);
b8e1343f6   David Howells   iget: stop the SY...
236
237
  	unlock_new_inode(inode);
  	return inode;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
238
239
  
  bad_inode:
b8e1343f6   David Howells   iget: stop the SY...
240
241
  	iget_failed(inode);
  	return ERR_PTR(-EIO);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
242
  }
a9185b41a   Christoph Hellwig   pass writeback_co...
243
  static int __sysv_write_inode(struct inode *inode, int wait)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
244
245
246
247
248
249
250
  {
  	struct super_block * sb = inode->i_sb;
  	struct sysv_sb_info * sbi = SYSV_SB(sb);
  	struct buffer_head * bh;
  	struct sysv_inode * raw_inode;
  	struct sysv_inode_info * si;
  	unsigned int ino, block;
05459ca81   Al Viro   repair sysv_write...
251
  	int err = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
252
253
254
255
256
257
  
  	ino = inode->i_ino;
  	if (!ino || ino > sbi->s_ninodes) {
  		printk("Bad inode number on dev %s: %d is out of range
  ",
  		       inode->i_sb->s_id, ino);
05459ca81   Al Viro   repair sysv_write...
258
  		return -EIO;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
259
260
261
262
263
  	}
  	raw_inode = sysv_raw_inode(sb, ino, &bh);
  	if (!raw_inode) {
  		printk("unable to read i-node block
  ");
05459ca81   Al Viro   repair sysv_write...
264
  		return -EIO;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
  	}
  
  	raw_inode->i_mode = cpu_to_fs16(sbi, inode->i_mode);
  	raw_inode->i_uid = cpu_to_fs16(sbi, fs_high2lowuid(inode->i_uid));
  	raw_inode->i_gid = cpu_to_fs16(sbi, fs_high2lowgid(inode->i_gid));
  	raw_inode->i_nlink = cpu_to_fs16(sbi, inode->i_nlink);
  	raw_inode->i_size = cpu_to_fs32(sbi, inode->i_size);
  	raw_inode->i_atime = cpu_to_fs32(sbi, inode->i_atime.tv_sec);
  	raw_inode->i_mtime = cpu_to_fs32(sbi, inode->i_mtime.tv_sec);
  	raw_inode->i_ctime = cpu_to_fs32(sbi, inode->i_ctime.tv_sec);
  
  	si = SYSV_I(inode);
  	if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
  		si->i_data[0] = cpu_to_fs32(sbi, old_encode_dev(inode->i_rdev));
  	for (block = 0; block < 10+1+1+1; block++)
  		write3byte(sbi, (u8 *)&si->i_data[block],
  			&raw_inode->i_data[3*block]);
  	mark_buffer_dirty(bh);
05459ca81   Al Viro   repair sysv_write...
283
284
285
286
287
288
289
290
291
  	if (wait) {
                  sync_dirty_buffer(bh);
                  if (buffer_req(bh) && !buffer_uptodate(bh)) {
                          printk ("IO error syncing sysv inode [%s:%08x]
  ",
                                  sb->s_id, ino);
                          err = -EIO;
                  }
          }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
292
  	brelse(bh);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
293
294
  	return 0;
  }
a9185b41a   Christoph Hellwig   pass writeback_co...
295
296
297
298
  int sysv_write_inode(struct inode *inode, struct writeback_control *wbc)
  {
  	return __sysv_write_inode(inode, wbc->sync_mode == WB_SYNC_ALL);
  }
05459ca81   Al Viro   repair sysv_write...
299
  int sysv_sync_inode(struct inode *inode)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
300
  {
a9185b41a   Christoph Hellwig   pass writeback_co...
301
  	return __sysv_write_inode(inode, 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
302
  }
d299eadc0   Al Viro   switch sysv to ->...
303
  static void sysv_evict_inode(struct inode *inode)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
304
  {
fef266580   Mark Fasheh   [PATCH] update fi...
305
  	truncate_inode_pages(&inode->i_data, 0);
d299eadc0   Al Viro   switch sysv to ->...
306
307
308
309
310
311
312
313
  	if (!inode->i_nlink) {
  		inode->i_size = 0;
  		sysv_truncate(inode);
  	}
  	invalidate_inode_buffers(inode);
  	end_writeback(inode);
  	if (!inode->i_nlink)
  		sysv_free_inode(inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
314
  }
e18b890bb   Christoph Lameter   [PATCH] slab: rem...
315
  static struct kmem_cache *sysv_inode_cachep;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
316
317
318
319
  
  static struct inode *sysv_alloc_inode(struct super_block *sb)
  {
  	struct sysv_inode_info *si;
e94b17660   Christoph Lameter   [PATCH] slab: rem...
320
  	si = kmem_cache_alloc(sysv_inode_cachep, GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
321
322
323
324
  	if (!si)
  		return NULL;
  	return &si->vfs_inode;
  }
fa0d7e3de   Nick Piggin   fs: icache RCU fr...
325
  static void sysv_i_callback(struct rcu_head *head)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
326
  {
fa0d7e3de   Nick Piggin   fs: icache RCU fr...
327
  	struct inode *inode = container_of(head, struct inode, i_rcu);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
328
329
  	kmem_cache_free(sysv_inode_cachep, SYSV_I(inode));
  }
fa0d7e3de   Nick Piggin   fs: icache RCU fr...
330
331
332
333
  static void sysv_destroy_inode(struct inode *inode)
  {
  	call_rcu(&inode->i_rcu, sysv_i_callback);
  }
51cc50685   Alexey Dobriyan   SL*B: drop kmem c...
334
  static void init_once(void *p)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
335
336
  {
  	struct sysv_inode_info *si = (struct sysv_inode_info *)p;
a35afb830   Christoph Lameter   Remove SLAB_CTOR_...
337
  	inode_init_once(&si->vfs_inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
338
  }
ee9b6d61a   Josef 'Jeff' Sipek   [PATCH] Mark stru...
339
  const struct super_operations sysv_sops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
340
341
  	.alloc_inode	= sysv_alloc_inode,
  	.destroy_inode	= sysv_destroy_inode,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
342
  	.write_inode	= sysv_write_inode,
d299eadc0   Al Viro   switch sysv to ->...
343
  	.evict_inode	= sysv_evict_inode,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
344
345
  	.put_super	= sysv_put_super,
  	.write_super	= sysv_write_super,
ad43ffdee   Christoph Hellwig   sysv: add ->sync_fs
346
  	.sync_fs	= sysv_sync_fs,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
347
348
349
350
351
352
353
354
  	.remount_fs	= sysv_remount,
  	.statfs		= sysv_statfs,
  };
  
  int __init sysv_init_icache(void)
  {
  	sysv_inode_cachep = kmem_cache_create("sysv_inode_cache",
  			sizeof(struct sysv_inode_info), 0,
4b6a9316f   Paul Jackson   [PATCH] cpuset me...
355
  			SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD,
20c2df83d   Paul Mundt   mm: Remove slab d...
356
  			init_once);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
357
358
359
360
361
362
363
364
365
  	if (!sysv_inode_cachep)
  		return -ENOMEM;
  	return 0;
  }
  
  void sysv_destroy_icache(void)
  {
  	kmem_cache_destroy(sysv_inode_cachep);
  }