9e025a6f5d8b21310ce500e40a1cd8d724c55edc
[people/sha0/winvblock.git] / src / util / winvblk.c
1 /**
2  * Copyright (C) 2009-2010, Shao Miller <shao.miller@yrdsb.edu.on.ca>.
3  * Copyright 2006-2008, V.
4  * For WinAoE contact information, see http://winaoe.org/
5  *
6  * This file is part of WinVBlock, derived from WinAoE.
7  *
8  * WinVBlock is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * WinVBlock is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with WinVBlock.  If not, see <http://www.gnu.org/licenses/>.
20  */
21
22 /**
23  * @file
24  *
25  * WinVBlock user-mode utility.
26  */
27
28 #include <windows.h>
29 #include <winioctl.h>
30 #include <stdio.h>
31 #include <malloc.h>
32
33 #include "winvblock.h"
34 #include "portable.h"
35 #include "mount.h"
36 #include "aoe.h"
37
38 /**
39  * Command routine.
40  *
41  * @v boot_bus          A handle to the boot bus device.
42  */
43 #define command_decl( x )       \
44                                 \
45 int STDCALL                     \
46 x (                             \
47   HANDLE boot_bus               \
48  )
49 /* Function pointer for a command routine. */
50 typedef command_decl(( *command_routine ));
51
52 winvblock__def_struct(option) {
53     char *name;
54     char *value;
55     int has_arg;
56   };
57
58 static option opt_h1 = {
59     "HELP", NULL, 0
60   };
61
62 static option opt_h2 = {
63     "?", NULL, 0
64   };
65
66 static option opt_cmd = {
67     "CMD", NULL, 1
68   };
69
70 static option opt_cyls = {
71     "C", NULL, 1
72   };
73
74 static option opt_heads = {
75     "H", NULL, 1
76   };
77
78 static option opt_spt = {
79     "S", NULL, 1
80   };
81
82 static option opt_disknum = {
83     "D", NULL, 1
84   };
85
86 static option opt_media = {
87     "M", NULL, 1
88   };
89
90 static option opt_uri = {
91     "U", NULL, 1
92   };
93
94 static option opt_mac = {
95     "MAC", NULL, 1
96   };
97
98 static option *options[] = {
99     &opt_h1,
100     &opt_h2,
101     &opt_cmd,
102     &opt_cyls,
103     &opt_heads,
104     &opt_spt,
105     &opt_disknum,
106     &opt_media,
107     &opt_uri,
108     &opt_mac,
109   };
110
111 static char present[] = "";
112 static char *invalid_opt = NULL;
113
114 static void cmdline_options(int argc, char **argv) {
115     int cur = 1;
116
117     while (cur < argc) {
118         char *cur_arg = argv[cur];
119         int opt;
120
121         /* Check if the argument is an option.  Look for '-', '--', or '/'. */
122         switch (*cur_arg) {
123             case '-':
124               cur_arg++;
125               if (*cur_arg == '-')
126                 cur_arg++;
127               break;
128
129             case '/':
130               cur_arg++;
131               break;
132
133             default:
134               invalid_opt = cur_arg;
135               return;
136           }
137         /* Convert possible option to upper-case. */
138         {   char *walker = cur_arg;
139
140             while (*walker) {
141                 *walker = toupper(*walker);
142                 walker++;
143               }
144           }
145         /* Check if the argument is a _valid_ option. */
146         opt = 0;
147         while (opt < sizeof (options) / sizeof (option *)) {
148             if (strcmp(cur_arg, options[opt]->name) == 0) {
149                 /*
150                  * We have a match.
151                  * Check if the option was already specified.
152                  */
153                 if (options[opt]->value != NULL) {
154                     printf(
155                         "%s specified more than once, making it invalid.\n",
156                         cur_arg
157                       );
158                     invalid_opt = cur_arg;
159                     return;
160                   }
161                 /*
162                  * The option's value is the next argument.  For boolean
163                  * options, we ignore the value anyway, but need to know the
164                  * option is present.
165                  */
166                 if (cur == argc - 1)
167                   options[opt]->value = present;
168                   else
169                   options[opt]->value = argv[cur + 1];
170                 cur += options[opt]->has_arg;
171                 break;
172               }
173             opt++;
174           }
175         /* Did we find no valid option match? */
176         if (opt == sizeof (options) / sizeof (option *)) {
177             invalid_opt = cur_arg;
178             return;
179           }
180         cur++;
181       }
182   }
183
184 static command_decl(cmd_help) {
185     char help_text[] = "\n\
186 WinVBlock user-land utility for disk control. (C) 2006-2008 V.,\n\
187                                               (C) 2009-2010 Shao Miller\n\
188 Usage:\n\
189   winvblk -cmd <command> [-d <disk number>] [-m <media>] [-u <uri or path>]\n\
190     [-mac <client mac address>] [-c <cyls>] [-h <heads>] [-s <sects per track>]\n\
191   winvblk -?\n\
192 \n\
193 Parameters:\n\
194   <command> is one of:\n\
195     scan   - Shows the reachable AoE targets.\n\
196     show   - Shows the mounted AoE targets.\n\
197     mount  - Mounts an AoE target.  Requires -mac and -u\n\
198     umount - Unmounts an AoE disk.  Requires -d\n\
199     attach - Attaches <filepath> disk image file.  Requires -u and -m.\n\
200              -c, -h, -s are optional.\n\
201     detach - Detaches file-backed disk.  Requires -d\n\
202   <uri or path> is something like:\n\
203     aoe:eX.Y        - Where X is the \"major\" (shelf) and Y is\n\
204                       the \"minor\" (slot)\n\
205     c:\\my_disk.hdd - The path to a disk image file or .ISO\n\
206   <media> is one of 'c' for CD/DVD, 'f' for floppy, 'h' for hard disk drive\n\
207 \n";
208     printf(help_text);
209     return 1;
210   }
211
212 static command_decl(cmd_scan) {
213     aoe__mount_targets_ptr targets;
214     DWORD bytes_returned;
215     winvblock__uint32 i;
216     winvblock__uint8 string[256];
217     int status = 2;
218
219     targets = malloc(
220         sizeof (aoe__mount_targets) +
221         (32 * sizeof (aoe__mount_target))
222       );
223     if (targets == NULL) {
224         printf("Out of memory\n");
225         goto err_alloc;
226       }
227
228     if (!DeviceIoControl(
229         boot_bus,
230         IOCTL_AOE_SCAN,
231         NULL,
232         0,
233         targets,
234         sizeof (aoe__mount_targets) + (32 * sizeof (aoe__mount_target)),
235         &bytes_returned,
236         (LPOVERLAPPED) NULL
237       )) {
238         printf("DeviceIoControl (%d)\n", (int) GetLastError());
239         status = 2;
240         goto err_ioctl;
241       }
242     if (targets->Count == 0) {
243         printf("No AoE targets found.\n");
244         goto err_no_targets;
245       }
246     printf("Client NIC          Target      Server MAC         Size\n");
247     for (i = 0; i < targets->Count && i < 10; i++) {
248         sprintf(
249             string,
250             "e%lu.%lu      ",
251             targets->Target[i].Major,
252             targets->Target[i].Minor
253           );
254         string[10] = 0;
255         printf(
256             " %02x:%02x:%02x:%02x:%02x:%02x  %s "
257               " %02x:%02x:%02x:%02x:%02x:%02x  %I64uM\n",
258             targets->Target[i].ClientMac[0],
259             targets->Target[i].ClientMac[1],
260             targets->Target[i].ClientMac[2],
261             targets->Target[i].ClientMac[3],
262             targets->Target[i].ClientMac[4],
263             targets->Target[i].ClientMac[5],
264             string,
265             targets->Target[i].ServerMac[0],
266             targets->Target[i].ServerMac[1],
267             targets->Target[i].ServerMac[2],
268             targets->Target[i].ServerMac[3],
269             targets->Target[i].ServerMac[4],
270             targets->Target[i].ServerMac[5],
271             targets->Target[i].LBASize / 2048
272           );
273       } /* for */
274
275     err_no_targets:
276
277     status = 0;
278
279     err_ioctl:
280
281     free(targets);
282     err_alloc:
283
284     return status;
285   }
286
287 static command_decl(cmd_show) {
288     aoe__mount_disks_ptr mounted_disks;
289     DWORD bytes_returned;
290     winvblock__uint32 i;
291     winvblock__uint8 string[256];
292     int status = 2;
293
294     mounted_disks = malloc(
295         sizeof (aoe__mount_disks) +
296         (32 * sizeof (aoe__mount_disk)) 
297       );
298     if (mounted_disks == NULL) {
299         printf("Out of memory\n");
300         goto err_alloc;
301       }
302   if (!DeviceIoControl(
303       boot_bus,
304       IOCTL_AOE_SHOW,
305       NULL,
306       0,
307       mounted_disks,
308       sizeof (aoe__mount_disks) + (32 * sizeof (aoe__mount_disk)),
309       &bytes_returned,
310       (LPOVERLAPPED) NULL
311     )) {
312       printf("DeviceIoControl (%d)\n", (int) GetLastError());
313       goto err_ioctl;
314     }
315
316     status = 0;
317
318     if (mounted_disks->Count == 0) {
319         printf("No AoE disks mounted.\n");
320         goto err_no_disks;
321       }
322     printf("Disk  Client NIC         Server MAC         Target      Size\n");
323     for (i = 0; i < mounted_disks->Count && i < 10; i++) {
324         sprintf(
325             string,
326             "e%lu.%lu      ",
327             mounted_disks->Disk[i].Major,
328             mounted_disks->Disk[i].Minor
329           );
330         string[10] = 0;
331         printf(
332             " %-4lu %02x:%02x:%02x:%02x:%02x:%02x  "
333               "%02x:%02x:%02x:%02x:%02x:%02x  %s  %I64uM\n",
334             mounted_disks->Disk[i].Disk,
335             mounted_disks->Disk[i].ClientMac[0],
336             mounted_disks->Disk[i].ClientMac[1],
337             mounted_disks->Disk[i].ClientMac[2],
338             mounted_disks->Disk[i].ClientMac[3],
339             mounted_disks->Disk[i].ClientMac[4],
340             mounted_disks->Disk[i].ClientMac[5],
341             mounted_disks->Disk[i].ServerMac[0],
342             mounted_disks->Disk[i].ServerMac[1],
343             mounted_disks->Disk[i].ServerMac[2],
344             mounted_disks->Disk[i].ServerMac[3],
345             mounted_disks->Disk[i].ServerMac[4],
346             mounted_disks->Disk[i].ServerMac[5],
347             string,
348             mounted_disks->Disk[i].LBASize / 2048
349           );
350       }
351
352     err_no_disks:
353
354     err_ioctl:
355
356     free(mounted_disks);
357     err_alloc:
358
359     return status;
360   }
361
362 static command_decl(cmd_mount) {
363     winvblock__uint8 mac_addr[6];
364     winvblock__uint32 ver_major, ver_minor;
365     winvblock__uint8 in_buf[sizeof (mount__filedisk) + 1024];
366     DWORD bytes_returned;
367
368     if (opt_mac.value == NULL || opt_uri.value == NULL) {
369         printf("-mac and -u options required.  See -? for help.\n");
370         return 1;
371       }
372     sscanf(
373         opt_mac.value,
374         "%02x:%02x:%02x:%02x:%02x:%02x",
375         (int *) (mac_addr + 0),
376         (int *) (mac_addr + 1),
377         (int *) (mac_addr + 2),
378         (int *) (mac_addr + 3),
379         (int *) (mac_addr + 4),
380         (int *) (mac_addr + 5)
381       );
382     sscanf(
383         opt_uri.value,
384         "aoe:e%lu.%lu",
385         (int *) &ver_major,
386         (int *) &ver_minor
387       );
388     printf(
389         "mounting e%lu.%lu from %02x:%02x:%02x:%02x:%02x:%02x\n",
390         (int) ver_major,
391         (int) ver_minor,
392         mac_addr[0],
393         mac_addr[1],
394         mac_addr[2],
395         mac_addr[3],
396         mac_addr[4],
397         mac_addr[5]
398       );
399     memcpy(in_buf, mac_addr, 6);
400     *((winvblock__uint16_ptr) (in_buf + 6)) = (winvblock__uint16) ver_major;
401     *((winvblock__uint8_ptr) (in_buf + 8)) = (winvblock__uint8) ver_minor;
402     if (!DeviceIoControl(
403         boot_bus,
404         IOCTL_AOE_MOUNT,
405         in_buf,
406         sizeof (in_buf),
407         NULL,
408         0,
409         &bytes_returned,
410         (LPOVERLAPPED) NULL
411       )) {
412         printf("DeviceIoControl (%d)\n", (int) GetLastError());
413         return 2;
414       }
415     return 0;
416   }
417
418 static command_decl(cmd_umount) {
419     winvblock__uint32 disk_num;
420     winvblock__uint8 in_buf[sizeof (mount__filedisk) + 1024];
421     DWORD bytes_returned;
422
423     if (opt_disknum.value == NULL) {
424         printf("-d option required.  See -? for help.\n");
425         return 1;
426       }
427     sscanf(opt_disknum.value, "%d", (int *) &disk_num);
428     printf("unmounting disk %d\n", (int) disk_num);
429     memcpy(in_buf, &disk_num, 4);
430     if (!DeviceIoControl(
431         boot_bus,
432         IOCTL_AOE_UMOUNT,
433         in_buf,
434         sizeof (in_buf),
435         NULL,
436         0,
437         &bytes_returned,
438         (LPOVERLAPPED) NULL
439       )) {
440         printf("DeviceIoControl (%d)\n", (int) GetLastError());
441         return 2;
442       }
443     return 0;
444   }
445
446 static command_decl(cmd_attach) {
447     mount__filedisk filedisk;
448     char obj_path_prefix[] = "\\??\\";
449     winvblock__uint8 in_buf[sizeof (mount__filedisk) + 1024];
450     DWORD bytes_returned;
451
452     if (opt_uri.value == NULL || opt_media.value == NULL) {
453         printf("-u and -m options required.  See -? for help.\n");
454         return 1;
455       }
456     filedisk.type = opt_media.value[0];
457     if (opt_cyls.value != NULL)
458       sscanf(opt_cyls.value, "%d", (int *) &filedisk.cylinders);
459     if (opt_heads.value != NULL)
460       sscanf(opt_heads.value, "%d", (int *) &filedisk.heads);
461     if (opt_spt.value != NULL)
462       sscanf(opt_spt.value, "%d", (int *) &filedisk.sectors);
463     memcpy(in_buf, &filedisk, sizeof (mount__filedisk));
464     memcpy(
465         in_buf + sizeof (mount__filedisk),
466         obj_path_prefix,
467         sizeof (obj_path_prefix)
468       );
469     memcpy(
470         in_buf + sizeof (mount__filedisk) + sizeof (obj_path_prefix) - 1,
471         opt_uri.value,
472         strlen(opt_uri.value) + 1
473       );
474     if (!DeviceIoControl(
475         boot_bus,
476         IOCTL_FILE_ATTACH,
477         in_buf,
478         sizeof (in_buf),
479         NULL,
480         0,
481         &bytes_returned,
482         (LPOVERLAPPED) NULL
483       )) {
484         printf("DeviceIoControl (%d)\n", (int) GetLastError());
485         return 2;
486       }
487     return 0;
488   }
489
490 static command_decl(cmd_detach) {
491     winvblock__uint32 disk_num;
492     winvblock__uint8 in_buf[sizeof (mount__filedisk) + 1024];
493     DWORD bytes_returned;
494
495     if (opt_disknum.value == NULL) {
496         printf("-d option required.  See -? for help.\n");
497         return 1;
498       }
499     sscanf(opt_disknum.value, "%d", (int *) &disk_num);
500     printf("Detaching file-backed disk %d\n", (int) disk_num);
501     memcpy(in_buf, &disk_num, 4);
502     if (!DeviceIoControl(
503         boot_bus,
504         IOCTL_FILE_DETACH,
505         in_buf,
506         sizeof (in_buf),
507         NULL,
508         0,
509         &bytes_returned,
510         (LPOVERLAPPED) NULL
511       )) {
512         printf("DeviceIoControl (%d)\n", (int) GetLastError());
513         return 2;
514       }
515     return 0;
516   }
517
518 int main(int argc, char **argv, char **envp) {
519     command_routine cmd = cmd_help;
520     HANDLE boot_bus = NULL;
521     int status = 1;
522
523     cmdline_options(argc, argv);
524     /* Check for invalid option. */
525     if (invalid_opt != NULL) {
526         printf("Use -? for help.  Invalid option: %s\n", invalid_opt);
527         goto err_bad_cmd;
528       }
529     /* Check for cry for help. */
530     if (opt_h1.value || opt_h2.value)
531       goto do_cmd;
532     /* Check for no command. */
533     if (opt_cmd.value == NULL)
534       goto do_cmd;
535     /* Check given command. */
536     if (strcmp(opt_cmd.value, "scan") == 0)
537       cmd = cmd_scan;
538     if (strcmp(opt_cmd.value, "show") == 0)
539       cmd = cmd_show;
540     if (strcmp(opt_cmd.value, "mount" ) == 0)
541       cmd = cmd_mount;
542     if (strcmp(opt_cmd.value, "umount") == 0)
543       cmd = cmd_umount;
544     if (strcmp(opt_cmd.value, "attach") == 0)
545       cmd = cmd_attach;
546     if (strcmp(opt_cmd.value, "detach") == 0)
547       cmd = cmd_detach;
548     /* Check for invalid command. */
549     if (cmd == cmd_help)
550       goto do_cmd;
551
552     boot_bus = CreateFile(
553         "\\\\.\\" winvblock__literal,
554         GENERIC_READ | GENERIC_WRITE,
555         FILE_SHARE_READ | FILE_SHARE_WRITE,
556         NULL,
557         OPEN_EXISTING,
558         0,
559         NULL
560       );
561     if (boot_bus == INVALID_HANDLE_VALUE) {
562         printf("CreateFile (%d)\n", (int) GetLastError());
563         goto err_handle;
564       }
565
566     do_cmd:
567     status = cmd(boot_bus);
568
569     if (boot_bus != NULL)
570       CloseHandle(boot_bus);
571     err_handle:
572
573     err_bad_cmd:
574
575     return status;
576   }