dir.c revision 1.11 1 /* $NetBSD: dir.c,v 1.11 2004/03/22 19:46:53 bouyer Exp $ */
2
3 /*
4 * Copyright (c) 1980, 1986, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32 /*
33 * Copyright (c) 1997 Manuel Bouyer.
34 *
35 * Redistribution and use in source and binary forms, with or without
36 * modification, are permitted provided that the following conditions
37 * are met:
38 * 1. Redistributions of source code must retain the above copyright
39 * notice, this list of conditions and the following disclaimer.
40 * 2. Redistributions in binary form must reproduce the above copyright
41 * notice, this list of conditions and the following disclaimer in the
42 * documentation and/or other materials provided with the distribution.
43 * 3. All advertising materials mentioning features or use of this software
44 * must display the following acknowledgement:
45 * This product includes software developed by Manuel Bouyer.
46 * 4. The name of the author may not be used to endorse or promote products
47 * derived from this software without specific prior written permission.
48 *
49 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
50 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
51 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
52 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
53 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
54 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
55 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
56 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
57 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
58 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
59 */
60
61 #include <sys/cdefs.h>
62 #ifndef lint
63 #if 0
64 static char sccsid[] = "@(#)dir.c 8.5 (Berkeley) 12/8/94";
65 #else
66 __RCSID("$NetBSD: dir.c,v 1.11 2004/03/22 19:46:53 bouyer Exp $");
67 #endif
68 #endif /* not lint */
69
70 #include <sys/param.h>
71 #include <sys/time.h>
72 #include <ufs/ufs/dir.h>
73 #include <ufs/ext2fs/ext2fs_dinode.h>
74 #include <ufs/ext2fs/ext2fs_dir.h>
75 #include <ufs/ext2fs/ext2fs.h>
76
77 #include <ufs/ufs/dinode.h> /* for IFMT & friends */
78
79 #include <stdio.h>
80 #include <stdlib.h>
81 #include <string.h>
82
83 #include "fsck.h"
84 #include "fsutil.h"
85 #include "extern.h"
86
87 char *lfname = "lost+found";
88 int lfmode = 01777;
89 struct ext2fs_dirtemplate emptydir = { 0, DIRBLKSIZ };
90 struct ext2fs_dirtemplate dirhead = {
91 0, 12, 1, EXT2_FT_DIR, ".",
92 0, DIRBLKSIZ - 12, 2, EXT2_FT_DIR, ".."
93 };
94 #undef DIRBLKSIZ
95
96 static int expanddir __P((struct ext2fs_dinode *, char *));
97 static void freedir __P((ino_t, ino_t));
98 static struct ext2fs_direct *fsck_readdir __P((struct inodesc *));
99 static struct bufarea *getdirblk __P((daddr_t, long));
100 static int lftempname __P((char *, ino_t));
101 static int mkentry __P((struct inodesc *));
102 static int chgino __P((struct inodesc *));
103
104 /*
105 * Propagate connected state through the tree.
106 */
107 void
108 propagate()
109 {
110 struct inoinfo **inpp, *inp, *pinp;
111 struct inoinfo **inpend;
112
113 /*
114 * Create a list of children for each directory.
115 */
116 inpend = &inpsort[inplast];
117 for (inpp = inpsort; inpp < inpend; inpp++) {
118 inp = *inpp;
119 if (inp->i_parent == 0 ||
120 inp->i_number == EXT2_ROOTINO)
121 continue;
122 pinp = getinoinfo(inp->i_parent);
123 inp->i_parentp = pinp;
124 inp->i_sibling = pinp->i_child;
125 pinp->i_child = inp;
126 }
127 inp = getinoinfo(EXT2_ROOTINO);
128 while (inp) {
129 statemap[inp->i_number] = DFOUND;
130 if (inp->i_child &&
131 statemap[inp->i_child->i_number] == DSTATE)
132 inp = inp->i_child;
133 else if (inp->i_sibling)
134 inp = inp->i_sibling;
135 else
136 inp = inp->i_parentp;
137 }
138 }
139
140 /*
141 * Scan each entry in a directory block.
142 */
143 int
144 dirscan(idesc)
145 struct inodesc *idesc;
146 {
147 struct ext2fs_direct *dp;
148 struct bufarea *bp;
149 int dsize, n;
150 long blksiz;
151 char *dbuf = NULL;
152
153 if ((dbuf = malloc(sblock.e2fs_bsize)) == NULL) {
154 fprintf(stderr, "out of memory");
155 exit(8);
156 }
157
158 if (idesc->id_type != DATA)
159 errexit("wrong type to dirscan %d\n", idesc->id_type);
160 if (idesc->id_entryno == 0 &&
161 (idesc->id_filesize & (sblock.e2fs_bsize - 1)) != 0)
162 idesc->id_filesize = roundup(idesc->id_filesize, sblock.e2fs_bsize);
163 blksiz = idesc->id_numfrags * sblock.e2fs_bsize;
164 if (chkrange(idesc->id_blkno, idesc->id_numfrags)) {
165 idesc->id_filesize -= blksiz;
166 return (SKIP);
167 }
168 idesc->id_loc = 0;
169 for (dp = fsck_readdir(idesc); dp != NULL; dp = fsck_readdir(idesc)) {
170 dsize = fs2h16(dp->e2d_reclen);
171 memcpy(dbuf, dp, (size_t)dsize);
172 idesc->id_dirp = (struct ext2fs_direct *)dbuf;
173 if ((n = (*idesc->id_func)(idesc)) & ALTERED) {
174 bp = getdirblk(idesc->id_blkno, blksiz);
175 memcpy(bp->b_un.b_buf + idesc->id_loc - dsize, dbuf,
176 (size_t)dsize);
177 dirty(bp);
178 sbdirty();
179 }
180 if (n & STOP) {
181 free(dbuf);
182 return (n);
183 }
184 }
185 free(dbuf);
186 return (idesc->id_filesize > 0 ? KEEPON : STOP);
187 }
188
189 /*
190 * get next entry in a directory.
191 */
192 static struct ext2fs_direct *
193 fsck_readdir(idesc)
194 struct inodesc *idesc;
195 {
196 struct ext2fs_direct *dp, *ndp;
197 struct bufarea *bp;
198 long size, blksiz, fix, dploc;
199
200 blksiz = idesc->id_numfrags * sblock.e2fs_bsize;
201 bp = getdirblk(idesc->id_blkno, blksiz);
202 if (idesc->id_loc % sblock.e2fs_bsize == 0 && idesc->id_filesize > 0 &&
203 idesc->id_loc < blksiz) {
204 dp = (struct ext2fs_direct *)(bp->b_un.b_buf + idesc->id_loc);
205 if (dircheck(idesc, dp))
206 goto dpok;
207 if (idesc->id_fix == IGNORE)
208 return (0);
209 fix = dofix(idesc, "DIRECTORY CORRUPTED");
210 bp = getdirblk(idesc->id_blkno, blksiz);
211 dp = (struct ext2fs_direct *)(bp->b_un.b_buf + idesc->id_loc);
212 dp->e2d_reclen = h2fs16(sblock.e2fs_bsize);
213 dp->e2d_ino = 0;
214 dp->e2d_namlen = 0;
215 dp->e2d_type = 0;
216 dp->e2d_name[0] = '\0';
217 if (fix)
218 dirty(bp);
219 idesc->id_loc += sblock.e2fs_bsize;
220 idesc->id_filesize -= sblock.e2fs_bsize;
221 return (dp);
222 }
223 dpok:
224 if (idesc->id_filesize <= 0 || idesc->id_loc >= blksiz)
225 return NULL;
226 dploc = idesc->id_loc;
227 dp = (struct ext2fs_direct *)(bp->b_un.b_buf + dploc);
228 idesc->id_loc += fs2h16(dp->e2d_reclen);
229 idesc->id_filesize -= fs2h16(dp->e2d_reclen);
230 if ((idesc->id_loc % sblock.e2fs_bsize) == 0)
231 return (dp);
232 ndp = (struct ext2fs_direct *)(bp->b_un.b_buf + idesc->id_loc);
233 if (idesc->id_loc < blksiz && idesc->id_filesize > 0 &&
234 dircheck(idesc, ndp) == 0) {
235 size = sblock.e2fs_bsize - (idesc->id_loc % sblock.e2fs_bsize);
236 idesc->id_loc += size;
237 idesc->id_filesize -= size;
238 if (idesc->id_fix == IGNORE)
239 return (0);
240 fix = dofix(idesc, "DIRECTORY CORRUPTED");
241 bp = getdirblk(idesc->id_blkno, blksiz);
242 dp = (struct ext2fs_direct *)(bp->b_un.b_buf + dploc);
243 dp->e2d_reclen = h2fs16(fs2h16(dp->e2d_reclen) + size);
244 if (fix)
245 dirty(bp);
246 }
247 return (dp);
248 }
249
250 /*
251 * Verify that a directory entry is valid.
252 * This is a superset of the checks made in the kernel.
253 */
254 int
255 dircheck(idesc, dp)
256 struct inodesc *idesc;
257 struct ext2fs_direct *dp;
258 {
259 int size;
260 char *cp;
261 int spaceleft;
262 u_int16_t reclen = fs2h16(dp->e2d_reclen);
263
264 spaceleft = sblock.e2fs_bsize - (idesc->id_loc % sblock.e2fs_bsize);
265 if (fs2h32(dp->e2d_ino) > maxino ||
266 reclen == 0 ||
267 reclen > spaceleft ||
268 (reclen & 0x3) != 0)
269 return (0);
270 if (dp->e2d_ino == 0)
271 return (1);
272 if (sblock.e2fs.e2fs_rev < E2FS_REV1 ||
273 (sblock.e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE) == 0)
274 if (dp->e2d_type != 0)
275 return (1);
276 size = EXT2FS_DIRSIZ(dp->e2d_namlen);
277 if (reclen < size ||
278 idesc->id_filesize < size /* ||
279 dp->e2d_namlen > EXT2FS_MAXNAMLEN */)
280 return (0);
281 for (cp = dp->e2d_name, size = 0; size < dp->e2d_namlen; size++)
282 if (*cp == '\0' || (*cp++ == '/'))
283 return (0);
284 return (1);
285 }
286
287 void
288 direrror(ino, errmesg)
289 ino_t ino;
290 char *errmesg;
291 {
292
293 fileerror(ino, ino, errmesg);
294 }
295
296 void
297 fileerror(cwd, ino, errmesg)
298 ino_t cwd, ino;
299 char *errmesg;
300 {
301 struct ext2fs_dinode *dp;
302 char pathbuf[MAXPATHLEN + 1];
303
304 pwarn("%s ", errmesg);
305 pinode(ino);
306 printf("\n");
307 getpathname(pathbuf, sizeof(pathbuf), cwd, ino);
308 if ((ino < EXT2_FIRSTINO && ino != EXT2_ROOTINO) || ino > maxino) {
309 pfatal("NAME=%s\n", pathbuf);
310 return;
311 }
312 dp = ginode(ino);
313 if (ftypeok(dp))
314 pfatal("%s=%s\n",
315 (fs2h16(dp->e2di_mode) & IFMT) == IFDIR ? "DIR" : "FILE", pathbuf);
316 else
317 pfatal("NAME=%s\n", pathbuf);
318 }
319
320 void
321 adjust(idesc, lcnt)
322 struct inodesc *idesc;
323 short lcnt;
324 {
325 struct ext2fs_dinode *dp;
326
327 dp = ginode(idesc->id_number);
328 if (fs2h16(dp->e2di_nlink) == lcnt) {
329 if (linkup(idesc->id_number, (ino_t)0) == 0)
330 clri(idesc, "UNREF", 0);
331 } else {
332 pwarn("LINK COUNT %s", (lfdir == idesc->id_number) ? lfname :
333 ((fs2h16(dp->e2di_mode) & IFMT) == IFDIR ? "DIR" : "FILE"));
334 pinode(idesc->id_number);
335 printf(" COUNT %d SHOULD BE %d",
336 fs2h16(dp->e2di_nlink), fs2h16(dp->e2di_nlink) - lcnt);
337 if (preen) {
338 if (lcnt < 0) {
339 printf("\n");
340 pfatal("LINK COUNT INCREASING");
341 }
342 printf(" (ADJUSTED)\n");
343 }
344 if (preen || reply("ADJUST") == 1) {
345 dp->e2di_nlink = h2fs16(fs2h16(dp->e2di_nlink) - lcnt);
346 inodirty();
347 }
348 }
349 }
350
351 static int
352 mkentry(idesc)
353 struct inodesc *idesc;
354 {
355 struct ext2fs_direct *dirp = idesc->id_dirp;
356 struct ext2fs_direct newent;
357 int newlen, oldlen;
358
359 newent.e2d_namlen = strlen(idesc->id_name);
360 if (sblock.e2fs.e2fs_rev > E2FS_REV0 &&
361 (sblock.e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE))
362 newent.e2d_type = inot2ext2dt(typemap[idesc->id_parent]);
363 newlen = EXT2FS_DIRSIZ(newent.e2d_namlen);
364 if (dirp->e2d_ino != 0)
365 oldlen = EXT2FS_DIRSIZ(dirp->e2d_namlen);
366 else
367 oldlen = 0;
368 if (fs2h16(dirp->e2d_reclen) - oldlen < newlen)
369 return (KEEPON);
370 newent.e2d_reclen = h2fs16(fs2h16(dirp->e2d_reclen) - oldlen);
371 dirp->e2d_reclen = h2fs16(oldlen);
372 dirp = (struct ext2fs_direct *)(((char *)dirp) + oldlen);
373 dirp->e2d_ino = h2fs32(idesc->id_parent); /* ino to be entered is in id_parent */
374 dirp->e2d_reclen = newent.e2d_reclen;
375 dirp->e2d_namlen = newent.e2d_namlen;
376 dirp->e2d_type = newent.e2d_type;
377 memcpy(dirp->e2d_name, idesc->id_name, (size_t)(dirp->e2d_namlen));
378 return (ALTERED|STOP);
379 }
380
381 static int
382 chgino(idesc)
383 struct inodesc *idesc;
384 {
385 struct ext2fs_direct *dirp = idesc->id_dirp;
386 u_int16_t namlen = dirp->e2d_namlen;
387
388 if (strlen(idesc->id_name) != namlen ||
389 strncmp(dirp->e2d_name, idesc->id_name, (int)namlen))
390 return (KEEPON);
391 dirp->e2d_ino = h2fs32(idesc->id_parent);
392 if (sblock.e2fs.e2fs_rev > E2FS_REV0 &&
393 (sblock.e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE))
394 dirp->e2d_type = inot2ext2dt(typemap[idesc->id_parent]);
395 else
396 dirp->e2d_type = 0;
397 return (ALTERED|STOP);
398 }
399
400 int
401 linkup(orphan, parentdir)
402 ino_t orphan;
403 ino_t parentdir;
404 {
405 struct ext2fs_dinode *dp;
406 int lostdir;
407 ino_t oldlfdir;
408 struct inodesc idesc;
409 char tempname[BUFSIZ];
410
411 memset(&idesc, 0, sizeof(struct inodesc));
412 dp = ginode(orphan);
413 lostdir = (fs2h16(dp->e2di_mode) & IFMT) == IFDIR;
414 pwarn("UNREF %s ", lostdir ? "DIR" : "FILE");
415 pinode(orphan);
416 if (preen && fs2h32(dp->e2di_size) == 0)
417 return (0);
418 if (preen)
419 printf(" (RECONNECTED)\n");
420 else
421 if (reply("RECONNECT") == 0)
422 return (0);
423 if (lfdir == 0) {
424 dp = ginode(EXT2_ROOTINO);
425 idesc.id_name = lfname;
426 idesc.id_type = DATA;
427 idesc.id_func = findino;
428 idesc.id_number = EXT2_ROOTINO;
429 if ((ckinode(dp, &idesc) & FOUND) != 0) {
430 lfdir = idesc.id_parent;
431 } else {
432 pwarn("NO lost+found DIRECTORY");
433 if (preen || reply("CREATE")) {
434 lfdir = allocdir(EXT2_ROOTINO, (ino_t)0, lfmode);
435 if (lfdir != 0) {
436 if (makeentry(EXT2_ROOTINO, lfdir, lfname) != 0) {
437 if (preen)
438 printf(" (CREATED)\n");
439 } else {
440 freedir(lfdir, EXT2_ROOTINO);
441 lfdir = 0;
442 if (preen)
443 printf("\n");
444 }
445 }
446 }
447 }
448 if (lfdir == 0) {
449 pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY");
450 printf("\n\n");
451 return (0);
452 }
453 }
454 dp = ginode(lfdir);
455 if ((fs2h16(dp->e2di_mode) & IFMT) != IFDIR) {
456 pfatal("lost+found IS NOT A DIRECTORY");
457 if (reply("REALLOCATE") == 0)
458 return (0);
459 oldlfdir = lfdir;
460 if ((lfdir = allocdir(EXT2_ROOTINO, (ino_t)0, lfmode)) == 0) {
461 pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n");
462 return (0);
463 }
464 if ((changeino(EXT2_ROOTINO, lfname, lfdir) & ALTERED) == 0) {
465 pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n");
466 return (0);
467 }
468 inodirty();
469 idesc.id_type = ADDR;
470 idesc.id_func = pass4check;
471 idesc.id_number = oldlfdir;
472 adjust(&idesc, lncntp[oldlfdir] + 1);
473 lncntp[oldlfdir] = 0;
474 dp = ginode(lfdir);
475 }
476 if (statemap[lfdir] != DFOUND) {
477 pfatal("SORRY. NO lost+found DIRECTORY\n\n");
478 return (0);
479 }
480 (void)lftempname(tempname, orphan);
481 if (makeentry(lfdir, orphan, tempname) == 0) {
482 pfatal("SORRY. NO SPACE IN lost+found DIRECTORY");
483 printf("\n\n");
484 return (0);
485 }
486 lncntp[orphan]--;
487 if (lostdir) {
488 if ((changeino(orphan, "..", lfdir) & ALTERED) == 0 &&
489 parentdir != (ino_t)-1)
490 (void)makeentry(orphan, lfdir, "..");
491 dp = ginode(lfdir);
492 dp->e2di_nlink = h2fs16(fs2h16(dp->e2di_nlink) +1);
493 inodirty();
494 lncntp[lfdir]++;
495 pwarn("DIR I=%u CONNECTED. ", orphan);
496 if (parentdir != (ino_t)-1)
497 printf("PARENT WAS I=%u\n", parentdir);
498 if (preen == 0)
499 printf("\n");
500 }
501 return (1);
502 }
503
504 /*
505 * fix an entry in a directory.
506 */
507 int
508 changeino(dir, name, newnum)
509 ino_t dir;
510 char *name;
511 ino_t newnum;
512 {
513 struct inodesc idesc;
514
515 memset(&idesc, 0, sizeof(struct inodesc));
516 idesc.id_type = DATA;
517 idesc.id_func = chgino;
518 idesc.id_number = dir;
519 idesc.id_fix = DONTKNOW;
520 idesc.id_name = name;
521 idesc.id_parent = newnum; /* new value for name */
522 return (ckinode(ginode(dir), &idesc));
523 }
524
525 /*
526 * make an entry in a directory
527 */
528 int
529 makeentry(parent, ino, name)
530 ino_t parent, ino;
531 char *name;
532 {
533 struct ext2fs_dinode *dp;
534 struct inodesc idesc;
535 char pathbuf[MAXPATHLEN + 1];
536
537 if ((parent < EXT2_FIRSTINO && parent != EXT2_ROOTINO)
538 || parent >= maxino ||
539 (ino < EXT2_FIRSTINO && ino < EXT2_ROOTINO) || ino >= maxino)
540 return (0);
541 memset(&idesc, 0, sizeof(struct inodesc));
542 idesc.id_type = DATA;
543 idesc.id_func = mkentry;
544 idesc.id_number = parent;
545 idesc.id_parent = ino; /* this is the inode to enter */
546 idesc.id_fix = DONTKNOW;
547 idesc.id_name = name;
548 dp = ginode(parent);
549 if (fs2h32(dp->e2di_size) % sblock.e2fs_bsize) {
550 dp->e2di_size =
551 h2fs32(roundup(fs2h32(dp->e2di_size), sblock.e2fs_bsize));
552 inodirty();
553 }
554 if ((ckinode(dp, &idesc) & ALTERED) != 0)
555 return (1);
556 getpathname(pathbuf, sizeof(pathbuf), parent, parent);
557 dp = ginode(parent);
558 if (expanddir(dp, pathbuf) == 0)
559 return (0);
560 return (ckinode(dp, &idesc) & ALTERED);
561 }
562
563 /*
564 * Attempt to expand the size of a directory
565 */
566 static int
567 expanddir(dp, name)
568 struct ext2fs_dinode *dp;
569 char *name;
570 {
571 daddr_t lastbn, newblk;
572 struct bufarea *bp;
573 char *firstblk;
574
575 if ((firstblk = malloc(sblock.e2fs_bsize)) == NULL) {
576 fprintf(stderr, "out of memory");
577 exit(8);
578 }
579
580 lastbn = lblkno(&sblock, fs2h32(dp->e2di_size));
581 if (lastbn >= NDADDR - 1 || fs2h32(dp->e2di_blocks[lastbn]) == 0 ||
582 fs2h32(dp->e2di_size) == 0)
583 return (0);
584 if ((newblk = allocblk()) == 0)
585 return (0);
586 dp->e2di_blocks[lastbn + 1] = dp->e2di_blocks[lastbn];
587 dp->e2di_blocks[lastbn] = h2fs32(newblk);
588 dp->e2di_size = h2fs32(fs2h32(dp->e2di_size) + sblock.e2fs_bsize);
589 dp->e2di_nblock = h2fs32(fs2h32(dp->e2di_nblock) + 1);
590 bp = getdirblk(fs2h32(dp->e2di_blocks[lastbn + 1]),
591 sblock.e2fs_bsize);
592 if (bp->b_errs)
593 goto bad;
594 memcpy(firstblk, bp->b_un.b_buf, sblock.e2fs_bsize);
595 bp = getdirblk(newblk, sblock.e2fs_bsize);
596 if (bp->b_errs)
597 goto bad;
598 memcpy(bp->b_un.b_buf, firstblk, sblock.e2fs_bsize);
599 dirty(bp);
600 bp = getdirblk(fs2h32(dp->e2di_blocks[lastbn + 1]),
601 sblock.e2fs_bsize);
602 if (bp->b_errs)
603 goto bad;
604 emptydir.dot_reclen = h2fs16(sblock.e2fs_bsize);
605 memcpy(bp->b_un.b_buf, &emptydir, sizeof emptydir);
606 pwarn("NO SPACE LEFT IN %s", name);
607 if (preen)
608 printf(" (EXPANDED)\n");
609 else if (reply("EXPAND") == 0)
610 goto bad;
611 dirty(bp);
612 inodirty();
613 return (1);
614 bad:
615 dp->e2di_blocks[lastbn] = dp->e2di_blocks[lastbn + 1];
616 dp->e2di_blocks[lastbn + 1] = 0;
617 dp->e2di_size = h2fs32(fs2h32(dp->e2di_size) - sblock.e2fs_bsize);
618 dp->e2di_nblock = h2fs32(fs2h32(dp->e2di_nblock) - 1);
619 freeblk(newblk);
620 return (0);
621 }
622
623 /*
624 * allocate a new directory
625 */
626 int
627 allocdir(parent, request, mode)
628 ino_t parent, request;
629 int mode;
630 {
631 ino_t ino;
632 struct ext2fs_dinode *dp;
633 struct bufarea *bp;
634 struct ext2fs_dirtemplate *dirp;
635
636 ino = allocino(request, IFDIR|mode);
637 dirhead.dot_reclen = h2fs16(12); /* XXX */
638 dirhead.dotdot_reclen = h2fs16(sblock.e2fs_bsize - 12); /* XXX */
639 dirhead.dot_namlen = 1;
640 if (sblock.e2fs.e2fs_rev > E2FS_REV0 &&
641 (sblock.e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE))
642 dirhead.dot_type = EXT2_FT_DIR;
643 else
644 dirhead.dot_type = 0;
645 dirhead.dotdot_namlen = 2;
646 if (sblock.e2fs.e2fs_rev > E2FS_REV0 &&
647 (sblock.e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE))
648 dirhead.dotdot_type = EXT2_FT_DIR;
649 else
650 dirhead.dotdot_type = 0;
651 dirp = &dirhead;
652 dirp->dot_ino = h2fs32(ino);
653 dirp->dotdot_ino = h2fs32(parent);
654 dp = ginode(ino);
655 bp = getdirblk(fs2h32(dp->e2di_blocks[0]), sblock.e2fs_bsize);
656 if (bp->b_errs) {
657 freeino(ino);
658 return (0);
659 }
660 memcpy(bp->b_un.b_buf, dirp, sizeof(struct ext2fs_dirtemplate));
661 dirty(bp);
662 dp->e2di_nlink = h2fs16(2);
663 inodirty();
664 if (ino == EXT2_ROOTINO) {
665 lncntp[ino] = fs2h16(dp->e2di_nlink);
666 cacheino(dp, ino);
667 return(ino);
668 }
669 if (statemap[parent] != DSTATE && statemap[parent] != DFOUND) {
670 freeino(ino);
671 return (0);
672 }
673 cacheino(dp, ino);
674 statemap[ino] = statemap[parent];
675 if (statemap[ino] == DSTATE) {
676 lncntp[ino] = fs2h16(dp->e2di_nlink);
677 lncntp[parent]++;
678 }
679 dp = ginode(parent);
680 dp->e2di_nlink = h2fs16(fs2h16(dp->e2di_nlink) + 1);
681 inodirty();
682 return (ino);
683 }
684
685 /*
686 * free a directory inode
687 */
688 static void
689 freedir(ino, parent)
690 ino_t ino, parent;
691 {
692 struct ext2fs_dinode *dp;
693
694 if (ino != parent) {
695 dp = ginode(parent);
696 dp->e2di_nlink = h2fs16(fs2h16(dp->e2di_nlink) - 1);
697 inodirty();
698 }
699 freeino(ino);
700 }
701
702 /*
703 * generate a temporary name for the lost+found directory.
704 */
705 static int
706 lftempname(bufp, ino)
707 char *bufp;
708 ino_t ino;
709 {
710 ino_t in;
711 char *cp;
712 int namlen;
713
714 cp = bufp + 2;
715 for (in = maxino; in > 0; in /= 10)
716 cp++;
717 *--cp = 0;
718 namlen = cp - bufp;
719 in = ino;
720 while (cp > bufp) {
721 *--cp = (in % 10) + '0';
722 in /= 10;
723 }
724 *cp = '#';
725 return (namlen);
726 }
727
728 /*
729 * Get a directory block.
730 * Insure that it is held until another is requested.
731 */
732 static struct bufarea *
733 getdirblk(blkno, size)
734 daddr_t blkno;
735 long size;
736 {
737
738 if (pdirbp != 0)
739 pdirbp->b_flags &= ~B_INUSE;
740 pdirbp = getdatablk(blkno, size);
741 return (pdirbp);
742 }
743