xref: /csrg-svn/sys/ufs/lfs/lfs_inode.c (revision 52222)
1 /*
2  * Copyright (c) 1986, 1989, 1991 Regents of the University of California.
3  * All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  *
7  *	@(#)lfs_inode.c	7.53 (Berkeley) 01/18/92
8  */
9 
10 #include <sys/param.h>
11 #include <sys/systm.h>
12 #include <sys/mount.h>
13 #include <sys/proc.h>
14 #include <sys/file.h>
15 #include <sys/buf.h>
16 #include <sys/vnode.h>
17 #include <sys/kernel.h>
18 #include <sys/malloc.h>
19 
20 #include <ufs/ufs/quota.h>
21 #include <ufs/ufs/inode.h>
22 #include <ufs/ufs/ufsmount.h>
23 #include <ufs/ufs/ufs_extern.h>
24 
25 #include <ufs/lfs/lfs.h>
26 #include <ufs/lfs/lfs_extern.h>
27 
28 int
29 lfs_init()
30 {
31 #ifdef VERBOSE
32 	printf("lfs_init\n");
33 #endif
34 	return (ufs_init());
35 }
36 
37 static daddr_t
38 lfs_itod(fs, ino)
39 	struct lfs *fs;
40 	ino_t ino;
41 {
42 	BUF *bp;
43 	IFILE *ifp;
44 	daddr_t iaddr;
45 
46 	/* Translate an inode number to a disk address. */
47 	if (ino == LFS_IFILE_INUM)
48 		return (fs->lfs_idaddr);
49 
50 	LFS_IENTRY(ifp, fs, ino, bp);
51 	iaddr = ifp->if_daddr;
52 	brelse(bp);
53 	return (iaddr);
54 }
55 
56 /*
57  * Look up an LFS dinode number to find its incore vnode.  If not already
58  * in core, read it in from the specified device.  Return the inode locked.
59  * Detection and handling of mount points must be done by the calling routine.
60  */
61 int
62 lfs_vget(mntp, ino, vpp)
63 	struct mount *mntp;
64 	ino_t ino;
65 	struct vnode **vpp;
66 {
67 	register struct lfs *fs;
68 	register struct inode *ip;
69 	struct buf *bp;
70 	struct vnode *vp;
71 	struct ufsmount *ump;
72 	dev_t dev;
73 	int error;
74 
75 #ifdef VERBOSE
76 	printf("lfs_vget\n");
77 #endif
78 	ump = VFSTOUFS(mntp);
79 	dev = ump->um_dev;
80 	if ((*vpp = ufs_ihashget(dev, ino)) != NULL)
81 		return (0);
82 
83 	/* Allocate new vnode/inode. */
84 	if (error = lfs_vcreate(mntp, ino, &vp)) {
85 		*vpp = NULL;
86 		return (error);
87 	}
88 	/*
89 	 * Put it onto its hash chain and lock it so that other requests for
90 	 * this inode will block if they arrive while we are sleeping waiting
91 	 * for old data structures to be purged or for the contents of the
92 	 * disk portion of this inode to be read.
93 	 */
94 	ip = VTOI(vp);
95 	ufs_ihashins(ip);
96 
97 	/* Read in the disk contents for the inode, copy into the inode. */
98 	ip->i_lfs = fs = ump->um_lfs;
99 	if (error = bread(ump->um_devvp, lfs_itod(fs, ino),
100 	    (int)fs->lfs_bsize, NOCRED, &bp)) {
101 		/*
102 		 * The inode does not contain anything useful, so it
103 		 * would be misleading to leave it on its hash chain.
104 		 * Iput() will return it to the free list.
105 		 */
106 		remque(ip);
107 		ip->i_forw = ip;
108 		ip->i_back = ip;
109 
110 		/* Unlock and discard unneeded inode. */
111 		ufs_iput(ip);
112 		brelse(bp);
113 		*vpp = NULL;
114 		return (error);
115 	}
116 	ip->i_din = *lfs_ifind(fs, ino, bp->b_un.b_dino);
117 	brelse(bp);
118 
119 	/*
120 	 * Initialize the vnode from the inode, check for aliases.  In all
121 	 * cases re-init ip, the underlying vnode/inode may have changed.
122 	 */
123 	if (error = ufs_vinit(mntp, &lfs_specops, LFS_FIFOOPS, &vp)) {
124 		ufs_iput(ip);
125 		*vpp = NULL;
126 		return (error);
127 	}
128 	/*
129 	 * Finish inode initialization now that aliasing has been resolved.
130 	 */
131 	ip->i_devvp = ump->um_devvp;
132 	VREF(ip->i_devvp);
133 	*vpp = vp;
134 	return (0);
135 }
136 
137 int
138 lfs_update(vp, ta, tm, waitfor)
139 	register struct vnode *vp;
140 	struct timeval *ta, *tm;
141         int waitfor;
142 {
143 	struct inode *ip;
144 
145 #ifdef VERBOSE
146 	printf("lfs_update\n");
147 #endif
148 	if (vp->v_mount->mnt_flag & MNT_RDONLY)
149 		return (0);
150 	ip = VTOI(vp);
151 	if ((ip->i_flag & (IUPD|IACC|ICHG|IMOD)) == 0)
152 		return (0);
153 	if (ip->i_flag&IACC)
154 		ip->i_atime = ta->tv_sec;
155 	if (ip->i_flag&IUPD) {
156 		ip->i_mtime = tm->tv_sec;
157 		INCRQUAD((ip)->i_modrev);
158 	}
159 	if (ip->i_flag&ICHG)
160 		ip->i_ctime = time.tv_sec;
161 	ip->i_flag &= ~(IUPD|IACC|ICHG|IMOD);
162 
163 	/*
164 	 * XXX
165 	 * I'm not real sure what to do here; once we have fsync and partial
166 	 * segments working in the LFS context, this must be fixed to be
167 	 * correct.  The contents of the inode have to be pushed back to
168 	 * stable storage.
169 	 */
170 	return (0);
171 }
172 
173 /* Update segment usage information when removing a block. */
174 #define UPDATE_SEGUSE { \
175 	LFS_SEGENTRY(sup, fs, lastseg, bp); \
176 	sup->su_nbytes -= fs->lfs_bsize * num; \
177 	LFS_UBWRITE(bp); \
178 	blocksreleased += num; \
179 }
180 
181 #define SEGDEC { \
182 	if (daddr != UNASSIGNED) { \
183 		if (lastseg != (seg = datosn(fs, daddr))) { \
184 			UPDATE_SEGUSE; \
185 			num = 1; \
186 			lastseg = seg; \
187 		} else \
188 			++num; \
189 	} \
190 }
191 
192 /*
193  * Truncate the inode ip to at most length size.  Update segment usage
194  * table information.
195  */
196 /* ARGSUSED */
197 int
198 lfs_truncate(vp, length, flags)
199 	struct vnode *vp;
200 	u_long length;
201 	int flags;
202 {
203 	register INDIR *ap;
204 	register int i;
205 	register daddr_t *daddrp;
206 	struct buf *bp;
207 	struct inode *ip;
208 	struct lfs *fs;
209 	INDIR a[NIADDR + 2], a_end[NIADDR + 2];
210 	SEGUSE *sup;
211 	daddr_t daddr, lastblock, lbn, olastblock;
212 	off_t off;
213 	long blocksreleased;
214 	int error, depth, lastseg, num, offset, seg, size;
215 
216 #ifdef VERBOSE
217 	printf("lfs_truncate\n");
218 #endif
219 	vnode_pager_setsize(vp, length);
220 	ip = VTOI(vp);
221 	/* If length is larger than the file, just update the times. */
222 	if (ip->i_size <= length) {
223 		ip->i_flag |= ICHG|IUPD;
224 		return (lfs_update(vp, &time, &time, 1));
225 	}
226 	/*
227 	 * Calculate index into inode's block list of last direct and indirect
228 	 * blocks (if any) which we want to keep.  Lastblock is 0 when the
229 	 * file is truncated to 0.
230 	 */
231 	fs = ip->i_lfs;
232 	lastblock = lblkno(fs, length + fs->lfs_bsize - 1);
233 	olastblock = lblkno(fs, ip->i_size + fs->lfs_bsize - 1) - 1;
234 
235 	/*
236 	 * Update the size of the file. If the file is not being truncated to
237 	 * a block boundry, the contents of the partial block following the end
238 	 * of the file must be zero'ed in case it ever become accessable again
239 	 * because of subsequent file growth.
240 	 */
241 	offset = blkoff(fs, length);
242 	if (offset == 0)
243 		ip->i_size = length;
244 	else {
245 		lbn = lblkno(fs, length);
246 #ifdef QUOTA
247 		if (error = getinoquota(ip))
248 			return (error);
249 #endif
250 		if (error = bread(vp, lbn, fs->lfs_bsize, NOCRED, &bp))
251 			return (error);
252 		ip->i_size = length;
253 		size = blksize(fs);
254 		(void)vnode_pager_uncache(vp);
255 		bzero(bp->b_un.b_addr + offset, (unsigned)(size - offset));
256 		allocbuf(bp, size);
257 		LFS_UBWRITE(bp);
258 	}
259 	/*
260 	 * Modify sup->su_nbyte counters for each deleted block; keep track
261 	 * of number of blocks removed for ip->i_blocks.
262 	 */
263 	blocksreleased = 0;
264 	num = 0;
265 	lastseg = -1;
266 
267 	for (lbn = olastblock; lbn >= lastblock;) {
268 		lfs_bmaparray(vp, lbn, &daddr, a, &depth);
269 		if (lbn == olastblock)
270 			for (i = NIADDR + 2; i--;)
271 				a_end[i] = a[i];
272 		switch (depth) {
273 		case 0:				/* Direct block. */
274 			daddr = ip->i_db[lbn];
275 			SEGDEC;
276 			ip->i_db[lbn] = 0;
277 			--lbn;
278 			break;
279 #ifdef DIAGNOSTIC
280 		case 1:				/* An indirect block. */
281 			panic("lfs_truncate: lfs_bmaparray returned depth 1");
282 			/* NOTREACHED */
283 #endif
284 		default:			/* Chain of indirect blocks. */
285 			ap = a + --depth;
286 			if (ap->in_off > 0 && lbn != lastblock) {
287 				lbn -= ap->in_off < lbn - lastblock ?
288 				    ap->in_off : lbn - lastblock;
289 				break;
290 			}
291 			for (; depth && (ap->in_off == 0 || lbn == lastblock);
292 			    --ap, --depth) {
293 				/*
294 				 * XXX
295 				 * The indirect block may not yet exist, so
296 				 * bread will create one just so we can free
297 				 * it.
298 				 */
299 				if (bread(vp,
300 				    ap->in_lbn, fs->lfs_bsize, NOCRED, &bp))
301 					panic("lfs_truncate: bread bno %d",
302 					    ap->in_lbn);
303 				daddrp = bp->b_un.b_daddr + ap->in_off;
304 				for (i = ap->in_off;
305 				    i++ <= a_end[depth].in_off;) {
306 					daddr = *daddrp++;
307 					SEGDEC;
308 				}
309 				a_end[depth].in_off=NINDIR(fs)-1;
310 				if (ap->in_off > 0 && lbn == lastblock) {
311 					bzero(bp->b_un.b_daddr + ap->in_off,
312 					    fs->lfs_bsize -
313 					    ap->in_off * sizeof(daddr_t));
314 					LFS_UBWRITE(bp);
315 				} else
316 					brelse (bp);
317 			}
318 			if (a[1].in_off == 0) {
319 				off = a[0].in_off;
320 				daddr = ip->i_ib[off];
321 				SEGDEC;
322 				ip->i_ib[off] = 0;
323 			}
324 			if (lbn == lastblock)
325 				--lbn;
326 			else {
327 				lbn -= NINDIR(fs);
328 				if (lbn < lastblock)
329 					lbn = lastblock;
330 			}
331 		}
332 	}
333 	if (lastseg != -1)
334 		UPDATE_SEGUSE;
335 
336 	ip->i_blocks -= blocksreleased;
337 	/*
338 	 * XXX
339 	 * Currently, we don't know when we allocate an indirect block, so
340 	 * ip->i_blocks isn't getting incremented appropriately.  As a result,
341 	 * when we delete any indirect blocks, we get a bad number here.
342 	 */
343 	if (ip->i_blocks < 0)
344 		ip->i_blocks = 0;
345 	ip->i_flag |= ICHG|IUPD;
346 	(void)vinvalbuf(vp, length > 0);
347 	error = lfs_update(vp, &time, &time, MNT_WAIT);
348 	return (0);
349 }
350