]>
Commit | Line | Data |
---|---|---|
3839e657 TT |
1 | /* |
2 | * mke2fs.c - Make a ext2fs filesystem. | |
3 | * | |
4 | * Copyright (C) 1994 Theodore Ts'o. This file may be redistributed | |
5 | * under the terms of the GNU Public License. | |
6 | */ | |
7 | ||
8 | /* Usage: mke2fs [options] device | |
9 | * | |
10 | * The device may be a block device or a image of one, but this isn't | |
11 | * enforced (but it's not much fun on a character device :-). | |
12 | */ | |
13 | ||
14 | #include <string.h> | |
15 | #include <fcntl.h> | |
16 | #include <ctype.h> | |
17 | #include <termios.h> | |
18 | #include <time.h> | |
19 | #include <getopt.h> | |
20 | #include <unistd.h> | |
21 | #include <stdlib.h> | |
22 | #include <mntent.h> | |
23 | #include <malloc.h> | |
24 | #include <sys/ioctl.h> | |
25 | #include <linux/ext2_fs.h> | |
26 | #include <linux/fs.h> | |
27 | ||
28 | #include "et/com_err.h" | |
29 | #include "ext2fs/ext2fs.h" | |
30 | #include "../version.h" | |
31 | ||
32 | #define STRIDE_LENGTH 8 | |
33 | ||
34 | extern int isatty(int); | |
35 | ||
36 | const char * program_name = "mke2fs"; | |
37 | const char * device_name = NULL; | |
38 | ||
39 | /* Command line options */ | |
40 | int cflag = 0; | |
41 | int verbose = 0; | |
42 | int quiet = 0; | |
43 | char *bad_blocks_filename = 0; | |
44 | ||
45 | struct ext2_super_block param; | |
46 | ||
47 | static void usage(NOARGS) | |
48 | { | |
49 | fprintf(stderr, | |
50 | "Usage: %s [-c|-t|-l filename] [-b block-size] " | |
51 | "[-f fragment-size]\n\t[-i bytes-per-inode] " | |
52 | "[-m reserved-blocks-percentage] [-v]\n" | |
53 | "\tdevice [blocks-count]\n", | |
54 | program_name); | |
55 | exit(1); | |
56 | } | |
57 | ||
58 | static int log2(int arg) | |
59 | { | |
60 | int l = 0; | |
61 | ||
62 | arg >>= 1; | |
63 | while (arg) { | |
64 | l++; | |
65 | arg >>= 1; | |
66 | } | |
67 | return l; | |
68 | } | |
69 | ||
70 | static long valid_offset (int fd, int offset) | |
71 | { | |
72 | char ch; | |
73 | ||
74 | if (lseek (fd, offset, 0) < 0) | |
75 | return 0; | |
76 | if (read (fd, &ch, 1) < 1) | |
77 | return 0; | |
78 | return 1; | |
79 | } | |
80 | ||
81 | static int count_blocks (int fd) | |
82 | { | |
83 | int high, low; | |
84 | ||
85 | low = 0; | |
86 | for (high = 1; valid_offset (fd, high); high *= 2) | |
87 | low = high; | |
88 | while (low < high - 1) | |
89 | { | |
90 | const int mid = (low + high) / 2; | |
91 | ||
92 | if (valid_offset (fd, mid)) | |
93 | low = mid; | |
94 | else | |
95 | high = mid; | |
96 | } | |
97 | valid_offset (fd, 0); | |
98 | return (low + 1) / 1024; | |
99 | } | |
100 | ||
101 | static int get_size(const char *file) | |
102 | { | |
103 | int fd; | |
104 | int size; | |
105 | ||
106 | fd = open(file, O_RDWR); | |
107 | if (fd < 0) { | |
108 | com_err("open", errno, "while trying to determine size of %s", | |
109 | file); | |
110 | exit(1); | |
111 | } | |
112 | if (ioctl(fd, BLKGETSIZE, &size) >= 0) { | |
113 | close(fd); | |
114 | return size / (EXT2_BLOCK_SIZE(¶m) / 512); | |
115 | } | |
116 | ||
117 | size = count_blocks(fd); | |
118 | close(fd); | |
119 | return size; | |
120 | } | |
121 | ||
122 | static void check_mount(NOARGS) | |
123 | { | |
124 | FILE * f; | |
125 | struct mntent * mnt; | |
126 | ||
127 | if ((f = setmntent (MOUNTED, "r")) == NULL) | |
128 | return; | |
129 | while ((mnt = getmntent (f)) != NULL) | |
130 | if (strcmp (device_name, mnt->mnt_fsname) == 0) | |
131 | break; | |
132 | endmntent (f); | |
133 | if (!mnt) | |
134 | return; | |
135 | ||
136 | fprintf(stderr, "%s is mounted; will not make a filesystem here!\n", | |
137 | device_name); | |
138 | exit(1); | |
139 | } | |
140 | ||
141 | /* | |
142 | * Helper function for read_bb_file and test_disk | |
143 | */ | |
144 | static void invalid_block(ext2_filsys fs, blk_t blk) | |
145 | { | |
146 | printf("Bad block %lu out of range; ignored.\n", blk); | |
147 | return; | |
148 | } | |
149 | ||
150 | /* | |
151 | * Reads the bad blocks list from a file | |
152 | */ | |
153 | static void read_bb_file(ext2_filsys fs, badblocks_list *bb_list, | |
154 | const char *bad_blocks_file) | |
155 | { | |
156 | FILE *f; | |
157 | errcode_t retval; | |
158 | ||
159 | f = fopen(bad_blocks_file, "r"); | |
160 | if (!f) { | |
161 | com_err("read_bad_blocks_file", errno, | |
162 | "while trying to open %s", bad_blocks_file); | |
163 | exit(1); | |
164 | } | |
165 | retval = ext2fs_read_bb_FILE(fs, f, bb_list, invalid_block); | |
166 | fclose (f); | |
167 | if (retval) { | |
168 | com_err("ext2fs_read_bb_FILE", retval, | |
169 | "while reading in list of bad blocks from file"); | |
170 | exit(1); | |
171 | } | |
172 | } | |
173 | ||
174 | /* | |
175 | * Runs the badblocks program to test the disk | |
176 | */ | |
177 | static void test_disk(ext2_filsys fs, badblocks_list *bb_list) | |
178 | { | |
179 | FILE *f; | |
180 | errcode_t retval; | |
181 | char buf[1024]; | |
182 | ||
183 | sprintf(buf, "badblocks %s%s %ld", quiet ? "" : "-s ", | |
184 | fs->device_name, | |
185 | fs->super->s_blocks_count); | |
186 | if (verbose) | |
187 | printf("Running command: %s\n", buf); | |
188 | f = popen(buf, "r"); | |
189 | if (!f) { | |
190 | com_err("popen", errno, | |
191 | "while trying run '%s'", buf); | |
192 | exit(1); | |
193 | } | |
194 | retval = ext2fs_read_bb_FILE(fs, f, bb_list, invalid_block); | |
195 | fclose (f); | |
196 | if (retval) { | |
197 | com_err("ext2fs_read_bb_FILE", retval, | |
198 | "while processing list of bad blocks from program"); | |
199 | exit(1); | |
200 | } | |
201 | } | |
202 | ||
203 | static void handle_bad_blocks(ext2_filsys fs, badblocks_list bb_list) | |
204 | { | |
205 | int i; | |
206 | int must_be_good; | |
207 | blk_t blk; | |
208 | badblocks_iterate bb_iter; | |
209 | errcode_t retval; | |
210 | ||
211 | if (!bb_list) | |
212 | return; | |
213 | ||
214 | /* | |
215 | * The primary superblock and group descriptors *must* be | |
216 | * good; if not, abort. | |
217 | */ | |
218 | must_be_good = fs->super->s_first_data_block + 1 + fs->desc_blocks; | |
219 | for (i = fs->super->s_first_data_block; i <= must_be_good; i++) { | |
220 | if (badblocks_list_test(bb_list, i)) { | |
221 | fprintf(stderr, "Block %d in primary superblock/group " | |
222 | "descriptor area bad.\n", i); | |
223 | fprintf(stderr, "Blocks %ld through %d must be good " | |
224 | "in order to build a filesystem.\n", | |
225 | fs->super->s_first_data_block, must_be_good); | |
226 | fprintf(stderr, "Aborting....\n"); | |
227 | exit(1); | |
228 | } | |
229 | } | |
230 | ||
231 | /* | |
232 | * Mark all the bad blocks as used... | |
233 | */ | |
234 | retval = badblocks_list_iterate_begin(bb_list, &bb_iter); | |
235 | if (retval) { | |
236 | com_err("badblocks_list_iterate_begin", retval, | |
237 | "while marking bad blocks as used"); | |
238 | exit(1); | |
239 | } | |
240 | while (badblocks_list_iterate(bb_iter, &blk)) | |
241 | ext2fs_mark_block_bitmap(fs, fs->block_map, blk); | |
242 | badblocks_list_iterate_end(bb_iter); | |
243 | } | |
244 | ||
245 | static void new_table_block(ext2_filsys fs, blk_t first_block, | |
246 | const char *name, int num, const char *buf, | |
247 | blk_t *new_block) | |
248 | { | |
249 | errcode_t retval; | |
250 | blk_t blk; | |
251 | int i; | |
252 | int count; | |
253 | ||
254 | retval = ext2fs_get_free_blocks(fs, first_block, | |
255 | first_block + fs->super->s_blocks_per_group, | |
256 | num, fs->block_map, new_block); | |
257 | if (retval) { | |
258 | printf("Could not allocate %d block(s) for %s: %s\n", | |
259 | num, name, error_message(retval)); | |
260 | ext2fs_unmark_valid(fs); | |
261 | return; | |
262 | } | |
263 | blk = *new_block; | |
264 | for (i=0; i < num; i += STRIDE_LENGTH, blk += STRIDE_LENGTH) { | |
265 | if (num-i > STRIDE_LENGTH) | |
266 | count = STRIDE_LENGTH; | |
267 | else | |
268 | count = num - i; | |
269 | retval = io_channel_write_blk(fs->io, blk, count, buf); | |
270 | if (retval) | |
271 | printf("Warning: could not write %d blocks starting " | |
272 | "at %ld for %s: %s\n", | |
273 | count, blk, name, error_message(retval)); | |
274 | } | |
275 | blk = *new_block; | |
276 | for (i = 0; i < num; i++, blk++) | |
277 | ext2fs_mark_block_bitmap(fs, fs->block_map, blk); | |
278 | } | |
279 | ||
280 | static void alloc_tables(ext2_filsys fs) | |
281 | { | |
282 | blk_t group_blk; | |
283 | int i; | |
284 | char *buf; | |
285 | int numblocks; | |
286 | ||
287 | buf = malloc(fs->blocksize * STRIDE_LENGTH); | |
288 | if (!buf) { | |
289 | com_err("malloc", ENOMEM, "while allocating zeroizing buffer"); | |
290 | exit(1); | |
291 | } | |
292 | memset(buf, 0, fs->blocksize * STRIDE_LENGTH); | |
293 | ||
294 | group_blk = fs->super->s_first_data_block; | |
295 | if (!quiet) | |
296 | printf("Writing inode tables: "); | |
297 | for (i = 0; i < fs->group_desc_count; i++) { | |
298 | if (!quiet) | |
299 | printf("%4d/%4ld", i, fs->group_desc_count); | |
300 | new_table_block(fs, group_blk, "block bitmap", 1, buf, | |
301 | &fs->group_desc[i].bg_block_bitmap); | |
302 | new_table_block(fs, group_blk, "inode bitmap", 1, buf, | |
303 | &fs->group_desc[i].bg_inode_bitmap); | |
304 | new_table_block(fs, group_blk, "inode table", | |
305 | fs->inode_blocks_per_group, buf, | |
306 | &fs->group_desc[i].bg_inode_table); | |
307 | ||
308 | if (i == fs->group_desc_count-1) { | |
309 | numblocks = (fs->super->s_blocks_count - | |
310 | fs->super->s_first_data_block) % | |
311 | fs->super->s_blocks_per_group; | |
312 | if (!numblocks) | |
313 | numblocks = fs->super->s_blocks_per_group; | |
314 | } else | |
315 | numblocks = fs->super->s_blocks_per_group; | |
316 | numblocks -= 3 + fs->desc_blocks + fs->inode_blocks_per_group; | |
317 | ||
318 | fs->group_desc[i].bg_free_blocks_count = numblocks; | |
319 | fs->group_desc[i].bg_free_inodes_count = | |
320 | fs->super->s_inodes_per_group; | |
321 | fs->group_desc[i].bg_used_dirs_count = 0; | |
322 | group_blk += fs->super->s_blocks_per_group; | |
323 | if (!quiet) | |
324 | printf("\b\b\b\b\b\b\b\b\b"); | |
325 | } | |
326 | if (!quiet) | |
327 | printf("done \n"); | |
328 | } | |
329 | ||
330 | static void create_root_dir(ext2_filsys fs) | |
331 | { | |
332 | errcode_t retval; | |
333 | ||
334 | retval = ext2fs_mkdir(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, 0); | |
335 | if (retval) { | |
336 | com_err("ext2fs_mkdir", retval, "while creating root dir"); | |
337 | exit(1); | |
338 | } | |
339 | } | |
340 | ||
341 | static void create_lost_and_found(ext2_filsys fs) | |
342 | { | |
343 | errcode_t retval; | |
344 | ino_t ino; | |
345 | const char *name = "lost+found"; | |
346 | int i; | |
347 | ||
348 | retval = ext2fs_mkdir(fs, EXT2_ROOT_INO, 0, name); | |
349 | if (retval) { | |
350 | com_err("ext2fs_mkdir", retval, "while creating /lost+found"); | |
351 | exit(1); | |
352 | } | |
353 | ||
354 | retval = ext2fs_lookup(fs, EXT2_ROOT_INO, name, strlen(name), 0, &ino); | |
355 | if (retval) { | |
356 | com_err("ext2_lookup", retval, "while looking up /lost+found"); | |
357 | exit(1); | |
358 | } | |
359 | ||
360 | for (i=1; i < EXT2_NDIR_BLOCKS; i++) { | |
361 | retval = ext2fs_expand_dir(fs, ino); | |
362 | if (retval) { | |
363 | com_err("ext2fs_expand_dir", retval, | |
364 | "while expanding /lost+found"); | |
365 | exit(1); | |
366 | } | |
367 | } | |
368 | } | |
369 | ||
370 | static void create_bad_block_inode(ext2_filsys fs, badblocks_list bb_list) | |
371 | { | |
372 | errcode_t retval; | |
373 | ||
374 | ext2fs_mark_inode_bitmap(fs, fs->inode_map, EXT2_BAD_INO); | |
375 | fs->group_desc[0].bg_free_inodes_count--; | |
376 | fs->super->s_free_inodes_count--; | |
377 | retval = ext2fs_update_bb_inode(fs, bb_list); | |
378 | if (retval) { | |
379 | com_err("ext2fs_update_bb_inode", retval, | |
380 | "while setting bad block inode"); | |
381 | exit(1); | |
382 | } | |
383 | ||
384 | } | |
385 | ||
386 | static void reserve_inodes(ext2_filsys fs) | |
387 | { | |
388 | ino_t i; | |
389 | int group; | |
390 | ||
391 | for (i = EXT2_ROOT_INO + 1; i < EXT2_FIRST_INO; i++) { | |
392 | ext2fs_mark_inode_bitmap (fs, fs->inode_map, i); | |
393 | group = ext2fs_group_of_ino(fs, i); | |
394 | fs->group_desc[group].bg_free_inodes_count--; | |
395 | fs->super->s_free_inodes_count--; | |
396 | } | |
397 | ext2fs_mark_ib_dirty(fs); | |
398 | } | |
399 | ||
400 | static void show_stats(ext2_filsys fs) | |
401 | { | |
402 | struct ext2_super_block *s = fs->super; | |
403 | blk_t group_block; | |
404 | int i, col_left; | |
405 | ||
406 | if (param.s_blocks_count != s->s_blocks_count) | |
407 | printf("warning: %ld blocks unused.\n\n", | |
408 | param.s_blocks_count - s->s_blocks_count); | |
409 | ||
410 | printf("%lu inodes, %lu blocks\n", s->s_inodes_count, | |
411 | s->s_blocks_count); | |
412 | printf("%lu blocks (%2.2f%%) reserved for the super user\n", | |
413 | s->s_r_blocks_count, | |
414 | 100.0 * s->s_r_blocks_count / s->s_blocks_count); | |
415 | printf("First data block=%lu\n", s->s_first_data_block); | |
416 | printf("Block size=%u (log=%lu)\n", fs->blocksize, | |
417 | s->s_log_block_size); | |
418 | printf("Fragment size=%u (log=%lu)\n", fs->fragsize, | |
419 | s->s_log_frag_size); | |
420 | printf("%lu block group%s\n", fs->group_desc_count, | |
421 | (fs->group_desc_count > 1) ? "s" : ""); | |
422 | printf("%lu blocks per group, %lu fragments per group\n", | |
423 | s->s_blocks_per_group, s->s_frags_per_group); | |
424 | printf("%lu inodes per group\n", s->s_inodes_per_group); | |
425 | ||
426 | if (fs->group_desc_count == 1) { | |
427 | printf("\n"); | |
428 | return; | |
429 | } | |
430 | ||
431 | printf("Superblock backups stored on blocks: "); | |
432 | group_block = s->s_first_data_block; | |
433 | col_left = 0; | |
434 | for (i = 1; i < fs->group_desc_count; i++) { | |
435 | group_block += s->s_blocks_per_group; | |
436 | if (!col_left--) { | |
437 | printf("\n\t"); | |
438 | col_left = 8; | |
439 | } | |
440 | printf("%lu", group_block); | |
441 | if (i != fs->group_desc_count - 1) | |
442 | printf(", "); | |
443 | } | |
444 | printf("\n\n"); | |
445 | } | |
446 | ||
447 | static void PRS(int argc, char *argv[]) | |
448 | { | |
449 | char c; | |
450 | int size; | |
451 | char * tmp; | |
452 | char *oldpath, newpath[PATH_MAX]; | |
453 | int inode_ratio = 4096; | |
454 | int reserved_ratio = 5; | |
455 | ||
456 | /* Update our PATH to include /sbin */ | |
457 | strcpy(newpath, "PATH=/sbin:"); | |
458 | if ((oldpath = getenv("PATH")) != NULL) | |
459 | strcat(newpath, oldpath); | |
460 | putenv(newpath); | |
461 | ||
462 | setbuf(stdout, NULL); | |
463 | setbuf(stderr, NULL); | |
464 | initialize_ext2_error_table(); | |
465 | memset(¶m, 0, sizeof(struct ext2_super_block)); | |
466 | ||
467 | fprintf (stderr, "mke2fs %s, %s for EXT2 FS %s, %s\n", | |
468 | E2FSPROGS_VERSION, E2FSPROGS_DATE, | |
469 | EXT2FS_VERSION, EXT2FS_DATE); | |
470 | if (argc && *argv) | |
471 | program_name = *argv; | |
472 | while ((c = getopt (argc, argv, "b:cf:g:i:l:m:qtv")) != EOF) | |
473 | switch (c) { | |
474 | case 'b': | |
475 | size = strtoul(optarg, &tmp, 0); | |
476 | if (size < 1024 || size > 4096 || *tmp) { | |
477 | com_err(program_name, 0, "bad block size - %s", | |
478 | optarg); | |
479 | exit(1); | |
480 | } | |
481 | param.s_log_block_size = | |
482 | log2(size >> EXT2_MIN_BLOCK_LOG_SIZE); | |
483 | break; | |
484 | case 'c': | |
485 | case 't': /* Check for bad blocks */ | |
486 | cflag = 1; | |
487 | break; | |
488 | case 'f': | |
489 | size = strtoul(optarg, &tmp, 0); | |
490 | if (size < 1024 || size > 4096 || *tmp) { | |
491 | com_err(program_name, 0, "bad fragment size - %s", | |
492 | optarg); | |
493 | exit(1); | |
494 | } | |
495 | param.s_log_frag_size = | |
496 | log2(size >> EXT2_MIN_BLOCK_LOG_SIZE); | |
497 | printf("Warning: fragments not supported. " | |
498 | "Ignoring -f option\n"); | |
499 | break; | |
500 | case 'g': | |
501 | param.s_blocks_per_group = strtoul(optarg, &tmp, 0); | |
502 | if (param.s_blocks_per_group < 256 || | |
503 | param.s_blocks_per_group > 8192 || *tmp) { | |
504 | com_err(program_name, 0, | |
505 | "bad blocks per group count - %s", | |
506 | optarg); | |
507 | exit(1); | |
508 | } | |
509 | break; | |
510 | case 'i': | |
511 | inode_ratio = strtoul(optarg, &tmp, 0); | |
512 | if (inode_ratio < 1024 || inode_ratio > 256 * 1024 || | |
513 | *tmp) { | |
514 | com_err(program_name, 0, "bad inode ratio - %s", | |
515 | optarg); | |
516 | exit(1); | |
517 | } | |
518 | break; | |
519 | case 'l': | |
520 | bad_blocks_filename = strdup(optarg); | |
521 | break; | |
522 | case 'm': | |
523 | reserved_ratio = strtoul(optarg, &tmp, 0); | |
524 | if (reserved_ratio > 50 || *tmp) { | |
525 | com_err(program_name, 0, | |
526 | "bad reserved blocks percent - %s", | |
527 | optarg); | |
528 | exit(1); | |
529 | } | |
530 | break; | |
531 | case 'v': | |
532 | verbose = 1; | |
533 | break; | |
534 | case 'q': | |
535 | quiet = 1; | |
536 | break; | |
537 | default: | |
538 | usage(); | |
539 | } | |
540 | if (optind == argc) | |
541 | usage(); | |
542 | device_name = argv[optind]; | |
543 | optind++; | |
544 | if (optind < argc) { | |
545 | param.s_blocks_count = strtoul(argv[optind++], &tmp, 0); | |
546 | if (*tmp) { | |
547 | com_err(program_name, 0, "bad blocks count - %s", | |
548 | argv[optind - 1]); | |
549 | exit(1); | |
550 | } | |
551 | } | |
552 | if (optind < argc) | |
553 | usage(); | |
554 | param.s_log_frag_size = param.s_log_block_size; | |
555 | ||
556 | if (!param.s_blocks_count) | |
557 | param.s_blocks_count = get_size(device_name); | |
558 | ||
559 | /* | |
560 | * Calculate number of inodes based on the inode ratio | |
561 | */ | |
562 | param.s_inodes_count = | |
563 | (param.s_blocks_count * EXT2_BLOCK_SIZE(¶m)) / inode_ratio; | |
564 | ||
565 | /* | |
566 | * Calculate number of blocks to reserve | |
567 | */ | |
568 | param.s_r_blocks_count = (param.s_blocks_count * reserved_ratio) / 100; | |
569 | } | |
570 | ||
571 | int main (int argc, char *argv[]) | |
572 | { | |
573 | errcode_t retval = 0; | |
574 | ext2_filsys fs; | |
575 | badblocks_list bb_list = 0; | |
576 | ||
577 | PRS(argc, argv); | |
578 | ||
579 | check_mount(); | |
580 | ||
581 | /* | |
582 | * Initialize the superblock.... | |
583 | */ | |
584 | retval = ext2fs_initialize(device_name, 0, ¶m, | |
585 | unix_io_manager, &fs); | |
586 | if (retval) { | |
587 | com_err(device_name, retval, "while setting up superblock"); | |
588 | exit(1); | |
589 | } | |
590 | ||
591 | if (!quiet) | |
592 | show_stats(fs); | |
593 | ||
594 | if (bad_blocks_filename) | |
595 | read_bb_file(fs, &bb_list, bad_blocks_filename); | |
596 | if (cflag) | |
597 | test_disk(fs, &bb_list); | |
598 | ||
599 | handle_bad_blocks(fs, bb_list); | |
600 | alloc_tables(fs); | |
601 | create_root_dir(fs); | |
602 | create_lost_and_found(fs); | |
603 | reserve_inodes(fs); | |
604 | create_bad_block_inode(fs, bb_list); | |
605 | ||
606 | if (!quiet) | |
607 | printf("Writing superblocks and " | |
608 | "filesystem accounting information: "); | |
609 | ext2fs_close(fs); | |
610 | if (!quiet) | |
611 | printf("done\n"); | |
612 | return 0; | |
613 | } |