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