xref: /netbsd-src/sbin/fsck_lfs/inode.c (revision 1ffa7b76c40339c17a0fb2a09fac93f287cfc046)
1 /* $NetBSD: inode.c,v 1.17 2003/04/02 10:39:27 fvdl Exp $	 */
2 
3 /*
4  * Copyright (c) 1997, 1998
5  *      Konrad Schroder.  All rights reserved.
6  * Copyright (c) 1980, 1986, 1993
7  *	The Regents of the University of California.  All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. All advertising materials mentioning features or use of this software
18  *    must display the following acknowledgement:
19  *	This product includes software developed by the University of
20  *	California, Berkeley and its contributors.
21  * 4. Neither the name of the University nor the names of its contributors
22  *    may be used to endorse or promote products derived from this software
23  *    without specific prior written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35  * SUCH DAMAGE.
36  */
37 
38 #include <sys/types.h>
39 #include <sys/param.h>
40 #include <sys/time.h>
41 #include <sys/buf.h>
42 #include <sys/mount.h>
43 
44 #include <ufs/ufs/inode.h>
45 #include <ufs/ufs/dir.h>
46 #define vnode uvnode
47 #include <ufs/lfs/lfs.h>
48 #undef vnode
49 
50 #include <err.h>
51 #ifndef SMALL
52 #include <pwd.h>
53 #endif
54 #include <stdio.h>
55 #include <stdlib.h>
56 #include <string.h>
57 
58 #include "bufcache.h"
59 #include "vnode.h"
60 #include "lfs.h"
61 
62 #include "fsck.h"
63 #include "fsutil.h"
64 #include "extern.h"
65 
66 extern SEGUSE *seg_table;
67 extern ufs_daddr_t *din_table;
68 
69 static int iblock(struct inodesc *, long, u_int64_t);
70 int blksreqd(struct lfs *, int);
71 int lfs_maxino(void);
72 
73 /*
74  * Get a dinode of a given inum.
75  * XXX combine this function with vget.
76  */
77 struct ufs1_dinode *
78 ginode(ino_t ino)
79 {
80 	struct uvnode *vp;
81 	struct ubuf *bp;
82 	IFILE *ifp;
83 
84 	vp = vget(fs, ino);
85 	if (vp == NULL)
86 		return NULL;
87 
88 	if (din_table[ino] == 0x0) {
89 		LFS_IENTRY(ifp, fs, ino, bp);
90 		din_table[ino] = ifp->if_daddr;
91 		seg_table[dtosn(fs, ifp->if_daddr)].su_nbytes += DINODE1_SIZE;
92 		brelse(bp);
93 	}
94 	return (VTOI(vp)->i_din.ffs1_din);
95 }
96 
97 /*
98  * Check validity of held blocks in an inode, recursing through all blocks.
99  */
100 int
101 ckinode(struct ufs1_dinode *dp, struct inodesc *idesc)
102 {
103 	ufs_daddr_t *ap, lbn;
104 	long ret, n, ndb, offset;
105 	struct ufs1_dinode dino;
106 	u_int64_t remsize, sizepb;
107 	mode_t mode;
108 	char pathbuf[MAXPATHLEN + 1];
109 	struct uvnode *vp, *thisvp;
110 
111 	if (idesc->id_fix != IGNORE)
112 		idesc->id_fix = DONTKNOW;
113 	idesc->id_entryno = 0;
114 	idesc->id_filesize = dp->di_size;
115 	mode = dp->di_mode & IFMT;
116 	if (mode == IFBLK || mode == IFCHR ||
117 	    (mode == IFLNK && (dp->di_size < fs->lfs_maxsymlinklen ||
118 		    (fs->lfs_maxsymlinklen == 0 &&
119 			dp->di_blocks == 0))))
120 		return (KEEPON);
121 	dino = *dp;
122 	ndb = howmany(dino.di_size, fs->lfs_bsize);
123 
124 	thisvp = vget(fs, dino.di_u.inumber);
125 	for (lbn = 0; lbn < NDADDR; lbn++) {
126 		ap = dino.di_db + lbn;
127 		if (thisvp)
128 			idesc->id_numfrags =
129 				numfrags(fs, VTOI(thisvp)->i_lfs_fragsize[lbn]);
130 		else {
131 			if (--ndb == 0 && (offset = blkoff(fs, dino.di_size)) != 0) {
132 				idesc->id_numfrags =
133 			    	numfrags(fs, fragroundup(fs, offset));
134 			} else
135 				idesc->id_numfrags = fs->lfs_frag;
136 		}
137 		if (*ap == 0) {
138 			if (idesc->id_type == DATA && ndb >= 0) {
139 				/* An empty block in a directory XXX */
140 				getpathname(pathbuf, idesc->id_number,
141 				    idesc->id_number);
142 				pfatal("DIRECTORY %s: CONTAINS EMPTY BLOCKS",
143 				    pathbuf);
144 				if (reply("ADJUST LENGTH") == 1) {
145 					vp = vget(fs, idesc->id_number);
146 					dp = VTOD(vp);
147 					dp->di_size = (ap - &dino.di_db[0]) *
148 					    fs->lfs_bsize;
149 					printf(
150 					    "YOU MUST RERUN FSCK AFTERWARDS\n");
151 					rerun = 1;
152 					inodirty(VTOI(vp));
153 				}
154 			}
155 			continue;
156 		}
157 		idesc->id_blkno = *ap;
158 		idesc->id_lblkno = ap - &dino.di_db[0];
159 		if (idesc->id_type == ADDR) {
160 			ret = (*idesc->id_func) (idesc);
161 		} else
162 			ret = dirscan(idesc);
163 		idesc->id_lblkno = 0;
164 		if (ret & STOP)
165 			return (ret);
166 	}
167 	idesc->id_numfrags = fs->lfs_frag;
168 	remsize = dino.di_size - fs->lfs_bsize * NDADDR;
169 	sizepb = fs->lfs_bsize;
170 	for (ap = &dino.di_ib[0], n = 1; n <= NIADDR; ap++, n++) {
171 		if (*ap) {
172 			idesc->id_blkno = *ap;
173 			ret = iblock(idesc, n, remsize);
174 			if (ret & STOP)
175 				return (ret);
176 		} else {
177 			if (idesc->id_type == DATA && remsize > 0) {
178 				/* An empty block in a directory XXX */
179 				getpathname(pathbuf, idesc->id_number,
180 				    idesc->id_number);
181 				pfatal("DIRECTORY %s: CONTAINS EMPTY BLOCKS",
182 				    pathbuf);
183 				if (reply("ADJUST LENGTH") == 1) {
184 					vp = vget(fs, idesc->id_number);
185 					dp = VTOD(vp);
186 					dp->di_size -= remsize;
187 					remsize = 0;
188 					printf(
189 					    "YOU MUST RERUN FSCK AFTERWARDS\n");
190 					rerun = 1;
191 					inodirty(VTOI(vp));
192 					break;
193 				}
194 			}
195 		}
196 		sizepb *= NINDIR(fs);
197 		remsize -= sizepb;
198 	}
199 	return (KEEPON);
200 }
201 
202 static int
203 iblock(struct inodesc *idesc, long ilevel, u_int64_t isize)
204 {
205 	ufs_daddr_t *ap, *aplim;
206 	struct ubuf *bp;
207 	int i, n, (*func) (struct inodesc *), nif;
208 	u_int64_t sizepb;
209 	char pathbuf[MAXPATHLEN + 1], buf[BUFSIZ];
210 	struct uvnode *devvp, *vp;
211 	int diddirty = 0;
212 
213 	if (idesc->id_type == ADDR) {
214 		func = idesc->id_func;
215 		n = (*func) (idesc);
216 		if ((n & KEEPON) == 0)
217 			return (n);
218 	} else
219 		func = dirscan;
220 	if (chkrange(idesc->id_blkno, fragstofsb(fs, idesc->id_numfrags)))
221 		return (SKIP);
222 
223 	devvp = fs->lfs_unlockvp;
224 	bread(devvp, fsbtodb(fs, idesc->id_blkno), fs->lfs_bsize, NOCRED, &bp);
225 	ilevel--;
226 	for (sizepb = fs->lfs_bsize, i = 0; i < ilevel; i++)
227 		sizepb *= NINDIR(fs);
228 	if (isize > sizepb * NINDIR(fs))
229 		nif = NINDIR(fs);
230 	else
231 		nif = howmany(isize, sizepb);
232 	if (idesc->id_func == pass1check && nif < NINDIR(fs)) {
233 		aplim = ((ufs_daddr_t *) bp->b_data) + NINDIR(fs);
234 		for (ap = ((ufs_daddr_t *) bp->b_data) + nif; ap < aplim; ap++) {
235 			if (*ap == 0)
236 				continue;
237 			(void) sprintf(buf, "PARTIALLY TRUNCATED INODE I=%u",
238 			    idesc->id_number);
239 			if (dofix(idesc, buf)) {
240 				*ap = 0;
241 				++diddirty;
242 			}
243 		}
244 	}
245 	aplim = ((ufs_daddr_t *) bp->b_data) + nif;
246 	for (ap = ((ufs_daddr_t *) bp->b_data); ap < aplim; ap++) {
247 		if (*ap) {
248 			idesc->id_blkno = *ap;
249 			if (ilevel == 0)
250 				n = (*func) (idesc);
251 			else
252 				n = iblock(idesc, ilevel, isize);
253 			if (n & STOP) {
254 				if (diddirty)
255 					VOP_BWRITE(bp);
256 				else
257 					brelse(bp);
258 				return (n);
259 			}
260 		} else {
261 			if (idesc->id_type == DATA && isize > 0) {
262 				/* An empty block in a directory XXX */
263 				getpathname(pathbuf, idesc->id_number,
264 				    idesc->id_number);
265 				pfatal("DIRECTORY %s: CONTAINS EMPTY BLOCKS",
266 				    pathbuf);
267 				if (reply("ADJUST LENGTH") == 1) {
268 					vp = vget(fs, idesc->id_number);
269 					VTOI(vp)->i_ffs1_size -= isize;
270 					isize = 0;
271 					printf(
272 					    "YOU MUST RERUN FSCK AFTERWARDS\n");
273 					rerun = 1;
274 					inodirty(VTOI(vp));
275 					if (diddirty)
276 						VOP_BWRITE(bp);
277 					else
278 						brelse(bp);
279 					return (STOP);
280 				}
281 			}
282 		}
283 		isize -= sizepb;
284 	}
285 	if (diddirty)
286 		VOP_BWRITE(bp);
287 	else
288 		brelse(bp);
289 	return (KEEPON);
290 }
291 /*
292  * Check that a block in a legal block number.
293  * Return 0 if in range, 1 if out of range.
294  */
295 int
296 chkrange(daddr_t blk, int cnt)
297 {
298 	if (blk < sntod(fs, 0)) {
299 		return (1);
300 	}
301 	if (blk > maxfsblock) {
302 		return (1);
303 	}
304 	if (blk + cnt < sntod(fs, 0)) {
305 		return (1);
306 	}
307 	if (blk + cnt > maxfsblock) {
308 		return (1);
309 	}
310 	return (0);
311 }
312 /*
313  * Routines to maintain information about directory inodes.
314  * This is built during the first pass and used during the
315  * second and third passes.
316  *
317  * Enter inodes into the cache.
318  */
319 void
320 cacheino(struct ufs1_dinode * dp, ino_t inumber)
321 {
322 	struct inoinfo *inp;
323 	struct inoinfo **inpp;
324 	unsigned int blks;
325 
326 	blks = howmany(dp->di_size, fs->lfs_bsize);
327 	if (blks > NDADDR)
328 		blks = NDADDR + NIADDR;
329 	inp = (struct inoinfo *)
330 	    malloc(sizeof(*inp) + (blks - 1) * sizeof(ufs_daddr_t));
331 	if (inp == NULL)
332 		return;
333 	inpp = &inphead[inumber % numdirs];
334 	inp->i_nexthash = *inpp;
335 	*inpp = inp;
336 	inp->i_child = inp->i_sibling = inp->i_parentp = 0;
337 	if (inumber == ROOTINO)
338 		inp->i_parent = ROOTINO;
339 	else
340 		inp->i_parent = (ino_t) 0;
341 	inp->i_dotdot = (ino_t) 0;
342 	inp->i_number = inumber;
343 	inp->i_isize = dp->di_size;
344 
345 	inp->i_numblks = blks * sizeof(ufs_daddr_t);
346 	memcpy(&inp->i_blks[0], &dp->di_db[0], (size_t) inp->i_numblks);
347 	if (inplast == listmax) {
348 		listmax += 100;
349 		inpsort = (struct inoinfo **) realloc((char *) inpsort,
350 		    (unsigned) listmax * sizeof(struct inoinfo *));
351 		if (inpsort == NULL)
352 			err(8, "cannot increase directory list\n");
353 	}
354 	inpsort[inplast++] = inp;
355 }
356 
357 /*
358  * Look up an inode cache structure.
359  */
360 struct inoinfo *
361 getinoinfo(ino_t inumber)
362 {
363 	register struct inoinfo *inp;
364 
365 	for (inp = inphead[inumber % numdirs]; inp; inp = inp->i_nexthash) {
366 		if (inp->i_number != inumber)
367 			continue;
368 		return (inp);
369 	}
370 	err(8, "cannot find inode %d\n", inumber);
371 	return ((struct inoinfo *) 0);
372 }
373 
374 /*
375  * Clean up all the inode cache structure.
376  */
377 void
378 inocleanup()
379 {
380 	register struct inoinfo **inpp;
381 
382 	if (inphead == NULL)
383 		return;
384 	for (inpp = &inpsort[inplast - 1]; inpp >= inpsort; inpp--)
385 		free((char *) (*inpp));
386 	free((char *) inphead);
387 	free((char *) inpsort);
388 	inphead = inpsort = NULL;
389 }
390 
391 void
392 inodirty(struct inode *ip)
393 {
394 	ip->i_flag |= IN_MODIFIED;
395 }
396 
397 void
398 clri(struct inodesc * idesc, char *type, int flag)
399 {
400 	struct ubuf *bp;
401 	IFILE *ifp;
402 	struct uvnode *vp;
403 
404 	vp = vget(fs, idesc->id_number);
405 	if (flag == 1) {
406 		pwarn("%s %s", type,
407 		      (VTOI(vp)->i_ffs1_mode & IFMT) == IFDIR ? "DIR" : "FILE");
408 		pinode(idesc->id_number);
409 	}
410 	if (flag == 2 || preen || reply("CLEAR") == 1) {
411 		if (preen && flag != 2)
412 			printf(" (CLEARED)\n");
413 		n_files--;
414 		(void) ckinode(VTOD(vp), idesc);
415 		clearinode(VTOD(vp));
416 		statemap[idesc->id_number] = USTATE;
417 		inodirty(VTOI(vp));
418 
419 		/* Send cleared inode to the free list */
420 
421 		LFS_IENTRY(ifp, fs, idesc->id_number, bp);
422 		ifp->if_daddr = LFS_UNUSED_DADDR;
423 		ifp->if_nextfree = fs->lfs_freehd;
424 		fs->lfs_freehd = idesc->id_number;
425 		sbdirty();
426 		VOP_BWRITE(bp);
427 	}
428 }
429 
430 int
431 findname(struct inodesc * idesc)
432 {
433 	register struct direct *dirp = idesc->id_dirp;
434 
435 	if (dirp->d_ino != idesc->id_parent)
436 		return (KEEPON);
437 	memcpy(idesc->id_name, dirp->d_name, (size_t) dirp->d_namlen + 1);
438 	return (STOP | FOUND);
439 }
440 
441 int
442 findino(struct inodesc * idesc)
443 {
444 	register struct direct *dirp = idesc->id_dirp;
445 
446 	if (dirp->d_ino == 0)
447 		return (KEEPON);
448 	if (strcmp(dirp->d_name, idesc->id_name) == 0 &&
449 	    dirp->d_ino >= ROOTINO && dirp->d_ino < maxino) {
450 		idesc->id_parent = dirp->d_ino;
451 		return (STOP | FOUND);
452 	}
453 	return (KEEPON);
454 }
455 
456 void
457 pinode(ino_t ino)
458 {
459 	register struct ufs1_dinode *dp;
460 	register char *p;
461 	struct passwd *pw;
462 	time_t t;
463 
464 	printf(" I=%u ", ino);
465 	if (ino < ROOTINO || ino >= maxino)
466 		return;
467 	dp = ginode(ino);
468 	if (dp) {
469 		printf(" OWNER=");
470 #ifndef SMALL
471 		if ((pw = getpwuid((int) dp->di_uid)) != 0)
472 			printf("%s ", pw->pw_name);
473 		else
474 #endif
475 			printf("%u ", (unsigned) dp->di_uid);
476 		printf("MODE=%o\n", dp->di_mode);
477 		if (preen)
478 			printf("%s: ", cdevname());
479 		printf("SIZE=%llu ", (unsigned long long) dp->di_size);
480 		t = dp->di_mtime;
481 		p = ctime(&t);
482 		printf("MTIME=%12.12s %4.4s ", &p[4], &p[20]);
483 	}
484 }
485 
486 void
487 blkerror(ino_t ino, char *type, daddr_t blk)
488 {
489 
490 	pfatal("%lld %s I=%u", (long long) blk, type, ino);
491 	printf("\n");
492 	if (exitonfail)
493 		exit(1);
494 	switch (statemap[ino]) {
495 
496 	case FSTATE:
497 		statemap[ino] = FCLEAR;
498 		return;
499 
500 	case DSTATE:
501 		statemap[ino] = DCLEAR;
502 		return;
503 
504 	case FCLEAR:
505 	case DCLEAR:
506 		return;
507 
508 	default:
509 		err(8, "BAD STATE %d TO BLKERR\n", statemap[ino]);
510 		/* NOTREACHED */
511 	}
512 }
513 /*
514  * allocate an unused inode
515  */
516 ino_t
517 allocino(ino_t request, int type)
518 {
519 	ino_t ino;
520 	struct ufs1_dinode *dp;
521 	time_t t;
522 	struct uvnode *vp;
523 	struct ubuf *bp;
524 
525 	if (request == 0)
526 		request = ROOTINO;
527 	else if (statemap[request] != USTATE)
528 		return (0);
529 	for (ino = request; ino < maxino; ino++)
530 		if (statemap[ino] == USTATE)
531 			break;
532 	if (ino == maxino)
533 		return (0);
534 	switch (type & IFMT) {
535 	case IFDIR:
536 		statemap[ino] = DSTATE;
537 		break;
538 	case IFREG:
539 	case IFLNK:
540 		statemap[ino] = FSTATE;
541 		break;
542 	default:
543 		return (0);
544 	}
545 	vp = vget(fs, ino);
546 	dp = (VTOI(vp)->i_din.ffs1_din);
547 	bp = getblk(vp, 0, fs->lfs_fsize);
548 	VOP_BWRITE(bp);
549 	dp->di_mode = type;
550 	(void) time(&t);
551 	dp->di_atime = t;
552 	dp->di_mtime = dp->di_ctime = dp->di_atime;
553 	dp->di_size = fs->lfs_fsize;
554 	dp->di_blocks = btofsb(fs, fs->lfs_fsize);
555 	n_files++;
556 	inodirty(VTOI(vp));
557 	typemap[ino] = IFTODT(type);
558 	return (ino);
559 }
560 /*
561  * deallocate an inode
562  */
563 void
564 freeino(ino_t ino)
565 {
566 	struct inodesc idesc;
567 	struct uvnode *vp;
568 
569 	memset(&idesc, 0, sizeof(struct inodesc));
570 	idesc.id_type = ADDR;
571 	idesc.id_func = pass4check;
572 	idesc.id_number = ino;
573 	vp = vget(fs, ino);
574 	(void) ckinode(VTOD(vp), &idesc);
575 	clearinode(VTOI(vp)->i_din.ffs1_din);
576 	inodirty(VTOI(vp));
577 	statemap[ino] = USTATE;
578 
579 	n_files--;
580 }
581