Commit | Line | Data |
---|---|---|
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 | |
35 | static char sccsid[] = "@(#)inode.c 5.18 (Berkeley) 3/19/91"; | |
78ed81a3 | 36 | static 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 | ||
48 | static ino_t startinum; | |
49 | ||
50 | ckinode(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 | ||
96 | iblock(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 | */ | |
159 | chkrange(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 | */ | |
195 | struct dinode * | |
196 | ginode(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 | */ | |
218 | ino_t nextino, lastinum; | |
219 | long readcnt, readpercg, fullcnt, inobufsize, partialcnt, partialsize; | |
220 | struct dinode *inodebuf; | |
221 | ||
222 | struct dinode * | |
223 | getnextinode(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 | ||
248 | resetinodebuf() | |
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 | ||
273 | freeinodebuf() | |
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 | */ | |
288 | cacheino(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 | */ | |
326 | struct inoinfo * | |
327 | getinoinfo(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 | */ | |
344 | inocleanup() | |
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 | ||
357 | inodirty() | |
358 | { | |
359 | ||
360 | dirty(pbp); | |
361 | } | |
362 | ||
363 | clri(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 | ||
387 | findname(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 | ||
398 | findino(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 | ||
413 | pinode(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 | ||
438 | blkerror(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 | */ | |
469 | ino_t | |
470 | allocino(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 | */ | |
516 | freeino(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 | } |