Raise cmake_minimum_required: 2.8 -> 3.4
[lwext4.git] / fs_test / lwext4_server.c
1 /*
2  * Copyright (c) 2014 Grzegorz Kostka (kostka.grzegorz@gmail.com)
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * - Redistributions of source code must retain the above copyright
10  *   notice, this list of conditions and the following disclaimer.
11  * - Redistributions in binary form must reproduce the above copyright
12  *   notice, this list of conditions and the following disclaimer in the
13  *   documentation and/or other materials provided with the distribution.
14  * - The name of the author may not be used to endorse or promote products
15  *   derived from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <unistd.h>
32 #include <string.h>
33 #include <stdint.h>
34 #include <stdbool.h>
35 #include <getopt.h>
36 #include <time.h>
37 #include <inttypes.h>
38 #include <sys/time.h>
39
40 #ifdef WIN32
41 #include <winsock2.h>
42 #include <ws2tcpip.h>
43 #include <windows.h>
44 #else
45 #include <sys/socket.h>
46 #include <netinet/in.h>
47 #include <arpa/inet.h>
48 #include <sys/types.h>
49 #endif
50
51 #include <ext4.h>
52 #include "../blockdev/linux/file_dev.h"
53 #include "../blockdev/windows/file_windows.h"
54
55
56 static int winsock_init(void);
57 static void winsock_fini(void);
58 static char *entry_to_str(uint8_t type);
59
60 #define MAX_FILES 64
61 #define MAX_DIRS 64
62
63 #define MAX_RW_BUFFER (1024 * 1024)
64 #define RW_BUFFER_PATERN ('x')
65
66 /**@brief   Default connection port*/
67 static int connection_port = 1234;
68
69 /**@brief   Default filesystem filename.*/
70 static char *ext4_fname = "ext2";
71
72 /**@brief   Verbose mode*/
73 static bool verbose = false;
74
75 /**@brief   Winpart mode*/
76 static bool winpart = false;
77
78 /**@brief   Blockdev handle*/
79 static struct ext4_blockdev *bd;
80
81 static bool cache_wb = false;
82
83 static char read_buffer[MAX_RW_BUFFER];
84 static char write_buffer[MAX_RW_BUFFER];
85
86 static const char *usage = "                                    \n\
87 Welcome in lwext4_server.                                       \n\
88 Copyright (c) 2013 Grzegorz Kostka (kostka.grzegorz@gmail.com)  \n\
89 Usage:                                                          \n\
90     --image     (-i) - ext2/3/4 image file                      \n\
91     --port      (-p) - server port                              \n\
92     --verbose   (-v) - verbose mode                             \n\
93     --winpart   (-w) - windows_partition mode                   \n\
94     --cache_wb  (-c) - cache writeback_mode                     \n\
95 \n";
96
97 /**@brief   Open file instance descriptor.*/
98 struct lwext4_files {
99         char name[255];
100         ext4_file fd;
101 };
102
103 /**@brief   Open directory instance descriptor.*/
104 struct lwext4_dirs {
105         char name[255];
106         ext4_dir fd;
107 };
108
109 /**@brief   Library call opcode.*/
110 struct lwext4_op_codes {
111         char *func;
112 };
113
114 /**@brief   Library call wraper.*/
115 struct lwext4_call {
116         int (*lwext4_call)(const char *p);
117 };
118
119 /**@brief  */
120 static struct lwext4_files file_tab[MAX_FILES];
121
122 /**@brief  */
123 static struct lwext4_dirs dir_tab[MAX_DIRS];
124
125 /**@brief  */
126 static struct lwext4_op_codes op_codes[] = {
127     "device_register",
128     "mount",
129     "umount",
130     "mount_point_stats",
131     "cache_write_back",
132     "fremove",
133     "fopen",
134     "fclose",
135     "fread",
136     "fwrite",
137     "fseek",
138     "ftell",
139     "fsize",
140     "dir_rm",
141     "dir_mk",
142     "dir_open",
143     "dir_close",
144     "dir_entry_get",
145
146     "multi_fcreate",
147     "multi_fwrite",
148     "multi_fread",
149     "multi_fremove",
150     "multi_dcreate",
151     "multi_dremove",
152     "stats_save",
153     "stats_check",
154 };
155
156 static int device_register(const char *p);
157 static int mount(const char *p);
158 static int umount(const char *p);
159 static int mount_point_stats(const char *p);
160 static int cache_write_back(const char *p);
161 static int fremove(const char *p);
162 static int file_open(const char *p);
163 static int file_close(const char *p);
164 static int file_read(const char *p);
165 static int file_write(const char *p);
166 static int file_seek(const char *p);
167 static int file_tell(const char *p);
168 static int file_size(const char *p);
169 static int dir_rm(const char *p);
170 static int dir_mk(const char *p);
171 static int dir_open(const char *p);
172 static int dir_close(const char *p);
173 static int dir_close(const char *p);
174 static int dir_entry_get(const char *p);
175
176 static int multi_fcreate(const char *p);
177 static int multi_fwrite(const char *p);
178 static int multi_fread(const char *p);
179 static int multi_fremove(const char *p);
180 static int multi_dcreate(const char *p);
181 static int multi_dremove(const char *p);
182 static int stats_save(const char *p);
183 static int stats_check(const char *p);
184
185 /**@brief  */
186 static struct lwext4_call op_call[] = {
187     device_register,   /*PARAMS(3):   0 cache_mode dev_name   */
188     mount,              /*PARAMS(2):   dev_name mount_point    */
189     umount,             /*PARAMS(1):   mount_point             */
190     mount_point_stats, /*PARAMS(2):   mount_point, 0          */
191     cache_write_back,  /*PARAMS(2):   mount_point, en         */
192     fremove,            /*PARAMS(1):   path                    */
193     file_open,          /*PARAMS(2):   fid path flags          */
194     file_close,         /*PARAMS(1):   fid                     */
195     file_read,          /*PARAMS(4):   fid 0 len 0             */
196     file_write,         /*PARAMS(4):   fid 0 len 0             */
197     file_seek,          /*PARAMS(2):   fid off origin          */
198     file_tell,          /*PARAMS(2):   fid exp                 */
199     file_size,          /*PARAMS(2):   fid exp                 */
200     dir_rm,             /*PARAMS(1):   path                    */
201     dir_mk,             /*PARAMS(1):   path                    */
202     dir_open,           /*PARAMS(2):   did, path               */
203     dir_close,          /*PARAMS(1):   did                     */
204     dir_entry_get,     /*PARAMS(2):   did, exp                */
205
206     multi_fcreate, /*PARAMS(3):   path prefix cnt         */
207     multi_fwrite,  /*PARAMS(4):   path prefix cnt size    */
208     multi_fread,   /*PARAMS(4):   path prefix cnt size    */
209     multi_fremove, /*PARAMS(2):   path prefix cnt         */
210     multi_dcreate, /*PARAMS(3):   path prefix cnt         */
211     multi_dremove, /*PARAMS(2):   path prefix             */
212     stats_save,    /*PARAMS(1):   path                    */
213     stats_check,   /*PARAMS(1):   path                    */
214 };
215
216 static clock_t get_ms(void)
217 {
218         struct timeval t;
219         gettimeofday(&t, NULL);
220         return (t.tv_sec * 1000) + (t.tv_usec / 1000);
221 }
222
223 /**@brief  */
224 static int exec_op_code(const char *opcode)
225 {
226         int i;
227         int r = -1;
228
229         for (i = 0; i < sizeof(op_codes) / sizeof(op_codes[0]); ++i) {
230
231                 if (strncmp(op_codes[i].func, opcode, strlen(op_codes[i].func)))
232                         continue;
233
234                 if (opcode[strlen(op_codes[i].func)] != ' ')
235                         continue;
236
237                 printf("%s\n", opcode);
238                 opcode += strlen(op_codes[i].func);
239                 /*Call*/
240
241                 clock_t t = get_ms();
242                 r = op_call[i].lwext4_call(opcode);
243
244                 printf("rc: %d, time: %ums\n", r, (unsigned int)(get_ms() - t));
245
246                 break;
247         }
248
249         return r;
250 }
251
252 static int server_open(void)
253 {
254         int fd = 0;
255         struct sockaddr_in serv_addr;
256
257         memset(&serv_addr, 0, sizeof(serv_addr));
258
259         if (winsock_init() < 0) {
260                 printf("winsock_init() error\n");
261                 exit(-1);
262         }
263
264         fd = socket(AF_INET, SOCK_STREAM, 0);
265         if (fd < 0) {
266                 printf("socket() error: %s\n", strerror(errno));
267                 exit(-1);
268         }
269
270         int yes = 1;
271         if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&yes,
272                        sizeof(int))) {
273                 printf("setsockopt() error: %s\n", strerror(errno));
274                 exit(-1);
275         }
276
277         serv_addr.sin_family = AF_INET;
278         serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
279         serv_addr.sin_port = htons(connection_port);
280
281         if (bind(fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr))) {
282                 printf("bind() error: %s\n", strerror(errno));
283                 exit(-1);
284         }
285
286         if (listen(fd, 1)) {
287                 printf("listen() error: %s\n", strerror(errno));
288                 exit(-1);
289         }
290
291         return fd;
292 }
293
294 static bool parse_opt(int argc, char **argv)
295 {
296         int option_index = 0;
297         int c;
298
299         static struct option long_options[] = {
300             {"image", required_argument, 0, 'i'},
301             {"port", required_argument, 0, 'p'},
302             {"verbose", no_argument, 0, 'v'},
303             {"winpart", no_argument, 0, 'w'},
304             {"cache_wb", no_argument, 0, 'c'},
305             {"version", no_argument, 0, 'x'},
306             {0, 0, 0, 0}};
307
308         while (-1 != (c = getopt_long(argc, argv, "i:p:vcwx", long_options,
309                                       &option_index))) {
310
311                 switch (c) {
312                 case 'i':
313                         ext4_fname = optarg;
314                         break;
315                 case 'p':
316                         connection_port = atoi(optarg);
317                         break;
318                 case 'v':
319                         verbose = true;
320                         break;
321                 case 'c':
322                         cache_wb = true;
323                         break;
324                 case 'w':
325                         winpart = true;
326                         break;
327                 case 'x':
328                         puts(VERSION);
329                         exit(0);
330                         break;
331                 default:
332                         printf("%s", usage);
333                         return false;
334                 }
335         }
336         return true;
337 }
338
339 int main(int argc, char *argv[])
340 {
341         int n;
342         int listenfd;
343         int connfd;
344         char op_code[128];
345
346         if (!parse_opt(argc, argv))
347                 return -1;
348
349         listenfd = server_open();
350
351         printf("lwext4_server: listening on port: %d\n", connection_port);
352
353         memset(write_buffer, RW_BUFFER_PATERN, MAX_RW_BUFFER);
354         while (1) {
355                 connfd = accept(listenfd, (struct sockaddr *)NULL, NULL);
356
357                 n = recv(connfd, op_code, sizeof(op_code), 0);
358
359                 if (n < 0) {
360                         printf("recv() error: %s fd = %d\n", strerror(errno),
361                                connfd);
362                         break;
363                 }
364
365                 op_code[n] = 0;
366
367                 int r = exec_op_code(op_code);
368
369                 n = send(connfd, (void *)&r, sizeof(r), 0);
370                 if (n < 0) {
371                         printf("send() error: %s fd = %d\n", strerror(errno),
372                                connfd);
373                         break;
374                 }
375
376                 close(connfd);
377         }
378
379         winsock_fini();
380         return 0;
381 }
382
383 static int device_register(const char *p)
384 {
385         int dev;
386         int cache_mode;
387         char dev_name[32];
388
389         if (sscanf(p, "%d %d %s", &dev, &cache_mode, dev_name) != 3) {
390                 printf("Param list error\n");
391                 return -1;
392         }
393
394 #ifdef WIN32
395         if (winpart) {
396                 file_windows_name_set(ext4_fname);
397                 bd = file_windows_dev_get();
398
399         } else
400 #endif
401         {
402                 file_dev_name_set(ext4_fname);
403                 bd = file_dev_get();
404         }
405
406         ext4_device_unregister_all();
407
408         return ext4_device_register(bd, dev_name);
409 }
410
411 static int mount(const char *p)
412 {
413         char dev_name[32];
414         char mount_point[32];
415         int rc;
416
417         if (sscanf(p, "%s %s", dev_name, mount_point) != 2) {
418                 printf("Param list error\n");
419                 return -1;
420         }
421
422         if (verbose)
423                 ext4_dmask_set(DEBUG_ALL);
424
425         rc = ext4_mount(dev_name, mount_point, false);
426         if (rc != EOK)
427                 return rc;
428
429         rc = ext4_recover(mount_point);
430         if (rc != EOK && rc != ENOTSUP)
431                 return rc;
432
433         rc = ext4_journal_start(mount_point);
434         if (rc != EOK)
435                 return rc;
436
437         if (cache_wb)
438                 ext4_cache_write_back(mount_point, 1);
439         return rc;
440 }
441
442 static int umount(const char *p)
443 {
444         char mount_point[32];
445         int rc;
446
447         if (sscanf(p, "%s", mount_point) != 1) {
448                 printf("Param list error\n");
449                 return -1;
450         }
451
452         if (cache_wb)
453                 ext4_cache_write_back(mount_point, 0);
454
455         rc = ext4_journal_stop(mount_point);
456         if (rc != EOK)
457                 return rc;
458
459         rc = ext4_umount(mount_point);
460         if (rc != EOK)
461                 return rc;
462
463         return rc;
464 }
465
466 static int mount_point_stats(const char *p)
467 {
468         char mount_point[32];
469         int d;
470         int rc;
471         struct ext4_mount_stats stats;
472
473         if (sscanf(p, "%s %d", mount_point, &d) != 2) {
474                 printf("Param list error\n");
475                 return -1;
476         }
477
478         rc = ext4_mount_point_stats(mount_point, &stats);
479
480         if (rc != EOK)
481                 return rc;
482
483         if (verbose) {
484                 printf("\tinodes_count = %" PRIu32"\n", stats.inodes_count);
485                 printf("\tfree_inodes_count = %" PRIu32"\n",
486                                 stats.free_inodes_count);
487                 printf("\tblocks_count = %" PRIu64"\n", stats.blocks_count);
488                 printf("\tfree_blocks_count = %" PRIu64"\n",
489                                 stats.free_blocks_count);
490                 printf("\tblock_size = %" PRIu32"\n", stats.block_size);
491                 printf("\tblock_group_count = %" PRIu32"\n",
492                                 stats.block_group_count);
493                 printf("\tblocks_per_group = %" PRIu32"\n",
494                                 stats.blocks_per_group);
495                 printf("\tinodes_per_group = %" PRIu32"\n",
496                                 stats.inodes_per_group);
497                 printf("\tvolume_name = %s\n", stats.volume_name);
498         }
499
500         return rc;
501 }
502
503 static int cache_write_back(const char *p)
504 {
505         char mount_point[32];
506         int en;
507
508         if (sscanf(p, "%s %d", mount_point, &en) != 2) {
509                 printf("Param list error\n");
510                 return -1;
511         }
512
513         return ext4_cache_write_back(mount_point, en);
514 }
515
516 static int fremove(const char *p)
517 {
518         char path[255];
519
520         if (sscanf(p, "%s", path) != 1) {
521                 printf("Param list error\n");
522                 return -1;
523         }
524
525         return ext4_fremove(path);
526 }
527
528 static int file_open(const char *p)
529 {
530         int fid = MAX_FILES;
531         char path[256];
532         char flags[8];
533         int rc;
534
535         if (sscanf(p, "%d %s %s", &fid, path, flags) != 3) {
536                 printf("Param list error\n");
537                 return -1;
538         }
539
540         if (!(fid < MAX_FILES)) {
541                 printf("File id too big\n");
542                 return -1;
543         }
544
545         rc = ext4_fopen(&file_tab[fid].fd, path, flags);
546
547         if (rc == EOK)
548                 strcpy(file_tab[fid].name, path);
549
550         return rc;
551 }
552
553 static int file_close(const char *p)
554 {
555         int fid = MAX_FILES;
556         int rc;
557
558         if (sscanf(p, "%d", &fid) != 1) {
559                 printf("Param list error\n");
560                 return -1;
561         }
562
563         if (!(fid < MAX_FILES)) {
564                 printf("File id too big\n");
565                 return -1;
566         }
567
568         if (file_tab[fid].name[0] == 0) {
569                 printf("File id empty\n");
570                 return -1;
571         }
572
573         rc = ext4_fclose(&file_tab[fid].fd);
574
575         if (rc == EOK)
576                 file_tab[fid].name[0] = 0;
577
578         return rc;
579 }
580
581 static int file_read(const char *p)
582 {
583         int fid = MAX_FILES;
584         int len;
585         int d;
586         int rc;
587         size_t rb;
588
589         if (sscanf(p, "%d %d %d %d", &fid, &d, &len, &d) != 4) {
590                 printf("Param list error\n");
591                 return -1;
592         }
593
594         if (!(fid < MAX_FILES)) {
595                 printf("File id too big\n");
596                 return -1;
597         }
598
599         if (file_tab[fid].name[0] == 0) {
600                 printf("File id empty\n");
601                 return -1;
602         }
603
604         while (len) {
605                 d = len > MAX_RW_BUFFER ? MAX_RW_BUFFER : len;
606
607                 memset(read_buffer, 0, MAX_RW_BUFFER);
608                 rc = ext4_fread(&file_tab[fid].fd, read_buffer, d, &rb);
609
610                 if (rc != EOK)
611                         break;
612
613                 if (rb != d) {
614                         printf("Read count error\n");
615                         return -1;
616                 }
617
618                 if (memcmp(read_buffer, write_buffer, d)) {
619                         printf("Read compare error\n");
620                         return -1;
621                 }
622
623                 len -= d;
624         }
625
626         return rc;
627 }
628
629 static int file_write(const const char *p)
630 {
631         int fid = MAX_FILES;
632         int d;
633         int rc;
634
635         int len;
636         size_t wb;
637
638         if (sscanf(p, "%d %d %d %d", &fid, &d, &len, &d) != 4) {
639                 printf("Param list error\n");
640                 return -1;
641         }
642
643         if (!(fid < MAX_FILES)) {
644                 printf("File id too big\n");
645                 return -1;
646         }
647
648         if (file_tab[fid].name[0] == 0) {
649                 printf("File id empty\n");
650                 return -1;
651         }
652
653         while (len) {
654                 d = len > MAX_RW_BUFFER ? MAX_RW_BUFFER : len;
655                 rc = ext4_fwrite(&file_tab[fid].fd, write_buffer, d, &wb);
656
657                 if (rc != EOK)
658                         break;
659
660                 if (wb != d) {
661                         printf("Write count error\n");
662                         return -1;
663                 }
664
665                 len -= d;
666         }
667
668         return rc;
669 }
670
671 static int file_seek(const char *p)
672 {
673         int fid = MAX_FILES;
674         int off;
675         int origin;
676
677         if (sscanf(p, "%d %d %d", &fid, &off, &origin) != 3) {
678                 printf("Param list error\n");
679                 return -1;
680         }
681
682         if (!(fid < MAX_FILES)) {
683                 printf("File id too big\n");
684                 return -1;
685         }
686
687         if (file_tab[fid].name[0] == 0) {
688                 printf("File id empty\n");
689                 return -1;
690         }
691
692         return ext4_fseek(&file_tab[fid].fd, off, origin);
693 }
694
695 static int file_tell(const char *p)
696 {
697         int fid = MAX_FILES;
698         uint32_t exp_pos;
699
700         if (sscanf(p, "%d %u", &fid, &exp_pos) != 2) {
701                 printf("Param list error\n");
702                 return -1;
703         }
704
705         if (!(fid < MAX_FILES)) {
706                 printf("File id too big\n");
707                 return -1;
708         }
709
710         if (file_tab[fid].name[0] == 0) {
711                 printf("File id empty\n");
712                 return -1;
713         }
714
715         if (exp_pos != ext4_ftell(&file_tab[fid].fd)) {
716                 printf("Expected filepos error\n");
717                 return -1;
718         }
719
720         return EOK;
721 }
722
723 static int file_size(const char *p)
724 {
725         int fid = MAX_FILES;
726         uint32_t exp_size;
727
728         if (sscanf(p, "%d %u", &fid, &exp_size) != 2) {
729                 printf("Param list error\n");
730                 return -1;
731         }
732
733         if (!(fid < MAX_FILES)) {
734                 printf("File id too big\n");
735                 return -1;
736         }
737
738         if (file_tab[fid].name[0] == 0) {
739                 printf("File id empty\n");
740                 return -1;
741         }
742
743         if (exp_size != ext4_fsize(&file_tab[fid].fd)) {
744                 printf("Expected filesize error\n");
745                 return -1;
746         }
747
748         return EOK;
749 }
750
751 static int dir_rm(const char *p)
752 {
753         char path[255];
754
755         if (sscanf(p, "%s", path) != 1) {
756                 printf("Param list error\n");
757                 return -1;
758         }
759
760         return ext4_dir_rm(path);
761 }
762
763 static int dir_mk(const char *p)
764 {
765         char path[255];
766
767         if (sscanf(p, "%s", path) != 1) {
768                 printf("Param list error\n");
769                 return -1;
770         }
771
772         return ext4_dir_mk(path);
773 }
774
775 static int dir_open(const char *p)
776 {
777         int did = MAX_DIRS;
778         char path[255];
779         int rc;
780
781         if (sscanf(p, "%d %s", &did, path) != 2) {
782                 printf("Param list error\n");
783                 return -1;
784         }
785
786         if (!(did < MAX_DIRS)) {
787                 printf("Dir id too big\n");
788                 return -1;
789         }
790
791         rc = ext4_dir_open(&dir_tab[did].fd, path);
792
793         if (rc == EOK)
794                 strcpy(dir_tab[did].name, path);
795
796         return rc;
797 }
798
799 static int dir_close(const char *p)
800 {
801         int did = MAX_DIRS;
802         int rc;
803
804         if (sscanf(p, "%d", &did) != 1) {
805                 printf("Param list error\n");
806                 return -1;
807         }
808
809         if (!(did < MAX_DIRS)) {
810                 printf("Dir id too big\n");
811                 return -1;
812         }
813
814         if (dir_tab[did].name[0] == 0) {
815                 printf("Dir id empty\n");
816                 return -1;
817         }
818
819         rc = ext4_dir_close(&dir_tab[did].fd);
820
821         if (rc == EOK)
822                 dir_tab[did].name[0] = 0;
823
824         return rc;
825 }
826
827 static int dir_entry_get(const char *p)
828 {
829         int did = MAX_DIRS;
830         int exp;
831         char name[256];
832
833         if (sscanf(p, "%d %d", &did, &exp) != 2) {
834                 printf("Param list error\n");
835                 return -1;
836         }
837
838         if (!(did < MAX_DIRS)) {
839                 printf("Dir id too big\n");
840                 return -1;
841         }
842
843         if (dir_tab[did].name[0] == 0) {
844                 printf("Dir id empty\n");
845                 return -1;
846         }
847
848         int idx = 0;
849         const ext4_direntry *d;
850
851         while ((d = ext4_dir_entry_next(&dir_tab[did].fd)) != NULL) {
852
853                 idx++;
854                 memcpy(name, d->name, d->name_length);
855                 name[d->name_length] = 0;
856                 if (verbose) {
857                         printf("\t%s %s\n", entry_to_str(d->inode_type), name);
858                 }
859         }
860
861         if (idx < 2) {
862                 printf("Minumum dir entry error\n");
863                 return -1;
864         }
865
866         if ((idx - 2) != exp) {
867                 printf("Expected dir entry error\n");
868                 return -1;
869         }
870
871         return EOK;
872 }
873
874 static int multi_fcreate(const char *p)
875 {
876         char path[256];
877         char path1[256];
878         char prefix[32];
879         int cnt;
880         int rc;
881         int i;
882         ext4_file fd;
883
884         if (sscanf(p, "%s %s %d", path, prefix, &cnt) != 3) {
885                 printf("Param list error\n");
886                 return -1;
887         }
888
889         for (i = 0; i < cnt; ++i) {
890                 sprintf(path1, "%s%s%d", path, prefix, i);
891                 rc = ext4_fopen(&fd, path1, "wb+");
892
893                 if (rc != EOK)
894                         break;
895         }
896
897         return rc;
898 }
899
900 static int multi_fwrite(const char *p)
901 {
902         char path[256];
903         char path1[256];
904         char prefix[32];
905         int cnt, i;
906         int len, ll;
907         int rc;
908         size_t d, wb;
909         ext4_file fd;
910
911         if (sscanf(p, "%s %s %d %d", path, prefix, &cnt, &ll) != 4) {
912                 printf("Param list error\n");
913                 return -1;
914         }
915
916         for (i = 0; i < cnt; ++i) {
917                 sprintf(path1, "%s%s%d", path, prefix, i);
918                 rc = ext4_fopen(&fd, path1, "rb+");
919
920                 if (rc != EOK)
921                         break;
922
923                 len = ll;
924                 while (len) {
925                         d = len > MAX_RW_BUFFER ? MAX_RW_BUFFER : len;
926                         rc = ext4_fwrite(&fd, write_buffer, d, &wb);
927
928                         if (rc != EOK)
929                                 break;
930
931                         if (wb != d) {
932                                 printf("Write count error\n");
933                                 return -1;
934                         }
935
936                         len -= d;
937                 }
938         }
939
940         return rc;
941 }
942
943 static int multi_fread(const char *p)
944 {
945         char path[256];
946         char path1[256];
947         char prefix[32];
948         int cnt;
949         int len, ll;
950         int rc ,i, d;
951         size_t rb;
952         ext4_file fd;
953
954         if (sscanf(p, "%s %s %d %d", path, prefix, &cnt, &ll) != 4) {
955                 printf("Param list error\n");
956                 return -1;
957         }
958
959         for (i = 0; i < cnt; ++i) {
960                 sprintf(path1, "%s%s%d", path, prefix, i);
961                 rc = ext4_fopen(&fd, path1, "rb+");
962
963                 if (rc != EOK)
964                         break;
965
966                 len = ll;
967                 while (len) {
968                         d = len > MAX_RW_BUFFER ? MAX_RW_BUFFER : len;
969
970                         memset(read_buffer, 0, MAX_RW_BUFFER);
971                         rc = ext4_fread(&fd, read_buffer, d, &rb);
972
973                         if (rc != EOK)
974                                 break;
975
976                         if (rb != d) {
977                                 printf("Read count error\n");
978                                 return -1;
979                         }
980
981                         if (memcmp(read_buffer, write_buffer, d)) {
982                                 printf("Read compare error\n");
983                                 return -1;
984                         }
985
986                         len -= d;
987                 }
988         }
989
990         return rc;
991 }
992
993 static int multi_fremove(const char *p)
994 {
995         char path[256];
996         char path1[256];
997         char prefix[32];
998         int cnt, i, rc;
999
1000         if (sscanf(p, "%s %s %d", path, prefix, &cnt) != 3) {
1001                 printf("Param list error\n");
1002                 return -1;
1003         }
1004
1005         for (i = 0; i < cnt; ++i) {
1006                 sprintf(path1, "%s%s%d", path, prefix, i);
1007                 rc = ext4_fremove(path1);
1008                 if (rc != EOK)
1009                         break;
1010         }
1011
1012         return rc;
1013 }
1014
1015 static int multi_dcreate(const char *p)
1016 {
1017         char path[256];
1018         char path1[256];
1019         char prefix[32];
1020         int cnt, i, rc;
1021
1022         if (sscanf(p, "%s %s %d", path, prefix, &cnt) != 3) {
1023                 printf("Param list error\n");
1024                 return -1;
1025         }
1026
1027         for (i = 0; i < cnt; ++i) {
1028                 sprintf(path1, "%s%s%d", path, prefix, i);
1029                 rc = ext4_dir_mk(path1);
1030                 if (rc != EOK)
1031                         break;
1032         }
1033
1034         return rc;
1035 }
1036
1037 static int multi_dremove(const char *p)
1038 {
1039         char path[256];
1040         char path1[256];
1041         char prefix[32];
1042         int cnt, i, rc;
1043
1044         if (sscanf(p, "%s %s %d", path, prefix, &cnt) != 3) {
1045                 printf("Param list error\n");
1046                 return -1;
1047         }
1048
1049         for (i = 0; i < cnt; ++i) {
1050                 sprintf(path1, "%s%s%d", path, prefix, i);
1051                 rc = ext4_dir_rm(path1);
1052                 if (rc != EOK)
1053                         break;
1054         }
1055
1056         return rc;
1057 }
1058
1059 struct ext4_mount_stats saved_stats;
1060
1061 static int stats_save(const char *p)
1062 {
1063         char path[256];
1064
1065         if (sscanf(p, "%s", path) != 1) {
1066                 printf("Param list error\n");
1067                 return -1;
1068         }
1069
1070         return ext4_mount_point_stats(path, &saved_stats);
1071 }
1072
1073 static int stats_check(const char *p)
1074 {
1075         char path[256];
1076         int rc;
1077
1078         struct ext4_mount_stats actual_stats;
1079
1080         if (sscanf(p, "%s", path) != 1) {
1081                 printf("Param list error\n");
1082                 return -1;
1083         }
1084
1085         rc = ext4_mount_point_stats(path, &actual_stats);
1086
1087         if (rc != EOK)
1088                 return rc;
1089
1090         if (memcmp(&saved_stats, &actual_stats,
1091                    sizeof(struct ext4_mount_stats))) {
1092                 if (verbose) {
1093                         printf("\tMount point stats error:\n");
1094                         printf("\tsaved_stats:\n");
1095                         printf("\tinodes_count = %" PRIu32"\n",
1096                                saved_stats.inodes_count);
1097                         printf("\tfree_inodes_count = %" PRIu32"\n",
1098                                saved_stats.free_inodes_count);
1099                         printf("\tblocks_count = %" PRIu64"\n",
1100                                saved_stats.blocks_count);
1101                         printf("\tfree_blocks_count = %" PRIu64"\n",
1102                                saved_stats.free_blocks_count);
1103                         printf("\tblock_size = %" PRIu32"\n",
1104                                         saved_stats.block_size);
1105                         printf("\tblock_group_count = %" PRIu32"\n",
1106                                saved_stats.block_group_count);
1107                         printf("\tblocks_per_group = %" PRIu32"\n",
1108                                saved_stats.blocks_per_group);
1109                         printf("\tinodes_per_group = %" PRIu32"\n",
1110                                saved_stats.inodes_per_group);
1111                         printf("\tvolume_name = %s\n", saved_stats.volume_name);
1112                         printf("\tactual_stats:\n");
1113                         printf("\tinodes_count = %" PRIu32"\n",
1114                                actual_stats.inodes_count);
1115                         printf("\tfree_inodes_count = %" PRIu32"\n",
1116                                actual_stats.free_inodes_count);
1117                         printf("\tblocks_count = %" PRIu64"\n",
1118                                actual_stats.blocks_count);
1119                         printf("\tfree_blocks_count = %" PRIu64"\n",
1120                                actual_stats.free_blocks_count);
1121                         printf("\tblock_size = %d\n", actual_stats.block_size);
1122                         printf("\tblock_group_count = %" PRIu32"\n",
1123                                actual_stats.block_group_count);
1124                         printf("\tblocks_per_group = %" PRIu32"\n",
1125                                actual_stats.blocks_per_group);
1126                         printf("\tinodes_per_group = %" PRIu32"\n",
1127                                actual_stats.inodes_per_group);
1128                         printf("\tvolume_name = %s\n",
1129                                actual_stats.volume_name);
1130                 }
1131                 return -1;
1132         }
1133
1134         return rc;
1135 }
1136
1137 static char *entry_to_str(uint8_t type)
1138 {
1139         switch (type) {
1140         case EXT4_DE_UNKNOWN:
1141                 return "[unk] ";
1142         case EXT4_DE_REG_FILE:
1143                 return "[fil] ";
1144         case EXT4_DE_DIR:
1145                 return "[dir] ";
1146         case EXT4_DE_CHRDEV:
1147                 return "[cha] ";
1148         case EXT4_DE_BLKDEV:
1149                 return "[blk] ";
1150         case EXT4_DE_FIFO:
1151                 return "[fif] ";
1152         case EXT4_DE_SOCK:
1153                 return "[soc] ";
1154         case EXT4_DE_SYMLINK:
1155                 return "[sym] ";
1156         default:
1157                 break;
1158         }
1159         return "[???]";
1160 }
1161
1162 static int winsock_init(void)
1163 {
1164 #if WIN32
1165         int rc;
1166         static WSADATA wsaData;
1167         rc = WSAStartup(MAKEWORD(2, 2), &wsaData);
1168         if (rc != 0) {
1169                 return -1;
1170         }
1171 #endif
1172         return 0;
1173 }
1174
1175 static void winsock_fini(void)
1176 {
1177 #if WIN32
1178         WSACleanup();
1179 #endif
1180 }