Statistics
| Branch: | Revision:

root / drivers / td.c @ abdb293f

History | View | Annotate | Download (14.4 kB)

1
/*
2
 * Copyright (c) 2007, XenSource Inc.
3
 * Copyright (c) 2010, Citrix Systems, Inc.
4
 *
5
 * All rights reserved.
6
 *
7
 * Redistribution and use in source and binary forms, with or without
8
 * modification, are permitted provided that the following conditions are met:
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
 *     * Neither the name of XenSource Inc. nor the names of its contributors
15
 *       may be used to endorse or promote products derived from this software
16
 *       without specific prior written permission.
17
 *
18
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
22
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
 */
30

    
31
#ifdef HAVE_CONFIG_H
32
#include "config.h"
33
#endif
34

    
35
#include <errno.h>
36
#include <fcntl.h>
37
#include <stdio.h>
38
#include <stdlib.h>
39
#include <sys/types.h>
40
#include <sys/stat.h>
41
#include <sys/resource.h>
42
#include <unistd.h>
43
#include <string.h>
44

    
45
#include "libvhd.h"
46
#include "vhd-util.h"
47
#include "tapdisk-utils.h"
48

    
49
#if 1
50
#define DFPRINTF(_f, _a...) fprintf ( stdout, _f , ## _a )
51
#else
52
#define DFPRINTF(_f, _a...) ((void)0)
53
#endif
54

    
55
typedef enum {
56
        TD_FIELD_HIDDEN  = 0,
57
        TD_FIELD_INVALID = 1
58
} td_field_t;
59

    
60
struct vdi_field {
61
        char       *name;
62
        td_field_t  id;
63
};
64

    
65
static struct vdi_field td_vdi_fields[TD_FIELD_INVALID] = {
66
        { .id = TD_FIELD_HIDDEN, .name = "hidden" }
67
};
68

    
69
typedef enum {
70
        TD_CMD_CREATE    = 0,
71
        TD_CMD_SNAPSHOT,
72
/*        TD_CMD_COALESCE,       */
73
        TD_CMD_QUERY,
74
/*         TD_CMD_RESIZE,         */
75
        TD_CMD_SET,
76
/*        TD_CMD_REPAIR,         */
77
/*        TD_CMD_FILL,           */
78
/*        TD_CMD_READ,           */
79
        TD_CMD_INVALID,
80
} td_command_t;
81

    
82
struct command {
83
        td_command_t  id;
84
        char         *name;
85
        int           needs_type;
86
};
87

    
88
struct command commands[TD_CMD_INVALID] = {
89
        { .id = TD_CMD_CREATE,   .name = "create",   .needs_type = 1 },
90
        { .id = TD_CMD_SNAPSHOT, .name = "snapshot", .needs_type = 1 },
91
/*        { .id = TD_CMD_COALESCE, .name = "coalesce", .needs_type = 1 },    */
92
        { .id =        TD_CMD_QUERY,    .name = "query",    .needs_type = 1 },
93
/*        { .id =        TD_CMD_RESIZE,   .name = "resize",   .needs_type = 1 },    */
94
        { .id = TD_CMD_SET,      .name = "set",      .needs_type = 1 },
95
/*        { .id = TD_CMD_REPAIR,   .name = "repair",   .needs_type = 1 },    */
96
/*        { .id = TD_CMD_FILL,     .name = "fill",     .needs_type = 1 },    */
97
/*        { .id = TD_CMD_READ,     .name = "read",     .needs_type = 1 },    */
98
};
99

    
100
typedef enum {
101
        TD_TYPE_VHD         = 0,
102
        TD_TYPE_AIO,
103
        TD_TYPE_INVALID,
104
} td_disk_t;
105

    
106
const char *td_disk_types[TD_TYPE_INVALID] = {
107
        "vhd",
108
        "aio",
109
};
110

    
111
#define print_commands()                                                \
112
        do {                                                                \
113
                int i;                                                        \
114
                fprintf(stderr, "COMMAND := { ");                        \
115
                fprintf(stderr, "%s", commands[0].name);                \
116
                for (i = 1; i < TD_CMD_INVALID; i++)                        \
117
                        fprintf(stderr, " | %s", commands[i].name);        \
118
                fprintf(stderr, " }\n");                                \
119
        } while (0)
120

    
121
#define print_disk_types()                                                \
122
        do {                                                                \
123
                int i;                                                        \
124
                fprintf(stderr, "TYPE := { ");                                \
125
                fprintf(stderr, "%s", td_disk_types[0]);                \
126
                for (i = 1; i < TD_TYPE_INVALID; i++)                        \
127
                        fprintf(stderr, " | %s", td_disk_types[i]);        \
128
                fprintf(stderr, " }\n");                                \
129
        } while (0);
130

    
131
#define print_field_names()                                                \
132
        do {                                                                \
133
                int i;                                                        \
134
                fprintf(stderr, "FIELD := { ");                                \
135
                fprintf(stderr, "%s", td_vdi_fields[0].name);                \
136
                for (i = 1; i < TD_FIELD_INVALID; i++)                        \
137
                        fprintf(stderr, " | %s", td_vdi_fields[i].name); \
138
                fprintf(stderr, " }\n");                                \
139
        } while (0)
140

    
141
void 
142
help(void)
143
{
144
        fprintf(stderr, "Tapdisk Utilities: v1.0.0\n");
145
        fprintf(stderr, "usage: td-util COMMAND [TYPE] [OPTIONS]\n");
146
        print_commands();
147
        print_disk_types();
148
        exit(-1);
149
}
150

    
151
struct command *
152
get_command(char *command)
153
{
154
        int i;
155

    
156
        for (i = 0; i < TD_CMD_INVALID; i++)
157
                if (!strcmp(command, commands[i].name))
158
                        return &commands[i];
159

    
160
        return NULL;
161
}
162

    
163
struct vdi_field *
164
get_field(char *field)
165
{
166
        int i;
167

    
168
        for (i = 0; i < TD_FIELD_INVALID; i++)
169
                if (!strcmp(field, td_vdi_fields[i].name))
170
                        return &td_vdi_fields[i];
171

    
172
        return NULL;
173
}
174

    
175
int
176
get_driver_type(char *type)
177
{
178
        int i;
179

    
180
        if (strnlen(type, 25) >= 25)
181
                return -ENAMETOOLONG;
182

    
183
        for (i = 0; i < TD_TYPE_INVALID; i++)
184
                if (!strcmp(type, td_disk_types[i]))
185
                        return i;
186

    
187
        return -TD_TYPE_INVALID;
188
}
189

    
190
int
191
td_create(int type, int argc, char *argv[])
192
{
193
        ssize_t mb;
194
        uint64_t size;
195
        char *name, *buf;
196
        int c, i, fd, sparse = 1, fixedsize = 0;
197

    
198
        while ((c = getopt(argc, argv, "hrb")) != -1) {
199
                switch(c) {
200
                case 'r':
201
                        sparse = 0;
202
                        break;
203
                case 'b':
204
                        fixedsize = 1;
205
                        break;
206
                default:
207
                        fprintf(stderr, "Unknown option %c\n", (char)c);
208
                case 'h':
209
                        goto usage;
210
                }
211
        }
212

    
213
        if (optind != (argc - 2))
214
                goto usage;
215

    
216
        mb   = 1 << 20;
217
        size = atoi(argv[optind++]);
218
        size = size << 20;
219
        name = argv[optind];
220

    
221
        if (strnlen(name, MAX_NAME_LEN) == MAX_NAME_LEN) {
222
                fprintf(stderr, "Device name too long\n");
223
                return ENAMETOOLONG;
224
        }
225

    
226
        if (type == TD_TYPE_VHD) {
227
                int cargc = 0;
228
                char sbuf[32], *cargv[10];
229

    
230
                size >>= 20;
231

    
232
                memset(cargv, 0, sizeof(cargv));
233
                snprintf(sbuf, sizeof(sbuf) - 1, "%"PRIu64, size);
234
                cargv[cargc++] = "create";
235
                cargv[cargc++] = "-n";
236
                cargv[cargc++] = name;
237
                cargv[cargc++] = "-s";
238
                cargv[cargc++] = sbuf;
239
                if (!sparse)
240
                        cargv[cargc++] = "-r";
241
                if (fixedsize)
242
                        cargv[cargc++] = "-b";
243

    
244
                return vhd_util_create(cargc, cargv);
245
        }
246

    
247
        /* generic create */
248
        if (sparse) {
249
                fprintf(stderr, "Cannot create sparse %s image\n",
250
                        td_disk_types[type]);
251
                return EINVAL;
252
        }
253

    
254
        buf = calloc(1, mb);
255
        if (!buf)
256
                return ENOMEM;
257

    
258
        fd = open(name, O_WRONLY | O_DIRECT | O_CREAT | O_TRUNC, 0644);
259
        if (fd == -1) {
260
                free(buf);
261
                return errno;
262
        }
263

    
264
        size >>= 20;
265
        for (i = 0; i < size; i++)
266
                if (write(fd, buf, mb) != mb) {
267
                        close(fd);
268
                        unlink(name);
269
                        free(buf);
270
                        return EIO;
271
                }
272

    
273
        close(fd);
274
        free(buf);
275
        return 0;
276

    
277
 usage:
278
        fprintf(stderr, "usage: td-util create %s [-h help] [-r reserve] "
279
                "[-b file_is_fixed_size] <SIZE(MB)> <FILENAME>\n",
280
                td_disk_types[type]);
281
        return EINVAL;
282
}
283

    
284
int
285
td_snapshot(int type, int argc, char *argv[])
286
{
287
        char *cargv[10];
288
        int c, err, cargc;
289
        struct stat stats;
290
        char *name, *backing, *limit = NULL;
291
        int fixedsize = 0, rawparent = 0;
292

    
293
        if (type != TD_TYPE_VHD) {
294
                fprintf(stderr, "Cannot create snapshot of %s image type\n",
295
                        td_disk_types[type]);
296
                return EINVAL;
297
        }
298

    
299
        while ((c = getopt(argc, argv, "hbml:")) != -1) {
300
                switch(c) {
301
                case 'b':
302
                        fixedsize = 1;
303
                        break;
304
                case 'm':
305
                        rawparent = 1;
306
                        break;
307
                case 'l':
308
                        limit = optarg;
309
                        break;
310
                case 'h':
311
                        err = 0;
312
                        goto usage;
313
                default:
314
                        err = EINVAL;
315
                        goto usage;
316
                }
317
        }
318

    
319
        if (optind != (argc - 2)) {
320
                err = EINVAL;
321
                goto usage;
322
        }
323

    
324
        name    = argv[optind++];
325
        backing = argv[optind++];
326

    
327
        if (strnlen(name, MAX_NAME_LEN) == MAX_NAME_LEN ||
328
            strnlen(backing, MAX_NAME_LEN) == MAX_NAME_LEN) {
329
                fprintf(stderr, "Device name too long\n");
330
                return ENAMETOOLONG;
331
        }
332

    
333
        if (stat(backing, &stats) == -1) {
334
                fprintf(stderr, "File %s not found\n", backing);
335
                return errno;
336
        }
337

    
338
        cargc = 0;
339
        memset(cargv, 0, sizeof(cargv));
340
        cargv[cargc++] = "snapshot";
341
        cargv[cargc++] = "-n";
342
        cargv[cargc++] = name;
343
        cargv[cargc++] = "-p";
344
        cargv[cargc++] = backing;
345
        if (fixedsize)
346
                cargv[cargc++] = "-b";
347
        if (rawparent)
348
                cargv[cargc++] = "-m";
349
        if (limit) {
350
                cargv[cargc++] = "-l";
351
                cargv[cargc++] = limit;
352
        }
353
        return vhd_util_snapshot(cargc, cargv);
354

    
355
 usage:
356
        fprintf(stderr, "usage: td-util snapshot %s [-h help] [-m parent_raw] "
357
                "[-b file_is_fixed_size] [-l snapshot depth limit] "
358
                "<FILENAME> <BACKING_FILENAME>\n", td_disk_types[type]);
359
        return err;
360
}
361

    
362
int
363
td_coalesce(int type, int argc, char *argv[])
364
{
365
        int c, ret, cargc;
366
        char *name, *cargv[3];
367

    
368
        if (type != TD_TYPE_VHD) {
369
                fprintf(stderr, "Cannot create snapshot of %s image type\n",
370
                        td_disk_types[type]);
371
                return EINVAL;
372
        }
373

    
374
        while ((c = getopt(argc, argv, "h")) != -1) {
375
                switch(c) {
376
                default:
377
                        fprintf(stderr, "Unknown option %c\n", (char)c);
378
                case 'h':
379
                        goto usage;
380
                }
381
        }
382

    
383
        if (optind != (argc - 1))
384
                goto usage;
385

    
386
        name = argv[optind++];
387

    
388
        if (strnlen(name, MAX_NAME_LEN) == MAX_NAME_LEN) {
389
                fprintf(stderr, "Device name too long\n");
390
                return ENAMETOOLONG;
391
        }
392

    
393
        cargc = 0;
394
        memset(cargv, 0, sizeof(cargv));
395
        cargv[cargc++] = "coalesce";
396
        cargv[cargc++] = "-n";
397
        cargv[cargc++] = name;
398
        ret = vhd_util_coalesce(cargc, cargv);
399
        if (ret)
400
                printf("coalesce failed: %d\n", ret);
401

    
402
        return ret;
403

    
404
 usage:
405
        fprintf(stderr, "usage: td-util coalesce %s [-h help] "
406
                "<FILENAME>\n", td_disk_types[type]);
407
        return EINVAL;
408
}
409

    
410
int
411
td_query(int type, int argc, char *argv[])
412
{
413
        char *name;
414
        int c, size = 0, parent = 0, fields = 0, depth = 0, err = 0;
415

    
416
        while ((c = getopt(argc, argv, "hvpfd")) != -1) {
417
                switch(c) {
418
                case 'v':
419
                        size = 1;
420
                        break;
421
                case 'p':
422
                        parent = 1;
423
                        break;
424
                case 'f':
425
                        fields = 1;
426
                        break;
427
                case 'd':
428
                        depth = 1;
429
                        break;
430
                case 'h':
431
                        err = 0;
432
                        goto usage;
433
                default:
434
                        err = EINVAL;
435
                        goto usage;
436
                }
437
        }
438

    
439
        if (optind != (argc - 1)) {
440
                err = EINVAL;
441
                goto usage;
442
        }
443

    
444
        name = argv[optind++];
445

    
446
        if (strnlen(name, MAX_NAME_LEN) == MAX_NAME_LEN) {
447
                fprintf(stderr, "Device name too long\n");
448
                return ENAMETOOLONG;
449
        }
450

    
451
        if (type == TD_TYPE_VHD) {
452
                vhd_context_t vhd;
453

    
454
                err = vhd_open(&vhd, name, VHD_OPEN_RDONLY);
455
                if (err) {
456
                        printf("failed opening %s: %d\n", name, err);
457
                        return err;
458
                }
459

    
460
                if (size)
461
                        printf("%"PRIu64"\n", vhd.footer.curr_size >> 20);
462

    
463
                if (parent) {
464
                        if (vhd.footer.type != HD_TYPE_DIFF)
465
                                printf("%s has no parent\n", name);
466
                        else {
467
                                char *pname;
468

    
469
                                err = vhd_parent_locator_get(&vhd, &pname);
470
                                if (err)
471
                                        printf("failed getting parent: %d\n",
472
                                               err);
473
                                else {
474
                                        printf("%s\n", pname);
475
                                        free(pname);
476
                                }
477
                        }
478
                }
479

    
480
                if (fields) {
481
                        int ret, hidden;
482

    
483
                        ret = vhd_hidden(&vhd, &hidden);
484
                        if (ret) {
485
                                printf("failed checking 'hidden' field: %d\n",
486
                                       ret);
487
                                err = (err ? : ret);
488
                        } else
489
                                printf("%s: %d\n",
490
                                       td_vdi_fields[TD_FIELD_HIDDEN].name,
491
                                       hidden);
492
                }
493

    
494
                if (depth) {
495
                        int ret, length;
496

    
497
                        ret = vhd_chain_depth(&vhd, &length);
498
                        if (ret)
499
                                printf("error checking chain depth: %d\n", ret);
500
                        else
501
                                printf("chain depth: %d\n", length);
502

    
503
                        err = (err ? : ret);
504
                }
505

    
506
                vhd_close(&vhd);
507

    
508
        } else if (type == TD_TYPE_AIO) {
509
                if (size) {
510
                        int fd;
511
                        uint64_t secs;
512
                        uint32_t ssize;
513

    
514
                        fd = open(name, O_RDONLY | O_LARGEFILE);
515
                        if (fd == -1) {
516
                                printf("failed opening %s: %d\n", name, errno);
517
                                return -errno;
518
                        }
519

    
520
                        err = tapdisk_get_image_size(fd, &secs, &ssize);
521
                        close(fd);
522

    
523
                        if (err) {
524
                                printf("failed getting size for %s: %d\n:",
525
                                       name, err);
526
                                return err;
527
                        }
528

    
529
                        printf("%"PRIu64"\n", secs >> 11);
530
                }
531

    
532
                if (parent)
533
                        printf("%s has no parent\n", name);
534

    
535
                if (fields) {
536
                        int i;
537

    
538
                        for (i = 0; i < TD_FIELD_INVALID; i++)
539
                                printf("%s: 0\n", td_vdi_fields[i].name);
540
                }
541
        }
542

    
543
        return err;
544

    
545
 usage:
546
        fprintf(stderr, "usage: td-util query %s [-h help] [-v virtsize] "
547
                "[-p parent] [-f fields]  <FILENAME>\n", td_disk_types[type]);
548
        return err;
549
}
550

    
551
int
552
td_set_field(int type, int argc, char *argv[])
553
{
554
        int c, cargc;
555
        struct vdi_field *field;
556
        char *name, *value, *cargv[7];
557

    
558
        if (type != TD_TYPE_VHD) {
559
                fprintf(stderr, "Cannot set fields of %s images\n",
560
                        td_disk_types[type]);
561
                return EINVAL;
562
        }
563

    
564
        while ((c = getopt(argc, argv, "h")) != -1) {
565
                switch(c) {
566
                default:
567
                        fprintf(stderr, "Unknown option %c\n", (char)c);
568
                case 'h':
569
                        goto usage;
570
                }
571
        }
572

    
573
        if (optind != (argc - 3))
574
                goto usage;
575

    
576
        name  = argv[optind++];
577

    
578
        field = get_field(argv[optind]);
579
        if (!field || field->id != TD_FIELD_HIDDEN) {
580
                fprintf(stderr, "Invalid field %s\n", argv[optind]);
581
                goto usage;
582
        }
583

    
584
        value = argv[++optind];
585

    
586
        cargc = 0;
587
        memset(cargv, 0, sizeof(cargv));
588
        cargv[cargc++] = "set";
589
        cargv[cargc++] = "-n";
590
        cargv[cargc++] = name;
591
        cargv[cargc++] = "-f";
592
        cargv[cargc++] = field->name;
593
        cargv[cargc++] = "-v";
594
        cargv[cargc++] = value;
595
        return vhd_util_set_field(cargc, cargv);
596

    
597
 usage:
598
        fprintf(stderr, "usage: td-util set %s [-h help] "
599
                "<FILENAME> <FIELD> <VALUE>\n", td_disk_types[type]);
600
        print_field_names();
601
        return EINVAL;
602
}
603

    
604
int
605
main(int argc, char *argv[])
606
{
607
        char **cargv;
608
        struct command *cmd;
609
        int cargc, i, type = -1, ret = 0;
610

    
611
#ifdef CORE_DUMP
612
        struct rlimit rlim;
613
        rlim.rlim_cur = RLIM_INFINITY;
614
        rlim.rlim_max = RLIM_INFINITY;
615
        if (setrlimit(RLIMIT_CORE, &rlim) < 0)
616
                fprintf(stderr, "setrlimit failed: %d\n", errno);
617
#endif
618

    
619
        if (argc < 2)
620
                help();
621

    
622
        cargc = argc - 1;
623
        cmd   = get_command(argv[1]);
624
        if (!cmd) {
625
                fprintf(stderr, "invalid COMMAND %s\n", argv[1]);
626
                help();
627
        }
628

    
629
        if (cmd->needs_type) {
630
                if (argc < 3) {
631
                        fprintf(stderr, "td-util %s requires a TYPE\n",
632
                                cmd->name);
633
                        print_disk_types();
634
                        exit(-1);
635
                }
636

    
637
                type = get_driver_type(argv[2]);
638
                if (type < 0) {
639
                        fprintf(stderr, "invalid TYPE '%s'.\n", argv[2]);
640
                        print_disk_types();
641
                        exit(-1);
642
                }
643
                --cargc;
644
        }
645

    
646
        cargv = malloc(sizeof(char *) * cargc);
647
        if (!cargv)
648
                exit(ENOMEM);
649

    
650
        cargv[0] = cmd->name;
651
        for (i = 1; i < cargc; i++)
652
                cargv[i] = argv[i + (argc - cargc)];
653

    
654
        switch(cmd->id) {
655
        case TD_CMD_CREATE:
656
                ret = td_create(type, cargc, cargv);
657
                break;
658
        case TD_CMD_SNAPSHOT:
659
                ret = td_snapshot(type, cargc, cargv);
660
                break;
661
/*
662
        case TD_CMD_COALESCE:
663
                ret = td_coalesce(type, cargc, cargv);
664
                break;
665
*/
666
        case TD_CMD_QUERY:
667
                ret = td_query(type, cargc, cargv);
668
                break;
669
/*
670
        case TD_CMD_RESIZE:
671
                ret = td_resize(type, cargc, cargv);
672
                break;
673
*/
674
        case TD_CMD_SET:
675
                ret = td_set_field(type, cargc, cargv);
676
                break;
677
/*
678
        case TD_CMD_REPAIR:
679
                ret = td_repair(type, cargc, cargv);
680
                break;
681
        case TD_CMD_FILL:
682
                ret = td_fill(type, cargc, cargv);
683
                break;
684
        case TD_CMD_READ:
685
                ret = td_read(type, cargc, cargv);
686
                break;
687
*/
688
        default:
689
        case TD_CMD_INVALID:
690
                ret = EINVAL;
691
                break;
692
        }
693

    
694
        free(cargv);
695

    
696
        return (ret >= 0 ? ret : -ret);
697
}