1 /*
2 * mk_hugefiles.c -- create huge files
3 */
4
5 #define _XOPEN_SOURCE 600 /* for inclusion of PATH_MAX in Solaris */
6 #define _BSD_SOURCE /* for makedev() and major() */
7 #define _DEFAULT_SOURCE /* since glibc 2.20 _BSD_SOURCE is deprecated */
8
9 #include "config.h"
10 #include <stdio.h>
11 #include <stdarg.h>
12 #include <string.h>
13 #include <strings.h>
14 #include <fcntl.h>
15 #include <ctype.h>
16 #include <time.h>
17 #ifdef __linux__
18 #include <sys/utsname.h>
19 #endif
20 #ifdef HAVE_GETOPT_H
21 #include <getopt.h>
22 #else
23 extern char *optarg;
24 extern int optind;
25 #endif
26 #ifdef HAVE_UNISTD_H
27 #include <unistd.h>
28 #endif
29 #ifdef HAVE_STDLIB_H
30 #include <stdlib.h>
31 #endif
32 #ifdef HAVE_ERRNO_H
33 #include <errno.h>
34 #endif
35 #ifdef HAVE_SYS_IOCTL_H
36 #include <sys/ioctl.h>
37 #endif
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #ifdef HAVE_SYS_SYSMACROS_H
41 #include <sys/sysmacros.h>
42 #endif
43 #include <libgen.h>
44 #include <limits.h>
45 #include <blkid/blkid.h>
46
47 #include "ext2fs/ext2_fs.h"
48 #include "ext2fs/ext2fsP.h"
49 #include "et/com_err.h"
50 #include "uuid/uuid.h"
51 #include "e2p/e2p.h"
52 #include "ext2fs/ext2fs.h"
53 #include "util.h"
54 #include "support/profile.h"
55 #include "support/prof_err.h"
56 #include "support/nls-enable.h"
57 #include "mke2fs.h"
58
59 static int uid;
60 static int gid;
61 static blk64_t num_blocks;
62 static blk64_t num_slack;
63 static unsigned long num_files;
64 static blk64_t goal;
65 static char *fn_prefix;
66 static int idx_digits;
67 static char *fn_buf;
68 static char *fn_numbuf;
69 int zero_hugefile = 1;
70
71 #define SYSFS_PATH_LEN 300
72 typedef char sysfs_path_t[SYSFS_PATH_LEN];
73
74 #ifndef HAVE_SNPRINTF
75 /*
76 * We are very careful to avoid needing to worry about buffer
77 * overflows, so we don't really need to use snprintf() except as an
78 * additional safety check. So if snprintf() is not present, it's
79 * safe to fall back to vsprintf(). This provides portability since
80 * vsprintf() is guaranteed by C89, while snprintf() is only
81 * guaranteed by C99 --- which for example, Microsoft Visual Studio
82 * has *still* not bothered to implement. :-/ (Not that I expect
83 * mke2fs to be ported to MS Visual Studio any time soon, but
84 * libext2fs *does* get built on Microsoft platforms, and we might
85 * want to move this into libext2fs some day.)
86 */
my_snprintf(char * str,size_t size,const char * format,...)87 static int my_snprintf(char *str, size_t size, const char *format, ...)
88 {
89 va_list ap;
90 int ret;
91
92 va_start(ap, format);
93 ret = vsprintf(str, format, ap);
94 va_end(ap);
95 return ret;
96 }
97
98 #define snprintf my_snprintf
99 #endif
100
101 /*
102 * Fall back to Linux's definitions of makedev and major are needed.
103 * The search_sysfs_block() function is highly unlikely to work on
104 * non-Linux systems anyway.
105 */
106 #ifndef makedev
107 #define makedev(maj, min) (((maj) << 8) + (min))
108 #endif
109
search_sysfs_block(dev_t devno,sysfs_path_t ret_path)110 static char *search_sysfs_block(dev_t devno, sysfs_path_t ret_path)
111 {
112 struct dirent *de, *p_de;
113 DIR *dir = NULL, *p_dir = NULL;
114 FILE *f;
115 sysfs_path_t path, p_path;
116 unsigned int major, minor;
117 char *ret = ret_path;
118
119 ret_path[0] = 0;
120 if ((dir = opendir("/sys/block")) == NULL)
121 return NULL;
122 while ((de = readdir(dir)) != NULL) {
123 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..") ||
124 strlen(de->d_name) > sizeof(path)-32)
125 continue;
126 snprintf(path, SYSFS_PATH_LEN,
127 "/sys/block/%s/dev", de->d_name);
128 f = fopen(path, "r");
129 if (f &&
130 (fscanf(f, "%u:%u", &major, &minor) == 2)) {
131 fclose(f); f = NULL;
132 if (makedev(major, minor) == devno) {
133 snprintf(ret_path, SYSFS_PATH_LEN,
134 "/sys/block/%s", de->d_name);
135 goto success;
136 }
137 #ifdef major
138 if (major(devno) != major)
139 continue;
140 #endif
141 }
142 if (f)
143 fclose(f);
144
145 snprintf(path, SYSFS_PATH_LEN, "/sys/block/%s", de->d_name);
146
147 if (p_dir)
148 closedir(p_dir);
149 if ((p_dir = opendir(path)) == NULL)
150 continue;
151 while ((p_de = readdir(p_dir)) != NULL) {
152 if (!strcmp(p_de->d_name, ".") ||
153 !strcmp(p_de->d_name, "..") ||
154 (strlen(p_de->d_name) >
155 SYSFS_PATH_LEN - strlen(path) - 32))
156 continue;
157 snprintf(p_path, SYSFS_PATH_LEN, "%s/%s/dev",
158 path, p_de->d_name);
159
160 f = fopen(p_path, "r");
161 if (f &&
162 (fscanf(f, "%u:%u", &major, &minor) == 2) &&
163 (((major << 8) + minor) == devno)) {
164 fclose(f);
165 snprintf(ret_path, SYSFS_PATH_LEN, "%s/%s",
166 path, p_de->d_name);
167 goto success;
168 }
169 if (f)
170 fclose(f);
171 }
172 }
173 ret = NULL;
174 success:
175 if (dir)
176 closedir(dir);
177 if (p_dir)
178 closedir(p_dir);
179 return ret;
180 }
181
get_partition_start(const char * device_name)182 static blk64_t get_partition_start(const char *device_name)
183 {
184 unsigned long long start;
185 sysfs_path_t path;
186 struct stat st;
187 FILE *f;
188 char *cp;
189 int n;
190
191 if ((stat(device_name, &st) < 0) || !S_ISBLK(st.st_mode))
192 return 0;
193
194 cp = search_sysfs_block(st.st_rdev, path);
195 if (!cp)
196 return 0;
197 if (strlen(path) > SYSFS_PATH_LEN - sizeof("/start"))
198 return 0;
199 strcat(path, "/start");
200 f = fopen(path, "r");
201 if (!f)
202 return 0;
203 n = fscanf(f, "%llu", &start);
204 fclose(f);
205 return (n == 1) ? start : 0;
206 }
207
create_directory(ext2_filsys fs,char * dir,ext2_ino_t * ret_ino)208 static errcode_t create_directory(ext2_filsys fs, char *dir,
209 ext2_ino_t *ret_ino)
210
211 {
212 struct ext2_inode inode;
213 ext2_ino_t ino = EXT2_ROOT_INO;
214 ext2_ino_t newdir;
215 errcode_t retval = 0;
216 char *fn, *cp, *next;
217
218 fn = malloc(strlen(dir) + 1);
219 if (fn == NULL)
220 return ENOMEM;
221
222 strcpy(fn, dir);
223 cp = fn;
224 while(1) {
225 next = strchr(cp, '/');
226 if (next)
227 *next++ = 0;
228 if (*cp) {
229 retval = ext2fs_new_inode(fs, ino, LINUX_S_IFDIR,
230 NULL, &newdir);
231 if (retval)
232 goto errout;
233
234 retval = ext2fs_mkdir(fs, ino, newdir, cp);
235 if (retval)
236 goto errout;
237
238 ino = newdir;
239 retval = ext2fs_read_inode(fs, ino, &inode);
240 if (retval)
241 goto errout;
242
243 inode.i_uid = uid & 0xFFFF;
244 ext2fs_set_i_uid_high(inode, (uid >> 16) & 0xffff);
245 inode.i_gid = gid & 0xFFFF;
246 ext2fs_set_i_gid_high(inode, (gid >> 16) & 0xffff);
247 retval = ext2fs_write_inode(fs, ino, &inode);
248 if (retval)
249 goto errout;
250 }
251 if (next == NULL || *next == '\0')
252 break;
253 cp = next;
254 }
255 errout:
256 free(fn);
257 if (retval == 0)
258 *ret_ino = ino;
259 return retval;
260 }
261
mk_hugefile(ext2_filsys fs,blk64_t num,ext2_ino_t dir,unsigned long idx,ext2_ino_t * ino)262 static errcode_t mk_hugefile(ext2_filsys fs, blk64_t num,
263 ext2_ino_t dir, unsigned long idx, ext2_ino_t *ino)
264
265 {
266 errcode_t retval;
267 blk64_t lblk, bend = 0;
268 __u64 size;
269 blk64_t left;
270 blk64_t count = 0;
271 struct ext2_inode inode;
272 ext2_extent_handle_t handle;
273
274 retval = ext2fs_new_inode(fs, 0, LINUX_S_IFREG, NULL, ino);
275 if (retval)
276 return retval;
277
278 memset(&inode, 0, sizeof(struct ext2_inode));
279 inode.i_mode = LINUX_S_IFREG | (0666 & ~fs->umask);
280 inode.i_links_count = 1;
281 inode.i_uid = uid & 0xFFFF;
282 ext2fs_set_i_uid_high(inode, (uid >> 16) & 0xffff);
283 inode.i_gid = gid & 0xFFFF;
284 ext2fs_set_i_gid_high(inode, (gid >> 16) & 0xffff);
285
286 retval = ext2fs_write_new_inode(fs, *ino, &inode);
287 if (retval)
288 return retval;
289
290 ext2fs_inode_alloc_stats2(fs, *ino, +1, 0);
291
292 retval = ext2fs_extent_open2(fs, *ino, &inode, &handle);
293 if (retval)
294 return retval;
295
296 /*
297 * We don't use ext2fs_fallocate() here because hugefiles are
298 * designed to be physically contiguous (if the block group
299 * descriptors are configured to be in a single block at the
300 * beginning of the file system, by using the
301 * packed_meta_blocks layout), with the extent tree blocks
302 * allocated near the beginning of the file system.
303 */
304 lblk = 0;
305 left = num ? num : 1;
306 while (left) {
307 blk64_t pblk, end;
308 blk64_t n = left;
309
310 retval = ext2fs_find_first_zero_block_bitmap2(fs->block_map,
311 goal, ext2fs_blocks_count(fs->super) - 1, &end);
312 if (retval)
313 goto errout;
314 goal = end;
315
316 retval = ext2fs_find_first_set_block_bitmap2(fs->block_map, goal,
317 ext2fs_blocks_count(fs->super) - 1, &bend);
318 if (retval == ENOENT) {
319 bend = ext2fs_blocks_count(fs->super);
320 if (num == 0)
321 left = 0;
322 }
323 if (!num || bend - goal < left)
324 n = bend - goal;
325 pblk = goal;
326 if (num)
327 left -= n;
328 goal += n;
329 count += n;
330 ext2fs_block_alloc_stats_range(fs, pblk, n, +1);
331
332 if (zero_hugefile) {
333 blk64_t ret_blk;
334 retval = ext2fs_zero_blocks2(fs, pblk, n,
335 &ret_blk, NULL);
336
337 if (retval)
338 com_err(program_name, retval,
339 _("while zeroing block %llu "
340 "for hugefile"), ret_blk);
341 }
342
343 while (n) {
344 blk64_t l = n;
345 struct ext2fs_extent newextent;
346
347 if (l > EXT_INIT_MAX_LEN)
348 l = EXT_INIT_MAX_LEN;
349
350 newextent.e_len = l;
351 newextent.e_pblk = pblk;
352 newextent.e_lblk = lblk;
353 newextent.e_flags = 0;
354
355 retval = ext2fs_extent_insert(handle,
356 EXT2_EXTENT_INSERT_AFTER, &newextent);
357 if (retval)
358 return retval;
359 pblk += l;
360 lblk += l;
361 n -= l;
362 }
363 }
364
365 retval = ext2fs_read_inode(fs, *ino, &inode);
366 if (retval)
367 goto errout;
368
369 retval = ext2fs_iblk_add_blocks(fs, &inode,
370 count / EXT2FS_CLUSTER_RATIO(fs));
371 if (retval)
372 goto errout;
373 size = (__u64) count * fs->blocksize;
374 retval = ext2fs_inode_size_set(fs, &inode, size);
375 if (retval)
376 goto errout;
377
378 retval = ext2fs_write_new_inode(fs, *ino, &inode);
379 if (retval)
380 goto errout;
381
382 if (idx_digits)
383 sprintf(fn_numbuf, "%0*lu", idx_digits, idx);
384 else if (num_files > 1)
385 sprintf(fn_numbuf, "%lu", idx);
386
387 retry:
388 retval = ext2fs_link(fs, dir, fn_buf, *ino, EXT2_FT_REG_FILE);
389 if (retval == EXT2_ET_DIR_NO_SPACE) {
390 retval = ext2fs_expand_dir(fs, dir);
391 if (retval)
392 goto errout;
393 goto retry;
394 }
395
396 if (retval)
397 goto errout;
398
399 errout:
400 if (handle)
401 ext2fs_extent_free(handle);
402
403 return retval;
404 }
405
calc_overhead(ext2_filsys fs,blk64_t num)406 static blk64_t calc_overhead(ext2_filsys fs, blk64_t num)
407 {
408 blk64_t e_blocks, e_blocks2, e_blocks3, e_blocks4;
409 int extents_per_block;
410 int extents = (num + EXT_INIT_MAX_LEN - 1) / EXT_INIT_MAX_LEN;
411
412 if (extents <= 4)
413 return 0;
414
415 /*
416 * This calculation is due to the fact that we are inefficient
417 * in how handle extent splits when appending to the end of
418 * the extent tree. Sigh. We should fix this so that we can
419 * actually store 340 extents per 4k block, instead of only 170.
420 */
421 extents_per_block = ((fs->blocksize -
422 sizeof(struct ext3_extent_header)) /
423 sizeof(struct ext3_extent));
424 extents_per_block = (extents_per_block/ 2) - 1;
425
426 e_blocks = (extents + extents_per_block - 1) / extents_per_block;
427 e_blocks2 = (e_blocks + extents_per_block - 1) / extents_per_block;
428 e_blocks3 = (e_blocks2 + extents_per_block - 1) / extents_per_block;
429 e_blocks4 = (e_blocks3 + extents_per_block - 1) / extents_per_block;
430 return e_blocks + e_blocks2 + e_blocks3 + e_blocks4;
431 }
432
433 /*
434 * Find the place where we should start allocating blocks for the huge
435 * files. Leave <slack> free blocks at the beginning of the file
436 * system for things like metadata blocks.
437 */
get_start_block(ext2_filsys fs,blk64_t slack)438 static blk64_t get_start_block(ext2_filsys fs, blk64_t slack)
439 {
440 errcode_t retval;
441 blk64_t blk = fs->super->s_first_data_block, next;
442 blk64_t last_blk = ext2fs_blocks_count(fs->super) - 1;
443
444 while (slack) {
445 retval = ext2fs_find_first_zero_block_bitmap2(fs->block_map,
446 blk, last_blk, &blk);
447 if (retval)
448 break;
449
450 retval = ext2fs_find_first_set_block_bitmap2(fs->block_map,
451 blk, last_blk, &next);
452 if (retval)
453 next = last_blk;
454
455 if (next - blk > slack) {
456 blk += slack;
457 break;
458 }
459
460 slack -= (next - blk);
461 blk = next;
462 }
463 return blk;
464 }
465
round_up_align(blk64_t b,unsigned long align,blk64_t part_offset)466 static blk64_t round_up_align(blk64_t b, unsigned long align,
467 blk64_t part_offset)
468 {
469 unsigned long m;
470
471 if (align == 0)
472 return b;
473 part_offset = part_offset % align;
474 m = (b + part_offset) % align;
475 if (m)
476 b += align - m;
477 return b;
478 }
479
mk_hugefiles(ext2_filsys fs,const char * device_name)480 errcode_t mk_hugefiles(ext2_filsys fs, const char *device_name)
481 {
482 unsigned long i;
483 ext2_ino_t dir;
484 errcode_t retval;
485 blk64_t fs_blocks, part_offset = 0;
486 unsigned long align;
487 int d, dsize;
488 char *t;
489
490 if (!get_bool_from_profile(fs_types, "make_hugefiles", 0))
491 return 0;
492
493 if (!ext2fs_has_feature_extents(fs->super))
494 return EXT2_ET_EXTENT_NOT_SUPPORTED;
495
496 uid = get_int_from_profile(fs_types, "hugefiles_uid", 0);
497 gid = get_int_from_profile(fs_types, "hugefiles_gid", 0);
498 fs->umask = get_int_from_profile(fs_types, "hugefiles_umask", 077);
499 num_files = get_int_from_profile(fs_types, "num_hugefiles", 0);
500 t = get_string_from_profile(fs_types, "hugefiles_slack", "1M");
501 num_slack = parse_num_blocks2(t, fs->super->s_log_block_size);
502 free(t);
503 t = get_string_from_profile(fs_types, "hugefiles_size", "0");
504 num_blocks = parse_num_blocks2(t, fs->super->s_log_block_size);
505 free(t);
506 t = get_string_from_profile(fs_types, "hugefiles_align", "0");
507 align = parse_num_blocks2(t, fs->super->s_log_block_size);
508 free(t);
509 if (get_bool_from_profile(fs_types, "hugefiles_align_disk", 0)) {
510 part_offset = get_partition_start(device_name) /
511 (fs->blocksize / 512);
512 if (part_offset % EXT2FS_CLUSTER_RATIO(fs)) {
513 fprintf(stderr,
514 _("Partition offset of %llu (%uk) blocks "
515 "not compatible with cluster size %u.\n"),
516 part_offset, fs->blocksize,
517 EXT2_CLUSTER_SIZE(fs->super));
518 exit(1);
519 }
520 }
521 num_blocks = round_up_align(num_blocks, align, 0);
522 zero_hugefile = get_bool_from_profile(fs_types, "zero_hugefiles",
523 zero_hugefile);
524
525 t = get_string_from_profile(fs_types, "hugefiles_dir", "/");
526 retval = create_directory(fs, t, &dir);
527 free(t);
528 if (retval)
529 return retval;
530
531 fn_prefix = get_string_from_profile(fs_types, "hugefiles_name",
532 "hugefile");
533 idx_digits = get_int_from_profile(fs_types, "hugefiles_digits", 5);
534 d = int_log10(num_files) + 1;
535 if (idx_digits > d)
536 d = idx_digits;
537 dsize = strlen(fn_prefix) + d + 16;
538 fn_buf = malloc(dsize);
539 if (!fn_buf) {
540 free(fn_prefix);
541 return ENOMEM;
542 }
543 strcpy(fn_buf, fn_prefix);
544 fn_numbuf = fn_buf + strlen(fn_prefix);
545 free(fn_prefix);
546
547 fs_blocks = ext2fs_free_blocks_count(fs->super);
548 if (fs_blocks < num_slack + align)
549 return ENOSPC;
550 fs_blocks -= num_slack + align;
551 if (num_blocks && num_blocks > fs_blocks)
552 return ENOSPC;
553 if (num_blocks == 0 && num_files == 0)
554 num_files = 1;
555
556 if (num_files == 0 && num_blocks) {
557 num_files = fs_blocks / num_blocks;
558 fs_blocks -= (num_files / 16) + 1;
559 fs_blocks -= calc_overhead(fs, num_blocks) * num_files;
560 num_files = fs_blocks / num_blocks;
561 }
562
563 if (num_blocks == 0 && num_files > 1) {
564 num_blocks = fs_blocks / num_files;
565 fs_blocks -= (num_files / 16) + 1;
566 fs_blocks -= calc_overhead(fs, num_blocks) * num_files;
567 num_blocks = fs_blocks / num_files;
568 }
569
570 num_slack += calc_overhead(fs, num_blocks) * num_files;
571 num_slack += (num_files / 16) + 1; /* space for dir entries */
572 goal = get_start_block(fs, num_slack);
573 goal = round_up_align(goal, align, part_offset);
574
575 if ((num_blocks ? num_blocks : fs_blocks) >
576 (0x80000000UL / fs->blocksize))
577 ext2fs_set_feature_large_file(fs->super);
578
579 if (!quiet) {
580 if (zero_hugefile && verbose)
581 printf("%s", _("Huge files will be zero'ed\n"));
582 printf(_("Creating %lu huge file(s) "), num_files);
583 if (num_blocks)
584 printf(_("with %llu blocks each"), num_blocks);
585 fputs(": ", stdout);
586 }
587 for (i=0; i < num_files; i++) {
588 ext2_ino_t ino;
589
590 retval = mk_hugefile(fs, num_blocks, dir, i, &ino);
591 if (retval) {
592 com_err(program_name, retval,
593 _("while creating huge file %lu"), i);
594 goto errout;
595 }
596 }
597 if (!quiet)
598 fputs(_("done\n"), stdout);
599
600 errout:
601 free(fn_buf);
602 return retval;
603 }
604