summaryrefslogtreecommitdiffstats
path: root/fs/ufs/ufs_inode.c
blob: 75329c5843f6907a5c5356734343cff875c6f11b (plain)
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
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
59
60
61
62
63
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
89
90
91
92
93
94
95
96
97
98
99
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
131
132
133
134
135
136
137
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
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
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
261
262
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
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
/*
 *  linux/fs/ufs/ufs_inode.c
 *
 * Copyright (C) 1996
 * Adrian Rodriguez (adrian@franklins-tower.rutgers.edu)
 * Laboratory for Computer Science Research Computing Facility
 * Rutgers, The State University of New Jersey
 *
 * Clean swab support on 19970406
 * by Francois-Rene Rideau <rideau@ens.fr>
 *
 */

#include <linux/fs.h>
#include <linux/ufs_fs.h>
#include <linux/sched.h>

#include "ufs_swab.h"

void ufs_print_inode(struct inode * inode)
{
	printk("ino %lu  mode 0%6.6o  lk %d  uid %d  gid %d"
	       "  sz %lu  blks %lu  cnt %u\n",
	       inode->i_ino, inode->i_mode, inode->i_nlink, inode->i_uid,
	       inode->i_gid, inode->i_size, inode->i_blocks, inode->i_count);
	printk("  db <0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x"
	       " 0x%x 0x%x 0x%x 0x%x>\n",
		inode->u.ufs_i.i_data[0], inode->u.ufs_i.i_data[1],
		inode->u.ufs_i.i_data[2], inode->u.ufs_i.i_data[3],
		inode->u.ufs_i.i_data[4], inode->u.ufs_i.i_data[5],
		inode->u.ufs_i.i_data[6], inode->u.ufs_i.i_data[7],
		inode->u.ufs_i.i_data[8], inode->u.ufs_i.i_data[9],
		inode->u.ufs_i.i_data[10], inode->u.ufs_i.i_data[11]);
	printk("  gen 0x%8.8x ib <0x%x 0x%x 0x%x>\n",
		inode->u.ufs_i.i_gen,
		inode->u.ufs_i.i_data[UFS_IND_BLOCK],
		inode->u.ufs_i.i_data[UFS_DIND_BLOCK],
		inode->u.ufs_i.i_data[UFS_TIND_BLOCK]);
}

#define inode_bmap(inode, nr) ((inode)->u.ufs_i.i_data[(nr)])

static inline int block_bmap (struct inode *inode, int block, int nr)
{
	struct buffer_head *bh;
	int tmp;
	__u32 bytesex = inode->i_sb->u.ufs_sb.s_flags & UFS_BYTESEX;
	/* XXX Split in fsize big blocks (Can't bread 8Kb). */ 
	tmp = nr >> (inode->i_sb->u.ufs_sb.s_fshift - 2);
	bh = bread (inode->i_dev, block + tmp, inode->i_sb->u.ufs_sb.s_fsize);
	if (!bh)
		return 0;
	nr &= ~(inode->i_sb->u.ufs_sb.s_fmask) >> 2;
	tmp = SWAB32(((__u32 *)bh->b_data)[nr]);
	brelse (bh);
	return tmp;
}

int ufs_bmap (struct inode * inode, int block)
{
	int i;
	int addr_per_block = UFS_ADDR_PER_BLOCK(inode->i_sb);
	int addr_per_block_bits = UFS_ADDR_PER_BLOCK_BITS(inode->i_sb);
	int lbn = ufs_lbn (inode->i_sb, block);
	int boff = ufs_boff (inode->i_sb, block);

	if (lbn < 0) {
		ufs_warning (inode->i_sb, "ufs_bmap", "block < 0");
		return 0;
	}
	if (lbn >= UFS_NDADDR + addr_per_block +
		(1 << (addr_per_block_bits * 2)) +
		((1 << (addr_per_block_bits * 2)) << addr_per_block_bits)) {
		ufs_warning (inode->i_sb, "ufs_bmap", "block > big");
		return 0;
	}
	if (lbn < UFS_NDADDR)
		return ufs_dbn (inode->i_sb, inode_bmap (inode, lbn), boff);
	lbn -= UFS_NDADDR;
	if (lbn < addr_per_block) {
		i = inode_bmap (inode, UFS_IND_BLOCK);
		if (!i)
			return 0;
		return ufs_dbn (inode->i_sb, block_bmap (inode, i, lbn), boff);
	}
	lbn -= addr_per_block;
	if (lbn < (1 << (addr_per_block_bits * 2))) {
		i = inode_bmap (inode, UFS_DIND_BLOCK);
		if (!i)
			return 0;
		i = block_bmap (inode, i, lbn >> addr_per_block_bits);
		if (!i)
			return 0;
		return ufs_dbn (inode->i_sb,
				block_bmap (inode, i, lbn & (addr_per_block-1)),
				boff);
	}
	lbn -= (1 << (addr_per_block_bits * 2));
	i = inode_bmap (inode, UFS_TIND_BLOCK);
	if (!i)
		return 0;
	i = block_bmap (inode, i, lbn >> (addr_per_block_bits * 2));
	if (!i)
		return 0;
	i = block_bmap (inode, i,
			(lbn >> addr_per_block_bits) & (addr_per_block - 1));
	if (!i)
		return 0;
	return ufs_dbn (inode->i_sb,
			block_bmap (inode, i, lbn & (addr_per_block-1)), boff);
}

/* XXX - ufs_read_inode is a mess */
void ufs_read_inode(struct inode * inode)
{
	struct super_block * sb;
	struct ufs_inode * ufsip;
	struct buffer_head * bh;
	__u32 bytesex = inode->i_sb->u.ufs_sb.s_flags & UFS_BYTESEX;

	sb = inode->i_sb;

	if (ufs_ino_ok(inode)) {
	        printk("ufs_read_inode: bad inum %lu", inode->i_ino);

	        return;
	}

#if 0
	printk("ufs_read_inode: ino %lu  cg %u  cgino %u  ipg %u  inopb %u\n",
	       inode->i_ino, ufs_ino2cg(inode),
	       (inode->i_ino%sb->u.ufs_sb.s_inopb),
	       sb->u.ufs_sb.s_ipg, sb->u.ufs_sb.s_inopb);
#endif
	bh = bread(inode->i_dev,
	           ufs_cgimin(inode->i_sb, ufs_ino2cg(inode)) +
	           (inode->i_ino%sb->u.ufs_sb.s_ipg)/(sb->u.ufs_sb.s_inopb/sb->u.ufs_sb.s_fsfrag),
	           BLOCK_SIZE);
	if (!bh) {
	        printk("ufs_read_inode: can't read inode %lu from dev %d/%d",
	               inode->i_ino, MAJOR(inode->i_dev), MINOR(inode->i_dev));
	        return;
	}

	ufsip = (struct ufs_inode *)bh->b_data;
	ufsip += (inode->i_ino%(sb->u.ufs_sb.s_inopb/sb->u.ufs_sb.s_fsfrag));

	/*
	 * Copy data to the in-core inode.
	 */
	inode->i_mode = SWAB16(ufsip->ui_mode);
	inode->i_nlink = SWAB16(ufsip->ui_nlink);
	if (inode->i_nlink == 0) {
	        /* XXX */
	        printk("ufs_read_inode: zero nlink ino %lu  dev %u/%u\n",
	               inode->i_ino, MAJOR(inode->i_dev), MINOR(inode->i_dev));
	        inode->i_nlink = 1;
	        printk("ufs_read_inode: fishy ino %lu pblk %lu dev %u/%u\n",
	               inode->i_ino,
	               ufs_cgimin(inode->i_sb, ufs_ino2cg(inode)) +
	               (inode->i_ino%sb->u.ufs_sb.s_ipg)/sb->u.ufs_sb.s_inopb,
	               MAJOR(inode->i_dev), MINOR(inode->i_dev));
	}
	/* XXX - debugging */
	if (SWAB32(ufsip->ui_gen) == 0) {
	        printk("ufs_read_inode: zero gen ino %lu pblk %lu dev %u/%u\n",
	               inode->i_ino,
	               ufs_cgimin(inode->i_sb, ufs_ino2cg(inode)) +
	               (inode->i_ino%sb->u.ufs_sb.s_ipg)/sb->u.ufs_sb.s_inopb,
	               MAJOR(inode->i_dev), MINOR(inode->i_dev));
	}
	/*
	 * Since Linux currently only has 16-bit uid_t and gid_t, we can't
	 * really support EFTs.  For the moment, we use 0 as the uid and gid
	 * if an inode has a uid or gid that won't fit in 16 bits.  This way
	 * random users can't get at these files, since they get dynamically
	 * "chown()ed" to root.
	 */
	if (SWAB16(ufsip->ui_suid) == UFS_USEEFT) {
	        /* EFT */
	        inode->i_uid = 0;
	        printk("ufs_read_inode: EFT uid %u ino %lu dev %u/%u, using %u\n",
	               SWAB32(ufsip->ui_uid), inode->i_ino, MAJOR(inode->i_dev), MINOR(inode->i_dev),
	               inode->i_uid);
	} else {
	        inode->i_uid = SWAB16(ufsip->ui_suid);
	}
	if (SWAB16(ufsip->ui_sgid) == UFS_USEEFT) {
	        /* EFT */
	        inode->i_gid = 0;
	        printk("ufs_read_inode: EFT gid %u ino %lu dev %u/%u, using %u\n",
	               SWAB32(ufsip->ui_gid), inode->i_ino, MAJOR(inode->i_dev), MINOR(inode->i_dev),
	               inode->i_gid);
	} else {
	        inode->i_gid = SWAB16(ufsip->ui_sgid);
	}

	/*
	 * Linux i_size is 32 bits on most architectures,
	 * so some files on a UFS filesystem may not
	 * be readable.  I let people access the first 32 bits worth of them.
	 * for the rw code, we may want to mark these inodes as read-only.
	 * XXX - bug Linus to make i_size a __u64 instead of a __u32.
	 */
	inode->u.ufs_i.i_size = SWAB64(ufsip->ui_size);
	/* KRR - Just type cast inode->u.ufs_i.i_size into off_t and
	 * worry about overflow later
         */
	inode->i_size = (off_t)inode->u.ufs_i.i_size;

	/*
	 * Linux doesn't keep tv_usec around in the kernel, so we discard it.
	 * XXX - I'm not sure what I should do about writing things.  I may
	 * want to keep this data, but for the moment I think I'll just write
	 * zeros for these fields when writing out inodes.
	 */
	inode->i_atime = SWAB32(ufsip->ui_atime.tv_sec);
	inode->i_mtime = SWAB32(ufsip->ui_mtime.tv_sec);
	inode->i_ctime = SWAB32(ufsip->ui_ctime.tv_sec);
	inode->i_blksize = sb->u.ufs_sb.s_fsize;
	inode->i_blocks = SWAB32(ufsip->ui_blocks);
	inode->i_version = ++event; /* see linux/kernel/sched.c */

	if (S_ISREG(inode->i_mode)) {
	        inode->i_op = &ufs_file_inode_operations;
	} else if (S_ISDIR(inode->i_mode)) {
	        inode->i_op = &ufs_dir_inode_operations;
	} else if (S_ISLNK(inode->i_mode)) {
	        inode->i_op = &ufs_symlink_inode_operations;
	} else if (S_ISCHR(inode->i_mode)) {
	        inode->i_op = &chrdev_inode_operations;
	} else if (S_ISBLK(inode->i_mode)) {
	        inode->i_op = &blkdev_inode_operations;
	} else if (S_ISFIFO(inode->i_mode)) {
	        init_fifo(inode);
	} else {
	        printk("ufs_read_inode: unknown file type 0%o ino %lu dev %d/%d\n",
	               inode->i_mode, inode->i_ino, MAJOR(inode->i_dev),
	               MINOR(inode->i_dev));
	        /* XXX - debugging */
	        ufs_print_inode(inode);
	        inode->i_op = &ufs_file_inode_operations;
	}

	/*
	 * ufs_read_super makes sure that UFS_NDADDR and UFS_NINDIR are sane.
	 */
	if (S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
	    S_ISLNK(inode->i_mode)) {
	        int i;

	        for (i = 0; i < UFS_NDADDR; i++) {
	                inode->u.ufs_i.i_data[i] = SWAB32(ufsip->ui_db[i]);
	        }
	        for (i = 0; i < UFS_NINDIR; i++) {
	                inode->u.ufs_i.i_data[UFS_IND_BLOCK + i] =
							SWAB32(ufsip->ui_ib[i]);
	        }
	}

	/* KRR - I need to check the SunOS header files, but for the time
	 * being, I'm going to tread ui_db[0] and [1] as a __u64 and swab
	 * them appropriately.  This should clean up any real endian problems,
	 * but we'll still need to add size checks in the write portion of
	 * the code.
	 */
	if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) {
	  inode->i_rdev = (kdev_t)SWAB64(*(__u64*)&ufsip->ui_db);
	}

	/* XXX - implement fast and slow symlinks */

	inode->u.ufs_i.i_flags = SWAB32(ufsip->ui_flags);
	inode->u.ufs_i.i_gen = SWAB32(ufsip->ui_gen); /* XXX - is this i_version? */
	inode->u.ufs_i.i_shadow = SWAB32(ufsip->ui_shadow); /* XXX */
	inode->u.ufs_i.i_uid = SWAB32(ufsip->ui_uid);
	inode->u.ufs_i.i_gid = SWAB32(ufsip->ui_gid);
	inode->u.ufs_i.i_oeftflag = SWAB32(ufsip->ui_oeftflag);

	brelse(bh);

	if (inode->i_sb->u.ufs_sb.s_flags & (UFS_DEBUG|UFS_DEBUG_INODE)) {
	        ufs_print_inode(inode);
	}

	return;
}

void ufs_put_inode (struct inode * inode)
{
	if (inode->i_sb->u.ufs_sb.s_flags & (UFS_DEBUG|UFS_DEBUG_INODE)) {
		printk("ufs_put_inode:\n");
	        ufs_print_inode(inode);
	}

	if (inode->i_nlink)
	        return;

	printk("ufs_put_inode: nlink == 0 for inum %lu on dev %d/%d\n",
	       inode->i_ino, MAJOR(inode->i_dev), MINOR(inode->i_dev));
	ufs_print_inode(inode);
	panic("ufs_put_inode: fs is read only, and nlink == 0");

	/* XXX - this code goes here eventually
	inode->i_size = 0;
	if (inode->i_blocks)
	        ufs_truncate(inode);
	ufs_free_inode(inode);
	*/

	return;
}