Blame view

fs/efs/inode.c 8.39 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
  /*
   * inode.c
   *
   * Copyright (c) 1999 Al Smith
   *
   * Portions derived from work (c) 1995,1996 Christian Vogelgsang,
   *              and from work (c) 1998 Mike Shaver.
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
9
10
11
  #include <linux/buffer_head.h>
  #include <linux/module.h>
  #include <linux/fs.h>
45254b4fb   Christoph Hellwig   efs: move headers...
12
13
  #include "efs.h"
  #include <linux/efs_fs_sb.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
14
15
16
17
18
19
20
21
22
  
  static int efs_readpage(struct file *file, struct page *page)
  {
  	return block_read_full_page(page,efs_get_block);
  }
  static sector_t _efs_bmap(struct address_space *mapping, sector_t block)
  {
  	return generic_block_bmap(mapping,block,efs_get_block);
  }
f5e54d6e5   Christoph Hellwig   [PATCH] mark addr...
23
  static const struct address_space_operations efs_aops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
24
  	.readpage = efs_readpage,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
  	.bmap = _efs_bmap
  };
  
  static inline void extent_copy(efs_extent *src, efs_extent *dst) {
  	/*
  	 * this is slightly evil. it doesn't just copy
  	 * efs_extent from src to dst, it also mangles
  	 * the bits so that dst ends up in cpu byte-order.
  	 */
  
  	dst->cooked.ex_magic  =  (unsigned int) src->raw[0];
  	dst->cooked.ex_bn     = ((unsigned int) src->raw[1] << 16) |
  				((unsigned int) src->raw[2] <<  8) |
  				((unsigned int) src->raw[3] <<  0);
  	dst->cooked.ex_length =  (unsigned int) src->raw[4];
  	dst->cooked.ex_offset = ((unsigned int) src->raw[5] << 16) |
  				((unsigned int) src->raw[6] <<  8) |
  				((unsigned int) src->raw[7] <<  0);
  	return;
  }
298384cd7   David Howells   iget: stop EFS fr...
45
  struct inode *efs_iget(struct super_block *super, unsigned long ino)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
46
47
48
49
50
  {
  	int i, inode_index;
  	dev_t device;
  	u32 rdev;
  	struct buffer_head *bh;
298384cd7   David Howells   iget: stop EFS fr...
51
52
  	struct efs_sb_info    *sb = SUPER_INFO(super);
  	struct efs_inode_info *in;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
53
54
  	efs_block_t block, offset;
  	struct efs_dinode *efs_inode;
298384cd7   David Howells   iget: stop EFS fr...
55
56
57
  	struct inode *inode;
  
  	inode = iget_locked(super, ino);
136eefa48   Dan Carpenter   efs: iget_locked(...
58
  	if (!inode)
298384cd7   David Howells   iget: stop EFS fr...
59
60
61
62
63
  		return ERR_PTR(-ENOMEM);
  	if (!(inode->i_state & I_NEW))
  		return inode;
  
  	in = INODE_INFO(inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
  	/*
  	** EFS layout:
  	**
  	** |   cylinder group    |   cylinder group    |   cylinder group ..etc
  	** |inodes|data          |inodes|data          |inodes|data       ..etc
  	**
  	** work out the inode block index, (considering initially that the
  	** inodes are stored as consecutive blocks). then work out the block
  	** number of that inode given the above layout, and finally the
  	** offset of the inode within that block.
  	*/
  
  	inode_index = inode->i_ino /
  		(EFS_BLOCKSIZE / sizeof(struct efs_dinode));
  
  	block = sb->fs_start + sb->first_block + 
  		(sb->group_size * (inode_index / sb->inode_blocks)) +
  		(inode_index % sb->inode_blocks);
  
  	offset = (inode->i_ino %
  			(EFS_BLOCKSIZE / sizeof(struct efs_dinode))) *
  		sizeof(struct efs_dinode);
  
  	bh = sb_bread(inode->i_sb, block);
  	if (!bh) {
f403d1dba   Fabian Frederick   fs/efs: add pr_fm...
89
90
  		pr_warn("%s() failed at block %d
  ", __func__, block);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
91
92
93
94
95
96
  		goto read_inode_error;
  	}
  
  	efs_inode = (struct efs_dinode *) (bh->b_data + offset);
      
  	inode->i_mode  = be16_to_cpu(efs_inode->di_mode);
bfe868486   Miklos Szeredi   filesystems: add ...
97
  	set_nlink(inode, be16_to_cpu(efs_inode->di_nlink));
5d4ea4da6   Eric W. Biederman   userns: Convert e...
98
99
  	i_uid_write(inode, (uid_t)be16_to_cpu(efs_inode->di_uid));
  	i_gid_write(inode, (gid_t)be16_to_cpu(efs_inode->di_gid));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
  	inode->i_size  = be32_to_cpu(efs_inode->di_size);
  	inode->i_atime.tv_sec = be32_to_cpu(efs_inode->di_atime);
  	inode->i_mtime.tv_sec = be32_to_cpu(efs_inode->di_mtime);
  	inode->i_ctime.tv_sec = be32_to_cpu(efs_inode->di_ctime);
  	inode->i_atime.tv_nsec = inode->i_mtime.tv_nsec = inode->i_ctime.tv_nsec = 0;
  
  	/* this is the number of blocks in the file */
  	if (inode->i_size == 0) {
  		inode->i_blocks = 0;
  	} else {
  		inode->i_blocks = ((inode->i_size - 1) >> EFS_BLOCKSIZE_BITS) + 1;
  	}
  
  	rdev = be16_to_cpu(efs_inode->di_u.di_dev.odev);
  	if (rdev == 0xffff) {
  		rdev = be32_to_cpu(efs_inode->di_u.di_dev.ndev);
  		if (sysv_major(rdev) > 0xfff)
  			device = 0;
  		else
  			device = MKDEV(sysv_major(rdev), sysv_minor(rdev));
  	} else
  		device = old_decode_dev(rdev);
  
  	/* get the number of extents for this object */
  	in->numextents = be16_to_cpu(efs_inode->di_numextents);
  	in->lastextent = 0;
  
  	/* copy the extents contained within the inode to memory */
  	for(i = 0; i < EFS_DIRECTEXTENTS; i++) {
  		extent_copy(&(efs_inode->di_u.di_extents[i]), &(in->extents[i]));
  		if (i < in->numextents && in->extents[i].cooked.ex_magic != 0) {
f403d1dba   Fabian Frederick   fs/efs: add pr_fm...
131
132
133
  			pr_warn("extent %d has bad magic number in inode %lu
  ",
  				i, inode->i_ino);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
134
135
136
137
138
139
  			brelse(bh);
  			goto read_inode_error;
  		}
  	}
  
  	brelse(bh);
d1826f2a3   Fabian Frederick   fs/efs: convert p...
140
141
142
  	pr_debug("efs_iget(): inode %lu, extents %d, mode %o
  ",
  		 inode->i_ino, in->numextents, inode->i_mode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
143
144
145
146
147
148
149
150
151
152
153
  	switch (inode->i_mode & S_IFMT) {
  		case S_IFDIR: 
  			inode->i_op = &efs_dir_inode_operations; 
  			inode->i_fop = &efs_dir_operations; 
  			break;
  		case S_IFREG:
  			inode->i_fop = &generic_ro_fops;
  			inode->i_data.a_ops = &efs_aops;
  			break;
  		case S_IFLNK:
  			inode->i_op = &page_symlink_inode_operations;
21fc61c73   Al Viro   don't put symlink...
154
  			inode_nohighmem(inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
155
156
157
158
159
160
161
162
  			inode->i_data.a_ops = &efs_symlink_aops;
  			break;
  		case S_IFCHR:
  		case S_IFBLK:
  		case S_IFIFO:
  			init_special_inode(inode, inode->i_mode, device);
  			break;
  		default:
f403d1dba   Fabian Frederick   fs/efs: add pr_fm...
163
164
  			pr_warn("unsupported inode mode %o
  ", inode->i_mode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
165
166
167
  			goto read_inode_error;
  			break;
  	}
298384cd7   David Howells   iget: stop EFS fr...
168
169
  	unlock_new_inode(inode);
  	return inode;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
170
171
          
  read_inode_error:
f403d1dba   Fabian Frederick   fs/efs: add pr_fm...
172
173
  	pr_warn("failed to read inode %lu
  ", inode->i_ino);
298384cd7   David Howells   iget: stop EFS fr...
174
175
  	iget_failed(inode);
  	return ERR_PTR(-EIO);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
176
177
178
179
180
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
206
207
208
209
210
211
212
213
214
215
216
217
  }
  
  static inline efs_block_t
  efs_extent_check(efs_extent *ptr, efs_block_t block, struct efs_sb_info *sb) {
  	efs_block_t start;
  	efs_block_t length;
  	efs_block_t offset;
  
  	/*
  	 * given an extent and a logical block within a file,
  	 * can this block be found within this extent ?
  	 */
  	start  = ptr->cooked.ex_bn;
  	length = ptr->cooked.ex_length;
  	offset = ptr->cooked.ex_offset;
  
  	if ((block >= offset) && (block < offset+length)) {
  		return(sb->fs_start + start + block - offset);
  	} else {
  		return 0;
  	}
  }
  
  efs_block_t efs_map_block(struct inode *inode, efs_block_t block) {
  	struct efs_sb_info    *sb = SUPER_INFO(inode->i_sb);
  	struct efs_inode_info *in = INODE_INFO(inode);
  	struct buffer_head    *bh = NULL;
  
  	int cur, last, first = 1;
  	int ibase, ioffset, dirext, direxts, indext, indexts;
  	efs_block_t iblock, result = 0, lastblock = 0;
  	efs_extent ext, *exts;
  
  	last = in->lastextent;
  
  	if (in->numextents <= EFS_DIRECTEXTENTS) {
  		/* first check the last extent we returned */
  		if ((result = efs_extent_check(&in->extents[last], block, sb)))
  			return result;
      
  		/* if we only have one extent then nothing can be found */
  		if (in->numextents == 1) {
f403d1dba   Fabian Frederick   fs/efs: add pr_fm...
218
219
  			pr_err("%s() failed to map (1 extent)
  ", __func__);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
  			return 0;
  		}
  
  		direxts = in->numextents;
  
  		/*
  		 * check the stored extents in the inode
  		 * start with next extent and check forwards
  		 */
  		for(dirext = 1; dirext < direxts; dirext++) {
  			cur = (last + dirext) % in->numextents;
  			if ((result = efs_extent_check(&in->extents[cur], block, sb))) {
  				in->lastextent = cur;
  				return result;
  			}
  		}
f403d1dba   Fabian Frederick   fs/efs: add pr_fm...
236
237
  		pr_err("%s() failed to map block %u (dir)
  ", __func__, block);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
238
239
  		return 0;
  	}
d1826f2a3   Fabian Frederick   fs/efs: convert p...
240
241
242
  	pr_debug("%s(): indirect search for logical block %u
  ",
  		 __func__, block);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
  	direxts = in->extents[0].cooked.ex_offset;
  	indexts = in->numextents;
  
  	for(indext = 0; indext < indexts; indext++) {
  		cur = (last + indext) % indexts;
  
  		/*
  		 * work out which direct extent contains `cur'.
  		 *
  		 * also compute ibase: i.e. the number of the first
  		 * indirect extent contained within direct extent `cur'.
  		 *
  		 */
  		ibase = 0;
  		for(dirext = 0; cur < ibase && dirext < direxts; dirext++) {
  			ibase += in->extents[dirext].cooked.ex_length *
  				(EFS_BLOCKSIZE / sizeof(efs_extent));
  		}
  
  		if (dirext == direxts) {
  			/* should never happen */
f403d1dba   Fabian Frederick   fs/efs: add pr_fm...
264
265
266
  			pr_err("couldn't find direct extent for indirect extent %d (block %u)
  ",
  			       cur, block);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
  			if (bh) brelse(bh);
  			return 0;
  		}
  		
  		/* work out block number and offset of this indirect extent */
  		iblock = sb->fs_start + in->extents[dirext].cooked.ex_bn +
  			(cur - ibase) /
  			(EFS_BLOCKSIZE / sizeof(efs_extent));
  		ioffset = (cur - ibase) %
  			(EFS_BLOCKSIZE / sizeof(efs_extent));
  
  		if (first || lastblock != iblock) {
  			if (bh) brelse(bh);
  
  			bh = sb_bread(inode->i_sb, iblock);
  			if (!bh) {
f403d1dba   Fabian Frederick   fs/efs: add pr_fm...
283
284
285
  				pr_err("%s() failed at block %d
  ",
  				       __func__, iblock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
286
287
  				return 0;
  			}
d1826f2a3   Fabian Frederick   fs/efs: convert p...
288
289
290
  			pr_debug("%s(): read indirect extent block %d
  ",
  				 __func__, iblock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
291
292
293
294
295
296
297
298
299
  			first = 0;
  			lastblock = iblock;
  		}
  
  		exts = (efs_extent *) bh->b_data;
  
  		extent_copy(&(exts[ioffset]), &ext);
  
  		if (ext.cooked.ex_magic != 0) {
f403d1dba   Fabian Frederick   fs/efs: add pr_fm...
300
301
302
  			pr_err("extent %d has bad magic number in block %d
  ",
  			       cur, iblock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
303
304
305
306
307
308
309
310
311
312
313
  			if (bh) brelse(bh);
  			return 0;
  		}
  
  		if ((result = efs_extent_check(&ext, block, sb))) {
  			if (bh) brelse(bh);
  			in->lastextent = cur;
  			return result;
  		}
  	}
  	if (bh) brelse(bh);
f403d1dba   Fabian Frederick   fs/efs: add pr_fm...
314
315
  	pr_err("%s() failed to map block %u (indir)
  ", __func__, block);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
316
317
318
319
  	return 0;
  }  
  
  MODULE_LICENSE("GPL");