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 <stdio.h>
23 #include <fcntl.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
27 #include <time.h>
28 #include <inttypes.h>
29 #include <string.h>
30 #include <signal.h>
31 #include <termios.h>
32 #include <sys/types.h>
33 #include <sys/stat.h>
34
35 #ifdef HAVE_CONFIG_H
36 #include "config.h"
37 #endif
38 #include "f2fs_io.h"
39
40 struct cmd_desc {
41 const char *cmd_name;
42 void (*cmd_func)(int, char **, const struct cmd_desc *);
43 const char *cmd_desc;
44 const char *cmd_help;
45 int cmd_flags;
46 };
47
48 #define shutdown_desc "shutdown filesystem"
49 #define shutdown_help \
50 "f2fs_io shutdown [level] [dir]\n\n" \
51 "Freeze and stop all IOs given mount point\n" \
52 "level can be\n" \
53 " 0 : going down with full sync\n" \
54 " 1 : going down with checkpoint only\n" \
55 " 2 : going down with no sync\n" \
56 " 3 : going down with metadata flush\n" \
57 " 4 : going down with fsck mark\n"
58
do_shutdown(int argc,char ** argv,const struct cmd_desc * cmd)59 static void do_shutdown(int argc, char **argv, const struct cmd_desc *cmd)
60 {
61 u32 flag;
62 int ret, fd;
63
64 if (argc != 3) {
65 fputs("Excess arguments\n\n", stderr);
66 fputs(cmd->cmd_help, stderr);
67 exit(1);
68 }
69
70 flag = atoi(argv[1]);
71 if (flag >= F2FS_GOING_DOWN_MAX) {
72 fputs("Wrong level\n\n", stderr);
73 fputs(cmd->cmd_help, stderr);
74 exit(1);
75 }
76 fd = open(argv[2], O_RDONLY);
77 if (fd == -1) {
78 fputs("Open failed\n\n", stderr);
79 fputs(cmd->cmd_help, stderr);
80 exit(1);
81 }
82
83 ret = ioctl(fd, F2FS_IOC_SHUTDOWN, &flag);
84 if (ret < 0) {
85 perror("F2FS_IOC_SHUTDOWN");
86 exit(1);
87 }
88 printf("Shutdown %s with level=%d\n", argv[2], flag);
89 exit(0);
90 }
91
92 #define pinfile_desc "pin file control"
93 #define pinfile_help \
94 "f2fs_io pinfile [get|set] [file]\n\n" \
95 "get/set pinning given the file\n" \
96
do_pinfile(int argc,char ** argv,const struct cmd_desc * cmd)97 static void do_pinfile(int argc, char **argv, const struct cmd_desc *cmd)
98 {
99 u32 pin;
100 int ret, fd;
101
102 if (argc != 3) {
103 fputs("Excess arguments\n\n", stderr);
104 fputs(cmd->cmd_help, stderr);
105 exit(1);
106 }
107
108 fd = open(argv[2], O_RDWR);
109 if (fd == -1) {
110 fputs("Open failed\n\n", stderr);
111 fputs(cmd->cmd_help, stderr);
112 exit(1);
113 }
114
115 ret = -1;
116 if (!strcmp(argv[1], "set")) {
117 pin = 1;
118 ret = ioctl(fd, F2FS_IOC_SET_PIN_FILE, &pin);
119 if (ret != 0) {
120 perror("set_pin_file failed");
121 exit(1);
122 }
123 printf("set_pin_file: %u blocks moved in %s\n", ret, argv[2]);
124 } else if (!strcmp(argv[1], "get")) {
125 unsigned int flags;
126
127 ret = ioctl(fd, F2FS_IOC_GET_PIN_FILE, &pin);
128 if (ret < 0) {
129 perror("pin_file failed");
130 exit(1);
131 }
132 ret = ioctl(fd, F2FS_IOC_GETFLAGS, &flags);
133 if (ret < 0) {
134 perror("get flags failed");
135 exit(1);
136 }
137 printf("get_pin_file: %s with %u blocks moved in %s\n",
138 (flags & F2FS_NOCOW_FL) ? "pinned" : "un-pinned",
139 pin, argv[2]);
140 }
141 exit(0);
142 }
143
144 #define write_desc "write data into file"
145 #define write_help \
146 "f2fs_io write [chunk_size in 4kb] [offset in chunk_size] [count] [pattern] [IO] [file_path]\n\n" \
147 "Write given patten data in file_path\n" \
148 "pattern can be\n" \
149 " zero : zeros\n" \
150 " inc_num : incrementing numbers\n" \
151 " rand : random numbers\n" \
152 "IO can be\n" \
153 " buffered : buffered IO\n" \
154 " dio : direct IO\n" \
155
do_write(int argc,char ** argv,const struct cmd_desc * cmd)156 static void do_write(int argc, char **argv, const struct cmd_desc *cmd)
157 {
158 u64 buf_size = 0, inc_num = 0, ret = 0, written = 0;
159 u64 offset;
160 char *buf = NULL;
161 unsigned bs, count, i;
162 int flags = 0;
163 int fd;
164
165 srand(time(0));
166
167 if (argc != 7) {
168 fputs("Excess arguments\n\n", stderr);
169 fputs(cmd->cmd_help, stderr);
170 exit(1);
171 }
172
173 bs = atoi(argv[1]);
174 if (bs > 1024) {
175 fputs("Too big chunk size - limit: 4MB\n\n", stderr);
176 exit(1);
177 }
178 buf_size = bs * 4096;
179
180 offset = atoi(argv[2]) * buf_size;
181
182 buf = aligned_alloc(4096, buf_size);
183 if (!buf) {
184 fputs("Memory alloc failed\n\n", stderr);
185 exit(1);
186 }
187 count = atoi(argv[3]);
188
189 if (!strcmp(argv[4], "zero")) {
190 memset(buf, 0, buf_size);
191 } else if (strcmp(argv[4], "inc_num") &&
192 strcmp(argv[4], "rand")) {
193 fputs("Wrong pattern type\n\n", stderr);
194 exit(1);
195 }
196
197 if (!strcmp(argv[5], "buffered")) {
198 flags |= O_DIRECT;
199 } else if (strcmp(argv[5], "dio")) {
200 fputs("Wrong IO type\n\n", stderr);
201 exit(1);
202 }
203
204 fd = open(argv[6], O_CREAT | O_WRONLY | flags, 0755);
205 if (fd == -1) {
206 fputs("Open failed\n\n", stderr);
207 exit(1);
208 }
209
210 for (i = 0; i < count; i++) {
211 if (!strcmp(argv[4], "inc_num"))
212 *(int *)buf = inc_num++;
213 else if (!strcmp(argv[4], "rand"))
214 *(int *)buf = rand();
215
216 /* write data */
217 ret = pwrite(fd, buf, buf_size, offset + buf_size * i);
218 if (ret != buf_size)
219 break;
220 written += ret;
221 }
222
223 printf("Written %"PRIu64" bytes with pattern=%s\n", written, argv[4]);
224 exit(0);
225 }
226
227 #define read_desc "read data from file"
228 #define read_help \
229 "f2fs_io read [chunk_size in 4kb] [offset in chunk_size] [count] [IO] [print_nbytes] [file_path]\n\n" \
230 "Read data in file_path and print nbytes\n" \
231 "IO can be\n" \
232 " buffered : buffered IO\n" \
233 " dio : direct IO\n" \
234
do_read(int argc,char ** argv,const struct cmd_desc * cmd)235 static void do_read(int argc, char **argv, const struct cmd_desc *cmd)
236 {
237 u64 buf_size = 0, ret = 0, read_cnt = 0;
238 u64 offset;
239 char *buf = NULL;
240 char *print_buf = NULL;
241 unsigned bs, count, i, print_bytes;
242 int flags = 0;
243 int fd;
244
245 if (argc != 7) {
246 fputs("Excess arguments\n\n", stderr);
247 fputs(cmd->cmd_help, stderr);
248 exit(1);
249 }
250
251 bs = atoi(argv[1]);
252 if (bs > 1024) {
253 fputs("Too big chunk size - limit: 4MB\n\n", stderr);
254 exit(1);
255 }
256 buf_size = bs * 4096;
257
258 offset = atoi(argv[2]) * buf_size;
259
260 buf = aligned_alloc(4096, buf_size);
261 if (!buf) {
262 fputs("Memory alloc failed\n\n", stderr);
263 exit(1);
264 }
265 count = atoi(argv[3]);
266 if (!strcmp(argv[4], "buffered")) {
267 flags |= O_DIRECT;
268 } else if (strcmp(argv[4], "dio")) {
269 fputs("Wrong IO type\n\n", stderr);
270 exit(1);
271 }
272
273 print_bytes = atoi(argv[5]);
274 if (print_bytes > buf_size) {
275 fputs("Print_nbytes should be less then chunk_size in kb\n\n", stderr);
276 exit(1);
277 }
278 print_buf = malloc(print_bytes);
279 if (!print_buf) {
280 fputs("Memory alloc failed\n\n", stderr);
281 exit(1);
282 }
283
284 fd = open(argv[6], O_RDONLY | flags);
285 if (fd == -1) {
286 fputs("Open failed\n\n", stderr);
287 exit(1);
288 }
289
290 for (i = 0; i < count; i++) {
291 ret = pread(fd, buf, buf_size, offset + buf_size * i);
292 if (ret != buf_size)
293 break;
294
295 read_cnt += ret;
296 if (i == 0)
297 memcpy(print_buf, buf, print_bytes);
298 }
299 printf("Read %"PRIu64" bytes and print %u bytes:\n", read_cnt, print_bytes);
300 printf("%08"PRIx64" : ", offset);
301 for (i = 1; i <= print_bytes; i++) {
302 printf("%02x", print_buf[i - 1]);
303 if (i % 16 == 0)
304 printf("\n%08"PRIx64" : ", offset + 16 * i);
305 else if (i % 2 == 0)
306 printf(" ");
307 }
308 printf("\n");
309 exit(0);
310 }
311
312 struct file_ext {
313 __u32 f_pos;
314 __u32 start_blk;
315 __u32 end_blk;
316 __u32 blk_count;
317 };
318
319 #ifndef FIBMAP
320 #define FIBMAP _IO(0x00, 1) /* bmap access */
321 #endif
322
323 #define fiemap_desc "get block address in file"
324 #define fiemap_help \
325 "f2fs_io fiemap [offset in 4kb] [count] [file_path]\n\n"\
326
do_fiemap(int argc,char ** argv,const struct cmd_desc * cmd)327 static void do_fiemap(int argc, char **argv, const struct cmd_desc *cmd)
328 {
329 u64 offset;
330 u32 blknum;
331 unsigned count, i;
332 int fd;
333
334 if (argc != 4) {
335 fputs("Excess arguments\n\n", stderr);
336 fputs(cmd->cmd_help, stderr);
337 exit(1);
338 }
339
340 offset = atoi(argv[1]);
341 count = atoi(argv[2]);
342
343 fd = open(argv[3], O_RDONLY | O_LARGEFILE);
344 if (fd == -1) {
345 fputs("Open failed\n\n", stderr);
346 exit(1);
347 }
348
349 printf("Fiemap: offset = %08"PRIx64" len = %d\n", offset, count);
350 for (i = 0; i < count; i++) {
351 blknum = offset + i;
352
353 if (ioctl(fd, FIBMAP, &blknum) < 0) {
354 fputs("FIBMAP failed\n\n", stderr);
355 exit(1);
356 }
357 printf("%u ", blknum);
358 }
359 printf("\n");
360 exit(0);
361 }
362
363 #define gc_urgent_desc "start/end/run gc_urgent for given time period"
364 #define gc_urgent_help \
365 "f2fs_io gc_urgent $dev [start/end/run] [time in sec]\n\n"\
366 " - f2fs_io gc_urgent sda21 start\n" \
367 " - f2fs_io gc_urgent sda21 end\n" \
368 " - f2fs_io gc_urgent sda21 run 10\n" \
369
do_gc_urgent(int argc,char ** argv,const struct cmd_desc * cmd)370 static void do_gc_urgent(int argc, char **argv, const struct cmd_desc *cmd)
371 {
372 char command[255];
373
374 if (argc == 3 && !strcmp(argv[2], "start")) {
375 printf("gc_urgent: start on %s\n", argv[1]);
376 sprintf(command, "echo %d > %s/%s/gc_urgent", 1, "/sys/fs/f2fs/", argv[1]);
377 if (system(command))
378 exit(1);
379 } else if (argc == 3 && !strcmp(argv[2], "end")) {
380 printf("gc_urgent: end on %s\n", argv[1]);
381 sprintf(command, "echo %d > %s/%s/gc_urgent", 0, "/sys/fs/f2fs/", argv[1]);
382 if (system(command))
383 exit(1);
384 } else if (argc == 4 && !strcmp(argv[2], "run")) {
385 printf("gc_urgent: start on %s for %d secs\n", argv[1], atoi(argv[3]));
386 sprintf(command, "echo %d > %s/%s/gc_urgent", 1, "/sys/fs/f2fs/", argv[1]);
387 if (system(command))
388 exit(1);
389 sleep(atoi(argv[3]));
390 printf("gc_urgent: end on %s for %d secs\n", argv[1], atoi(argv[3]));
391 sprintf(command, "echo %d > %s/%s/gc_urgent", 0, "/sys/fs/f2fs/", argv[1]);
392 if (system(command))
393 exit(1);
394 } else {
395 fputs("Excess arguments\n\n", stderr);
396 fputs(cmd->cmd_help, stderr);
397 exit(1);
398 }
399 }
400
401 #define CMD_HIDDEN 0x0001
402 #define CMD(name) { #name, do_##name, name##_desc, name##_help, 0 }
403 #define _CMD(name) { #name, do_##name, NULL, NULL, CMD_HIDDEN }
404
405 static void do_help(int argc, char **argv, const struct cmd_desc *cmd);
406 const struct cmd_desc cmd_list[] = {
407 _CMD(help),
408 CMD(shutdown),
409 CMD(pinfile),
410 CMD(write),
411 CMD(read),
412 CMD(fiemap),
413 CMD(gc_urgent),
414 { NULL, NULL, NULL, NULL, 0 }
415 };
416
do_help(int argc,char ** argv,const struct cmd_desc * UNUSED (cmd))417 static void do_help(int argc, char **argv, const struct cmd_desc *UNUSED(cmd))
418 {
419 const struct cmd_desc *p;
420
421 if (argc > 1) {
422 for (p = cmd_list; p->cmd_name; p++) {
423 if (p->cmd_flags & CMD_HIDDEN)
424 continue;
425 if (strcmp(p->cmd_name, argv[1]) == 0) {
426 putc('\n', stdout);
427 fputs("USAGE:\n ", stdout);
428 fputs(p->cmd_help, stdout);
429 exit(0);
430 }
431 }
432 printf("Unknown command: %s\n\n", argv[1]);
433 }
434
435 fputs("Available commands:\n", stdout);
436 for (p = cmd_list; p->cmd_name; p++) {
437 if (p->cmd_flags & CMD_HIDDEN)
438 continue;
439 printf(" %-20s %s\n", p->cmd_name, p->cmd_desc);
440 }
441 printf("\nTo get more information on a command, "
442 "type 'f2fs_io help cmd'\n");
443 exit(0);
444 }
445
die_signal_handler(int UNUSED (signum),siginfo_t * UNUSED (siginfo),void * UNUSED (context))446 static void die_signal_handler(int UNUSED(signum), siginfo_t *UNUSED(siginfo),
447 void *UNUSED(context))
448 {
449 exit(-1);
450 }
451
sigcatcher_setup(void)452 static void sigcatcher_setup(void)
453 {
454 struct sigaction sa;
455
456 memset(&sa, 0, sizeof(struct sigaction));
457 sa.sa_sigaction = die_signal_handler;
458 sa.sa_flags = SA_SIGINFO;
459
460 sigaction(SIGHUP, &sa, 0);
461 sigaction(SIGINT, &sa, 0);
462 sigaction(SIGQUIT, &sa, 0);
463 sigaction(SIGFPE, &sa, 0);
464 sigaction(SIGILL, &sa, 0);
465 sigaction(SIGBUS, &sa, 0);
466 sigaction(SIGSEGV, &sa, 0);
467 sigaction(SIGABRT, &sa, 0);
468 sigaction(SIGPIPE, &sa, 0);
469 sigaction(SIGALRM, &sa, 0);
470 sigaction(SIGTERM, &sa, 0);
471 sigaction(SIGUSR1, &sa, 0);
472 sigaction(SIGUSR2, &sa, 0);
473 sigaction(SIGPOLL, &sa, 0);
474 sigaction(SIGPROF, &sa, 0);
475 sigaction(SIGSYS, &sa, 0);
476 sigaction(SIGTRAP, &sa, 0);
477 sigaction(SIGVTALRM, &sa, 0);
478 sigaction(SIGXCPU, &sa, 0);
479 sigaction(SIGXFSZ, &sa, 0);
480 }
481
main(int argc,char ** argv)482 int main(int argc, char **argv)
483 {
484 const struct cmd_desc *cmd;
485
486 if (argc < 2)
487 do_help(argc, argv, cmd_list);
488
489 sigcatcher_setup();
490 for (cmd = cmd_list; cmd->cmd_name; cmd++) {
491 if (strcmp(cmd->cmd_name, argv[1]) == 0) {
492 cmd->cmd_func(argc - 1, argv + 1, cmd);
493 exit(0);
494 }
495 }
496 printf("Unknown command: %s\n\n", argv[1]);
497 do_help(1, argv, cmd_list);
498 return 0;
499 }
500