xref: /original-bsd/sys/ufs/lfs/lfs_inode.c (revision fac0c393)
1 /*
2  * Copyright (c) 1986, 1989, 1991, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  *
7  *	@(#)lfs_inode.c	8.7 (Berkeley) 03/21/95
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 <vm/vm.h>
21 
22 #include <ufs/ufs/quota.h>
23 #include <ufs/ufs/inode.h>
24 #include <ufs/ufs/ufsmount.h>
25 #include <ufs/ufs/ufs_extern.h>
26 
27 #include <ufs/lfs/lfs.h>
28 #include <ufs/lfs/lfs_extern.h>
29 
30 int
31 lfs_init()
32 {
33 	return (ufs_init());
34 }
35 
36 /* Search a block for a specific dinode. */
37 struct dinode *
38 lfs_ifind(fs, ino, dip)
39 	struct lfs *fs;
40 	ino_t ino;
41 	register struct dinode *dip;
42 {
43 	register int cnt;
44 	register struct dinode *ldip;
45 
46 	for (cnt = INOPB(fs), ldip = dip + (cnt - 1); cnt--; --ldip)
47 		if (ldip->di_inumber == ino)
48 			return (ldip);
49 
50 	panic("lfs_ifind: dinode %u not found", ino);
51 	/* NOTREACHED */
52 }
53 
54 int
55 lfs_update(ap)
56 	struct vop_update_args /* {
57 		struct vnode *a_vp;
58 		struct timeval *a_access;
59 		struct timeval *a_modify;
60 		int a_waitfor;
61 	} */ *ap;
62 {
63 	struct vnode *vp = ap->a_vp;
64 	struct inode *ip;
65 
66 	if (vp->v_mount->mnt_flag & MNT_RDONLY)
67 		return (0);
68 	ip = VTOI(vp);
69 	if ((ip->i_flag &
70 	    (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)) == 0)
71 		return (0);
72 	if (ip->i_flag & IN_ACCESS)
73 		ip->i_atime = ap->a_access->tv_sec;
74 	if (ip->i_flag & IN_UPDATE) {
75 		ip->i_mtime = ap->a_modify->tv_sec;
76 		(ip)->i_modrev++;
77 	}
78 	if (ip->i_flag & IN_CHANGE)
79 		ip->i_ctime = time.tv_sec;
80 	ip->i_flag &= ~(IN_ACCESS | IN_CHANGE | IN_UPDATE);
81 
82 	if (!(ip->i_flag & IN_MODIFIED))
83 		++(VFSTOUFS(vp->v_mount)->um_lfs->lfs_uinodes);
84 	ip->i_flag |= IN_MODIFIED;
85 
86 	/* If sync, push back the vnode and any dirty blocks it may have. */
87 	return (ap->a_waitfor & LFS_SYNC ? lfs_vflush(vp) : 0);
88 }
89 
90 /* Update segment usage information when removing a block. */
91 #define UPDATE_SEGUSE \
92 	if (lastseg != -1) { \
93 		LFS_SEGENTRY(sup, fs, lastseg, sup_bp); \
94 		if ((num << fs->lfs_bshift) > sup->su_nbytes) \
95 			panic("lfs_truncate: negative bytes in segment %d\n", \
96 			    lastseg); \
97 		sup->su_nbytes -= num << fs->lfs_bshift; \
98 		e1 = VOP_BWRITE(sup_bp); \
99 		blocksreleased += num; \
100 	}
101 
102 #define SEGDEC { \
103 	if (daddr != 0) { \
104 		if (lastseg != (seg = datosn(fs, daddr))) { \
105 			UPDATE_SEGUSE; \
106 			num = 1; \
107 			lastseg = seg; \
108 		} else \
109 			++num; \
110 	} \
111 }
112 
113 /*
114  * Truncate the inode ip to at most length size.  Update segment usage
115  * table information.
116  */
117 /* ARGSUSED */
118 int
119 lfs_truncate(ap)
120 	struct vop_truncate_args /* {
121 		struct vnode *a_vp;
122 		off_t a_length;
123 		int a_flags;
124 		struct ucred *a_cred;
125 		struct proc *a_p;
126 	} */ *ap;
127 {
128 	register struct indir *inp;
129 	register int i;
130 	register ufs_daddr_t *daddrp;
131 	register struct vnode *vp = ap->a_vp;
132 	off_t length = ap->a_length;
133 	struct buf *bp, *sup_bp;
134 	struct timeval tv;
135 	struct ifile *ifp;
136 	struct inode *ip;
137 	struct lfs *fs;
138 	struct indir a[NIADDR + 2], a_end[NIADDR + 2];
139 	SEGUSE *sup;
140 	ufs_daddr_t daddr, lastblock, lbn, olastblock;
141 	long off, a_released, blocksreleased, i_released;
142 	int e1, e2, depth, lastseg, num, offset, seg, size;
143 
144 	ip = VTOI(vp);
145 	tv = time;
146 	if (vp->v_type == VLNK && vp->v_mount->mnt_maxsymlinklen > 0) {
147 #ifdef DIAGNOSTIC
148 		if (length != 0)
149 			panic("lfs_truncate: partial truncate of symlink");
150 #endif
151 		bzero((char *)&ip->i_shortlink, (u_int)ip->i_size);
152 		ip->i_size = 0;
153 		ip->i_flag |= IN_CHANGE | IN_UPDATE;
154 		return (VOP_UPDATE(vp, &tv, &tv, 0));
155 	}
156 	vnode_pager_setsize(vp, (u_long)length);
157 
158 	fs = ip->i_lfs;
159 
160 	/* If length is larger than the file, just update the times. */
161 	if (ip->i_size <= length) {
162 		ip->i_flag |= IN_CHANGE | IN_UPDATE;
163 		return (VOP_UPDATE(vp, &tv, &tv, 0));
164 	}
165 
166 	/*
167 	 * Calculate index into inode's block list of last direct and indirect
168 	 * blocks (if any) which we want to keep.  Lastblock is 0 when the
169 	 * file is truncated to 0.
170 	 */
171 	lastblock = lblkno(fs, length + fs->lfs_bsize - 1);
172 	olastblock = lblkno(fs, ip->i_size + fs->lfs_bsize - 1) - 1;
173 
174 	/*
175 	 * Update the size of the file. If the file is not being truncated to
176 	 * a block boundry, the contents of the partial block following the end
177 	 * of the file must be zero'ed in case it ever become accessable again
178 	 * because of subsequent file growth.
179 	 */
180 	offset = blkoff(fs, length);
181 	if (offset == 0)
182 		ip->i_size = length;
183 	else {
184 		lbn = lblkno(fs, length);
185 #ifdef QUOTA
186 		if (e1 = getinoquota(ip))
187 			return (e1);
188 #endif
189 		if (e1 = bread(vp, lbn, fs->lfs_bsize, NOCRED, &bp))
190 			return (e1);
191 		ip->i_size = length;
192 		size = blksize(fs);
193 		(void)vnode_pager_uncache(vp);
194 		bzero((char *)bp->b_data + offset, (u_int)(size - offset));
195 		allocbuf(bp, size);
196 		if (e1 = VOP_BWRITE(bp))
197 			return (e1);
198 	}
199 	/*
200 	 * Modify sup->su_nbyte counters for each deleted block; keep track
201 	 * of number of blocks removed for ip->i_blocks.
202 	 */
203 	blocksreleased = 0;
204 	num = 0;
205 	lastseg = -1;
206 
207 	for (lbn = olastblock; lbn >= lastblock;) {
208 		/* XXX use run length from bmap array to make this faster */
209 		ufs_bmaparray(vp, lbn, &daddr, a, &depth, NULL);
210 		if (lbn == olastblock)
211 			for (i = NIADDR + 2; i--;)
212 				a_end[i] = a[i];
213 		switch (depth) {
214 		case 0:				/* Direct block. */
215 			daddr = ip->i_db[lbn];
216 			SEGDEC;
217 			ip->i_db[lbn] = 0;
218 			--lbn;
219 			break;
220 #ifdef DIAGNOSTIC
221 		case 1:				/* An indirect block. */
222 			panic("lfs_truncate: ufs_bmaparray returned depth 1");
223 			/* NOTREACHED */
224 #endif
225 		default:			/* Chain of indirect blocks. */
226 			inp = a + --depth;
227 			if (inp->in_off > 0 && lbn != lastblock) {
228 				lbn -= inp->in_off < lbn - lastblock ?
229 				    inp->in_off : lbn - lastblock;
230 				break;
231 			}
232 			for (; depth && (inp->in_off == 0 || lbn == lastblock);
233 			    --inp, --depth) {
234 				if (bread(vp,
235 				    inp->in_lbn, fs->lfs_bsize, NOCRED, &bp))
236 					panic("lfs_truncate: bread bno %d",
237 					    inp->in_lbn);
238 				daddrp = (ufs_daddr_t *)bp->b_data +
239 				    inp->in_off;
240 				for (i = inp->in_off;
241 				    i++ <= a_end[depth].in_off;) {
242 					daddr = *daddrp++;
243 					SEGDEC;
244 				}
245 				a_end[depth].in_off = NINDIR(fs) - 1;
246 				if (inp->in_off == 0)
247 					brelse (bp);
248 				else {
249 					bzero((ufs_daddr_t *)bp->b_data +
250 					    inp->in_off, fs->lfs_bsize -
251 					    inp->in_off * sizeof(ufs_daddr_t));
252 					if (e1 = VOP_BWRITE(bp))
253 						return (e1);
254 				}
255 			}
256 			if (depth == 0 && a[1].in_off == 0) {
257 				off = a[0].in_off;
258 				daddr = ip->i_ib[off];
259 				SEGDEC;
260 				ip->i_ib[off] = 0;
261 			}
262 			if (lbn == lastblock || lbn <= NDADDR)
263 				--lbn;
264 			else {
265 				lbn -= NINDIR(fs);
266 				if (lbn < lastblock)
267 					lbn = lastblock;
268 			}
269 		}
270 	}
271 	UPDATE_SEGUSE;
272 
273 	/* If truncating the file to 0, update the version number. */
274 	if (length == 0) {
275 		LFS_IENTRY(ifp, fs, ip->i_number, bp);
276 		++ifp->if_version;
277 		(void) VOP_BWRITE(bp);
278 	}
279 
280 #ifdef DIAGNOSTIC
281 	if (ip->i_blocks < fsbtodb(fs, blocksreleased)) {
282 		printf("lfs_truncate: block count < 0\n");
283 		blocksreleased = ip->i_blocks;
284 	}
285 #endif
286 	ip->i_blocks -= fsbtodb(fs, blocksreleased);
287 	fs->lfs_bfree +=  fsbtodb(fs, blocksreleased);
288 	ip->i_flag |= IN_CHANGE | IN_UPDATE;
289 	/*
290 	 * Traverse dirty block list counting number of dirty buffers
291 	 * that are being deleted out of the cache, so that the lfs_avail
292 	 * field can be updated.
293 	 */
294 	a_released = 0;
295 	i_released = 0;
296 	for (bp = vp->v_dirtyblkhd.lh_first; bp; bp = bp->b_vnbufs.le_next)
297 		if (bp->b_flags & B_LOCKED) {
298 			++a_released;
299 			/*
300 			 * XXX
301 			 * When buffers are created in the cache, their block
302 			 * number is set equal to their logical block number.
303 			 * If that is still true, we are assuming that the
304 			 * blocks are new (not yet on disk) and weren't
305 			 * counted above.  However, there is a slight chance
306 			 * that a block's disk address is equal to its logical
307 			 * block number in which case, we'll get an overcounting
308 			 * here.
309 			 */
310 			if (bp->b_blkno == bp->b_lblkno)
311 				++i_released;
312 		}
313 	blocksreleased = fsbtodb(fs, i_released);
314 #ifdef DIAGNOSTIC
315 	if (blocksreleased > ip->i_blocks) {
316 		printf("lfs_inode: Warning! %s\n",
317 		    "more blocks released from inode than are in inode");
318 		blocksreleased = ip->i_blocks;
319 	}
320 #endif
321 	fs->lfs_bfree += blocksreleased;
322 	ip->i_blocks -= blocksreleased;
323 #ifdef DIAGNOSTIC
324 	if (length == 0 && ip->i_blocks != 0)
325 		printf("lfs_inode: Warning! %s%d%s\n",
326 		    "Truncation to zero, but ", ip->i_blocks,
327 		    " blocks left on inode");
328 #endif
329 	fs->lfs_avail += fsbtodb(fs, a_released);
330 	e1 = vinvalbuf(vp, (length > 0) ? V_SAVE : 0, ap->a_cred, ap->a_p,
331 	    0, 0);
332 	e2 = VOP_UPDATE(vp, &tv, &tv, 0);
333 	return (e1 ? e1 : e2 ? e2 : 0);
334 }
335