1 /* $NetBSD: dir.c,v 1.23 2008/03/16 23:17:55 lukem Exp $ */
4 * Copyright (c) 1980, 1986, 1993
5 * The Regents of the University of California. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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.
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
32 #include <sys/types.h>
33 #include <sys/param.h>
36 #include <sys/mount.h>
38 #include <ufs/ufs/inode.h>
39 #include <ufs/ufs/dir.h>
40 #include <ufs/ufs/ufsmount.h>
41 #include <ufs/lfs/lfs.h>
56 const char *lfname
= "lost+found";
58 struct dirtemplate emptydir
= {
60 .dot_reclen
= DIRBLKSIZ
,
62 struct dirtemplate dirhead
= {
69 .dotdot_reclen
= DIRBLKSIZ
- 12,
70 .dotdot_type
= DT_DIR
,
74 struct odirtemplate odirhead
= {
80 .dotdot_reclen
= DIRBLKSIZ
- 12,
85 static int expanddir(struct uvnode
*, struct ufs1_dinode
*, char *);
86 static void freedir(ino_t
, ino_t
);
87 static struct direct
*fsck_readdir(struct uvnode
*, struct inodesc
*);
88 static int lftempname(char *, ino_t
);
89 static int mkentry(struct inodesc
*);
90 static int chgino(struct inodesc
*);
93 * Propagate connected state through the tree.
98 struct inoinfo
**inpp
, *inp
, *pinp
;
99 struct inoinfo
**inpend
;
102 * Create a list of children for each directory.
104 inpend
= &inpsort
[inplast
];
105 for (inpp
= inpsort
; inpp
< inpend
; inpp
++) {
107 if (inp
->i_parent
== 0 ||
108 inp
->i_number
== ROOTINO
)
110 pinp
= getinoinfo(inp
->i_parent
);
111 inp
->i_parentp
= pinp
;
112 inp
->i_sibling
= pinp
->i_child
;
115 inp
= getinoinfo(ROOTINO
);
117 statemap
[inp
->i_number
] = DFOUND
;
119 statemap
[inp
->i_child
->i_number
] == DSTATE
)
121 else if (inp
->i_sibling
)
122 inp
= inp
->i_sibling
;
124 inp
= inp
->i_parentp
;
129 * Scan each entry in a directory block.
132 dirscan(struct inodesc
*idesc
)
138 char dbuf
[DIRBLKSIZ
];
141 if (idesc
->id_type
!= DATA
)
142 errexit("wrong type to dirscan %d", idesc
->id_type
);
143 if (idesc
->id_entryno
== 0 &&
144 (idesc
->id_filesize
& (DIRBLKSIZ
- 1)) != 0)
145 idesc
->id_filesize
= roundup(idesc
->id_filesize
, DIRBLKSIZ
);
146 blksiz
= idesc
->id_numfrags
* fs
->lfs_fsize
;
147 if (chkrange(idesc
->id_blkno
, fragstofsb(fs
, idesc
->id_numfrags
))) {
148 idesc
->id_filesize
-= blksiz
;
153 vp
= vget(fs
, idesc
->id_number
);
154 for (dp
= fsck_readdir(vp
, idesc
); dp
!= NULL
;
155 dp
= fsck_readdir(vp
, idesc
)) {
156 dsize
= dp
->d_reclen
;
157 memcpy(dbuf
, dp
, (size_t) dsize
);
158 idesc
->id_dirp
= (struct direct
*) dbuf
;
159 if ((n
= (*idesc
->id_func
) (idesc
)) & ALTERED
) {
160 bread(vp
, idesc
->id_lblkno
, blksiz
, NOCRED
, 0, &bp
);
161 memcpy(bp
->b_data
+ idesc
->id_loc
- dsize
, dbuf
,
169 return (idesc
->id_filesize
> 0 ? KEEPON
: STOP
);
173 * get next entry in a directory.
175 static struct direct
*
176 fsck_readdir(struct uvnode
*vp
, struct inodesc
*idesc
)
178 struct direct
*dp
, *ndp
;
180 long size
, blksiz
, fix
, dploc
;
182 blksiz
= idesc
->id_numfrags
* fs
->lfs_fsize
;
183 bread(vp
, idesc
->id_lblkno
, blksiz
, NOCRED
, 0, &bp
);
184 if (idesc
->id_loc
% DIRBLKSIZ
== 0 && idesc
->id_filesize
> 0 &&
185 idesc
->id_loc
< blksiz
) {
186 dp
= (struct direct
*) (bp
->b_data
+ idesc
->id_loc
);
187 if (dircheck(idesc
, dp
))
190 if (idesc
->id_fix
== IGNORE
)
192 fix
= dofix(idesc
, "DIRECTORY CORRUPTED");
193 bread(vp
, idesc
->id_lblkno
, blksiz
, NOCRED
, 0, &bp
);
194 dp
= (struct direct
*) (bp
->b_data
+ idesc
->id_loc
);
195 dp
->d_reclen
= DIRBLKSIZ
;
199 dp
->d_name
[0] = '\0';
204 idesc
->id_loc
+= DIRBLKSIZ
;
205 idesc
->id_filesize
-= DIRBLKSIZ
;
209 if (idesc
->id_filesize
<= 0 || idesc
->id_loc
>= blksiz
) {
213 dploc
= idesc
->id_loc
;
214 dp
= (struct direct
*) (bp
->b_data
+ dploc
);
215 idesc
->id_loc
+= dp
->d_reclen
;
216 idesc
->id_filesize
-= dp
->d_reclen
;
217 if ((idesc
->id_loc
% DIRBLKSIZ
) == 0) {
221 ndp
= (struct direct
*) (bp
->b_data
+ idesc
->id_loc
);
222 if (idesc
->id_loc
< blksiz
&& idesc
->id_filesize
> 0 &&
223 dircheck(idesc
, ndp
) == 0) {
225 size
= DIRBLKSIZ
- (idesc
->id_loc
% DIRBLKSIZ
);
226 idesc
->id_loc
+= size
;
227 idesc
->id_filesize
-= size
;
228 if (idesc
->id_fix
== IGNORE
)
230 fix
= dofix(idesc
, "DIRECTORY CORRUPTED");
231 bread(vp
, idesc
->id_lblkno
, blksiz
, NOCRED
, 0, &bp
);
232 dp
= (struct direct
*) (bp
->b_data
+ dploc
);
233 dp
->d_reclen
+= size
;
245 * Verify that a directory entry is valid.
246 * This is a superset of the checks made in the kernel.
249 dircheck(struct inodesc
*idesc
, struct direct
*dp
)
256 spaceleft
= DIRBLKSIZ
- (idesc
->id_loc
% DIRBLKSIZ
);
257 if (dp
->d_ino
>= maxino
||
259 dp
->d_reclen
> spaceleft
||
260 (dp
->d_reclen
& 0x3) != 0) {
261 pwarn("ino too large, reclen=0, reclen>space, or reclen&3!=0\n");
262 pwarn("dp->d_ino = 0x%x\tdp->d_reclen = 0x%x\n",
263 dp
->d_ino
, dp
->d_reclen
);
264 pwarn("maxino = %llu\tspaceleft = 0x%x\n",
265 (unsigned long long)maxino
, spaceleft
);
270 size
= DIRSIZ(0, dp
, 0);
271 namlen
= dp
->d_namlen
;
273 if (dp
->d_reclen
< size
||
274 idesc
->id_filesize
< size
||
275 /* namlen > MAXNAMLEN || */
277 printf("reclen<size, filesize<size, namlen too large, or type>15\n");
280 for (cp
= dp
->d_name
, size
= 0; size
< namlen
; size
++)
281 if (*cp
== '\0' || (*cp
++ == '/')) {
282 printf("name contains NUL or /\n");
286 printf("name size misstated\n");
293 direrror(ino_t ino
, const char *errmesg
)
296 fileerror(ino
, ino
, errmesg
);
300 fileerror(ino_t cwd
, ino_t ino
, const char *errmesg
)
302 char pathbuf
[MAXPATHLEN
+ 1];
305 pwarn("%s ", errmesg
);
308 pwarn("PARENT=%lld\n", (long long)cwd
);
309 getpathname(pathbuf
, sizeof(pathbuf
), cwd
, ino
);
310 if (ino
< ROOTINO
|| ino
>= maxino
) {
311 pfatal("NAME=%s\n", pathbuf
);
316 pfatal("INO is NULL\n");
318 if (ftypeok(VTOD(vp
)))
320 (VTOI(vp
)->i_ffs1_mode
& IFMT
) == IFDIR
?
321 "DIR" : "FILE", pathbuf
);
323 pfatal("NAME=%s\n", pathbuf
);
328 adjust(struct inodesc
*idesc
, short lcnt
)
331 struct ufs1_dinode
*dp
;
333 vp
= vget(fs
, idesc
->id_number
);
335 if (dp
->di_nlink
== lcnt
) {
336 if (linkup(idesc
->id_number
, (ino_t
) 0) == 0)
337 clri(idesc
, "UNREF", 0);
339 pwarn("LINK COUNT %s", (lfdir
== idesc
->id_number
) ? lfname
:
340 ((dp
->di_mode
& IFMT
) == IFDIR
? "DIR" : "FILE"));
341 pinode(idesc
->id_number
);
342 printf(" COUNT %d SHOULD BE %d",
343 dp
->di_nlink
, dp
->di_nlink
- lcnt
);
347 pfatal("LINK COUNT INCREASING");
349 printf(" (ADJUSTED)\n");
351 if (preen
|| reply("ADJUST") == 1) {
352 dp
->di_nlink
-= lcnt
;
359 mkentry(struct inodesc
*idesc
)
361 struct direct
*dirp
= idesc
->id_dirp
;
362 struct direct newent
;
365 newent
.d_namlen
= strlen(idesc
->id_name
);
366 newlen
= DIRSIZ(0, &newent
, 0);
367 if (dirp
->d_ino
!= 0)
368 oldlen
= DIRSIZ(0, dirp
, 0);
371 if (dirp
->d_reclen
- oldlen
< newlen
)
373 newent
.d_reclen
= dirp
->d_reclen
- oldlen
;
374 dirp
->d_reclen
= oldlen
;
375 dirp
= (struct direct
*) (((char *) dirp
) + oldlen
);
376 dirp
->d_ino
= idesc
->id_parent
; /* ino to be entered is in id_parent */
377 dirp
->d_reclen
= newent
.d_reclen
;
378 dirp
->d_type
= typemap
[idesc
->id_parent
];
379 dirp
->d_namlen
= newent
.d_namlen
;
380 memcpy(dirp
->d_name
, idesc
->id_name
, (size_t) dirp
->d_namlen
+ 1);
381 return (ALTERED
| STOP
);
385 chgino(struct inodesc
*idesc
)
387 struct direct
*dirp
= idesc
->id_dirp
;
389 if (memcmp(dirp
->d_name
, idesc
->id_name
, (int) dirp
->d_namlen
+ 1))
391 dirp
->d_ino
= idesc
->id_parent
;
392 dirp
->d_type
= typemap
[idesc
->id_parent
];
393 return (ALTERED
| STOP
);
397 linkup(ino_t orphan
, ino_t parentdir
)
399 struct ufs1_dinode
*dp
;
402 struct inodesc idesc
;
403 char tempname
[BUFSIZ
];
406 memset(&idesc
, 0, sizeof(struct inodesc
));
407 vp
= vget(fs
, orphan
);
409 lostdir
= (dp
->di_mode
& IFMT
) == IFDIR
;
410 pwarn("UNREF %s ", lostdir
? "DIR" : "FILE");
412 if (preen
&& dp
->di_size
== 0)
415 printf(" (RECONNECTED)\n");
416 else if (reply("RECONNECT") == 0)
419 dp
= ginode(ROOTINO
);
420 idesc
.id_name
= lfname
;
421 idesc
.id_type
= DATA
;
422 idesc
.id_func
= findino
;
423 idesc
.id_number
= ROOTINO
;
424 if ((ckinode(dp
, &idesc
) & FOUND
) != 0) {
425 lfdir
= idesc
.id_parent
;
427 pwarn("NO lost+found DIRECTORY");
428 if (preen
|| reply("CREATE")) {
429 lfdir
= allocdir(ROOTINO
, (ino_t
) 0, lfmode
);
431 if (makeentry(ROOTINO
, lfdir
, lfname
) != 0) {
433 printf(" (CREATED)\n");
435 freedir(lfdir
, ROOTINO
);
444 pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY");
449 vp
= vget(fs
, lfdir
);
451 if ((dp
->di_mode
& IFMT
) != IFDIR
) {
452 pfatal("lost+found IS NOT A DIRECTORY");
453 if (reply("REALLOCATE") == 0)
456 if ((lfdir
= allocdir(ROOTINO
, (ino_t
) 0, lfmode
)) == 0) {
457 pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n");
460 if ((changeino(ROOTINO
, lfname
, lfdir
) & ALTERED
) == 0) {
461 pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n");
465 idesc
.id_type
= ADDR
;
466 idesc
.id_func
= pass4check
;
467 idesc
.id_number
= oldlfdir
;
468 adjust(&idesc
, lncntp
[oldlfdir
] + 1);
469 lncntp
[oldlfdir
] = 0;
470 vp
= vget(fs
, lfdir
);
473 if (statemap
[lfdir
] != DFOUND
) {
474 pfatal("SORRY. NO lost+found DIRECTORY\n\n");
477 (void) lftempname(tempname
, orphan
);
478 if (makeentry(lfdir
, orphan
, tempname
) == 0) {
479 pfatal("SORRY. NO SPACE IN lost+found DIRECTORY");
485 if ((changeino(orphan
, "..", lfdir
) & ALTERED
) == 0 &&
486 parentdir
!= (ino_t
) - 1)
487 (void) makeentry(orphan
, lfdir
, "..");
488 vp
= vget(fs
, lfdir
);
489 VTOI(vp
)->i_ffs1_nlink
++;
492 pwarn("DIR I=%llu CONNECTED. ", (unsigned long long)orphan
);
493 if (parentdir
!= (ino_t
) - 1)
494 printf("PARENT WAS I=%llu\n",
495 (unsigned long long)parentdir
);
503 * fix an entry in a directory.
506 changeino(ino_t dir
, const char *name
, ino_t newnum
)
508 struct inodesc idesc
;
510 memset(&idesc
, 0, sizeof(struct inodesc
));
511 idesc
.id_type
= DATA
;
512 idesc
.id_func
= chgino
;
513 idesc
.id_number
= dir
;
514 idesc
.id_fix
= DONTKNOW
;
515 idesc
.id_name
= name
;
516 idesc
.id_parent
= newnum
; /* new value for name */
518 return (ckinode(ginode(dir
), &idesc
));
522 * make an entry in a directory
525 makeentry(ino_t parent
, ino_t ino
, const char *name
)
527 struct ufs1_dinode
*dp
;
528 struct inodesc idesc
;
529 char pathbuf
[MAXPATHLEN
+ 1];
532 if (parent
< ROOTINO
|| parent
>= maxino
||
533 ino
< ROOTINO
|| ino
>= maxino
)
535 memset(&idesc
, 0, sizeof(struct inodesc
));
536 idesc
.id_type
= DATA
;
537 idesc
.id_func
= mkentry
;
538 idesc
.id_number
= parent
;
539 idesc
.id_parent
= ino
; /* this is the inode to enter */
540 idesc
.id_fix
= DONTKNOW
;
541 idesc
.id_name
= name
;
542 vp
= vget(fs
, parent
);
544 if (dp
->di_size
% DIRBLKSIZ
) {
545 dp
->di_size
= roundup(dp
->di_size
, DIRBLKSIZ
);
548 if ((ckinode(dp
, &idesc
) & ALTERED
) != 0)
550 getpathname(pathbuf
, sizeof(pathbuf
), parent
, parent
);
551 vp
= vget(fs
, parent
);
553 if (expanddir(vp
, dp
, pathbuf
) == 0)
555 return (ckinode(dp
, &idesc
) & ALTERED
);
559 * Attempt to expand the size of a directory
562 expanddir(struct uvnode
*vp
, struct ufs1_dinode
*dp
, char *name
)
566 char *cp
, firstblk
[DIRBLKSIZ
];
568 lastbn
= lblkno(fs
, dp
->di_size
);
569 if (lastbn
>= NDADDR
- 1 || dp
->di_db
[lastbn
] == 0 || dp
->di_size
== 0)
571 dp
->di_db
[lastbn
+ 1] = dp
->di_db
[lastbn
];
572 dp
->di_db
[lastbn
] = 0;
573 bp
= getblk(vp
, lastbn
, fs
->lfs_bsize
);
575 dp
->di_size
+= fs
->lfs_bsize
;
576 dp
->di_blocks
+= btofsb(fs
, fs
->lfs_bsize
);
577 bread(vp
, dp
->di_db
[lastbn
+ 1],
578 (long) dblksize(fs
, dp
, lastbn
+ 1), NOCRED
, 0, &bp
);
579 if (bp
->b_flags
& B_ERROR
)
581 memcpy(firstblk
, bp
->b_data
, DIRBLKSIZ
);
582 bread(vp
, lastbn
, fs
->lfs_bsize
, NOCRED
, 0, &bp
);
583 if (bp
->b_flags
& B_ERROR
)
585 memcpy(bp
->b_data
, firstblk
, DIRBLKSIZ
);
586 for (cp
= &bp
->b_data
[DIRBLKSIZ
];
587 cp
< &bp
->b_data
[fs
->lfs_bsize
];
589 memcpy(cp
, &emptydir
, sizeof emptydir
);
591 bread(vp
, dp
->di_db
[lastbn
+ 1],
592 (long) dblksize(fs
, dp
, lastbn
+ 1), NOCRED
, 0, &bp
);
593 if (bp
->b_flags
& B_ERROR
)
595 memcpy(bp
->b_data
, &emptydir
, sizeof emptydir
);
596 pwarn("NO SPACE LEFT IN %s", name
);
598 printf(" (EXPANDED)\n");
599 else if (reply("EXPAND") == 0)
605 dp
->di_db
[lastbn
] = dp
->di_db
[lastbn
+ 1];
606 dp
->di_db
[lastbn
+ 1] = 0;
607 dp
->di_size
-= fs
->lfs_bsize
;
608 dp
->di_blocks
-= btofsb(fs
, fs
->lfs_bsize
);
613 * allocate a new directory
616 allocdir(ino_t parent
, ino_t request
, int mode
)
620 struct ufs1_dinode
*dp
;
622 struct dirtemplate
*dirp
;
625 ino
= allocino(request
, IFDIR
| mode
);
628 dirp
->dotdot_ino
= parent
;
631 bread(vp
, dp
->di_db
[0], fs
->lfs_fsize
, NOCRED
, 0, &bp
);
632 if (bp
->b_flags
& B_ERROR
) {
637 memcpy(bp
->b_data
, dirp
, sizeof(struct dirtemplate
));
638 for (cp
= &bp
->b_data
[DIRBLKSIZ
];
639 cp
< &bp
->b_data
[fs
->lfs_fsize
];
641 memcpy(cp
, &emptydir
, sizeof emptydir
);
645 if (ino
== ROOTINO
) {
646 lncntp
[ino
] = dp
->di_nlink
;
650 if (statemap
[parent
] != DSTATE
&& statemap
[parent
] != DFOUND
) {
655 statemap
[ino
] = statemap
[parent
];
656 if (statemap
[ino
] == DSTATE
) {
657 lncntp
[ino
] = dp
->di_nlink
;
660 vp
= vget(fs
, parent
);
668 * free a directory inode
671 freedir(ino_t ino
, ino_t parent
)
676 vp
= vget(fs
, parent
);
677 VTOI(vp
)->i_ffs1_nlink
--;
684 * generate a temporary name for the lost+found directory.
687 lftempname(char *bufp
, ino_t ino
)
694 for (in
= maxino
; in
> 0; in
/= 10)
700 *--cp
= (in
% 10) + '0';