]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sbin/tunefs/tunefs.c
Import of liblzma, xz, xzdec, lzmainfo from vendor branch
[FreeBSD/FreeBSD.git] / sbin / tunefs / tunefs.c
1 /*
2  * Copyright (c) 1983, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 4. Neither the name of the University nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29
30 #if 0
31 #ifndef lint
32 static const char copyright[] =
33 "@(#) Copyright (c) 1983, 1993\n\
34         The Regents of the University of California.  All rights reserved.\n";
35 #endif /* not lint */
36
37 #ifndef lint
38 static char sccsid[] = "@(#)tunefs.c    8.2 (Berkeley) 4/19/94";
39 #endif /* not lint */
40 #endif
41 #include <sys/cdefs.h>
42 __FBSDID("$FreeBSD$");
43
44 /*
45  * tunefs: change layout parameters to an existing file system.
46  */
47 #include <sys/param.h>
48 #include <sys/mount.h>
49 #include <sys/disklabel.h>
50 #include <sys/stat.h>
51
52 #include <ufs/ufs/ufsmount.h>
53 #include <ufs/ufs/dinode.h>
54 #include <ufs/ffs/fs.h>
55 #include <ufs/ufs/dir.h>
56
57 #include <ctype.h>
58 #include <err.h>
59 #include <fcntl.h>
60 #include <fstab.h>
61 #include <libufs.h>
62 #include <paths.h>
63 #include <stdio.h>
64 #include <stdlib.h>
65 #include <stdint.h>
66 #include <string.h>
67 #include <unistd.h>
68
69 /* the optimization warning string template */
70 #define OPTWARN "should optimize for %s with minfree %s %d%%"
71
72 struct uufsd disk;
73 #define sblock disk.d_fs
74
75 void usage(void);
76 void printfs(void);
77 int journal_alloc(int64_t size);
78 void journal_clear(void);
79 void sbdirty(void);
80
81 int
82 main(int argc, char *argv[])
83 {
84         char *avalue, *jvalue, *Jvalue, *Lvalue, *lvalue, *Nvalue, *nvalue;
85         const char *special, *on;
86         const char *name;
87         int active;
88         int Aflag, aflag, eflag, evalue, fflag, fvalue, jflag, Jflag, Lflag;
89         int lflag, mflag, mvalue, Nflag, nflag, oflag, ovalue, pflag, sflag;
90         int svalue, Sflag, Svalue;
91         int ch, found_arg, i;
92         const char *chg[2];
93         struct ufs_args args;
94         struct statfs stfs;
95
96         if (argc < 3)
97                 usage();
98         Aflag = aflag = eflag = fflag = jflag = Jflag = Lflag = lflag = 0;
99         mflag = Nflag = nflag = oflag = pflag = sflag = 0;
100         avalue = jvalue = Jvalue = Lvalue = lvalue = Nvalue = nvalue = NULL;
101         evalue = fvalue = mvalue = ovalue = svalue = Svalue = 0;
102         active = 0;
103         found_arg = 0;          /* At least one arg is required. */
104         while ((ch = getopt(argc, argv, "Aa:e:f:j:J:L:l:m:N:n:o:ps:S:")) != -1)
105                 switch (ch) {
106
107                 case 'A':
108                         found_arg = 1;
109                         Aflag++;
110                         break;
111
112                 case 'a':
113                         found_arg = 1;
114                         name = "POSIX.1e ACLs";
115                         avalue = optarg;
116                         if (strcmp(avalue, "enable") &&
117                             strcmp(avalue, "disable")) {
118                                 errx(10, "bad %s (options are %s)",
119                                     name, "`enable' or `disable'");
120                         }
121                         aflag = 1;
122                         break;
123
124                 case 'e':
125                         found_arg = 1;
126                         name = "maximum blocks per file in a cylinder group";
127                         evalue = atoi(optarg);
128                         if (evalue < 1)
129                                 errx(10, "%s must be >= 1 (was %s)",
130                                     name, optarg);
131                         eflag = 1;
132                         break;
133
134                 case 'f':
135                         found_arg = 1;
136                         name = "average file size";
137                         fvalue = atoi(optarg);
138                         if (fvalue < 1)
139                                 errx(10, "%s must be >= 1 (was %s)",
140                                     name, optarg);
141                         fflag = 1;
142                         break;
143
144                 case 'j':
145                         found_arg = 1;
146                         name = "softdep journaled file system";
147                         jvalue = optarg;
148                         if (strcmp(jvalue, "enable") &&
149                             strcmp(jvalue, "disable")) {
150                                 errx(10, "bad %s (options are %s)",
151                                     name, "`enable' or `disable'");
152                         }
153                         jflag = 1;
154                         break;
155
156                 case 'J':
157                         found_arg = 1;
158                         name = "gjournaled file system";
159                         Jvalue = optarg;
160                         if (strcmp(Jvalue, "enable") &&
161                             strcmp(Jvalue, "disable")) {
162                                 errx(10, "bad %s (options are %s)",
163                                     name, "`enable' or `disable'");
164                         }
165                         Jflag = 1;
166                         break;
167
168
169                 case 'L':
170                         found_arg = 1;
171                         name = "volume label";
172                         Lvalue = optarg;
173                         i = -1;
174                         while (isalnum(Lvalue[++i]));
175                         if (Lvalue[i] != '\0') {
176                                 errx(10,
177                                 "bad %s. Valid characters are alphanumerics.",
178                                     name);
179                         }
180                         if (strlen(Lvalue) >= MAXVOLLEN) {
181                                 errx(10, "bad %s. Length is longer than %d.",
182                                     name, MAXVOLLEN - 1);
183                         }
184                         Lflag = 1;
185                         break;
186
187                 case 'l':
188                         found_arg = 1;
189                         name = "multilabel MAC file system";
190                         lvalue = optarg;
191                         if (strcmp(lvalue, "enable") &&
192                             strcmp(lvalue, "disable")) {
193                                 errx(10, "bad %s (options are %s)",
194                                     name, "`enable' or `disable'");
195                         }
196                         lflag = 1;
197                         break;
198
199                 case 'm':
200                         found_arg = 1;
201                         name = "minimum percentage of free space";
202                         mvalue = atoi(optarg);
203                         if (mvalue < 0 || mvalue > 99)
204                                 errx(10, "bad %s (%s)", name, optarg);
205                         mflag = 1;
206                         break;
207
208                 case 'N':
209                         found_arg = 1;
210                         name = "NFSv4 ACLs";
211                         Nvalue = optarg;
212                         if (strcmp(Nvalue, "enable") &&
213                             strcmp(Nvalue, "disable")) {
214                                 errx(10, "bad %s (options are %s)",
215                                     name, "`enable' or `disable'");
216                         }
217                         Nflag = 1;
218                         break;
219
220                 case 'n':
221                         found_arg = 1;
222                         name = "soft updates";
223                         nvalue = optarg;
224                         if (strcmp(nvalue, "enable") != 0 &&
225                             strcmp(nvalue, "disable") != 0) {
226                                 errx(10, "bad %s (options are %s)",
227                                     name, "`enable' or `disable'");
228                         }
229                         nflag = 1;
230                         break;
231
232                 case 'o':
233                         found_arg = 1;
234                         name = "optimization preference";
235                         if (strcmp(optarg, "space") == 0)
236                                 ovalue = FS_OPTSPACE;
237                         else if (strcmp(optarg, "time") == 0)
238                                 ovalue = FS_OPTTIME;
239                         else
240                                 errx(10,
241                                     "bad %s (options are `space' or `time')",
242                                     name);
243                         oflag = 1;
244                         break;
245
246                 case 'p':
247                         found_arg = 1;
248                         pflag = 1;
249                         break;
250
251                 case 's':
252                         found_arg = 1;
253                         name = "expected number of files per directory";
254                         svalue = atoi(optarg);
255                         if (svalue < 1)
256                                 errx(10, "%s must be >= 1 (was %s)",
257                                     name, optarg);
258                         sflag = 1;
259                         break;
260
261                 case 'S':
262                         found_arg = 1;
263                         name = "Softdep Journal Size";
264                         Svalue = atoi(optarg);
265                         if (Svalue < SUJ_MIN)
266                                 errx(10, "%s must be >= %d (was %s)",
267                                     name, SUJ_MIN, optarg);
268                         Sflag = 1;
269                         break;
270
271                 default:
272                         usage();
273                 }
274         argc -= optind;
275         argv += optind;
276         if (found_arg == 0 || argc != 1)
277                 usage();
278
279         on = special = argv[0];
280         if (ufs_disk_fillout(&disk, special) == -1)
281                 goto err;
282         if (disk.d_name != special) {
283                 if (statfs(special, &stfs) != 0)
284                         warn("Can't stat %s", special);
285                 if (strcmp(special, stfs.f_mntonname) == 0)
286                         active = 1;
287         }
288
289         if (pflag) {
290                 printfs();
291                 exit(0);
292         }
293         if (Lflag) {
294                 name = "volume label";
295                 strlcpy(sblock.fs_volname, Lvalue, MAXVOLLEN);
296         }
297         if (aflag) {
298                 name = "POSIX.1e ACLs";
299                 if (strcmp(avalue, "enable") == 0) {
300                         if (sblock.fs_flags & FS_ACLS) {
301                                 warnx("%s remains unchanged as enabled", name);
302                         } else if (sblock.fs_flags & FS_NFS4ACLS) {
303                                 warnx("%s and NFSv4 ACLs are mutually "
304                                     "exclusive", name);
305                         } else {
306                                 sblock.fs_flags |= FS_ACLS;
307                                 warnx("%s set", name);
308                         }
309                 } else if (strcmp(avalue, "disable") == 0) {
310                         if ((~sblock.fs_flags & FS_ACLS) ==
311                             FS_ACLS) {
312                                 warnx("%s remains unchanged as disabled",
313                                     name);
314                         } else {
315                                 sblock.fs_flags &= ~FS_ACLS;
316                                 warnx("%s cleared", name);
317                         }
318                 }
319         }
320         if (eflag) {
321                 name = "maximum blocks per file in a cylinder group";
322                 if (sblock.fs_maxbpg == evalue)
323                         warnx("%s remains unchanged as %d", name, evalue);
324                 else {
325                         warnx("%s changes from %d to %d",
326                             name, sblock.fs_maxbpg, evalue);
327                         sblock.fs_maxbpg = evalue;
328                 }
329         }
330         if (fflag) {
331                 name = "average file size";
332                 if (sblock.fs_avgfilesize == (unsigned)fvalue) {
333                         warnx("%s remains unchanged as %d", name, fvalue);
334                 }
335                 else {
336                         warnx("%s changes from %d to %d",
337                                         name, sblock.fs_avgfilesize, fvalue);
338                         sblock.fs_avgfilesize = fvalue;
339                 }
340         }
341         if (jflag) {
342                 name = "soft updates journaling";
343                 if (strcmp(jvalue, "enable") == 0) {
344                         if ((sblock.fs_flags & (FS_DOSOFTDEP | FS_SUJ)) ==
345                             (FS_DOSOFTDEP | FS_SUJ)) {
346                                 warnx("%s remains unchanged as enabled", name);
347                         } else if (sblock.fs_clean == 0) {
348                                 warnx("%s cannot be enabled until fsck is run",
349                                     name);
350                         } else if (journal_alloc(Svalue) != 0) {
351                                 warnx("%s can not be enabled", name);
352                         } else {
353                                 sblock.fs_flags |= FS_DOSOFTDEP | FS_SUJ;
354                                 warnx("%s set", name);
355                         }
356                 } else if (strcmp(jvalue, "disable") == 0) {
357                         if ((~sblock.fs_flags & FS_SUJ) == FS_SUJ) {
358                                 warnx("%s remains unchanged as disabled", name);
359                         } else {
360                                 journal_clear();
361                                 sblock.fs_flags &= ~(FS_DOSOFTDEP | FS_SUJ);
362                                 sblock.fs_sujfree = 0;
363                                 warnx("%s cleared, "
364                                     "remove .sujournal to reclaim space", name);
365                         }
366                 }
367         }
368         if (Jflag) {
369                 name = "gjournal";
370                 if (strcmp(Jvalue, "enable") == 0) {
371                         if (sblock.fs_flags & FS_GJOURNAL) {
372                                 warnx("%s remains unchanged as enabled", name);
373                         } else {
374                                 sblock.fs_flags |= FS_GJOURNAL;
375                                 warnx("%s set", name);
376                         }
377                 } else if (strcmp(Jvalue, "disable") == 0) {
378                         if ((~sblock.fs_flags & FS_GJOURNAL) ==
379                             FS_GJOURNAL) {
380                                 warnx("%s remains unchanged as disabled",
381                                     name);
382                         } else {
383                                 sblock.fs_flags &= ~FS_GJOURNAL;
384                                 warnx("%s cleared", name);
385                         }
386                 }
387         }
388         if (lflag) {
389                 name = "multilabel";
390                 if (strcmp(lvalue, "enable") == 0) {
391                         if (sblock.fs_flags & FS_MULTILABEL) {
392                                 warnx("%s remains unchanged as enabled", name);
393                         } else {
394                                 sblock.fs_flags |= FS_MULTILABEL;
395                                 warnx("%s set", name);
396                         }
397                 } else if (strcmp(lvalue, "disable") == 0) {
398                         if ((~sblock.fs_flags & FS_MULTILABEL) ==
399                             FS_MULTILABEL) {
400                                 warnx("%s remains unchanged as disabled",
401                                     name);
402                         } else {
403                                 sblock.fs_flags &= ~FS_MULTILABEL;
404                                 warnx("%s cleared", name);
405                         }
406                 }
407         }
408         if (mflag) {
409                 name = "minimum percentage of free space";
410                 if (sblock.fs_minfree == mvalue)
411                         warnx("%s remains unchanged as %d%%", name, mvalue);
412                 else {
413                         warnx("%s changes from %d%% to %d%%",
414                                     name, sblock.fs_minfree, mvalue);
415                         sblock.fs_minfree = mvalue;
416                         if (mvalue >= MINFREE && sblock.fs_optim == FS_OPTSPACE)
417                                 warnx(OPTWARN, "time", ">=", MINFREE);
418                         if (mvalue < MINFREE && sblock.fs_optim == FS_OPTTIME)
419                                 warnx(OPTWARN, "space", "<", MINFREE);
420                 }
421         }
422         if (Nflag) {
423                 name = "NFSv4 ACLs";
424                 if (strcmp(Nvalue, "enable") == 0) {
425                         if (sblock.fs_flags & FS_NFS4ACLS) {
426                                 warnx("%s remains unchanged as enabled", name);
427                         } else if (sblock.fs_flags & FS_ACLS) {
428                                 warnx("%s and POSIX.1e ACLs are mutually "
429                                     "exclusive", name);
430                         } else {
431                                 sblock.fs_flags |= FS_NFS4ACLS;
432                                 warnx("%s set", name);
433                         }
434                 } else if (strcmp(Nvalue, "disable") == 0) {
435                         if ((~sblock.fs_flags & FS_NFS4ACLS) ==
436                             FS_NFS4ACLS) {
437                                 warnx("%s remains unchanged as disabled",
438                                     name);
439                         } else {
440                                 sblock.fs_flags &= ~FS_NFS4ACLS;
441                                 warnx("%s cleared", name);
442                         }
443                 }
444         }
445         if (nflag) {
446                 name = "soft updates";
447                 if (strcmp(nvalue, "enable") == 0) {
448                         if (sblock.fs_flags & FS_DOSOFTDEP)
449                                 warnx("%s remains unchanged as enabled", name);
450                         else if (sblock.fs_clean == 0) {
451                                 warnx("%s cannot be enabled until fsck is run",
452                                     name);
453                         } else {
454                                 sblock.fs_flags |= FS_DOSOFTDEP;
455                                 warnx("%s set", name);
456                         }
457                 } else if (strcmp(nvalue, "disable") == 0) {
458                         if ((~sblock.fs_flags & FS_DOSOFTDEP) == FS_DOSOFTDEP)
459                                 warnx("%s remains unchanged as disabled", name);
460                         else {
461                                 sblock.fs_flags &= ~FS_DOSOFTDEP;
462                                 warnx("%s cleared", name);
463                         }
464                 }
465         }
466         if (oflag) {
467                 name = "optimization preference";
468                 chg[FS_OPTSPACE] = "space";
469                 chg[FS_OPTTIME] = "time";
470                 if (sblock.fs_optim == ovalue)
471                         warnx("%s remains unchanged as %s", name, chg[ovalue]);
472                 else {
473                         warnx("%s changes from %s to %s",
474                                     name, chg[sblock.fs_optim], chg[ovalue]);
475                         sblock.fs_optim = ovalue;
476                         if (sblock.fs_minfree >= MINFREE &&
477                             ovalue == FS_OPTSPACE)
478                                 warnx(OPTWARN, "time", ">=", MINFREE);
479                         if (sblock.fs_minfree < MINFREE && ovalue == FS_OPTTIME)
480                                 warnx(OPTWARN, "space", "<", MINFREE);
481                 }
482         }
483         if (sflag) {
484                 name = "expected number of files per directory";
485                 if (sblock.fs_avgfpdir == (unsigned)svalue) {
486                         warnx("%s remains unchanged as %d", name, svalue);
487                 }
488                 else {
489                         warnx("%s changes from %d to %d",
490                                         name, sblock.fs_avgfpdir, svalue);
491                         sblock.fs_avgfpdir = svalue;
492                 }
493         }
494
495         if (sbwrite(&disk, Aflag) == -1)
496                 goto err;
497         ufs_disk_close(&disk);
498         if (active) {
499                 bzero(&args, sizeof(args));
500                 if (mount("ufs", on,
501                     stfs.f_flags | MNT_UPDATE | MNT_RELOAD, &args) < 0)
502                         err(9, "%s: reload", special);
503                 warnx("file system reloaded");
504         }
505         exit(0);
506 err:
507         if (disk.d_error != NULL)
508                 errx(11, "%s: %s", special, disk.d_error);
509         else
510                 err(12, "%s", special);
511 }
512
513 void
514 sbdirty(void)
515 {
516         disk.d_fs.fs_flags |= FS_UNCLEAN | FS_NEEDSFSCK;
517         disk.d_fs.fs_clean = 0;
518 }
519
520 int blocks;
521 static char clrbuf[MAXBSIZE];
522
523 static ufs2_daddr_t
524 journal_balloc(void)
525 {
526         ufs2_daddr_t blk;
527         struct cg *cgp;
528         int valid;
529         static int contig = 1;
530
531         cgp = &disk.d_cg;
532         for (;;) {
533                 blk = cgballoc(&disk);
534                 if (blk > 0)
535                         break;
536                 /*
537                  * If we failed to allocate a block from this cg, move to
538                  * the next.
539                  */
540                 if (cgwrite(&disk) < 0) {
541                         warn("Failed to write updated cg");
542                         return (-1);
543                 }
544                 while ((valid = cgread(&disk)) == 1) {
545                         /*
546                          * Try to minimize fragmentation by requiring a minimum
547                          * number of blocks present.
548                          */
549                         if (cgp->cg_cs.cs_nbfree > 128 * 1024 * 1024)
550                                 break;
551                         if (contig == 0 && cgp->cg_cs.cs_nbfree)
552                                 break;
553                 }
554                 if (valid)
555                         continue;
556                 /*
557                  * Try once through looking only for large contiguous regions
558                  * and again taking any space we can find.
559                  */
560                 if (contig) {
561                         contig = 0;
562                         disk.d_ccg = 0;
563                         warnx("Journal file fragmented.");
564                         continue;
565                 }
566                 warnx("Failed to find sufficient free blocks for the journal");
567                 return -1;
568         }
569         if (bwrite(&disk, fsbtodb(&sblock, blk), clrbuf,
570             sblock.fs_bsize) <= 0) {
571                 warn("Failed to initialize new block");
572                 return -1;
573         }
574         return (blk);
575 }
576
577 /*
578  * Search a directory block for the SUJ_FILE.
579  */
580 static ino_t
581 dir_search(ufs2_daddr_t blk, int bytes)
582 {
583         char block[MAXBSIZE];
584         struct direct *dp;
585         int off;
586
587         if (bread(&disk, fsbtodb(&sblock, blk), block, bytes) <= 0) {
588                 warn("Failed to read dir block");
589                 return (-1);
590         }
591         for (off = 0; off < bytes; off += dp->d_reclen) {
592                 dp = (struct direct *)&block[off];
593                 if (dp->d_reclen == 0)
594                         break;
595                 if (dp->d_ino == 0)
596                         continue;
597                 if (dp->d_namlen != strlen(SUJ_FILE))
598                         continue;
599                 if (bcmp(dp->d_name, SUJ_FILE, dp->d_namlen) != 0)
600                         continue;
601                 return (dp->d_ino);
602         }
603
604         return (0);
605 }
606
607 /*
608  * Search in the ROOTINO for the SUJ_FILE.  If it exists we can not enable
609  * journaling.
610  */
611 static ino_t
612 journal_findfile(void)
613 {
614         struct ufs1_dinode *dp1;
615         struct ufs2_dinode *dp2;
616         ino_t ino;
617         int mode;
618         void *ip;
619         int i;
620
621         if (getino(&disk, &ip, ROOTINO, &mode) != 0) {
622                 warn("Failed to get root inode");
623                 return (-1);
624         }
625         dp2 = ip;
626         dp1 = ip;
627         if (sblock.fs_magic == FS_UFS1_MAGIC) {
628                 if ((off_t)dp1->di_size >= lblktosize(&sblock, NDADDR)) {
629                         warnx("ROOTINO extends beyond direct blocks.");
630                         return (-1);
631                 }
632                 for (i = 0; i < NDADDR; i++) {
633                         if (dp1->di_db[i] == 0)
634                                 break;
635                         if ((ino = dir_search(dp1->di_db[i],
636                             sblksize(&sblock, (off_t)dp1->di_size, i))) != 0)
637                                 return (ino);
638                 }
639         } else {
640                 if ((off_t)dp1->di_size >= lblktosize(&sblock, NDADDR)) {
641                         warnx("ROOTINO extends beyond direct blocks.");
642                         return (-1);
643                 }
644                 for (i = 0; i < NDADDR; i++) {
645                         if (dp2->di_db[i] == 0)
646                                 break;
647                         if ((ino = dir_search(dp2->di_db[i],
648                             sblksize(&sblock, (off_t)dp2->di_size, i))) != 0)
649                                 return (ino);
650                 }
651         }
652
653         return (0);
654 }
655
656 /*
657  * Insert the journal at inode 'ino' into directory blk 'blk' at the first
658  * free offset of 'off'.  DIRBLKSIZ blocks after off are initialized as
659  * empty.
660  */
661 static int
662 dir_insert(ufs2_daddr_t blk, off_t off, ino_t ino)
663 {
664         struct direct *dp;
665         char block[MAXBSIZE];
666
667         if (bread(&disk, fsbtodb(&sblock, blk), block, sblock.fs_bsize) <= 0) {
668                 warn("Failed to read dir block");
669                 return (-1);
670         }
671         bzero(&block[off], sblock.fs_bsize - off);
672         dp = (struct direct *)&block[off];
673         dp->d_ino = ino;
674         dp->d_reclen = DIRBLKSIZ;
675         dp->d_type = DT_REG;
676         dp->d_namlen = strlen(SUJ_FILE);
677         bcopy(SUJ_FILE, &dp->d_name, strlen(SUJ_FILE));
678         off += DIRBLKSIZ;
679         for (; off < sblock.fs_bsize; off += DIRBLKSIZ) {
680                 dp = (struct direct *)&block[off];
681                 dp->d_ino = 0;
682                 dp->d_reclen = DIRBLKSIZ;
683                 dp->d_type = DT_UNKNOWN;
684         }
685         if (bwrite(&disk, fsbtodb(&sblock, blk), block, sblock.fs_bsize) <= 0) {
686                 warn("Failed to write dir block");
687                 return (-1);
688         }
689         return (0);
690 }
691
692 /*
693  * Extend a directory block in 'blk' by copying it to a full size block
694  * and inserting the new journal inode into .sujournal.
695  */
696 static int
697 dir_extend(ufs2_daddr_t blk, ufs2_daddr_t nblk, off_t size, ino_t ino)
698 {
699         char block[MAXBSIZE];
700
701         if (bread(&disk, fsbtodb(&sblock, blk), block, size) <= 0) {
702                 warn("Failed to read dir block");
703                 return (-1);
704         }
705         if (bwrite(&disk, fsbtodb(&sblock, nblk), block, size) <= 0) {
706                 warn("Failed to write dir block");
707                 return (-1);
708         }
709
710         return dir_insert(nblk, size, ino);
711 }
712
713 /*
714  * Insert the journal file into the ROOTINO directory.  We always extend the
715  * last frag
716  */
717 static int
718 journal_insertfile(ino_t ino)
719 {
720         struct ufs1_dinode *dp1;
721         struct ufs2_dinode *dp2;
722         void *ip;
723         ufs2_daddr_t nblk;
724         ufs2_daddr_t blk;
725         ufs_lbn_t lbn;
726         int size;
727         int mode;
728         int off;
729
730         if (getino(&disk, &ip, ROOTINO, &mode) != 0) {
731                 warn("Failed to get root inode");
732                 sbdirty();
733                 return (-1);
734         }
735         dp2 = ip;
736         dp1 = ip;
737         blk = 0;
738         size = 0;
739         nblk = journal_balloc();
740         if (nblk <= 0)
741                 return (-1);
742         /*
743          * For simplicity sake we aways extend the ROOTINO into a new
744          * directory block rather than searching for space and inserting
745          * into an existing block.  However, if the rootino has frags
746          * have to free them and extend the block.
747          */
748         if (sblock.fs_magic == FS_UFS1_MAGIC) {
749                 lbn = lblkno(&sblock, dp1->di_size);
750                 off = blkoff(&sblock, dp1->di_size);
751                 blk = dp1->di_db[lbn];
752                 size = sblksize(&sblock, (off_t)dp1->di_size, lbn);
753         } else {
754                 lbn = lblkno(&sblock, dp2->di_size);
755                 off = blkoff(&sblock, dp2->di_size);
756                 blk = dp2->di_db[lbn];
757                 size = sblksize(&sblock, (off_t)dp2->di_size, lbn);
758         }
759         if (off != 0) {
760                 if (dir_extend(blk, nblk, off, ino) == -1)
761                         return (-1);
762         } else {
763                 blk = 0;
764                 if (dir_insert(nblk, 0, ino) == -1)
765                         return (-1);
766         }
767         if (sblock.fs_magic == FS_UFS1_MAGIC) {
768                 dp1->di_blocks += (sblock.fs_bsize - size) / DEV_BSIZE;
769                 dp1->di_db[lbn] = nblk;
770                 dp1->di_size = lblktosize(&sblock, lbn+1);
771         } else {
772                 dp2->di_blocks += (sblock.fs_bsize - size) / DEV_BSIZE;
773                 dp2->di_db[lbn] = nblk;
774                 dp2->di_size = lblktosize(&sblock, lbn+1);
775         }
776         if (putino(&disk) < 0) {
777                 warn("Failed to write root inode");
778                 return (-1);
779         }
780         if (cgwrite(&disk) < 0) {
781                 warn("Failed to write updated cg");
782                 sbdirty();
783                 return (-1);
784         }
785         if (blk) {
786                 if (cgbfree(&disk, blk, size) < 0) {
787                         warn("Failed to write cg");
788                         return (-1);
789                 }
790         }
791
792         return (0);
793 }
794
795 static int
796 indir_fill(ufs2_daddr_t blk, int level, int *resid)
797 {
798         char indirbuf[MAXBSIZE];
799         ufs1_daddr_t *bap1;
800         ufs2_daddr_t *bap2;
801         ufs2_daddr_t nblk;
802         int ncnt;
803         int cnt;
804         int i;
805
806         bzero(indirbuf, sizeof(indirbuf));
807         bap1 = (ufs1_daddr_t *)indirbuf;
808         bap2 = (void *)bap1;
809         cnt = 0;
810         for (i = 0; i < NINDIR(&sblock) && *resid != 0; i++) {
811                 nblk = journal_balloc();
812                 if (nblk <= 0)
813                         return (-1);
814                 cnt++;
815                 if (sblock.fs_magic == FS_UFS1_MAGIC)
816                         *bap1++ = nblk;
817                 else
818                         *bap2++ = nblk;
819                 if (level != 0) {
820                         ncnt = indir_fill(nblk, level - 1, resid);
821                         if (ncnt <= 0)
822                                 return (-1);
823                         cnt += ncnt;
824                 } else 
825                         (*resid)--;
826         }
827         if (bwrite(&disk, fsbtodb(&sblock, blk), indirbuf,
828             sblock.fs_bsize) <= 0) {
829                 warn("Failed to write indirect");
830                 return (-1);
831         }
832         return (cnt);
833 }
834
835 /*
836  * Clear the flag bits so the journal can be removed.
837  */
838 void
839 journal_clear(void)
840 {
841         struct ufs1_dinode *dp1;
842         struct ufs2_dinode *dp2;
843         ino_t ino;
844         int mode;
845         void *ip;
846
847         ino = journal_findfile();
848         if (ino == (ino_t)-1 || ino == 0) {
849                 warnx("Journal file does not exist");
850                 return;
851         }
852         printf("Clearing journal flags from inode %d\n", ino);
853         if (getino(&disk, &ip, ino, &mode) != 0) {
854                 warn("Failed to get journal inode");
855                 return;
856         }
857         dp2 = ip;
858         dp1 = ip;
859         if (sblock.fs_magic == FS_UFS1_MAGIC)
860                 dp1->di_flags = 0;
861         else
862                 dp2->di_flags = 0;
863         if (putino(&disk) < 0) {
864                 warn("Failed to write journal inode");
865                 return;
866         }
867 }
868
869 int
870 journal_alloc(int64_t size)
871 {
872         struct ufs1_dinode *dp1;
873         struct ufs2_dinode *dp2;
874         ufs2_daddr_t blk;
875         void *ip;
876         struct cg *cgp;
877         int resid;
878         ino_t ino;
879         int blks;
880         int mode;
881         int i;
882
883         cgp = &disk.d_cg;
884         ino = 0;
885
886         /*
887          * If the journal file exists we can't allocate it.
888          */
889         ino = journal_findfile();
890         if (ino == (ino_t)-1)
891                 return (-1);
892         if (ino > 0) {
893                 warnx("Journal file %s already exists, please remove.",
894                     SUJ_FILE);
895                 return (-1);
896         }
897         /*
898          * If the user didn't supply a size pick one based on the filesystem
899          * size constrained with hardcoded MIN and MAX values.  We opt for
900          * 1/1024th of the filesystem up to MAX but not exceeding one CG and
901          * not less than the MIN.
902          */
903         if (size == 0) {
904                 size = (sblock.fs_size * sblock.fs_bsize) / 1024;
905                 size = MIN(SUJ_MAX, size);
906                 if (size / sblock.fs_fsize > sblock.fs_fpg)
907                         size = sblock.fs_fpg * sblock.fs_fsize;
908                 size = MAX(SUJ_MIN, size);
909         }
910         resid = blocks = size / sblock.fs_bsize;
911         if (sblock.fs_cstotal.cs_nbfree < blocks) {
912                 warn("Insufficient free space for %jd byte journal", size);
913                 return (-1);
914         }
915         /*
916          * Find a cg with enough blocks to satisfy the journal
917          * size.  Presently the journal does not span cgs.
918          */
919         while (cgread(&disk) == 1) {
920                 if (cgp->cg_cs.cs_nifree == 0)
921                         continue;
922                 ino = cgialloc(&disk);
923                 if (ino <= 0)
924                         break;
925                 printf("Using inode %d in cg %d for %jd byte journal\n", 
926                     ino, cgp->cg_cgx, size);
927                 if (getino(&disk, &ip, ino, &mode) != 0) {
928                         warn("Failed to get allocated inode");
929                         sbdirty();
930                         goto out;
931                 }
932                 /*
933                  * We leave fields unrelated to the number of allocated
934                  * blocks and size uninitialized.  This causes legacy
935                  * fsck implementations to clear the inode.
936                  */
937                 dp2 = ip;
938                 dp1 = ip;
939                 if (sblock.fs_magic == FS_UFS1_MAGIC) {
940                         bzero(dp1, sizeof(*dp1));
941                         dp1->di_size = size;
942                         dp1->di_mode = IFREG | IREAD;
943                         dp1->di_nlink = 1;
944                         dp1->di_flags = SF_IMMUTABLE | SF_NOUNLINK | UF_NODUMP;
945                 } else {
946                         bzero(dp2, sizeof(*dp2));
947                         dp2->di_size = size;
948                         dp2->di_mode = IFREG | IREAD;
949                         dp2->di_nlink = 1;
950                         dp2->di_flags = SF_IMMUTABLE | SF_NOUNLINK | UF_NODUMP;
951                 }
952                 for (i = 0; i < NDADDR && resid; i++, resid--) {
953                         blk = journal_balloc();
954                         if (blk <= 0)
955                                 goto out;
956                         if (sblock.fs_magic == FS_UFS1_MAGIC) {
957                                 dp1->di_db[i] = blk;
958                                 dp1->di_blocks++;
959                         } else {
960                                 dp2->di_db[i] = blk;
961                                 dp2->di_blocks++;
962                         }
963                 }
964                 for (i = 0; i < NIADDR && resid; i++) {
965                         blk = journal_balloc();
966                         if (blk <= 0)
967                                 goto out;
968                         blks = indir_fill(blk, i, &resid) + 1;
969                         if (blks <= 0) {
970                                 sbdirty();
971                                 goto out;
972                         }
973                         if (sblock.fs_magic == FS_UFS1_MAGIC) {
974                                 dp1->di_ib[i] = blk;
975                                 dp1->di_blocks += blks;
976                         } else {
977                                 dp2->di_ib[i] = blk;
978                                 dp2->di_blocks += blks;
979                         }
980                 }
981                 if (sblock.fs_magic == FS_UFS1_MAGIC)
982                         dp1->di_blocks *= sblock.fs_bsize / disk.d_bsize;
983                 else
984                         dp2->di_blocks *= sblock.fs_bsize / disk.d_bsize;
985                 if (putino(&disk) < 0) {
986                         warn("Failed to write inode");
987                         sbdirty();
988                         return (-1);
989                 }
990                 if (cgwrite(&disk) < 0) {
991                         warn("Failed to write updated cg");
992                         sbdirty();
993                         return (-1);
994                 }
995                 if (journal_insertfile(ino) < 0) {
996                         sbdirty();
997                         return (-1);
998                 }
999                 sblock.fs_sujfree = 0;
1000                 return (0);
1001         }
1002         warnx("Insufficient free space for the journal.");
1003 out:
1004         return (-1);
1005 }
1006
1007 void
1008 usage(void)
1009 {
1010         fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n",
1011 "usage: tunefs [-A] [-a enable | disable] [-e maxbpg] [-f avgfilesize]",
1012 "              [-J enable | disable] [-j enable | disable]", 
1013 "              [-L volname] [-l enable | disable] [-m minfree]",
1014 "              [-N enable | disable] [-n enable | disable]",
1015 "              [-o space | time] [-p] [-s avgfpdir] special | filesystem");
1016         exit(2);
1017 }
1018
1019 void
1020 printfs(void)
1021 {
1022         warnx("POSIX.1e ACLs: (-a)                                %s",
1023                 (sblock.fs_flags & FS_ACLS)? "enabled" : "disabled");
1024         warnx("NFSv4 ACLs: (-N)                                   %s",
1025                 (sblock.fs_flags & FS_NFS4ACLS)? "enabled" : "disabled");
1026         warnx("MAC multilabel: (-l)                               %s",
1027                 (sblock.fs_flags & FS_MULTILABEL)? "enabled" : "disabled");
1028         warnx("soft updates: (-n)                                 %s", 
1029                 (sblock.fs_flags & FS_DOSOFTDEP)? "enabled" : "disabled");
1030         warnx("soft update journaling: (-j)                       %s", 
1031                 (sblock.fs_flags & FS_SUJ)? "enabled" : "disabled");
1032         warnx("gjournal: (-J)                                     %s",
1033                 (sblock.fs_flags & FS_GJOURNAL)? "enabled" : "disabled");
1034         warnx("maximum blocks per file in a cylinder group: (-e)  %d",
1035               sblock.fs_maxbpg);
1036         warnx("average file size: (-f)                            %d",
1037               sblock.fs_avgfilesize);
1038         warnx("average number of files in a directory: (-s)       %d",
1039               sblock.fs_avgfpdir);
1040         warnx("minimum percentage of free space: (-m)             %d%%",
1041               sblock.fs_minfree);
1042         warnx("optimization preference: (-o)                      %s",
1043               sblock.fs_optim == FS_OPTSPACE ? "space" : "time");
1044         if (sblock.fs_minfree >= MINFREE &&
1045             sblock.fs_optim == FS_OPTSPACE)
1046                 warnx(OPTWARN, "time", ">=", MINFREE);
1047         if (sblock.fs_minfree < MINFREE &&
1048             sblock.fs_optim == FS_OPTTIME)
1049                 warnx(OPTWARN, "space", "<", MINFREE);
1050         warnx("volume label: (-L)                                 %s",
1051                 sblock.fs_volname);
1052 }