• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * f2fs_io.c - f2fs ioctl utility
3  *
4  * Author: Jaegeuk Kim <jaegeuk@kernel.org>
5  *
6  * Copied portion of the code from ../f2fscrypt.c
7  */
8 
9 #ifndef _GNU_SOURCE
10 #define _GNU_SOURCE
11 #endif
12 #ifndef O_LARGEFILE
13 #define O_LARGEFILE 0
14 #endif
15 #ifndef __SANE_USERSPACE_TYPES__
16 #define __SANE_USERSPACE_TYPES__       /* For PPC64, to get LL64 types */
17 #endif
18 
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <getopt.h>
22 #include <inttypes.h>
23 #include <limits.h>
24 #include <linux/fs.h>
25 #include <signal.h>
26 #include <stdarg.h>
27 #include <stdbool.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <sys/mman.h>
32 #include <sys/sendfile.h>
33 #include <sys/stat.h>
34 #include <sys/types.h>
35 #include <termios.h>
36 #include <time.h>
37 #include <unistd.h>
38 #include <sys/xattr.h>
39 
40 #ifdef HAVE_CONFIG_H
41 #include <config.h>
42 #endif
43 #include <android_config.h>
44 
45 #include "f2fs_io.h"
46 
47 struct cmd_desc {
48 	const char *cmd_name;
49 	void (*cmd_func)(int, char **, const struct cmd_desc *);
50 	const char *cmd_desc;
51 	const char *cmd_help;
52 	int cmd_flags;
53 };
54 
55 static void __attribute__((noreturn))
do_die(const char * format,va_list va,int err)56 do_die(const char *format, va_list va, int err)
57 {
58 	vfprintf(stderr, format, va);
59 	if (err)
60 		fprintf(stderr, ": %s", strerror(err));
61 	putc('\n', stderr);
62 	exit(1);
63 }
64 
65 static void __attribute__((noreturn, format(printf, 1, 2)))
die_errno(const char * format,...)66 die_errno(const char *format, ...)
67 {
68 	va_list va;
69 
70 	va_start(va, format);
71 	do_die(format, va, errno);
72 	va_end(va);
73 }
74 
75 static void __attribute__((noreturn, format(printf, 1, 2)))
die(const char * format,...)76 die(const char *format, ...)
77 {
78 	va_list va;
79 
80 	va_start(va, format);
81 	do_die(format, va, 0);
82 	va_end(va);
83 }
84 
xmalloc(size_t size)85 static void *xmalloc(size_t size)
86 {
87 	void *p = malloc(size);
88 
89 	if (!p)
90 		die("Memory alloc failed (requested %zu bytes)", size);
91 	return p;
92 }
93 
aligned_xalloc(size_t alignment,size_t size)94 static void *aligned_xalloc(size_t alignment, size_t size)
95 {
96 	void *p = aligned_alloc(alignment, size);
97 
98 	if (!p)
99 		die("Memory alloc failed (requested %zu bytes)", size);
100 	return p;
101 }
102 
xopen(const char * pathname,int flags,mode_t mode)103 static int xopen(const char *pathname, int flags, mode_t mode)
104 {
105 	int fd = open(pathname, flags, mode);
106 
107 	if (fd < 0)
108 		die_errno("Failed to open %s", pathname);
109 	return fd;
110 }
111 
xread(int fd,void * buf,size_t count)112 static ssize_t xread(int fd, void *buf, size_t count)
113 {
114 	ssize_t ret = read(fd, buf, count);
115 
116 	if (ret < 0)
117 		die_errno("read failed");
118 	return ret;
119 }
120 
full_write(int fd,const void * buf,size_t count)121 static void full_write(int fd, const void *buf, size_t count)
122 {
123 	while (count) {
124 		ssize_t ret = write(fd, buf, count);
125 
126 		if (ret < 0)
127 			die_errno("write failed");
128 		buf = (char *)buf + ret;
129 		count -= ret;
130 	}
131 }
132 
133 #ifdef HAVE_MACH_TIME_H
get_current_us()134 static u64 get_current_us()
135 {
136 	return mach_absolute_time() / 1000;
137 }
138 #elif defined(HAVE_CLOCK_GETTIME) && defined(HAVE_CLOCK_BOOTTIME)
get_current_us()139 static u64 get_current_us()
140 {
141 	struct timespec t;
142 	t.tv_sec = t.tv_nsec = 0;
143 	clock_gettime(CLOCK_BOOTTIME, &t);
144 	return (u64)t.tv_sec * 1000000LL + t.tv_nsec / 1000;
145 }
146 #else
get_current_us()147 static u64 get_current_us()
148 {
149 	return 0;
150 }
151 #endif
152 
153 #define fsync_desc "fsync"
154 #define fsync_help						\
155 "f2fs_io fsync [file]\n\n"					\
156 "fsync given the file\n"					\
157 
do_fsync(int argc,char ** argv,const struct cmd_desc * cmd)158 static void do_fsync(int argc, char **argv, const struct cmd_desc *cmd)
159 {
160 	int fd;
161 
162 	if (argc != 2) {
163 		fputs("Excess arguments\n\n", stderr);
164 		fputs(cmd->cmd_help, stderr);
165 		exit(1);
166 	}
167 
168 	fd = xopen(argv[1], O_WRONLY, 0);
169 
170 	if (fsync(fd) != 0)
171 		die_errno("fsync failed");
172 
173 	printf("fsync a file\n");
174 	exit(0);
175 }
176 
177 #define fdatasync_desc "fdatasync"
178 #define fdatasync_help						\
179 "f2fs_io fdatasync [file]\n\n"					\
180 "fdatasync given the file\n"					\
181 
do_fdatasync(int argc,char ** argv,const struct cmd_desc * cmd)182 static void do_fdatasync(int argc, char **argv, const struct cmd_desc *cmd)
183 {
184 	int fd;
185 
186 	if (argc != 2) {
187 		fputs("Excess arguments\n\n", stderr);
188 		fputs(cmd->cmd_help, stderr);
189 		exit(1);
190 	}
191 
192 	fd = xopen(argv[1], O_WRONLY, 0);
193 
194 	if (fdatasync(fd) != 0)
195 		die_errno("fdatasync failed");
196 
197 	printf("fdatasync a file\n");
198 	exit(0);
199 }
200 
201 #define set_verity_desc "Set fs-verity"
202 #define set_verity_help					\
203 "f2fs_io set_verity [file]\n\n"				\
204 "Set fsverity bit given a file\n"			\
205 
do_set_verity(int argc,char ** argv,const struct cmd_desc * cmd)206 static void do_set_verity(int argc, char **argv, const struct cmd_desc *cmd)
207 {
208 	int ret, fd;
209 	struct fsverity_enable_arg args = {.version = 1};
210 
211 	args.hash_algorithm = FS_VERITY_HASH_ALG_SHA256;
212 	args.block_size = F2FS_DEFAULT_BLKSIZE;
213 
214 	if (argc != 2) {
215 		fputs("Excess arguments\n\n", stderr);
216 		fputs(cmd->cmd_help, stderr);
217 		exit(1);
218 	}
219 	fd = open(argv[1], O_RDONLY);
220 
221 	ret = ioctl(fd, FS_IOC_ENABLE_VERITY, &args);
222 	if (ret < 0) {
223 		perror("FS_IOC_ENABLE_VERITY");
224 		exit(1);
225 	}
226 
227 	printf("Set fsverity bit to %s\n", argv[1]);
228 	exit(0);
229 }
230 
231 #define getflags_desc "getflags ioctl"
232 #define getflags_help						\
233 "f2fs_io getflags [file]\n\n"					\
234 "get a flag given the file\n"					\
235 "flag can show \n"						\
236 "  encryption\n"						\
237 "  nocow(pinned)\n"						\
238 "  inline_data\n"						\
239 "  verity\n"							\
240 "  casefold\n"							\
241 "  compression\n"						\
242 "  nocompression\n"						\
243 "  immutable\n"
244 
do_getflags(int argc,char ** argv,const struct cmd_desc * cmd)245 static void do_getflags(int argc, char **argv, const struct cmd_desc *cmd)
246 {
247 	long flag = 0;
248 	int ret, fd;
249 	int exist = 0;
250 
251 	if (argc != 2) {
252 		fputs("Excess arguments\n\n", stderr);
253 		fputs(cmd->cmd_help, stderr);
254 		exit(1);
255 	}
256 
257 	fd = xopen(argv[1], O_RDONLY, 0);
258 
259 	ret = ioctl(fd, F2FS_IOC_GETFLAGS, &flag);
260 	printf("get a flag on %s ret=%d, flags=", argv[1], ret);
261 	if (flag & FS_CASEFOLD_FL) {
262 		printf("casefold");
263 		exist = 1;
264 	}
265 	if (flag & FS_COMPR_FL) {
266 		if (exist)
267 			printf(",");
268 		printf("compression");
269 		exist = 1;
270 	}
271 	if (flag & FS_NOCOMP_FL) {
272 		if (exist)
273 			printf(",");
274 		printf("nocompression");
275 		exist = 1;
276 	}
277 	if (flag & FS_ENCRYPT_FL) {
278 		if (exist)
279 			printf(",");
280 		printf("encrypt");
281 		exist = 1;
282 	}
283 	if (flag & FS_VERITY_FL) {
284 		if (exist)
285 			printf(",");
286 		printf("verity");
287 		exist = 1;
288 	}
289 	if (flag & FS_INLINE_DATA_FL) {
290 		if (exist)
291 			printf(",");
292 		printf("inline_data");
293 		exist = 1;
294 	}
295 	if (flag & FS_NOCOW_FL) {
296 		if (exist)
297 			printf(",");
298 		printf("nocow(pinned)");
299 		exist = 1;
300 	}
301 	if (flag & FS_IMMUTABLE_FL) {
302 		if (exist)
303 			printf(",");
304 		printf("immutable");
305 		exist = 1;
306 	}
307 	if (!exist)
308 		printf("none");
309 	printf("\n");
310 	exit(0);
311 }
312 
313 #define setflags_desc "setflags ioctl"
314 #define setflags_help						\
315 "f2fs_io setflags [flag] [file]\n\n"				\
316 "set a flag given the file\n"					\
317 "flag can be\n"							\
318 "  casefold\n"							\
319 "  compression\n"						\
320 "  nocompression\n"						\
321 "  immutable\n"							\
322 "  nocow\n"
323 
do_setflags(int argc,char ** argv,const struct cmd_desc * cmd)324 static void do_setflags(int argc, char **argv, const struct cmd_desc *cmd)
325 {
326 	long flag = 0;
327 	int ret, fd;
328 
329 	if (argc != 3) {
330 		fputs("Excess arguments\n\n", stderr);
331 		fputs(cmd->cmd_help, stderr);
332 		exit(1);
333 	}
334 
335 	fd = xopen(argv[2], O_RDONLY, 0);
336 
337 	ret = ioctl(fd, F2FS_IOC_GETFLAGS, &flag);
338 	printf("get a flag on %s ret=%d, flags=%lx\n", argv[1], ret, flag);
339 	if (ret)
340 		die_errno("F2FS_IOC_GETFLAGS failed");
341 
342 	if (!strcmp(argv[1], "casefold"))
343 		flag |= FS_CASEFOLD_FL;
344 	else if (!strcmp(argv[1], "compression"))
345 		flag |= FS_COMPR_FL;
346 	else if (!strcmp(argv[1], "nocompression"))
347 		flag |= FS_NOCOMP_FL;
348 	else if (!strcmp(argv[1], "immutable"))
349 		flag |= FS_IMMUTABLE_FL;
350 	else if (!strcmp(argv[1], "nocow"))
351 		flag |= FS_NOCOW_FL;
352 
353 	ret = ioctl(fd, F2FS_IOC_SETFLAGS, &flag);
354 	printf("set a flag on %s ret=%d, flags=%s\n", argv[2], ret, argv[1]);
355 	exit(0);
356 }
357 
358 #define clearflags_desc "clearflags ioctl"
359 #define clearflags_help						\
360 "f2fs_io clearflags [flag] [file]\n\n"				\
361 "clear a flag given the file\n"					\
362 "flag can be\n"							\
363 "  compression\n"						\
364 "  nocompression\n"						\
365 "  immutable\n"							\
366 "  nocow\n"
367 
do_clearflags(int argc,char ** argv,const struct cmd_desc * cmd)368 static void do_clearflags(int argc, char **argv, const struct cmd_desc *cmd)
369 {
370 	long flag = 0;
371 	int ret, fd;
372 
373 	if (argc != 3) {
374 		fputs("Excess arguments\n\n", stderr);
375 		fputs(cmd->cmd_help, stderr);
376 		exit(1);
377 	}
378 
379 	fd = xopen(argv[2], O_RDONLY, 0);
380 
381 	ret = ioctl(fd, F2FS_IOC_GETFLAGS, &flag);
382 	printf("get a flag on %s ret=%d, flags=%lx\n", argv[1], ret, flag);
383 	if (ret)
384 		die_errno("F2FS_IOC_GETFLAGS failed");
385 
386 	if (!strcmp(argv[1], "compression"))
387 		flag &= ~FS_COMPR_FL;
388 	else if (!strcmp(argv[1], "nocompression"))
389 		flag &= ~FS_NOCOMP_FL;
390 	else if (!strcmp(argv[1], "immutable"))
391 		flag &= ~FS_IMMUTABLE_FL;
392 	else if (!strcmp(argv[1], "nocow"))
393 		flag &= ~FS_NOCOW_FL;
394 
395 	ret = ioctl(fd, F2FS_IOC_SETFLAGS, &flag);
396 	printf("clear a flag on %s ret=%d, flags=%s\n", argv[2], ret, argv[1]);
397 	exit(0);
398 }
399 
400 #define shutdown_desc "shutdown filesystem"
401 #define shutdown_help					\
402 "f2fs_io shutdown [level] [dir]\n\n"			\
403 "Freeze and stop all IOs given mount point\n"		\
404 "level can be\n"					\
405 "  0 : going down with full sync\n"			\
406 "  1 : going down with checkpoint only\n"		\
407 "  2 : going down with no sync\n"			\
408 "  3 : going down with metadata flush\n"		\
409 "  4 : going down with fsck mark\n"
410 
do_shutdown(int argc,char ** argv,const struct cmd_desc * cmd)411 static void do_shutdown(int argc, char **argv, const struct cmd_desc *cmd)
412 {
413 	u32 flag;
414 	int ret, fd;
415 
416 	if (argc != 3) {
417 		fputs("Excess arguments\n\n", stderr);
418 		fputs(cmd->cmd_help, stderr);
419 		exit(1);
420 	}
421 
422 	flag = atoi(argv[1]);
423 	if (flag >= F2FS_GOING_DOWN_MAX) {
424 		fputs("Wrong level\n\n", stderr);
425 		fputs(cmd->cmd_help, stderr);
426 		exit(1);
427 	}
428 	fd = xopen(argv[2], O_RDONLY, 0);
429 
430 	ret = ioctl(fd, F2FS_IOC_SHUTDOWN, &flag);
431 	if (ret < 0)
432 		die_errno("F2FS_IOC_SHUTDOWN failed");
433 
434 	printf("Shutdown %s with level=%d\n", argv[2], flag);
435 	exit(0);
436 }
437 
438 #define fadvise_desc "fadvise"
439 #define fadvise_help						\
440 "f2fs_io fadvise [advice] [offset] [length] [file]\n\n"		\
441 "fadvice given the file\n"					\
442 "advice can be\n"						\
443 " willneed\n"							\
444 " dontneed\n"							\
445 " noreuse\n"							\
446 " sequential\n"							\
447 " random\n"							\
448 
do_fadvise(int argc,char ** argv,const struct cmd_desc * cmd)449 static void do_fadvise(int argc, char **argv, const struct cmd_desc *cmd)
450 {
451 	int fd, advice;
452 	off_t offset, length;
453 
454 	if (argc != 5) {
455 		fputs("Excess arguments\n\n", stderr);
456 		fputs(cmd->cmd_help, stderr);
457 		exit(1);
458 	}
459 
460 	fd = xopen(argv[4], O_RDWR, 0);
461 
462 	if (!strcmp(argv[1], "willneed")) {
463 		advice = POSIX_FADV_WILLNEED;
464 	} else if (!strcmp(argv[1], "dontneed")) {
465 		advice = POSIX_FADV_DONTNEED;
466 	} else if (!strcmp(argv[1], "noreuse")) {
467 		advice = POSIX_FADV_NOREUSE;
468 	} else if (!strcmp(argv[1], "sequential")) {
469 		advice = POSIX_FADV_SEQUENTIAL;
470 	} else if (!strcmp(argv[1], "random")) {
471 		advice = POSIX_FADV_RANDOM;
472 	} else {
473 		fputs("Wrong advice\n\n", stderr);
474 		fputs(cmd->cmd_help, stderr);
475 		exit(1);
476 	}
477 
478 	offset = atoi(argv[2]);
479 	length = atoll(argv[3]);
480 
481 	if (posix_fadvise(fd, offset, length, advice) != 0)
482 		die_errno("fadvise failed");
483 
484 	printf("fadvice %s to a file: %s\n", argv[1], argv[4]);
485 	exit(0);
486 }
487 
488 #define ioprio_desc "ioprio"
489 #define ioprio_help						\
490 "f2fs_io ioprio [hint] [file]\n\n"				\
491 "ioprio given the file\n"					\
492 "hint can be\n"							\
493 " ioprio_write\n"						\
494 
do_ioprio(int argc,char ** argv,const struct cmd_desc * cmd)495 static void do_ioprio(int argc, char **argv, const struct cmd_desc *cmd)
496 {
497 	int fd, hint;
498 
499 	if (argc != 3) {
500 		fputs("Excess arguments\n\n", stderr);
501 		fputs(cmd->cmd_help, stderr);
502 		exit(1);
503 	}
504 
505 	fd = xopen(argv[2], O_RDWR, 0);
506 
507 	if (!strcmp(argv[1], "ioprio_write")) {
508 		hint = F2FS_IOPRIO_WRITE;
509 	} else {
510 		fputs("Not supported hint\n\n", stderr);
511 		fputs(cmd->cmd_help, stderr);
512 		exit(1);
513 	}
514 
515 	if (ioctl(fd, F2FS_IOC_IO_PRIO, &hint) != 0)
516 		die_errno("ioprio failed");
517 
518 	printf("ioprio_hint %d to a file: %s\n", hint, argv[2]);
519 	exit(0);
520 }
521 
522 #define pinfile_desc "pin file control"
523 #define pinfile_help						\
524 "f2fs_io pinfile [get|set|unset] [file] {size}\n\n"		\
525 "get/set/unset pinning given the file\n"			\
526 "{size} is fallocate length and optional only for set operations\n"
527 
do_pinfile(int argc,char ** argv,const struct cmd_desc * cmd)528 static void do_pinfile(int argc, char **argv, const struct cmd_desc *cmd)
529 {
530 	u32 pin;
531 	int ret, fd;
532 
533 	if (argc < 3 || argc > 4) {
534 		fputs("Excess arguments\n\n", stderr);
535 		fputs(cmd->cmd_help, stderr);
536 		exit(1);
537 	}
538 
539 	fd = xopen(argv[2], O_RDWR, 0);
540 
541 	ret = -1;
542 	if (!strcmp(argv[1], "set")) {
543 		pin = 1;
544 		ret = ioctl(fd, F2FS_IOC_SET_PIN_FILE, &pin);
545 		if (ret != 0)
546 			die_errno("F2FS_IOC_SET_PIN_FILE failed");
547 		if (argc != 4) {
548 			printf("%s pinfile: %u blocks moved in %s\n",
549 						argv[1], ret, argv[2]);
550 			exit(0);
551 		}
552 
553 		struct stat st;
554 		if (fallocate(fd, 0, 0, atoll(argv[3])) != 0)
555 			die_errno("fallocate failed");
556 		if (fstat(fd, &st) != 0)
557 			die_errno("fstat failed");
558 		printf("%s pinfile: %u blocks moved and fallocate %"PRIu64" bytes in %s\n",
559 					argv[1], ret, st.st_size, argv[2]);
560 	} else if (!strcmp(argv[1], "unset")) {
561 		pin = 0;
562 		ret = ioctl(fd, F2FS_IOC_SET_PIN_FILE, &pin);
563 		if (ret != 0)
564 			die_errno("F2FS_IOC_SET_PIN_FILE failed");
565 		printf("%s pinfile in %s\n", argv[1], argv[2]);
566 	} else if (!strcmp(argv[1], "get")) {
567 		unsigned int flags;
568 
569 		ret = ioctl(fd, F2FS_IOC_GET_PIN_FILE, &pin);
570 		if (ret < 0)
571 			die_errno("F2FS_IOC_GET_PIN_FILE failed");
572 
573 		ret = ioctl(fd, F2FS_IOC_GETFLAGS, &flags);
574 		if (ret < 0)
575 			die_errno("F2FS_IOC_GETFLAGS failed");
576 
577 		printf("get_pin_file: %s with %u blocks moved in %s\n",
578 				(flags & F2FS_NOCOW_FL) ? "pinned" : "un-pinned",
579 				pin, argv[2]);
580 	}
581 	exit(0);
582 }
583 
584 #define fallocate_desc "fallocate"
585 #define fallocate_help						\
586 "f2fs_io fallocate [-c] [-i] [-p] [-z] [keep_size] [offset] [length] [file]\n\n"	\
587 "fallocate given the file\n"					\
588 " [keep_size] : 1 or 0\n"					\
589 " -c : collapse range\n"					\
590 " -i : insert range\n"						\
591 " -p : punch hole\n"						\
592 " -z : zero range\n"						\
593 
do_fallocate(int argc,char ** argv,const struct cmd_desc * cmd)594 static void do_fallocate(int argc, char **argv, const struct cmd_desc *cmd)
595 {
596 	int fd;
597 	off_t offset, length;
598 	struct stat sb;
599 	int mode = 0;
600 	int c;
601 
602 	while ((c = getopt(argc, argv, "cipz")) != -1) {
603 		switch (c) {
604 		case 'c':
605 			mode |= FALLOC_FL_COLLAPSE_RANGE;
606 			break;
607 		case 'i':
608 			mode |= FALLOC_FL_INSERT_RANGE;
609 			break;
610 		case 'p':
611 			mode |= FALLOC_FL_PUNCH_HOLE;
612 			break;
613 		case 'z':
614 			mode |= FALLOC_FL_ZERO_RANGE;
615 			break;
616 		default:
617 			fputs(cmd->cmd_help, stderr);
618 			exit(2);
619 		}
620 	}
621 	argc -= optind;
622 	argv += optind;
623 
624 	if (argc != 4) {
625 		fputs("Excess arguments\n\n", stderr);
626 		fputs(cmd->cmd_help, stderr);
627 		exit(1);
628 	}
629 
630 	if (!strcmp(argv[0], "1"))
631 		mode |= FALLOC_FL_KEEP_SIZE;
632 
633 	offset = atoll(argv[1]);
634 	length = atoll(argv[2]);
635 
636 	fd = xopen(argv[3], O_RDWR, 0);
637 
638 	if (fallocate(fd, mode, offset, length) != 0)
639 		die_errno("fallocate failed");
640 
641 	if (fstat(fd, &sb) != 0)
642 		die_errno("fstat failed");
643 
644 	printf("fallocated a file: i_size=%"PRIu64", i_blocks=%"PRIu64"\n", sb.st_size, sb.st_blocks);
645 	exit(0);
646 }
647 
648 #define erase_desc "erase a block device"
649 #define erase_help				\
650 "f2fs_io erase [block_device_path]\n\n"		\
651 "Send DISCARD | BLKSECDISCARD comamnd to"	\
652 "block device in block_device_path\n"		\
653 
do_erase(int argc,char ** argv,const struct cmd_desc * cmd)654 static void do_erase(int argc, char **argv, const struct cmd_desc *cmd)
655 {
656 	int fd, ret;
657 	struct stat st;
658 	u64 range[2];
659 
660 	if (argc != 2) {
661 		fputs("Excess arguments\n\n", stderr);
662 		fputs(cmd->cmd_help, stderr);
663 		exit(1);
664 	}
665 
666 	if (stat(argv[1], &st) != 0) {
667 		fputs("stat error\n", stderr);
668 		exit(1);
669 	}
670 
671 	if (!S_ISBLK(st.st_mode)) {
672 		fputs(argv[1], stderr);
673 		fputs(" is not a block device\n", stderr);
674 		exit(1);
675 	}
676 
677 	fd = xopen(argv[1], O_WRONLY, 0);
678 
679 	range[0] = 0;
680 	ret = ioctl(fd, BLKGETSIZE64, &range[1]);
681 	if (ret < 0) {
682 		fputs("get size failed\n", stderr);
683 		exit(1);
684 	}
685 
686 	ret = ioctl(fd, BLKSECDISCARD, &range);
687 	if (ret < 0) {
688 		ret = ioctl(fd, BLKDISCARD, &range);
689 		if (ret < 0) {
690 			fputs("Discard failed\n", stderr);
691 			exit(1);
692 		}
693 	}
694 
695 	exit(0);
696 }
697 
do_write_with_advice(int argc,char ** argv,const struct cmd_desc * cmd,bool with_advice)698 static void do_write_with_advice(int argc, char **argv,
699 			const struct cmd_desc *cmd, bool with_advice)
700 {
701 	u64 buf_size = 0, inc_num = 0, written = 0;
702 	u64 offset;
703 	char *buf = NULL;
704 	unsigned bs, count, i;
705 	int flags = 0;
706 	int fd;
707 	u64 total_time = 0, max_time = 0, max_time_t = 0;
708 	bool atomic_commit = false, atomic_abort = false, replace = false;
709 	int useconds = 0;
710 
711 	srand(time(0));
712 
713 	bs = atoi(argv[1]);
714 	if (bs > 1024)
715 		die("Too big chunk size - limit: 4MB");
716 
717 	buf_size = bs * F2FS_DEFAULT_BLKSIZE;
718 
719 	offset = atoi(argv[2]) * buf_size;
720 
721 	buf = aligned_xalloc(F2FS_DEFAULT_BLKSIZE, buf_size);
722 	count = atoi(argv[3]);
723 
724 	if (!strcmp(argv[4], "zero"))
725 		memset(buf, 0, buf_size);
726 	else if (strcmp(argv[4], "inc_num") && strcmp(argv[4], "rand"))
727 		die("Wrong pattern type");
728 
729 	if (!strcmp(argv[5], "dio")) {
730 		flags |= O_DIRECT;
731 	} else if (!strcmp(argv[5], "dsync")) {
732 		flags |= O_DIRECT | O_DSYNC;
733 	} else if (!strcmp(argv[5], "osync")) {
734 		flags |= O_SYNC;
735 	} else if (!strcmp(argv[5], "atomic_commit")) {
736 		atomic_commit = true;
737 	} else if (!strcmp(argv[5], "atomic_abort")) {
738 		atomic_abort = true;
739 	} else if (!strcmp(argv[5], "atomic_rcommit")) {
740 		atomic_commit = true;
741 		replace = true;
742 	} else if (!strcmp(argv[5], "atomic_rabort")) {
743 		atomic_abort = true;
744 		replace = true;
745 	} else if (strcmp(argv[5], "buffered")) {
746 		die("Wrong IO type");
747 	}
748 
749 	if (!with_advice) {
750 		fd = xopen(argv[6], O_CREAT | O_WRONLY | flags, 0755);
751 	} else {
752 		unsigned char advice;
753 		int ret;
754 
755 		if (!strcmp(argv[6], "hot"))
756 			advice = FADVISE_HOT_BIT;
757 		else if (!strcmp(argv[6], "cold"))
758 			advice = FADVISE_COLD_BIT;
759 		else
760 			die("Wrong Advise type");
761 
762 		fd = xopen(argv[7], O_CREAT | O_WRONLY | flags, 0755);
763 
764 		ret = fsetxattr(fd, F2FS_SYSTEM_ADVISE_NAME,
765 				    (char *)&advice, 1, XATTR_CREATE);
766 		if (ret) {
767 			fputs("fsetxattr advice failed\n", stderr);
768 			exit(1);
769 		}
770 	}
771 
772 	if (atomic_commit || atomic_abort) {
773 		int ret;
774 
775 		if (argc == 8)
776 			useconds = atoi(argv[7]) * 1000;
777 
778 		if (replace)
779 			ret = ioctl(fd, F2FS_IOC_START_ATOMIC_REPLACE);
780 		else
781 			ret = ioctl(fd, F2FS_IOC_START_ATOMIC_WRITE);
782 
783 		if (ret < 0) {
784 			fputs("setting atomic file mode failed\n", stderr);
785 			exit(1);
786 		}
787 	}
788 
789 	total_time = get_current_us();
790 	for (i = 0; i < count; i++) {
791 		uint64_t ret;
792 
793 		if (!strcmp(argv[4], "inc_num"))
794 			*(int *)buf = inc_num++;
795 		else if (!strcmp(argv[4], "rand"))
796 			*(int *)buf = rand();
797 
798 		/* write data */
799 		max_time_t = get_current_us();
800 		ret = pwrite(fd, buf, buf_size, offset + buf_size * i);
801 		max_time_t = get_current_us() - max_time_t;
802 		if (max_time < max_time_t)
803 			max_time = max_time_t;
804 		if (ret != buf_size)
805 			break;
806 		written += ret;
807 	}
808 
809 	if (useconds)
810 		usleep(useconds);
811 
812 	if (atomic_commit) {
813 		int ret;
814 
815 		ret = ioctl(fd, F2FS_IOC_COMMIT_ATOMIC_WRITE);
816 		if (ret < 0) {
817 			fputs("committing atomic write failed\n", stderr);
818 			exit(1);
819 		}
820 	} else if (atomic_abort) {
821 		int ret;
822 
823 		ret = ioctl(fd, F2FS_IOC_ABORT_VOLATILE_WRITE);
824 		if (ret < 0) {
825 			fputs("aborting atomic write failed\n", stderr);
826 			exit(1);
827 		}
828 	}
829 
830 	printf("Written %"PRIu64" bytes with pattern=%s, total_time=%"PRIu64" us, max_latency=%"PRIu64" us\n",
831 				written, argv[4],
832 				get_current_us() - total_time,
833 				max_time);
834 	exit(0);
835 }
836 
837 #define write_desc "write data into file"
838 #define write_help					\
839 "f2fs_io write [chunk_size in 4kb] [offset in chunk_size] [count] [pattern] [IO] [file_path] {delay}\n\n"	\
840 "Write given patten data in file_path\n"		\
841 "pattern can be\n"					\
842 "  zero          : zeros\n"				\
843 "  inc_num       : incrementing numbers\n"		\
844 "  rand          : random numbers\n"			\
845 "IO can be\n"						\
846 "  buffered      : buffered IO\n"			\
847 "  dio           : O_DIRECT\n"				\
848 "  dsync         : O_DIRECT | O_DSYNC\n"		\
849 "  osync         : O_SYNC\n"				\
850 "  atomic_commit : atomic write & commit\n"		\
851 "  atomic_abort  : atomic write & abort\n"		\
852 "  atomic_rcommit: atomic replace & commit\n"		\
853 "  atomic_rabort : atomic replace & abort\n"		\
854 "{delay} is in ms unit and optional only for atomic operations\n"
855 
do_write(int argc,char ** argv,const struct cmd_desc * cmd)856 static void do_write(int argc, char **argv, const struct cmd_desc *cmd)
857 {
858 	if (argc < 7 || argc > 8) {
859 		fputs("Excess arguments\n\n", stderr);
860 		fputs(cmd->cmd_help, stderr);
861 		exit(1);
862 	}
863 	do_write_with_advice(argc, argv, cmd, false);
864 }
865 
866 #define write_advice_desc "write data into file with a hint"
867 #define write_advice_help					\
868 "f2fs_io write_advice [chunk_size in 4kb] [offset in chunk_size] [count] [pattern] [IO] [advise] [file_path] {delay}\n\n"	\
869 "Write given patten data in file_path\n"		\
870 "pattern can be\n"					\
871 "  zero          : zeros\n"				\
872 "  inc_num       : incrementing numbers\n"		\
873 "  rand          : random numbers\n"			\
874 "IO can be\n"						\
875 "  buffered      : buffered IO\n"			\
876 "  dio           : O_DIRECT\n"				\
877 "  dsync         : O_DIRECT | O_DSYNC\n"		\
878 "  osync         : O_SYNC\n"				\
879 "  atomic_commit : atomic write & commit\n"		\
880 "  atomic_abort  : atomic write & abort\n"		\
881 "  atomic_rcommit: atomic replace & commit\n"		\
882 "  atomic_rabort : atomic replace & abort\n"		\
883 "advise can be\n"					\
884 "  cold : indicate a cold file\n"			\
885 "  hot  : indicate a hot file\n"			\
886 "{delay} is in ms unit and optional only for atomic operations\n"
887 
do_write_advice(int argc,char ** argv,const struct cmd_desc * cmd)888 static void do_write_advice(int argc, char **argv, const struct cmd_desc *cmd)
889 {
890 	if (argc < 8 || argc > 9) {
891 		fputs("Excess arguments\n\n", stderr);
892 		fputs(cmd->cmd_help, stderr);
893 		exit(1);
894 	}
895 	do_write_with_advice(argc, argv, cmd, true);
896 }
897 
898 #define read_desc "read data from file"
899 #define read_help					\
900 "f2fs_io read [chunk_size in 4kb] [offset in chunk_size] [count] [IO] [advice] [print_nbytes] [file_path]\n\n"	\
901 "Read data in file_path and print nbytes\n"		\
902 "IO can be\n"						\
903 "  buffered : buffered IO\n"				\
904 "  dio      : direct IO\n"				\
905 "  mmap     : mmap IO\n"				\
906 "advice can be\n"					\
907 " 1 : set sequential|willneed\n"			\
908 " 0 : none\n"						\
909 
do_read(int argc,char ** argv,const struct cmd_desc * cmd)910 static void do_read(int argc, char **argv, const struct cmd_desc *cmd)
911 {
912 	u64 buf_size = 0, ret = 0, read_cnt = 0;
913 	u64 offset;
914 	char *buf = NULL;
915 	char *data;
916 	char *print_buf = NULL;
917 	unsigned bs, count, i, print_bytes;
918 	u64 total_time = 0;
919 	int flags = 0;
920 	int do_mmap = 0;
921 	int fd, advice;
922 
923 	if (argc != 8) {
924 		fputs("Excess arguments\n\n", stderr);
925 		fputs(cmd->cmd_help, stderr);
926 		exit(1);
927 	}
928 
929 	bs = atoi(argv[1]);
930 	if (bs > 256 * 1024)
931 		die("Too big chunk size - limit: 1GB");
932 	buf_size = bs * F2FS_DEFAULT_BLKSIZE;
933 
934 	offset = atoi(argv[2]) * buf_size;
935 
936 	buf = aligned_xalloc(F2FS_DEFAULT_BLKSIZE, buf_size);
937 
938 	count = atoi(argv[3]);
939 	if (!strcmp(argv[4], "dio"))
940 		flags |= O_DIRECT;
941 	else if (!strcmp(argv[4], "mmap"))
942 		do_mmap = 1;
943 	else if (strcmp(argv[4], "buffered"))
944 		die("Wrong IO type");
945 
946 	print_bytes = atoi(argv[6]);
947 	if (print_bytes > buf_size)
948 		die("Print_nbytes should be less then chunk_size in kb");
949 
950 	print_buf = xmalloc(print_bytes);
951 
952 	fd = xopen(argv[7], O_RDONLY | flags, 0);
953 
954 	advice = atoi(argv[5]);
955 	if (advice) {
956 		if (posix_fadvise(fd, 0, F2FS_DEFAULT_BLKSIZE,
957 				POSIX_FADV_SEQUENTIAL) != 0)
958 			die_errno("fadvise failed");
959 		if (posix_fadvise(fd, 0, F2FS_DEFAULT_BLKSIZE,
960 				POSIX_FADV_WILLNEED) != 0)
961 			die_errno("fadvise failed");
962 		printf("fadvise SEQUENTIAL|WILLNEED to a file: %s\n", argv[7]);
963 	}
964 
965 	total_time = get_current_us();
966 	if (do_mmap) {
967 		data = mmap(NULL, count * buf_size, PROT_READ,
968 						MAP_SHARED | MAP_POPULATE, fd, offset);
969 		if (data == MAP_FAILED)
970 			die("Mmap failed");
971 	}
972 	if (!do_mmap) {
973 		for (i = 0; i < count; i++) {
974 			ret = pread(fd, buf, buf_size, offset + buf_size * i);
975 			if (ret != buf_size) {
976 				printf("pread expected: %"PRIu64", readed: %"PRIu64"\n",
977 						buf_size, ret);
978 				if (ret > 0) {
979 					read_cnt += ret;
980 					memcpy(print_buf, buf, print_bytes);
981 				}
982 				break;
983 			}
984 
985 			read_cnt += ret;
986 			if (i == 0)
987 				memcpy(print_buf, buf, print_bytes);
988 		}
989 	} else {
990 		read_cnt = count * buf_size;
991 		memcpy(print_buf, data, print_bytes);
992 	}
993 	printf("Read %"PRIu64" bytes total_time = %"PRIu64" us, BW = %.Lf MB/s print %u bytes:\n",
994 		read_cnt, get_current_us() - total_time,
995 		((long double)read_cnt / (get_current_us() - total_time)), print_bytes);
996 	printf("%08"PRIx64" : ", offset);
997 	for (i = 1; i <= print_bytes; i++) {
998 		printf("%02x", print_buf[i - 1]);
999 		if (i % 16 == 0)
1000 			printf("\n%08"PRIx64" : ", offset + 16 * i);
1001 		else if (i % 2 == 0)
1002 			printf(" ");
1003 	}
1004 	printf("\n");
1005 	exit(0);
1006 }
1007 
1008 #define fragread_desc "read data with a fragmented buffer from file"
1009 #define fragread_help					\
1010 "f2fs_io fragread [chunk_size in 4kb] [offset in chunk_size] [count] [advice] [file_path]\n\n"	\
1011 "Read data in file_path and print nbytes\n"		\
1012 "advice can be\n"					\
1013 " 1 : set sequential|willneed\n"			\
1014 " 0 : none\n"						\
1015 
1016 #ifndef PAGE_SIZE
1017 #define PAGE_SIZE sysconf(_SC_PAGESIZE)
1018 #endif
1019 #define ALLOC_SIZE (2 * 1024 * 1024 - 4 * 1024) // 2MB - 4KB
1020 
do_fragread(int argc,char ** argv,const struct cmd_desc * cmd)1021 static void do_fragread(int argc, char **argv, const struct cmd_desc *cmd)
1022 {
1023 	u64 buf_size = 0, ret = 0, read_cnt = 0;
1024 	u64 offset;
1025 	char *buf = NULL;
1026 	uintptr_t idx, ptr;
1027 	unsigned bs, count, i;
1028 	u64 total_time = 0;
1029 	int flags = 0, alloc_count = 0;
1030 	void *mem_hole, **mem_holes;
1031 	int fd, advice;
1032 
1033 	if (argc != 6) {
1034 		fputs("Excess arguments\n\n", stderr);
1035 		fputs(cmd->cmd_help, stderr);
1036 		exit(1);
1037 	}
1038 
1039 	bs = atoi(argv[1]);
1040 	if (bs > 256 * 1024)
1041 		die("Too big chunk size - limit: 1GB");
1042 	buf_size = bs * F2FS_DEFAULT_BLKSIZE;
1043 
1044 	offset = atoi(argv[2]) * buf_size;
1045 	count = atoi(argv[3]);
1046 	advice = atoi(argv[4]);
1047 	mem_holes = xmalloc(sizeof(void *) * (buf_size / PAGE_SIZE));
1048 
1049 	/* 1. Allocate the buffer using mmap. */
1050 	buf = mmap(NULL, buf_size, PROT_READ | PROT_WRITE,
1051 				MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
1052 
1053 	/* 2. Loop and touch each page. */
1054 	for (idx = (uintptr_t)buf; idx < (uintptr_t)buf + buf_size;
1055 						idx += PAGE_SIZE)
1056 	{
1057 		/* Touch the current page. */
1058 		volatile char *page = (volatile char *)idx;
1059 		*page;
1060 
1061 		/* 3. Allocate (2M - 4K) memory using mmap and touch all of it. */
1062 		mem_hole = mmap(NULL, ALLOC_SIZE, PROT_READ | PROT_WRITE,
1063 					MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
1064 		if (mem_hole == MAP_FAILED)
1065 			die_errno("map failed");
1066 
1067 		/* Store the allocated memory pointer. */
1068 		mem_holes[alloc_count++] = mem_hole;
1069 
1070 		/* Touch all allocated memory. */
1071 		for (ptr = (uintptr_t)mem_hole;
1072 			ptr < (uintptr_t)mem_hole + ALLOC_SIZE;
1073 						ptr += PAGE_SIZE) {
1074 			volatile char *alloc_page = (volatile char *)ptr;
1075 			*alloc_page;
1076 		}
1077 	}
1078 	printf("Touched allocated memory: count = %u\n", alloc_count);
1079 	printf(" - allocated memory: = ");
1080 	for (idx = 0; idx < 5; idx++)
1081 		printf(" %p", mem_holes[idx]);
1082 	printf("\n");
1083 
1084 	/* Pin the pages. */
1085 	if (mlock(buf, buf_size))
1086 		die_errno("mlock failed");
1087 
1088 	fd = xopen(argv[5], O_RDONLY | flags, 0);
1089 
1090 	if (advice) {
1091 		if (posix_fadvise(fd, 0, F2FS_DEFAULT_BLKSIZE,
1092 				POSIX_FADV_SEQUENTIAL) != 0)
1093 			die_errno("fadvise failed");
1094 		if (posix_fadvise(fd, 0, F2FS_DEFAULT_BLKSIZE,
1095 				POSIX_FADV_WILLNEED) != 0)
1096 			die_errno("fadvise failed");
1097 		printf("fadvise SEQUENTIAL|WILLNEED to a file: %s\n", argv[5]);
1098 	}
1099 
1100 	total_time = get_current_us();
1101 
1102 	for (i = 0; i < count; i++) {
1103 		ret = pread(fd, buf, buf_size, offset + buf_size * i);
1104 		if (ret != buf_size) {
1105 			printf("pread expected: %"PRIu64", readed: %"PRIu64"\n",
1106 					buf_size, ret);
1107 			if (ret > 0)
1108 				read_cnt += ret;
1109 			break;
1110 		}
1111 
1112 		read_cnt += ret;
1113 	}
1114 	printf("Fragmented_Read %"PRIu64" bytes total_time = %"PRIu64" us, BW = %.Lf MB/s\n",
1115 		read_cnt, get_current_us() - total_time,
1116 		((long double)read_cnt / (get_current_us() - total_time)));
1117 	printf("\n");
1118 	exit(0);
1119 }
1120 
1121 #define randread_desc "random read data from file"
1122 #define randread_help					\
1123 "f2fs_io randread [chunk_size in 4kb] [count] [IO] [advise] [file_path]\n\n"	\
1124 "Do random read data in file_path\n"		\
1125 "IO can be\n"						\
1126 "  buffered : buffered IO\n"				\
1127 "  dio      : direct IO\n"				\
1128 "  mmap     : mmap IO\n"				\
1129 "advice can be\n"					\
1130 " 1 : set random|willneed\n"				\
1131 " 0 : none\n"						\
1132 
do_randread(int argc,char ** argv,const struct cmd_desc * cmd)1133 static void do_randread(int argc, char **argv, const struct cmd_desc *cmd)
1134 {
1135 	u64 buf_size = 0, ret = 0, read_cnt = 0;
1136 	u64 idx, end_idx, aligned_size;
1137 	char *buf = NULL;
1138 	char *data;
1139 	unsigned bs, count, i, j;
1140 	u64 total_time = 0, elapsed_time = 0;
1141 	int flags = 0;
1142 	int do_mmap = 0;
1143 	int fd, advice;
1144 	time_t t;
1145 	struct stat stbuf;
1146 
1147 	if (argc != 6) {
1148 		fputs("Excess arguments\n\n", stderr);
1149 		fputs(cmd->cmd_help, stderr);
1150 		exit(1);
1151 	}
1152 
1153 	bs = atoi(argv[1]);
1154 	if (bs > 1024)
1155 		die("Too big chunk size - limit: 4MB");
1156 	buf_size = bs * F2FS_DEFAULT_BLKSIZE;
1157 
1158 	buf = aligned_xalloc(F2FS_DEFAULT_BLKSIZE, buf_size);
1159 
1160 	count = atoi(argv[2]);
1161 	if (!strcmp(argv[3], "dio"))
1162 		flags |= O_DIRECT;
1163 	else if (!strcmp(argv[3], "mmap"))
1164 		do_mmap = 1;
1165 	else if (strcmp(argv[3], "buffered"))
1166 		die("Wrong IO type");
1167 
1168 	fd = xopen(argv[5], O_RDONLY | flags, 0);
1169 
1170 	advice = atoi(argv[4]);
1171 	if (advice) {
1172 		if (posix_fadvise(fd, 0, stbuf.st_size, POSIX_FADV_RANDOM) != 0)
1173 			die_errno("fadvise failed");
1174 		if (posix_fadvise(fd, 0, 4096, POSIX_FADV_WILLNEED) != 0)
1175 			die_errno("fadvise failed");
1176 		printf("fadvise RANDOM|WILLNEED to a file: %s\n", argv[5]);
1177 	}
1178 
1179 	if (fstat(fd, &stbuf) != 0)
1180 		die_errno("fstat of source file failed");
1181 
1182 	aligned_size = (u64)stbuf.st_size & ~((u64)(F2FS_DEFAULT_BLKSIZE - 1));
1183 	if (aligned_size < buf_size)
1184 		die("File is too small to random read");
1185 	end_idx = (u64)(aligned_size - buf_size) / (u64)F2FS_DEFAULT_BLKSIZE + 1;
1186 
1187 	if (do_mmap) {
1188 		data = mmap(NULL, stbuf.st_size, PROT_READ, MAP_SHARED, fd, 0);
1189 		if (data == MAP_FAILED)
1190 			die("Mmap failed");
1191 		if (madvise((void *)data, stbuf.st_size, MADV_RANDOM) != 0)
1192 			die_errno("madvise failed");
1193 	}
1194 
1195 	srand((unsigned) time(&t));
1196 
1197 	total_time = get_current_us();
1198 
1199 	for (i = 0; i < count; i++) {
1200 		idx = rand() % end_idx;
1201 
1202 		if (!do_mmap) {
1203 			ret = pread(fd, buf, buf_size, 4096 * idx);
1204 			if (ret != buf_size)
1205 				break;
1206 		} else {
1207 			for (j = 0; j < bs; j++)
1208 				*buf = data[4096 * (idx + j)];
1209 		}
1210 		read_cnt += buf_size;
1211 	}
1212 	elapsed_time = get_current_us() - total_time;
1213 
1214 	printf("Read %"PRIu64" bytes total_time = %"PRIu64" us, avg. latency = %.Lf us, IOPs= %.Lf, BW = %.Lf MB/s\n",
1215 		read_cnt, elapsed_time,
1216 		(long double)elapsed_time / count,
1217 		(long double)count * 1000 * 1000 / elapsed_time,
1218 		(long double)read_cnt / elapsed_time);
1219 	exit(0);
1220 }
1221 
1222 #define fiemap_desc "get block address in file"
1223 #define fiemap_help					\
1224 "f2fs_io fiemap [offset in 4kb] [count in 4kb] [file_path]\n\n"\
1225 
1226 #if defined(HAVE_LINUX_FIEMAP_H) && defined(HAVE_LINUX_FS_H)
do_fiemap(int argc,char ** argv,const struct cmd_desc * cmd)1227 static void do_fiemap(int argc, char **argv, const struct cmd_desc *cmd)
1228 {
1229 	unsigned int i;
1230 	int fd, extents_mem_size;
1231 	u64 start, length;
1232 	u32 mapped_extents;
1233 	struct fiemap *fm = xmalloc(sizeof(struct fiemap));
1234 
1235 	if (argc != 4) {
1236 		fputs("Excess arguments\n\n", stderr);
1237 		fputs(cmd->cmd_help, stderr);
1238 		exit(1);
1239 	}
1240 
1241 	memset(fm, 0, sizeof(struct fiemap));
1242 	start = (u64)atoi(argv[1]) * F2FS_DEFAULT_BLKSIZE;
1243 	length = (u64)atoi(argv[2]) * F2FS_DEFAULT_BLKSIZE;
1244 	fm->fm_start = start;
1245 	fm->fm_length = length;
1246 
1247 	fd = xopen(argv[3], O_RDONLY | O_LARGEFILE, 0);
1248 
1249 	printf("Fiemap: offset = %"PRIu64" len = %"PRIu64"\n",
1250 				start / F2FS_DEFAULT_BLKSIZE,
1251 				length / F2FS_DEFAULT_BLKSIZE);
1252 	if (ioctl(fd, FS_IOC_FIEMAP, fm) < 0)
1253 		die_errno("FIEMAP failed");
1254 
1255 	mapped_extents = fm->fm_mapped_extents;
1256 	extents_mem_size = sizeof(struct fiemap_extent) * mapped_extents;
1257 	free(fm);
1258 	fm = xmalloc(sizeof(struct fiemap) + extents_mem_size);
1259 
1260 	memset(fm, 0, sizeof(struct fiemap) + extents_mem_size);
1261 	fm->fm_start = start;
1262 	fm->fm_length = length;
1263 	fm->fm_extent_count = mapped_extents;
1264 
1265 	if (ioctl(fd, FS_IOC_FIEMAP, fm) < 0)
1266 		die_errno("FIEMAP failed");
1267 
1268 	printf("\t%-17s%-17s%-17s%s\n", "logical addr.", "physical addr.", "length", "flags");
1269 	for (i = 0; i < fm->fm_mapped_extents; i++) {
1270 		printf("%d\t%.16llx %.16llx %.16llx %.8x\n", i,
1271 		    fm->fm_extents[i].fe_logical, fm->fm_extents[i].fe_physical,
1272 		    fm->fm_extents[i].fe_length, fm->fm_extents[i].fe_flags);
1273 
1274 		if (fm->fm_extents[i].fe_flags & FIEMAP_EXTENT_LAST)
1275 			break;
1276 	}
1277 	printf("\n");
1278 	free(fm);
1279 	exit(0);
1280 }
1281 #else
do_fiemap(int UNUSED (argc),char ** UNUSED (argv),const struct cmd_desc * UNUSED (cmd))1282 static void do_fiemap(int UNUSED(argc), char **UNUSED(argv),
1283 			const struct cmd_desc *UNUSED(cmd))
1284 {
1285 	die("Not support for this platform");
1286 }
1287 #endif
1288 
1289 #define gc_urgent_desc "start/end/run gc_urgent for given time period"
1290 #define gc_urgent_help					\
1291 "f2fs_io gc_urgent $dev [start/end/run] [time in sec]\n\n"\
1292 " - f2fs_io gc_urgent sda21 start\n"		\
1293 " - f2fs_io gc_urgent sda21 end\n"		\
1294 " - f2fs_io gc_urgent sda21 run 10\n"		\
1295 
do_gc_urgent(int argc,char ** argv,const struct cmd_desc * cmd)1296 static void do_gc_urgent(int argc, char **argv, const struct cmd_desc *cmd)
1297 {
1298 	char command[255];
1299 
1300 	if (argc == 3 && !strcmp(argv[2], "start")) {
1301 		printf("gc_urgent: start on %s\n", argv[1]);
1302 		sprintf(command, "echo %d > %s/%s/gc_urgent", 1, "/sys/fs/f2fs/", argv[1]);
1303 		if (system(command))
1304 			exit(1);
1305 	} else if (argc == 3 && !strcmp(argv[2], "end")) {
1306 		printf("gc_urgent: end on %s\n", argv[1]);
1307 		sprintf(command, "echo %d > %s/%s/gc_urgent", 0, "/sys/fs/f2fs/", argv[1]);
1308 		if (system(command))
1309 			exit(1);
1310 	} else if (argc == 4 && !strcmp(argv[2], "run")) {
1311 		printf("gc_urgent: start on %s for %d secs\n", argv[1], atoi(argv[3]));
1312 		sprintf(command, "echo %d > %s/%s/gc_urgent", 1, "/sys/fs/f2fs/", argv[1]);
1313 		if (system(command))
1314 			exit(1);
1315 		sleep(atoi(argv[3]));
1316 		printf("gc_urgent: end on %s for %d secs\n", argv[1], atoi(argv[3]));
1317 		sprintf(command, "echo %d > %s/%s/gc_urgent", 0, "/sys/fs/f2fs/", argv[1]);
1318 		if (system(command))
1319 			exit(1);
1320 	} else {
1321 		fputs("Excess arguments\n\n", stderr);
1322 		fputs(cmd->cmd_help, stderr);
1323 		exit(1);
1324 	}
1325 }
1326 
1327 #define defrag_file_desc "do defragment on file"
1328 #define defrag_file_help						\
1329 "f2fs_io defrag_file [start] [length] [file_path]\n\n"		\
1330 "  start     : start offset of defragment region, unit: bytes\n"	\
1331 "  length    : bytes number of defragment region\n"			\
1332 
do_defrag_file(int argc,char ** argv,const struct cmd_desc * cmd)1333 static void do_defrag_file(int argc, char **argv, const struct cmd_desc *cmd)
1334 {
1335 	struct f2fs_defragment df;
1336 	u64 len;
1337 	int ret, fd;
1338 
1339 	if (argc != 4) {
1340 		fputs("Excess arguments\n\n", stderr);
1341 		fputs(cmd->cmd_help, stderr);
1342 		exit(1);
1343 	}
1344 
1345 	df.start = atoll(argv[1]);
1346 	df.len = len = atoll(argv[2]);
1347 
1348 	fd = xopen(argv[3], O_RDWR, 0);
1349 
1350 	ret = ioctl(fd, F2FS_IOC_DEFRAGMENT, &df);
1351 	if (ret < 0)
1352 		die_errno("F2FS_IOC_DEFRAGMENT failed");
1353 
1354 	printf("defrag %s in region[%"PRIu64", %"PRIu64"]\n",
1355 			argv[3], df.start, df.start + len);
1356 	exit(0);
1357 }
1358 
1359 #define copy_desc "copy a file"
1360 #define copy_help							\
1361 "f2fs_io copy [-d] [-m] [-s] src_path dst_path\n\n"			\
1362 "  src_path  : path to source file\n"					\
1363 "  dst_path  : path to destination file\n"				\
1364 "  -d        : use direct I/O\n"					\
1365 "  -m        : mmap the source file\n"					\
1366 "  -s        : use sendfile\n"						\
1367 
do_copy(int argc,char ** argv,const struct cmd_desc * cmd)1368 static void do_copy(int argc, char **argv, const struct cmd_desc *cmd)
1369 {
1370 	int c;
1371 	int src_fd;
1372 	int dst_fd;
1373 	int open_flags = 0;
1374 	bool mmap_source_file = false;
1375 	bool use_sendfile = false;
1376 	ssize_t ret;
1377 
1378 	while ((c = getopt(argc, argv, "dms")) != -1) {
1379 		switch (c) {
1380 		case 'd':
1381 			open_flags |= O_DIRECT;
1382 			break;
1383 		case 'm':
1384 			mmap_source_file = true;
1385 			break;
1386 		case 's':
1387 			use_sendfile = true;
1388 			break;
1389 		default:
1390 			fputs(cmd->cmd_help, stderr);
1391 			exit(2);
1392 		}
1393 	}
1394 	argc -= optind;
1395 	argv += optind;
1396 	if (argc != 2) {
1397 		fputs("Wrong number of arguments\n\n", stderr);
1398 		fputs(cmd->cmd_help, stderr);
1399 		exit(2);
1400 	}
1401 	if (mmap_source_file && use_sendfile)
1402 		die("-m and -s are mutually exclusive");
1403 
1404 	src_fd = xopen(argv[0], O_RDONLY | open_flags, 0);
1405 	dst_fd = xopen(argv[1], O_WRONLY | O_CREAT | O_TRUNC | open_flags, 0644);
1406 
1407 	if (mmap_source_file) {
1408 		struct stat stbuf;
1409 		void *src_addr;
1410 
1411 		if (fstat(src_fd, &stbuf) != 0)
1412 			die_errno("fstat of source file failed");
1413 
1414 		if ((size_t)stbuf.st_size != stbuf.st_size)
1415 			die("Source file is too large");
1416 
1417 		src_addr = mmap(NULL, stbuf.st_size, PROT_READ, MAP_SHARED,
1418 				src_fd, 0);
1419 		if (src_addr == MAP_FAILED)
1420 			die("mmap of source file failed");
1421 
1422 		full_write(dst_fd, src_addr, stbuf.st_size);
1423 
1424 		munmap(src_addr, stbuf.st_size);
1425 	} else if (use_sendfile) {
1426 		while ((ret = sendfile(dst_fd, src_fd, NULL, INT_MAX)) > 0)
1427 			;
1428 		if (ret < 0)
1429 			die_errno("sendfile failed");
1430 	} else {
1431 		char *buf = aligned_xalloc(F2FS_DEFAULT_BLKSIZE, F2FS_DEFAULT_BLKSIZE);
1432 
1433 		while ((ret = xread(src_fd, buf, F2FS_DEFAULT_BLKSIZE)) > 0)
1434 			full_write(dst_fd, buf, ret);
1435 		free(buf);
1436 	}
1437 	close(src_fd);
1438 	close(dst_fd);
1439 }
1440 
1441 #define get_cblocks_desc "get number of reserved blocks on compress inode"
1442 #define get_cblocks_help "f2fs_io get_cblocks [file]\n\n"
1443 
do_get_cblocks(int argc,char ** argv,const struct cmd_desc * cmd)1444 static void do_get_cblocks(int argc, char **argv, const struct cmd_desc *cmd)
1445 {
1446 	unsigned long long blkcnt;
1447 	int ret, fd;
1448 
1449 	if (argc != 2) {
1450 		fputs("Excess arguments\n\n", stderr);
1451 		fputs(cmd->cmd_help, stderr);
1452 		exit(1);
1453 	}
1454 
1455 	fd = xopen(argv[1], O_RDONLY, 0);
1456 
1457 	ret = ioctl(fd, F2FS_IOC_GET_COMPRESS_BLOCKS, &blkcnt);
1458 	if (ret < 0)
1459 		die_errno("F2FS_IOC_GET_COMPRESS_BLOCKS failed");
1460 
1461 	printf("%llu\n", blkcnt);
1462 
1463 	exit(0);
1464 }
1465 
1466 #define release_cblocks_desc "release reserved blocks on compress inode"
1467 #define release_cblocks_help "f2fs_io release_cblocks [file]\n\n"
1468 
do_release_cblocks(int argc,char ** argv,const struct cmd_desc * cmd)1469 static void do_release_cblocks(int argc, char **argv, const struct cmd_desc *cmd)
1470 {
1471 	unsigned long long blkcnt;
1472 	int ret, fd;
1473 
1474 	if (argc != 2) {
1475 		fputs("Excess arguments\n\n", stderr);
1476 		fputs(cmd->cmd_help, stderr);
1477 		exit(1);
1478 	}
1479 
1480 	fd = xopen(argv[1], O_RDONLY, 0);
1481 
1482 	ret = ioctl(fd, F2FS_IOC_RELEASE_COMPRESS_BLOCKS, &blkcnt);
1483 	if (ret < 0)
1484 		die_errno("F2FS_IOC_RELEASE_COMPRESS_BLOCKS failed");
1485 
1486 	printf("%llu\n", blkcnt);
1487 
1488 	exit(0);
1489 }
1490 
1491 #define reserve_cblocks_desc "reserve blocks on compress inode"
1492 #define reserve_cblocks_help "f2fs_io reserve_cblocks [file]\n\n"
1493 
do_reserve_cblocks(int argc,char ** argv,const struct cmd_desc * cmd)1494 static void do_reserve_cblocks(int argc, char **argv, const struct cmd_desc *cmd)
1495 {
1496 	unsigned long long blkcnt;
1497 	int ret, fd;
1498 
1499 	if (argc != 2) {
1500 		fputs("Excess arguments\n\n", stderr);
1501 		fputs(cmd->cmd_help, stderr);
1502 		exit(1);
1503 	}
1504 
1505 	fd = xopen(argv[1], O_RDONLY, 0);
1506 
1507 	ret = ioctl(fd, F2FS_IOC_RESERVE_COMPRESS_BLOCKS, &blkcnt);
1508 	if (ret < 0)
1509 		die_errno("F2FS_IOC_RESERVE_COMPRESS_BLOCKS failed");
1510 
1511 	printf("%llu\n", blkcnt);
1512 
1513 	exit(0);
1514 }
1515 
1516 #define get_coption_desc "get compression option of a compressed file"
1517 #define get_coption_help						\
1518 "f2fs_io get_coption [file]\n\n"	\
1519 "  algorithm        : compression algorithm (0:lzo, 1: lz4, 2:zstd, 3:lzorle)\n"	\
1520 "  log_cluster_size : compression cluster log size (2 <= log_size <= 8)\n"
1521 
do_get_coption(int argc,char ** argv,const struct cmd_desc * cmd)1522 static void do_get_coption(int argc, char **argv, const struct cmd_desc *cmd)
1523 {
1524 	struct f2fs_comp_option option;
1525 	int ret, fd;
1526 
1527 	if (argc != 2) {
1528 		fputs("Excess arguments\n\n", stderr);
1529 		fputs(cmd->cmd_help, stderr);
1530 		exit(1);
1531 	}
1532 
1533 	fd = xopen(argv[1], O_RDONLY, 0);
1534 
1535 	ret = ioctl(fd, F2FS_IOC_GET_COMPRESS_OPTION, &option);
1536 	if (ret < 0)
1537 		die_errno("F2FS_IOC_GET_COMPRESS_OPTION failed");
1538 
1539 	printf("compression algorithm:%u\n", option.algorithm);
1540 	printf("compression cluster log size:%u\n", option.log_cluster_size);
1541 
1542 	exit(0);
1543 }
1544 
1545 #define set_coption_desc "set compression option of a compressed file"
1546 #define set_coption_help						\
1547 "f2fs_io set_coption [algorithm] [log_cluster_size] [file_path]\n\n"	\
1548 "  algorithm        : compression algorithm (0:lzo, 1: lz4, 2:zstd, 3:lzorle)\n"	\
1549 "  log_cluster_size : compression cluster log size (2 <= log_size <= 8)\n"
1550 
do_set_coption(int argc,char ** argv,const struct cmd_desc * cmd)1551 static void do_set_coption(int argc, char **argv, const struct cmd_desc *cmd)
1552 {
1553 	struct f2fs_comp_option option;
1554 	int fd, ret;
1555 
1556 	if (argc != 4) {
1557 		fputs("Excess arguments\n\n", stderr);
1558 		fputs(cmd->cmd_help, stderr);
1559 		exit(1);
1560 	}
1561 
1562 	option.algorithm = atoi(argv[1]);
1563 	option.log_cluster_size = atoi(argv[2]);
1564 
1565 	fd = xopen(argv[3], O_WRONLY, 0);
1566 
1567 	ret = ioctl(fd, F2FS_IOC_SET_COMPRESS_OPTION, &option);
1568 	if (ret < 0)
1569 		die_errno("F2FS_IOC_SET_COMPRESS_OPTION failed");
1570 
1571 	printf("set compression option: algorithm=%u, log_cluster_size=%u\n",
1572 			option.algorithm, option.log_cluster_size);
1573 	exit(0);
1574 }
1575 
1576 #define decompress_desc "decompress an already compressed file"
1577 #define decompress_help "f2fs_io decompress [file_path]\n\n"
1578 
do_decompress(int argc,char ** argv,const struct cmd_desc * cmd)1579 static void do_decompress(int argc, char **argv, const struct cmd_desc *cmd)
1580 {
1581 	int fd, ret;
1582 
1583 	if (argc != 2) {
1584 		fputs("Excess arguments\n\n", stderr);
1585 		fputs(cmd->cmd_help, stderr);
1586 		exit(1);
1587 	}
1588 
1589 	fd = xopen(argv[1], O_WRONLY, 0);
1590 
1591 	ret = ioctl(fd, F2FS_IOC_DECOMPRESS_FILE);
1592 	if (ret < 0)
1593 		die_errno("F2FS_IOC_DECOMPRESS_FILE failed");
1594 
1595 	exit(0);
1596 }
1597 
1598 #define compress_desc "compress a compression enabled file"
1599 #define compress_help "f2fs_io compress [file_path]\n\n"
1600 
do_compress(int argc,char ** argv,const struct cmd_desc * cmd)1601 static void do_compress(int argc, char **argv, const struct cmd_desc *cmd)
1602 {
1603 	int fd, ret;
1604 
1605 	if (argc != 2) {
1606 		fputs("Excess arguments\n\n", stderr);
1607 		fputs(cmd->cmd_help, stderr);
1608 		exit(1);
1609 	}
1610 
1611 	fd = xopen(argv[1], O_WRONLY, 0);
1612 
1613 	ret = ioctl(fd, F2FS_IOC_COMPRESS_FILE);
1614 	if (ret < 0)
1615 		die_errno("F2FS_IOC_COMPRESS_FILE failed");
1616 
1617 	exit(0);
1618 }
1619 
1620 #define get_filename_encrypt_mode_desc "get file name encrypt mode"
1621 #define get_filename_encrypt_mode_help					\
1622 "f2fs_io filename_encrypt_mode [file or directory path]\n\n"		\
1623 "Get the file name encription mode of the given file/directory.\n"	\
1624 
do_get_filename_encrypt_mode(int argc,char ** argv,const struct cmd_desc * cmd)1625 static void do_get_filename_encrypt_mode (int argc, char **argv,
1626 						const struct cmd_desc *cmd)
1627 {
1628 	static const char *enc_name[] = {
1629 		"invalid", /* FSCRYPT_MODE_INVALID (0) */
1630 		"aes-256-xts", /* FSCRYPT_MODE_AES_256_XTS (1) */
1631 		"aes-256-gcm", /* FSCRYPT_MODE_AES_256_GCM (2) */
1632 		"aes-256-cbc", /* FSCRYPT_MODE_AES_256_CBC (3) */
1633 		"aes-256-cts", /* FSCRYPT_MODE_AES_256_CTS (4) */
1634 		"aes-128-cbc", /* FSCRYPT_MODE_AES_128_CBC (5) */
1635 		"aes-128-cts", /* FSCRYPT_MODE_AES_128_CTS (6) */
1636 		"speck128-256-xts", /* FSCRYPT_MODE_SPECK128_256_XTS (7) */
1637 		"speck128-256-cts", /* FSCRYPT_MODE_SPECK128_256_CTS (8) */
1638 		"adiantum", /* FSCRYPT_MODE_ADIANTUM (9) */
1639 		"aes-256-hctr2", /* FSCRYPT_MODE_AES_256_HCTR2 (10) */
1640 	};
1641 	int fd, mode, ret;
1642 	struct fscrypt_get_policy_ex_arg arg;
1643 
1644 	if (argc != 2) {
1645 		fputs("Excess arguments\n\n", stderr);
1646 		fputs(cmd->cmd_help, stderr);
1647 		exit(1);
1648 	}
1649 
1650 	fd = xopen(argv[1], O_RDONLY, 0);
1651 	arg.policy_size = sizeof(arg.policy);
1652 	ret = ioctl(fd, FS_IOC_GET_ENCRYPTION_POLICY_EX, &arg);
1653 	if (ret != 0 && errno == ENOTTY)
1654 		ret = ioctl(fd, FS_IOC_GET_ENCRYPTION_POLICY, arg.policy.v1);
1655 	close(fd);
1656 
1657 	if (ret) {
1658 		perror("FS_IOC_GET_ENCRYPTION_POLICY|_EX");
1659 		exit(1);
1660 	}
1661 
1662 	switch (arg.policy.version) {
1663 	case FSCRYPT_POLICY_V1:
1664 		mode = arg.policy.v1.filenames_encryption_mode;
1665 		break;
1666 	case FSCRYPT_POLICY_V2:
1667 		mode = arg.policy.v2.filenames_encryption_mode;
1668 		break;
1669 	default:
1670 		printf("Do not support policy version: %d\n",
1671 							arg.policy.version);
1672 		exit(1);
1673 	}
1674 
1675 	if (mode >= sizeof(enc_name)/sizeof(enc_name[0])) {
1676 		printf("Do not support algorithm: %d\n", mode);
1677 		exit(1);
1678 	}
1679 	printf ("%s\n", enc_name[mode]);
1680 	exit(0);
1681 }
1682 
1683 #define rename_desc "rename source to target file with fsync option"
1684 #define rename_help							\
1685 "f2fs_io rename [src_path] [target_path] [fsync_after_rename]\n\n"	\
1686 "e.g., f2fs_io rename source dest 1\n"					\
1687 "      1. open(source)\n"						\
1688 "      2. rename(source, dest)\n"					\
1689 "      3. fsync(source)\n"						\
1690 "      4. close(source)\n"
1691 
do_rename(int argc,char ** argv,const struct cmd_desc * cmd)1692 static void do_rename(int argc, char **argv, const struct cmd_desc *cmd)
1693 {
1694 	int fd = -1;
1695 	int ret;
1696 
1697 	if (argc != 4) {
1698 		fputs("Excess arguments\n\n", stderr);
1699 		fputs(cmd->cmd_help, stderr);
1700 		exit(1);
1701 	}
1702 
1703 	if (atoi(argv[3]))
1704 		fd = xopen(argv[1], O_WRONLY, 0);
1705 
1706 	ret = rename(argv[1], argv[2]);
1707 	if (ret < 0)
1708 		die_errno("rename failed");
1709 
1710 	if (fd >= 0) {
1711 		if (fsync(fd) != 0)
1712 			die_errno("fsync failed: %s", argv[1]);
1713 		close(fd);
1714 	}
1715 	exit(0);
1716 }
1717 
1718 #define gc_desc "trigger filesystem GC"
1719 #define gc_help "f2fs_io gc sync_mode [file_path]\n\n"
1720 
do_gc(int argc,char ** argv,const struct cmd_desc * cmd)1721 static void do_gc(int argc, char **argv, const struct cmd_desc *cmd)
1722 {
1723 	u32 sync;
1724 	int ret, fd;
1725 
1726 	if (argc != 3) {
1727 		fputs("Excess arguments\n\n", stderr);
1728 		fputs(cmd->cmd_help, stderr);
1729 		exit(1);
1730 	}
1731 
1732 	sync = atoi(argv[1]);
1733 
1734 	fd = xopen(argv[2], O_RDONLY, 0);
1735 
1736 	ret = ioctl(fd, F2FS_IOC_GARBAGE_COLLECT, &sync);
1737 	if (ret < 0)
1738 		die_errno("F2FS_IOC_GARBAGE_COLLECT failed");
1739 
1740 	printf("trigger %s gc ret=%d\n",
1741 		sync ? "synchronous" : "asynchronous", ret);
1742 	exit(0);
1743 }
1744 
1745 #define checkpoint_desc "trigger filesystem checkpoint"
1746 #define checkpoint_help "f2fs_io checkpoint [file_path]\n\n"
1747 
do_checkpoint(int argc,char ** argv,const struct cmd_desc * cmd)1748 static void do_checkpoint(int argc, char **argv, const struct cmd_desc *cmd)
1749 {
1750 	int ret, fd;
1751 
1752 	if (argc != 2) {
1753 		fputs("Excess arguments\n\n", stderr);
1754 		fputs(cmd->cmd_help, stderr);
1755 		exit(1);
1756 	}
1757 
1758 	fd = xopen(argv[1], O_WRONLY, 0);
1759 
1760 	ret = ioctl(fd, F2FS_IOC_WRITE_CHECKPOINT);
1761 	if (ret < 0)
1762 		die_errno("F2FS_IOC_WRITE_CHECKPOINT failed");
1763 
1764 	printf("trigger filesystem checkpoint ret=%d\n", ret);
1765 	exit(0);
1766 }
1767 
1768 #define precache_extents_desc "trigger precache extents"
1769 #define precache_extents_help "f2fs_io precache_extents [file_path]\n\n"
1770 
do_precache_extents(int argc,char ** argv,const struct cmd_desc * cmd)1771 static void do_precache_extents(int argc, char **argv, const struct cmd_desc *cmd)
1772 {
1773 	int ret, fd;
1774 
1775 	if (argc != 2) {
1776 		fputs("Excess arguments\n\n", stderr);
1777 		fputs(cmd->cmd_help, stderr);
1778 		exit(1);
1779 	}
1780 
1781 	fd = xopen(argv[1], O_WRONLY, 0);
1782 
1783 	ret = ioctl(fd, F2FS_IOC_PRECACHE_EXTENTS);
1784 	if (ret < 0)
1785 		die_errno("F2FS_IOC_PRECACHE_EXTENTS failed");
1786 
1787 	printf("trigger precache extents ret=%d\n", ret);
1788 	exit(0);
1789 }
1790 
1791 #define move_range_desc "moving a range of data blocks from source file to destination file"
1792 #define move_range_help						\
1793 "f2fs_io move_range [src_path] [dst_path] [src_start] [dst_start] "	\
1794 "[length]\n\n"								\
1795 "  src_path  : path to source file\n"					\
1796 "  dst_path  : path to destination file\n"				\
1797 "  src_start : start offset of src file move region, unit: bytes\n"	\
1798 "  dst_start : start offset of dst file move region, unit: bytes\n"	\
1799 "  length    : size to move\n"						\
1800 
do_move_range(int argc,char ** argv,const struct cmd_desc * cmd)1801 static void do_move_range(int argc, char **argv, const struct cmd_desc *cmd)
1802 {
1803 	struct f2fs_move_range range;
1804 	int ret, fd;
1805 
1806 	if (argc != 6) {
1807 		fputs("Excess arguments\n\n", stderr);
1808 		fputs(cmd->cmd_help, stderr);
1809 		exit(1);
1810 	}
1811 
1812 	fd = xopen(argv[1], O_RDWR, 0);
1813 	range.dst_fd = xopen(argv[2], O_RDWR | O_CREAT, 0644);
1814 	range.pos_in = atoll(argv[3]);
1815 	range.pos_out = atoll(argv[4]);
1816 	range.len = atoll(argv[5]);
1817 
1818 	ret = ioctl(fd, F2FS_IOC_MOVE_RANGE, &range);
1819 	if (ret < 0)
1820 		die_errno("F2FS_IOC_MOVE_RANGE failed");
1821 
1822 	printf("move range ret=%d\n", ret);
1823 	exit(0);
1824 }
1825 
1826 #define gc_range_desc "trigger filesystem gc_range"
1827 #define gc_range_help "f2fs_io gc_range [sync_mode] [start] [length] [file_path]\n\n"\
1828 "  sync_mode : 0: asynchronous, 1: synchronous\n"			\
1829 "  start     : start offset of defragment region, unit: 4kb\n"	\
1830 "  length    : bytes number of defragment region, unit: 4kb\n"	\
1831 
do_gc_range(int argc,char ** argv,const struct cmd_desc * cmd)1832 static void do_gc_range(int argc, char **argv, const struct cmd_desc *cmd)
1833 {
1834 	struct f2fs_gc_range range;
1835 	int ret, fd;
1836 
1837 	if (argc != 5) {
1838 		fputs("Excess arguments\n\n", stderr);
1839 		fputs(cmd->cmd_help, stderr);
1840 		exit(1);
1841 	}
1842 
1843 	range.sync = atoi(argv[1]);
1844 	range.start = (u64)atoi(argv[2]);
1845 	range.len = (u64)atoi(argv[3]);
1846 
1847 	fd = xopen(argv[4], O_RDWR, 0);
1848 
1849 	ret = ioctl(fd, F2FS_IOC_GARBAGE_COLLECT_RANGE, &range);
1850 	if (ret < 0) {
1851 		die_errno("F2FS_IOC_GARBAGE_COLLECT_RANGE failed");
1852 	}
1853 
1854 	printf("trigger %s gc_range [%"PRIu64", %"PRIu64"] ret=%d\n",
1855 		range.sync ? "synchronous" : "asynchronous",
1856 		range.start, range.len, ret);
1857 	exit(0);
1858 }
1859 
1860 #define listxattr_desc "listxattr"
1861 #define listxattr_help "f2fs_io listxattr [file_path]\n\n"
1862 
do_listxattr(int argc,char ** argv,const struct cmd_desc * cmd)1863 static void do_listxattr(int argc, char **argv, const struct cmd_desc *cmd)
1864 {
1865 	char *buf, *key, *val;
1866 	ssize_t buflen, vallen, keylen;
1867 
1868 	if (argc != 2) {
1869 		fputs("Excess arguments\n\n", stderr);
1870 		fputs(cmd->cmd_help, stderr);
1871 		exit(1);
1872 	}
1873 
1874 	buflen = listxattr(argv[1], NULL, 0);
1875 	if (buflen == -1) {
1876 		perror("listxattr");
1877 		exit(1);
1878 	}
1879 	if (buflen == 0) {
1880 		printf("%s has no attributes.\n", argv[1]);
1881 		exit(0);
1882 	}
1883 	buf = xmalloc(buflen);
1884 	buflen = listxattr(argv[1], buf, buflen);
1885 	if (buflen == -1) {
1886 		perror("listxattr");
1887 		exit(1);
1888 	}
1889 
1890 	key = buf;
1891 	while (buflen > 0) {
1892 		printf("%s: ", key);
1893 		vallen = getxattr(argv[1], key, NULL, 0);
1894 		if (vallen == -1) {
1895 			perror("getxattr");
1896 			exit(1);
1897 		}
1898 		if (vallen == 0) {
1899 			printf("<no value>");
1900 		} else {
1901 			val = xmalloc(vallen + 1);
1902 			vallen = getxattr(argv[1], key, val, vallen);
1903 			if (vallen == -1) {
1904 				perror("getxattr");
1905 				exit(1);
1906 			}
1907 			val[vallen] = 0;
1908 			printf("%s", val);
1909 			free(val);
1910 		}
1911 		printf("\n");
1912 		keylen = strlen(key) + 1;
1913 		buflen -= keylen;
1914 		key += keylen;
1915 	}
1916 	exit(0);
1917 }
1918 
1919 #define setxattr_desc "setxattr"
1920 #define setxattr_help "f2fs_io setxattr [name] [value] [file_path]\n\n"
1921 
do_setxattr(int argc,char ** argv,const struct cmd_desc * cmd)1922 static void do_setxattr(int argc, char **argv, const struct cmd_desc *cmd)
1923 {
1924 	int ret;
1925 	char *value;
1926 	unsigned char tmp;
1927 
1928 	if (argc != 4) {
1929 		fputs("Excess arguments\n\n", stderr);
1930 		fputs(cmd->cmd_help, stderr);
1931 		exit(1);
1932 	}
1933 
1934 	if (!strcmp(argv[1], F2FS_SYSTEM_ADVISE_NAME)) {
1935 		tmp = strtoul(argv[2], NULL, 0);
1936 		value = (char *)&tmp;
1937 	} else {
1938 		value = argv[2];
1939 	}
1940 
1941 	ret = setxattr(argv[3], argv[1], value, strlen(argv[2]), XATTR_CREATE);
1942 	printf("setxattr %s CREATE: name: %s, value: %s: ret=%d\n",
1943 			argv[3], argv[1], argv[2], ret);
1944 	if (ret < 0 && errno == EEXIST) {
1945 		ret = setxattr(argv[3], argv[1], value, strlen(argv[2]), XATTR_REPLACE);
1946 		printf("setxattr %s REPLACE: name: %s, value: %s: ret=%d\n",
1947 				argv[3], argv[1], argv[2], ret);
1948 	}
1949 	if (ret < 0)
1950 		perror("setxattr");
1951 	exit(0);
1952 }
1953 
1954 #define removexattr_desc "removexattr"
1955 #define removexattr_help "f2fs_io removexattr [name] [file_path]\n\n"
1956 
do_removexattr(int argc,char ** argv,const struct cmd_desc * cmd)1957 static void do_removexattr(int argc, char **argv, const struct cmd_desc *cmd)
1958 {
1959 	int ret;
1960 
1961 	if (argc != 3) {
1962 		fputs("Excess arguments\n\n", stderr);
1963 		fputs(cmd->cmd_help, stderr);
1964 		exit(1);
1965 	}
1966 
1967 	ret = removexattr(argv[2], argv[1]);
1968 	printf("removexattr %s REMOVE: name: %s: ret=%d\n", argv[1], argv[2], ret);
1969 	exit(0);
1970 }
1971 
1972 #define lseek_desc "do lseek for a file"
1973 #define lseek_help					\
1974 "f2fs_io lseek [whence] [offset] [file_path]\n\n"	\
1975 "Do lseek file data in file_path and return the adjusted file offset\n"	\
1976 "whence can be\n"					\
1977 "  set  : SEEK_SET, The file offset is set to offset bytes\n"	\
1978 "  cur  : SEEK_CUR, The file offset is set to its current location plus offset bytes\n"	\
1979 "  end  : SEEK_END, The file offset is set to the size of the file plus offset bytes\n"	\
1980 "  data : SEEK_DATA, set the file offset to the next data location from offset\n"	\
1981 "  hole : SEEK_HOLE, set the file offset to the next hole from offset\n"
1982 
do_lseek(int argc,char ** argv,const struct cmd_desc * cmd)1983 static void do_lseek(int argc, char **argv, const struct cmd_desc *cmd)
1984 {
1985 	int fd, whence;
1986 	off_t offset, ret;
1987 
1988 	if (argc != 4) {
1989 		fputs("Excess arguments\n\n", stderr);
1990 		fputs(cmd->cmd_help, stderr);
1991 		exit(1);
1992 	}
1993 
1994 	offset = atoll(argv[2]);
1995 
1996 	if (!strcmp(argv[1], "set"))
1997 		whence = SEEK_SET;
1998 	else if (!strcmp(argv[1], "cur"))
1999 		whence = SEEK_CUR;
2000 	else if (!strcmp(argv[1], "end"))
2001 		whence = SEEK_END;
2002 	else if (!strcmp(argv[1], "data"))
2003 		whence = SEEK_DATA;
2004 	else if (!strcmp(argv[1], "hole"))
2005 		whence = SEEK_HOLE;
2006 	else
2007 		die("Wrong whence type");
2008 
2009 	fd = xopen(argv[3], O_RDONLY, 0);
2010 
2011 	ret = lseek(fd, offset, whence);
2012 	if (ret < 0)
2013 		die_errno("lseek failed");
2014 	printf("returned offset=%lld\n", (long long)ret);
2015 	exit(0);
2016 }
2017 
2018 #define get_advise_desc "get_advise"
2019 #define get_advise_help "f2fs_io get_advise [file_path]\n\n"
2020 
do_get_advise(int argc,char ** argv,const struct cmd_desc * cmd)2021 static void do_get_advise(int argc, char **argv, const struct cmd_desc *cmd)
2022 {
2023 	int ret;
2024 	unsigned char value;
2025 
2026 	if (argc != 2) {
2027 		fputs("Excess arguments\n\n", stderr);
2028 		fputs(cmd->cmd_help, stderr);
2029 		exit(1);
2030 	}
2031 
2032 	ret = getxattr(argv[1], F2FS_SYSTEM_ADVISE_NAME, &value, sizeof(value));
2033 	if (ret != sizeof(value)) {
2034 		perror("getxattr");
2035 		exit(1);
2036 	}
2037 
2038 	printf("i_advise=0x%x, advise_type: ", value);
2039 	if (value & FADVISE_COLD_BIT)
2040 		printf("cold ");
2041 	if (value & FADVISE_LOST_PINO_BIT)
2042 		printf("lost_pino ");
2043 	if (value & FADVISE_ENCRYPT_BIT)
2044 		printf("encrypt ");
2045 	if (value & FADVISE_ENC_NAME_BIT)
2046 		printf("enc_name ");
2047 	if (value & FADVISE_KEEP_SIZE_BIT)
2048 		printf("keep_size ");
2049 	if (value & FADVISE_HOT_BIT)
2050 		printf("hot ");
2051 	if (value & FADVISE_VERITY_BIT)
2052 		printf("verity ");
2053 	if (value & FADVISE_TRUNC_BIT)
2054 		printf("trunc ");
2055 	printf("\n");
2056 }
2057 
2058 #define ftruncate_desc "ftruncate a file"
2059 #define ftruncate_help					\
2060 "f2fs_io ftruncate [length] [file_path]\n\n"	\
2061 "Do ftruncate a file in file_path with the length\n"	\
2062 
do_ftruncate(int argc,char ** argv,const struct cmd_desc * cmd)2063 static void do_ftruncate(int argc, char **argv, const struct cmd_desc *cmd)
2064 {
2065 	int fd, ret;
2066 	off_t length;
2067 
2068 	if (argc != 3) {
2069 		fputs("Excess arguments\n\n", stderr);
2070 		fputs(cmd->cmd_help, stderr);
2071 		exit(1);
2072 	}
2073 
2074 	length = atoll(argv[1]);
2075 	fd = xopen(argv[2], O_WRONLY, 0);
2076 
2077 	ret = ftruncate(fd, length);
2078 	if (ret < 0)
2079 		die_errno("ftruncate failed");
2080 	exit(0);
2081 }
2082 
2083 #define CMD_HIDDEN 	0x0001
2084 #define CMD(name) { #name, do_##name, name##_desc, name##_help, 0 }
2085 #define _CMD(name) { #name, do_##name, NULL, NULL, CMD_HIDDEN }
2086 
2087 static void do_help(int argc, char **argv, const struct cmd_desc *cmd);
2088 const struct cmd_desc cmd_list[] = {
2089 	_CMD(help),
2090 	CMD(fsync),
2091 	CMD(fdatasync),
2092 	CMD(set_verity),
2093 	CMD(getflags),
2094 	CMD(setflags),
2095 	CMD(clearflags),
2096 	CMD(shutdown),
2097 	CMD(pinfile),
2098 	CMD(fadvise),
2099 	CMD(fallocate),
2100 	CMD(erase),
2101 	CMD(write),
2102 	CMD(write_advice),
2103 	CMD(read),
2104 	CMD(randread),
2105 	CMD(fragread),
2106 	CMD(fiemap),
2107 	CMD(gc_urgent),
2108 	CMD(defrag_file),
2109 	CMD(copy),
2110 	CMD(get_cblocks),
2111 	CMD(release_cblocks),
2112 	CMD(reserve_cblocks),
2113 	CMD(get_coption),
2114 	CMD(set_coption),
2115 	CMD(decompress),
2116 	CMD(compress),
2117 	CMD(get_filename_encrypt_mode),
2118 	CMD(rename),
2119 	CMD(gc),
2120 	CMD(checkpoint),
2121 	CMD(precache_extents),
2122 	CMD(move_range),
2123 	CMD(gc_range),
2124 	CMD(listxattr),
2125 	CMD(setxattr),
2126 	CMD(removexattr),
2127 	CMD(lseek),
2128 	CMD(get_advise),
2129 	CMD(ioprio),
2130 	CMD(ftruncate),
2131 	{ NULL, NULL, NULL, NULL, 0 }
2132 };
2133 
do_help(int argc,char ** argv,const struct cmd_desc * UNUSED (cmd))2134 static void do_help(int argc, char **argv, const struct cmd_desc *UNUSED(cmd))
2135 {
2136 	const struct cmd_desc *p;
2137 
2138 	if (argc > 1) {
2139 		for (p = cmd_list; p->cmd_name; p++) {
2140 			if (p->cmd_flags & CMD_HIDDEN)
2141 				continue;
2142 			if (strcmp(p->cmd_name, argv[1]) == 0) {
2143 				putc('\n', stdout);
2144 				fputs("USAGE:\n  ", stdout);
2145 				fputs(p->cmd_help, stdout);
2146 				exit(0);
2147 			}
2148 		}
2149 		printf("Unknown command: %s\n\n", argv[1]);
2150 	}
2151 
2152 	fputs("Available commands:\n", stdout);
2153 	for (p = cmd_list; p->cmd_name; p++) {
2154 		if (p->cmd_flags & CMD_HIDDEN)
2155 			continue;
2156 		printf("  %-20s %s\n", p->cmd_name, p->cmd_desc);
2157 	}
2158 	printf("\nTo get more information on a command, "
2159 	       "type 'f2fs_io help cmd'\n");
2160 	exit(0);
2161 }
2162 
die_signal_handler(int UNUSED (signum),siginfo_t * UNUSED (siginfo),void * UNUSED (context))2163 static void die_signal_handler(int UNUSED(signum), siginfo_t *UNUSED(siginfo),
2164 				void *UNUSED(context))
2165 {
2166 	exit(-1);
2167 }
2168 
sigcatcher_setup(void)2169 static void sigcatcher_setup(void)
2170 {
2171 	struct sigaction	sa;
2172 
2173 	memset(&sa, 0, sizeof(struct sigaction));
2174 	sa.sa_sigaction = die_signal_handler;
2175 	sa.sa_flags = SA_SIGINFO;
2176 
2177 	sigaction(SIGHUP, &sa, 0);
2178 	sigaction(SIGINT, &sa, 0);
2179 	sigaction(SIGQUIT, &sa, 0);
2180 	sigaction(SIGFPE, &sa, 0);
2181 	sigaction(SIGILL, &sa, 0);
2182 	sigaction(SIGBUS, &sa, 0);
2183 	sigaction(SIGSEGV, &sa, 0);
2184 	sigaction(SIGABRT, &sa, 0);
2185 	sigaction(SIGPIPE, &sa, 0);
2186 	sigaction(SIGALRM, &sa, 0);
2187 	sigaction(SIGTERM, &sa, 0);
2188 	sigaction(SIGUSR1, &sa, 0);
2189 	sigaction(SIGUSR2, &sa, 0);
2190 	sigaction(SIGPOLL, &sa, 0);
2191 	sigaction(SIGPROF, &sa, 0);
2192 	sigaction(SIGSYS, &sa, 0);
2193 	sigaction(SIGTRAP, &sa, 0);
2194 	sigaction(SIGVTALRM, &sa, 0);
2195 	sigaction(SIGXCPU, &sa, 0);
2196 	sigaction(SIGXFSZ, &sa, 0);
2197 }
2198 
main(int argc,char ** argv)2199 int main(int argc, char **argv)
2200 {
2201 	const struct cmd_desc *cmd;
2202 
2203 	if (argc < 2)
2204 		do_help(argc, argv, cmd_list);
2205 
2206 	sigcatcher_setup();
2207 	for (cmd = cmd_list; cmd->cmd_name; cmd++) {
2208 		if (strcmp(cmd->cmd_name, argv[1]) == 0) {
2209 			cmd->cmd_func(argc - 1, argv + 1, cmd);
2210 			exit(0);
2211 		}
2212 	}
2213 	printf("Unknown command: %s\n\n", argv[1]);
2214 	do_help(1, argv, cmd_list);
2215 	return 0;
2216 }
2217