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