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