Whamcloud - gitweb
ChangeLog, mke2fs.c:
[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 #endif
29 #ifdef HAVE_UNISTD_H
30 #include <unistd.h>
31 #endif
32 #ifdef HAVE_STDLIB_H
33 #include <stdlib.h>
34 #endif
35 #ifdef HAVE_ERRNO_H
36 #include <errno.h>
37 #endif
38 #ifdef HAVE_MNTENT_H
39 #include <mntent.h>
40 #endif
41 #include <sys/ioctl.h>
42 #include <sys/types.h>
43
44 #ifdef HAVE_LINUX_FS_H
45 #include <linux/fs.h>
46 #endif
47 #include <linux/ext2_fs.h>
48 #ifdef HAVE_LINUX_MAJOR_H
49 #include <linux/major.h>
50 #include <sys/stat.h>           /* Only need sys/stat.h for major nr test */
51 #endif
52
53 #include "et/com_err.h"
54 #include "uuid/uuid.h"
55 #include "e2p/e2p.h"
56 #include "ext2fs/ext2fs.h"
57 #include "../version.h"
58
59 /* Everything is STDC, these days */
60 #define NOARGS void
61
62 #define STRIDE_LENGTH 8
63
64 #ifndef sparc
65 #define ZAP_BOOTBLOCK
66 #endif
67
68 extern int isatty(int);
69 extern FILE *fpopen(const char *cmd, const char *mode);
70
71 const char * program_name = "mke2fs";
72 const char * device_name = NULL;
73
74 /* Command line options */
75 int     cflag = 0;
76 int     verbose = 0;
77 int     quiet = 0;
78 int     super_only = 0;
79 int     force = 0;
80 int     noaction = 0;
81 char    *bad_blocks_filename = 0;
82 __u32   fs_stride = 0;
83
84 struct ext2_super_block param;
85 char *creator_os = NULL;
86 char *volume_label = NULL;
87 char *mount_dir = NULL;
88
89 static void usage(NOARGS), check_plausibility(NOARGS), check_mount(NOARGS);
90
91 static void usage(NOARGS)
92 {
93         fprintf(stderr, "Usage: %s [-c|-t|-l filename] [-b block-size] "
94         "[-f fragment-size]\n\t[-i bytes-per-inode] "
95         " [-N number-of-inodes]\n\t[-m reserved-blocks-percentage] "
96         "[-o creator-os] [-g blocks-per-group]\n\t[-L volume-label] "
97         "[-M last-mounted-directory] [-O feature[,...]]\n\t"
98         "[-r fs-revision] [-R raid_opts] [-s sparse-super-flag]\n\t"
99         "[-qvSV] device [blocks-count]\n", 
100                 program_name);
101         exit(1);
102 }
103
104 static int log2(int arg)
105 {
106         int     l = 0;
107
108         arg >>= 1;
109         while (arg) {
110                 l++;
111                 arg >>= 1;
112         }
113         return l;
114 }
115
116 static int log10(unsigned int arg)
117 {
118         int     l;
119
120         for (l=0; arg ; l++)
121                 arg = arg / 10;
122         return l;
123 }
124
125 static void proceed_question(NOARGS)
126 {
127         fflush(stdout);
128         fflush(stderr);
129         printf("Proceed anyway? (y,n) ");
130         if (getchar() != 'y')
131                 exit(1);
132 }
133
134 #ifndef SCSI_BLK_MAJOR
135 #define SCSI_BLK_MAJOR(M)  ((M) == SCSI_DISK_MAJOR || (M) == SCSI_CDROM_MAJOR)
136 #endif
137
138 static void check_plausibility(NOARGS)
139 {
140 #ifdef HAVE_LINUX_MAJOR_H
141         int val;
142         struct stat s;
143         
144         val = stat(device_name, &s);
145         
146         if(val == -1) {
147                 fprintf(stderr, "Could not stat %s --- %s\n",
148                         device_name, error_message(errno));
149                 if (errno == ENOENT)
150                         fprintf(stderr, "\nThe device apparently does "
151                                "not exist; did you specify it correctly?\n");
152                 exit(1);
153         }
154         if(!S_ISBLK(s.st_mode)) {
155                 printf("%s is not a block special device.\n", device_name);
156                 proceed_question();
157                 return;
158         } else if ((MAJOR(s.st_rdev) == HD_MAJOR &&
159                     MINOR(s.st_rdev)%64 == 0) ||
160                    (SCSI_BLK_MAJOR(MAJOR(s.st_rdev)) &&
161                        MINOR(s.st_rdev)%16 == 0)) {
162                 printf("%s is entire device, not just one partition!\n", 
163                        device_name);
164                 proceed_question();
165         }
166 #endif
167 }
168
169 static void check_mount(NOARGS)
170 {
171         errcode_t       retval;
172         int             mount_flags;
173
174         retval = ext2fs_check_if_mounted(device_name, &mount_flags);
175         if (retval) {
176                 com_err("ext2fs_check_if_mount", retval,
177                         "while determining whether %s is mounted.",
178                         device_name);
179                 return;
180         }
181         if (!(mount_flags & EXT2_MF_MOUNTED))
182                 return;
183
184         fprintf(stderr, "%s is mounted; ", device_name);
185         if (force) {
186                 fprintf(stderr, "mke2fs forced anyway.  "
187                         "Hope /etc/mtab is incorrect.\n");
188         } else {
189                 fprintf(stderr, "will not make a filesystem here!\n");
190                 exit(1);
191         }
192 }
193
194 /*
195  * This function sets the default parameters for a filesystem
196  *
197  * The type is specified by the user.  The size is the maximum size
198  * (in megabytes) for which a set of parameters applies, with a size
199  * of zero meaning that it is the default parameter for the type.
200  * Note that order is important in the table below.
201  */
202 static char default_str[] = "default";
203 struct mke2fs_defaults {
204         const char      *type;
205         int             size;
206         int             blocksize;
207         int             inode_ratio;
208 } settings[] = {
209         { default_str, 0, 4096, 8192 },
210         { default_str, 512, 1024, 4096 },
211         { default_str, 3, 1024, 8192 },
212         { "news", 0, 4096, 4096 },
213         { 0, 0, 0, 0},
214 };
215
216 static void set_fs_defaults(char *fs_type, struct ext2fs_sb *super,
217                             int blocksize, int *inode_ratio)
218 {
219         int     megs;
220         int     ratio = 0;
221         struct mke2fs_defaults *p;
222
223         megs = (super->s_blocks_count * (EXT2_BLOCK_SIZE(super) / 1024) /
224                 1024);
225         if (inode_ratio)
226                 ratio = *inode_ratio;
227         if (!fs_type)
228                 fs_type = default_str;
229         for (p = settings; p->type; p++) {
230                 if ((strcmp(p->type, fs_type) != 0) &&
231                     (strcmp(p->type, default_str) != 0))
232                         continue;
233                 if ((p->size != 0) &&
234                     (megs > p->size))
235                         continue;
236                 if (ratio == 0)
237                         *inode_ratio = p->inode_ratio;
238                 if (blocksize == 0) {
239                         super->s_log_frag_size = super->s_log_block_size =
240                                 log2(p->blocksize >> EXT2_MIN_BLOCK_LOG_SIZE);
241                 }
242         }
243         if (blocksize == 0)
244                 super->s_blocks_count /= EXT2_BLOCK_SIZE(super) / 1024;
245 }
246
247 /*
248  * Helper function for read_bb_file and test_disk
249  */
250 static void invalid_block(ext2_filsys fs, blk_t blk)
251 {
252         printf("Bad block %u out of range; ignored.\n", blk);
253         return;
254 }
255
256 /*
257  * Reads the bad blocks list from a file
258  */
259 static void read_bb_file(ext2_filsys fs, badblocks_list *bb_list,
260                          const char *bad_blocks_file)
261 {
262         FILE            *f;
263         errcode_t       retval;
264
265         f = fopen(bad_blocks_file, "r");
266         if (!f) {
267                 com_err("read_bad_blocks_file", errno,
268                         "while trying to open %s", bad_blocks_file);
269                 exit(1);
270         }
271         retval = ext2fs_read_bb_FILE(fs, f, bb_list, invalid_block);
272         fclose (f);
273         if (retval) {
274                 com_err("ext2fs_read_bb_FILE", retval,
275                         "while reading in list of bad blocks from file");
276                 exit(1);
277         }
278 }
279
280 /*
281  * Runs the badblocks program to test the disk
282  */
283 static void test_disk(ext2_filsys fs, badblocks_list *bb_list)
284 {
285         FILE            *f;
286         errcode_t       retval;
287         char            buf[1024];
288
289         sprintf(buf, "badblocks -b %d %s%s %d", fs->blocksize,
290                 quiet ? "" : "-s ", fs->device_name,
291                 fs->super->s_blocks_count);
292         if (verbose)
293                 printf("Running command: %s\n", buf);
294         f = popen(buf, "r");
295         if (!f) {
296                 com_err("popen", errno,
297                         "while trying run '%s'", buf);
298                 exit(1);
299         }
300         retval = ext2fs_read_bb_FILE(fs, f, bb_list, invalid_block);
301         pclose(f);
302         if (retval) {
303                 com_err("ext2fs_read_bb_FILE", retval,
304                         "while processing list of bad blocks from program");
305                 exit(1);
306         }
307 }
308
309 static void handle_bad_blocks(ext2_filsys fs, badblocks_list bb_list)
310 {
311         int                     i, j;
312         int                     must_be_good;
313         blk_t                   blk;
314         badblocks_iterate       bb_iter;
315         errcode_t               retval;
316         blk_t                   group_block;
317         int                     group;
318         int                     group_bad;
319
320         if (!bb_list)
321                 return;
322         
323         /*
324          * The primary superblock and group descriptors *must* be
325          * good; if not, abort.
326          */
327         must_be_good = fs->super->s_first_data_block + 1 + fs->desc_blocks;
328         for (i = fs->super->s_first_data_block; i <= must_be_good; i++) {
329                 if (badblocks_list_test(bb_list, i)) {
330                         fprintf(stderr, "Block %d in primary superblock/group "
331                                 "descriptor area bad.\n", i);
332                         fprintf(stderr, "Blocks %d through %d must be good "
333                                 "in order to build a filesystem.\n",
334                                 fs->super->s_first_data_block, must_be_good);
335                         fprintf(stderr, "Aborting....\n");
336                         exit(1);
337                 }
338         }
339
340         /*
341          * See if any of the bad blocks are showing up in the backup
342          * superblocks and/or group descriptors.  If so, issue a
343          * warning and adjust the block counts appropriately.
344          */
345         group_block = fs->super->s_first_data_block +
346                 fs->super->s_blocks_per_group;
347         
348         for (i = 1; i < fs->group_desc_count; i++) {
349                 group_bad = 0;
350                 for (j=0; j < fs->desc_blocks+1; j++) {
351                         if (badblocks_list_test(bb_list, group_block +
352                                                 j)) {
353                                 if (!group_bad) 
354                                         fprintf(stderr,
355 "Warning: the backup superblock/group descriptors at block %d contain\n"
356 "       bad blocks.\n\n",
357                                                 group_block);
358                                 group_bad++;
359                                 group = ext2fs_group_of_blk(fs, group_block+j);
360                                 fs->group_desc[group].bg_free_blocks_count++;
361                                 fs->super->s_free_blocks_count++;
362                         }
363                 }
364                 group_block += fs->super->s_blocks_per_group;
365         }
366         
367         /*
368          * Mark all the bad blocks as used...
369          */
370         retval = badblocks_list_iterate_begin(bb_list, &bb_iter);
371         if (retval) {
372                 com_err("badblocks_list_iterate_begin", retval,
373                         "while marking bad blocks as used");
374                 exit(1);
375         }
376         while (badblocks_list_iterate(bb_iter, &blk)) 
377                 ext2fs_mark_block_bitmap(fs->block_map, blk);
378         badblocks_list_iterate_end(bb_iter);
379 }
380
381 static void write_inode_tables(ext2_filsys fs)
382 {
383         errcode_t       retval;
384         blk_t           blk;
385         int             i, j, num, count;
386         char            *buf;
387         char            format[20], backup[80];
388         int             sync_kludge = 0;
389         char            *mke2fs_sync;
390
391         mke2fs_sync = getenv("MKE2FS_SYNC");
392         if (mke2fs_sync)
393                 sync_kludge = atoi(mke2fs_sync);
394
395         buf = malloc(fs->blocksize * STRIDE_LENGTH);
396         if (!buf) {
397                 com_err("malloc", ENOMEM, "while allocating zeroizing buffer");
398                 exit(1);
399         }
400         memset(buf, 0, fs->blocksize * STRIDE_LENGTH);
401
402         /*
403          * Figure out how many digits we need
404          */
405         i = log10(fs->group_desc_count);
406         sprintf(format, "%%%dd/%%%dld", i, i);
407         memset(backup, '\b', sizeof(backup)-1);
408         backup[sizeof(backup)-1] = 0;
409         if ((2*i)+1 < sizeof(backup))
410                 backup[(2*i)+1] = 0;
411
412         if (!quiet)
413                 printf("Writing inode tables: ");
414         for (i = 0; i < fs->group_desc_count; i++) {
415                 if (!quiet)
416                         printf(format, i, fs->group_desc_count);
417                 
418                 blk = fs->group_desc[i].bg_inode_table;
419                 num = fs->inode_blocks_per_group;
420                 
421                 for (j=0; j < num; j += STRIDE_LENGTH, blk += STRIDE_LENGTH) {
422                         if (num-j > STRIDE_LENGTH)
423                                 count = STRIDE_LENGTH;
424                         else
425                                 count = num - j;
426                         retval = io_channel_write_blk(fs->io, blk, count, buf);
427                         if (retval)
428                                 printf("Warning: could not write %d blocks "
429                                        "in inode table starting at %d: %s\n",
430                                        count, blk, error_message(retval));
431                 }
432                 if (!quiet) 
433                         fputs(backup, stdout);
434                 if (sync_kludge) {
435                         if (sync_kludge == 1)
436                                 sync();
437                         else if ((i % sync_kludge) == 0)
438                                 sync();
439                 }
440         }
441         free(buf);
442         if (!quiet)
443                 fputs("done                            \n", stdout);
444 }
445
446 static void create_root_dir(ext2_filsys fs)
447 {
448         errcode_t       retval;
449         struct ext2_inode       inode;
450
451         retval = ext2fs_mkdir(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, 0);
452         if (retval) {
453                 com_err("ext2fs_mkdir", retval, "while creating root dir");
454                 exit(1);
455         }
456         if (geteuid()) {
457                 retval = ext2fs_read_inode(fs, EXT2_ROOT_INO, &inode);
458                 if (retval) {
459                         com_err("ext2fs_read_inode", retval,
460                                 "while reading root inode");
461                         exit(1);
462                 }
463                 inode.i_uid = getuid();
464                 if (inode.i_uid)
465                         inode.i_gid = getgid();
466                 retval = ext2fs_write_inode(fs, EXT2_ROOT_INO, &inode);
467                 if (retval) {
468                         com_err("ext2fs_write_inode", retval,
469                                 "while setting root inode ownership");
470                         exit(1);
471                 }
472         }
473 }
474
475 static void create_lost_and_found(ext2_filsys fs)
476 {
477         errcode_t               retval;
478         ino_t                   ino;
479         const char              *name = "lost+found";
480         int                     i;
481         int                     lpf_size = 0;
482
483         retval = ext2fs_mkdir(fs, EXT2_ROOT_INO, 0, name);
484         if (retval) {
485                 com_err("ext2fs_mkdir", retval, "while creating /lost+found");
486                 exit(1);
487         }
488
489         retval = ext2fs_lookup(fs, EXT2_ROOT_INO, name, strlen(name), 0, &ino);
490         if (retval) {
491                 com_err("ext2_lookup", retval, "while looking up /lost+found");
492                 exit(1);
493         }
494         
495         for (i=1; i < EXT2_NDIR_BLOCKS; i++) {
496                 if ((lpf_size += fs->blocksize) >= 16*1024)
497                         break;
498                 retval = ext2fs_expand_dir(fs, ino);
499                 if (retval) {
500                         com_err("ext2fs_expand_dir", retval,
501                                 "while expanding /lost+found");
502                         exit(1);
503                 }
504         }               
505 }
506
507 static void create_bad_block_inode(ext2_filsys fs, badblocks_list bb_list)
508 {
509         errcode_t       retval;
510         
511         ext2fs_mark_inode_bitmap(fs->inode_map, EXT2_BAD_INO);
512         fs->group_desc[0].bg_free_inodes_count--;
513         fs->super->s_free_inodes_count--;
514         retval = ext2fs_update_bb_inode(fs, bb_list);
515         if (retval) {
516                 com_err("ext2fs_update_bb_inode", retval,
517                         "while setting bad block inode");
518                 exit(1);
519         }
520
521 }
522
523 static void reserve_inodes(ext2_filsys fs)
524 {
525         ino_t   i;
526         int     group;
527
528         for (i = EXT2_ROOT_INO + 1; i < EXT2_FIRST_INODE(fs->super); i++) {
529                 ext2fs_mark_inode_bitmap(fs->inode_map, i);
530                 group = ext2fs_group_of_ino(fs, i);
531                 fs->group_desc[group].bg_free_inodes_count--;
532                 fs->super->s_free_inodes_count--;
533         }
534         ext2fs_mark_ib_dirty(fs);
535 }
536
537 #ifdef ZAP_BOOTBLOCK
538 static void zap_bootblock(ext2_filsys fs)
539 {
540         char buf[512];
541         int retval;
542
543         memset(buf, 0, 512);
544         
545         retval = io_channel_write_blk(fs->io, 0, -512, buf);
546         if (retval)
547                 printf("Warning: could not erase block 0: %s\n", 
548                        error_message(retval));
549 }
550 #endif
551         
552
553 static void show_stats(ext2_filsys fs)
554 {
555         struct ext2fs_sb        *s = (struct ext2fs_sb *) fs->super;
556         char                    buf[80];
557         blk_t                   group_block;
558         int                     i, need, col_left;
559         
560         if (param.s_blocks_count != s->s_blocks_count)
561                 printf("warning: %d blocks unused.\n\n",
562                        param.s_blocks_count - s->s_blocks_count);
563
564         memset(buf, 0, sizeof(buf));
565         strncpy(buf, s->s_volume_name, sizeof(s->s_volume_name));
566         printf("Filesystem label=%s\n", buf);
567         printf("OS type: ");
568         switch (fs->super->s_creator_os) {
569             case EXT2_OS_LINUX: printf ("Linux"); break;
570             case EXT2_OS_HURD:  printf ("GNU/Hurd");   break;
571             case EXT2_OS_MASIX: printf ("Masix"); break;
572             default:            printf ("(unknown os)");
573         }
574         printf("\n");
575         printf("Block size=%u (log=%u)\n", fs->blocksize,
576                 s->s_log_block_size);
577         printf("Fragment size=%u (log=%u)\n", fs->fragsize,
578                 s->s_log_frag_size);
579         printf("%u inodes, %u blocks\n", s->s_inodes_count,
580                s->s_blocks_count);
581         printf("%u blocks (%2.2f%%) reserved for the super user\n",
582                 s->s_r_blocks_count,
583                100.0 * s->s_r_blocks_count / s->s_blocks_count);
584         printf("First data block=%u\n", s->s_first_data_block);
585         printf("%lu block group%s\n", fs->group_desc_count,
586                 (fs->group_desc_count > 1) ? "s" : "");
587         printf("%u blocks per group, %u fragments per group\n",
588                s->s_blocks_per_group, s->s_frags_per_group);
589         printf("%u inodes per group\n", s->s_inodes_per_group);
590
591         if (fs->group_desc_count == 1) {
592                 printf("\n");
593                 return;
594         }
595         
596         printf("Superblock backups stored on blocks: ");
597         group_block = s->s_first_data_block;
598         col_left = 0;
599         for (i = 1; i < fs->group_desc_count; i++) {
600                 group_block += s->s_blocks_per_group;
601                 if (!ext2fs_bg_has_super(fs, i))
602                         continue;
603                 if (i != 1)
604                         printf(", ");
605                 need = log10(group_block) + 2;
606                 if (need > col_left) {
607                         printf("\n\t");
608                         col_left = 72;
609                 }
610                 col_left -= need;
611                 printf("%u", group_block);
612         }
613         printf("\n\n");
614 }
615
616 #ifndef HAVE_STRCASECMP
617 static int strcasecmp (char *s1, char *s2)
618 {
619         while (*s1 && *s2) {
620                 int ch1 = *s1++, ch2 = *s2++;
621                 if (isupper (ch1))
622                         ch1 = tolower (ch1);
623                 if (isupper (ch2))
624                         ch2 = tolower (ch2);
625                 if (ch1 != ch2)
626                         return ch1 - ch2;
627         }
628         return *s1 ? 1 : *s2 ? -1 : 0;
629 }
630 #endif
631
632 /*
633  * Set the S_CREATOR_OS field.  Return true if OS is known,
634  * otherwise, 0.
635  */
636 static int set_os(struct ext2_super_block *sb, char *os)
637 {
638         if (isdigit (*os))
639                 sb->s_creator_os = atoi (os);
640         else if (strcasecmp(os, "linux") == 0)
641                 sb->s_creator_os = EXT2_OS_LINUX;
642         else if (strcasecmp(os, "GNU") == 0 || strcasecmp(os, "hurd") == 0)
643                 sb->s_creator_os = EXT2_OS_HURD;
644         else if (strcasecmp(os, "masix") == 0)
645                 sb->s_creator_os = EXT2_OS_MASIX;
646         else
647                 return 0;
648         return 1;
649 }
650
651 #define PATH_SET "PATH=/sbin"
652
653 static void parse_raid_opts(const char *opts)
654 {
655         char    *buf, *token, *next, *p, *arg;
656         int     len;
657         int     raid_usage = 0;
658
659         len = strlen(opts);
660         buf = malloc(len+1);
661         if (!buf) {
662                 fprintf(stderr, "Couldn't allocate memory to parse "
663                         "raid options!\n");
664                 exit(1);
665         }
666         strcpy(buf, opts);
667         for (token = buf; token && *token; token = next) {
668                 p = strchr(token, ',');
669                 next = 0;
670                 if (p) {
671                         *p = 0;
672                         next = p+1;
673                 } 
674                 arg = strchr(token, '=');
675                 if (arg) {
676                         *arg = 0;
677                         arg++;
678                 }
679                 if (strcmp(token, "stride") == 0) {
680                         if (!arg) {
681                                 raid_usage++;
682                                 continue;
683                         }
684                         fs_stride = strtoul(arg, &p, 0);
685                         if (*p || (fs_stride == 0)) {
686                                 fprintf(stderr, "Invalid stride parameter.\n");
687                                 raid_usage++;
688                                 continue;
689                         }
690                 } else
691                         raid_usage++;
692         }
693         if (raid_usage) {
694                 fprintf(stderr, "\nBad raid options specified.\n\n"
695                         "Raid options are separated by commas, "
696                         "and may take an argument which\n"
697                         "\tis set off by an equals ('=') sign.\n\n"
698                         "Valid raid options are:\n"
699                         "\tstride=<stride length in blocks>\n\n");
700                 exit(1);
701         }
702 }       
703
704 static __u32 ok_features[3] = {
705         0,                                      /* Compat */
706         EXT2_FEATURE_INCOMPAT_FILETYPE,         /* Incompat */
707         EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER     /* R/O compat */
708 };
709
710
711 static void PRS(int argc, char *argv[])
712 {
713         int             c;
714         int             size;
715         char *          tmp;
716         blk_t           max = 8192;
717         int             blocksize = 0;
718         int             inode_ratio = 0;
719         int             reserved_ratio = 5;
720         ino_t           num_inodes = 0;
721         errcode_t       retval;
722         int             sparse_option = 0;
723         char *          oldpath = getenv("PATH");
724         struct ext2fs_sb *param_ext2 = (struct ext2fs_sb *) &param;
725         char *          raid_opts = 0;
726         char *          fs_type = 0;
727         const char *    feature_set = "filetype,sparse_super";
728         blk_t           dev_size;
729 #ifdef linux
730         struct          utsname ut;
731
732         if (uname(&ut)) {
733                 perror("uname");
734                 exit(1);
735         }
736         if ((ut.release[0] == '1') ||
737             (ut.release[0] == '2' && ut.release[1] == '.' &&
738              ut.release[2] < '2' && ut.release[3] == '.'))
739                 feature_set = 0;
740 #endif
741         /* Update our PATH to include /sbin  */
742         if (oldpath) {
743                 char *newpath;
744                 
745                 newpath = malloc(sizeof (PATH_SET) + 1 + strlen (oldpath));
746                 strcpy (newpath, PATH_SET);
747                 strcat (newpath, ":");
748                 strcat (newpath, oldpath);
749                 putenv (newpath);
750         } else
751                 putenv (PATH_SET);
752
753         setbuf(stdout, NULL);
754         setbuf(stderr, NULL);
755         initialize_ext2_error_table();
756         memset(&param, 0, sizeof(struct ext2_super_block));
757         param.s_rev_level = 1;  /* Create revision 1 filesystems now */
758         
759         fprintf (stderr, "mke2fs %s, %s for EXT2 FS %s, %s\n",
760                  E2FSPROGS_VERSION, E2FSPROGS_DATE,
761                  EXT2FS_VERSION, EXT2FS_DATE);
762         if (argc && *argv)
763                 program_name = *argv;
764         while ((c = getopt (argc, argv,
765                     "b:cf:g:i:l:m:no:qr:R:s:tvI:ST:FL:M:N:O:V")) != EOF)
766                 switch (c) {
767                 case 'b':
768                         blocksize = strtoul(optarg, &tmp, 0);
769                         if (blocksize < 1024 || blocksize > 4096 || *tmp) {
770                                 com_err(program_name, 0, "bad block size - %s",
771                                         optarg);
772                                 exit(1);
773                         }
774                         param.s_log_block_size =
775                                 log2(blocksize >> EXT2_MIN_BLOCK_LOG_SIZE);
776                         max = blocksize * 8;
777                         break;
778                 case 'c':
779                 case 't':       /* Check for bad blocks */
780                         cflag = 1;
781                         break;
782                 case 'f':
783                         size = strtoul(optarg, &tmp, 0);
784                         if (size < 1024 || size > 4096 || *tmp) {
785                                 com_err(program_name, 0, "bad fragment size - %s",
786                                         optarg);
787                                 exit(1);
788                         }
789                         param.s_log_frag_size =
790                                 log2(size >> EXT2_MIN_BLOCK_LOG_SIZE);
791                         printf("Warning: fragments not supported.  "
792                                "Ignoring -f option\n");
793                         break;
794                 case 'g':
795                         param.s_blocks_per_group = strtoul(optarg, &tmp, 0);
796                         if (*tmp) {
797                                 com_err(program_name, 0,
798                                         "Illegal number for blocks per group");
799                                 exit(1);
800                         }
801                         if ((param.s_blocks_per_group % 8) != 0) {
802                                 com_err(program_name, 0,
803                                 "blocks per group must be multiple of 8");
804                                 exit(1);
805                         }
806                         break;
807                 case 'i':
808                         inode_ratio = strtoul(optarg, &tmp, 0);
809                         if (inode_ratio < 1024 || inode_ratio > 256 * 1024 ||
810                             *tmp) {
811                                 com_err(program_name, 0, "bad inode ratio - %s",
812                                         optarg);
813                                 exit(1);
814                         }
815                         break;
816                 case 'l':
817                         bad_blocks_filename = malloc(strlen(optarg)+1);
818                         if (!bad_blocks_filename) {
819                                 com_err(program_name, ENOMEM,
820                                         "in malloc for bad_blocks_filename");
821                                 exit(1);
822                         }
823                         strcpy(bad_blocks_filename, optarg);
824                         break;
825                 case 'm':
826                         reserved_ratio = strtoul(optarg, &tmp, 0);
827                         if (reserved_ratio > 50 || *tmp) {
828                                 com_err(program_name, 0,
829                                         "bad reserved blocks percent - %s",
830                                         optarg);
831                                 exit(1);
832                         }
833                         break;
834                 case 'n':
835                         noaction++;
836                         break;
837                 case 'o':
838                         creator_os = optarg;
839                         break;
840                 case 'r':
841                         param.s_rev_level = atoi(optarg);
842                         break;
843                 case 's':
844                         sparse_option = atoi(optarg);
845                         break;
846 #ifdef EXT2_DYNAMIC_REV
847                 case 'I':
848                         param.s_inode_size = atoi(optarg);
849                         break;
850 #endif
851                 case 'N':
852                         num_inodes = atoi(optarg);
853                         break;
854                 case 'v':
855                         verbose = 1;
856                         break;
857                 case 'q':
858                         quiet = 1;
859                         break;
860                 case 'F':
861                         force = 1;
862                         break;
863                 case 'L':
864                         volume_label = optarg;
865                         break;
866                 case 'M':
867                         mount_dir = optarg;
868                         break;
869                 case 'O':
870                         feature_set = optarg;
871                         break;
872                 case 'R':
873                         raid_opts = optarg;
874                         break;
875                 case 'S':
876                         super_only = 1;
877                         break;
878                 case 'T':
879                         fs_type = optarg;
880                         break;
881                 case 'V':
882                         /* Print version number and exit */
883                         fprintf(stderr, "\tUsing %s\n",
884                                 error_message(EXT2_ET_BASE));
885                         exit(0);
886                 default:
887                         usage();
888                 }
889         if (optind == argc)
890                 usage();
891         device_name = argv[optind];
892         optind++;
893         if (optind < argc) {
894                 param.s_blocks_count = strtoul(argv[optind++], &tmp, 0);
895                 if (*tmp) {
896                         com_err(program_name, 0, "bad blocks count - %s",
897                                 argv[optind - 1]);
898                         exit(1);
899                 }
900         }
901         if (optind < argc)
902                 usage();
903
904         if (raid_opts)
905                 parse_raid_opts(raid_opts);
906
907         if (!force)
908                 check_plausibility();
909         check_mount();
910
911         param.s_log_frag_size = param.s_log_block_size;
912
913         if (noaction && param.s_blocks_count) {
914                 dev_size = param.s_blocks_count;
915                 retval = 0;
916         } else
917                 retval = ext2fs_get_device_size(device_name,
918                                                 EXT2_BLOCK_SIZE(&param),
919                                                 &dev_size);
920         if (retval && (retval != EXT2_ET_UNIMPLEMENTED)) {
921                 com_err(program_name, retval,
922                         "while trying to determine filesystem size");
923                 exit(1);
924         }
925         if (!param.s_blocks_count) {
926                 if (retval == EXT2_ET_UNIMPLEMENTED) {
927                         com_err(program_name, 0,
928                                 "Couldn't determine device size; you "
929                                 "must specify\nthe size of the "
930                                 "filesystem\n");
931                         exit(1);
932                 } else
933                         param.s_blocks_count = dev_size;
934         } else if (!force && (param.s_blocks_count > dev_size)) {
935                 com_err(program_name, 0,
936                         "Filesystem larger than apparent filesystem size.");
937                 proceed_question();
938         }
939
940         set_fs_defaults(fs_type, param_ext2, blocksize, &inode_ratio);
941
942         if (param.s_blocks_per_group) {
943                 if (param.s_blocks_per_group < 256 ||
944                     param.s_blocks_per_group > max || *tmp) {
945                         com_err(program_name, 0,
946                                 "blocks per group count out of range");
947                         exit(1);
948                 }
949         }
950
951         /*
952          * Calculate number of inodes based on the inode ratio
953          */
954         param.s_inodes_count = num_inodes ? num_inodes : 
955                 ((__u64) param.s_blocks_count * EXT2_BLOCK_SIZE(&param))
956                         / inode_ratio;
957
958         /*
959          * Calculate number of blocks to reserve
960          */
961         param.s_r_blocks_count = (param.s_blocks_count * reserved_ratio) / 100;
962
963 #ifdef EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER
964         if (sparse_option)
965                 param_ext2->s_feature_ro_compat |=
966                         EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER;
967 #endif
968         if (feature_set && !strncasecmp(feature_set, "none", 4))
969                 feature_set = 0;
970         if (feature_set && e2p_edit_feature(feature_set,
971                                             &param_ext2->s_feature_compat,
972                                             ok_features)) {
973                 fprintf(stderr, "Invalid filesystem option set: %s\n",
974                         feature_set);
975                 exit(1);
976         }
977 }
978                                         
979 int main (int argc, char *argv[])
980 {
981         errcode_t       retval = 0;
982         ext2_filsys     fs;
983         badblocks_list  bb_list = 0;
984         struct ext2fs_sb *s;
985         
986         PRS(argc, argv);
987
988         /*
989          * Initialize the superblock....
990          */
991         retval = ext2fs_initialize(device_name, 0, &param,
992                                    unix_io_manager, &fs);
993         if (retval) {
994                 com_err(device_name, retval, "while setting up superblock");
995                 exit(1);
996         }
997
998         /*
999          * Generate a UUID for it...
1000          */
1001         s = (struct ext2fs_sb *) fs->super;
1002         uuid_generate(s->s_uuid);
1003
1004         /*
1005          * Override the creator OS, if applicable
1006          */
1007         if (creator_os && !set_os(fs->super, creator_os)) {
1008                 com_err (program_name, 0, "unknown os - %s", creator_os);
1009                 exit(1);
1010         }
1011
1012         /*
1013          * Set the volume label...
1014          */
1015         if (volume_label) {
1016                 memset(s->s_volume_name, 0, sizeof(s->s_volume_name));
1017                 strncpy(s->s_volume_name, volume_label,
1018                         sizeof(s->s_volume_name));
1019         }
1020
1021         /*
1022          * Set the last mount directory
1023          */
1024         if (mount_dir) {
1025                 memset(s->s_last_mounted, 0, sizeof(s->s_last_mounted));
1026                 strncpy(s->s_last_mounted, mount_dir,
1027                         sizeof(s->s_last_mounted));
1028         }
1029         
1030         if (!quiet || noaction)
1031                 show_stats(fs);
1032
1033         if (noaction)
1034                 exit(0);
1035
1036         if (bad_blocks_filename)
1037                 read_bb_file(fs, &bb_list, bad_blocks_filename);
1038         if (cflag)
1039                 test_disk(fs, &bb_list);
1040
1041         handle_bad_blocks(fs, bb_list);
1042         fs->stride = fs_stride;
1043         retval = ext2fs_allocate_tables(fs);
1044         if (retval) {
1045                 com_err(program_name, retval,
1046                         "while trying to allocate filesystem tables");
1047                 exit(1);
1048         }
1049         if (super_only) {
1050                 fs->super->s_state |= EXT2_ERROR_FS;
1051                 fs->flags &= ~(EXT2_FLAG_IB_DIRTY|EXT2_FLAG_BB_DIRTY);
1052         } else {
1053                 write_inode_tables(fs);
1054                 create_root_dir(fs);
1055                 create_lost_and_found(fs);
1056                 reserve_inodes(fs);
1057                 create_bad_block_inode(fs, bb_list);
1058 #ifdef ZAP_BOOTBLOCK
1059                 zap_bootblock(fs);
1060 #endif
1061         }
1062         
1063         if (!quiet)
1064                 printf("Writing superblocks and "
1065                        "filesystem accounting information: ");
1066         retval = ext2fs_flush(fs);
1067         if (retval) {
1068                 printf("\nWarning, had trouble writing out superblocks.");
1069         }
1070         if (!quiet)
1071                 printf("done\n");
1072         ext2fs_close(fs);
1073         return 0;
1074 }