xref: /csrg-svn/sbin/fsck/dir.c (revision 17944)
1 #ifndef lint
2 static char version[] = "@(#)dir.c	3.5 (Berkeley) 02/11/85";
3 #endif
4 
5 #include <sys/param.h>
6 #include <sys/inode.h>
7 #include <sys/fs.h>
8 #define KERNEL
9 #include <sys/dir.h>
10 #undef KERNEL
11 #include "fsck.h"
12 
13 #define MINDIRSIZE	(sizeof (struct dirtemplate))
14 
15 char	*endpathname = &pathname[BUFSIZ - 2];
16 char	*lfname = "lost+found";
17 struct	dirtemplate emptydir = { 0, DIRBLKSIZ };
18 
19 DIRECT	*fsck_readdir();
20 
21 descend(parentino, inumber)
22 	struct inodesc *parentino;
23 	ino_t inumber;
24 {
25 	register DINODE *dp;
26 	struct inodesc curino;
27 
28 	bzero((char *)&curino, sizeof(struct inodesc));
29 	if (statemap[inumber] != DSTATE)
30 		errexit("BAD INODE %d TO DESCEND", statemap[inumber]);
31 	statemap[inumber] = DFOUND;
32 	dp = ginode(inumber);
33 	if (dp->di_size == 0) {
34 		direrr(inumber, "ZERO LENGTH DIRECTORY");
35 		if (reply("REMOVE") == 1)
36 			statemap[inumber] = DCLEAR;
37 		return;
38 	}
39 	if (dp->di_size < MINDIRSIZE) {
40 		direrr(inumber, "DIRECTORY TOO SHORT");
41 		dp->di_size = MINDIRSIZE;
42 		if (reply("FIX") == 1)
43 			inodirty();
44 	}
45 	curino.id_type = DATA;
46 	curino.id_func = parentino->id_func;
47 	curino.id_parent = parentino->id_number;
48 	curino.id_number = inumber;
49 	curino.id_filesize = dp->di_size;
50 	(void)ckinode(dp, &curino);
51 }
52 
53 dirscan(idesc)
54 	register struct inodesc *idesc;
55 {
56 	register DIRECT *dp;
57 	int dsize, n;
58 	long blksiz;
59 	char dbuf[DIRBLKSIZ];
60 
61 	if (idesc->id_type != DATA)
62 		errexit("wrong type to dirscan %d\n", idesc->id_type);
63 	blksiz = idesc->id_numfrags * sblock.fs_fsize;
64 	if (outrange(idesc->id_blkno, idesc->id_numfrags)) {
65 		idesc->id_filesize -= blksiz;
66 		return (SKIP);
67 	}
68 	idesc->id_loc = 0;
69 	for (dp = fsck_readdir(idesc); dp != NULL; dp = fsck_readdir(idesc)) {
70 		dsize = dp->d_reclen;
71 		bcopy((char *)dp, dbuf, dsize);
72 		idesc->id_dirp = (DIRECT *)dbuf;
73 		if ((n = (*idesc->id_func)(idesc)) & ALTERED) {
74 			if (getblk(&fileblk, idesc->id_blkno, blksiz) != NULL) {
75 				bcopy(dbuf, (char *)dp, dsize);
76 				dirty(&fileblk);
77 				sbdirty();
78 			} else
79 				n &= ~ALTERED;
80 		}
81 		if (n & STOP)
82 			return (n);
83 	}
84 	return (idesc->id_filesize > 0 ? KEEPON : STOP);
85 }
86 
87 /*
88  * get next entry in a directory.
89  */
90 DIRECT *
91 fsck_readdir(idesc)
92 	register struct inodesc *idesc;
93 {
94 	register DIRECT *dp, *ndp;
95 	long size, blksiz;
96 
97 	blksiz = idesc->id_numfrags * sblock.fs_fsize;
98 	if (getblk(&fileblk, idesc->id_blkno, blksiz) == NULL) {
99 		idesc->id_filesize -= blksiz - idesc->id_loc;
100 		return NULL;
101 	}
102 	if (idesc->id_loc % DIRBLKSIZ == 0 && idesc->id_filesize > 0 &&
103 	    idesc->id_loc < blksiz) {
104 		dp = (DIRECT *)(dirblk.b_buf + idesc->id_loc);
105 		if (dircheck(idesc, dp))
106 			goto dpok;
107 		idesc->id_loc += DIRBLKSIZ;
108 		idesc->id_filesize -= DIRBLKSIZ;
109 		dp->d_reclen = DIRBLKSIZ;
110 		dp->d_ino = 0;
111 		dp->d_namlen = 0;
112 		dp->d_name[0] = '\0';
113 		if (dofix(idesc, "DIRECTORY CORRUPTED"))
114 			dirty(&fileblk);
115 		return (dp);
116 	}
117 dpok:
118 	if (idesc->id_filesize <= 0 || idesc->id_loc >= blksiz)
119 		return NULL;
120 	dp = (DIRECT *)(dirblk.b_buf + idesc->id_loc);
121 	idesc->id_loc += dp->d_reclen;
122 	idesc->id_filesize -= dp->d_reclen;
123 	if ((idesc->id_loc % DIRBLKSIZ) == 0)
124 		return (dp);
125 	ndp = (DIRECT *)(dirblk.b_buf + idesc->id_loc);
126 	if (idesc->id_loc < blksiz && idesc->id_filesize > 0 &&
127 	    dircheck(idesc, ndp) == 0) {
128 		size = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ);
129 		dp->d_reclen += size;
130 		idesc->id_loc += size;
131 		idesc->id_filesize -= size;
132 		if (dofix(idesc, "DIRECTORY CORRUPTED"))
133 			dirty(&fileblk);
134 	}
135 	return (dp);
136 }
137 
138 /*
139  * Verify that a directory entry is valid.
140  * This is a superset of the checks made in the kernel.
141  */
142 dircheck(idesc, dp)
143 	struct inodesc *idesc;
144 	register DIRECT *dp;
145 {
146 	register int size;
147 	register char *cp;
148 	int spaceleft;
149 
150 	size = DIRSIZ(dp);
151 	spaceleft = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ);
152 	if (dp->d_ino < imax &&
153 	    dp->d_reclen != 0 &&
154 	    dp->d_reclen <= spaceleft &&
155 	    (dp->d_reclen & 0x3) == 0 &&
156 	    dp->d_reclen >= size &&
157 	    idesc->id_filesize >= size &&
158 	    dp->d_namlen <= MAXNAMLEN) {
159 		if (dp->d_ino == 0)
160 			return (1);
161 		for (cp = dp->d_name, size = 0; size < dp->d_namlen; size++)
162 			if (*cp == 0 || (*cp++ & 0200))
163 				return (0);
164 		if (*cp == 0)
165 			return (1);
166 	}
167 	return (0);
168 }
169 
170 direrr(ino, s)
171 	ino_t ino;
172 	char *s;
173 {
174 	register DINODE *dp;
175 
176 	pwarn("%s ", s);
177 	pinode(ino);
178 	printf("\n");
179 	if (ino < ROOTINO || ino > imax) {
180 		pfatal("NAME=%s\n", pathname);
181 		return;
182 	}
183 	dp = ginode(ino);
184 	if (ftypeok(dp))
185 		pfatal("%s=%s\n", DIRCT(dp) ? "DIR" : "FILE", pathname);
186 	else
187 		pfatal("NAME=%s\n", pathname);
188 }
189 
190 adjust(idesc, lcnt)
191 	register struct inodesc *idesc;
192 	short lcnt;
193 {
194 	register DINODE *dp;
195 
196 	dp = ginode(idesc->id_number);
197 	if (dp->di_nlink == lcnt) {
198 		if (linkup(idesc->id_number, (ino_t)0) == 0)
199 			clri(idesc, "UNREF", 0);
200 	} else {
201 		pwarn("LINK COUNT %s", (lfdir == idesc->id_number) ? lfname :
202 			(DIRCT(dp) ? "DIR" : "FILE"));
203 		pinode(idesc->id_number);
204 		printf(" COUNT %d SHOULD BE %d",
205 			dp->di_nlink, dp->di_nlink-lcnt);
206 		if (preen) {
207 			if (lcnt < 0) {
208 				printf("\n");
209 				pfatal("LINK COUNT INCREASING");
210 			}
211 			printf(" (ADJUSTED)\n");
212 		}
213 		if (preen || reply("ADJUST") == 1) {
214 			dp->di_nlink -= lcnt;
215 			inodirty();
216 		}
217 	}
218 }
219 
220 mkentry(idesc)
221 	struct inodesc *idesc;
222 {
223 	register DIRECT *dirp = idesc->id_dirp;
224 	DIRECT newent;
225 	int newlen, oldlen;
226 
227 	newent.d_namlen = 11;
228 	newlen = DIRSIZ(&newent);
229 	if (dirp->d_ino != 0)
230 		oldlen = DIRSIZ(dirp);
231 	else
232 		oldlen = 0;
233 	if (dirp->d_reclen - oldlen < newlen)
234 		return (KEEPON);
235 	newent.d_reclen = dirp->d_reclen - oldlen;
236 	dirp->d_reclen = oldlen;
237 	dirp = (struct direct *)(((char *)dirp) + oldlen);
238 	dirp->d_ino = idesc->id_parent;	/* ino to be entered is in id_parent */
239 	dirp->d_reclen = newent.d_reclen;
240 	dirp->d_namlen = lftempname(dirp->d_name, idesc->id_parent);
241 	return (ALTERED|STOP);
242 }
243 
244 chgdd(idesc)
245 	struct inodesc *idesc;
246 {
247 	register DIRECT *dirp = idesc->id_dirp;
248 
249 	if (dirp->d_name[0] == '.' && dirp->d_name[1] == '.' &&
250 	dirp->d_name[2] == 0) {
251 		dirp->d_ino = lfdir;
252 		return (ALTERED|STOP);
253 	}
254 	return (KEEPON);
255 }
256 
257 linkup(orphan, pdir)
258 	ino_t orphan;
259 	ino_t pdir;
260 {
261 	register DINODE *dp;
262 	int lostdir, len;
263 	struct inodesc idesc;
264 
265 	bzero((char *)&idesc, sizeof(struct inodesc));
266 	dp = ginode(orphan);
267 	lostdir = DIRCT(dp);
268 	pwarn("UNREF %s ", lostdir ? "DIR" : "FILE");
269 	pinode(orphan);
270 	if (preen && dp->di_size == 0)
271 		return (0);
272 	if (preen)
273 		printf(" (RECONNECTED)\n");
274 	else
275 		if (reply("RECONNECT") == 0)
276 			return (0);
277 	pathp = pathname;
278 	*pathp++ = '/';
279 	*pathp = '\0';
280 	if (lfdir == 0) {
281 		dp = ginode(ROOTINO);
282 		idesc.id_name = lfname;
283 		idesc.id_type = DATA;
284 		idesc.id_func = findino;
285 		idesc.id_number = ROOTINO;
286 		idesc.id_filesize = dp->di_size;
287 		(void)ckinode(dp, &idesc);
288 		lfdir = idesc.id_parent;
289 		if (lfdir < ROOTINO || lfdir > imax)
290 			lfdir = 0;
291 		if (lfdir == 0) {
292 			pfatal("SORRY. NO lost+found DIRECTORY");
293 			printf("\n\n");
294 			return (0);
295 		}
296 	}
297 	dp = ginode(lfdir);
298 	if (!DIRCT(dp) || statemap[lfdir] != DFOUND) {
299 		pfatal("SORRY. NO lost+found DIRECTORY");
300 		printf("\n\n");
301 		return (0);
302 	}
303 	if (dp->di_size % DIRBLKSIZ) {
304 		dp->di_size = roundup(dp->di_size, DIRBLKSIZ);
305 		inodirty();
306 	}
307 	len = strlen(lfname);
308 	bcopy(lfname, pathp, len + 1);
309 	pathp += len;
310 	idesc.id_type = DATA;
311 	idesc.id_func = mkentry;
312 	idesc.id_number = lfdir;
313 	idesc.id_filesize = dp->di_size;
314 	idesc.id_parent = orphan;	/* this is the inode to enter */
315 	idesc.id_fix = DONTKNOW;
316 	if (makeentry(dp, &idesc) == 0) {
317 		pfatal("SORRY. NO SPACE IN lost+found DIRECTORY");
318 		printf("\n\n");
319 		return (0);
320 	}
321 	lncntp[orphan]--;
322 	*pathp++ = '/';
323 	pathp += lftempname(pathp, orphan);
324 	if (lostdir) {
325 		dp = ginode(orphan);
326 		idesc.id_type = DATA;
327 		idesc.id_func = chgdd;
328 		idesc.id_number = orphan;
329 		idesc.id_filesize = dp->di_size;
330 		idesc.id_fix = DONTKNOW;
331 		(void)ckinode(dp, &idesc);
332 		dp = ginode(lfdir);
333 		dp->di_nlink++;
334 		inodirty();
335 		lncntp[lfdir]++;
336 		pwarn("DIR I=%u CONNECTED. ", orphan);
337 		printf("PARENT WAS I=%u\n", pdir);
338 		if (preen == 0)
339 			printf("\n");
340 	}
341 	return (1);
342 }
343 
344 /*
345  * make an entry in a directory
346  */
347 makeentry(dp, idesc)
348 	DINODE *dp;
349 	struct inodesc *idesc;
350 {
351 
352 	if ((ckinode(dp, idesc) & ALTERED) != 0)
353 		return (1);
354 	if (expanddir(dp) == 0)
355 		return (0);
356 	idesc->id_filesize = dp->di_size;
357 	return (ckinode(dp, idesc) & ALTERED);
358 }
359 
360 /*
361  * Attempt to expand the size of a directory
362  */
363 expanddir(dp)
364 	register DINODE *dp;
365 {
366 	daddr_t lastbn, newblk;
367 	char *cp, firstblk[DIRBLKSIZ];
368 
369 	lastbn = lblkno(&sblock, dp->di_size);
370 	if (lastbn >= NDADDR - 1)
371 		return (0);
372 	if ((newblk = allocblk(sblock.fs_frag)) == 0)
373 		return (0);
374 	dp->di_db[lastbn + 1] = dp->di_db[lastbn];
375 	dp->di_db[lastbn] = newblk;
376 	dp->di_size += sblock.fs_bsize;
377 	dp->di_blocks += btodb(sblock.fs_bsize);
378 	if (getblk(&fileblk, dp->di_db[lastbn + 1],
379 	    dblksize(&sblock, dp, lastbn + 1)) == NULL)
380 		goto bad;
381 	bcopy(dirblk.b_buf, firstblk, DIRBLKSIZ);
382 	if (getblk(&fileblk, newblk, sblock.fs_bsize) == NULL)
383 		goto bad;
384 	bcopy(firstblk, dirblk.b_buf, DIRBLKSIZ);
385 	for (cp = &dirblk.b_buf[DIRBLKSIZ];
386 	     cp < &dirblk.b_buf[sblock.fs_bsize];
387 	     cp += DIRBLKSIZ)
388 		bcopy((char *)&emptydir, cp, sizeof emptydir);
389 	dirty(&fileblk);
390 	if (getblk(&fileblk, dp->di_db[lastbn + 1],
391 	    dblksize(&sblock, dp, lastbn + 1)) == NULL)
392 		goto bad;
393 	bcopy((char *)&emptydir, dirblk.b_buf, sizeof emptydir);
394 	pwarn("NO SPACE LEFT IN %s", pathname);
395 	if (preen)
396 		printf(" (EXPANDED)\n");
397 	else if (reply("EXPAND") == 0)
398 		goto bad;
399 	dirty(&fileblk);
400 	inodirty();
401 	return (1);
402 bad:
403 	dp->di_db[lastbn] = dp->di_db[lastbn + 1];
404 	dp->di_db[lastbn + 1] = 0;
405 	dp->di_size -= sblock.fs_bsize;
406 	dp->di_blocks -= btodb(sblock.fs_bsize);
407 	freeblk(newblk, sblock.fs_frag);
408 	return (0);
409 }
410 
411 /*
412  * generate a temporary name for the lost+found directory.
413  */
414 lftempname(bufp, ino)
415 	char *bufp;
416 	ino_t ino;
417 {
418 	register ino_t in;
419 	register char *cp;
420 	int namlen;
421 
422 	cp = bufp + 2;
423 	for (in = imax; in > 0; in /= 10)
424 		cp++;
425 	*--cp = 0;
426 	namlen = cp - bufp;
427 	in = ino;
428 	while (cp > bufp) {
429 		*--cp = (in % 10) + '0';
430 		in /= 10;
431 	}
432 	*cp = '#';
433 	return (namlen);
434 }
435