386BSD 0.1 development
[unix-history] / usr / src / sbin / fsck / inode.c
CommitLineData
702f0073
WJ
1/*
2 * Copyright (c) 1980, 1986 The Regents of the University of California.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#ifndef lint
35static char sccsid[] = "@(#)inode.c 5.18 (Berkeley) 3/19/91";
36#endif /* not lint */
37
38#include <sys/param.h>
39#include <ufs/dinode.h>
40#include <ufs/fs.h>
41#include <ufs/dir.h>
42#include <pwd.h>
43#include <stdlib.h>
44#include <string.h>
45#include "fsck.h"
46
47static ino_t startinum;
48
49ckinode(dp, idesc)
50 struct dinode *dp;
51 register struct inodesc *idesc;
52{
53 register daddr_t *ap;
54 long ret, n, ndb, offset;
55 struct dinode dino;
56
57 if (idesc->id_fix != IGNORE)
58 idesc->id_fix = DONTKNOW;
59 idesc->id_entryno = 0;
60 idesc->id_filesize = dp->di_size;
61 if ((dp->di_mode & IFMT) == IFBLK || (dp->di_mode & IFMT) == IFCHR)
62 return (KEEPON);
63 dino = *dp;
64 ndb = howmany(dino.di_size, sblock.fs_bsize);
65 for (ap = &dino.di_db[0]; ap < &dino.di_db[NDADDR]; ap++) {
66 if (--ndb == 0 && (offset = blkoff(&sblock, dino.di_size)) != 0)
67 idesc->id_numfrags =
68 numfrags(&sblock, fragroundup(&sblock, offset));
69 else
70 idesc->id_numfrags = sblock.fs_frag;
71 if (*ap == 0)
72 continue;
73 idesc->id_blkno = *ap;
74 if (idesc->id_type == ADDR)
75 ret = (*idesc->id_func)(idesc);
76 else
77 ret = dirscan(idesc);
78 if (ret & STOP)
79 return (ret);
80 }
81 idesc->id_numfrags = sblock.fs_frag;
82 for (ap = &dino.di_ib[0], n = 1; n <= NIADDR; ap++, n++) {
83 if (*ap) {
84 idesc->id_blkno = *ap;
85 ret = iblock(idesc, n,
86 dino.di_size - sblock.fs_bsize * NDADDR);
87 if (ret & STOP)
88 return (ret);
89 }
90 }
91 return (KEEPON);
92}
93
94iblock(idesc, ilevel, isize)
95 struct inodesc *idesc;
96 register long ilevel;
97 u_long isize;
98{
99 register daddr_t *ap;
100 register daddr_t *aplim;
101 int i, n, (*func)(), nif, sizepb;
102 register struct bufarea *bp;
103 char buf[BUFSIZ];
104 extern int dirscan(), pass1check();
105
106 if (idesc->id_type == ADDR) {
107 func = idesc->id_func;
108 if (((n = (*func)(idesc)) & KEEPON) == 0)
109 return (n);
110 } else
111 func = dirscan;
112 if (chkrange(idesc->id_blkno, idesc->id_numfrags))
113 return (SKIP);
114 bp = getdatablk(idesc->id_blkno, sblock.fs_bsize);
115 ilevel--;
116 for (sizepb = sblock.fs_bsize, i = 0; i < ilevel; i++)
117 sizepb *= NINDIR(&sblock);
118 nif = isize / sizepb + 1;
119 if (nif > NINDIR(&sblock))
120 nif = NINDIR(&sblock);
121 if (idesc->id_func == pass1check && nif < NINDIR(&sblock)) {
122 aplim = &bp->b_un.b_indir[NINDIR(&sblock)];
123 for (ap = &bp->b_un.b_indir[nif]; ap < aplim; ap++) {
124 if (*ap == 0)
125 continue;
126 (void)sprintf(buf, "PARTIALLY TRUNCATED INODE I=%lu",
127 idesc->id_number);
128 if (dofix(idesc, buf)) {
129 *ap = 0;
130 dirty(bp);
131 }
132 }
133 flush(fswritefd, bp);
134 }
135 aplim = &bp->b_un.b_indir[nif];
136 for (ap = bp->b_un.b_indir, i = 1; ap < aplim; ap++, i++) {
137 if (*ap) {
138 idesc->id_blkno = *ap;
139 if (ilevel > 0)
140 n = iblock(idesc, ilevel, isize - i * sizepb);
141 else
142 n = (*func)(idesc);
143 if (n & STOP) {
144 bp->b_flags &= ~B_INUSE;
145 return (n);
146 }
147 }
148 }
149 bp->b_flags &= ~B_INUSE;
150 return (KEEPON);
151}
152
153/*
154 * Check that a block in a legal block number.
155 * Return 0 if in range, 1 if out of range.
156 */
157chkrange(blk, cnt)
158 daddr_t blk;
159 int cnt;
160{
161 register int c;
162
163 if ((unsigned)(blk + cnt) > maxfsblock)
164 return (1);
165 c = dtog(&sblock, blk);
166 if (blk < cgdmin(&sblock, c)) {
167 if ((blk + cnt) > cgsblock(&sblock, c)) {
168 if (debug) {
169 printf("blk %ld < cgdmin %ld;",
170 blk, cgdmin(&sblock, c));
171 printf(" blk + cnt %ld > cgsbase %ld\n",
172 blk + cnt, cgsblock(&sblock, c));
173 }
174 return (1);
175 }
176 } else {
177 if ((blk + cnt) > cgbase(&sblock, c+1)) {
178 if (debug) {
179 printf("blk %ld >= cgdmin %ld;",
180 blk, cgdmin(&sblock, c));
181 printf(" blk + cnt %ld > sblock.fs_fpg %ld\n",
182 blk+cnt, sblock.fs_fpg);
183 }
184 return (1);
185 }
186 }
187 return (0);
188}
189
190/*
191 * General purpose interface for reading inodes.
192 */
193struct dinode *
194ginode(inumber)
195 ino_t inumber;
196{
197 daddr_t iblk;
198
199 if (inumber < ROOTINO || inumber > maxino)
200 errexit("bad inode number %d to ginode\n", inumber);
201 if (startinum == 0 ||
202 inumber < startinum || inumber >= startinum + INOPB(&sblock)) {
203 iblk = itod(&sblock, inumber);
204 if (pbp != 0)
205 pbp->b_flags &= ~B_INUSE;
206 pbp = getdatablk(iblk, sblock.fs_bsize);
207 startinum = (inumber / INOPB(&sblock)) * INOPB(&sblock);
208 }
209 return (&pbp->b_un.b_dinode[inumber % INOPB(&sblock)]);
210}
211
212/*
213 * Special purpose version of ginode used to optimize first pass
214 * over all the inodes in numerical order.
215 */
216ino_t nextino, lastinum;
217long readcnt, readpercg, fullcnt, inobufsize, partialcnt, partialsize;
218struct dinode *inodebuf;
219
220struct dinode *
221getnextinode(inumber)
222 ino_t inumber;
223{
224 long size;
225 daddr_t dblk;
226 static struct dinode *dp;
227
228 if (inumber != nextino++ || inumber > maxino)
229 errexit("bad inode number %d to nextinode\n", inumber);
230 if (inumber >= lastinum) {
231 readcnt++;
232 dblk = fsbtodb(&sblock, itod(&sblock, lastinum));
233 if (readcnt % readpercg == 0) {
234 size = partialsize;
235 lastinum += partialcnt;
236 } else {
237 size = inobufsize;
238 lastinum += fullcnt;
239 }
240 (void)bread(fsreadfd, (char *)inodebuf, dblk, size); /* ??? */
241 dp = inodebuf;
242 }
243 return (dp++);
244}
245
246resetinodebuf()
247{
248
249 startinum = 0;
250 nextino = 0;
251 lastinum = 0;
252 readcnt = 0;
253 inobufsize = blkroundup(&sblock, INOBUFSIZE);
254 fullcnt = inobufsize / sizeof(struct dinode);
255 readpercg = sblock.fs_ipg / fullcnt;
256 partialcnt = sblock.fs_ipg % fullcnt;
257 partialsize = partialcnt * sizeof(struct dinode);
258 if (partialcnt != 0) {
259 readpercg++;
260 } else {
261 partialcnt = fullcnt;
262 partialsize = inobufsize;
263 }
264 if (inodebuf == NULL &&
265 (inodebuf = (struct dinode *)malloc((unsigned)inobufsize)) == NULL)
266 errexit("Cannot allocate space for inode buffer\n");
267 while (nextino < ROOTINO)
268 (void)getnextinode(nextino);
269}
270
271freeinodebuf()
272{
273
274 if (inodebuf != NULL)
275 free((char *)inodebuf);
276 inodebuf = NULL;
277}
278
279/*
280 * Routines to maintain information about directory inodes.
281 * This is built during the first pass and used during the
282 * second and third passes.
283 *
284 * Enter inodes into the cache.
285 */
286cacheino(dp, inumber)
287 register struct dinode *dp;
288 ino_t inumber;
289{
290 register struct inoinfo *inp;
291 struct inoinfo **inpp;
292 unsigned int blks;
293
294 blks = howmany(dp->di_size, sblock.fs_bsize);
295 if (blks > NDADDR)
296 blks = NDADDR + NIADDR;
297 inp = (struct inoinfo *)
298 malloc(sizeof(*inp) + (blks - 1) * sizeof(daddr_t));
299 if (inp == NULL)
300 return;
301 inpp = &inphead[inumber % numdirs];
302 inp->i_nexthash = *inpp;
303 *inpp = inp;
304 inp->i_parent = (ino_t)0;
305 inp->i_dotdot = (ino_t)0;
306 inp->i_number = inumber;
307 inp->i_isize = dp->di_size;
308 inp->i_numblks = blks * sizeof(daddr_t);
309 bcopy((char *)&dp->di_db[0], (char *)&inp->i_blks[0],
310 (size_t)inp->i_numblks);
311 if (inplast == listmax) {
312 listmax += 100;
313 inpsort = (struct inoinfo **)realloc((char *)inpsort,
314 (unsigned)listmax * sizeof(struct inoinfo *));
315 if (inpsort == NULL)
316 errexit("cannot increase directory list");
317 }
318 inpsort[inplast++] = inp;
319}
320
321/*
322 * Look up an inode cache structure.
323 */
324struct inoinfo *
325getinoinfo(inumber)
326 ino_t inumber;
327{
328 register struct inoinfo *inp;
329
330 for (inp = inphead[inumber % numdirs]; inp; inp = inp->i_nexthash) {
331 if (inp->i_number != inumber)
332 continue;
333 return (inp);
334 }
335 errexit("cannot find inode %d\n", inumber);
336 return ((struct inoinfo *)0);
337}
338
339/*
340 * Clean up all the inode cache structure.
341 */
342inocleanup()
343{
344 register struct inoinfo **inpp;
345
346 if (inphead == NULL)
347 return;
348 for (inpp = &inpsort[inplast - 1]; inpp >= inpsort; inpp--)
349 free((char *)(*inpp));
350 free((char *)inphead);
351 free((char *)inpsort);
352 inphead = inpsort = NULL;
353}
354
355inodirty()
356{
357
358 dirty(pbp);
359}
360
361clri(idesc, type, flag)
362 register struct inodesc *idesc;
363 char *type;
364 int flag;
365{
366 register struct dinode *dp;
367
368 dp = ginode(idesc->id_number);
369 if (flag == 1) {
370 pwarn("%s %s", type,
371 (dp->di_mode & IFMT) == IFDIR ? "DIR" : "FILE");
372 pinode(idesc->id_number);
373 }
374 if (preen || reply("CLEAR") == 1) {
375 if (preen)
376 printf(" (CLEARED)\n");
377 n_files--;
378 (void)ckinode(dp, idesc);
379 clearinode(dp);
380 statemap[idesc->id_number] = USTATE;
381 inodirty();
382 }
383}
384
385findname(idesc)
386 struct inodesc *idesc;
387{
388 register struct direct *dirp = idesc->id_dirp;
389
390 if (dirp->d_ino != idesc->id_parent)
391 return (KEEPON);
392 bcopy(dirp->d_name, idesc->id_name, (size_t)dirp->d_namlen + 1);
393 return (STOP|FOUND);
394}
395
396findino(idesc)
397 struct inodesc *idesc;
398{
399 register struct direct *dirp = idesc->id_dirp;
400
401 if (dirp->d_ino == 0)
402 return (KEEPON);
403 if (strcmp(dirp->d_name, idesc->id_name) == 0 &&
404 dirp->d_ino >= ROOTINO && dirp->d_ino <= maxino) {
405 idesc->id_parent = dirp->d_ino;
406 return (STOP|FOUND);
407 }
408 return (KEEPON);
409}
410
411pinode(ino)
412 ino_t ino;
413{
414 register struct dinode *dp;
415 register char *p;
416 struct passwd *pw;
417 char *ctime();
418
419 printf(" I=%lu ", ino);
420 if (ino < ROOTINO || ino > maxino)
421 return;
422 dp = ginode(ino);
423 printf(" OWNER=");
424 if ((pw = getpwuid((int)dp->di_uid)) != 0)
425 printf("%s ", pw->pw_name);
426 else
427 printf("%u ", (unsigned)dp->di_uid);
428 printf("MODE=%o\n", dp->di_mode);
429 if (preen)
430 printf("%s: ", devname);
431 printf("SIZE=%lu ", dp->di_size);
432 p = ctime(&dp->di_mtime);
433 printf("MTIME=%12.12s %4.4s ", &p[4], &p[20]);
434}
435
436blkerror(ino, type, blk)
437 ino_t ino;
438 char *type;
439 daddr_t blk;
440{
441
442 pfatal("%ld %s I=%lu", blk, type, ino);
443 printf("\n");
444 switch (statemap[ino]) {
445
446 case FSTATE:
447 statemap[ino] = FCLEAR;
448 return;
449
450 case DSTATE:
451 statemap[ino] = DCLEAR;
452 return;
453
454 case FCLEAR:
455 case DCLEAR:
456 return;
457
458 default:
459 errexit("BAD STATE %d TO BLKERR", statemap[ino]);
460 /* NOTREACHED */
461 }
462}
463
464/*
465 * allocate an unused inode
466 */
467ino_t
468allocino(request, type)
469 ino_t request;
470 int type;
471{
472 register ino_t ino;
473 register struct dinode *dp;
474
475 if (request == 0)
476 request = ROOTINO;
477 else if (statemap[request] != USTATE)
478 return (0);
479 for (ino = request; ino < maxino; ino++)
480 if (statemap[ino] == USTATE)
481 break;
482 if (ino == maxino)
483 return (0);
484 switch (type & IFMT) {
485 case IFDIR:
486 statemap[ino] = DSTATE;
487 break;
488 case IFREG:
489 case IFLNK:
490 statemap[ino] = FSTATE;
491 break;
492 default:
493 return (0);
494 }
495 dp = ginode(ino);
496 dp->di_db[0] = allocblk((long)1);
497 if (dp->di_db[0] == 0) {
498 statemap[ino] = USTATE;
499 return (0);
500 }
501 dp->di_mode = type;
502 (void)time(&dp->di_atime);
503 dp->di_mtime = dp->di_ctime = dp->di_atime;
504 dp->di_size = sblock.fs_fsize;
505 dp->di_blocks = btodb(sblock.fs_fsize);
506 n_files++;
507 inodirty();
508 return (ino);
509}
510
511/*
512 * deallocate an inode
513 */
514freeino(ino)
515 ino_t ino;
516{
517 struct inodesc idesc;
518 extern int pass4check();
519 struct dinode *dp;
520
521 bzero((char *)&idesc, sizeof(struct inodesc));
522 idesc.id_type = ADDR;
523 idesc.id_func = pass4check;
524 idesc.id_number = ino;
525 dp = ginode(ino);
526 (void)ckinode(dp, &idesc);
527 clearinode(dp);
528 inodirty();
529 statemap[ino] = USTATE;
530 n_files--;
531}