Statistics
| Branch: | Revision:

root / drivers / tapdisk-image.c @ abdb293f

History | View | Annotate | Download (10.7 kB)

1
/* 
2
 * Copyright (c) 2008, XenSource Inc.
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 are met:
7
 *     * Redistributions of source code must retain the above copyright
8
 *       notice, this list of conditions and the following disclaimer.
9
 *     * Redistributions in binary form must reproduce the above copyright
10
 *       notice, this list of conditions and the following disclaimer in the
11
 *       documentation and/or other materials provided with the distribution.
12
 *     * Neither the name of XenSource Inc. nor the names of its contributors
13
 *       may be used to endorse or promote products derived from this software
14
 *       without specific prior written permission.
15
 *
16
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
20
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
 */
28

    
29
#ifdef HAVE_CONFIG_H
30
#include "config.h"
31
#endif
32

    
33
#include <errno.h>
34
#include <unistd.h>
35
#include <stdlib.h>
36
#include <stdio.h>
37
#include <limits.h>
38
#include <regex.h>
39

    
40
#include "tapdisk-image.h"
41
#include "tapdisk-driver.h"
42
#include "tapdisk-server.h"
43
#include "tapdisk-stats.h"
44
#include "tapdisk-interface.h"
45
#include "tapdisk-disktype.h"
46
#include "tapdisk-storage.h"
47

    
48
#define DBG(_f, _a...)       tlog_syslog(TLOG_DBG, _f, ##_a)
49
#define INFO(_f, _a...)      tlog_syslog(TLOG_INFO, _f, ##_a)
50
#define ERR(_err, _f, _a...) tlog_error(_err, _f, ##_a)
51

    
52
#define BUG() td_panic()
53

    
54
#define BUG_ON(_cond)                                                \
55
        if (unlikely(_cond)) {                                        \
56
                ERR(-EINVAL, "(%s) = %d", #_cond, _cond);        \
57
                BUG();                                                \
58
        }
59

    
60
#define ARRAY_SIZE(_a) (sizeof(_a)/sizeof((_a)[0]))
61

    
62
td_image_t *
63
tapdisk_image_allocate(const char *file, int type, td_flag_t flags)
64
{
65
        int err;
66
        td_image_t *image;
67

    
68
        image = calloc(1, sizeof(td_image_t));
69
        if (!image)
70
                return NULL;
71

    
72
        err = tapdisk_namedup(&image->name, file);
73
        if (err) {
74
                free(image);
75
                return NULL;
76
        }
77

    
78
        image->type    = type;
79
        image->flags   = flags;
80
        INIT_LIST_HEAD(&image->next);
81

    
82
        return image;
83
}
84

    
85
void
86
tapdisk_image_free(td_image_t *image)
87
{
88
        if (!image)
89
                return;
90

    
91
        list_del(&image->next);
92

    
93
        free(image->name);
94
        tapdisk_driver_free(image->driver);
95
        free(image);
96
}
97

    
98
int
99
tapdisk_image_check_td_request(td_image_t *image, td_request_t treq)
100
{
101
        int rdonly, err;
102
        td_disk_info_t *info;
103

    
104
        err = -EINVAL;
105

    
106
        info   = &image->info;
107
        rdonly = td_flag_test(image->flags, TD_OPEN_RDONLY);
108

    
109
        if (treq.op != TD_OP_READ && treq.op != TD_OP_WRITE)
110
                goto fail;
111

    
112
        if (treq.op == TD_OP_WRITE && rdonly) {
113
                err = -EPERM;
114
                goto fail;
115
        }
116

    
117
        if (treq.secs <= 0 || treq.sec + treq.secs > info->size)
118
                goto fail;
119

    
120
        return 0;
121

    
122
fail:
123
        ERR(err, "bad td request on %s (%s, %"PRIu64"): %d at %"PRIu64,
124
            image->name, (rdonly ? "ro" : "rw"), info->size, treq.op,
125
            treq.sec + treq.secs);
126
        return err;
127

    
128
}
129

    
130
int
131
tapdisk_image_check_request(td_image_t *image, td_vbd_request_t *vreq)
132
{
133
        td_driver_t *driver;
134
        td_disk_info_t *info;
135
        int i, rdonly, secs, err;
136

    
137
        driver = image->driver;
138
        if (!driver)
139
                return -ENODEV;
140

    
141
        info   = &driver->info;
142
        rdonly = td_flag_test(image->flags, TD_OPEN_RDONLY);
143

    
144
        secs = 0;
145

    
146
        if (vreq->iovcnt < 0) {
147
                err = -EINVAL;
148
                goto fail;
149
        }
150

    
151
        for (i = 0; i < vreq->iovcnt; i++)
152
                secs += vreq->iov[i].secs;
153

    
154
        switch (vreq->op) {
155
        case TD_OP_WRITE:
156
                if (rdonly) {
157
                        err = -EPERM;
158
                        goto fail;
159
                }
160
                /* continue */
161
        case TD_OP_READ:
162
                if (vreq->sec + secs > info->size) {
163
                        err = -EINVAL;
164
                        goto fail;
165
                }
166
                break;
167
        default:
168
                err = -EOPNOTSUPP;
169
                goto fail;
170
        }
171

    
172
        return 0;
173

    
174
fail:
175
        ERR(err, "bad request on %s (%s, %"PRIu64"): req %s op %d at %"PRIu64,
176
            image->name, (rdonly ? "ro" : "rw"), info->size, vreq->name,
177
            vreq->op, vreq->sec + secs);
178

    
179
        return err;
180
}
181

    
182
void
183
tapdisk_image_close(td_image_t *image)
184
{
185
        td_close(image);
186
        tapdisk_image_free(image);
187
}
188

    
189
int
190
tapdisk_image_open(int type, const char *name, int flags, td_image_t **_image)
191
{
192
        td_image_t *image;
193
        int err;
194

    
195
        image = tapdisk_image_allocate(name, type, flags);
196
        if (!image) {
197
                err = -ENOMEM;
198
                goto fail;
199
        }
200

    
201
        err = td_load(image);
202
        if (!err)
203
                goto done;
204

    
205
        image->driver = tapdisk_driver_allocate(image->type,
206
                                                image->name,
207
                                                image->flags);
208
        if (!image->driver) {
209
                err = -ENOMEM;
210
                goto fail;
211
        }
212

    
213
        err = td_open(image);
214
        if (err)
215
                goto fail;
216

    
217
done:
218
        *_image = image;
219
        return 0;
220

    
221
fail:
222
        if (image)
223
                tapdisk_image_close(image);
224
        return err;
225
}
226

    
227
static int
228
tapdisk_image_open_parent(td_image_t *image, td_image_t **_parent)
229
{
230
        td_image_t *parent = NULL;
231
        td_disk_id_t id;
232
        int err;
233

    
234
        memset(&id, 0, sizeof(id));
235
        id.flags = image->flags;
236

    
237
        err = td_get_parent_id(image, &id);
238
        if (err == TD_NO_PARENT) {
239
                err = 0;
240
                goto out;
241
        }
242
        if (err)
243
                return err;
244

    
245
        err = tapdisk_image_open(id.type, id.name, id.flags, &parent);
246
        if (err)
247
                return err;
248

    
249
out:
250
        *_parent = parent;
251
        return 0;
252
}
253

    
254
static int
255
tapdisk_image_open_parents(td_image_t *image)
256
{
257
        td_image_t *parent;
258
        int err;
259

    
260
        do {
261
                err = tapdisk_image_open_parent(image, &parent);
262
                if (err)
263
                        break;
264

    
265
                if (parent) {
266
                        list_add(&parent->next, &image->next);
267
                        image = parent;
268
                }
269
        } while (parent);
270

    
271
        return err;
272
}
273

    
274
void
275
tapdisk_image_close_chain(struct list_head *list)
276
{
277
        td_image_t *image, *next;
278

    
279
        tapdisk_for_each_image_safe(image, next, list)
280
                tapdisk_image_close(image);
281
}
282

    
283
static int
284
__tapdisk_image_open_chain(int type, const char *name, int flags,
285
                           struct list_head *_head, int prt_devnum)
286
{
287
        struct list_head head = LIST_HEAD_INIT(head);
288
        td_image_t *image;
289
        int err;
290

    
291
        err = tapdisk_image_open(type, name, flags, &image);
292
        if (err)
293
                goto fail;
294

    
295
        list_add_tail(&image->next, &head);
296

    
297
        if (unlikely(prt_devnum >= 0)) {
298
                char dev[32];
299
                snprintf(dev, sizeof(dev),
300
                         "%s%d", BLKTAP2_IO_DEVICE, prt_devnum);
301
                err = tapdisk_image_open(DISK_TYPE_AIO, dev,
302
                                         flags|TD_OPEN_RDONLY, &image);
303
                if (err)
304
                        goto fail;
305

    
306
                list_add_tail(&image->next, &head);
307
                goto done;
308
        }
309

    
310
        err = tapdisk_image_open_parents(image);
311
        if (err)
312
                goto fail;
313

    
314
done:
315
        list_splice(&head, _head);
316
        return 0;
317

    
318
fail:
319
        tapdisk_image_close_chain(&head);
320
        return err;
321
}
322

    
323
int
324
tapdisk_image_parse_flags(char *args, unsigned long *_flags)
325
{
326
        unsigned long flags = 0;
327
        char *token;
328

    
329
        BUG_ON(!args);
330

    
331
        do {
332
                token = strtok(args, ",");
333
                if (!token)
334
                        break;
335

    
336
                switch (token[0]) {
337
                case 'r':
338
                        if (!strcmp(token, "ro")) {
339
                                flags |= TD_OPEN_RDONLY;
340
                                break;
341
                        }
342
                        goto fail;
343

    
344
                default:
345
                        goto fail;
346
                }
347

    
348
                args = NULL;
349
        } while (1);
350

    
351
        *_flags |= flags;
352

    
353
        return 0;
354

    
355
fail:
356
        ERR(-EINVAL, "Invalid token '%s'", token);
357
        return -EINVAL;
358
}
359

    
360
static int
361
tapdisk_image_open_x_chain(const char *path, struct list_head *_head)
362
{
363
        struct list_head head = LIST_HEAD_INIT(head);
364
        td_image_t *image = NULL, *next;
365
        regex_t _im, *im = NULL, _ws, *ws = NULL;
366
        FILE *s;
367
        int err;
368

    
369
        s = fopen(path, "r");
370
        if (!s) {
371
                err = -errno;
372
                goto fail;
373
        }
374

    
375
        err = regcomp(&_ws, "^[:space:]*$", REG_NOSUB);
376
        if (err)
377
                goto fail;
378
        ws = &_ws;
379

    
380
        err = regcomp(&_im,
381
                      "^([^:]+):([^ \t]+)([ \t]+([a-z,]+))?",
382
                      REG_EXTENDED|REG_NEWLINE);
383
        if (err)
384
                goto fail;
385
        im = &_im;
386

    
387
        do {
388
                char line[512], *l;
389
                regmatch_t match[5];
390
                char *typename, *path, *args = NULL;
391
                unsigned long flags;
392
                int type;
393

    
394
                l = fgets(line, sizeof(line), s);
395
                if (!l)
396
                        break;
397

    
398
                err = regexec(im, line, ARRAY_SIZE(match), match, 0);
399
                if (err) {
400
                        err = regexec(ws, line, ARRAY_SIZE(match), match, 0);
401
                        if (!err)
402
                                continue;
403
                        err = -EINVAL;
404
                        goto fail;
405
                }
406

    
407
                line[match[1].rm_eo] = 0;
408
                typename = line + match[1].rm_so;
409

    
410
                line[match[2].rm_eo] = 0;
411
                path = line + match[2].rm_so;
412

    
413
                if (match[4].rm_so >= 0) {
414
                        line[match[4].rm_eo] = 0;
415
                        args = line + match[4].rm_so;
416
                }
417

    
418
                type = tapdisk_disktype_find(typename);
419
                if (type < 0) {
420
                        err = type;
421
                        goto fail;
422
                }
423

    
424
                flags = 0;
425

    
426
                if (args) {
427
                        err = tapdisk_image_parse_flags(args, &flags);
428
                        if (err)
429
                                goto fail;
430
                }
431

    
432
                err = tapdisk_image_open(type, path, flags, &image);
433
                if (err)
434
                        goto fail;
435

    
436
                list_add_tail(&image->next, &head);
437
        } while (1);
438

    
439
        if (!image) {
440
                err = -EINVAL;
441
                goto fail;
442
        }
443

    
444
        err = tapdisk_image_open_parents(image);
445
        if (err)
446
                goto fail;
447

    
448
        list_splice(&head, _head);
449
out:
450
        if (im)
451
                regfree(im);
452
        if (ws)
453
                regfree(ws);
454
        if (s)
455
                fclose(s);
456

    
457
        return err;
458

    
459
fail:
460
        tapdisk_for_each_image_safe(image, next, &head)
461
                tapdisk_image_free(image);
462

    
463
        goto out;
464
}
465

    
466
int
467
tapdisk_image_open_chain(const char *desc, int flags, int prt_devnum,
468
                         struct list_head *head)
469
{
470
        const char *name;
471
        int type, err;
472

    
473
        type = tapdisk_disktype_parse_params(desc, &name);
474
        if (type >= 0)
475
                return __tapdisk_image_open_chain(type, name, flags, head,
476
                                                  prt_devnum);
477

    
478
        err = type;
479

    
480
        if (err == -ENOENT && strlen(desc) >= 3) {
481
                switch (desc[2]) {
482
                case 'c':
483
                        if (!strncmp(desc, "x-chain", strlen("x-chain")))
484
                                err = tapdisk_image_open_x_chain(name, head);
485
                        break;
486
                }
487
        }
488

    
489
        return err;
490
}
491

    
492
int
493
tapdisk_image_validate_chain(struct list_head *head)
494
{
495
        td_image_t *image, *parent;
496
        int flags, err;
497

    
498
        INFO("VBD CHAIN:\n");
499

    
500
        tapdisk_for_each_image_reverse(parent, head) {
501
                image = tapdisk_image_entry(parent->next.prev);
502

    
503
                if (image == tapdisk_image_entry(head))
504
                        break;
505

    
506
                err = td_validate_parent(image, parent);
507
                if (err)
508
                        return err;
509

    
510
                flags = tapdisk_disk_types[image->type]->flags;
511
                if (flags & DISK_TYPE_FILTER) {
512
                        image->driver->info = parent->driver->info;
513
                        image->info         = parent->info;
514
                }
515
        }
516

    
517
        tapdisk_for_each_image(image, head) {
518
                INFO("%s: type:%s(%d) storage:%s(%d)\n",
519
                     image->name,
520
                     tapdisk_disk_types[image->type]->name,
521
                     image->type,
522
                     tapdisk_storage_name(image->driver->storage),
523
                     image->driver->storage);
524
        }
525

    
526
        return 0;
527
}
528

    
529
void
530
tapdisk_image_stats(td_image_t *image, td_stats_t *st)
531
{
532
        tapdisk_stats_enter(st, '{');
533
        tapdisk_stats_field(st, "name", "s", image->name);
534

    
535
        tapdisk_stats_field(st, "hits", "[");
536
        tapdisk_stats_val(st, "llu", image->stats.hits.rd);
537
        tapdisk_stats_val(st, "llu", image->stats.hits.wr);
538
        tapdisk_stats_leave(st, ']');
539

    
540
        tapdisk_stats_field(st, "fail", "[");
541
        tapdisk_stats_val(st, "llu", image->stats.fail.rd);
542
        tapdisk_stats_val(st, "llu", image->stats.fail.wr);
543
        tapdisk_stats_leave(st, ']');
544

    
545
        tapdisk_stats_field(st, "driver", "{");
546
        tapdisk_driver_stats(image->driver, st);
547
        tapdisk_stats_leave(st, '}');
548

    
549
        tapdisk_stats_leave(st, '}');
550
}