add ufs_check_export
[unix-history] / usr / src / sys / ufs / ffs / ffs_inode.c
CommitLineData
da7c5cc6 1/*
7188ac27
KM
2 * Copyright (c) 1982, 1986, 1989 Regents of the University of California.
3 * All rights reserved.
da7c5cc6 4 *
b702c21d 5 * %sccs.include.redist.c%
7188ac27 6 *
d056f176 7 * @(#)ffs_inode.c 7.60 (Berkeley) %G%
da7c5cc6 8 */
5d5124a1 9
0a52434b
KB
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>
5d5124a1 19
8847a2f7
KM
20#include <vm/vm.h>
21
0a52434b
KB
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>
c6f5111d 26
0a52434b
KB
27#include <ufs/ffs/fs.h>
28#include <ufs/ffs/ffs_extern.h>
3ebac878 29
0a52434b 30static int ffs_indirtrunc __P((struct inode *, daddr_t, daddr_t, int, long *));
3ebac878 31
0a52434b
KB
32int
33ffs_init()
5d5124a1 34{
0a52434b 35 return (ufs_init());
5d5124a1
BJ
36}
37
a1e9dd57 38/*
832eaef9 39 * Update the access, modified, and inode change times as specified
a9013e03 40 * by the IACC, IUPD, and ICHG flags respectively. The IMOD flag
832eaef9
KM
41 * is used to specify that the inode needs to be updated but that
42 * the times have already been set. The access and modified times
43 * are taken from the second and third parameters; the inode change
44 * time is always taken from the current time. If waitfor is set,
45 * then wait for the disk write of the inode to complete.
5d5124a1 46 */
0a52434b 47int
ebfa7d99
KM
48ffs_update(ap)
49 struct vop_update_args /* {
50 struct vnode *a_vp;
51 struct timeval *a_ta;
52 struct timeval *a_tm;
53 int a_waitfor;
54 } */ *ap;
5d5124a1 55{
7188ac27 56 struct buf *bp;
a9013e03 57 struct inode *ip;
5d5124a1 58 struct dinode *dp;
ec67a3ce 59 register struct fs *fs;
5d5124a1 60
e1b76915 61 if (ap->a_vp->v_mount->mnt_flag & MNT_RDONLY)
7188ac27 62 return (0);
e1b76915 63 ip = VTOI(ap->a_vp);
a9013e03
KM
64 if ((ip->i_flag & (IUPD|IACC|ICHG|IMOD)) == 0)
65 return (0);
7188ac27 66 if (ip->i_flag&IACC)
7e11a0c9 67 ip->i_atime.ts_sec = ap->a_ta->tv_sec;
baaa0677 68 if (ip->i_flag&IUPD) {
7e11a0c9 69 ip->i_mtime.ts_sec = ap->a_tm->tv_sec;
d9011ad6 70 ip->i_modrev++;
baaa0677 71 }
7188ac27 72 if (ip->i_flag&ICHG)
7e11a0c9 73 ip->i_ctime.ts_sec = time.tv_sec;
7188ac27 74 ip->i_flag &= ~(IUPD|IACC|ICHG|IMOD);
6cb9e1e3 75 fs = ip->i_fs;
9de329b9
KM
76 /*
77 * Ensure that uid and gid are correct. This is a temporary
78 * fix until fsck has been changed to do the update.
79 */
6cb9e1e3
KM
80 if (fs->fs_inodefmt < FS_44INODEFMT) { /* XXX */
81 ip->i_din.di_ouid = ip->i_uid; /* XXX */
82 ip->i_din.di_ogid = ip->i_gid; /* XXX */
83 } /* XXX */
a9013e03
KM
84 if (error = bread(ip->i_devvp, fsbtodb(fs, itod(fs, ip->i_number)),
85 (int)fs->fs_bsize, NOCRED, &bp)) {
86 brelse(bp);
87 return (error);
88 }
7188ac27 89 dp = bp->b_un.b_dino + itoo(fs, ip->i_number);
a1e9dd57 90 *dp = ip->i_din;
e1b76915 91 if (ap->a_waitfor)
7188ac27 92 return (bwrite(bp));
a9013e03 93 else {
7188ac27
KM
94 bdwrite(bp);
95 return (0);
5d5124a1
BJ
96 }
97}
98
9c03b2c0
SL
99#define SINGLE 0 /* index of single indirect block */
100#define DOUBLE 1 /* index of double indirect block */
101#define TRIPLE 2 /* index of triple indirect block */
5d5124a1 102/*
a1e9dd57
KM
103 * Truncate the inode ip to at most length size. Free affected disk
104 * blocks -- the blocks of the file are removed in reverse order.
5d5124a1 105 */
ebfa7d99
KM
106ffs_truncate(ap)
107 struct vop_truncate_args /* {
108 struct vnode *a_vp;
109 off_t a_length;
110 int a_flags;
111 struct ucred *a_cred;
112 struct proc *a_p;
113 } */ *ap;
5d5124a1 114{
406c9a0d 115 register struct vnode *ovp = ap->a_vp;
4f083fd7 116 register daddr_t lastblock;
a9013e03 117 register struct inode *oip;
a5e62f37 118 daddr_t bn, lbn, lastiblock[NIADDR];
ebfa7d99 119 off_t length = ap->a_length;
6459ebe0 120 register struct fs *fs;
9c03b2c0 121 register struct inode *ip;
28821bc5 122 struct buf *bp;
0308fc84 123 int offset, size, level;
7188ac27 124 long count, nblocks, blocksreleased = 0;
d056f176 125 struct timeval tv;
28821bc5 126 register int i;
e038406d 127 int aflags, error, allerror;
9c03b2c0 128 struct inode tip;
0308fc84 129 off_t osize;
4f083fd7 130
406c9a0d 131 oip = VTOI(ovp);
d056f176 132 tv = time;
e11d370a
KM
133 if (ovp->v_type == VLNK && ovp->v_mount->mnt_maxsymlinklen > 0) {
134#ifdef DIAGNOSTIC
ebfa7d99 135 if (length != 0)
e11d370a
KM
136 panic("ffs_truncate: partial truncate of symlink");
137#endif
138 bzero((char *)&oip->i_shortlink, (u_int)oip->i_size);
139 oip->i_size = 0;
140 oip->i_flag |= ICHG|IUPD;
d056f176 141 return (VOP_UPDATE(ovp, &tv, &tv, 1));
e11d370a 142 }
ebfa7d99 143 if (oip->i_size <= length) {
7b2e4f05 144 oip->i_flag |= ICHG|IUPD;
d056f176 145 return (VOP_UPDATE(ovp, &tv, &tv, 1));
7b2e4f05 146 }
ebfa7d99 147 vnode_pager_setsize(ovp, (u_long)length);
c0bb1685 148 /*
9c03b2c0
SL
149 * Calculate index into inode's block list of
150 * last direct and indirect blocks (if any)
151 * which we want to keep. Lastblock is -1 when
152 * the file is truncated to 0.
c0bb1685 153 */
9c03b2c0 154 fs = oip->i_fs;
ebfa7d99 155 lastblock = lblkno(fs, length + fs->fs_bsize - 1) - 1;
9c03b2c0
SL
156 lastiblock[SINGLE] = lastblock - NDADDR;
157 lastiblock[DOUBLE] = lastiblock[SINGLE] - NINDIR(fs);
158 lastiblock[TRIPLE] = lastiblock[DOUBLE] - NINDIR(fs) * NINDIR(fs);
08d9a8ec 159 nblocks = btodb(fs->fs_bsize);
6459ebe0 160 /*
28821bc5
KM
161 * Update the size of the file. If the file is not being
162 * truncated to a block boundry, the contents of the
163 * partial block following the end of the file must be
164 * zero'ed in case it ever become accessable again because
165 * of subsequent file growth.
166 */
167 osize = oip->i_size;
ebfa7d99 168 offset = blkoff(fs, length);
28821bc5 169 if (offset == 0) {
ebfa7d99 170 oip->i_size = length;
28821bc5 171 } else {
ebfa7d99 172 lbn = lblkno(fs, length);
e038406d 173 aflags = B_CLRBUF;
e1b76915 174 if (ap->a_flags & IO_SYNC)
e038406d 175 aflags |= B_SYNC;
4b61628b
KM
176#ifdef QUOTA
177 if (error = getinoquota(oip))
178 return (error);
179#endif
e1b76915 180 if (error = ffs_balloc(oip, lbn, offset, ap->a_cred, &bp, aflags))
7188ac27 181 return (error);
ebfa7d99 182 oip->i_size = length;
28821bc5 183 size = blksize(fs, oip, lbn);
406c9a0d 184 (void) vnode_pager_uncache(ovp);
a5e62f37 185 bzero(bp->b_un.b_addr + offset, (unsigned)(size - offset));
9cf42d55 186 allocbuf(bp, size);
e1b76915 187 if (ap->a_flags & IO_SYNC)
e038406d
KM
188 bwrite(bp);
189 else
190 bdwrite(bp);
28821bc5
KM
191 }
192 /*
0a52434b
KB
193 * Update file and block pointers on disk before we start freeing
194 * blocks. If we crash before free'ing blocks below, the blocks
195 * will be returned to the free list. lastiblock values are also
196 * normalized to -1 for calls to ffs_indirtrunc below.
6459ebe0 197 */
9c03b2c0 198 tip = *oip;
28821bc5 199 tip.i_size = osize;
9c03b2c0
SL
200 for (level = TRIPLE; level >= SINGLE; level--)
201 if (lastiblock[level] < 0) {
202 oip->i_ib[level] = 0;
203 lastiblock[level] = -1;
4f083fd7 204 }
9c03b2c0
SL
205 for (i = NDADDR - 1; i > lastblock; i--)
206 oip->i_db[i] = 0;
9c03b2c0 207 oip->i_flag |= ICHG|IUPD;
ebfa7d99 208 allerror = vinvalbuf(ovp, length > 0, ap->a_cred, ap->a_p);
d056f176 209 if (error = VOP_UPDATE(ovp, &tv, &tv, MNT_WAIT))
e534e43a 210 allerror = error;
9c03b2c0 211
6459ebe0 212 /*
9c03b2c0 213 * Indirect blocks first.
6459ebe0 214 */
28821bc5 215 ip = &tip;
9c03b2c0
SL
216 for (level = TRIPLE; level >= SINGLE; level--) {
217 bn = ip->i_ib[level];
4f083fd7 218 if (bn != 0) {
0a52434b
KB
219 error = ffs_indirtrunc(ip,
220 bn, lastiblock[level], level, &count);
7188ac27
KM
221 if (error)
222 allerror = error;
223 blocksreleased += count;
9c03b2c0
SL
224 if (lastiblock[level] < 0) {
225 ip->i_ib[level] = 0;
0308fc84 226 ffs_blkfree(ip, bn, fs->fs_bsize);
9c03b2c0 227 blocksreleased += nblocks;
9c03b2c0
SL
228 }
229 }
230 if (lastiblock[level] >= 0)
231 goto done;
4f083fd7 232 }
9c03b2c0 233
6459ebe0 234 /*
9c03b2c0 235 * All whole direct blocks or frags.
6459ebe0 236 */
4f083fd7 237 for (i = NDADDR - 1; i > lastblock; i--) {
0308fc84 238 register long bsize;
4f083fd7 239
6459ebe0 240 bn = ip->i_db[i];
4f083fd7 241 if (bn == 0)
5d5124a1 242 continue;
4f083fd7 243 ip->i_db[i] = 0;
0308fc84 244 bsize = blksize(fs, ip, i);
0a52434b 245 ffs_blkfree(ip, bn, bsize);
0b355a6e 246 blocksreleased += btodb(bsize);
4f083fd7 247 }
9c03b2c0
SL
248 if (lastblock < 0)
249 goto done;
250
4f083fd7
SL
251 /*
252 * Finally, look for a change in size of the
253 * last direct block; release any frags.
254 */
9c03b2c0
SL
255 bn = ip->i_db[lastblock];
256 if (bn != 0) {
0308fc84 257 long oldspace, newspace;
9c03b2c0 258
4f083fd7
SL
259 /*
260 * Calculate amount of space we're giving
261 * back as old block size minus new block size.
262 */
9c03b2c0 263 oldspace = blksize(fs, ip, lastblock);
ebfa7d99 264 ip->i_size = length;
9c03b2c0
SL
265 newspace = blksize(fs, ip, lastblock);
266 if (newspace == 0)
267 panic("itrunc: newspace");
268 if (oldspace - newspace > 0) {
4f083fd7
SL
269 /*
270 * Block number of space to be free'd is
271 * the old block # plus the number of frags
272 * required for the storage we're keeping.
273 */
9c03b2c0 274 bn += numfrags(fs, newspace);
0a52434b 275 ffs_blkfree(ip, bn, oldspace - newspace);
08d9a8ec 276 blocksreleased += btodb(oldspace - newspace);
4f083fd7 277 }
5d5124a1 278 }
4f083fd7 279done:
9c03b2c0
SL
280/* BEGIN PARANOIA */
281 for (level = SINGLE; level <= TRIPLE; level++)
282 if (ip->i_ib[level] != oip->i_ib[level])
283 panic("itrunc1");
284 for (i = 0; i < NDADDR; i++)
285 if (ip->i_db[i] != oip->i_db[i])
286 panic("itrunc2");
287/* END PARANOIA */
08d9a8ec
SL
288 oip->i_blocks -= blocksreleased;
289 if (oip->i_blocks < 0) /* sanity */
290 oip->i_blocks = 0;
291 oip->i_flag |= ICHG;
b4567e9c 292#ifdef QUOTA
4b61628b
KM
293 if (!getinoquota(oip))
294 (void) chkdq(oip, -blocksreleased, NOCRED, 0);
89045c38 295#endif
7188ac27 296 return (allerror);
5d5124a1
BJ
297}
298
4f083fd7 299/*
0a52434b
KB
300 * Release blocks associated with the inode ip and stored in the indirect
301 * block bn. Blocks are free'd in LIFO order up to (but not including)
302 * lastbn. If level is greater than SINGLE, the block is an indirect block
303 * and recursive calls to indirtrunc must be used to cleanse other indirect
304 * blocks.
9c03b2c0
SL
305 *
306 * NB: triple indirect blocks are untested.
4f083fd7 307 */
0a52434b
KB
308static int
309ffs_indirtrunc(ip, bn, lastbn, level, countp)
6459ebe0 310 register struct inode *ip;
4f083fd7 311 daddr_t bn, lastbn;
9c03b2c0 312 int level;
7188ac27 313 long *countp;
5d5124a1 314{
4f083fd7 315 register int i;
b30358ab 316 struct buf *bp;
9c03b2c0 317 register struct fs *fs = ip->i_fs;
b30358ab
KM
318 register daddr_t *bap;
319 daddr_t *copy, nb, last;
7188ac27
KM
320 long blkcount, factor;
321 int nblocks, blocksreleased = 0;
322 int error, allerror = 0;
5d5124a1 323
9c03b2c0
SL
324 /*
325 * Calculate index in current block of last
326 * block to be kept. -1 indicates the entire
327 * block so we need not calculate the index.
328 */
329 factor = 1;
330 for (i = SINGLE; i < level; i++)
331 factor *= NINDIR(fs);
4f083fd7 332 last = lastbn;
9c03b2c0
SL
333 if (lastbn > 0)
334 last /= factor;
08d9a8ec 335 nblocks = btodb(fs->fs_bsize);
9c03b2c0
SL
336 /*
337 * Get buffer of block pointers, zero those
338 * entries corresponding to blocks to be free'd,
339 * and update on disk copy first.
340 */
ec67a3ce
MK
341#ifdef SECSIZE
342 bp = bread(ip->i_dev, fsbtodb(fs, bn), (int)fs->fs_bsize,
343 fs->fs_dbsize);
344#else SECSIZE
a937f856
KM
345 error = bread(ip->i_devvp, fsbtodb(fs, bn), (int)fs->fs_bsize,
346 NOCRED, &bp);
7188ac27 347 if (error) {
9c03b2c0 348 brelse(bp);
7188ac27
KM
349 *countp = 0;
350 return (error);
9c03b2c0
SL
351 }
352 bap = bp->b_un.b_daddr;
b30358ab
KM
353 MALLOC(copy, daddr_t *, fs->fs_bsize, M_TEMP, M_WAITOK);
354 bcopy((caddr_t)bap, (caddr_t)copy, (u_int)fs->fs_bsize);
9c03b2c0
SL
355 bzero((caddr_t)&bap[last + 1],
356 (u_int)(NINDIR(fs) - (last + 1)) * sizeof (daddr_t));
e038406d
KM
357 if (last == -1)
358 bp->b_flags |= B_INVAL;
7188ac27
KM
359 error = bwrite(bp);
360 if (error)
361 allerror = error;
b30358ab 362 bap = copy;
4f083fd7 363
9c03b2c0
SL
364 /*
365 * Recursively free totally unused blocks.
366 */
367 for (i = NINDIR(fs) - 1; i > last; i--) {
5d5124a1 368 nb = bap[i];
4f083fd7 369 if (nb == 0)
5d5124a1 370 continue;
7188ac27 371 if (level > SINGLE) {
0a52434b
KB
372 if (error = ffs_indirtrunc(ip,
373 nb, (daddr_t)-1, level - 1, &blkcount))
7188ac27
KM
374 allerror = error;
375 blocksreleased += blkcount;
376 }
0308fc84 377 ffs_blkfree(ip, nb, fs->fs_bsize);
4f083fd7 378 blocksreleased += nblocks;
4f083fd7 379 }
9c03b2c0
SL
380
381 /*
382 * Recursively free last partial block.
383 */
384 if (level > SINGLE && lastbn >= 0) {
385 last = lastbn % factor;
4f083fd7 386 nb = bap[i];
7188ac27 387 if (nb != 0) {
0a52434b
KB
388 if (error =
389 ffs_indirtrunc(ip, nb, last, level - 1, &blkcount))
7188ac27
KM
390 allerror = error;
391 blocksreleased += blkcount;
392 }
5d5124a1 393 }
b30358ab 394 FREE(copy, M_TEMP);
7188ac27
KM
395 *countp = blocksreleased;
396 return (allerror);
5d5124a1 397}