2 * mk_hugefiles.c -- create huge files
5 #define _XOPEN_SOURCE 600 /* for inclusion of PATH_MAX in Solaris */
6 #define _BSD_SOURCE /* for makedev() and major() */
17 #include <sys/utsname.h>
34 #include <sys/ioctl.h>
35 #include <sys/types.h>
39 #include <blkid/blkid.h>
41 #include "ext2fs/ext2_fs.h"
42 #include "ext2fs/ext2fsP.h"
43 #include "et/com_err.h"
44 #include "uuid/uuid.h"
46 #include "ext2fs/ext2fs.h"
50 #include "nls-enable.h"
55 static blk64_t num_blocks;
56 static blk64_t num_slack;
57 static unsigned long num_files;
59 static char *fn_prefix;
60 static int idx_digits;
62 static char *fn_numbuf;
63 int zero_hugefile = 1;
65 #define SYSFS_PATH_LEN 256
66 typedef char sysfs_path_t[SYSFS_PATH_LEN];
70 * We are very careful to avoid needing to worry about buffer
71 * overflows, so we don't really need to use snprintf() except as an
72 * additional safety check. So if snprintf() is not present, it's
73 * safe to fall back to vsprintf(). This provides portability since
74 * vsprintf() is guaranteed by C89, while snprintf() is only
75 * guaranteed by C99 --- which for example, Microsoft Visual Studio
76 * has *still* not bothered to implement. :-/ (Not that I expect
77 * mke2fs to be ported to MS Visual Studio any time soon, but
78 * libext2fs *does* get built on Microsoft platforms, and we might
79 * want to move this into libext2fs some day.)
81 static int my_snprintf(char *str, size_t size, const char *format, ...)
87 ret = vsprintf(str, format, ap);
92 #define snprintf my_snprintf
96 * Fall back to Linux's definitions of makedev and major are needed.
97 * The search_sysfs_block() function is highly unlikely to work on
98 * non-Linux systems anyway.
101 #define makedev(maj, min) (((maj) << 8) + (min))
104 static char *search_sysfs_block(dev_t devno, sysfs_path_t ret_path)
106 struct dirent *de, *p_de;
107 DIR *dir = NULL, *p_dir = NULL;
109 sysfs_path_t path, p_path;
110 unsigned int major, minor;
111 char *ret = ret_path;
114 if ((dir = opendir("/sys/block")) == NULL)
116 while ((de = readdir(dir)) != NULL) {
117 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..") ||
118 strlen(de->d_name) > sizeof(path)-32)
120 snprintf(path, SYSFS_PATH_LEN,
121 "/sys/block/%s/dev", de->d_name);
122 f = fopen(path, "r");
124 (fscanf(f, "%u:%u", &major, &minor) == 2)) {
126 if (makedev(major, minor) == devno) {
127 snprintf(ret_path, SYSFS_PATH_LEN,
128 "/sys/block/%s", de->d_name);
132 if (major(devno) != major)
139 snprintf(path, SYSFS_PATH_LEN, "/sys/block/%s", de->d_name);
143 if ((p_dir = opendir(path)) == NULL)
145 while ((p_de = readdir(p_dir)) != NULL) {
146 if (!strcmp(p_de->d_name, ".") ||
147 !strcmp(p_de->d_name, "..") ||
148 (strlen(p_de->d_name) >
149 SYSFS_PATH_LEN - strlen(path) - 32))
151 snprintf(p_path, SYSFS_PATH_LEN, "%s/%s/dev",
154 f = fopen(p_path, "r");
156 (fscanf(f, "%u:%u", &major, &minor) == 2) &&
157 (((major << 8) + minor) == devno)) {
159 snprintf(ret_path, SYSFS_PATH_LEN, "%s/%s",
176 static blk64_t get_partition_start(const char *device_name)
178 unsigned long long start;
185 if ((stat(device_name, &st) < 0) || !S_ISBLK(st.st_mode))
188 cp = search_sysfs_block(st.st_rdev, path);
191 if (strlen(path) > SYSFS_PATH_LEN - sizeof("/start"))
193 strcat(path, "/start");
194 f = fopen(path, "r");
197 n = fscanf(f, "%llu", &start);
199 return (n == 1) ? start : 0;
202 static errcode_t create_directory(ext2_filsys fs, char *dir,
206 struct ext2_inode inode;
207 ext2_ino_t ino = EXT2_ROOT_INO;
209 errcode_t retval = 0;
210 char *fn, *cp, *next;
212 fn = malloc(strlen(dir) + 1);
219 next = strchr(cp, '/');
223 retval = ext2fs_new_inode(fs, ino, LINUX_S_IFDIR,
228 retval = ext2fs_mkdir(fs, ino, newdir, cp);
233 retval = ext2fs_read_inode(fs, ino, &inode);
237 inode.i_uid = uid & 0xFFFF;
238 ext2fs_set_i_uid_high(inode, (uid >> 16) & 0xffff);
239 inode.i_gid = gid & 0xFFFF;
240 ext2fs_set_i_gid_high(inode, (gid >> 16) & 0xffff);
241 retval = ext2fs_write_inode(fs, ino, &inode);
245 if (next == NULL || *next == '\0')
256 static errcode_t mk_hugefile(ext2_filsys fs, blk64_t num,
257 ext2_ino_t dir, unsigned long idx, ext2_ino_t *ino)
261 blk64_t lblk, bend = 0;
265 struct ext2_inode inode;
266 ext2_extent_handle_t handle;
268 retval = ext2fs_new_inode(fs, 0, LINUX_S_IFREG, NULL, ino);
272 memset(&inode, 0, sizeof(struct ext2_inode));
273 inode.i_mode = LINUX_S_IFREG | (0666 & ~fs->umask);
274 inode.i_links_count = 1;
275 inode.i_uid = uid & 0xFFFF;
276 ext2fs_set_i_uid_high(inode, (uid >> 16) & 0xffff);
277 inode.i_gid = gid & 0xFFFF;
278 ext2fs_set_i_gid_high(inode, (gid >> 16) & 0xffff);
280 retval = ext2fs_write_new_inode(fs, *ino, &inode);
284 ext2fs_inode_alloc_stats2(fs, *ino, +1, 0);
286 retval = ext2fs_extent_open2(fs, *ino, &inode, &handle);
291 left = num ? num : 1;
296 retval = ext2fs_find_first_zero_block_bitmap2(fs->block_map,
297 goal, ext2fs_blocks_count(fs->super) - 1, &end);
302 retval = ext2fs_find_first_set_block_bitmap2(fs->block_map, goal,
303 ext2fs_blocks_count(fs->super) - 1, &bend);
304 if (retval == ENOENT) {
305 bend = ext2fs_blocks_count(fs->super);
309 if (!num || bend - goal < left)
316 ext2fs_block_alloc_stats_range(fs, pblk, n, +1);
320 retval = ext2fs_zero_blocks2(fs, pblk, n,
324 com_err(program_name, retval,
325 _("while zeroing block %llu "
326 "for hugefile"), ret_blk);
331 struct ext2fs_extent newextent;
333 if (l > EXT_INIT_MAX_LEN)
334 l = EXT_INIT_MAX_LEN;
337 newextent.e_pblk = pblk;
338 newextent.e_lblk = lblk;
339 newextent.e_flags = 0;
341 retval = ext2fs_extent_insert(handle,
342 EXT2_EXTENT_INSERT_AFTER, &newextent);
351 retval = ext2fs_read_inode(fs, *ino, &inode);
355 retval = ext2fs_iblk_add_blocks(fs, &inode,
356 count / EXT2FS_CLUSTER_RATIO(fs));
359 size = (__u64) count * fs->blocksize;
360 inode.i_size = size & 0xffffffff;
361 inode.i_size_high = (size >> 32);
363 retval = ext2fs_write_new_inode(fs, *ino, &inode);
368 sprintf(fn_numbuf, "%0*lu", idx_digits, idx);
369 else if (num_files > 1)
370 sprintf(fn_numbuf, "%lu", idx);
373 retval = ext2fs_link(fs, dir, fn_buf, *ino, EXT2_FT_REG_FILE);
374 if (retval == EXT2_ET_DIR_NO_SPACE) {
375 retval = ext2fs_expand_dir(fs, dir);
386 ext2fs_extent_free(handle);
391 static blk64_t calc_overhead(ext2_filsys fs, blk64_t num)
393 blk64_t e_blocks, e_blocks2, e_blocks3, e_blocks4;
394 int extents_per_block;
395 int extents = (num + EXT_INIT_MAX_LEN - 1) / EXT_INIT_MAX_LEN;
401 * This calculation is due to the fact that we are inefficient
402 * in how handle extent splits when appending to the end of
403 * the extent tree. Sigh. We should fix this so that we can
404 * actually store 340 extents per 4k block, instead of only 170.
406 extents_per_block = ((fs->blocksize -
407 sizeof(struct ext3_extent_header)) /
408 sizeof(struct ext3_extent));
409 extents_per_block = (extents_per_block/ 2) - 1;
411 e_blocks = (extents + extents_per_block - 1) / extents_per_block;
412 e_blocks2 = (e_blocks + extents_per_block - 1) / extents_per_block;
413 e_blocks3 = (e_blocks2 + extents_per_block - 1) / extents_per_block;
414 e_blocks4 = (e_blocks3 + extents_per_block - 1) / extents_per_block;
415 return e_blocks + e_blocks2 + e_blocks3 + e_blocks4;
419 * Find the place where we should start allocating blocks for the huge
420 * files. Leave <slack> free blocks at the beginning of the file
421 * system for things like metadata blocks.
423 static blk64_t get_start_block(ext2_filsys fs, blk64_t slack)
426 blk64_t blk = fs->super->s_first_data_block, next;
427 blk64_t last_blk = ext2fs_blocks_count(fs->super) - 1;
430 retval = ext2fs_find_first_zero_block_bitmap2(fs->block_map,
431 blk, last_blk, &blk);
435 retval = ext2fs_find_first_set_block_bitmap2(fs->block_map,
436 blk, last_blk, &next);
441 if (next - blk > slack) {
446 slack -= (next - blk);
452 static blk64_t round_up_align(blk64_t b, unsigned long align,
459 part_offset = part_offset % align;
460 m = (b + part_offset) % align;
466 errcode_t mk_hugefiles(ext2_filsys fs, const char *device_name)
471 blk64_t fs_blocks, part_offset;
476 if (!get_bool_from_profile(fs_types, "make_hugefiles", 0))
479 uid = get_int_from_profile(fs_types, "hugefiles_uid", 0);
480 gid = get_int_from_profile(fs_types, "hugefiles_gid", 0);
481 fs->umask = get_int_from_profile(fs_types, "hugefiles_umask", 077);
482 num_files = get_int_from_profile(fs_types, "num_hugefiles", 0);
483 t = get_string_from_profile(fs_types, "hugefiles_slack", "1M");
484 num_slack = parse_num_blocks2(t, fs->super->s_log_block_size);
486 t = get_string_from_profile(fs_types, "hugefiles_size", "0");
487 num_blocks = parse_num_blocks2(t, fs->super->s_log_block_size);
489 t = get_string_from_profile(fs_types, "hugefiles_align", "0");
490 align = parse_num_blocks2(t, fs->super->s_log_block_size);
492 if (get_bool_from_profile(fs_types, "hugefiles_align_disk", 0))
493 part_offset = get_partition_start(device_name) /
494 (fs->blocksize / 512);
495 num_blocks = round_up_align(num_blocks, align, 0);
496 zero_hugefile = get_bool_from_profile(fs_types, "zero_hugefiles",
499 t = get_string_from_profile(fs_types, "hugefiles_dir", "/");
500 retval = create_directory(fs, t, &dir);
505 fn_prefix = get_string_from_profile(fs_types, "hugefiles_name",
507 idx_digits = get_int_from_profile(fs_types, "hugefiles_digits", 5);
508 d = int_log10(num_files) + 1;
511 dsize = strlen(fn_prefix) + d + 16;
512 fn_buf = malloc(dsize);
517 strcpy(fn_buf, fn_prefix);
518 fn_numbuf = fn_buf + strlen(fn_prefix);
521 fs_blocks = ext2fs_free_blocks_count(fs->super);
522 if (fs_blocks < num_slack + align)
524 fs_blocks -= num_slack + align;
525 if (num_blocks && num_blocks > fs_blocks)
527 if (num_blocks == 0 && num_files == 0)
530 if (num_files == 0 && num_blocks) {
531 num_files = fs_blocks / num_blocks;
532 fs_blocks -= (num_files / 16) + 1;
533 fs_blocks -= calc_overhead(fs, num_blocks) * num_files;
534 num_files = fs_blocks / num_blocks;
537 if (num_blocks == 0 && num_files > 1) {
538 num_blocks = fs_blocks / num_files;
539 fs_blocks -= (num_files / 16) + 1;
540 fs_blocks -= calc_overhead(fs, num_blocks) * num_files;
541 num_blocks = fs_blocks / num_files;
544 num_slack += calc_overhead(fs, num_blocks) * num_files;
545 num_slack += (num_files / 16) + 1; /* space for dir entries */
546 goal = get_start_block(fs, num_slack);
547 goal = round_up_align(goal, align, part_offset);
549 if ((num_blocks ? num_blocks : fs_blocks) >
550 (0x80000000UL / fs->blocksize))
551 fs->super->s_feature_ro_compat |=
552 EXT2_FEATURE_RO_COMPAT_LARGE_FILE;
555 if (zero_hugefile && verbose)
556 printf("%s", _("Huge files will be zero'ed\n"));
557 printf(_("Creating %lu huge file(s) "), num_files);
559 printf(_("with %llu blocks each"), num_blocks);
562 for (i=0; i < num_files; i++) {
565 retval = mk_hugefile(fs, num_blocks, dir, i, &ino);
567 com_err(program_name, retval,
568 _("while creating huge file %lu"), i);
573 fputs(_("done\n"), stdout);