• 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 _LARGEFILE_SOURCE
13 #define _LARGEFILE_SOURCE
14 #endif
15 #ifndef _LARGEFILE64_SOURCE
16 #define _LARGEFILE64_SOURCE
17 #endif
18 #ifndef O_LARGEFILE
19 #define O_LARGEFILE 0
20 #endif
21 
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <getopt.h>
25 #include <inttypes.h>
26 #include <limits.h>
27 #include <linux/fs.h>
28 #include <signal.h>
29 #include <stdarg.h>
30 #include <stdbool.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <sys/mman.h>
35 #include <sys/sendfile.h>
36 #include <sys/stat.h>
37 #include <sys/types.h>
38 #include <termios.h>
39 #include <time.h>
40 #include <unistd.h>
41 
42 #ifdef HAVE_CONFIG_H
43 #include "config.h"
44 #endif
45 #include <android_config.h>
46 
47 #include "f2fs_io.h"
48 
49 struct cmd_desc {
50 	const char *cmd_name;
51 	void (*cmd_func)(int, char **, const struct cmd_desc *);
52 	const char *cmd_desc;
53 	const char *cmd_help;
54 	int cmd_flags;
55 };
56 
57 static void __attribute__((noreturn))
do_die(const char * format,va_list va,int err)58 do_die(const char *format, va_list va, int err)
59 {
60 	vfprintf(stderr, format, va);
61 	if (err)
62 		fprintf(stderr, ": %s", strerror(err));
63 	putc('\n', stderr);
64 	exit(1);
65 }
66 
67 static void __attribute__((noreturn, format(printf, 1, 2)))
die_errno(const char * format,...)68 die_errno(const char *format, ...)
69 {
70 	va_list va;
71 
72 	va_start(va, format);
73 	do_die(format, va, errno);
74 	va_end(va);
75 }
76 
77 static void __attribute__((noreturn, format(printf, 1, 2)))
die(const char * format,...)78 die(const char *format, ...)
79 {
80 	va_list va;
81 
82 	va_start(va, format);
83 	do_die(format, va, 0);
84 	va_end(va);
85 }
86 
xmalloc(size_t size)87 static void *xmalloc(size_t size)
88 {
89 	void *p = malloc(size);
90 
91 	if (!p)
92 		die("Memory alloc failed (requested %zu bytes)", size);
93 	return p;
94 }
95 
aligned_xalloc(size_t alignment,size_t size)96 static void *aligned_xalloc(size_t alignment, size_t size)
97 {
98 	void *p = aligned_alloc(alignment, size);
99 
100 	if (!p)
101 		die("Memory alloc failed (requested %zu bytes)", size);
102 	return p;
103 }
104 
xopen(const char * pathname,int flags,mode_t mode)105 static int xopen(const char *pathname, int flags, mode_t mode)
106 {
107 	int fd = open(pathname, flags, mode);
108 
109 	if (fd < 0)
110 		die_errno("Failed to open %s", pathname);
111 	return fd;
112 }
113 
xread(int fd,void * buf,size_t count)114 static ssize_t xread(int fd, void *buf, size_t count)
115 {
116 	ssize_t ret = read(fd, buf, count);
117 
118 	if (ret < 0)
119 		die_errno("read failed");
120 	return ret;
121 }
122 
full_write(int fd,const void * buf,size_t count)123 static void full_write(int fd, const void *buf, size_t count)
124 {
125 	while (count) {
126 		ssize_t ret = write(fd, buf, count);
127 
128 		if (ret < 0)
129 			die_errno("write failed");
130 		buf = (char *)buf + ret;
131 		count -= ret;
132 	}
133 }
134 
135 #if defined(__APPLE__)
get_current_us()136 static u64 get_current_us()
137 {
138 #ifdef HAVE_MACH_TIME_H
139 	return mach_absolute_time() / 1000;
140 #else
141 	return 0;
142 #endif
143 }
144 #else
get_current_us()145 static u64 get_current_us()
146 {
147 	struct timespec t;
148 	t.tv_sec = t.tv_nsec = 0;
149 	clock_gettime(CLOCK_BOOTTIME, &t);
150 	return (u64)t.tv_sec * 1000000LL + t.tv_nsec / 1000;
151 }
152 #endif
153 
154 #define fsync_desc "fsync"
155 #define fsync_help						\
156 "f2fs_io fsync [file]\n\n"					\
157 "fsync given the file\n"					\
158 
do_fsync(int argc,char ** argv,const struct cmd_desc * cmd)159 static void do_fsync(int argc, char **argv, const struct cmd_desc *cmd)
160 {
161 	int fd;
162 
163 	if (argc != 2) {
164 		fputs("Excess arguments\n\n", stderr);
165 		fputs(cmd->cmd_help, stderr);
166 		exit(1);
167 	}
168 
169 	fd = xopen(argv[1], O_WRONLY, 0);
170 
171 	if (fsync(fd) != 0)
172 		die_errno("fsync failed");
173 
174 	printf("fsync a file\n");
175 	exit(0);
176 }
177 
178 #define set_verity_desc "Set fs-verity"
179 #define set_verity_help					\
180 "f2fs_io set_verity [file]\n\n"				\
181 "Set fsverity bit given a file\n"			\
182 
do_set_verity(int argc,char ** argv,const struct cmd_desc * cmd)183 static void do_set_verity(int argc, char **argv, const struct cmd_desc *cmd)
184 {
185 	int ret, fd;
186 
187 	if (argc != 2) {
188 		fputs("Excess arguments\n\n", stderr);
189 		fputs(cmd->cmd_help, stderr);
190 		exit(1);
191 	}
192 
193 	fd = open(argv[1], O_RDWR);
194 
195 	ret = ioctl(fd, FS_IOC_ENABLE_VERITY);
196 	if (ret < 0) {
197 		perror("FS_IOC_ENABLE_VERITY");
198 		exit(1);
199 	}
200 
201 	printf("Set fsverity bit to %s\n", argv[1]);
202 	exit(0);
203 }
204 
205 #define getflags_desc "getflags ioctl"
206 #define getflags_help						\
207 "f2fs_io getflags [file]\n\n"					\
208 "get a flag given the file\n"					\
209 "flag can show \n"						\
210 "  encryption\n"						\
211 "  nocow(pinned)\n"						\
212 "  inline_data\n"						\
213 "  verity\n"							\
214 "  casefold\n"							\
215 "  compression\n"						\
216 "  nocompression\n"						\
217 "  immutable\n"
218 
do_getflags(int argc,char ** argv,const struct cmd_desc * cmd)219 static void do_getflags(int argc, char **argv, const struct cmd_desc *cmd)
220 {
221 	long flag = 0;
222 	int ret, fd;
223 	int exist = 0;
224 
225 	if (argc != 2) {
226 		fputs("Excess arguments\n\n", stderr);
227 		fputs(cmd->cmd_help, stderr);
228 		exit(1);
229 	}
230 
231 	fd = xopen(argv[1], O_RDONLY, 0);
232 
233 	ret = ioctl(fd, F2FS_IOC_GETFLAGS, &flag);
234 	printf("get a flag on %s ret=%d, flags=", argv[1], ret);
235 	if (flag & FS_CASEFOLD_FL) {
236 		printf("casefold");
237 		exist = 1;
238 	}
239 	if (flag & FS_COMPR_FL) {
240 		if (exist)
241 			printf(",");
242 		printf("compression");
243 		exist = 1;
244 	}
245 	if (flag & FS_NOCOMP_FL) {
246 		if (exist)
247 			printf(",");
248 		printf("nocompression");
249 		exist = 1;
250 	}
251 	if (flag & FS_ENCRYPT_FL) {
252 		if (exist)
253 			printf(",");
254 		printf("encrypt");
255 		exist = 1;
256 	}
257 	if (flag & FS_VERITY_FL) {
258 		if (exist)
259 			printf(",");
260 		printf("verity");
261 		exist = 1;
262 	}
263 	if (flag & FS_INLINE_DATA_FL) {
264 		if (exist)
265 			printf(",");
266 		printf("inline_data");
267 		exist = 1;
268 	}
269 	if (flag & FS_NOCOW_FL) {
270 		if (exist)
271 			printf(",");
272 		printf("nocow(pinned)");
273 		exist = 1;
274 	}
275 	if (flag & FS_IMMUTABLE_FL) {
276 		if (exist)
277 			printf(",");
278 		printf("immutable");
279 		exist = 1;
280 	}
281 	if (!exist)
282 		printf("none");
283 	printf("\n");
284 	exit(0);
285 }
286 
287 #define setflags_desc "setflags ioctl"
288 #define setflags_help						\
289 "f2fs_io setflags [flag] [file]\n\n"				\
290 "set a flag given the file\n"					\
291 "flag can be\n"							\
292 "  casefold\n"							\
293 "  compression\n"						\
294 "  nocompression\n"						\
295 "  noimmutable\n"
296 
do_setflags(int argc,char ** argv,const struct cmd_desc * cmd)297 static void do_setflags(int argc, char **argv, const struct cmd_desc *cmd)
298 {
299 	long flag = 0;
300 	int ret, fd;
301 
302 	if (argc != 3) {
303 		fputs("Excess arguments\n\n", stderr);
304 		fputs(cmd->cmd_help, stderr);
305 		exit(1);
306 	}
307 
308 	fd = xopen(argv[2], O_RDONLY, 0);
309 
310 	ret = ioctl(fd, F2FS_IOC_GETFLAGS, &flag);
311 	printf("get a flag on %s ret=%d, flags=%lx\n", argv[1], ret, flag);
312 	if (ret)
313 		die_errno("F2FS_IOC_GETFLAGS failed");
314 
315 	if (!strcmp(argv[1], "casefold"))
316 		flag |= FS_CASEFOLD_FL;
317 	else if (!strcmp(argv[1], "compression"))
318 		flag |= FS_COMPR_FL;
319 	else if (!strcmp(argv[1], "nocompression"))
320 		flag |= FS_NOCOMP_FL;
321 	else if (!strcmp(argv[1], "noimmutable"))
322 		flag &= ~FS_IMMUTABLE_FL;
323 
324 	ret = ioctl(fd, F2FS_IOC_SETFLAGS, &flag);
325 	printf("set a flag on %s ret=%d, flags=%s\n", argv[2], ret, argv[1]);
326 	exit(0);
327 }
328 
329 #define shutdown_desc "shutdown filesystem"
330 #define shutdown_help					\
331 "f2fs_io shutdown [level] [dir]\n\n"			\
332 "Freeze and stop all IOs given mount point\n"		\
333 "level can be\n"					\
334 "  0 : going down with full sync\n"			\
335 "  1 : going down with checkpoint only\n"		\
336 "  2 : going down with no sync\n"			\
337 "  3 : going down with metadata flush\n"		\
338 "  4 : going down with fsck mark\n"
339 
do_shutdown(int argc,char ** argv,const struct cmd_desc * cmd)340 static void do_shutdown(int argc, char **argv, const struct cmd_desc *cmd)
341 {
342 	u32 flag;
343 	int ret, fd;
344 
345 	if (argc != 3) {
346 		fputs("Excess arguments\n\n", stderr);
347 		fputs(cmd->cmd_help, stderr);
348 		exit(1);
349 	}
350 
351 	flag = atoi(argv[1]);
352 	if (flag >= F2FS_GOING_DOWN_MAX) {
353 		fputs("Wrong level\n\n", stderr);
354 		fputs(cmd->cmd_help, stderr);
355 		exit(1);
356 	}
357 	fd = xopen(argv[2], O_RDONLY, 0);
358 
359 	ret = ioctl(fd, F2FS_IOC_SHUTDOWN, &flag);
360 	if (ret < 0)
361 		die_errno("F2FS_IOC_SHUTDOWN failed");
362 
363 	printf("Shutdown %s with level=%d\n", argv[2], flag);
364 	exit(0);
365 }
366 
367 #define pinfile_desc "pin file control"
368 #define pinfile_help						\
369 "f2fs_io pinfile [get|set] [file]\n\n"			\
370 "get/set pinning given the file\n"				\
371 
do_pinfile(int argc,char ** argv,const struct cmd_desc * cmd)372 static void do_pinfile(int argc, char **argv, const struct cmd_desc *cmd)
373 {
374 	u32 pin;
375 	int ret, fd;
376 
377 	if (argc != 3) {
378 		fputs("Excess arguments\n\n", stderr);
379 		fputs(cmd->cmd_help, stderr);
380 		exit(1);
381 	}
382 
383 	fd = xopen(argv[2], O_RDONLY, 0);
384 
385 	ret = -1;
386 	if (!strcmp(argv[1], "set")) {
387 		pin = 1;
388 		ret = ioctl(fd, F2FS_IOC_SET_PIN_FILE, &pin);
389 		if (ret != 0)
390 			die_errno("F2FS_IOC_SET_PIN_FILE failed");
391 		printf("set_pin_file: %u blocks moved in %s\n", ret, argv[2]);
392 	} else if (!strcmp(argv[1], "get")) {
393 		unsigned int flags;
394 
395 		ret = ioctl(fd, F2FS_IOC_GET_PIN_FILE, &pin);
396 		if (ret < 0)
397 			die_errno("F2FS_IOC_GET_PIN_FILE failed");
398 
399 		ret = ioctl(fd, F2FS_IOC_GETFLAGS, &flags);
400 		if (ret < 0)
401 			die_errno("F2FS_IOC_GETFLAGS failed");
402 
403 		printf("get_pin_file: %s with %u blocks moved in %s\n",
404 				(flags & F2FS_NOCOW_FL) ? "pinned" : "un-pinned",
405 				pin, argv[2]);
406 	}
407 	exit(0);
408 }
409 
410 #define fallocate_desc "fallocate"
411 #define fallocate_help						\
412 "f2fs_io fallocate [keep_size] [offset] [length] [file]\n\n"	\
413 "fallocate given the file\n"					\
414 " [keep_size] : 1 or 0\n"					\
415 
do_fallocate(int argc,char ** argv,const struct cmd_desc * cmd)416 static void do_fallocate(int argc, char **argv, const struct cmd_desc *cmd)
417 {
418 	int fd;
419 	off_t offset, length;
420 	struct stat sb;
421 	int mode = 0;
422 
423 	if (argc != 5) {
424 		fputs("Excess arguments\n\n", stderr);
425 		fputs(cmd->cmd_help, stderr);
426 		exit(1);
427 	}
428 
429 	if (!strcmp(argv[1], "1"))
430 		mode |= FALLOC_FL_KEEP_SIZE;
431 
432 	offset = atoi(argv[2]);
433 	length = atoll(argv[3]);
434 
435 	fd = xopen(argv[4], O_RDWR, 0);
436 
437 	if (fallocate(fd, mode, offset, length) != 0)
438 		die_errno("fallocate failed");
439 
440 	if (fstat(fd, &sb) != 0)
441 		die_errno("fstat failed");
442 
443 	printf("fallocated a file: i_size=%"PRIu64", i_blocks=%"PRIu64"\n", sb.st_size, sb.st_blocks);
444 	exit(0);
445 }
446 
447 #define erase_desc "erase a block device"
448 #define erase_help				\
449 "f2fs_io erase [block_device_path]\n\n"		\
450 "Send DISCARD | BLKSECDISCARD comamnd to"	\
451 "block device in block_device_path\n"		\
452 
do_erase(int argc,char ** argv,const struct cmd_desc * cmd)453 static void do_erase(int argc, char **argv, const struct cmd_desc *cmd)
454 {
455 	int fd, ret;
456 	struct stat st;
457 	u64 range[2];
458 
459 	if (argc != 2) {
460 		fputs("Excess arguments\n\n", stderr);
461 		fputs(cmd->cmd_help, stderr);
462 		exit(1);
463 	}
464 
465 	if (stat(argv[1], &st) != 0) {
466 		fputs("stat error\n", stderr);
467 		exit(1);
468 	}
469 
470 	if (!S_ISBLK(st.st_mode)) {
471 		fputs(argv[1], stderr);
472 		fputs(" is not a block device\n", stderr);
473 		exit(1);
474 	}
475 
476 	fd = xopen(argv[1], O_WRONLY, 0);
477 
478 	range[0] = 0;
479 	ret = ioctl(fd, BLKGETSIZE64, &range[1]);
480 	if (ret < 0) {
481 		fputs("get size failed\n", stderr);
482 		exit(1);
483 	}
484 
485 	ret = ioctl(fd, BLKSECDISCARD, &range);
486 	if (ret < 0) {
487 		ret = ioctl(fd, BLKDISCARD, &range);
488 		if (ret < 0) {
489 			fputs("Discard failed\n", stderr);
490 			exit(1);
491 		}
492 	}
493 
494 	exit(0);
495 }
496 
497 #define write_desc "write data into file"
498 #define write_help					\
499 "f2fs_io write [chunk_size in 4kb] [offset in chunk_size] [count] [pattern] [IO] [file_path]\n\n"	\
500 "Write given patten data in file_path\n"		\
501 "pattern can be\n"					\
502 "  zero     : zeros\n"					\
503 "  inc_num  : incrementing numbers\n"			\
504 "  rand     : random numbers\n"				\
505 "IO can be\n"						\
506 "  buffered : buffered IO\n"				\
507 "  dio      : direct IO\n"				\
508 "  osync    : O_SYNC\n"					\
509 
do_write(int argc,char ** argv,const struct cmd_desc * cmd)510 static void do_write(int argc, char **argv, const struct cmd_desc *cmd)
511 {
512 	u64 buf_size = 0, inc_num = 0, ret = 0, written = 0;
513 	u64 offset;
514 	char *buf = NULL;
515 	unsigned bs, count, i;
516 	int flags = 0;
517 	int fd;
518 	u64 total_time = 0, max_time = 0, max_time_t = 0;
519 
520 	srand(time(0));
521 
522 	if (argc != 7) {
523 		fputs("Excess arguments\n\n", stderr);
524 		fputs(cmd->cmd_help, stderr);
525 		exit(1);
526 	}
527 
528 	bs = atoi(argv[1]);
529 	if (bs > 1024)
530 		die("Too big chunk size - limit: 4MB");
531 
532 	buf_size = bs * 4096;
533 
534 	offset = atoi(argv[2]) * buf_size;
535 
536 	buf = aligned_xalloc(4096, buf_size);
537 	count = atoi(argv[3]);
538 
539 	if (!strcmp(argv[4], "zero"))
540 		memset(buf, 0, buf_size);
541 	else if (strcmp(argv[4], "inc_num") && strcmp(argv[4], "rand"))
542 		die("Wrong pattern type");
543 
544 	if (!strcmp(argv[5], "dio"))
545 		flags |= O_DIRECT;
546 	else if (!strcmp(argv[5], "osync"))
547 		flags |= O_SYNC;
548 	else if (strcmp(argv[5], "buffered"))
549 		die("Wrong IO type");
550 
551 	fd = xopen(argv[6], O_CREAT | O_WRONLY | flags, 0755);
552 
553 	total_time = get_current_us();
554 	for (i = 0; i < count; i++) {
555 		if (!strcmp(argv[4], "inc_num"))
556 			*(int *)buf = inc_num++;
557 		else if (!strcmp(argv[4], "rand"))
558 			*(int *)buf = rand();
559 
560 		/* write data */
561 		max_time_t = get_current_us();
562 		ret = pwrite(fd, buf, buf_size, offset + buf_size * i);
563 		max_time_t = get_current_us() - max_time_t;
564 		if (max_time < max_time_t)
565 			max_time = max_time_t;
566 		if (ret != buf_size)
567 			break;
568 		written += ret;
569 	}
570 
571 	printf("Written %"PRIu64" bytes with pattern=%s, total_time=%"PRIu64" us, max_latency=%"PRIu64" us\n",
572 				written, argv[4],
573 				get_current_us() - total_time,
574 				max_time);
575 	exit(0);
576 }
577 
578 #define read_desc "read data from file"
579 #define read_help					\
580 "f2fs_io read [chunk_size in 4kb] [offset in chunk_size] [count] [IO] [print_nbytes] [file_path]\n\n"	\
581 "Read data in file_path and print nbytes\n"		\
582 "IO can be\n"						\
583 "  buffered : buffered IO\n"				\
584 "  dio      : direct IO\n"				\
585 "  mmap     : mmap IO\n"				\
586 
do_read(int argc,char ** argv,const struct cmd_desc * cmd)587 static void do_read(int argc, char **argv, const struct cmd_desc *cmd)
588 {
589 	u64 buf_size = 0, ret = 0, read_cnt = 0;
590 	u64 offset;
591 	char *buf = NULL;
592 	char *data;
593 	char *print_buf = NULL;
594 	unsigned bs, count, i, print_bytes;
595 	int flags = 0;
596 	int do_mmap = 0;
597 	int fd;
598 
599 	if (argc != 7) {
600 		fputs("Excess arguments\n\n", stderr);
601 		fputs(cmd->cmd_help, stderr);
602 		exit(1);
603 	}
604 
605 	bs = atoi(argv[1]);
606 	if (bs > 1024)
607 		die("Too big chunk size - limit: 4MB");
608 	buf_size = bs * 4096;
609 
610 	offset = atoi(argv[2]) * buf_size;
611 
612 	buf = aligned_xalloc(4096, buf_size);
613 
614 	count = atoi(argv[3]);
615 	if (!strcmp(argv[4], "dio"))
616 		flags |= O_DIRECT;
617 	else if (!strcmp(argv[4], "mmap"))
618 		do_mmap = 1;
619 	else if (strcmp(argv[4], "buffered"))
620 		die("Wrong IO type");
621 
622 	print_bytes = atoi(argv[5]);
623 	if (print_bytes > buf_size)
624 		die("Print_nbytes should be less then chunk_size in kb");
625 
626 	print_buf = xmalloc(print_bytes);
627 
628 	fd = xopen(argv[6], O_RDONLY | flags, 0);
629 
630 	if (do_mmap) {
631 		data = mmap(NULL, count * buf_size, PROT_READ,
632 						MAP_SHARED, fd, offset);
633 		if (data == MAP_FAILED)
634 			die("Mmap failed");
635 	}
636 
637 	for (i = 0; i < count; i++) {
638 		if (do_mmap) {
639 			memcpy(buf, data + offset + buf_size * i, buf_size);
640 			ret = buf_size;
641 		} else {
642 			ret = pread(fd, buf, buf_size, offset + buf_size * i);
643 		}
644 		if (ret != buf_size)
645 			break;
646 
647 		read_cnt += ret;
648 		if (i == 0)
649 			memcpy(print_buf, buf, print_bytes);
650 	}
651 	printf("Read %"PRIu64" bytes and print %u bytes:\n", read_cnt, print_bytes);
652 	printf("%08"PRIx64" : ", offset);
653 	for (i = 1; i <= print_bytes; i++) {
654 		printf("%02x", print_buf[i - 1]);
655 		if (i % 16 == 0)
656 			printf("\n%08"PRIx64" : ", offset + 16 * i);
657 		else if (i % 2 == 0)
658 			printf(" ");
659 	}
660 	printf("\n");
661 	exit(0);
662 }
663 
664 #define randread_desc "random read data from file"
665 #define randread_help					\
666 "f2fs_io randread [chunk_size in 4kb] [count] [IO] [file_path]\n\n"	\
667 "Do random read data in file_path\n"		\
668 "IO can be\n"						\
669 "  buffered : buffered IO\n"				\
670 "  dio      : direct IO\n"				\
671 
do_randread(int argc,char ** argv,const struct cmd_desc * cmd)672 static void do_randread(int argc, char **argv, const struct cmd_desc *cmd)
673 {
674 	u64 buf_size = 0, ret = 0, read_cnt = 0;
675 	u64 idx, end_idx, aligned_size;
676 	char *buf = NULL;
677 	unsigned bs, count, i;
678 	int flags = 0;
679 	int fd;
680 	time_t t;
681 	struct stat stbuf;
682 
683 	if (argc != 5) {
684 		fputs("Excess arguments\n\n", stderr);
685 		fputs(cmd->cmd_help, stderr);
686 		exit(1);
687 	}
688 
689 	bs = atoi(argv[1]);
690 	if (bs > 1024)
691 		die("Too big chunk size - limit: 4MB");
692 	buf_size = bs * 4096;
693 
694 	buf = aligned_xalloc(4096, buf_size);
695 
696 	count = atoi(argv[2]);
697 	if (!strcmp(argv[3], "dio"))
698 		flags |= O_DIRECT;
699 	else if (strcmp(argv[3], "buffered"))
700 		die("Wrong IO type");
701 
702 	fd = xopen(argv[4], O_RDONLY | flags, 0);
703 
704 	if (fstat(fd, &stbuf) != 0)
705 		die_errno("fstat of source file failed");
706 
707 	aligned_size = (u64)stbuf.st_size & ~((u64)(4096 - 1));
708 	if (aligned_size < buf_size)
709 		die("File is too small to random read");
710 	end_idx = (u64)(aligned_size - buf_size) / (u64)4096 + 1;
711 
712 	srand((unsigned) time(&t));
713 
714 	for (i = 0; i < count; i++) {
715 		idx = rand() % end_idx;
716 
717 		ret = pread(fd, buf, buf_size, 4096 * idx);
718 		if (ret != buf_size)
719 			break;
720 
721 		read_cnt += ret;
722 	}
723 	printf("Read %"PRIu64" bytes\n", read_cnt);
724 	exit(0);
725 }
726 
727 #define fiemap_desc "get block address in file"
728 #define fiemap_help					\
729 "f2fs_io fiemap [offset in 4kb] [count] [file_path]\n\n"\
730 
731 #if defined(HAVE_LINUX_FIEMAP_H) && defined(HAVE_LINUX_FS_H)
do_fiemap(int argc,char ** argv,const struct cmd_desc * cmd)732 static void do_fiemap(int argc, char **argv, const struct cmd_desc *cmd)
733 {
734 	unsigned count, i;
735 	int fd;
736 	__u64 phy_addr;
737 	struct fiemap *fm = xmalloc(sizeof(struct fiemap) +
738 			sizeof(struct fiemap_extent));
739 
740 	if (argc != 4) {
741 		fputs("Excess arguments\n\n", stderr);
742 		fputs(cmd->cmd_help, stderr);
743 		exit(1);
744 	}
745 
746 	fm->fm_start = atoi(argv[1]) * F2FS_BLKSIZE;
747 	fm->fm_length = F2FS_BLKSIZE;
748 	fm->fm_extent_count = 1;
749 	count = atoi(argv[2]);
750 
751 	fd = xopen(argv[3], O_RDONLY | O_LARGEFILE, 0);
752 
753 	printf("Fiemap: offset = %08"PRIx64" len = %d\n",
754 				(u64)fm->fm_start / F2FS_BLKSIZE, count);
755 	for (i = 0; i < count; i++) {
756 		if (ioctl(fd, FS_IOC_FIEMAP, fm) < 0)
757 			die_errno("FIEMAP failed");
758 
759 		phy_addr = fm->fm_extents[0].fe_physical / F2FS_BLKSIZE;
760 		if (phy_addr == NEW_ADDR)
761 			printf("NEW_ADDR ");
762 		else
763 			printf("%llu ", phy_addr);
764 		fm->fm_start += F2FS_BLKSIZE;
765 	}
766 	printf("\n");
767 	free(fm);
768 	exit(0);
769 }
770 #else
do_fiemap(int UNUSED (argc),char ** UNUSED (argv),const struct cmd_desc * UNUSED (cmd))771 static void do_fiemap(int UNUSED(argc), char **UNUSED(argv),
772 			const struct cmd_desc *UNUSED(cmd))
773 {
774 	die("Not support for this platform");
775 }
776 #endif
777 
778 #define gc_urgent_desc "start/end/run gc_urgent for given time period"
779 #define gc_urgent_help					\
780 "f2fs_io gc_urgent $dev [start/end/run] [time in sec]\n\n"\
781 " - f2fs_io gc_urgent sda21 start\n"		\
782 " - f2fs_io gc_urgent sda21 end\n"		\
783 " - f2fs_io gc_urgent sda21 run 10\n"		\
784 
do_gc_urgent(int argc,char ** argv,const struct cmd_desc * cmd)785 static void do_gc_urgent(int argc, char **argv, const struct cmd_desc *cmd)
786 {
787 	char command[255];
788 
789 	if (argc == 3 && !strcmp(argv[2], "start")) {
790 		printf("gc_urgent: start on %s\n", argv[1]);
791 		sprintf(command, "echo %d > %s/%s/gc_urgent", 1, "/sys/fs/f2fs/", argv[1]);
792 		if (system(command))
793 			exit(1);
794 	} else if (argc == 3 && !strcmp(argv[2], "end")) {
795 		printf("gc_urgent: end on %s\n", argv[1]);
796 		sprintf(command, "echo %d > %s/%s/gc_urgent", 0, "/sys/fs/f2fs/", argv[1]);
797 		if (system(command))
798 			exit(1);
799 	} else if (argc == 4 && !strcmp(argv[2], "run")) {
800 		printf("gc_urgent: start on %s for %d secs\n", argv[1], atoi(argv[3]));
801 		sprintf(command, "echo %d > %s/%s/gc_urgent", 1, "/sys/fs/f2fs/", argv[1]);
802 		if (system(command))
803 			exit(1);
804 		sleep(atoi(argv[3]));
805 		printf("gc_urgent: end on %s for %d secs\n", argv[1], atoi(argv[3]));
806 		sprintf(command, "echo %d > %s/%s/gc_urgent", 0, "/sys/fs/f2fs/", argv[1]);
807 		if (system(command))
808 			exit(1);
809 	} else {
810 		fputs("Excess arguments\n\n", stderr);
811 		fputs(cmd->cmd_help, stderr);
812 		exit(1);
813 	}
814 }
815 
816 #define defrag_file_desc "do defragment on file"
817 #define defrag_file_help						\
818 "f2fs_io defrag_file [start] [length] [file_path]\n\n"		\
819 "  start     : start offset of defragment region, unit: bytes\n"	\
820 "  length    : bytes number of defragment region\n"			\
821 
do_defrag_file(int argc,char ** argv,const struct cmd_desc * cmd)822 static void do_defrag_file(int argc, char **argv, const struct cmd_desc *cmd)
823 {
824 	struct f2fs_defragment df;
825 	u64 len;
826 	int ret, fd;
827 
828 	if (argc != 4) {
829 		fputs("Excess arguments\n\n", stderr);
830 		fputs(cmd->cmd_help, stderr);
831 		exit(1);
832 	}
833 
834 	df.start = atoll(argv[1]);
835 	df.len = len = atoll(argv[2]);
836 
837 	fd = xopen(argv[3], O_RDWR, 0);
838 
839 	ret = ioctl(fd, F2FS_IOC_DEFRAGMENT, &df);
840 	if (ret < 0)
841 		die_errno("F2FS_IOC_DEFRAGMENT failed");
842 
843 	printf("defrag %s in region[%"PRIu64", %"PRIu64"]\n",
844 			argv[3], df.start, df.start + len);
845 	exit(0);
846 }
847 
848 #define copy_desc "copy a file"
849 #define copy_help							\
850 "f2fs_io copy [-d] [-m] [-s] src_path dst_path\n\n"			\
851 "  src_path  : path to source file\n"					\
852 "  dst_path  : path to destination file\n"				\
853 "  -d        : use direct I/O\n"					\
854 "  -m        : mmap the source file\n"					\
855 "  -s        : use sendfile\n"						\
856 
do_copy(int argc,char ** argv,const struct cmd_desc * cmd)857 static void do_copy(int argc, char **argv, const struct cmd_desc *cmd)
858 {
859 	int c;
860 	int src_fd;
861 	int dst_fd;
862 	int open_flags = 0;
863 	bool mmap_source_file = false;
864 	bool use_sendfile = false;
865 	ssize_t ret;
866 
867 	while ((c = getopt(argc, argv, "dms")) != -1) {
868 		switch (c) {
869 		case 'd':
870 			open_flags |= O_DIRECT;
871 			break;
872 		case 'm':
873 			mmap_source_file = true;
874 			break;
875 		case 's':
876 			use_sendfile = true;
877 			break;
878 		default:
879 			fputs(cmd->cmd_help, stderr);
880 			exit(2);
881 		}
882 	}
883 	argc -= optind;
884 	argv += optind;
885 	if (argc != 2) {
886 		fputs("Wrong number of arguments\n\n", stderr);
887 		fputs(cmd->cmd_help, stderr);
888 		exit(2);
889 	}
890 	if (mmap_source_file && use_sendfile)
891 		die("-m and -s are mutually exclusive");
892 
893 	src_fd = xopen(argv[0], O_RDONLY | open_flags, 0);
894 	dst_fd = xopen(argv[1], O_WRONLY | O_CREAT | O_TRUNC | open_flags, 0644);
895 
896 	if (mmap_source_file) {
897 		struct stat stbuf;
898 		void *src_addr;
899 
900 		if (fstat(src_fd, &stbuf) != 0)
901 			die_errno("fstat of source file failed");
902 
903 		if ((size_t)stbuf.st_size != stbuf.st_size)
904 			die("Source file is too large");
905 
906 		src_addr = mmap(NULL, stbuf.st_size, PROT_READ, MAP_SHARED,
907 				src_fd, 0);
908 		if (src_addr == MAP_FAILED)
909 			die("mmap of source file failed");
910 
911 		full_write(dst_fd, src_addr, stbuf.st_size);
912 
913 		munmap(src_addr, stbuf.st_size);
914 	} else if (use_sendfile) {
915 		while ((ret = sendfile(dst_fd, src_fd, NULL, INT_MAX)) > 0)
916 			;
917 		if (ret < 0)
918 			die_errno("sendfile failed");
919 	} else {
920 		char *buf = aligned_xalloc(4096, 4096);
921 
922 		while ((ret = xread(src_fd, buf, 4096)) > 0)
923 			full_write(dst_fd, buf, ret);
924 		free(buf);
925 	}
926 	close(src_fd);
927 	close(dst_fd);
928 }
929 
930 #define get_cblocks_desc "get number of reserved blocks on compress inode"
931 #define get_cblocks_help "f2fs_io get_cblocks [file]\n\n"
932 
do_get_cblocks(int argc,char ** argv,const struct cmd_desc * cmd)933 static void do_get_cblocks(int argc, char **argv, const struct cmd_desc *cmd)
934 {
935 	unsigned long long blkcnt;
936 	int ret, fd;
937 
938 	if (argc != 2) {
939 		fputs("Excess arguments\n\n", stderr);
940 		fputs(cmd->cmd_help, stderr);
941 		exit(1);
942 	}
943 
944 	fd = xopen(argv[1], O_RDONLY, 0);
945 
946 	ret = ioctl(fd, F2FS_IOC_GET_COMPRESS_BLOCKS, &blkcnt);
947 	if (ret < 0)
948 		die_errno("F2FS_IOC_GET_COMPRESS_BLOCKS failed");
949 
950 	printf("%llu\n", blkcnt);
951 
952 	exit(0);
953 }
954 
955 #define release_cblocks_desc "release reserved blocks on compress inode"
956 #define release_cblocks_help "f2fs_io release_cblocks [file]\n\n"
957 
do_release_cblocks(int argc,char ** argv,const struct cmd_desc * cmd)958 static void do_release_cblocks(int argc, char **argv, const struct cmd_desc *cmd)
959 {
960 	unsigned long long blkcnt;
961 	int ret, fd;
962 
963 	if (argc != 2) {
964 		fputs("Excess arguments\n\n", stderr);
965 		fputs(cmd->cmd_help, stderr);
966 		exit(1);
967 	}
968 
969 	fd = xopen(argv[1], O_RDONLY, 0);
970 
971 	ret = ioctl(fd, F2FS_IOC_RELEASE_COMPRESS_BLOCKS, &blkcnt);
972 	if (ret < 0)
973 		die_errno("F2FS_IOC_RELEASE_COMPRESS_BLOCKS failed");
974 
975 	printf("%llu\n", blkcnt);
976 
977 	exit(0);
978 }
979 
980 #define reserve_cblocks_desc "reserve blocks on compress inode"
981 #define reserve_cblocks_help "f2fs_io reserve_cblocks [file]\n\n"
982 
do_reserve_cblocks(int argc,char ** argv,const struct cmd_desc * cmd)983 static void do_reserve_cblocks(int argc, char **argv, const struct cmd_desc *cmd)
984 {
985 	unsigned long long blkcnt;
986 	int ret, fd;
987 
988 	if (argc != 2) {
989 		fputs("Excess arguments\n\n", stderr);
990 		fputs(cmd->cmd_help, stderr);
991 		exit(1);
992 	}
993 
994 	fd = xopen(argv[1], O_RDONLY, 0);
995 
996 	ret = ioctl(fd, F2FS_IOC_RESERVE_COMPRESS_BLOCKS, &blkcnt);
997 	if (ret < 0)
998 		die_errno("F2FS_IOC_RESERVE_COMPRESS_BLOCKS failed");
999 
1000 	printf("%llu\n", blkcnt);
1001 
1002 	exit(0);
1003 }
1004 
1005 #define get_coption_desc "get compression option of a compressed file"
1006 #define get_coption_help						\
1007 "f2fs_io get_coption [file]\n\n"	\
1008 "  algorithm        : compression algorithm (0:lzo, 1: lz4, 2:zstd, 3:lzorle)\n"	\
1009 "  log_cluster_size : compression cluster log size (2 <= log_size <= 8)\n"
1010 
do_get_coption(int argc,char ** argv,const struct cmd_desc * cmd)1011 static void do_get_coption(int argc, char **argv, const struct cmd_desc *cmd)
1012 {
1013 	struct f2fs_comp_option option;
1014 	int ret, fd;
1015 
1016 	if (argc != 2) {
1017 		fputs("Excess arguments\n\n", stderr);
1018 		fputs(cmd->cmd_help, stderr);
1019 		exit(1);
1020 	}
1021 
1022 	fd = xopen(argv[1], O_RDONLY, 0);
1023 
1024 	ret = ioctl(fd, F2FS_IOC_GET_COMPRESS_OPTION, &option);
1025 	if (ret < 0)
1026 		die_errno("F2FS_IOC_GET_COMPRESS_OPTION failed");
1027 
1028 	printf("compression algorithm:%u\n", option.algorithm);
1029 	printf("compression cluster log size:%u\n", option.log_cluster_size);
1030 
1031 	exit(0);
1032 }
1033 
1034 #define set_coption_desc "set compression option of a compressed file"
1035 #define set_coption_help						\
1036 "f2fs_io set_coption [algorithm] [log_cluster_size] [file_path]\n\n"	\
1037 "  algorithm        : compression algorithm (0:lzo, 1: lz4, 2:zstd, 3:lzorle)\n"	\
1038 "  log_cluster_size : compression cluster log size (2 <= log_size <= 8)\n"
1039 
do_set_coption(int argc,char ** argv,const struct cmd_desc * cmd)1040 static void do_set_coption(int argc, char **argv, const struct cmd_desc *cmd)
1041 {
1042 	struct f2fs_comp_option option;
1043 	int fd, ret;
1044 
1045 	if (argc != 4) {
1046 		fputs("Excess arguments\n\n", stderr);
1047 		fputs(cmd->cmd_help, stderr);
1048 		exit(1);
1049 	}
1050 
1051 	option.algorithm = atoi(argv[1]);
1052 	option.log_cluster_size = atoi(argv[2]);
1053 
1054 	fd = xopen(argv[3], O_WRONLY, 0);
1055 
1056 	ret = ioctl(fd, F2FS_IOC_SET_COMPRESS_OPTION, &option);
1057 	if (ret < 0)
1058 		die_errno("F2FS_IOC_SET_COMPRESS_OPTION failed");
1059 
1060 	printf("set compression option: algorithm=%u, log_cluster_size=%u\n",
1061 			option.algorithm, option.log_cluster_size);
1062 	exit(0);
1063 }
1064 
1065 #define decompress_desc "decompress an already compressed file"
1066 #define decompress_help "f2fs_io decompress [file_path]\n\n"
1067 
do_decompress(int argc,char ** argv,const struct cmd_desc * cmd)1068 static void do_decompress(int argc, char **argv, const struct cmd_desc *cmd)
1069 {
1070 	int fd, ret;
1071 
1072 	if (argc != 2) {
1073 		fputs("Excess arguments\n\n", stderr);
1074 		fputs(cmd->cmd_help, stderr);
1075 		exit(1);
1076 	}
1077 
1078 	fd = xopen(argv[1], O_WRONLY, 0);
1079 
1080 	ret = ioctl(fd, F2FS_IOC_DECOMPRESS_FILE);
1081 	if (ret < 0)
1082 		die_errno("F2FS_IOC_DECOMPRESS_FILE failed");
1083 
1084 	exit(0);
1085 }
1086 
1087 #define compress_desc "compress a compression enabled file"
1088 #define compress_help "f2fs_io compress [file_path]\n\n"
1089 
do_compress(int argc,char ** argv,const struct cmd_desc * cmd)1090 static void do_compress(int argc, char **argv, const struct cmd_desc *cmd)
1091 {
1092 	int fd, ret;
1093 
1094 	if (argc != 2) {
1095 		fputs("Excess arguments\n\n", stderr);
1096 		fputs(cmd->cmd_help, stderr);
1097 		exit(1);
1098 	}
1099 
1100 	fd = xopen(argv[1], O_WRONLY, 0);
1101 
1102 	ret = ioctl(fd, F2FS_IOC_COMPRESS_FILE);
1103 	if (ret < 0)
1104 		die_errno("F2FS_IOC_COMPRESS_FILE failed");
1105 
1106 	exit(0);
1107 }
1108 
1109 #define get_filename_encrypt_mode_desc "get file name encrypt mode"
1110 #define get_filename_encrypt_mode_help					\
1111 "f2fs_io filename_encrypt_mode [file or directory path]\n\n"		\
1112 "Get the file name encription mode of the given file/directory.\n"	\
1113 
do_get_filename_encrypt_mode(int argc,char ** argv,const struct cmd_desc * cmd)1114 static void do_get_filename_encrypt_mode (int argc, char **argv,
1115 						const struct cmd_desc *cmd)
1116 {
1117 	static const char *enc_name[] = {
1118 		"invalid", /* FS_ENCRYPTION_MODE_INVALID (0) */
1119 		"aes-256-xts", /* FS_ENCRYPTION_MODE_AES_256_XTS (1) */
1120 		"aes-256-gcm", /* FS_ENCRYPTION_MODE_AES_256_GCM (2) */
1121 		"aes-256-cbc", /* FS_ENCRYPTION_MODE_AES_256_CBC (3) */
1122 		"aes-256-cts", /* FS_ENCRYPTION_MODE_AES_256_CTS (4) */
1123 		"aes-128-cbc", /* FS_ENCRYPTION_MODE_AES_128_CBC (5) */
1124 		"aes-128-cts", /* FS_ENCRYPTION_MODE_AES_128_CTS (6) */
1125 		"speck128-256-xts", /* FS_ENCRYPTION_MODE_SPECK128_256_XTS (7) */
1126 		"speck128-256-cts", /* FS_ENCRYPTION_MODE_SPECK128_256_CTS (8) */
1127 		"adiantum", /* FS_ENCRYPTION_MODE_ADIANTUM (9) */
1128 	};
1129 	int fd, mode, ret;
1130 	struct fscrypt_get_policy_ex_arg arg;
1131 
1132 	if (argc != 2) {
1133 		fputs("Excess arguments\n\n", stderr);
1134 		fputs(cmd->cmd_help, stderr);
1135 		exit(1);
1136 	}
1137 
1138 	fd = xopen(argv[1], O_RDONLY, 0);
1139 	arg.policy_size = sizeof(arg.policy);
1140 	ret = ioctl(fd, FS_IOC_GET_ENCRYPTION_POLICY_EX, &arg);
1141 	if (ret != 0 && errno == ENOTTY)
1142 		ret = ioctl(fd, FS_IOC_GET_ENCRYPTION_POLICY, arg.policy.v1);
1143 	close(fd);
1144 
1145 	if (ret) {
1146 		perror("FS_IOC_GET_ENCRYPTION_POLICY|_EX");
1147 		exit(1);
1148 	}
1149 
1150 	switch (arg.policy.version) {
1151 	case FSCRYPT_POLICY_V1:
1152 		mode = arg.policy.v1.filenames_encryption_mode;
1153 		break;
1154 	case FSCRYPT_POLICY_V2:
1155 		mode = arg.policy.v2.filenames_encryption_mode;
1156 		break;
1157 	default:
1158 		printf("Do not support policy version: %d\n",
1159 							arg.policy.version);
1160 		exit(1);
1161 	}
1162 
1163 	if (mode >= sizeof(enc_name)/sizeof(enc_name[0])) {
1164 		printf("Do not support algorithm: %d\n", mode);
1165 		exit(1);
1166 	}
1167 	printf ("%s\n", enc_name[mode]);
1168 	exit(0);
1169 }
1170 
1171 #define CMD_HIDDEN 	0x0001
1172 #define CMD(name) { #name, do_##name, name##_desc, name##_help, 0 }
1173 #define _CMD(name) { #name, do_##name, NULL, NULL, CMD_HIDDEN }
1174 
1175 static void do_help(int argc, char **argv, const struct cmd_desc *cmd);
1176 const struct cmd_desc cmd_list[] = {
1177 	_CMD(help),
1178 	CMD(fsync),
1179 	CMD(set_verity),
1180 	CMD(getflags),
1181 	CMD(setflags),
1182 	CMD(shutdown),
1183 	CMD(pinfile),
1184 	CMD(fallocate),
1185 	CMD(erase),
1186 	CMD(write),
1187 	CMD(read),
1188 	CMD(randread),
1189 	CMD(fiemap),
1190 	CMD(gc_urgent),
1191 	CMD(defrag_file),
1192 	CMD(copy),
1193 	CMD(get_cblocks),
1194 	CMD(release_cblocks),
1195 	CMD(reserve_cblocks),
1196 	CMD(get_coption),
1197 	CMD(set_coption),
1198 	CMD(decompress),
1199 	CMD(compress),
1200 	CMD(get_filename_encrypt_mode),
1201 	{ NULL, NULL, NULL, NULL, 0 }
1202 };
1203 
do_help(int argc,char ** argv,const struct cmd_desc * UNUSED (cmd))1204 static void do_help(int argc, char **argv, const struct cmd_desc *UNUSED(cmd))
1205 {
1206 	const struct cmd_desc *p;
1207 
1208 	if (argc > 1) {
1209 		for (p = cmd_list; p->cmd_name; p++) {
1210 			if (p->cmd_flags & CMD_HIDDEN)
1211 				continue;
1212 			if (strcmp(p->cmd_name, argv[1]) == 0) {
1213 				putc('\n', stdout);
1214 				fputs("USAGE:\n  ", stdout);
1215 				fputs(p->cmd_help, stdout);
1216 				exit(0);
1217 			}
1218 		}
1219 		printf("Unknown command: %s\n\n", argv[1]);
1220 	}
1221 
1222 	fputs("Available commands:\n", stdout);
1223 	for (p = cmd_list; p->cmd_name; p++) {
1224 		if (p->cmd_flags & CMD_HIDDEN)
1225 			continue;
1226 		printf("  %-20s %s\n", p->cmd_name, p->cmd_desc);
1227 	}
1228 	printf("\nTo get more information on a command, "
1229 	       "type 'f2fs_io help cmd'\n");
1230 	exit(0);
1231 }
1232 
die_signal_handler(int UNUSED (signum),siginfo_t * UNUSED (siginfo),void * UNUSED (context))1233 static void die_signal_handler(int UNUSED(signum), siginfo_t *UNUSED(siginfo),
1234 				void *UNUSED(context))
1235 {
1236 	exit(-1);
1237 }
1238 
sigcatcher_setup(void)1239 static void sigcatcher_setup(void)
1240 {
1241 	struct sigaction	sa;
1242 
1243 	memset(&sa, 0, sizeof(struct sigaction));
1244 	sa.sa_sigaction = die_signal_handler;
1245 	sa.sa_flags = SA_SIGINFO;
1246 
1247 	sigaction(SIGHUP, &sa, 0);
1248 	sigaction(SIGINT, &sa, 0);
1249 	sigaction(SIGQUIT, &sa, 0);
1250 	sigaction(SIGFPE, &sa, 0);
1251 	sigaction(SIGILL, &sa, 0);
1252 	sigaction(SIGBUS, &sa, 0);
1253 	sigaction(SIGSEGV, &sa, 0);
1254 	sigaction(SIGABRT, &sa, 0);
1255 	sigaction(SIGPIPE, &sa, 0);
1256 	sigaction(SIGALRM, &sa, 0);
1257 	sigaction(SIGTERM, &sa, 0);
1258 	sigaction(SIGUSR1, &sa, 0);
1259 	sigaction(SIGUSR2, &sa, 0);
1260 	sigaction(SIGPOLL, &sa, 0);
1261 	sigaction(SIGPROF, &sa, 0);
1262 	sigaction(SIGSYS, &sa, 0);
1263 	sigaction(SIGTRAP, &sa, 0);
1264 	sigaction(SIGVTALRM, &sa, 0);
1265 	sigaction(SIGXCPU, &sa, 0);
1266 	sigaction(SIGXFSZ, &sa, 0);
1267 }
1268 
main(int argc,char ** argv)1269 int main(int argc, char **argv)
1270 {
1271 	const struct cmd_desc *cmd;
1272 
1273 	if (argc < 2)
1274 		do_help(argc, argv, cmd_list);
1275 
1276 	sigcatcher_setup();
1277 	for (cmd = cmd_list; cmd->cmd_name; cmd++) {
1278 		if (strcmp(cmd->cmd_name, argv[1]) == 0) {
1279 			cmd->cmd_func(argc - 1, argv + 1, cmd);
1280 			exit(0);
1281 		}
1282 	}
1283 	printf("Unknown command: %s\n\n", argv[1]);
1284 	do_help(1, argv, cmd_list);
1285 	return 0;
1286 }
1287