]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sbin/fsck_ffs/setup.c
MFV: xz 5.4.5
[FreeBSD/FreeBSD.git] / sbin / fsck_ffs / setup.c
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 #if 0
33 #ifndef lint
34 static const char sccsid[] = "@(#)setup.c       8.10 (Berkeley) 5/9/95";
35 #endif /* not lint */
36 #endif
37 #include <sys/cdefs.h>
38 #include <sys/param.h>
39 #include <sys/disk.h>
40 #include <sys/stat.h>
41 #define FSTYPENAMES
42 #include <sys/disklabel.h>
43 #include <sys/file.h>
44 #include <sys/sysctl.h>
45
46 #include <ufs/ufs/dinode.h>
47 #include <ufs/ffs/fs.h>
48
49 #include <ctype.h>
50 #include <err.h>
51 #include <errno.h>
52 #include <limits.h>
53 #include <stdint.h>
54 #include <string.h>
55
56 #include "fsck.h"
57
58 struct inohash *inphash;               /* hash list of directory inode info */
59 struct inoinfo **inpsort;              /* disk order list of directory inodes */
60 struct inode snaplist[FSMAXSNAP + 1];  /* list of active snapshots */
61 int snapcnt;                           /* number of active snapshots */
62 char *copybuf;                         /* buffer to copy snapshot blocks */
63
64 static int sbhashfailed;
65 #define POWEROF2(num)   (((num) & ((num) - 1)) == 0)
66
67 static int calcsb(char *dev, int devfd, struct fs *fs);
68 static void saverecovery(int readfd, int writefd);
69 static int chkrecovery(int devfd);
70 static int getlbnblkno(struct inodesc *);
71 static int checksnapinfo(struct inode *);
72
73 /*
74  * Read in a superblock finding an alternate if necessary.
75  * Return 1 if successful, 0 if unsuccessful, -1 if file system
76  * is already clean (ckclean and preen mode only).
77  */
78 int
79 setup(char *dev)
80 {
81         long i, bmapsize;
82         struct inode ip;
83
84         /*
85          * We are expected to have an open file descriptor and a superblock.
86          */
87         if (fsreadfd < 0 || havesb == 0) {
88                 if (debug) {
89                         if (fsreadfd < 0)
90                                 printf("setup: missing fsreadfd\n");
91                         else
92                                 printf("setup: missing superblock\n");
93                 }
94                 return (0);
95         }
96         if (preen == 0)
97                 printf("** %s", dev);
98         if (bkgrdflag == 0 &&
99             (nflag || (fswritefd = open(dev, O_WRONLY)) < 0)) {
100                 fswritefd = -1;
101                 if (preen)
102                         pfatal("NO WRITE ACCESS");
103                 printf(" (NO WRITE)");
104         }
105         if (preen == 0)
106                 printf("\n");
107         if (sbhashfailed != 0) {
108                 pwarn("SUPERBLOCK CHECK HASH FAILED");
109                 if (fswritefd == -1)
110                         pwarn("OPENED READONLY SO CANNOT CORRECT CHECK HASH\n");
111                 else if (preen || reply("CORRECT CHECK HASH") != 0) {
112                         if (preen)
113                                 printf(" (CORRECTED)\n");
114                         sblock.fs_clean = 0;
115                         sbdirty();
116                 }
117         }
118         if (skipclean && ckclean && sblock.fs_clean) {
119                 pwarn("FILE SYSTEM CLEAN; SKIPPING CHECKS\n");
120                 return (-1);
121         }
122         maxfsblock = sblock.fs_size;
123         maxino = sblock.fs_ncg * sblock.fs_ipg;
124         /*
125          * Check and potentially fix certain fields in the super block.
126          */
127         if (sblock.fs_optim != FS_OPTTIME && sblock.fs_optim != FS_OPTSPACE) {
128                 pfatal("UNDEFINED OPTIMIZATION IN SUPERBLOCK");
129                 if (reply("SET TO DEFAULT") == 1) {
130                         sblock.fs_optim = FS_OPTTIME;
131                         sbdirty();
132                 }
133         }
134         if ((sblock.fs_minfree < 0 || sblock.fs_minfree > 99)) {
135                 pfatal("IMPOSSIBLE MINFREE=%d IN SUPERBLOCK",
136                         sblock.fs_minfree);
137                 if (reply("SET TO DEFAULT") == 1) {
138                         sblock.fs_minfree = 10;
139                         sbdirty();
140                 }
141         }
142         if (sblock.fs_magic == FS_UFS1_MAGIC &&
143             sblock.fs_old_inodefmt < FS_44INODEFMT) {
144                 pwarn("Format of file system is too old.\n");
145                 pwarn("Must update to modern format using a version of fsck\n");
146                 pfatal("from before 2002 with the command ``fsck -c 2''\n");
147                 exit(EEXIT);
148         }
149         if (preen == 0 && yflag == 0 && sblock.fs_magic == FS_UFS2_MAGIC &&
150             fswritefd != -1 && chkrecovery(fsreadfd) == 0 &&
151             reply("SAVE DATA TO FIND ALTERNATE SUPERBLOCKS") != 0)
152                 saverecovery(fsreadfd, fswritefd);
153         /*
154          * allocate and initialize the necessary maps
155          */
156         bufinit();
157         bmapsize = roundup(howmany(maxfsblock, CHAR_BIT), sizeof(short));
158         blockmap = Calloc((unsigned)bmapsize, sizeof (char));
159         if (blockmap == NULL) {
160                 printf("cannot alloc %u bytes for blockmap\n",
161                     (unsigned)bmapsize);
162                 goto badsb;
163         }
164         inostathead = Calloc(sblock.fs_ncg, sizeof(struct inostatlist));
165         if (inostathead == NULL) {
166                 printf("cannot alloc %u bytes for inostathead\n",
167                     (unsigned)(sizeof(struct inostatlist) * (sblock.fs_ncg)));
168                 goto badsb;
169         }
170         numdirs = sblock.fs_cstotal.cs_ndir;
171         dirhash = MAX(numdirs / 2, 1);
172         inplast = 0;
173         listmax = numdirs + 10;
174         inpsort = (struct inoinfo **)Calloc(listmax, sizeof(struct inoinfo *));
175         inphash = (struct inohash *)Calloc(dirhash, sizeof(struct inohash));
176         if (inpsort == NULL || inphash == NULL) {
177                 printf("cannot alloc %ju bytes for inphash\n",
178                     (uintmax_t)numdirs * sizeof(struct inoinfo *));
179                 goto badsb;
180         }
181         if (sblock.fs_flags & FS_DOSOFTDEP)
182                 usedsoftdep = 1;
183         else
184                 usedsoftdep = 0;
185         /*
186          * Collect any snapshot inodes so that we can allow them to
187          * claim any blocks that we free. The code for doing this is
188          * imported here and into inode.c from sys/ufs/ffs/ffs_snapshot.c.
189          */
190         for (snapcnt = 0; snapcnt < FSMAXSNAP; snapcnt++) {
191                 if (sblock.fs_snapinum[snapcnt] == 0)
192                         break;
193                 ginode(sblock.fs_snapinum[snapcnt], &ip);
194                 if ((DIP(ip.i_dp, di_mode) & IFMT) == IFREG &&
195                     (DIP(ip.i_dp, di_flags) & SF_SNAPSHOT) != 0 &&
196                     checksnapinfo(&ip)) {
197                         if (debug)
198                                 printf("Load snapshot %jd\n",
199                                     (intmax_t)sblock.fs_snapinum[snapcnt]);
200                         snaplist[snapcnt] = ip;
201                         continue;
202                 }
203                 printf("Removing non-snapshot inode %ju from snapshot list\n",
204                     (uintmax_t)sblock.fs_snapinum[snapcnt]);
205                 irelse(&ip);
206                 for (i = snapcnt + 1; i < FSMAXSNAP; i++) {
207                         if (sblock.fs_snapinum[i] == 0)
208                                 break;
209                         sblock.fs_snapinum[i - 1] = sblock.fs_snapinum[i];
210                 }
211                 sblock.fs_snapinum[i - 1] = 0;
212                 snapcnt--;
213                 sbdirty();
214         }
215         if (snapcnt > 0 && copybuf == NULL) {
216                 copybuf = Balloc(sblock.fs_bsize);
217                 if (copybuf == NULL)
218                         errx(EEXIT, "cannot allocate space for snapshot "
219                             "copy buffer");
220         }
221         return (1);
222
223 badsb:
224         ckfini(0);
225         return (0);
226 }
227
228 /*
229  * Check for valid snapshot information.
230  *
231  * Each snapshot has a list of blocks that have been copied. This list
232  * is consulted before checking the snapshot inode. Its purpose is to
233  * speed checking of commonly checked blocks and to avoid recursive
234  * checks of the snapshot inode. In particular, the list must contain
235  * the superblock, the superblock summary information, and all the
236  * cylinder group blocks. The list may contain other commonly checked
237  * pointers such as those of the blocks that contain the snapshot inodes.
238  * The list is sorted into block order to allow binary search lookup.
239  *
240  * The twelve direct direct block pointers of the snapshot are always
241  * copied, so we test for them first before checking the list itself
242  * (i.e., they are not in the list).
243  *
244  * The checksnapinfo() routine needs to ensure that the list contains at
245  * least the super block, its summary information, and the cylinder groups.
246  * Here we check the list first for the superblock, zero or more cylinder
247  * groups up to the location of the superblock summary information, the
248  * summary group information, and any remaining cylinder group maps that
249  * follow it. We skip over any other entries in the list.
250  */
251 #define CHKBLKINLIST(chkblk)                                            \
252         /* All UFS_NDADDR blocks are copied */                          \
253         if ((chkblk) >= UFS_NDADDR) {                                   \
254                 /* Skip over blocks that are not of interest */         \
255                 while (*blkp < (chkblk) && blkp < lastblkp)             \
256                         blkp++;                                         \
257                 /* Fail if end of list and not all blocks found */      \
258                 if (blkp >= lastblkp) {                                 \
259                         pwarn("UFS%d snapshot inode %jd failed: "       \
260                             "improper block list length (%jd)\n",       \
261                             sblock.fs_magic == FS_UFS1_MAGIC ? 1 : 2,   \
262                             (intmax_t)snapip->i_number,                 \
263                             (intmax_t)(lastblkp - &snapblklist[0]));    \
264                         status = 0;                                     \
265                 }                                                       \
266                 /* Fail if block we seek is missing */                  \
267                 else if (*blkp++ != (chkblk)) {                         \
268                         pwarn("UFS%d snapshot inode %jd failed: "       \
269                             "block list (%jd) != %s (%jd)\n",           \
270                             sblock.fs_magic == FS_UFS1_MAGIC ? 1 : 2,   \
271                             (intmax_t)snapip->i_number,                 \
272                             (intmax_t)blkp[-1], #chkblk,                \
273                             (intmax_t)chkblk);                          \
274                         status = 0;                                     \
275                 }                                                       \
276         }
277
278 static int
279 checksnapinfo(struct inode *snapip)
280 {
281         struct fs *fs;
282         struct bufarea *bp;
283         struct inodesc idesc;
284         daddr_t *snapblklist, *blkp, *lastblkp, csblkno;
285         int cg, loc, len, status;
286         ufs_lbn_t lbn;
287         size_t size;
288
289         fs = &sblock;
290         memset(&idesc, 0, sizeof(struct inodesc));
291         idesc.id_type = ADDR;
292         idesc.id_func = getlbnblkno;
293         idesc.id_number = snapip->i_number;
294         lbn = howmany(fs->fs_size, fs->fs_frag);
295         idesc.id_parent = lbn;          /* sought after blkno */
296         if ((ckinode(snapip->i_dp, &idesc) & FOUND) == 0)
297                 return (0);
298         size = fragroundup(fs,
299             DIP(snapip->i_dp, di_size) - lblktosize(fs, lbn));
300         bp = getdatablk(idesc.id_parent, size, BT_DATA);
301         if (bp->b_errs != 0)
302                 return (0);
303         snapblklist = (daddr_t *)bp->b_un.b_buf;
304         /*
305          * snapblklist[0] is the size of the list
306          * snapblklist[1] is the first element of the list
307          *
308          * We need to be careful to bound the size of the list and verify
309          * that we have not run off the end of it if it or its size has
310          * been corrupted.
311          */
312         blkp = &snapblklist[1];
313         lastblkp = &snapblklist[MAX(0,
314             MIN(snapblklist[0] + 1, size / sizeof(daddr_t)))];
315         status = 1;
316         /* Check that the superblock is listed. */
317         CHKBLKINLIST(lblkno(fs, fs->fs_sblockloc));
318         if (status == 0)
319                 goto out;
320         /*
321          * Calculate where the summary information is located.
322          * Usually it is in the first cylinder group, but growfs
323          * may move it to the first cylinder group that it adds.
324          *
325          * Check all cylinder groups up to the summary information.
326          */
327         csblkno = fragstoblks(fs, fs->fs_csaddr);
328         for (cg = 0; cg < fs->fs_ncg; cg++) {
329                 if (fragstoblks(fs, cgtod(fs, cg)) > csblkno)
330                         break;
331                 CHKBLKINLIST(fragstoblks(fs, cgtod(fs, cg)));
332                 if (status == 0)
333                         goto out;
334         }
335         /* Check the summary information block(s). */
336         len = howmany(fs->fs_cssize, fs->fs_bsize);
337         for (loc = 0; loc < len; loc++) {
338                 CHKBLKINLIST(csblkno + loc);
339                 if (status == 0)
340                         goto out;
341         }
342         /* Check the remaining cylinder groups. */
343         for (; cg < fs->fs_ncg; cg++) {
344                 CHKBLKINLIST(fragstoblks(fs, cgtod(fs, cg)));
345                 if (status == 0)
346                         goto out;
347         }
348 out:
349         brelse(bp);
350         return (status);
351 }
352
353 /*
354  * Return the block number associated with a specified inode lbn.
355  * Requested lbn is in id_parent. If found, block is returned in
356  * id_parent.
357  */
358 static int
359 getlbnblkno(struct inodesc *idesc)
360 {
361
362         if (idesc->id_lbn < idesc->id_parent)
363                 return (KEEPON);
364         idesc->id_parent = idesc->id_blkno;
365         return (STOP | FOUND);
366 }
367
368 /*
369  * Open a device or file to be checked by fsck.
370  */
371 int
372 openfilesys(char *dev)
373 {
374         struct stat statb;
375         int saved_fsreadfd;
376
377         if (stat(dev, &statb) < 0)
378                 return (0);
379         if ((statb.st_mode & S_IFMT) != S_IFCHR &&
380             (statb.st_mode & S_IFMT) != S_IFBLK) {
381                 if (bkgrdflag != 0 && (statb.st_flags & SF_SNAPSHOT) == 0) {
382                         pwarn("BACKGROUND FSCK LACKS A SNAPSHOT\n");
383                         return (0);
384                 }
385                 if (bkgrdflag != 0) {
386                         cursnapshot = statb.st_ino;
387                 } else {
388                         pwarn("%s IS NOT A DISK DEVICE\n", dev);
389                         if (preen || reply("CONTINUE") == 0)
390                                 return (0);
391                 }
392         }
393         saved_fsreadfd = fsreadfd;
394         if ((fsreadfd = open(dev, O_RDONLY)) < 0) {
395                 fsreadfd = saved_fsreadfd;
396                 return (0);
397         }
398         if (saved_fsreadfd != -1)
399                 close(saved_fsreadfd);
400         return (1);
401 }
402
403 /*
404  * Read in the super block and its summary info.
405  */
406 int
407 readsb(void)
408 {
409         struct fs *fs;
410
411         sbhashfailed = 0;
412         readcnt[sblk.b_type]++;
413         /*
414          * If bflag is given, then check just that superblock.
415          */
416         if (bflag) {
417                 switch (sbget(fsreadfd, &fs, bflag * dev_bsize, 0)) {
418                 case 0:
419                         goto goodsb;
420                 case EINTEGRITY:
421                         printf("Check hash failed for superblock at %jd\n",
422                             bflag);
423                         return (0);
424                 case ENOENT:
425                         printf("%jd is not a file system superblock\n", bflag);
426                         return (0);
427                 case EIO:
428                 default:
429                         printf("I/O error reading %jd\n", bflag);
430                         return (0);
431                 }
432         }
433         /*
434          * Check for the standard superblock and use it if good.
435          */
436         if (sbget(fsreadfd, &fs, UFS_STDSB, UFS_NOMSG) == 0)
437                 goto goodsb;
438         /*
439          * Check if the only problem is a check-hash failure.
440          */
441         skipclean = 0;
442         if (sbget(fsreadfd, &fs, UFS_STDSB, UFS_NOMSG | UFS_NOHASHFAIL) == 0) {
443                 sbhashfailed = 1;
444                 goto goodsb;
445         }
446         /*
447          * Do an exhaustive search for a usable superblock.
448          */
449         switch (sbsearch(fsreadfd, &fs, 0)) {
450         case 0:
451                 goto goodsb;
452         case ENOENT:
453                 printf("SEARCH FOR ALTERNATE SUPER-BLOCK FAILED. "
454                     "YOU MUST USE THE\n-b OPTION TO FSCK TO SPECIFY "
455                     "THE LOCATION OF AN ALTERNATE\nSUPER-BLOCK TO "
456                     "SUPPLY NEEDED INFORMATION; SEE fsck_ffs(8).\n");
457                 return (0);
458         case EIO:
459         default:
460                 printf("I/O error reading a usable superblock\n");
461                 return (0);
462         }
463
464 goodsb:
465         memcpy(&sblock, fs, fs->fs_sbsize);
466         free(fs);
467         /*
468          * Compute block size that the file system is based on,
469          * according to fsbtodb, and adjust superblock block number
470          * so we can tell if this is an alternate later.
471          */
472         dev_bsize = sblock.fs_fsize / fsbtodb(&sblock, 1);
473         sblk.b_bno = sblock.fs_sblockactualloc / dev_bsize;
474         sblk.b_size = SBLOCKSIZE;
475         /*
476          * If not yet done, update UFS1 superblock with new wider fields.
477          */
478         if (sblock.fs_magic == FS_UFS1_MAGIC &&
479             sblock.fs_maxbsize != sblock.fs_bsize) {
480                 sblock.fs_maxbsize = sblock.fs_bsize;
481                 sblock.fs_time = sblock.fs_old_time;
482                 sblock.fs_size = sblock.fs_old_size;
483                 sblock.fs_dsize = sblock.fs_old_dsize;
484                 sblock.fs_csaddr = sblock.fs_old_csaddr;
485                 sblock.fs_cstotal.cs_ndir = sblock.fs_old_cstotal.cs_ndir;
486                 sblock.fs_cstotal.cs_nbfree = sblock.fs_old_cstotal.cs_nbfree;
487                 sblock.fs_cstotal.cs_nifree = sblock.fs_old_cstotal.cs_nifree;
488                 sblock.fs_cstotal.cs_nffree = sblock.fs_old_cstotal.cs_nffree;
489         }
490         havesb = 1;
491         return (1);
492 }
493
494 void
495 sblock_init(void)
496 {
497
498         fsreadfd = -1;
499         fswritefd = -1;
500         fsmodified = 0;
501         lfdir = 0;
502         initbarea(&sblk, BT_SUPERBLK);
503         sblk.b_un.b_buf = Balloc(SBLOCKSIZE);
504         if (sblk.b_un.b_buf == NULL)
505                 errx(EEXIT, "cannot allocate space for superblock");
506         dev_bsize = secsize = DEV_BSIZE;
507 }
508
509 /*
510  * Calculate a prototype superblock based on information in the boot area.
511  * When done the cgsblock macro can be calculated and the fs_ncg field
512  * can be used. Do NOT attempt to use other macros without verifying that
513  * their needed information is available!
514  */
515 static int
516 calcsb(char *dev, int devfd, struct fs *fs)
517 {
518         struct fsrecovery *fsr;
519         char *fsrbuf;
520         u_int secsize;
521
522         /*
523          * We need fragments-per-group and the partition-size.
524          *
525          * Newfs stores these details at the end of the boot block area
526          * at the start of the filesystem partition. If they have been
527          * overwritten by a boot block, we fail. But usually they are
528          * there and we can use them.
529          */
530         if (ioctl(devfd, DIOCGSECTORSIZE, &secsize) == -1)
531                 return (0);
532         fsrbuf = Balloc(secsize);
533         if (fsrbuf == NULL)
534                 errx(EEXIT, "calcsb: cannot allocate recovery buffer");
535         if (blread(devfd, fsrbuf,
536             (SBLOCK_UFS2 - secsize) / dev_bsize, secsize) != 0) {
537                 free(fsrbuf);
538                 return (0);
539         }
540         fsr = (struct fsrecovery *)&fsrbuf[secsize - sizeof *fsr];
541         if (fsr->fsr_magic != FS_UFS2_MAGIC) {
542                 free(fsrbuf);
543                 return (0);
544         }
545         memset(fs, 0, sizeof(struct fs));
546         fs->fs_fpg = fsr->fsr_fpg;
547         fs->fs_fsbtodb = fsr->fsr_fsbtodb;
548         fs->fs_sblkno = fsr->fsr_sblkno;
549         fs->fs_magic = fsr->fsr_magic;
550         fs->fs_ncg = fsr->fsr_ncg;
551         free(fsrbuf);
552         return (1);
553 }
554
555 /*
556  * Check to see if recovery information exists.
557  * Return 1 if it exists or cannot be created.
558  * Return 0 if it does not exist and can be created.
559  */
560 static int
561 chkrecovery(int devfd)
562 {
563         struct fsrecovery *fsr;
564         char *fsrbuf;
565         u_int secsize, rdsize;
566
567         /*
568          * Could not determine if backup material exists, so do not
569          * offer to create it.
570          */
571         fsrbuf = NULL;
572         rdsize = sblock.fs_fsize;
573         if (ioctl(devfd, DIOCGSECTORSIZE, &secsize) == -1 ||
574             rdsize % secsize != 0 ||
575             (fsrbuf = Balloc(rdsize)) == NULL ||
576             blread(devfd, fsrbuf, (SBLOCK_UFS2 - rdsize) / dev_bsize,
577               rdsize) != 0) {
578                 free(fsrbuf);
579                 return (1);
580         }
581         /*
582          * Recovery material has already been created, so do not
583          * need to create it again.
584          */
585         fsr = (struct fsrecovery *)&fsrbuf[rdsize - sizeof *fsr];
586         if (fsr->fsr_magic == FS_UFS2_MAGIC) {
587                 free(fsrbuf);
588                 return (1);
589         }
590         /*
591          * Recovery material has not been created and can be if desired.
592          */
593         free(fsrbuf);
594         return (0);
595 }
596
597 /*
598  * Read the last filesystem-size piece of the boot block, replace the
599  * last 20 bytes with the recovery information, then write it back.
600  * The recovery information only works for UFS2 filesystems.
601  */
602 static void
603 saverecovery(int readfd, int writefd)
604 {
605         struct fsrecovery *fsr;
606         char *fsrbuf;
607         u_int secsize, rdsize;
608
609         fsrbuf = NULL;
610         rdsize = sblock.fs_fsize;
611         if (sblock.fs_magic != FS_UFS2_MAGIC ||
612             ioctl(readfd, DIOCGSECTORSIZE, &secsize) == -1 ||
613             rdsize % secsize != 0 ||
614             (fsrbuf = Balloc(rdsize)) == NULL ||
615             blread(readfd, fsrbuf, (SBLOCK_UFS2 - rdsize) / dev_bsize,
616               rdsize) != 0) {
617                 printf("RECOVERY DATA COULD NOT BE CREATED\n");
618                 free(fsrbuf);
619                 return;
620         }
621         fsr = (struct fsrecovery *)&fsrbuf[rdsize - sizeof *fsr];
622         fsr->fsr_magic = sblock.fs_magic;
623         fsr->fsr_fpg = sblock.fs_fpg;
624         fsr->fsr_fsbtodb = sblock.fs_fsbtodb;
625         fsr->fsr_sblkno = sblock.fs_sblkno;
626         fsr->fsr_ncg = sblock.fs_ncg;
627         blwrite(writefd, fsrbuf, (SBLOCK_UFS2 - rdsize) / dev_bsize, rdsize);
628         free(fsrbuf);
629 }