xref: /original-bsd/sbin/fsck/utilities.c (revision fcd500a9)
1 /*
2  * Copyright (c) 1980 Regents of the University of California.
3  * All rights reserved.  The Berkeley software License Agreement
4  * specifies the terms and conditions for redistribution.
5  */
6 
7 #ifndef lint
8 static char sccsid[] = "@(#)utilities.c	5.10 (Berkeley) 04/07/87";
9 #endif not lint
10 
11 #include <stdio.h>
12 #include <ctype.h>
13 #include <sys/param.h>
14 #include <sys/inode.h>
15 #include <sys/fs.h>
16 #include <sys/dir.h>
17 #include "fsck.h"
18 
19 long	lseek();
20 
21 ftypeok(dp)
22 	DINODE *dp;
23 {
24 	switch (dp->di_mode & IFMT) {
25 
26 	case IFDIR:
27 	case IFREG:
28 	case IFBLK:
29 	case IFCHR:
30 	case IFLNK:
31 	case IFSOCK:
32 		return (1);
33 
34 	default:
35 		if (debug)
36 			printf("bad file type 0%o\n", dp->di_mode);
37 		return (0);
38 	}
39 }
40 
41 reply(s)
42 	char *s;
43 {
44 	char line[80];
45 	int cont = (strcmp(s, "CONTINUE") == 0);
46 
47 	if (preen)
48 		pfatal("INTERNAL ERROR: GOT TO reply()");
49 	printf("\n%s? ", s);
50 	if (!cont && (nflag || dfile.wfdes < 0)) {
51 		printf(" no\n\n");
52 		return (0);
53 	}
54 	if (yflag || (cont && nflag)) {
55 		printf(" yes\n\n");
56 		return (1);
57 	}
58 	if (getline(stdin, line, sizeof(line)) == EOF)
59 		errexit("\n");
60 	printf("\n");
61 	if (line[0] == 'y' || line[0] == 'Y')
62 		return (1);
63 	else
64 		return (0);
65 }
66 
67 getline(fp, loc, maxlen)
68 	FILE *fp;
69 	char *loc;
70 {
71 	register n;
72 	register char *p, *lastloc;
73 
74 	p = loc;
75 	lastloc = &p[maxlen-1];
76 	while ((n = getc(fp)) != '\n') {
77 		if (n == EOF)
78 			return (EOF);
79 		if (!isspace(n) && p < lastloc)
80 			*p++ = n;
81 	}
82 	*p = 0;
83 	return (p - loc);
84 }
85 
86 BUFAREA *
87 getblk(bp, blk, size)
88 	register BUFAREA *bp;
89 	daddr_t blk;
90 	long size;
91 {
92 	register struct filecntl *fcp;
93 	daddr_t dblk;
94 
95 	fcp = &dfile;
96 	dblk = fsbtodb(&sblock, blk);
97 	if (bp->b_bno == dblk)
98 		return (bp);
99 	flush(fcp, bp);
100 	bp->b_errs = bread(fcp, bp->b_un.b_buf, dblk, size);
101 	bp->b_bno = dblk;
102 	bp->b_size = size;
103 	return (bp);
104 }
105 
106 flush(fcp, bp)
107 	struct filecntl *fcp;
108 	register BUFAREA *bp;
109 {
110 	register int i, j;
111 
112 	if (!bp->b_dirty)
113 		return;
114 	if (bp->b_errs != 0)
115 		pfatal("WRITING %sZERO'ED BLOCK %d TO DISK\n",
116 		    (bp->b_errs == bp->b_size / dev_bsize) ? "" : "PARTIALLY ",
117 		    bp->b_bno);
118 	bp->b_dirty = 0;
119 	bp->b_errs = 0;
120 	bwrite(fcp, bp->b_un.b_buf, bp->b_bno, (long)bp->b_size);
121 	if (bp != &sblk)
122 		return;
123 	for (i = 0, j = 0; i < sblock.fs_cssize; i += sblock.fs_bsize, j++) {
124 		bwrite(&dfile, (char *)sblock.fs_csp[j],
125 		    fsbtodb(&sblock, sblock.fs_csaddr + j * sblock.fs_frag),
126 		    sblock.fs_cssize - i < sblock.fs_bsize ?
127 		    sblock.fs_cssize - i : sblock.fs_bsize);
128 	}
129 }
130 
131 rwerr(s, blk)
132 	char *s;
133 	daddr_t blk;
134 {
135 
136 	if (preen == 0)
137 		printf("\n");
138 	pfatal("CANNOT %s: BLK %ld", s, blk);
139 	if (reply("CONTINUE") == 0)
140 		errexit("Program terminated\n");
141 }
142 
143 ckfini()
144 {
145 
146 	flush(&dfile, &fileblk);
147 	flush(&dfile, &sblk);
148 	if (havesb && sblk.b_bno != SBOFF / dev_bsize &&
149 	    !preen && reply("UPDATE STANDARD SUPERBLOCK")) {
150 		sblk.b_bno = SBOFF / dev_bsize;
151 		sbdirty();
152 		flush(&dfile, &sblk);
153 	}
154 	flush(&dfile, &inoblk);
155 	flush(&dfile, &cgblk);
156 	(void)close(dfile.rfdes);
157 	(void)close(dfile.wfdes);
158 }
159 
160 bread(fcp, buf, blk, size)
161 	register struct filecntl *fcp;
162 	char *buf;
163 	daddr_t blk;
164 	long size;
165 {
166 	char *cp;
167 	int i, errs;
168 
169 	if (lseek(fcp->rfdes, blk * dev_bsize, 0) < 0)
170 		rwerr("SEEK", blk);
171 	else if (read(fcp->rfdes, buf, (int)size) == size)
172 		return (0);
173 	rwerr("READ", blk);
174 	if (lseek(fcp->rfdes, blk * dev_bsize, 0) < 0)
175 		rwerr("SEEK", blk);
176 	errs = 0;
177 	bzero(buf, size);
178 	printf("THE FOLLOWING DISK SECTORS COULD NOT BE READ:");
179 	for (cp = buf, i = 0; i < size; i += secsize, cp += secsize) {
180 		if (read(fcp->rfdes, cp, secsize) < 0) {
181 			lseek(fcp->rfdes, blk * dev_bsize + i + secsize, 0);
182 			if (secsize != dev_bsize && dev_bsize != 1)
183 				printf(" %d (%d),",
184 				    (blk * dev_bsize + i) / secsize,
185 				    blk + i / dev_bsize);
186 			else
187 				printf(" %d,", blk + i / dev_bsize);
188 			errs++;
189 		}
190 	}
191 	printf("\n");
192 	return (errs);
193 }
194 
195 bwrite(fcp, buf, blk, size)
196 	register struct filecntl *fcp;
197 	char *buf;
198 	daddr_t blk;
199 	long size;
200 {
201 	int i;
202 	char *cp;
203 
204 	if (fcp->wfdes < 0)
205 		return;
206 	if (lseek(fcp->wfdes, blk * dev_bsize, 0) < 0)
207 		rwerr("SEEK", blk);
208 	else if (write(fcp->wfdes, buf, (int)size) == size) {
209 		fcp->mod = 1;
210 		return;
211 	}
212 	rwerr("WRITE", blk);
213 	if (lseek(fcp->wfdes, blk * dev_bsize, 0) < 0)
214 		rwerr("SEEK", blk);
215 	printf("THE FOLLOWING SECTORS COULD NOT BE WRITTEN:");
216 	for (cp = buf, i = 0; i < size; i += dev_bsize, cp += dev_bsize)
217 		if (write(fcp->wfdes, cp, dev_bsize) < 0) {
218 			lseek(fcp->rfdes, blk * dev_bsize + i + dev_bsize, 0);
219 			printf(" %d,", blk + i / dev_bsize);
220 		}
221 	printf("\n");
222 	return;
223 }
224 
225 /*
226  * allocate a data block with the specified number of fragments
227  */
228 allocblk(frags)
229 	int frags;
230 {
231 	register int i, j, k;
232 
233 	if (frags <= 0 || frags > sblock.fs_frag)
234 		return (0);
235 	for (i = 0; i < fmax - sblock.fs_frag; i += sblock.fs_frag) {
236 		for (j = 0; j <= sblock.fs_frag - frags; j++) {
237 			if (getbmap(i + j))
238 				continue;
239 			for (k = 1; k < frags; k++)
240 				if (getbmap(i + j + k))
241 					break;
242 			if (k < frags) {
243 				j += k;
244 				continue;
245 			}
246 			for (k = 0; k < frags; k++)
247 				setbmap(i + j + k);
248 			n_blks += frags;
249 			return (i + j);
250 		}
251 	}
252 	return (0);
253 }
254 
255 /*
256  * Free a previously allocated block
257  */
258 freeblk(blkno, frags)
259 	daddr_t blkno;
260 	int frags;
261 {
262 	struct inodesc idesc;
263 
264 	idesc.id_blkno = blkno;
265 	idesc.id_numfrags = frags;
266 	pass4check(&idesc);
267 }
268 
269 /*
270  * Find a pathname
271  */
272 getpathname(namebuf, curdir, ino)
273 	char *namebuf;
274 	ino_t curdir, ino;
275 {
276 	int len;
277 	register char *cp;
278 	struct inodesc idesc;
279 	extern int findname();
280 
281 	if (statemap[ino] != DSTATE && statemap[ino] != DFOUND) {
282 		strcpy(namebuf, "?");
283 		return;
284 	}
285 	bzero(&idesc, sizeof(struct inodesc));
286 	idesc.id_type = DATA;
287 	cp = &namebuf[BUFSIZ - 1];
288 	*cp = '\0';
289 	if (curdir != ino) {
290 		idesc.id_parent = curdir;
291 		goto namelookup;
292 	}
293 	while (ino != ROOTINO) {
294 		idesc.id_number = ino;
295 		idesc.id_func = findino;
296 		idesc.id_name = "..";
297 		if ((ckinode(ginode(ino), &idesc) & FOUND) == 0)
298 			break;
299 	namelookup:
300 		idesc.id_number = idesc.id_parent;
301 		idesc.id_parent = ino;
302 		idesc.id_func = findname;
303 		idesc.id_name = namebuf;
304 		if ((ckinode(ginode(idesc.id_number), &idesc) & FOUND) == 0)
305 			break;
306 		len = strlen(namebuf);
307 		cp -= len;
308 		if (cp < &namebuf[MAXNAMLEN])
309 			break;
310 		bcopy(namebuf, cp, len);
311 		*--cp = '/';
312 		ino = idesc.id_number;
313 	}
314 	if (ino != ROOTINO) {
315 		strcpy(namebuf, "?");
316 		return;
317 	}
318 	bcopy(cp, namebuf, &namebuf[BUFSIZ] - cp);
319 }
320 
321 catch()
322 {
323 
324 	ckfini();
325 	exit(12);
326 }
327 
328 /*
329  * When preening, allow a single quit to signal
330  * a special exit after filesystem checks complete
331  * so that reboot sequence may be interrupted.
332  */
333 catchquit()
334 {
335 	extern returntosingle;
336 
337 	printf("returning to single-user after filesystem check\n");
338 	returntosingle = 1;
339 	(void)signal(SIGQUIT, SIG_DFL);
340 }
341 
342 /*
343  * Ignore a single quit signal; wait and flush just in case.
344  * Used by child processes in preen.
345  */
346 voidquit()
347 {
348 
349 	sleep(1);
350 	(void)signal(SIGQUIT, SIG_IGN);
351 	(void)signal(SIGQUIT, SIG_DFL);
352 }
353 
354 /*
355  * determine whether an inode should be fixed.
356  */
357 dofix(idesc, msg)
358 	register struct inodesc *idesc;
359 	char *msg;
360 {
361 
362 	switch (idesc->id_fix) {
363 
364 	case DONTKNOW:
365 		if (idesc->id_type == DATA)
366 			direrr(idesc->id_number, msg);
367 		else
368 			pwarn(msg);
369 		if (preen) {
370 			printf(" (SALVAGED)\n");
371 			idesc->id_fix = FIX;
372 			return (ALTERED);
373 		}
374 		if (reply("SALVAGE") == 0) {
375 			idesc->id_fix = NOFIX;
376 			return (0);
377 		}
378 		idesc->id_fix = FIX;
379 		return (ALTERED);
380 
381 	case FIX:
382 		return (ALTERED);
383 
384 	case NOFIX:
385 		return (0);
386 
387 	default:
388 		errexit("UNKNOWN INODESC FIX MODE %d\n", idesc->id_fix);
389 	}
390 	/* NOTREACHED */
391 }
392 
393 /* VARARGS1 */
394 errexit(s1, s2, s3, s4)
395 	char *s1;
396 {
397 	printf(s1, s2, s3, s4);
398 	exit(8);
399 }
400 
401 /*
402  * An inconsistency occured which shouldn't during normal operations.
403  * Die if preening, otherwise just printf.
404  */
405 /* VARARGS1 */
406 pfatal(s, a1, a2, a3)
407 	char *s;
408 {
409 
410 	if (preen) {
411 		printf("%s: ", devname);
412 		printf(s, a1, a2, a3);
413 		printf("\n");
414 		printf("%s: UNEXPECTED INCONSISTENCY; RUN fsck MANUALLY.\n",
415 			devname);
416 		exit(8);
417 	}
418 	printf(s, a1, a2, a3);
419 }
420 
421 /*
422  * Pwarn is like printf when not preening,
423  * or a warning (preceded by filename) when preening.
424  */
425 /* VARARGS1 */
426 pwarn(s, a1, a2, a3, a4, a5, a6)
427 	char *s;
428 {
429 
430 	if (preen)
431 		printf("%s: ", devname);
432 	printf(s, a1, a2, a3, a4, a5, a6);
433 }
434 
435 #ifndef lint
436 /*
437  * Stub for routines from kernel.
438  */
439 panic(s)
440 	char *s;
441 {
442 
443 	pfatal("INTERNAL INCONSISTENCY:");
444 	errexit(s);
445 }
446 #endif
447