Whamcloud - gitweb
Add support for on-line resizing ala the resize inode. This patch
[tools/e2fsprogs.git] / misc / mke2fs.c
1 /*
2  * mke2fs.c - Make a ext2fs filesystem.
3  * 
4  * Copyright (C) 1994, 1995, 1996, 1997 Theodore Ts'o.
5  *
6  * %Begin-Header%
7  * This file may be redistributed under the terms of the GNU Public
8  * License.
9  * %End-Header%
10  */
11
12 /* Usage: mke2fs [options] device
13  * 
14  * The device may be a block device or a image of one, but this isn't
15  * enforced (but it's not much fun on a character device :-). 
16  */
17
18 #include <stdio.h>
19 #include <string.h>
20 #include <fcntl.h>
21 #include <ctype.h>
22 #include <time.h>
23 #ifdef __linux__
24 #include <sys/utsname.h>
25 #endif
26 #ifdef HAVE_GETOPT_H
27 #include <getopt.h>
28 #else
29 extern char *optarg;
30 extern int optind;
31 #endif
32 #ifdef HAVE_UNISTD_H
33 #include <unistd.h>
34 #endif
35 #ifdef HAVE_STDLIB_H
36 #include <stdlib.h>
37 #endif
38 #ifdef HAVE_ERRNO_H
39 #include <errno.h>
40 #endif
41 #ifdef HAVE_MNTENT_H
42 #include <mntent.h>
43 #endif
44 #include <sys/ioctl.h>
45 #include <sys/types.h>
46
47 #include "ext2fs/ext2_fs.h"
48 #include "et/com_err.h"
49 #include "uuid/uuid.h"
50 #include "e2p/e2p.h"
51 #include "ext2fs/ext2fs.h"
52 #include "util.h"
53 #include "../version.h"
54 #include "nls-enable.h"
55
56 #define STRIDE_LENGTH 8
57
58 #ifndef __sparc__
59 #define ZAP_BOOTBLOCK
60 #endif
61
62 extern int isatty(int);
63 extern FILE *fpopen(const char *cmd, const char *mode);
64
65 const char * program_name = "mke2fs";
66 const char * device_name /* = NULL */;
67
68 /* Command line options */
69 int     cflag;
70 int     verbose;
71 int     quiet;
72 int     super_only;
73 int     force;
74 int     noaction;
75 int     journal_size;
76 int     journal_flags;
77 char    *bad_blocks_filename;
78 __u32   fs_stride;
79
80 struct ext2_super_block param;
81 char *creator_os;
82 char *volume_label;
83 char *mount_dir;
84 char *journal_device;
85 int sync_kludge;        /* Set using the MKE2FS_SYNC env. option */
86
87 int sys_page_size = 4096;
88 int linux_version_code = 0;
89
90 static void usage(void)
91 {
92         fprintf(stderr, _("Usage: %s [-c|-t|-l filename] [-b block-size] "
93         "[-f fragment-size]\n\t[-i bytes-per-inode] [-j] [-J journal-options]"
94         " [-N number-of-inodes]\n\t[-m reserved-blocks-percentage] "
95         "[-o creator-os] [-g blocks-per-group]\n\t[-L volume-label] "
96         "[-M last-mounted-directory] [-O feature[,...]]\n\t"
97         "[-r fs-revision] [-R options] [-qvSV] device [blocks-count]\n"),
98                 program_name);
99         exit(1);
100 }
101
102 static int int_log2(int arg)
103 {
104         int     l = 0;
105
106         arg >>= 1;
107         while (arg) {
108                 l++;
109                 arg >>= 1;
110         }
111         return l;
112 }
113
114 static int int_log10(unsigned int arg)
115 {
116         int     l;
117
118         for (l=0; arg ; l++)
119                 arg = arg / 10;
120         return l;
121 }
122
123 static int parse_version_number(const char *s)
124 {
125         int     major, minor, rev;
126         char    *endptr;
127         const char *cp = s;
128
129         if (!s)
130                 return 0;
131         major = strtol(cp, &endptr, 10);
132         if (cp == endptr || *endptr != '.')
133                 return 0;
134         cp = endptr + 1;
135         minor = strtol(cp, &endptr, 10);
136         if (cp == endptr || *endptr != '.')
137                 return 0;
138         cp = endptr + 1;
139         rev = strtol(cp, &endptr, 10);
140         if (cp == endptr)
141                 return 0;
142         return ((((major * 256) + minor) * 256) + rev);
143 }
144
145
146
147 /*
148  * This function sets the default parameters for a filesystem
149  *
150  * The type is specified by the user.  The size is the maximum size
151  * (in megabytes) for which a set of parameters applies, with a size
152  * of zero meaning that it is the default parameter for the type.
153  * Note that order is important in the table below.
154  */
155 #define DEF_MAX_BLOCKSIZE -1
156 static char default_str[] = "default";
157 struct mke2fs_defaults {
158         const char      *type;
159         int             size;
160         int             blocksize;
161         int             inode_ratio;
162 } settings[] = {
163         { default_str, 0, 4096, 8192 },
164         { default_str, 512, 1024, 4096 },
165         { default_str, 3, 1024, 8192 },
166         { "journal", 0, 4096, 8192 },
167         { "news", 0, 4096, 4096 },
168         { "largefile", 0, DEF_MAX_BLOCKSIZE, 1024 * 1024 },
169         { "largefile4", 0, DEF_MAX_BLOCKSIZE, 4096 * 1024 },
170         { 0, 0, 0, 0},
171 };
172
173 static void set_fs_defaults(const char *fs_type,
174                             struct ext2_super_block *super,
175                             int blocksize, int sector_size,
176                             int *inode_ratio)
177 {
178         int     megs;
179         int     ratio = 0;
180         struct mke2fs_defaults *p;
181         int     use_bsize = 1024;
182
183         megs = super->s_blocks_count * (EXT2_BLOCK_SIZE(super) / 1024) / 1024;
184         if (inode_ratio)
185                 ratio = *inode_ratio;
186         if (!fs_type)
187                 fs_type = default_str;
188         for (p = settings; p->type; p++) {
189                 if ((strcmp(p->type, fs_type) != 0) &&
190                     (strcmp(p->type, default_str) != 0))
191                         continue;
192                 if ((p->size != 0) && (megs > p->size))
193                         continue;
194                 if (ratio == 0)
195                         *inode_ratio = p->inode_ratio < blocksize ?
196                                 blocksize : p->inode_ratio;
197                 use_bsize = p->blocksize;
198         }
199         if (blocksize <= 0) {
200                 if (use_bsize == DEF_MAX_BLOCKSIZE) {
201                         use_bsize = sys_page_size;
202                         if ((linux_version_code < (2*65536 + 6*256)) &&
203                             (use_bsize > 4096))
204                                 use_bsize = 4096;
205                 }
206                 if (sector_size && use_bsize < sector_size)
207                         use_bsize = sector_size;
208                 if ((blocksize < 0) && (use_bsize < (-blocksize)))
209                         use_bsize = -blocksize;
210                 blocksize = use_bsize;
211                 super->s_blocks_count /= blocksize / 1024;
212         }
213         super->s_log_frag_size = super->s_log_block_size =
214                 int_log2(blocksize >> EXT2_MIN_BLOCK_LOG_SIZE);
215 }
216
217
218 /*
219  * Helper function for read_bb_file and test_disk
220  */
221 static void invalid_block(ext2_filsys fs EXT2FS_ATTR((unused)), blk_t blk)
222 {
223         fprintf(stderr, _("Bad block %u out of range; ignored.\n"), blk);
224         return;
225 }
226
227 /*
228  * Reads the bad blocks list from a file
229  */
230 static void read_bb_file(ext2_filsys fs, badblocks_list *bb_list,
231                          const char *bad_blocks_file)
232 {
233         FILE            *f;
234         errcode_t       retval;
235
236         f = fopen(bad_blocks_file, "r");
237         if (!f) {
238                 com_err("read_bad_blocks_file", errno,
239                         _("while trying to open %s"), bad_blocks_file);
240                 exit(1);
241         }
242         retval = ext2fs_read_bb_FILE(fs, f, bb_list, invalid_block);
243         fclose (f);
244         if (retval) {
245                 com_err("ext2fs_read_bb_FILE", retval,
246                         _("while reading in list of bad blocks from file"));
247                 exit(1);
248         }
249 }
250
251 /*
252  * Runs the badblocks program to test the disk
253  */
254 static void test_disk(ext2_filsys fs, badblocks_list *bb_list)
255 {
256         FILE            *f;
257         errcode_t       retval;
258         char            buf[1024];
259
260         sprintf(buf, "badblocks -b %d %s%s%s %d", fs->blocksize,
261                 quiet ? "" : "-s ", (cflag > 1) ? "-w " : "",
262                 fs->device_name, fs->super->s_blocks_count);
263         if (verbose)
264                 printf(_("Running command: %s\n"), buf);
265         f = popen(buf, "r");
266         if (!f) {
267                 com_err("popen", errno,
268                         _("while trying run '%s'"), buf);
269                 exit(1);
270         }
271         retval = ext2fs_read_bb_FILE(fs, f, bb_list, invalid_block);
272         pclose(f);
273         if (retval) {
274                 com_err("ext2fs_read_bb_FILE", retval,
275                         _("while processing list of bad blocks from program"));
276                 exit(1);
277         }
278 }
279
280 static void handle_bad_blocks(ext2_filsys fs, badblocks_list bb_list)
281 {
282         dgrp_t                  i;
283         blk_t                   j;
284         unsigned                must_be_good;
285         blk_t                   blk;
286         badblocks_iterate       bb_iter;
287         errcode_t               retval;
288         blk_t                   group_block;
289         int                     group;
290         int                     group_bad;
291
292         if (!bb_list)
293                 return;
294         
295         /*
296          * The primary superblock and group descriptors *must* be
297          * good; if not, abort.
298          */
299         must_be_good = fs->super->s_first_data_block + 1 + fs->desc_blocks;
300         for (i = fs->super->s_first_data_block; i <= must_be_good; i++) {
301                 if (ext2fs_badblocks_list_test(bb_list, i)) {
302                         fprintf(stderr, _("Block %d in primary "
303                                 "superblock/group descriptor area bad.\n"), i);
304                         fprintf(stderr, _("Blocks %d through %d must be good "
305                                 "in order to build a filesystem.\n"),
306                                 fs->super->s_first_data_block, must_be_good);
307                         fputs(_("Aborting....\n"), stderr);
308                         exit(1);
309                 }
310         }
311
312         /*
313          * See if any of the bad blocks are showing up in the backup
314          * superblocks and/or group descriptors.  If so, issue a
315          * warning and adjust the block counts appropriately.
316          */
317         group_block = fs->super->s_first_data_block +
318                 fs->super->s_blocks_per_group;
319         
320         for (i = 1; i < fs->group_desc_count; i++) {
321                 group_bad = 0;
322                 for (j=0; j < fs->desc_blocks+1; j++) {
323                         if (ext2fs_badblocks_list_test(bb_list,
324                                                        group_block + j)) {
325                                 if (!group_bad) 
326                                         fprintf(stderr,
327 _("Warning: the backup superblock/group descriptors at block %d contain\n"
328 "       bad blocks.\n\n"),
329                                                 group_block);
330                                 group_bad++;
331                                 group = ext2fs_group_of_blk(fs, group_block+j);
332                                 fs->group_desc[group].bg_free_blocks_count++;
333                                 fs->super->s_free_blocks_count++;
334                         }
335                 }
336                 group_block += fs->super->s_blocks_per_group;
337         }
338         
339         /*
340          * Mark all the bad blocks as used...
341          */
342         retval = ext2fs_badblocks_list_iterate_begin(bb_list, &bb_iter);
343         if (retval) {
344                 com_err("ext2fs_badblocks_list_iterate_begin", retval,
345                         _("while marking bad blocks as used"));
346                 exit(1);
347         }
348         while (ext2fs_badblocks_list_iterate(bb_iter, &blk)) 
349                 ext2fs_mark_block_bitmap(fs->block_map, blk);
350         ext2fs_badblocks_list_iterate_end(bb_iter);
351 }
352
353 /*
354  * These functions implement a generalized progress meter.
355  */
356 struct progress_struct {
357         char            format[20];
358         char            backup[80];
359         __u32           max;
360         int             skip_progress;
361 };
362
363 static void progress_init(struct progress_struct *progress,
364                           const char *label,__u32 max)
365 {
366         int     i;
367
368         memset(progress, 0, sizeof(struct progress_struct));
369         if (quiet)
370                 return;
371
372         /*
373          * Figure out how many digits we need
374          */
375         i = int_log10(max);
376         sprintf(progress->format, "%%%dd/%%%dld", i, i);
377         memset(progress->backup, '\b', sizeof(progress->backup)-1);
378         progress->backup[sizeof(progress->backup)-1] = 0;
379         if ((2*i)+1 < (int) sizeof(progress->backup))
380                 progress->backup[(2*i)+1] = 0;
381         progress->max = max;
382
383         progress->skip_progress = 0;
384         if (getenv("MKE2FS_SKIP_PROGRESS"))
385                 progress->skip_progress++;
386
387         fputs(label, stdout);
388         fflush(stdout);
389 }
390
391 static void progress_update(struct progress_struct *progress, __u32 val)
392 {
393         if ((progress->format[0] == 0) || progress->skip_progress)
394                 return;
395         printf(progress->format, val, progress->max);
396         fputs(progress->backup, stdout);
397 }
398
399 static void progress_close(struct progress_struct *progress)
400 {
401         if (progress->format[0] == 0)
402                 return;
403         fputs(_("done                            \n"), stdout);
404 }
405
406
407 /*
408  * Helper function which zeros out _num_ blocks starting at _blk_.  In
409  * case of an error, the details of the error is returned via _ret_blk_
410  * and _ret_count_ if they are non-NULL pointers.  Returns 0 on
411  * success, and an error code on an error.
412  *
413  * As a special case, if the first argument is NULL, then it will
414  * attempt to free the static zeroizing buffer.  (This is to keep
415  * programs that check for memory leaks happy.)
416  */
417 static errcode_t zero_blocks(ext2_filsys fs, blk_t blk, int num,
418                              struct progress_struct *progress,
419                              blk_t *ret_blk, int *ret_count)
420 {
421         int             j, count, next_update, next_update_incr;
422         static char     *buf;
423         errcode_t       retval;
424
425         /* If fs is null, clean up the static buffer and return */
426         if (!fs) {
427                 if (buf) {
428                         free(buf);
429                         buf = 0;
430                 }
431                 return 0;
432         }
433         /* Allocate the zeroizing buffer if necessary */
434         if (!buf) {
435                 buf = malloc(fs->blocksize * STRIDE_LENGTH);
436                 if (!buf) {
437                         com_err("malloc", ENOMEM,
438                                 _("while allocating zeroizing buffer"));
439                         exit(1);
440                 }
441                 memset(buf, 0, fs->blocksize * STRIDE_LENGTH);
442         }
443         /* OK, do the write loop */
444         next_update = 0;
445         next_update_incr = num / 100;
446         if (next_update_incr < 1)
447                 next_update_incr = 1;
448         for (j=0; j < num; j += STRIDE_LENGTH, blk += STRIDE_LENGTH) {
449                 count = num - j;
450                 if (count > STRIDE_LENGTH)
451                         count = STRIDE_LENGTH;
452                 retval = io_channel_write_blk(fs->io, blk, count, buf);
453                 if (retval) {
454                         if (ret_count)
455                                 *ret_count = count;
456                         if (ret_blk)
457                                 *ret_blk = blk;
458                         return retval;
459                 }
460                 if (progress && j > next_update) {
461                         next_update += num / 100;
462                         progress_update(progress, blk);
463                 }
464         }
465         return 0;
466 }       
467
468 static void write_inode_tables(ext2_filsys fs)
469 {
470         errcode_t       retval;
471         blk_t           blk;
472         dgrp_t          i;
473         int             num;
474         struct progress_struct progress;
475
476         if (quiet)
477                 memset(&progress, 0, sizeof(progress));
478         else
479                 progress_init(&progress, _("Writing inode tables: "),
480                               fs->group_desc_count);
481
482         for (i = 0; i < fs->group_desc_count; i++) {
483                 progress_update(&progress, i);
484                 
485                 blk = fs->group_desc[i].bg_inode_table;
486                 num = fs->inode_blocks_per_group;
487
488                 retval = zero_blocks(fs, blk, num, 0, &blk, &num);
489                 if (retval) {
490                         fprintf(stderr, _("\nCould not write %d blocks "
491                                 "in inode table starting at %d: %s\n"),
492                                 num, blk, error_message(retval));
493                         exit(1);
494                 }
495                 if (sync_kludge) {
496                         if (sync_kludge == 1)
497                                 sync();
498                         else if ((i % sync_kludge) == 0)
499                                 sync();
500                 }
501         }
502         zero_blocks(0, 0, 0, 0, 0, 0);
503         progress_close(&progress);
504 }
505
506 static void create_root_dir(ext2_filsys fs)
507 {
508         errcode_t       retval;
509         struct ext2_inode       inode;
510
511         retval = ext2fs_mkdir(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, 0);
512         if (retval) {
513                 com_err("ext2fs_mkdir", retval, _("while creating root dir"));
514                 exit(1);
515         }
516         if (geteuid()) {
517                 retval = ext2fs_read_inode(fs, EXT2_ROOT_INO, &inode);
518                 if (retval) {
519                         com_err("ext2fs_read_inode", retval,
520                                 _("while reading root inode"));
521                         exit(1);
522                 }
523                 inode.i_uid = getuid();
524                 if (inode.i_uid)
525                         inode.i_gid = getgid();
526                 retval = ext2fs_write_inode(fs, EXT2_ROOT_INO, &inode);
527                 if (retval) {
528                         com_err("ext2fs_write_inode", retval,
529                                 _("while setting root inode ownership"));
530                         exit(1);
531                 }
532         }
533 }
534
535 static void create_lost_and_found(ext2_filsys fs)
536 {
537         errcode_t               retval;
538         ext2_ino_t              ino;
539         const char              *name = "lost+found";
540         int                     i;
541         int                     lpf_size = 0;
542
543         fs->umask = 077;
544         retval = ext2fs_mkdir(fs, EXT2_ROOT_INO, 0, name);
545         if (retval) {
546                 com_err("ext2fs_mkdir", retval,
547                         _("while creating /lost+found"));
548                 exit(1);
549         }
550
551         retval = ext2fs_lookup(fs, EXT2_ROOT_INO, name, strlen(name), 0, &ino);
552         if (retval) {
553                 com_err("ext2_lookup", retval,
554                         _("while looking up /lost+found"));
555                 exit(1);
556         }
557         
558         for (i=1; i < EXT2_NDIR_BLOCKS; i++) {
559                 if ((lpf_size += fs->blocksize) >= 16*1024)
560                         break;
561                 retval = ext2fs_expand_dir(fs, ino);
562                 if (retval) {
563                         com_err("ext2fs_expand_dir", retval,
564                                 _("while expanding /lost+found"));
565                         exit(1);
566                 }
567         }
568 }
569
570 static void create_bad_block_inode(ext2_filsys fs, badblocks_list bb_list)
571 {
572         errcode_t       retval;
573         
574         ext2fs_mark_inode_bitmap(fs->inode_map, EXT2_BAD_INO);
575         fs->group_desc[0].bg_free_inodes_count--;
576         fs->super->s_free_inodes_count--;
577         retval = ext2fs_update_bb_inode(fs, bb_list);
578         if (retval) {
579                 com_err("ext2fs_update_bb_inode", retval,
580                         _("while setting bad block inode"));
581                 exit(1);
582         }
583
584 }
585
586 static void reserve_inodes(ext2_filsys fs)
587 {
588         ext2_ino_t      i;
589         int             group;
590
591         for (i = EXT2_ROOT_INO + 1; i < EXT2_FIRST_INODE(fs->super); i++) {
592                 ext2fs_mark_inode_bitmap(fs->inode_map, i);
593                 group = ext2fs_group_of_ino(fs, i);
594                 fs->group_desc[group].bg_free_inodes_count--;
595                 fs->super->s_free_inodes_count--;
596         }
597         ext2fs_mark_ib_dirty(fs);
598 }
599
600 #define BSD_DISKMAGIC   (0x82564557UL)  /* The disk magic number */
601 #define BSD_MAGICDISK   (0x57455682UL)  /* The disk magic number reversed */
602 #define BSD_LABEL_OFFSET        64
603
604 static void zap_sector(ext2_filsys fs, int sect, int nsect)
605 {
606         char *buf;
607         int retval;
608         unsigned int *magic;
609
610         buf = malloc(512*nsect);
611         if (!buf) {
612                 printf(_("Out of memory erasing sectors %d-%d\n"),
613                        sect, sect + nsect - 1);
614                 exit(1);
615         }
616
617         if (sect == 0) {
618                 /* Check for a BSD disklabel, and don't erase it if so */
619                 retval = io_channel_read_blk(fs->io, 0, -512, buf);
620                 if (retval)
621                         fprintf(stderr,
622                                 _("Warning: could not read block 0: %s\n"),
623                                 error_message(retval));
624                 else {
625                         magic = (unsigned int *) (buf + BSD_LABEL_OFFSET);
626                         if ((*magic == BSD_DISKMAGIC) ||
627                             (*magic == BSD_MAGICDISK))
628                                 return;
629                 }
630         }
631
632         memset(buf, 0, 512*nsect);
633         io_channel_set_blksize(fs->io, 512);
634         retval = io_channel_write_blk(fs->io, sect, -512*nsect, buf);
635         io_channel_set_blksize(fs->io, fs->blocksize);
636         free(buf);
637         if (retval)
638                 fprintf(stderr, _("Warning: could not erase sector %d: %s\n"),
639                         sect, error_message(retval));
640 }
641
642 static void create_journal_dev(ext2_filsys fs)
643 {
644         struct progress_struct progress;
645         errcode_t               retval;
646         char                    *buf;
647         blk_t                   blk;
648         int                     count;
649
650         retval = ext2fs_create_journal_superblock(fs,
651                                   fs->super->s_blocks_count, 0, &buf);
652         if (retval) {
653                 com_err("create_journal_dev", retval,
654                         _("while initializing journal superblock"));
655                 exit(1);
656         }
657         if (quiet)
658                 memset(&progress, 0, sizeof(progress));
659         else
660                 progress_init(&progress, _("Zeroing journal device: "),
661                               fs->super->s_blocks_count);
662
663         retval = zero_blocks(fs, 0, fs->super->s_blocks_count,
664                              &progress, &blk, &count);
665         if (retval) {
666                 com_err("create_journal_dev", retval,
667                         _("while zeroing journal device (block %u, count %d)"),
668                         blk, count);
669                 exit(1);
670         }
671         zero_blocks(0, 0, 0, 0, 0, 0);
672
673         retval = io_channel_write_blk(fs->io,
674                                       fs->super->s_first_data_block+1,
675                                       1, buf);
676         if (retval) {
677                 com_err("create_journal_dev", retval,
678                         _("while writing journal superblock"));
679                 exit(1);
680         }
681         progress_close(&progress);
682 }
683
684 static void show_stats(ext2_filsys fs)
685 {
686         struct ext2_super_block *s = fs->super;
687         char                    buf[80];
688         blk_t                   group_block;
689         dgrp_t                  i;
690         int                     need, col_left;
691         
692         if (param.s_blocks_count != s->s_blocks_count)
693                 fprintf(stderr, _("warning: %d blocks unused.\n\n"),
694                        param.s_blocks_count - s->s_blocks_count);
695
696         memset(buf, 0, sizeof(buf));
697         strncpy(buf, s->s_volume_name, sizeof(s->s_volume_name));
698         printf(_("Filesystem label=%s\n"), buf);
699         fputs(_("OS type: "), stdout);
700         switch (fs->super->s_creator_os) {
701             case EXT2_OS_LINUX: fputs("Linux", stdout); break;
702             case EXT2_OS_HURD:  fputs("GNU/Hurd", stdout);   break;
703             case EXT2_OS_MASIX: fputs ("Masix", stdout); break;
704             default:            fputs(_("(unknown os)"), stdout);
705         }
706         printf("\n");
707         printf(_("Block size=%u (log=%u)\n"), fs->blocksize,
708                 s->s_log_block_size);
709         printf(_("Fragment size=%u (log=%u)\n"), fs->fragsize,
710                 s->s_log_frag_size);
711         printf(_("%u inodes, %u blocks\n"), s->s_inodes_count,
712                s->s_blocks_count);
713         printf(_("%u blocks (%2.2f%%) reserved for the super user\n"),
714                 s->s_r_blocks_count,
715                100.0 * s->s_r_blocks_count / s->s_blocks_count);
716         printf(_("First data block=%u\n"), s->s_first_data_block);
717         if (s->s_reserved_gdt_blocks)
718                 printf(_("Maximum filesystem blocks=%lu\n"),
719                        (s->s_reserved_gdt_blocks + fs->desc_blocks) *
720                        (fs->blocksize / sizeof(struct ext2_group_desc)) *
721                        s->s_blocks_per_group);
722         if (fs->group_desc_count > 1)
723                 printf(_("%u block groups\n"), fs->group_desc_count);
724         else
725                 printf(_("%u block group\n"), fs->group_desc_count);
726         printf(_("%u blocks per group, %u fragments per group\n"),
727                s->s_blocks_per_group, s->s_frags_per_group);
728         printf(_("%u inodes per group\n"), s->s_inodes_per_group);
729
730         if (fs->group_desc_count == 1) {
731                 printf("\n");
732                 return;
733         }
734
735         printf(_("Superblock backups stored on blocks: "));
736         group_block = s->s_first_data_block;
737         col_left = 0;
738         for (i = 1; i < fs->group_desc_count; i++) {
739                 group_block += s->s_blocks_per_group;
740                 if (!ext2fs_bg_has_super(fs, i))
741                         continue;
742                 if (i != 1)
743                         printf(", ");
744                 need = int_log10(group_block) + 2;
745                 if (need > col_left) {
746                         printf("\n\t");
747                         col_left = 72;
748                 }
749                 col_left -= need;
750                 printf("%u", group_block);
751         }
752         printf("\n\n");
753 }
754
755 /*
756  * Set the S_CREATOR_OS field.  Return true if OS is known,
757  * otherwise, 0.
758  */
759 static int set_os(struct ext2_super_block *sb, char *os)
760 {
761         if (isdigit (*os))
762                 sb->s_creator_os = atoi (os);
763         else if (strcasecmp(os, "linux") == 0)
764                 sb->s_creator_os = EXT2_OS_LINUX;
765         else if (strcasecmp(os, "GNU") == 0 || strcasecmp(os, "hurd") == 0)
766                 sb->s_creator_os = EXT2_OS_HURD;
767         else if (strcasecmp(os, "masix") == 0)
768                 sb->s_creator_os = EXT2_OS_MASIX;
769         else
770                 return 0;
771         return 1;
772 }
773
774 #define PATH_SET "PATH=/sbin"
775
776 static void parse_r_opts(struct ext2_super_block *param, const char *opts)
777 {
778         char    *buf, *token, *next, *p, *arg;
779         int     len;
780         int     r_usage = 0;
781
782         len = strlen(opts);
783         buf = malloc(len+1);
784         if (!buf) {
785                 fprintf(stderr,
786                         _("Couldn't allocate memory to parse options!\n"));
787                 exit(1);
788         }
789         strcpy(buf, opts);
790         for (token = buf; token && *token; token = next) {
791                 p = strchr(token, ',');
792                 next = 0;
793                 if (p) {
794                         *p = 0;
795                         next = p+1;
796                 }
797                 arg = strchr(token, '=');
798                 if (arg) {
799                         *arg = 0;
800                         arg++;
801                 }
802                 if (strcmp(token, "stride") == 0) {
803                         if (!arg) {
804                                 r_usage++;
805                                 continue;
806                         }
807                         fs_stride = strtoul(arg, &p, 0);
808                         if (*p || (fs_stride == 0)) {
809                                 fprintf(stderr,
810                                         _("Invalid stride parameter.\n"));
811                                 r_usage++;
812                                 continue;
813                         }
814                 } else if (!strcmp(token, "resize")) {
815                         unsigned long resize = 1;
816                         int tmp;
817
818                         if (!arg) {
819                                 r_usage++;
820                                 continue;
821                         }
822
823                         p = &arg[strlen(arg) - 1];
824
825                         switch(*p++) {
826                         case 'T':
827                         case 't': resize <<= 10; /* no break */
828                         case 'G':
829                         case 'g': resize <<= 10; /* no break */
830                         case 'M':
831                         case 'm': resize <<= 10; /* no break */
832                         case 'K':
833                         case 'k': resize >>= param->s_log_block_size -10; *p = 0; break;
834                         case 'b': resize >>= param->s_log_block_size - 9; *p = 0; break;
835                         case '0': break;
836                         case '1': break;
837                         case '2': break;
838                         case '3': break;
839                         case '4': break;
840                         case '5': break;
841                         case '6': break;
842                         case '7': break;
843                         case '8': break;
844                         case '9': break;
845                         default: r_usage++; continue;
846                         }
847
848                         resize *= strtoul(arg, NULL, 0);
849
850                         if (resize == 0) {
851                                 fprintf(stderr,
852                                         _("Invalid resize parameter.\n"));
853                                 r_usage++;
854                                 continue;
855                         }
856                         param->s_feature_compat |=
857                                 EXT2_FEATURE_COMPAT_RESIZE_INODE;
858                         tmp = param->s_blocks_per_group;
859                         if (tmp > EXT2_MAX_BLOCKS_PER_GROUP(param))
860                                 tmp = EXT2_MAX_BLOCKS_PER_GROUP(param);
861                         resize = (resize + tmp - 1) / tmp;
862                         tmp = (1 << param->s_log_block_size) /
863                                 sizeof(struct ext2_group_desc);
864                         resize = (resize + tmp - 1) / tmp;
865                         /* XXX param->s_res_gdt_blocks = resize - existing
866                         cur_groups = (resize - sb->s_first_data_block +
867                                       EXT2_BLOCKS_PER_GROUP(super) - 1) /bpg;
868                         cur_gdb = (cur_groups + gdpb - 1) / gdpb;
869                         */
870
871                 } else
872                         r_usage++;
873         }
874         if (r_usage) {
875                 fprintf(stderr, _("\nBad options specified.\n\n"
876                         "Options are separated by commas, "
877                         "and may take an argument which\n"
878                         "\tis set off by an equals ('=') sign.\n\n"
879                         "Valid raid options are:\n"
880                         "\tstride=<stride length in blocks>\n"
881                         "\tresize=<resize maximum size in blocks>\n\n"));
882                 exit(1);
883         }
884 }       
885
886 static __u32 ok_features[3] = {
887         EXT3_FEATURE_COMPAT_HAS_JOURNAL |
888                 EXT2_FEATURE_COMPAT_RESIZE_INODE |
889                 EXT2_FEATURE_COMPAT_DIR_INDEX,  /* Compat */
890         EXT2_FEATURE_INCOMPAT_FILETYPE|         /* Incompat */
891                 EXT3_FEATURE_INCOMPAT_JOURNAL_DEV|
892                 EXT2_FEATURE_INCOMPAT_META_BG,
893         EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER     /* R/O compat */
894 };
895
896
897 static void PRS(int argc, char *argv[])
898 {
899         int             b, c;
900         int             size;
901         char *          tmp;
902         int             blocksize = 0;
903         int             inode_ratio = 0;
904         int             inode_size = 0;
905         int             reserved_ratio = 5;
906         int             sector_size = 0;
907         int             show_version_only = 0;
908         ext2_ino_t      num_inodes = 0;
909         errcode_t       retval;
910         char *          oldpath = getenv("PATH");
911         char *          r_opts = 0;
912         const char *    fs_type = 0;
913         blk_t           dev_size;
914 #ifdef __linux__
915         struct          utsname ut;
916 #endif
917         long            sysval;
918
919         /* Update our PATH to include /sbin  */
920         if (oldpath) {
921                 char *newpath;
922                 
923                 newpath = malloc(sizeof (PATH_SET) + 1 + strlen (oldpath));
924                 strcpy (newpath, PATH_SET);
925                 strcat (newpath, ":");
926                 strcat (newpath, oldpath);
927                 putenv (newpath);
928         } else
929                 putenv (PATH_SET);
930
931         tmp = getenv("MKE2FS_SYNC");
932         if (tmp)
933                 sync_kludge = atoi(tmp);
934
935         /* Determine the system page size if possible */
936 #ifdef HAVE_SYSCONF
937 #if (!defined(_SC_PAGESIZE) && defined(_SC_PAGE_SIZE))
938 #define _SC_PAGESIZE _SC_PAGE_SIZE
939 #endif
940 #ifdef _SC_PAGESIZE
941         sysval = sysconf(_SC_PAGESIZE);
942         if (sysval > 0)
943                 sys_page_size = sysval;
944 #endif /* _SC_PAGESIZE */
945 #endif /* HAVE_SYSCONF */
946         
947         setbuf(stdout, NULL);
948         setbuf(stderr, NULL);
949         initialize_ext2_error_table();
950         memset(&param, 0, sizeof(struct ext2_super_block));
951         param.s_rev_level = 1;  /* Create revision 1 filesystems now */
952         param.s_feature_incompat |= EXT2_FEATURE_INCOMPAT_FILETYPE;
953         param.s_feature_ro_compat |= EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER;
954 #if 0
955         param.s_feature_compat |= EXT2_FEATURE_COMPAT_DIR_INDEX;
956 #endif
957
958 #ifdef __linux__
959         if (uname(&ut)) {
960                 perror("uname");
961                 exit(1);
962         }
963         linux_version_code = parse_version_number(ut.release);
964         if (linux_version_code && linux_version_code < (2*65536 + 2*256)) {
965                 param.s_rev_level = 0;
966                 param.s_feature_incompat = 0;
967                 param.s_feature_compat = 0;
968                 param.s_feature_ro_compat = 0;
969         }
970 #endif
971
972         if (argc && *argv) {
973                 program_name = get_progname(*argv);
974
975                 /* If called as mkfs.ext3, create a journal inode */
976                 if (!strcmp(program_name, "mkfs.ext3"))
977                         journal_size = -1;
978         }
979
980         while ((c = getopt (argc, argv,
981                     "b:cf:g:i:jl:m:no:qr:R:s:tvI:J:ST:FL:M:N:O:V")) != EOF)
982                 switch (c) {
983                 case 'b':
984                         blocksize = strtol(optarg, &tmp, 0);
985                         b = (blocksize > 0) ? blocksize : -blocksize;
986                         if (b < EXT2_MIN_BLOCK_SIZE ||
987                             b > EXT2_MAX_BLOCK_SIZE || *tmp) {
988                                 com_err(program_name, 0,
989                                         _("bad block size - %s"), optarg);
990                                 exit(1);
991                         }
992                         if (blocksize > 4096)
993                                 fprintf(stderr, _("Warning: blocksize %d not "
994                                                   "usable on most systems.\n"),
995                                         blocksize);
996                         if (blocksize > 0) 
997                                 param.s_log_block_size =
998                                         int_log2(blocksize >>
999                                                  EXT2_MIN_BLOCK_LOG_SIZE);
1000                         break;
1001                 case 'c':       /* Check for bad blocks */
1002                 case 't':       /* deprecated */
1003                         cflag++;
1004                         break;
1005                 case 'f':
1006                         size = strtoul(optarg, &tmp, 0);
1007                         if (size < EXT2_MIN_BLOCK_SIZE ||
1008                             size > EXT2_MAX_BLOCK_SIZE || *tmp) {
1009                                 com_err(program_name, 0,
1010                                         _("bad fragment size - %s"),
1011                                         optarg);
1012                                 exit(1);
1013                         }
1014                         param.s_log_frag_size =
1015                                 int_log2(size >> EXT2_MIN_BLOCK_LOG_SIZE);
1016                         fprintf(stderr, _("Warning: fragments not supported.  "
1017                                "Ignoring -f option\n"));
1018                         break;
1019                 case 'g':
1020                         param.s_blocks_per_group = strtoul(optarg, &tmp, 0);
1021                         if (*tmp) {
1022                                 com_err(program_name, 0,
1023                                         _("Illegal number for blocks per group"));
1024                                 exit(1);
1025                         }
1026                         if ((param.s_blocks_per_group % 8) != 0) {
1027                                 com_err(program_name, 0,
1028                                 _("blocks per group must be multiple of 8"));
1029                                 exit(1);
1030                         }
1031                         break;
1032                 case 'i':
1033                         inode_ratio = strtoul(optarg, &tmp, 0);
1034                         if (inode_ratio < EXT2_MIN_BLOCK_SIZE ||
1035                             inode_ratio > EXT2_MAX_BLOCK_SIZE * 1024 ||
1036                             *tmp) {
1037                                 com_err(program_name, 0,
1038                                         _("bad inode ratio %s (min %d/max %d"),
1039                                         optarg, EXT2_MIN_BLOCK_SIZE,
1040                                         EXT2_MAX_BLOCK_SIZE);
1041                                 exit(1);
1042                         }
1043                         break;
1044                 case 'J':
1045                         parse_journal_opts(optarg);
1046                         break;
1047                 case 'j':
1048                         param.s_feature_compat |=
1049                                 EXT3_FEATURE_COMPAT_HAS_JOURNAL;
1050                         if (!journal_size)
1051                                 journal_size = -1;
1052                         break;
1053                 case 'l':
1054                         bad_blocks_filename = malloc(strlen(optarg)+1);
1055                         if (!bad_blocks_filename) {
1056                                 com_err(program_name, ENOMEM,
1057                                         _("in malloc for bad_blocks_filename"));
1058                                 exit(1);
1059                         }
1060                         strcpy(bad_blocks_filename, optarg);
1061                         break;
1062                 case 'm':
1063                         reserved_ratio = strtoul(optarg, &tmp, 0);
1064                         if (reserved_ratio > 50 || *tmp) {
1065                                 com_err(program_name, 0,
1066                                         _("bad reserved blocks percent - %s"),
1067                                         optarg);
1068                                 exit(1);
1069                         }
1070                         break;
1071                 case 'n':
1072                         noaction++;
1073                         break;
1074                 case 'o':
1075                         creator_os = optarg;
1076                         break;
1077                 case 'r':
1078                         param.s_rev_level = atoi(optarg);
1079                         if (param.s_rev_level == EXT2_GOOD_OLD_REV) {
1080                                 param.s_feature_incompat = 0;
1081                                 param.s_feature_compat = 0;
1082                                 param.s_feature_ro_compat = 0;
1083                         }
1084                         break;
1085                 case 's':       /* deprecated */
1086                         if (atoi(optarg))
1087                                 param.s_feature_ro_compat |=
1088                                         EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER;
1089                         else 
1090                                 param.s_feature_ro_compat &=
1091                                         ~EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER;
1092                         break;
1093 #ifdef EXT2_DYNAMIC_REV
1094                 case 'I':
1095                         inode_size = strtoul(optarg, &tmp, 0);
1096                         if (*tmp) {
1097                                 com_err(program_name, 0,
1098                                         _("bad inode size - %s"), optarg);
1099                                 exit(1);
1100                         }
1101                         break;
1102 #endif
1103                 case 'N':
1104                         num_inodes = atoi(optarg);
1105                         break;
1106                 case 'v':
1107                         verbose = 1;
1108                         break;
1109                 case 'q':
1110                         quiet = 1;
1111                         break;
1112                 case 'F':
1113                         force = 1;
1114                         break;
1115                 case 'L':
1116                         volume_label = optarg;
1117                         break;
1118                 case 'M':
1119                         mount_dir = optarg;
1120                         break;
1121                 case 'O':
1122                         if (!strcmp(optarg, "none")) {
1123                                 param.s_feature_compat = 0;
1124                                 param.s_feature_incompat = 0;
1125                                 param.s_feature_ro_compat = 0;
1126                                 break;
1127                         }
1128                         if (e2p_edit_feature(optarg,
1129                                             &param.s_feature_compat,
1130                                             ok_features)) {
1131                                 fprintf(stderr,
1132                                         _("Invalid filesystem option set: %s\n"), optarg);
1133                                 exit(1);
1134                         }
1135                         break;
1136                 case 'R':
1137                         r_opts = optarg;
1138                         break;
1139                 case 'S':
1140                         super_only = 1;
1141                         break;
1142                 case 'T':
1143                         fs_type = optarg;
1144                         break;
1145                 case 'V':
1146                         /* Print version number and exit */
1147                         show_version_only++;
1148                         break;
1149                 default:
1150                         usage();
1151                 }
1152         if ((optind == argc) && !show_version_only)
1153                 usage();
1154         device_name = argv[optind];
1155         optind++;
1156         if (optind < argc) {
1157                 unsigned long long tmp2 = strtoull(argv[optind++], &tmp, 0);
1158
1159                 if ((*tmp) || (tmp2 > 0xfffffffful)) {
1160                         com_err(program_name, 0, _("bad blocks count - %s"),
1161                                 argv[optind - 1]);
1162                         exit(1);
1163                 }
1164                 param.s_blocks_count = tmp2;
1165         }
1166         if (optind < argc)
1167                 usage();
1168
1169         if (!quiet || show_version_only)
1170                 fprintf (stderr, "mke2fs %s (%s)\n", E2FSPROGS_VERSION, 
1171                          E2FSPROGS_DATE);
1172
1173         if (show_version_only) {
1174                 fprintf(stderr, _("\tUsing %s\n"), 
1175                         error_message(EXT2_ET_BASE));
1176                 exit(0);
1177         }
1178
1179         /*
1180          * If there's no blocksize specified and there is a journal
1181          * device, use it to figure out the blocksize
1182          */
1183         if (blocksize <= 0 && journal_device) {
1184                 ext2_filsys     jfs;
1185                 io_manager      io_ptr;
1186
1187 #ifdef CONFIG_TESTIO_DEBUG
1188                 io_ptr = test_io_manager;
1189                 test_io_backing_manager = unix_io_manager;
1190 #else
1191                 io_ptr = unix_io_manager;
1192 #endif
1193                 retval = ext2fs_open(journal_device,
1194                                      EXT2_FLAG_JOURNAL_DEV_OK, 0,
1195                                      0, io_ptr, &jfs);
1196                 if (retval) {
1197                         com_err(program_name, retval,
1198                                 _("while trying to open journal device %s\n"),
1199                                 journal_device);
1200                         exit(1);
1201                 }
1202                 if ((blocksize < 0) && (jfs->blocksize < (unsigned) (-blocksize))) {
1203                         com_err(program_name, 0,
1204                                 _("Journal dev blocksize (%d) smaller than "
1205                                   "minimum blocksize %d\n"), jfs->blocksize,
1206                                 -blocksize);
1207                         exit(1);
1208                 }
1209                 blocksize = jfs->blocksize;
1210                 param.s_log_block_size =
1211                         int_log2(blocksize >> EXT2_MIN_BLOCK_LOG_SIZE);
1212                 ext2fs_close(jfs);
1213         }
1214
1215         if (blocksize > sys_page_size) {
1216                 if (!force) {
1217                         com_err(program_name, 0,
1218                                 _("%d-byte blocks too big for system (max %d)"),
1219                                 blocksize, sys_page_size);
1220                         proceed_question();
1221                 }
1222                 fprintf(stderr, _("Warning: %d-byte blocks too big for system "
1223                                   "(max %d), forced to continue\n"),
1224                         blocksize, sys_page_size);
1225         }
1226         if ((blocksize > 4096) &&
1227             (param.s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL))
1228                 fprintf(stderr, "\nWarning: some 2.4 kernels do not support "
1229                         "blocksizes greater than 4096 \n\tusing ext3."
1230                         "  Use -b 4096 if this is an issue for you.\n\n");
1231
1232         if (param.s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) {
1233                 if (!fs_type)
1234                         fs_type = "journal";
1235                 reserved_ratio = 0;
1236                 param.s_feature_incompat = EXT3_FEATURE_INCOMPAT_JOURNAL_DEV;
1237                 param.s_feature_compat = 0;
1238                 param.s_feature_ro_compat = 0;
1239         }
1240         if (param.s_rev_level == EXT2_GOOD_OLD_REV &&
1241             (param.s_feature_compat || param.s_feature_ro_compat ||
1242              param.s_feature_incompat))
1243                 param.s_rev_level = 1;  /* Create a revision 1 filesystem */
1244
1245         if (!force)
1246                 check_plausibility(device_name);
1247         check_mount(device_name, force, _("filesystem"));
1248
1249         param.s_log_frag_size = param.s_log_block_size;
1250
1251         if (noaction && param.s_blocks_count) {
1252                 dev_size = param.s_blocks_count;
1253                 retval = 0;
1254         } else
1255                 retval = ext2fs_get_device_size(device_name,
1256                                                 EXT2_BLOCK_SIZE(&param),
1257                                                 &dev_size);
1258         if (retval && (retval != EXT2_ET_UNIMPLEMENTED)) {
1259                 com_err(program_name, retval,
1260                         _("while trying to determine filesystem size"));
1261                 exit(1);
1262         }
1263         if (!param.s_blocks_count) {
1264                 if (retval == EXT2_ET_UNIMPLEMENTED) {
1265                         com_err(program_name, 0,
1266                                 _("Couldn't determine device size; you "
1267                                 "must specify\nthe size of the "
1268                                 "filesystem\n"));
1269                         exit(1);
1270                 } else {
1271                         if (dev_size == 0) {
1272                                 com_err(program_name, 0,
1273                                 _("Device size reported to be zero.  "
1274                                   "Invalid partition specified, or\n\t"
1275                                   "partition table wasn't reread "
1276                                   "after running fdisk, due to\n\t"
1277                                   "a modified partition being busy "
1278                                   "and in use.  You may need to reboot\n\t"
1279                                   "to re-read your partition table.\n"
1280                                   ));
1281                                 exit(1);
1282                         }
1283                         param.s_blocks_count = dev_size;
1284                         if (sys_page_size > EXT2_BLOCK_SIZE(&param))
1285                                 param.s_blocks_count &= ~((sys_page_size /
1286                                                            EXT2_BLOCK_SIZE(&param))-1);
1287                 }
1288                 
1289         } else if (!force && (param.s_blocks_count > dev_size)) {
1290                 com_err(program_name, 0,
1291                         _("Filesystem larger than apparent device size."));
1292                 proceed_question();
1293         }
1294
1295         /*
1296          * If the user asked for HAS_JOURNAL, then make sure a journal
1297          * gets created.
1298          */
1299         if ((param.s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL) &&
1300             !journal_size)
1301                 journal_size = -1;
1302
1303         /* Set first meta blockgroup via an environment variable */
1304         /* (this is mostly for debugging purposes) */
1305         if ((param.s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) &&
1306             ((tmp = getenv("MKE2FS_FIRST_META_BG"))))
1307                 param.s_first_meta_bg = atoi(tmp);
1308
1309         /* Get the hardware sector size, if available */
1310         retval = ext2fs_get_device_sectsize(device_name, &sector_size);
1311         if (retval) {
1312                 com_err(program_name, retval,
1313                         _("while trying to determine hardware sector size"));
1314                 exit(1);
1315         }
1316
1317         if ((tmp = getenv("MKE2FS_DEVICE_SECTSIZE")) != NULL)
1318                 sector_size = atoi(tmp);
1319         
1320         set_fs_defaults(fs_type, &param, blocksize, sector_size, &inode_ratio);
1321         blocksize = EXT2_BLOCK_SIZE(&param);
1322         
1323         if (r_opts)
1324                 parse_r_opts(&param, r_opts);
1325
1326         /* Since sparse_super is the default, we would only have a problem
1327          * here if it was explicitly disabled.
1328          */
1329         if ((param.s_feature_compat & EXT2_FEATURE_COMPAT_RESIZE_INODE) &&
1330             !(param.s_feature_ro_compat&EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)) {
1331                 com_err(program_name, 0,
1332                         _("reserved online resize blocks not supported "
1333                           "on non-sparse filesystem"));
1334                 exit(1);
1335         }
1336
1337         if (param.s_blocks_per_group) {
1338                 if (param.s_blocks_per_group < 256 ||
1339                     param.s_blocks_per_group > 8 * (unsigned) blocksize) {
1340                         com_err(program_name, 0,
1341                                 _("blocks per group count out of range"));
1342                         exit(1);
1343                 }
1344         }
1345
1346         if (inode_size) {
1347                 if (inode_size < EXT2_GOOD_OLD_INODE_SIZE ||
1348                     inode_size > EXT2_BLOCK_SIZE(&param) ||
1349                     inode_size & (inode_size - 1)) {
1350                         com_err(program_name, 0,
1351                                 _("bad inode size %d (min %d/max %d)"),
1352                                 inode_size, EXT2_GOOD_OLD_INODE_SIZE,
1353                                 blocksize);
1354                         exit(1);
1355                 }
1356                 if (inode_size != EXT2_GOOD_OLD_INODE_SIZE)
1357                         fprintf(stderr, _("Warning: %d-byte inodes not usable "
1358                                 "on most systems\n"),
1359                                 inode_size);
1360                 param.s_inode_size = inode_size;
1361         }
1362
1363         /*
1364          * Calculate number of inodes based on the inode ratio
1365          */
1366         param.s_inodes_count = num_inodes ? num_inodes : 
1367                 ((__u64) param.s_blocks_count * blocksize)
1368                         / inode_ratio;
1369
1370         /*
1371          * Calculate number of blocks to reserve
1372          */
1373         param.s_r_blocks_count = (param.s_blocks_count * reserved_ratio) / 100;
1374 }
1375
1376 int main (int argc, char *argv[])
1377 {
1378         errcode_t       retval = 0;
1379         ext2_filsys     fs;
1380         badblocks_list  bb_list = 0;
1381         int             journal_blocks;
1382         unsigned int    i;
1383         int             val;
1384         io_manager      io_ptr;
1385
1386 #ifdef ENABLE_NLS
1387         setlocale(LC_MESSAGES, "");
1388         setlocale(LC_CTYPE, "");
1389         bindtextdomain(NLS_CAT_NAME, LOCALEDIR);
1390         textdomain(NLS_CAT_NAME);
1391 #endif
1392         PRS(argc, argv);
1393
1394 #ifdef CONFIG_TESTIO_DEBUG
1395         io_ptr = test_io_manager;
1396         test_io_backing_manager = unix_io_manager;
1397 #else
1398         io_ptr = unix_io_manager;
1399 #endif
1400
1401         /*
1402          * Initialize the superblock....
1403          */
1404         retval = ext2fs_initialize(device_name, 0, &param,
1405                                    io_ptr, &fs);
1406         if (retval) {
1407                 com_err(device_name, retval, _("while setting up superblock"));
1408                 exit(1);
1409         }
1410
1411         /*
1412          * Wipe out the old on-disk superblock
1413          */
1414         if (!noaction)
1415                 zap_sector(fs, 2, 6);
1416
1417         /*
1418          * Generate a UUID for it...
1419          */
1420         uuid_generate(fs->super->s_uuid);
1421
1422         /*
1423          * Initialize the directory index variables
1424          */
1425         fs->super->s_def_hash_version = EXT2_HASH_TEA;
1426         uuid_generate((unsigned char *) fs->super->s_hash_seed);
1427
1428         /*
1429          * Add "jitter" to the superblock's check interval so that we
1430          * don't check all the filesystems at the same time.  We use a
1431          * kludgy hack of using the UUID to derive a random jitter value.
1432          */
1433         for (i = 0, val = 0 ; i < sizeof(fs->super->s_uuid); i++)
1434                 val += fs->super->s_uuid[i];
1435         fs->super->s_max_mnt_count += val % EXT2_DFL_MAX_MNT_COUNT;
1436
1437         /*
1438          * Override the creator OS, if applicable
1439          */
1440         if (creator_os && !set_os(fs->super, creator_os)) {
1441                 com_err (program_name, 0, _("unknown os - %s"), creator_os);
1442                 exit(1);
1443         }
1444
1445         /*
1446          * For the Hurd, we will turn off filetype since it doesn't
1447          * support it.
1448          */
1449         if (fs->super->s_creator_os == EXT2_OS_HURD)
1450                 fs->super->s_feature_incompat &=
1451                         ~EXT2_FEATURE_INCOMPAT_FILETYPE;
1452
1453         /*
1454          * Set the volume label...
1455          */
1456         if (volume_label) {
1457                 memset(fs->super->s_volume_name, 0,
1458                        sizeof(fs->super->s_volume_name));
1459                 strncpy(fs->super->s_volume_name, volume_label,
1460                         sizeof(fs->super->s_volume_name));
1461         }
1462
1463         /*
1464          * Set the last mount directory
1465          */
1466         if (mount_dir) {
1467                 memset(fs->super->s_last_mounted, 0,
1468                        sizeof(fs->super->s_last_mounted));
1469                 strncpy(fs->super->s_last_mounted, mount_dir,
1470                         sizeof(fs->super->s_last_mounted));
1471         }
1472         
1473         if (!quiet || noaction)
1474                 show_stats(fs);
1475
1476         if (noaction)
1477                 exit(0);
1478
1479         if (fs->super->s_feature_incompat &
1480             EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) {
1481                 create_journal_dev(fs);
1482                 exit(ext2fs_close(fs) ? 1 : 0);
1483         }
1484
1485         if (bad_blocks_filename)
1486                 read_bb_file(fs, &bb_list, bad_blocks_filename);
1487         if (cflag)
1488                 test_disk(fs, &bb_list);
1489
1490         handle_bad_blocks(fs, bb_list);
1491         fs->stride = fs_stride;
1492         retval = ext2fs_allocate_tables(fs);
1493         if (retval) {
1494                 com_err(program_name, retval,
1495                         _("while trying to allocate filesystem tables"));
1496                 exit(1);
1497         }
1498         if (super_only) {
1499                 fs->super->s_state |= EXT2_ERROR_FS;
1500                 fs->flags &= ~(EXT2_FLAG_IB_DIRTY|EXT2_FLAG_BB_DIRTY);
1501         } else {
1502                 /* rsv must be a power of two (64kB is MD RAID sb alignment) */
1503                 unsigned int rsv = 65536 / fs->blocksize;
1504                 unsigned long blocks = fs->super->s_blocks_count;
1505                 unsigned long start;
1506                 blk_t ret_blk;
1507
1508 #ifdef ZAP_BOOTBLOCK
1509                 zap_sector(fs, 0, 2);
1510 #endif
1511
1512                 /*
1513                  * Wipe out any old MD RAID (or other) metadata at the end
1514                  * of the device.  This will also verify that the device is
1515                  * as large as we think.  Be careful with very small devices.
1516                  */
1517                 start = (blocks & ~(rsv - 1));
1518                 if (start > rsv)
1519                         start -= rsv;
1520                 if (start > 0)
1521                         retval = zero_blocks(fs, start, blocks - start,
1522                                              NULL, &ret_blk, NULL);
1523
1524                 if (retval) {
1525                         com_err(program_name, retval,
1526                                 _("while zeroing block %u at end of filesystem"),
1527                                 ret_blk);
1528                 }
1529                 write_inode_tables(fs);
1530                 create_root_dir(fs);
1531                 create_lost_and_found(fs);
1532                 reserve_inodes(fs);
1533                 create_bad_block_inode(fs, bb_list);
1534                 retval = ext2fs_create_resize_inode(fs);
1535                 if (retval) {
1536                         com_err("ext2fs_create_resize_inode", retval,
1537                                 _("while reserving blocks for online resize"));
1538                         exit(1);
1539                 }
1540         }
1541
1542         if (journal_device) {
1543                 ext2_filsys     jfs;
1544                 
1545                 if (!force)
1546                         check_plausibility(journal_device); 
1547                 check_mount(journal_device, force, _("journal"));
1548
1549                 retval = ext2fs_open(journal_device, EXT2_FLAG_RW|
1550                                      EXT2_FLAG_JOURNAL_DEV_OK, 0,
1551                                      fs->blocksize, unix_io_manager, &jfs);
1552                 if (retval) {
1553                         com_err(program_name, retval,
1554                                 _("while trying to open journal device %s\n"),
1555                                 journal_device);
1556                         exit(1);
1557                 }
1558                 if (!quiet) {
1559                         printf(_("Adding journal to device %s: "), 
1560                                journal_device);
1561                         fflush(stdout);
1562                 }
1563                 retval = ext2fs_add_journal_device(fs, jfs);
1564                 if(retval) {
1565                         com_err (program_name, retval, 
1566                                  _("\n\twhile trying to add journal to device %s"), 
1567                                  journal_device);
1568                         exit(1);
1569                 }
1570                 if (!quiet)
1571                         printf(_("done\n"));
1572                 ext2fs_close(jfs);
1573                 free(journal_device);
1574         } else if (journal_size) {
1575                 journal_blocks = figure_journal_size(journal_size, fs);
1576
1577                 if (!journal_blocks) {
1578                         fs->super->s_feature_compat &=
1579                                 ~EXT3_FEATURE_COMPAT_HAS_JOURNAL;
1580                         goto no_journal;
1581                 }
1582                 if (!quiet) {
1583                         printf(_("Creating journal (%d blocks): "),
1584                                journal_blocks);
1585                         fflush(stdout);
1586                 }
1587                 retval = ext2fs_add_journal_inode(fs, journal_blocks,
1588                                                   journal_flags);
1589                 if (retval) {
1590                         com_err (program_name, retval,
1591                                  _("\n\twhile trying to create journal"));
1592                         exit(1);
1593                 }
1594                 if (!quiet)
1595                         printf(_("done\n"));
1596         }
1597 no_journal:
1598
1599         if (!quiet)
1600                 printf(_("Writing superblocks and "
1601                        "filesystem accounting information: "));
1602         retval = ext2fs_flush(fs);
1603         if (retval) {
1604                 fprintf(stderr,
1605                         _("\nWarning, had trouble writing out superblocks."));
1606         }
1607         if (!quiet) {
1608                 printf(_("done\n\n"));
1609                 if (!getenv("MKE2FS_SKIP_CHECK_MSG"))
1610                         print_check_message(fs);
1611         }
1612         val = ext2fs_close(fs);
1613         return (retval || val) ? 1 : 0;
1614 }