2 * mk_hugefiles.c -- create huge files
13 #include <sys/utsname.h>
30 #ifdef HAVE_SYS_IOCTL_H
31 #include <sys/ioctl.h>
33 #include <sys/types.h>
35 #ifdef HAVE_SYS_SYSMACROS_H
36 #include <sys/sysmacros.h>
40 #include <blkid/blkid.h>
42 #include "ext2fs/ext2_fs.h"
43 #include "ext2fs/ext2fsP.h"
44 #include "et/com_err.h"
45 #include "uuid/uuid.h"
47 #include "ext2fs/ext2fs.h"
49 #include "support/profile.h"
50 #include "support/prof_err.h"
51 #include "support/nls-enable.h"
56 static blk64_t num_blocks
;
57 static blk64_t num_slack
;
58 static unsigned long num_files
;
60 static char *fn_prefix
;
61 static int idx_digits
;
63 static char *fn_numbuf
;
64 int zero_hugefile
= 1;
67 get_partition_start(const char *device_name
EXT2FS_ATTR((unused
)))
70 unsigned long long start
;
76 if ((stat(device_name
, &st
) < 0) || !S_ISBLK(st
.st_mode
))
79 sprintf(path
, "/sys/dev/block/%d:%d/start",
80 major(st
.st_rdev
), minor(st
.st_rdev
));
84 n
= fscanf(f
, "%llu", &start
);
86 return (n
== 1) ? start
: 0;
92 static errcode_t
create_directory(ext2_filsys fs
, char *dir
,
96 struct ext2_inode inode
;
97 ext2_ino_t ino
= EXT2_ROOT_INO
;
100 char *fn
, *cp
, *next
;
102 fn
= malloc(strlen(dir
) + 1);
109 next
= strchr(cp
, '/');
113 retval
= ext2fs_new_inode(fs
, ino
, LINUX_S_IFDIR
,
118 retval
= ext2fs_mkdir(fs
, ino
, newdir
, cp
);
123 retval
= ext2fs_read_inode(fs
, ino
, &inode
);
127 inode
.i_uid
= uid
& 0xFFFF;
128 ext2fs_set_i_uid_high(inode
, (uid
>> 16) & 0xffff);
129 inode
.i_gid
= gid
& 0xFFFF;
130 ext2fs_set_i_gid_high(inode
, (gid
>> 16) & 0xffff);
131 retval
= ext2fs_write_inode(fs
, ino
, &inode
);
135 if (next
== NULL
|| *next
== '\0')
146 static errcode_t
mk_hugefile(ext2_filsys fs
, blk64_t num
,
147 ext2_ino_t dir
, unsigned long idx
, ext2_ino_t
*ino
)
151 blk64_t lblk
, bend
= 0;
155 struct ext2_inode inode
;
156 ext2_extent_handle_t handle
;
158 retval
= ext2fs_new_inode(fs
, 0, LINUX_S_IFREG
, NULL
, ino
);
162 memset(&inode
, 0, sizeof(struct ext2_inode
));
163 inode
.i_mode
= LINUX_S_IFREG
| (0666 & ~fs
->umask
);
164 inode
.i_links_count
= 1;
165 inode
.i_uid
= uid
& 0xFFFF;
166 ext2fs_set_i_uid_high(inode
, (uid
>> 16) & 0xffff);
167 inode
.i_gid
= gid
& 0xFFFF;
168 ext2fs_set_i_gid_high(inode
, (gid
>> 16) & 0xffff);
170 retval
= ext2fs_write_new_inode(fs
, *ino
, &inode
);
174 ext2fs_inode_alloc_stats2(fs
, *ino
, +1, 0);
176 retval
= ext2fs_extent_open2(fs
, *ino
, &inode
, &handle
);
181 * We don't use ext2fs_fallocate() here because hugefiles are
182 * designed to be physically contiguous (if the block group
183 * descriptors are configured to be in a single block at the
184 * beginning of the file system, by using the
185 * packed_meta_blocks layout), with the extent tree blocks
186 * allocated near the beginning of the file system.
189 left
= num
? num
: 1;
194 retval
= ext2fs_find_first_zero_block_bitmap2(fs
->block_map
,
195 goal
, ext2fs_blocks_count(fs
->super
) - 1, &end
);
200 retval
= ext2fs_find_first_set_block_bitmap2(fs
->block_map
, goal
,
201 ext2fs_blocks_count(fs
->super
) - 1, &bend
);
202 if (retval
== ENOENT
) {
203 bend
= ext2fs_blocks_count(fs
->super
);
207 if (!num
|| bend
- goal
< left
)
214 ext2fs_block_alloc_stats_range(fs
, pblk
, n
, +1);
218 retval
= ext2fs_zero_blocks2(fs
, pblk
, n
,
222 com_err(program_name
, retval
,
223 _("while zeroing block %llu "
225 (unsigned long long) ret_blk
);
230 struct ext2fs_extent newextent
;
232 if (l
> EXT_INIT_MAX_LEN
)
233 l
= EXT_INIT_MAX_LEN
;
236 newextent
.e_pblk
= pblk
;
237 newextent
.e_lblk
= lblk
;
238 newextent
.e_flags
= 0;
240 retval
= ext2fs_extent_insert(handle
,
241 EXT2_EXTENT_INSERT_AFTER
, &newextent
);
250 retval
= ext2fs_read_inode(fs
, *ino
, &inode
);
254 retval
= ext2fs_iblk_add_blocks(fs
, &inode
,
255 count
/ EXT2FS_CLUSTER_RATIO(fs
));
258 size
= (__u64
) count
* fs
->blocksize
;
259 retval
= ext2fs_inode_size_set(fs
, &inode
, size
);
263 retval
= ext2fs_write_new_inode(fs
, *ino
, &inode
);
268 sprintf(fn_numbuf
, "%0*lu", idx_digits
, idx
);
269 else if (num_files
> 1)
270 sprintf(fn_numbuf
, "%lu", idx
);
273 retval
= ext2fs_link(fs
, dir
, fn_buf
, *ino
, EXT2_FT_REG_FILE
);
274 if (retval
== EXT2_ET_DIR_NO_SPACE
) {
275 retval
= ext2fs_expand_dir(fs
, dir
);
286 ext2fs_extent_free(handle
);
291 static blk64_t
calc_overhead(ext2_filsys fs
, blk64_t num
)
293 blk64_t e_blocks
, e_blocks2
, e_blocks3
, e_blocks4
;
294 int extents_per_block
;
295 int extents
= (num
+ EXT_INIT_MAX_LEN
- 1) / EXT_INIT_MAX_LEN
;
301 * This calculation is due to the fact that we are inefficient
302 * in how handle extent splits when appending to the end of
303 * the extent tree. Sigh. We should fix this so that we can
304 * actually store 340 extents per 4k block, instead of only 170.
306 extents_per_block
= ((fs
->blocksize
-
307 sizeof(struct ext3_extent_header
)) /
308 sizeof(struct ext3_extent
));
309 extents_per_block
= (extents_per_block
/ 2) - 1;
311 e_blocks
= (extents
+ extents_per_block
- 1) / extents_per_block
;
312 e_blocks2
= (e_blocks
+ extents_per_block
- 1) / extents_per_block
;
313 e_blocks3
= (e_blocks2
+ extents_per_block
- 1) / extents_per_block
;
314 e_blocks4
= (e_blocks3
+ extents_per_block
- 1) / extents_per_block
;
315 return (e_blocks
+ e_blocks2
+ e_blocks3
+ e_blocks4
) *
316 EXT2FS_CLUSTER_RATIO(fs
);
320 * Find the place where we should start allocating blocks for the huge
321 * files. Leave <slack> free blocks at the beginning of the file
322 * system for things like metadata blocks.
324 static blk64_t
get_start_block(ext2_filsys fs
, blk64_t slack
)
327 blk64_t blk
= fs
->super
->s_first_data_block
, next
;
328 blk64_t last_blk
= ext2fs_blocks_count(fs
->super
) - 1;
331 retval
= ext2fs_find_first_zero_block_bitmap2(fs
->block_map
,
332 blk
, last_blk
, &blk
);
336 retval
= ext2fs_find_first_set_block_bitmap2(fs
->block_map
,
337 blk
, last_blk
, &next
);
341 if (next
- blk
> slack
) {
346 slack
-= (next
- blk
);
352 static blk64_t
round_up_align(blk64_t b
, unsigned long align
,
359 part_offset
= part_offset
% align
;
360 m
= (b
+ part_offset
) % align
;
366 errcode_t
mk_hugefiles(ext2_filsys fs
, const char *device_name
)
371 blk64_t fs_blocks
, part_offset
= 0;
376 if (!get_bool_from_profile(fs_types
, "make_hugefiles", 0))
379 if (!ext2fs_has_feature_extents(fs
->super
))
380 return EXT2_ET_EXTENT_NOT_SUPPORTED
;
382 uid
= get_int_from_profile(fs_types
, "hugefiles_uid", 0);
383 gid
= get_int_from_profile(fs_types
, "hugefiles_gid", 0);
384 fs
->umask
= get_int_from_profile(fs_types
, "hugefiles_umask", 077);
385 num_files
= get_int_from_profile(fs_types
, "num_hugefiles", 0);
386 t
= get_string_from_profile(fs_types
, "hugefiles_slack", "1M");
387 num_slack
= parse_num_blocks2(t
, fs
->super
->s_log_block_size
);
389 t
= get_string_from_profile(fs_types
, "hugefiles_size", "0");
390 num_blocks
= parse_num_blocks2(t
, fs
->super
->s_log_block_size
);
392 t
= get_string_from_profile(fs_types
, "hugefiles_align", "0");
393 align
= parse_num_blocks2(t
, fs
->super
->s_log_block_size
);
395 if (get_bool_from_profile(fs_types
, "hugefiles_align_disk", 0)) {
396 part_offset
= get_partition_start(device_name
) /
397 (fs
->blocksize
/ 512);
398 if (part_offset
% EXT2FS_CLUSTER_RATIO(fs
)) {
400 _("Partition offset of %llu (%uk) blocks "
401 "not compatible with cluster size %u.\n"),
402 (unsigned long long) part_offset
, fs
->blocksize
,
403 EXT2_CLUSTER_SIZE(fs
->super
));
407 num_blocks
= round_up_align(num_blocks
, align
, 0);
408 zero_hugefile
= get_bool_from_profile(fs_types
, "zero_hugefiles",
411 t
= get_string_from_profile(fs_types
, "hugefiles_dir", "/");
412 retval
= create_directory(fs
, t
, &dir
);
417 fn_prefix
= get_string_from_profile(fs_types
, "hugefiles_name",
419 idx_digits
= get_int_from_profile(fs_types
, "hugefiles_digits", 5);
420 d
= int_log10(num_files
) + 1;
423 dsize
= strlen(fn_prefix
) + d
+ 16;
424 fn_buf
= malloc(dsize
);
429 strcpy(fn_buf
, fn_prefix
);
430 fn_numbuf
= fn_buf
+ strlen(fn_prefix
);
433 fs_blocks
= ext2fs_free_blocks_count(fs
->super
);
434 if (fs_blocks
< num_slack
+ align
)
436 fs_blocks
-= num_slack
+ align
;
437 if (num_blocks
&& num_blocks
> fs_blocks
)
439 if (num_blocks
== 0 && num_files
== 0)
442 if (num_files
== 0 && num_blocks
) {
443 num_files
= fs_blocks
/ num_blocks
;
444 fs_blocks
-= (num_files
/ 16) + 1;
445 fs_blocks
-= calc_overhead(fs
, num_blocks
) * num_files
;
446 num_files
= fs_blocks
/ num_blocks
;
449 if (num_blocks
== 0 && num_files
> 1) {
450 num_blocks
= fs_blocks
/ num_files
;
451 fs_blocks
-= (num_files
/ 16) + 1;
452 fs_blocks
-= calc_overhead(fs
, num_blocks
) * num_files
;
453 num_blocks
= fs_blocks
/ num_files
;
456 num_slack
+= (calc_overhead(fs
, num_blocks
? num_blocks
: fs_blocks
) *
458 num_slack
+= (num_files
/ 16) + 1; /* space for dir entries */
459 goal
= get_start_block(fs
, num_slack
);
460 goal
= round_up_align(goal
, align
, part_offset
);
462 if ((num_blocks
? num_blocks
: fs_blocks
) >
463 (0x80000000UL
/ fs
->blocksize
))
464 ext2fs_set_feature_large_file(fs
->super
);
467 if (zero_hugefile
&& verbose
)
468 printf("%s", _("Huge files will be zero'ed\n"));
469 printf(_("Creating %lu huge file(s) "), num_files
);
471 printf(_("with %llu blocks each"),
472 (unsigned long long) num_blocks
);
475 for (i
=0; i
< num_files
; i
++) {
478 retval
= mk_hugefile(fs
, num_blocks
, dir
, i
, &ino
);
480 com_err(program_name
, retval
,
481 _("while creating huge file %lu"), i
);
486 fputs(_("done\n"), stdout
);