Branch data Line data Source code
1 : : /*
2 : : * Copyright (c) 2016, Citrix Systems, Inc.
3 : : *
4 : : * All rights reserved.
5 : : *
6 : : * Redistribution and use in source and binary forms, with or without
7 : : * modification, are permitted provided that the following conditions are met:
8 : : *
9 : : * 1. Redistributions of source code must retain the above copyright
10 : : * notice, this list of conditions and the following disclaimer.
11 : : * 2. 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 : : * 3. Neither the name of the copyright holder nor the names of its
15 : : * contributors may be used to endorse or promote products derived from
16 : : * this software 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 <unistd.h>
37 : : #include <stdlib.h>
38 : : #include <stdio.h>
39 : : #include <limits.h>
40 : : #include <regex.h>
41 : : #include <glob.h>
42 : :
43 : : #include "blktap.h"
44 : : #include "tapdisk-image.h"
45 : : #include "tapdisk-driver.h"
46 : : #include "tapdisk-server.h"
47 : : #include "tapdisk-stats.h"
48 : : #include "tapdisk-interface.h"
49 : : #include "tapdisk-disktype.h"
50 : : #include "tapdisk-storage.h"
51 : : #include "util.h"
52 : :
53 : : #define DBG(_f, _a...) tlog_syslog(TLOG_DBG, _f, ##_a)
54 : : #define INFO(_f, _a...) tlog_syslog(TLOG_INFO, _f, ##_a)
55 : : #define ERR(_err, _f, _a...) tlog_error(_err, _f, ##_a)
56 : :
57 : : #define BUG() td_panic()
58 : :
59 : : #define BUG_ON(_cond) \
60 : : if (unlikely(_cond)) { \
61 : : ERR(-EINVAL, "(%s) = %d", #_cond, _cond); \
62 : : BUG(); \
63 : : }
64 : :
65 : : td_image_t *
66 : 2 : tapdisk_image_allocate(const char *file, int type, td_flag_t flags)
67 : : {
68 : : int err;
69 : : td_image_t *image;
70 : :
71 : 2 : image = calloc(1, sizeof(td_image_t));
72 [ + - ]: 2 : if (!image)
73 : : return NULL;
74 : :
75 : 2 : err = tapdisk_namedup(&image->name, file);
76 [ - + ]: 2 : if (err) {
77 : 0 : free(image);
78 : 0 : return NULL;
79 : : }
80 : :
81 : 2 : image->type = type;
82 : 2 : image->flags = flags;
83 : 2 : INIT_LIST_HEAD(&image->next);
84 : :
85 : 2 : return image;
86 : : }
87 : :
88 : : void
89 : 2 : tapdisk_image_free(td_image_t *image)
90 : : {
91 [ + - ]: 2 : if (!image)
92 : 2 : return;
93 : :
94 : 2 : list_del(&image->next);
95 : :
96 : 2 : free(image->name);
97 : 2 : tapdisk_driver_free(image->driver);
98 : 2 : free(image);
99 : : }
100 : :
101 : : int
102 : 0 : tapdisk_image_check_td_request(td_image_t *image, td_request_t treq)
103 : : {
104 : : int rdonly, err;
105 : : td_disk_info_t *info;
106 : :
107 : 0 : err = -EINVAL;
108 : :
109 : 0 : info = &image->info;
110 : 0 : rdonly = td_flag_test(image->flags, TD_OPEN_RDONLY);
111 : :
112 [ # # ]: 0 : if (treq.op != TD_OP_READ && treq.op != TD_OP_WRITE && treq.op != TD_OP_BLOCK_STATUS)
113 : : goto fail;
114 : :
115 [ # # ][ # # ]: 0 : if (treq.op == TD_OP_WRITE && rdonly) {
116 : : err = -EPERM;
117 : : goto fail;
118 : : }
119 : :
120 [ # # ][ # # ]: 0 : if (treq.secs <= 0 || treq.sec + treq.secs > info->size)
121 : : goto fail;
122 : :
123 : : return 0;
124 : :
125 : : fail:
126 [ # # ]: 0 : ERR(err, "bad td request on %s (%s, %"PRIu64"): %d at %"PRIu64,
127 : : image->name, (rdonly ? "ro" : "rw"), info->size, treq.op,
128 : : treq.sec + treq.secs);
129 : 0 : return err;
130 : :
131 : : }
132 : :
133 : : int
134 : 0 : tapdisk_image_check_request(td_image_t *image, td_vbd_request_t *vreq)
135 : : {
136 : : td_driver_t *driver;
137 : : td_disk_info_t *info;
138 : : int i, rdonly, secs, err;
139 : :
140 : 0 : driver = image->driver;
141 [ # # ]: 0 : if (!driver)
142 : : return -ENODEV;
143 : :
144 : 0 : info = &driver->info;
145 : 0 : rdonly = td_flag_test(image->flags, TD_OPEN_RDONLY);
146 : :
147 : 0 : secs = 0;
148 : :
149 [ # # ]: 0 : if (vreq->iovcnt < 0) {
150 : : err = -EINVAL;
151 : : goto fail;
152 : : }
153 : :
154 [ # # ]: 0 : for (i = 0; i < vreq->iovcnt; i++)
155 : 0 : secs += vreq->iov[i].secs;
156 : :
157 [ # # # ]: 0 : switch (vreq->op) {
158 : : case TD_OP_WRITE:
159 [ # # ]: 0 : if (rdonly) {
160 : : err = -EPERM;
161 : : goto fail;
162 : : }
163 : : /* continue */
164 : : case TD_OP_READ: /* fall through */
165 : : case TD_OP_BLOCK_STATUS:
166 [ # # ]: 0 : if (vreq->sec + secs > info->size) {
167 : : err = -EINVAL;
168 : : goto fail;
169 : : }
170 : : break;
171 : : default:
172 : : err = -EOPNOTSUPP;
173 : : goto fail;
174 : : }
175 : :
176 : : return 0;
177 : :
178 : : fail:
179 [ # # ]: 0 : ERR(err, "bad request on %s (%s, %"PRIu64"): req %s op %d at %"PRIu64,
180 : : image->name, (rdonly ? "ro" : "rw"), info->size, vreq->name,
181 : : vreq->op, vreq->sec + secs);
182 : :
183 : 0 : return err;
184 : : }
185 : :
186 : : void
187 : 1 : tapdisk_image_close(td_image_t *image)
188 : : {
189 : 1 : td_close(image);
190 : 1 : tapdisk_image_free(image);
191 : 1 : }
192 : :
193 : : int
194 : 0 : tapdisk_image_open(int type, const char *name, int flags,
195 : : struct td_vbd_encryption *encryption, td_image_t **_image)
196 : : {
197 : : td_image_t *image;
198 : : int err;
199 : :
200 : 0 : image = tapdisk_image_allocate(name, type, flags);
201 [ # # ]: 0 : if (!image) {
202 : : err = -ENOMEM;
203 : : goto fail;
204 : : }
205 : :
206 : 0 : err = td_load(image);
207 [ # # ]: 0 : if (!err)
208 : : goto done;
209 : :
210 : 0 : image->driver = tapdisk_driver_allocate(image->type,
211 : 0 : image->name,
212 : : image->flags);
213 [ # # ]: 0 : if (!image->driver) {
214 : : err = -ENOMEM;
215 : : goto fail;
216 : : }
217 : :
218 : 0 : err = td_open(image, encryption);
219 [ # # ]: 0 : if (err)
220 : : goto fail;
221 : :
222 : : done:
223 : 0 : *_image = image;
224 : 0 : return 0;
225 : :
226 : : fail:
227 [ # # ]: 0 : if (image)
228 : 0 : tapdisk_image_close(image);
229 : 0 : return err;
230 : : }
231 : :
232 : : static int
233 : 0 : tapdisk_image_open_parent(td_image_t *image, struct td_vbd_encryption *encryption,
234 : : td_image_t **_parent)
235 : : {
236 : 0 : td_image_t *parent = NULL;
237 : : td_disk_id_t id;
238 : : int err;
239 : :
240 : : memset(&id, 0, sizeof(id));
241 : 0 : id.flags = image->flags;
242 : :
243 : 0 : err = td_get_parent_id(image, &id);
244 [ # # ]: 0 : if (err == TD_NO_PARENT) {
245 : : err = 0;
246 : : goto out;
247 : : }
248 [ # # ]: 0 : if (err)
249 : 0 : return err;
250 : :
251 [ # # ]: 0 : if (((id.flags & TD_OPEN_NO_O_DIRECT) == TD_OPEN_NO_O_DIRECT) &&
252 : : ((id.flags & TD_OPEN_LOCAL_CACHE) == TD_OPEN_LOCAL_CACHE))
253 : 0 : id.flags &= ~TD_OPEN_NO_O_DIRECT;
254 : 0 : err = tapdisk_image_open(id.type, id.name, id.flags, encryption, &parent);
255 : : /* Name has been duped to driver_name */
256 : 0 : free(id.name);
257 [ # # ]: 0 : if (err)
258 : : return err;
259 : :
260 : : out:
261 : 0 : *_parent = parent;
262 : 0 : return err;
263 : : }
264 : :
265 : : static int
266 : 0 : tapdisk_image_open_parents(td_image_t *image, struct td_vbd_encryption *encryption)
267 : : {
268 : : td_image_t *parent;
269 : : int err;
270 : :
271 : : do {
272 : 0 : err = tapdisk_image_open_parent(image, encryption, &parent);
273 [ # # ]: 0 : if (err)
274 : : break;
275 : :
276 [ # # ]: 0 : if (parent) {
277 : 0 : list_add(&parent->next, &image->next);
278 : 0 : image = parent;
279 : : }
280 [ # # ]: 0 : } while (parent);
281 : :
282 : 0 : return err;
283 : : }
284 : :
285 : : void
286 : 0 : tapdisk_image_close_chain(struct list_head *list)
287 : : {
288 : : td_image_t *image, *next;
289 : :
290 [ # # ]: 0 : tapdisk_for_each_image_safe(image, next, list)
291 : 0 : tapdisk_image_close(image);
292 : 0 : }
293 : :
294 : : static int
295 : 0 : find_parent_nbd_path(int prt_devnum, char **prt_nbd_path)
296 : : {
297 : : int err;
298 : : char *pattern;
299 : 0 : glob_t glbuf = { 0 };
300 : :
301 : : /* This is glob pattern not a regex */
302 : 0 : err = asprintf(&pattern, "%s/nbd[0-9]*.%d", BLKTAP2_CONTROL_DIR, prt_devnum);
303 [ # # ]: 0 : if (err == -1) {
304 : 0 : return -errno;
305 : : }
306 : :
307 : 0 : err = glob(pattern, 0, NULL, &glbuf);
308 [ # # # ]: 0 : switch (err) {
309 : : case GLOB_NOMATCH:
310 : 0 : EPRINTF("%s, failed to find parent NBD path", pattern);
311 : : err = -ENOENT;
312 : : goto done;
313 : :
314 : : case GLOB_ABORTED:
315 : : case GLOB_NOSPACE:
316 : 0 : err = -errno;
317 : 0 : EPRINTF("%s: glob failed, err %d", pattern, err);
318 : : goto done;
319 : : }
320 : :
321 [ # # ]: 0 : if (glbuf.gl_pathc != 1) {
322 : : /* Got more than one match */
323 : 0 : err = -EINVAL;
324 : 0 : EPRINTF("%s got more than one match, %lu", pattern, glbuf.gl_pathc);
325 : : goto done;
326 : : }
327 : 0 : *prt_nbd_path = strdup(glbuf.gl_pathv[0]);
328 : 0 : err = 0;
329 : :
330 : : done:
331 : 0 : globfree(&glbuf);
332 : 0 : free(pattern);
333 : :
334 : 0 : return err;
335 : : }
336 : :
337 : : /**
338 : : * Opens the image and all of its parents.
339 : : *
340 : : * @param type DISK_TYPE_* (see tapdisk-disktype.h)
341 : : * @param name /path/to/file
342 : : * @param flags
343 : : * @param _head
344 : : * @param prt_devnum parent minor (optional)
345 : : * @returns
346 : : */
347 : : static int
348 : 0 : __tapdisk_image_open_chain(int type, const char *name, int flags,
349 : : struct td_vbd_encryption *encryption, struct list_head *_head,
350 : : int prt_devnum)
351 : : {
352 : 0 : char *prt_nbd_path = NULL;
353 : 0 : struct list_head head = LIST_HEAD_INIT(head);
354 : : td_image_t *image;
355 : : int err;
356 : :
357 : 0 : err = tapdisk_image_open(type, name, flags, encryption, &image);
358 [ # # ]: 0 : if (err)
359 : : goto fail;
360 : :
361 : 0 : list_add_tail(&image->next, &head);
362 : :
363 [ # # ]: 0 : if (unlikely(prt_devnum >= 0)) {
364 : 0 : err = find_parent_nbd_path(prt_devnum, &prt_nbd_path);
365 [ # # ]: 0 : if (err != 0) {
366 : 0 : ERR(err, "Failed to find NBD path for parent, %d", prt_devnum);
367 : : goto fail;
368 : : }
369 : : /* Don't open parent NBD in secondary mode*/
370 : 0 : flags &= ~TD_OPEN_SECONDARY;
371 : 0 : err = tapdisk_image_open(DISK_TYPE_NBD, prt_nbd_path,
372 : : flags | TD_OPEN_RDONLY,
373 : : encryption, &image);
374 [ # # ]: 0 : if (err)
375 : : goto fail;
376 : :
377 : 0 : free(prt_nbd_path);
378 : 0 : prt_nbd_path = NULL;
379 : 0 : list_add_tail(&image->next, &head);
380 : : goto done;
381 : : }
382 : :
383 : 0 : err = tapdisk_image_open_parents(image, encryption);
384 [ # # ]: 0 : if (err)
385 : : goto fail;
386 : :
387 : : done:
388 : : list_splice(&head, _head);
389 : 0 : return 0;
390 : :
391 : : fail:
392 : 0 : free(prt_nbd_path);
393 : :
394 : 0 : tapdisk_image_close_chain(&head);
395 : : return err;
396 : : }
397 : :
398 : : int
399 : 0 : tapdisk_image_parse_flags(char *args, unsigned long *_flags)
400 : : {
401 : 0 : unsigned long flags = 0;
402 : : char *token;
403 : :
404 [ # # ]: 0 : BUG_ON(!args);
405 : :
406 : : do {
407 : 0 : token = strtok(args, ",");
408 [ # # ]: 0 : if (!token)
409 : : break;
410 : :
411 [ # # ]: 0 : switch (token[0]) {
412 : : case 'r':
413 [ # # ][ # # ]: 0 : if (!strcmp(token, "ro")) {
[ # # ]
414 : : flags |= TD_OPEN_RDONLY;
415 : : break;
416 : : }
417 : : goto fail;
418 : :
419 : : default:
420 : : goto fail;
421 : : }
422 : :
423 : : args = NULL;
424 : : } while (1);
425 : :
426 : 0 : *_flags |= flags;
427 : :
428 : 0 : return 0;
429 : :
430 : : fail:
431 : 0 : ERR(-EINVAL, "Invalid token '%s'", token);
432 : 0 : return -EINVAL;
433 : : }
434 : :
435 : : static int
436 : 0 : tapdisk_image_open_x_chain(const char *path, struct td_vbd_encryption *encryption,
437 : : struct list_head *_head)
438 : : {
439 : 0 : struct list_head head = LIST_HEAD_INIT(head);
440 : 0 : td_image_t *image = NULL, *next;
441 : 0 : regex_t _im, *im = NULL, _ws, *ws = NULL;
442 : : FILE *s;
443 : : int err;
444 : :
445 : 0 : s = fopen(path, "r");
446 [ # # ]: 0 : if (!s) {
447 : 0 : err = -errno;
448 : 0 : goto fail;
449 : : }
450 : :
451 : 0 : err = regcomp(&_ws, "^[:space:]*$", REG_NOSUB);
452 [ # # ]: 0 : if (err)
453 : : goto fail;
454 : 0 : ws = &_ws;
455 : :
456 : 0 : err = regcomp(&_im,
457 : : "^([^:]+):([^ \t]+)([ \t]+([a-z,]+))?",
458 : : REG_EXTENDED|REG_NEWLINE);
459 [ # # ]: 0 : if (err)
460 : : goto fail;
461 : : im = &_im;
462 : :
463 : : do {
464 : : char line[512], *l;
465 : : regmatch_t match[5];
466 : 0 : char *typename, *path, *args = NULL;
467 : : unsigned long flags;
468 : : int type;
469 : :
470 : 0 : l = fgets(line, sizeof(line), s);
471 [ # # ]: 0 : if (!l)
472 : : break;
473 : :
474 : 0 : err = regexec(im, line, ARRAY_SIZE(match), match, 0);
475 [ # # ]: 0 : if (err) {
476 : 0 : err = regexec(ws, line, ARRAY_SIZE(match), match, 0);
477 [ # # ]: 0 : if (!err)
478 : 0 : continue;
479 : : err = -EINVAL;
480 : 0 : goto fail;
481 : : }
482 : :
483 : 0 : line[match[1].rm_eo] = 0;
484 : 0 : typename = line + match[1].rm_so;
485 : :
486 : 0 : line[match[2].rm_eo] = 0;
487 : 0 : path = line + match[2].rm_so;
488 : :
489 [ # # ]: 0 : if (match[4].rm_so >= 0) {
490 : 0 : line[match[4].rm_eo] = 0;
491 : 0 : args = line + match[4].rm_so;
492 : : }
493 : :
494 : 0 : type = tapdisk_disktype_find(typename);
495 [ # # ]: 0 : if (type < 0) {
496 : : err = type;
497 : : goto fail;
498 : : }
499 : :
500 : 0 : flags = 0;
501 : :
502 [ # # ]: 0 : if (args) {
503 : 0 : err = tapdisk_image_parse_flags(args, &flags);
504 [ # # ]: 0 : if (err)
505 : : goto fail;
506 : : }
507 : :
508 : 0 : err = tapdisk_image_open(type, path, flags, encryption, &image);
509 [ # # ]: 0 : if (err)
510 : : goto fail;
511 : :
512 : 0 : list_add_tail(&image->next, &head);
513 : : } while (1);
514 : :
515 [ # # ]: 0 : if (!image) {
516 : : err = -EINVAL;
517 : : goto fail;
518 : : }
519 : :
520 : 0 : err = tapdisk_image_open_parents(image, encryption);
521 [ # # ]: 0 : if (err)
522 : : goto fail;
523 : :
524 : : list_splice(&head, _head);
525 : : out:
526 [ # # ]: 0 : if (im)
527 : 0 : regfree(im);
528 [ # # ]: 0 : if (ws)
529 : 0 : regfree(ws);
530 [ # # ]: 0 : if (s)
531 : 0 : fclose(s);
532 : :
533 : 0 : return err;
534 : :
535 : : fail:
536 [ # # ]: 0 : tapdisk_for_each_image_safe(image, next, &head)
537 : 0 : tapdisk_image_free(image);
538 : :
539 : : goto out;
540 : : }
541 : :
542 : : int
543 : 0 : tapdisk_image_open_chain(const char *desc, int flags, int prt_devnum,
544 : : struct td_vbd_encryption *encryption, struct list_head *head)
545 : : {
546 : : const char *name;
547 : : int type, err;
548 : :
549 : 0 : type = tapdisk_disktype_parse_params(desc, &name);
550 [ # # ]: 0 : if (type >= 0)
551 : 0 : return __tapdisk_image_open_chain(type, name, flags, encryption,
552 : : head, prt_devnum);
553 : :
554 : 0 : err = type;
555 : :
556 [ # # ][ # # ]: 0 : if (err == -ENOENT && strlen(desc) >= 3) {
557 [ # # ]: 0 : switch (desc[2]) {
558 : : case 'c':
559 [ # # ]: 0 : if (!strncmp(desc, "x-chain", strlen("x-chain")))
560 : 0 : err = tapdisk_image_open_x_chain(name, encryption, head);
561 : : break;
562 : : }
563 : : }
564 : :
565 : 0 : return err;
566 : : }
567 : :
568 : : int
569 : 0 : tapdisk_image_validate_chain(struct list_head *head)
570 : : {
571 : : td_image_t *image, *parent;
572 : : int flags, err;
573 : :
574 : 0 : INFO("VBD CHAIN:\n");
575 : :
576 [ # # ]: 0 : tapdisk_for_each_image_reverse(parent, head) {
577 : 0 : image = tapdisk_image_entry(parent->next.prev);
578 : :
579 [ # # ]: 0 : if (image == tapdisk_image_entry(head))
580 : : break;
581 : :
582 : 0 : err = td_validate_parent(image, parent);
583 [ # # ]: 0 : if (err)
584 : : return err;
585 : :
586 : 0 : flags = tapdisk_disk_types[image->type]->flags;
587 [ # # ]: 0 : if (flags & DISK_TYPE_FILTER) {
588 : 0 : image->driver->info = parent->driver->info;
589 : 0 : image->info = parent->info;
590 : : }
591 : : }
592 : :
593 [ # # ]: 0 : tapdisk_for_each_image(image, head) {
594 : 0 : INFO("%s: type:%s(%d) storage:%s(%d)\n",
595 : : image->name,
596 : : tapdisk_disk_types[image->type]->name,
597 : : image->type,
598 : : tapdisk_storage_name(image->driver->storage),
599 : : image->driver->storage);
600 : : }
601 : :
602 : : return 0;
603 : : }
604 : :
605 : : void
606 : 0 : tapdisk_image_stats(td_image_t *image, td_stats_t *st)
607 : : {
608 : 0 : tapdisk_stats_enter(st, '{');
609 : 0 : tapdisk_stats_field(st, "name", "s", image->name);
610 : :
611 : 0 : tapdisk_stats_field(st, "hits", "[");
612 : 0 : tapdisk_stats_val(st, "llu", image->stats.hits.rd);
613 : 0 : tapdisk_stats_val(st, "llu", image->stats.hits.wr);
614 : 0 : tapdisk_stats_leave(st, ']');
615 : :
616 : 0 : tapdisk_stats_field(st, "fail", "[");
617 : 0 : tapdisk_stats_val(st, "llu", image->stats.fail.rd);
618 : 0 : tapdisk_stats_val(st, "llu", image->stats.fail.wr);
619 : 0 : tapdisk_stats_leave(st, ']');
620 : :
621 : 0 : tapdisk_stats_field(st, "driver", "{");
622 : 0 : tapdisk_driver_stats(image->driver, st);
623 : 0 : tapdisk_stats_leave(st, '}');
624 : :
625 : 0 : tapdisk_stats_leave(st, '}');
626 : 0 : }
|