Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sbin/fsck_ffs/dir.c
39475 views
1
/*-
2
* SPDX-License-Identifier: BSD-3-Clause
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
#include <sys/param.h>
33
#include <sys/time.h>
34
#include <sys/types.h>
35
#include <sys/sysctl.h>
36
37
#include <ufs/ufs/dinode.h>
38
#include <ufs/ufs/dir.h>
39
#include <ufs/ffs/fs.h>
40
41
#include <err.h>
42
#include <string.h>
43
44
#include "fsck.h"
45
46
static struct dirtemplate emptydir = {
47
0, DIRBLKSIZ, DT_UNKNOWN, 0, "",
48
0, 0, DT_UNKNOWN, 0, ""
49
};
50
static struct dirtemplate dirhead = {
51
0, 12, DT_DIR, 1, ".",
52
0, DIRBLKSIZ - 12, DT_DIR, 2, ".."
53
};
54
55
static int chgino(struct inodesc *);
56
static int dircheck(struct inodesc *, struct bufarea *, struct direct *);
57
static int expanddir(struct inode *ip, char *name);
58
static struct direct *fsck_readdir(struct inodesc *);
59
static struct bufarea *getdirblk(ufs2_daddr_t blkno, long size);
60
static int lftempname(char *bufp, ino_t ino);
61
static int mkentry(struct inodesc *);
62
63
/*
64
* Propagate connected state through the tree.
65
*/
66
void
67
propagate(void)
68
{
69
struct inoinfo **inpp, *inp;
70
struct inoinfo **inpend;
71
long change;
72
73
inpend = &inpsort[inplast];
74
do {
75
change = 0;
76
for (inpp = inpsort; inpp < inpend; inpp++) {
77
inp = *inpp;
78
if (inp->i_parent == 0)
79
continue;
80
if (inoinfo(inp->i_parent)->ino_state == DFOUND &&
81
INO_IS_DUNFOUND(inp->i_number)) {
82
inoinfo(inp->i_number)->ino_state = DFOUND;
83
check_dirdepth(inp);
84
change++;
85
}
86
}
87
} while (change > 0);
88
}
89
90
/*
91
* Check that the recorded depth of the directory is correct.
92
*/
93
void
94
check_dirdepth(struct inoinfo *inp)
95
{
96
struct inoinfo *parentinp;
97
struct inode ip;
98
union dinode *dp;
99
int saveresolved;
100
size_t size;
101
static int updateasked, dirdepthupdate;
102
103
if ((parentinp = getinoinfo(inp->i_parent)) == NULL) {
104
pfatal("check_dirdepth: UNKNOWN PARENT DIR");
105
return;
106
}
107
/*
108
* If depth is correct, nothing to do.
109
*/
110
if (parentinp->i_depth + 1 == inp->i_depth)
111
return;
112
/*
113
* Only the root inode should have depth of 0, so if any other
114
* directory has a depth of 0 then this is an old filesystem
115
* that has not been tracking directory depth. Ask just once
116
* whether it should start tracking directory depth.
117
*/
118
if (inp->i_depth == 0 && updateasked == 0) {
119
updateasked = 1;
120
if (preen) {
121
pwarn("UPDATING FILESYSTEM TO TRACK DIRECTORY DEPTH\n");
122
dirdepthupdate = 1;
123
} else {
124
/*
125
* The file system can be marked clean even if
126
* a directory does not have the right depth.
127
* Hence, resolved should not be cleared when
128
* the filesystem does not update directory depths.
129
*/
130
saveresolved = resolved;
131
dirdepthupdate =
132
reply("UPDATE FILESYSTEM TO TRACK DIRECTORY DEPTH");
133
resolved = saveresolved;
134
}
135
}
136
/*
137
* If we are not converting or we are running in no-write mode
138
* there is nothing more to do.
139
*/
140
if ((inp->i_depth == 0 && dirdepthupdate == 0) ||
141
(fswritefd < 0 && bkgrdflag == 0))
142
return;
143
/*
144
* Individual directory at wrong depth. Report it and correct if
145
* in preen mode or ask if in interactive mode. Note that if a
146
* directory is renamed to a new location that is at a different
147
* level in the tree, its depth will be recalculated, but none of
148
* the directories that it contains will be updated. Thus it is
149
* not unexpected to find directories with incorrect depths. No
150
* operational harm will come from this though new directory
151
* placement in the subtree may not be as optimal until the depths
152
* of the affected directories are corrected.
153
*
154
* To avoid much spurious output on otherwise clean filesystems
155
* we only generate detailed output when the debug flag is given.
156
*/
157
ginode(inp->i_number, &ip);
158
dp = ip.i_dp;
159
if (inp->i_depth != 0 && debug) {
160
pwarn("DIRECTORY");
161
prtinode(&ip);
162
printf(" DEPTH %d SHOULD BE %d", inp->i_depth,
163
parentinp->i_depth + 1);
164
if (preen == 0 && reply("ADJUST") == 0) {
165
irelse(&ip);
166
return;
167
}
168
if (preen)
169
printf(" (ADJUSTED)\n");
170
}
171
inp->i_depth = parentinp->i_depth + 1;
172
if (bkgrdflag == 0) {
173
DIP_SET(dp, di_dirdepth, inp->i_depth);
174
inodirty(&ip);
175
} else {
176
cmd.value = inp->i_number;
177
cmd.size = (int64_t)inp->i_depth - DIP(dp, di_dirdepth);
178
if (debug)
179
printf("adjdepth ino %ld amt %jd\n", (long)cmd.value,
180
(intmax_t)cmd.size);
181
size = MIBSIZE;
182
if (sysctlnametomib("vfs.ffs.adjdepth", adjdepth, &size) < 0 ||
183
sysctl(adjdepth, MIBSIZE, 0, 0, &cmd, sizeof cmd) == -1)
184
rwerror("ADJUST INODE DEPTH", cmd.value);
185
}
186
irelse(&ip);
187
}
188
189
/*
190
* Scan each entry in a directory block.
191
*/
192
int
193
dirscan(struct inodesc *idesc)
194
{
195
struct direct *dp;
196
struct bufarea *bp;
197
u_int dsize, n;
198
long blksiz;
199
char dbuf[DIRBLKSIZ];
200
201
if (idesc->id_type != DATA)
202
errx(EEXIT, "wrong type to dirscan %d", idesc->id_type);
203
if (idesc->id_entryno == 0 &&
204
(idesc->id_filesize & (DIRBLKSIZ - 1)) != 0)
205
idesc->id_filesize = roundup(idesc->id_filesize, DIRBLKSIZ);
206
blksiz = idesc->id_numfrags * sblock.fs_fsize;
207
if (chkrange(idesc->id_blkno, idesc->id_numfrags)) {
208
idesc->id_filesize -= blksiz;
209
return (SKIP);
210
}
211
idesc->id_loc = 0;
212
for (dp = fsck_readdir(idesc); dp != NULL; dp = fsck_readdir(idesc)) {
213
dsize = dp->d_reclen;
214
if (dsize > sizeof(dbuf))
215
dsize = sizeof(dbuf);
216
memmove(dbuf, dp, (size_t)dsize);
217
idesc->id_dirp = (struct direct *)dbuf;
218
if ((n = (*idesc->id_func)(idesc)) & ALTERED) {
219
bp = getdirblk(idesc->id_blkno, blksiz);
220
if (bp->b_errs != 0)
221
return (STOP);
222
memmove(bp->b_un.b_buf + idesc->id_loc - dsize, dbuf,
223
(size_t)dsize);
224
dirty(bp);
225
sbdirty();
226
}
227
if (n & STOP)
228
return (n);
229
}
230
return (idesc->id_filesize > 0 ? KEEPON : STOP);
231
}
232
233
/*
234
* Get and verify the next entry in a directory.
235
* We also verify that if there is another entry in the block that it is
236
* valid, so if it is not valid it can be subsumed into the current entry.
237
*/
238
static struct direct *
239
fsck_readdir(struct inodesc *idesc)
240
{
241
struct direct *dp, *ndp;
242
struct bufarea *bp;
243
long size, blksiz, subsume_ndp;
244
245
subsume_ndp = 0;
246
blksiz = idesc->id_numfrags * sblock.fs_fsize;
247
if (idesc->id_filesize <= 0 || idesc->id_loc >= blksiz)
248
return (NULL);
249
bp = getdirblk(idesc->id_blkno, blksiz);
250
if (bp->b_errs != 0)
251
return (NULL);
252
dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc);
253
/*
254
* Only need to check current entry if it is the first in the
255
* block, as later entries will have been checked in the
256
* previous call to this function.
257
*/
258
if (idesc->id_loc % DIRBLKSIZ != 0 || dircheck(idesc, bp, dp) != 0) {
259
/*
260
* Current entry is good, update to point at next.
261
*/
262
idesc->id_loc += dp->d_reclen;
263
idesc->id_filesize -= dp->d_reclen;
264
/*
265
* If at end of directory block, just return this entry.
266
*/
267
if (idesc->id_filesize <= 0 || idesc->id_loc >= blksiz ||
268
idesc->id_loc % DIRBLKSIZ == 0)
269
return (dp);
270
/*
271
* If the next entry good, return this entry.
272
*/
273
ndp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc);
274
if (dircheck(idesc, bp, ndp) != 0)
275
return (dp);
276
/*
277
* The next entry is bad, so subsume it and the remainder
278
* of this directory block into this entry.
279
*/
280
subsume_ndp = 1;
281
}
282
/*
283
* Current or next entry is bad. Zap current entry or
284
* subsume next entry into current entry as appropriate.
285
*/
286
size = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ);
287
idesc->id_loc += size;
288
idesc->id_filesize -= size;
289
if (idesc->id_fix == IGNORE)
290
return (NULL);
291
if (subsume_ndp) {
292
memset(ndp, 0, size);
293
dp->d_reclen += size;
294
} else {
295
memset(dp, 0, size);
296
dp->d_reclen = size;
297
}
298
if (dofix(idesc, "DIRECTORY CORRUPTED"))
299
dirty(bp);
300
return (dp);
301
}
302
303
/*
304
* Verify that a directory entry is valid.
305
* This is a superset of the checks made in the kernel.
306
* Also optionally clears padding and unused directory space.
307
*
308
* Returns 0 if the entry is bad, 1 if the entry is good.
309
*/
310
static int
311
dircheck(struct inodesc *idesc, struct bufarea *bp, struct direct *dp)
312
{
313
size_t size;
314
char *cp;
315
u_int8_t namlen;
316
int spaceleft, modified, unused;
317
318
spaceleft = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ);
319
size = DIRSIZ(0, dp);
320
if (dp->d_reclen == 0 ||
321
dp->d_reclen > spaceleft ||
322
dp->d_reclen < size ||
323
idesc->id_filesize < size ||
324
(dp->d_reclen & (DIR_ROUNDUP - 1)) != 0)
325
goto bad;
326
modified = 0;
327
if (dp->d_ino == 0) {
328
if (!zflag || fswritefd < 0)
329
return (1);
330
/*
331
* Special case of an unused directory entry. Normally only
332
* occurs at the beginning of a directory block when the block
333
* contains no entries. Other than the first entry in a
334
* directory block, the kernel coalesces unused space with
335
* the previous entry by extending its d_reclen. However,
336
* when cleaning up a directory, fsck may set d_ino to zero
337
* in the middle of a directory block. If we're clearing out
338
* directory cruft (-z flag), then make sure that all directory
339
* space in entries with d_ino == 0 gets fully cleared.
340
*/
341
if (dp->d_type != 0) {
342
dp->d_type = 0;
343
modified = 1;
344
}
345
if (dp->d_namlen != 0) {
346
dp->d_namlen = 0;
347
modified = 1;
348
}
349
unused = dp->d_reclen - __offsetof(struct direct, d_name);
350
for (cp = dp->d_name; unused > 0; unused--, cp++) {
351
if (*cp != '\0') {
352
*cp = '\0';
353
modified = 1;
354
}
355
}
356
if (modified)
357
dirty(bp);
358
return (1);
359
}
360
/*
361
* The d_type field should not be tested here. A bad type is an error
362
* in the entry itself but is not a corruption of the directory
363
* structure itself. So blowing away all the remaining entries in the
364
* directory block is inappropriate. Rather the type error should be
365
* checked in pass1 and fixed there.
366
*
367
* The name validation should also be done in pass1 although the
368
* check to see if the name is longer than fits in the space
369
* allocated for it (i.e., the *cp != '\0' fails after exiting the
370
* loop below) then it really is a structural error that requires
371
* the stronger action taken here.
372
*/
373
namlen = dp->d_namlen;
374
if (namlen == 0 || dp->d_type > 15)
375
goto bad;
376
for (cp = dp->d_name, size = 0; size < namlen; size++) {
377
if (*cp == '\0' || *cp++ == '/')
378
goto bad;
379
}
380
if (*cp != '\0')
381
goto bad;
382
if (zflag && fswritefd >= 0) {
383
/*
384
* Clear unused directory entry space, including the d_name
385
* padding.
386
*/
387
/* First figure the number of pad bytes. */
388
unused = roundup2(namlen + 1, DIR_ROUNDUP) - (namlen + 1);
389
390
/* Add in the free space to the end of the record. */
391
unused += dp->d_reclen - DIRSIZ(0, dp);
392
393
/*
394
* Now clear out the unused space, keeping track if we actually
395
* changed anything.
396
*/
397
for (cp = &dp->d_name[namlen + 1]; unused > 0; unused--, cp++) {
398
if (*cp != '\0') {
399
*cp = '\0';
400
modified = 1;
401
}
402
}
403
404
if (modified)
405
dirty(bp);
406
}
407
return (1);
408
409
bad:
410
if (debug)
411
printf("Bad dir: ino %d reclen %d namlen %d type %d name %s\n",
412
dp->d_ino, dp->d_reclen, dp->d_namlen, dp->d_type,
413
dp->d_name);
414
return (0);
415
}
416
417
void
418
direrror(ino_t ino, const char *errmesg)
419
{
420
421
fileerror(ino, ino, errmesg);
422
}
423
424
void
425
fileerror(ino_t cwd, ino_t ino, const char *errmesg)
426
{
427
struct inode ip;
428
union dinode *dp;
429
char pathbuf[MAXPATHLEN + 1];
430
431
pwarn("%s ", errmesg);
432
if (ino < UFS_ROOTINO || ino >= maxino) {
433
pfatal("out-of-range inode number %ju", (uintmax_t)ino);
434
return;
435
}
436
ginode(ino, &ip);
437
dp = ip.i_dp;
438
prtinode(&ip);
439
printf("\n");
440
getpathname(pathbuf, cwd, ino);
441
if (ftypeok(dp))
442
pfatal("%s=%s\n",
443
(DIP(dp, di_mode) & IFMT) == IFDIR ? "DIR" : "FILE",
444
pathbuf);
445
else
446
pfatal("NAME=%s\n", pathbuf);
447
irelse(&ip);
448
}
449
450
void
451
adjust(struct inodesc *idesc, int lcnt)
452
{
453
struct inode ip;
454
union dinode *dp;
455
int saveresolved;
456
457
ginode(idesc->id_number, &ip);
458
dp = ip.i_dp;
459
if (DIP(dp, di_nlink) == lcnt) {
460
/*
461
* If we have not hit any unresolved problems, are running
462
* in preen mode, and are on a file system using soft updates,
463
* then just toss any partially allocated files.
464
*/
465
if (resolved && (preen || bkgrdflag) && usedsoftdep) {
466
clri(idesc, "UNREF", 1);
467
irelse(&ip);
468
return;
469
} else {
470
/*
471
* The file system can be marked clean even if
472
* a file is not linked up, but is cleared.
473
* Hence, resolved should not be cleared when
474
* linkup is answered no, but clri is answered yes.
475
*/
476
saveresolved = resolved;
477
if (linkup(idesc->id_number, (ino_t)0, NULL) == 0) {
478
resolved = saveresolved;
479
clri(idesc, "UNREF", 0);
480
irelse(&ip);
481
return;
482
}
483
/*
484
* Account for the new reference created by linkup().
485
*/
486
lcnt--;
487
}
488
}
489
if (lcnt != 0) {
490
pwarn("LINK COUNT %s", (lfdir == idesc->id_number) ? lfname :
491
((DIP(dp, di_mode) & IFMT) == IFDIR ? "DIR" : "FILE"));
492
prtinode(&ip);
493
printf(" COUNT %d SHOULD BE %d",
494
DIP(dp, di_nlink), DIP(dp, di_nlink) - lcnt);
495
if (preen || usedsoftdep) {
496
if (lcnt < 0) {
497
printf("\n");
498
pfatal("LINK COUNT INCREASING");
499
}
500
if (preen)
501
printf(" (ADJUSTED)\n");
502
}
503
if (preen || reply("ADJUST") == 1) {
504
if (bkgrdflag == 0) {
505
DIP_SET(dp, di_nlink, DIP(dp, di_nlink) - lcnt);
506
inodirty(&ip);
507
} else {
508
cmd.value = idesc->id_number;
509
cmd.size = -lcnt;
510
if (debug)
511
printf("adjrefcnt ino %ld amt %lld\n",
512
(long)cmd.value,
513
(long long)cmd.size);
514
if (sysctl(adjrefcnt, MIBSIZE, 0, 0,
515
&cmd, sizeof cmd) == -1)
516
rwerror("ADJUST INODE LINK COUNT",
517
cmd.value);
518
}
519
}
520
}
521
irelse(&ip);
522
}
523
524
static int
525
mkentry(struct inodesc *idesc)
526
{
527
struct direct *dirp = idesc->id_dirp;
528
struct direct newent;
529
int newlen, oldlen;
530
531
newent.d_namlen = strlen(idesc->id_name);
532
newlen = DIRSIZ(0, &newent);
533
if (dirp->d_ino != 0)
534
oldlen = DIRSIZ(0, dirp);
535
else
536
oldlen = 0;
537
if (dirp->d_reclen - oldlen < newlen)
538
return (KEEPON);
539
newent.d_reclen = dirp->d_reclen - oldlen;
540
dirp->d_reclen = oldlen;
541
dirp = (struct direct *)(((char *)dirp) + oldlen);
542
dirp->d_ino = idesc->id_parent; /* ino to be entered is in id_parent */
543
dirp->d_reclen = newent.d_reclen;
544
dirp->d_type = inoinfo(idesc->id_parent)->ino_type;
545
dirp->d_namlen = newent.d_namlen;
546
memmove(dirp->d_name, idesc->id_name, (size_t)newent.d_namlen + 1);
547
return (ALTERED|STOP);
548
}
549
550
static int
551
chgino(struct inodesc *idesc)
552
{
553
struct direct *dirp = idesc->id_dirp;
554
555
if (memcmp(dirp->d_name, idesc->id_name, (int)dirp->d_namlen + 1))
556
return (KEEPON);
557
dirp->d_ino = idesc->id_parent;
558
dirp->d_type = inoinfo(idesc->id_parent)->ino_type;
559
return (ALTERED|STOP);
560
}
561
562
int
563
linkup(ino_t orphan, ino_t parentdir, char *name)
564
{
565
struct inode ip;
566
union dinode *dp;
567
int lostdir, depth;
568
ino_t oldlfdir;
569
struct inoinfo *inp;
570
struct inodesc idesc;
571
char tempname[BUFSIZ];
572
573
memset(&idesc, 0, sizeof(struct inodesc));
574
ginode(orphan, &ip);
575
dp = ip.i_dp;
576
lostdir = (DIP(dp, di_mode) & IFMT) == IFDIR;
577
pwarn("UNREF %s ", lostdir ? "DIR" : "FILE");
578
prtinode(&ip);
579
printf("\n");
580
if (preen && DIP(dp, di_size) == 0) {
581
irelse(&ip);
582
return (0);
583
}
584
irelse(&ip);
585
if (cursnapshot != 0) {
586
pfatal("FILE LINKUP IN SNAPSHOT");
587
return (0);
588
}
589
if (preen)
590
printf(" (RECONNECTED)\n");
591
else if (reply("RECONNECT") == 0)
592
return (0);
593
if (lfdir == 0) {
594
ginode(UFS_ROOTINO, &ip);
595
idesc.id_name = strdup(lfname);
596
idesc.id_type = DATA;
597
idesc.id_func = findino;
598
idesc.id_number = UFS_ROOTINO;
599
if ((ckinode(ip.i_dp, &idesc) & FOUND) != 0) {
600
lfdir = idesc.id_parent;
601
} else {
602
pwarn("NO lost+found DIRECTORY");
603
if (preen || reply("CREATE")) {
604
lfdir = allocdir(UFS_ROOTINO, (ino_t)0, lfmode);
605
if (lfdir != 0) {
606
if (makeentry(UFS_ROOTINO, lfdir,
607
lfname) != 0) {
608
numdirs++;
609
if (preen)
610
printf(" (CREATED)\n");
611
} else {
612
freedirino(lfdir, UFS_ROOTINO);
613
lfdir = 0;
614
if (preen)
615
printf("\n");
616
}
617
}
618
}
619
}
620
irelse(&ip);
621
free(idesc.id_name);
622
if (lfdir == 0) {
623
pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY");
624
printf("\n\n");
625
return (0);
626
}
627
}
628
ginode(lfdir, &ip);
629
dp = ip.i_dp;
630
if ((DIP(dp, di_mode) & IFMT) != IFDIR) {
631
pfatal("lost+found IS NOT A DIRECTORY");
632
if (reply("REALLOCATE") == 0) {
633
irelse(&ip);
634
return (0);
635
}
636
oldlfdir = lfdir;
637
if ((lfdir = allocdir(UFS_ROOTINO, (ino_t)0, lfmode)) == 0) {
638
pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n");
639
irelse(&ip);
640
return (0);
641
}
642
if ((changeino(UFS_ROOTINO, lfname, lfdir, 1) & ALTERED) == 0) {
643
pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n");
644
irelse(&ip);
645
return (0);
646
}
647
idesc.id_type = inoinfo(oldlfdir)->ino_idtype;
648
idesc.id_func = freeblock;
649
idesc.id_number = oldlfdir;
650
adjust(&idesc, inoinfo(oldlfdir)->ino_linkcnt + 1);
651
inoinfo(oldlfdir)->ino_linkcnt = 0;
652
inodirty(&ip);
653
irelse(&ip);
654
ginode(lfdir, &ip);
655
dp = ip.i_dp;
656
}
657
if (inoinfo(lfdir)->ino_state != DFOUND) {
658
pfatal("SORRY. NO lost+found DIRECTORY\n\n");
659
irelse(&ip);
660
return (0);
661
}
662
(void)lftempname(tempname, orphan);
663
if (makeentry(lfdir, orphan, (name ? name : tempname)) == 0) {
664
pfatal("SORRY. NO SPACE IN lost+found DIRECTORY");
665
printf("\n\n");
666
irelse(&ip);
667
return (0);
668
}
669
inoinfo(orphan)->ino_linkcnt--;
670
if (lostdir) {
671
depth = DIP(dp, di_dirdepth) + 1;
672
if ((changeino(orphan, "..", lfdir, depth) & ALTERED) == 0 &&
673
parentdir != (ino_t)-1)
674
(void)makeentry(orphan, lfdir, "..");
675
DIP_SET(dp, di_nlink, DIP(dp, di_nlink) + 1);
676
inodirty(&ip);
677
inoinfo(lfdir)->ino_linkcnt++;
678
pwarn("DIR I=%lu CONNECTED. ", (u_long)orphan);
679
inp = getinoinfo(parentdir);
680
if (parentdir != (ino_t)-1 && inp != NULL) {
681
printf("PARENT WAS I=%lu\n", (u_long)parentdir);
682
/*
683
* If the parent directory did not have to
684
* be replaced then because of the ordering
685
* guarantees, has had the link count incremented
686
* for the child, but no entry was made. This
687
* fixes the parent link count so that fsck does
688
* not need to be rerun.
689
*/
690
if ((inp->i_flags & INFO_NEW) != 0)
691
inoinfo(parentdir)->ino_linkcnt++;
692
}
693
if (preen == 0)
694
printf("\n");
695
}
696
irelse(&ip);
697
return (1);
698
}
699
700
/*
701
* fix an entry in a directory.
702
*/
703
int
704
changeino(ino_t dir, const char *name, ino_t newnum, int depth)
705
{
706
struct inodesc idesc;
707
struct inode ip;
708
int error;
709
710
memset(&idesc, 0, sizeof(struct inodesc));
711
idesc.id_type = DATA;
712
idesc.id_func = chgino;
713
idesc.id_number = dir;
714
idesc.id_fix = DONTKNOW;
715
idesc.id_name = strdup(name);
716
idesc.id_parent = newnum; /* new value for name */
717
ginode(dir, &ip);
718
if (((error = ckinode(ip.i_dp, &idesc)) & ALTERED) && newnum != 0) {
719
DIP_SET(ip.i_dp, di_dirdepth, depth);
720
inodirty(&ip);
721
getinoinfo(dir)->i_depth = depth;
722
}
723
free(idesc.id_name);
724
irelse(&ip);
725
return (error);
726
}
727
728
/*
729
* make an entry in a directory
730
*/
731
int
732
makeentry(ino_t parent, ino_t ino, const char *name)
733
{
734
struct inode ip;
735
union dinode *dp;
736
struct inodesc idesc;
737
int retval;
738
char pathbuf[MAXPATHLEN + 1];
739
740
if (parent < UFS_ROOTINO || parent >= maxino ||
741
ino < UFS_ROOTINO || ino >= maxino)
742
return (0);
743
memset(&idesc, 0, sizeof(struct inodesc));
744
idesc.id_type = DATA;
745
idesc.id_func = mkentry;
746
idesc.id_number = parent;
747
idesc.id_parent = ino; /* this is the inode to enter */
748
idesc.id_fix = DONTKNOW;
749
idesc.id_name = strdup(name);
750
ginode(parent, &ip);
751
dp = ip.i_dp;
752
if (DIP(dp, di_size) % DIRBLKSIZ) {
753
DIP_SET(dp, di_size, roundup(DIP(dp, di_size), DIRBLKSIZ));
754
inodirty(&ip);
755
}
756
if ((ckinode(dp, &idesc) & ALTERED) != 0) {
757
irelse(&ip);
758
free(idesc.id_name);
759
return (1);
760
}
761
getpathname(pathbuf, parent, parent);
762
if (expanddir(&ip, pathbuf) == 0) {
763
irelse(&ip);
764
free(idesc.id_name);
765
return (0);
766
}
767
retval = ckinode(dp, &idesc) & ALTERED;
768
irelse(&ip);
769
free(idesc.id_name);
770
return (retval);
771
}
772
773
/*
774
* Attempt to expand the size of a directory
775
*/
776
static int
777
expanddir(struct inode *ip, char *name)
778
{
779
ufs2_daddr_t lastlbn, oldblk, newblk, indirblk;
780
size_t filesize, lastlbnsize;
781
struct bufarea *bp, *nbp;
782
struct inodesc idesc;
783
union dinode *dp;
784
long cg, indiralloced;
785
char *cp;
786
787
nbp = NULL;
788
indiralloced = newblk = indirblk = 0;
789
memset(&idesc, 0, sizeof(struct inodesc));
790
idesc.id_type = ADDR;
791
pwarn("NO SPACE LEFT IN %s", name);
792
if (!preen && reply("EXPAND") == 0)
793
return (0);
794
cg = ino_to_cg(&sblock, ip->i_number);
795
dp = ip->i_dp;
796
filesize = DIP(dp, di_size);
797
lastlbn = lblkno(&sblock, filesize);
798
/*
799
* We only expand lost+found to a single indirect block.
800
*/
801
if ((DIP(dp, di_mode) & IFMT) != IFDIR || filesize == 0 ||
802
lastlbn >= UFS_NDADDR + NINDIR(&sblock))
803
goto bad;
804
/*
805
* If last block is a fragment, expand it to a full size block.
806
*/
807
lastlbnsize = sblksize(&sblock, filesize, lastlbn);
808
if (lastlbnsize > 0 && lastlbnsize < sblock.fs_bsize) {
809
oldblk = DIP(dp, di_db[lastlbn]);
810
bp = getdirblk(oldblk, lastlbnsize);
811
if (bp->b_errs)
812
goto bad;
813
newblk = allocblk(cg, sblock.fs_frag, std_checkblkavail);
814
if (newblk == 0)
815
goto bad;
816
nbp = getdatablk(newblk, sblock.fs_bsize, BT_DIRDATA);
817
if (nbp->b_errs)
818
goto bad;
819
DIP_SET(dp, di_db[lastlbn], newblk);
820
DIP_SET(dp, di_size, filesize + sblock.fs_bsize - lastlbnsize);
821
DIP_SET(dp, di_blocks, DIP(dp, di_blocks) +
822
btodb(sblock.fs_bsize - lastlbnsize));
823
inodirty(ip);
824
memmove(nbp->b_un.b_buf, bp->b_un.b_buf, lastlbnsize);
825
memset(&nbp->b_un.b_buf[lastlbnsize], 0,
826
sblock.fs_bsize - lastlbnsize);
827
for (cp = &nbp->b_un.b_buf[lastlbnsize];
828
cp < &nbp->b_un.b_buf[sblock.fs_bsize];
829
cp += DIRBLKSIZ)
830
memmove(cp, &emptydir, sizeof emptydir);
831
dirty(nbp);
832
brelse(nbp);
833
binval(bp);
834
idesc.id_blkno = oldblk;
835
idesc.id_numfrags = numfrags(&sblock, lastlbnsize);
836
(void)freeblock(&idesc);
837
if (preen)
838
printf(" (EXPANDED)\n");
839
return (1);
840
}
841
if ((newblk = allocblk(cg, sblock.fs_frag, std_checkblkavail)) == 0)
842
goto bad;
843
bp = getdirblk(newblk, sblock.fs_bsize);
844
if (bp->b_errs)
845
goto bad;
846
memset(bp->b_un.b_buf, 0, sblock.fs_bsize);
847
for (cp = bp->b_un.b_buf;
848
cp < &bp->b_un.b_buf[sblock.fs_bsize];
849
cp += DIRBLKSIZ)
850
memmove(cp, &emptydir, sizeof emptydir);
851
dirty(bp);
852
if (lastlbn < UFS_NDADDR) {
853
DIP_SET(dp, di_db[lastlbn], newblk);
854
} else {
855
/*
856
* Allocate indirect block if needed.
857
*/
858
if ((indirblk = DIP(dp, di_ib[0])) == 0) {
859
indirblk = allocblk(cg, sblock.fs_frag,
860
std_checkblkavail);
861
if (indirblk == 0) {
862
binval(bp);
863
goto bad;
864
}
865
indiralloced = 1;
866
}
867
nbp = getdatablk(indirblk, sblock.fs_bsize, BT_LEVEL1);
868
if (nbp->b_errs)
869
goto bad;
870
if (indiralloced) {
871
memset(nbp->b_un.b_buf, 0, sblock.fs_bsize);
872
DIP_SET(dp, di_ib[0], indirblk);
873
DIP_SET(dp, di_blocks,
874
DIP(dp, di_blocks) + btodb(sblock.fs_bsize));
875
inodirty(ip);
876
}
877
IBLK_SET(nbp, lastlbn - UFS_NDADDR, newblk);
878
dirty(nbp);
879
brelse(nbp);
880
}
881
DIP_SET(dp, di_size, filesize + sblock.fs_bsize);
882
DIP_SET(dp, di_blocks, DIP(dp, di_blocks) + btodb(sblock.fs_bsize));
883
inodirty(ip);
884
if (preen)
885
printf(" (EXPANDED)\n");
886
return (1);
887
bad:
888
pfatal(" (EXPANSION FAILED)\n");
889
if (nbp != NULL) {
890
binval(bp);
891
brelse(nbp);
892
}
893
if (newblk != 0) {
894
idesc.id_blkno = newblk;
895
idesc.id_numfrags = sblock.fs_frag;
896
(void)freeblock(&idesc);
897
}
898
if (indiralloced) {
899
idesc.id_blkno = indirblk;
900
idesc.id_numfrags = sblock.fs_frag;
901
(void)freeblock(&idesc);
902
}
903
return (0);
904
}
905
906
/*
907
* allocate a new directory
908
*/
909
ino_t
910
allocdir(ino_t parent, ino_t request, int mode)
911
{
912
ino_t ino;
913
char *cp;
914
struct inode ip;
915
union dinode *dp;
916
struct bufarea *bp;
917
struct dirtemplate *dirp;
918
struct inoinfo *inp, *parentinp;
919
920
ino = allocino(request, IFDIR|mode);
921
if (ino == 0)
922
return (0);
923
dirp = &dirhead;
924
dirp->dot_ino = ino;
925
dirp->dotdot_ino = parent;
926
ginode(ino, &ip);
927
dp = ip.i_dp;
928
bp = getdirblk(DIP(dp, di_db[0]), sblock.fs_fsize);
929
if (bp->b_errs) {
930
freeino(ino);
931
irelse(&ip);
932
return (0);
933
}
934
memmove(bp->b_un.b_buf, dirp, sizeof(struct dirtemplate));
935
for (cp = &bp->b_un.b_buf[DIRBLKSIZ];
936
cp < &bp->b_un.b_buf[sblock.fs_fsize];
937
cp += DIRBLKSIZ)
938
memmove(cp, &emptydir, sizeof emptydir);
939
dirty(bp);
940
DIP_SET(dp, di_nlink, 2);
941
inodirty(&ip);
942
if (ino == UFS_ROOTINO) {
943
inp = cacheino(dp, ino);
944
inp->i_parent = parent;
945
inp->i_dotdot = parent;
946
inp->i_flags |= INFO_NEW;
947
inoinfo(ino)->ino_type = DT_DIR;
948
inoinfo(ino)->ino_linkcnt = DIP(dp, di_nlink);
949
irelse(&ip);
950
return(ino);
951
}
952
if (!INO_IS_DVALID(parent)) {
953
freeino(ino);
954
irelse(&ip);
955
return (0);
956
}
957
inp = cacheino(dp, ino);
958
inp->i_parent = parent;
959
inp->i_dotdot = parent;
960
inp->i_flags |= INFO_NEW;
961
if ((parentinp = getinoinfo(inp->i_parent)) == NULL) {
962
pfatal("allocdir: UNKNOWN PARENT DIR");
963
} else {
964
inp->i_depth = parentinp->i_depth + 1;
965
DIP_SET(dp, di_dirdepth, inp->i_depth);
966
inodirty(&ip);
967
}
968
inoinfo(ino)->ino_type = DT_DIR;
969
inoinfo(ino)->ino_state = inoinfo(parent)->ino_state;
970
if (inoinfo(ino)->ino_state == DSTATE) {
971
inoinfo(ino)->ino_linkcnt = DIP(dp, di_nlink);
972
inoinfo(parent)->ino_linkcnt++;
973
}
974
irelse(&ip);
975
ginode(parent, &ip);
976
dp = ip.i_dp;
977
DIP_SET(dp, di_nlink, DIP(dp, di_nlink) + 1);
978
inodirty(&ip);
979
irelse(&ip);
980
return (ino);
981
}
982
983
/*
984
* free a directory inode
985
*/
986
void
987
freedirino(ino_t ino, ino_t parent)
988
{
989
struct inode ip;
990
union dinode *dp;
991
992
if (ino != parent) {
993
ginode(parent, &ip);
994
dp = ip.i_dp;
995
DIP_SET(dp, di_nlink, DIP(dp, di_nlink) - 1);
996
inodirty(&ip);
997
irelse(&ip);
998
}
999
removecachedino(ino);
1000
freeino(ino);
1001
}
1002
1003
/*
1004
* generate a temporary name for the lost+found directory.
1005
*/
1006
static int
1007
lftempname(char *bufp, ino_t ino)
1008
{
1009
ino_t in;
1010
char *cp;
1011
int namlen;
1012
1013
cp = bufp + 2;
1014
for (in = maxino; in > 0; in /= 10)
1015
cp++;
1016
*--cp = 0;
1017
namlen = cp - bufp;
1018
in = ino;
1019
while (cp > bufp) {
1020
*--cp = (in % 10) + '0';
1021
in /= 10;
1022
}
1023
*cp = '#';
1024
return (namlen);
1025
}
1026
1027
/*
1028
* Get a directory block.
1029
* Insure that it is held until another is requested.
1030
*/
1031
static struct bufarea *
1032
getdirblk(ufs2_daddr_t blkno, long size)
1033
{
1034
1035
if (pdirbp != NULL && pdirbp->b_errs == 0)
1036
brelse(pdirbp);
1037
pdirbp = getdatablk(blkno, size, BT_DIRDATA);
1038
return (pdirbp);
1039
}
1040
1041