• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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"),
341 					(unsigned long long) ret_blk);
342 		}
343 
344 		while (n) {
345 			blk64_t l = n;
346 			struct ext2fs_extent newextent;
347 
348 			if (l > EXT_INIT_MAX_LEN)
349 				l = EXT_INIT_MAX_LEN;
350 
351 			newextent.e_len = l;
352 			newextent.e_pblk = pblk;
353 			newextent.e_lblk = lblk;
354 			newextent.e_flags = 0;
355 
356 			retval = ext2fs_extent_insert(handle,
357 					EXT2_EXTENT_INSERT_AFTER, &newextent);
358 			if (retval)
359 				return retval;
360 			pblk += l;
361 			lblk += l;
362 			n -= l;
363 		}
364 	}
365 
366 	retval = ext2fs_read_inode(fs, *ino, &inode);
367 	if (retval)
368 		goto errout;
369 
370 	retval = ext2fs_iblk_add_blocks(fs, &inode,
371 					count / EXT2FS_CLUSTER_RATIO(fs));
372 	if (retval)
373 		goto errout;
374 	size = (__u64) count * fs->blocksize;
375 	retval = ext2fs_inode_size_set(fs, &inode, size);
376 	if (retval)
377 		goto errout;
378 
379 	retval = ext2fs_write_new_inode(fs, *ino, &inode);
380 	if (retval)
381 		goto errout;
382 
383 	if (idx_digits)
384 		sprintf(fn_numbuf, "%0*lu", idx_digits, idx);
385 	else if (num_files > 1)
386 		sprintf(fn_numbuf, "%lu", idx);
387 
388 retry:
389 	retval = ext2fs_link(fs, dir, fn_buf, *ino, EXT2_FT_REG_FILE);
390 	if (retval == EXT2_ET_DIR_NO_SPACE) {
391 		retval = ext2fs_expand_dir(fs, dir);
392 		if (retval)
393 			goto errout;
394 		goto retry;
395 	}
396 
397 	if (retval)
398 		goto errout;
399 
400 errout:
401 	if (handle)
402 		ext2fs_extent_free(handle);
403 
404 	return retval;
405 }
406 
calc_overhead(ext2_filsys fs,blk64_t num)407 static blk64_t calc_overhead(ext2_filsys fs, blk64_t num)
408 {
409 	blk64_t e_blocks, e_blocks2, e_blocks3, e_blocks4;
410 	int extents_per_block;
411 	int extents = (num + EXT_INIT_MAX_LEN - 1) / EXT_INIT_MAX_LEN;
412 
413 	if (extents <= 4)
414 		return 0;
415 
416 	/*
417 	 * This calculation is due to the fact that we are inefficient
418 	 * in how handle extent splits when appending to the end of
419 	 * the extent tree.  Sigh.  We should fix this so that we can
420 	 * actually store 340 extents per 4k block, instead of only 170.
421 	 */
422 	extents_per_block = ((fs->blocksize -
423 			      sizeof(struct ext3_extent_header)) /
424 			     sizeof(struct ext3_extent));
425 	extents_per_block = (extents_per_block/ 2) - 1;
426 
427 	e_blocks = (extents + extents_per_block - 1) / extents_per_block;
428 	e_blocks2 = (e_blocks + extents_per_block - 1) / extents_per_block;
429 	e_blocks3 = (e_blocks2 + extents_per_block - 1) / extents_per_block;
430 	e_blocks4 = (e_blocks3 + extents_per_block - 1) / extents_per_block;
431 	return (e_blocks + e_blocks2 + e_blocks3 + e_blocks4) *
432 		EXT2FS_CLUSTER_RATIO(fs);
433 }
434 
435 /*
436  * Find the place where we should start allocating blocks for the huge
437  * files.  Leave <slack> free blocks at the beginning of the file
438  * system for things like metadata blocks.
439  */
get_start_block(ext2_filsys fs,blk64_t slack)440 static blk64_t get_start_block(ext2_filsys fs, blk64_t slack)
441 {
442 	errcode_t retval;
443 	blk64_t blk = fs->super->s_first_data_block, next;
444 	blk64_t last_blk = ext2fs_blocks_count(fs->super) - 1;
445 
446 	while (slack) {
447 		retval = ext2fs_find_first_zero_block_bitmap2(fs->block_map,
448 						blk, last_blk, &blk);
449 		if (retval)
450 			break;
451 
452 		retval = ext2fs_find_first_set_block_bitmap2(fs->block_map,
453 						blk, last_blk, &next);
454 		if (retval)
455 			next = last_blk;
456 
457 		if (next - blk > slack) {
458 			blk += slack;
459 			break;
460 		}
461 
462 		slack -= (next - blk);
463 		blk = next;
464 	}
465 	return blk;
466 }
467 
round_up_align(blk64_t b,unsigned long align,blk64_t part_offset)468 static blk64_t round_up_align(blk64_t b, unsigned long align,
469 			      blk64_t part_offset)
470 {
471 	unsigned long m;
472 
473 	if (align == 0)
474 		return b;
475 	part_offset = part_offset % align;
476 	m = (b + part_offset) % align;
477 	if (m)
478 		b += align - m;
479 	return b;
480 }
481 
mk_hugefiles(ext2_filsys fs,const char * device_name)482 errcode_t mk_hugefiles(ext2_filsys fs, const char *device_name)
483 {
484 	unsigned long	i;
485 	ext2_ino_t	dir;
486 	errcode_t	retval;
487 	blk64_t		fs_blocks, part_offset = 0;
488 	unsigned long	align;
489 	int		d, dsize;
490 	char		*t;
491 
492 	if (!get_bool_from_profile(fs_types, "make_hugefiles", 0))
493 		return 0;
494 
495 	if (!ext2fs_has_feature_extents(fs->super))
496 		return EXT2_ET_EXTENT_NOT_SUPPORTED;
497 
498 	uid = get_int_from_profile(fs_types, "hugefiles_uid", 0);
499 	gid = get_int_from_profile(fs_types, "hugefiles_gid", 0);
500 	fs->umask = get_int_from_profile(fs_types, "hugefiles_umask", 077);
501 	num_files = get_int_from_profile(fs_types, "num_hugefiles", 0);
502 	t = get_string_from_profile(fs_types, "hugefiles_slack", "1M");
503 	num_slack = parse_num_blocks2(t, fs->super->s_log_block_size);
504 	free(t);
505 	t = get_string_from_profile(fs_types, "hugefiles_size", "0");
506 	num_blocks = parse_num_blocks2(t, fs->super->s_log_block_size);
507 	free(t);
508 	t = get_string_from_profile(fs_types, "hugefiles_align", "0");
509 	align = parse_num_blocks2(t, fs->super->s_log_block_size);
510 	free(t);
511 	if (get_bool_from_profile(fs_types, "hugefiles_align_disk", 0)) {
512 		part_offset = get_partition_start(device_name) /
513 			(fs->blocksize / 512);
514 		if (part_offset % EXT2FS_CLUSTER_RATIO(fs)) {
515 			fprintf(stderr,
516 				_("Partition offset of %llu (%uk) blocks "
517 				  "not compatible with cluster size %u.\n"),
518 				(unsigned long long) part_offset, fs->blocksize,
519 				EXT2_CLUSTER_SIZE(fs->super));
520 			exit(1);
521 		}
522 	}
523 	num_blocks = round_up_align(num_blocks, align, 0);
524 	zero_hugefile = get_bool_from_profile(fs_types, "zero_hugefiles",
525 					      zero_hugefile);
526 
527 	t = get_string_from_profile(fs_types, "hugefiles_dir", "/");
528 	retval = create_directory(fs, t, &dir);
529 	free(t);
530 	if (retval)
531 		return retval;
532 
533 	fn_prefix = get_string_from_profile(fs_types, "hugefiles_name",
534 					    "hugefile");
535 	idx_digits = get_int_from_profile(fs_types, "hugefiles_digits", 5);
536 	d = int_log10(num_files) + 1;
537 	if (idx_digits > d)
538 		d = idx_digits;
539 	dsize = strlen(fn_prefix) + d + 16;
540 	fn_buf = malloc(dsize);
541 	if (!fn_buf) {
542 		free(fn_prefix);
543 		return ENOMEM;
544 	}
545 	strcpy(fn_buf, fn_prefix);
546 	fn_numbuf = fn_buf + strlen(fn_prefix);
547 	free(fn_prefix);
548 
549 	fs_blocks = ext2fs_free_blocks_count(fs->super);
550 	if (fs_blocks < num_slack + align)
551 		return ENOSPC;
552 	fs_blocks -= num_slack + align;
553 	if (num_blocks && num_blocks > fs_blocks)
554 		return ENOSPC;
555 	if (num_blocks == 0 && num_files == 0)
556 		num_files = 1;
557 
558 	if (num_files == 0 && num_blocks) {
559 		num_files = fs_blocks / num_blocks;
560 		fs_blocks -= (num_files / 16) + 1;
561 		fs_blocks -= calc_overhead(fs, num_blocks) * num_files;
562 		num_files = fs_blocks / num_blocks;
563 	}
564 
565 	if (num_blocks == 0 && num_files > 1) {
566 		num_blocks = fs_blocks / num_files;
567 		fs_blocks -= (num_files / 16) + 1;
568 		fs_blocks -= calc_overhead(fs, num_blocks) * num_files;
569 		num_blocks = fs_blocks / num_files;
570 	}
571 
572 	num_slack += (calc_overhead(fs, num_blocks ? num_blocks : fs_blocks) *
573 		      num_files);
574 	num_slack += (num_files / 16) + 1; /* space for dir entries */
575 	goal = get_start_block(fs, num_slack);
576 	goal = round_up_align(goal, align, part_offset);
577 
578 	if ((num_blocks ? num_blocks : fs_blocks) >
579 	    (0x80000000UL / fs->blocksize))
580 		ext2fs_set_feature_large_file(fs->super);
581 
582 	if (!quiet) {
583 		if (zero_hugefile && verbose)
584 			printf("%s", _("Huge files will be zero'ed\n"));
585 		printf(_("Creating %lu huge file(s) "), num_files);
586 		if (num_blocks)
587 			printf(_("with %llu blocks each"),
588 			       (unsigned long long) num_blocks);
589 		fputs(": ", stdout);
590 	}
591 	for (i=0; i < num_files; i++) {
592 		ext2_ino_t ino;
593 
594 		retval = mk_hugefile(fs, num_blocks, dir, i, &ino);
595 		if (retval) {
596 			com_err(program_name, retval,
597 				_("while creating huge file %lu"), i);
598 			goto errout;
599 		}
600 	}
601 	if (!quiet)
602 		fputs(_("done\n"), stdout);
603 
604 errout:
605 	free(fn_buf);
606 	return retval;
607 }
608