storage_backend.c 19.9 KB
Newer Older
1
/*
2
 * storage_backend.c: internal storage driver backend contract
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
 *
 * Copyright (C) 2007-2008 Red Hat, Inc.
 * Copyright (C) 2007-2008 Daniel P. Berrange
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
 *
 * Author: Daniel P. Berrange <berrange@redhat.com>
 */

#include <config.h>

#include <string.h>
27
#include <stdio.h>
D
Daniel P. Berrange 已提交
28
#if HAVE_REGEX_H
29
#include <regex.h>
D
Daniel P. Berrange 已提交
30
#endif
31
#include <sys/types.h>
D
Daniel P. Berrange 已提交
32
#if HAVE_SYS_WAIT_H
33
#include <sys/wait.h>
D
Daniel P. Berrange 已提交
34
#endif
35
#include <unistd.h>
36 37 38
#include <fcntl.h>
#include <stdint.h>
#include <sys/stat.h>
39
#include <sys/param.h>
40 41 42 43 44
#include <dirent.h>

#if HAVE_SELINUX
#include <selinux/selinux.h>
#endif
45

46
#include "virterror_internal.h"
D
Daniel P. Berrange 已提交
47 48
#include "util.h"
#include "memory.h"
49
#include "node_device.h"
D
Daniel P. Berrange 已提交
50 51

#include "storage_backend.h"
52

53 54 55 56 57 58
#if WITH_STORAGE_LVM
#include "storage_backend_logical.h"
#endif
#if WITH_STORAGE_ISCSI
#include "storage_backend_iscsi.h"
#endif
59 60 61
#if WITH_STORAGE_SCSI
#include "storage_backend_scsi.h"
#endif
62 63 64 65 66 67 68
#if WITH_STORAGE_DISK
#include "storage_backend_disk.h"
#endif
#if WITH_STORAGE_DIR
#include "storage_backend_fs.h"
#endif

69 70 71
#ifndef DEV_BSIZE
#define DEV_BSIZE 512
#endif
72

73 74
#define VIR_FROM_THIS VIR_FROM_STORAGE

75 76 77 78 79 80 81 82 83 84 85 86 87 88
static virStorageBackendPtr backends[] = {
#if WITH_STORAGE_DIR
    &virStorageBackendDirectory,
#endif
#if WITH_STORAGE_FS
    &virStorageBackendFileSystem,
    &virStorageBackendNetFileSystem,
#endif
#if WITH_STORAGE_LVM
    &virStorageBackendLogical,
#endif
#if WITH_STORAGE_ISCSI
    &virStorageBackendISCSI,
#endif
89 90 91
#if WITH_STORAGE_SCSI
    &virStorageBackendSCSI,
#endif
92 93 94 95 96
#if WITH_STORAGE_DISK
    &virStorageBackendDisk,
#endif
    NULL
};
97 98


99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
#if defined(UDEVADM) || defined(UDEVSETTLE)
void virWaitForDevices(virConnectPtr conn)
{
#ifdef UDEVADM
    const char *const settleprog[] = { UDEVADM, "settle", NULL };
#else
    const char *const settleprog[] = { UDEVSETTLE, NULL };
#endif
    int exitstatus;

    if (access(settleprog[0], X_OK) != 0)
        return;

    /*
     * NOTE: we ignore errors here; this is just to make sure that any device
     * nodes that are being created finish before we try to scan them.
     * If this fails for any reason, we still have the backup of polling for
     * 5 seconds for device nodes.
     */
    virRun(conn, settleprog, &exitstatus);
}
#else
void virWaitForDevices(virConnectPtr conn ATTRIBUTE_UNUSED) {}
#endif


125 126
virStorageBackendPtr
virStorageBackendForType(int type) {
127
    unsigned int i;
128
    for (i = 0; backends[i]; i++)
129 130 131
        if (backends[i]->type == type)
            return backends[i];

132 133 134 135 136 137
    virStorageReportError(NULL, VIR_ERR_INTERNAL_ERROR,
                          _("missing backend for pool type %d"), type);
    return NULL;
}


138
int
139 140 141 142
virStorageBackendUpdateVolTargetInfo(virConnectPtr conn,
                                     virStorageVolTargetPtr target,
                                     unsigned long long *allocation,
                                     unsigned long long *capacity)
143 144 145
{
    int ret, fd;

146
    if ((fd = open(target->path, O_RDONLY)) < 0) {
147 148
        virReportSystemError(conn, errno,
                             _("cannot open volume '%s'"),
149
                             target->path);
150 151 152
        return -1;
    }

153 154 155 156 157
    ret = virStorageBackendUpdateVolTargetInfoFD(conn,
                                                 target,
                                                 fd,
                                                 allocation,
                                                 capacity);
158 159 160 161 162 163

    close(fd);

    return ret;
}

164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185
int
virStorageBackendUpdateVolInfo(virConnectPtr conn,
                               virStorageVolDefPtr vol,
                               int withCapacity)
{
    int ret;

    if ((ret = virStorageBackendUpdateVolTargetInfo(conn,
                                                    &vol->target,
                                                    &vol->allocation,
                                                    withCapacity ? &vol->capacity : NULL)) < 0)
        return ret;

    if (vol->backingStore.path &&
        (ret = virStorageBackendUpdateVolTargetInfo(conn,
                                                    &vol->backingStore,
                                                    NULL, NULL)) < 0)
        return ret;

    return 0;
}

186 187 188 189 190 191 192 193 194 195 196
/*
 * virStorageBackendUpdateVolTargetInfoFD:
 * @conn: connection to report errors on
 * @target: target definition ptr of volume to update
 * @fd: fd of storage volume to update
 * @allocation: If not NULL, updated allocation information will be stored
 * @capacity: If not NULL, updated capacity info will be stored
 *
 * Returns 0 for success-1 on a legitimate error condition,
 *    -2 if passed FD isn't a regular, char, or block file.
 */
197
int
198 199 200 201 202
virStorageBackendUpdateVolTargetInfoFD(virConnectPtr conn,
                                       virStorageVolTargetPtr target,
                                       int fd,
                                       unsigned long long *allocation,
                                       unsigned long long *capacity)
203 204 205 206 207 208 209
{
    struct stat sb;
#if HAVE_SELINUX
    security_context_t filecon = NULL;
#endif

    if (fstat(fd, &sb) < 0) {
210 211
        virReportSystemError(conn, errno,
                             _("cannot stat file '%s'"),
212
                             target->path);
213 214 215 216 217 218 219 220
        return -1;
    }

    if (!S_ISREG(sb.st_mode) &&
        !S_ISCHR(sb.st_mode) &&
        !S_ISBLK(sb.st_mode))
        return -2;

221 222
    if (allocation) {
        if (S_ISREG(sb.st_mode)) {
D
Daniel P. Berrange 已提交
223
#ifndef __MINGW32__
224
            *allocation = (unsigned long long)sb.st_blocks *
225
                          (unsigned long long)DEV_BSIZE;
D
Daniel P. Berrange 已提交
226
#else
227
            *allocation = sb.st_size;
D
Daniel P. Berrange 已提交
228
#endif
229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250
            /* Regular files may be sparse, so logical size (capacity) is not same
             * as actual allocation above
             */
            if (capacity)
                *capacity = sb.st_size;
        } else {
            off_t end;
            /* XXX this is POSIX compliant, but doesn't work for for CHAR files,
             * only BLOCK. There is a Linux specific ioctl() for getting
             * size of both CHAR / BLOCK devices we should check for in
             * configure
             */
            end = lseek(fd, 0, SEEK_END);
            if (end == (off_t)-1) {
                virReportSystemError(conn, errno,
                                     _("cannot seek to end of file '%s'"),
                                     target->path);
                return -1;
            }
            *allocation = end;
            if (capacity)
                *capacity = end;
251 252 253
        }
    }

254 255 256
    target->perms.mode = sb.st_mode & S_IRWXUGO;
    target->perms.uid = sb.st_uid;
    target->perms.gid = sb.st_gid;
257

258
    VIR_FREE(target->perms.label);
259 260

#if HAVE_SELINUX
261
    /* XXX: make this a security driver call */
262
    if (fgetfilecon(fd, &filecon) == -1) {
263
        if (errno != ENODATA && errno != ENOTSUP) {
264 265
            virReportSystemError(conn, errno,
                                 _("cannot get file context of '%s'"),
266
                                 target->path);
267 268
            return -1;
        } else {
269
            target->perms.label = NULL;
270 271
        }
    } else {
272 273
        target->perms.label = strdup(filecon);
        if (target->perms.label == NULL) {
274
            virReportOOMError(conn);
275 276 277
            return -1;
        }
        freecon(filecon);
278 279
    }
#else
280
    target->perms.label = NULL;
281 282 283 284 285
#endif

    return 0;
}

286 287
void virStorageBackendWaitForDevices(virConnectPtr conn)
{
288
    virWaitForDevices(conn);
289
    return;
290 291
}

292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307
/*
 * Given a volume path directly in /dev/XXX, iterate over the
 * entries in the directory pool->def->target.path and find the
 * first symlink pointing to the volume path.
 *
 * If, the target.path is /dev/, then return the original volume
 * path.
 *
 * If no symlink is found, then return the original volume path
 *
 * Typically target.path is one of the /dev/disk/by-XXX dirs
 * with stable paths.
 */
char *
virStorageBackendStablePath(virConnectPtr conn,
                            virStoragePoolObjPtr pool,
308
                            const char *devpath)
309 310 311
{
    DIR *dh;
    struct dirent *dent;
312
    char *stablepath;
313
    int opentries = 0;
314 315 316 317 318

    /* Short circuit if pool has no target, or if its /dev */
    if (pool->def->target.path == NULL ||
        STREQ(pool->def->target.path, "/dev") ||
        STREQ(pool->def->target.path, "/dev/"))
319
        goto ret_strdup;
320

321 322 323 324 325 326
    /* Skip whole thing for a pool which isn't in /dev
     * so we don't mess will filesystem/dir based pools
     */
    if (!STRPREFIX(pool->def->target.path, "/dev"))
        goto ret_strdup;

327 328 329
    /* We loop here because /dev/disk/by-{id,path} may not have existed
     * before we started this operation, so we have to give it some time to
     * get created.
330
     */
331
 reopen:
332
    if ((dh = opendir(pool->def->target.path)) == NULL) {
333 334 335 336 337
        opentries++;
        if (errno == ENOENT && opentries < 50) {
            usleep(100 * 1000);
            goto reopen;
        }
338 339 340
        virReportSystemError(conn, errno,
                             _("cannot read dir '%s'"),
                             pool->def->target.path);
341 342 343
        return NULL;
    }

344 345 346 347 348
    /* The pool is pointing somewhere like /dev/disk/by-path
     * or /dev/disk/by-id, so we need to check all symlinks in
     * the target directory and figure out which one points
     * to this device node
     */
349 350 351 352
    while ((dent = readdir(dh)) != NULL) {
        if (dent->d_name[0] == '.')
            continue;

353 354 355
        if (virAsprintf(&stablepath, "%s/%s",
                        pool->def->target.path,
                        dent->d_name) == -1) {
356
            virReportOOMError(conn);
357 358 359 360 361 362 363 364 365
            closedir(dh);
            return NULL;
        }

        if (virFileLinkPointsTo(stablepath, devpath)) {
            closedir(dh);
            return stablepath;
        }

366
        VIR_FREE(stablepath);
367 368 369 370
    }

    closedir(dh);

371
 ret_strdup:
372 373 374
    /* Couldn't find any matching stable link so give back
     * the original non-stable dev path
     */
375 376 377 378

    stablepath = strdup(devpath);

    if (stablepath == NULL)
379
        virReportOOMError(conn);
380 381

    return stablepath;
382 383
}

D
Daniel P. Berrange 已提交
384 385

#ifndef __MINGW32__
386 387 388 389
/*
 * Run an external program.
 *
 * Read its output and apply a series of regexes to each line
R
Richard W.M. Jones 已提交
390
 * When the entire set of regexes has matched consecutively
391 392 393 394 395
 * then run a callback passing in all the matches
 */
int
virStorageBackendRunProgRegex(virConnectPtr conn,
                              virStoragePoolObjPtr pool,
396
                              const char *const*prog,
397 398 399 400
                              int nregex,
                              const char **regex,
                              int *nvars,
                              virStorageBackendListVolRegexFunc func,
401 402
                              void *data,
                              int *outexit)
403
{
404 405
    int fd = -1, exitstatus, err, failed = 1;
    pid_t child = 0;
406 407 408 409 410 411 412 413 414
    FILE *list = NULL;
    regex_t *reg;
    regmatch_t *vars = NULL;
    char line[1024];
    int maxReg = 0, i, j;
    int totgroups = 0, ngroup = 0, maxvars = 0;
    char **groups;

    /* Compile all regular expressions */
415
    if (VIR_ALLOC_N(reg, nregex) < 0) {
416
        virReportOOMError(conn);
417 418 419 420 421 422 423 424 425 426 427 428
        return -1;
    }

    for (i = 0 ; i < nregex ; i++) {
        err = regcomp(&reg[i], regex[i], REG_EXTENDED);
        if (err != 0) {
            char error[100];
            regerror(err, &reg[i], error, sizeof(error));
            virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
                                  _("Failed to compile regex %s"), error);
            for (j = 0 ; j <= i ; j++)
                regfree(&reg[j]);
429
            VIR_FREE(reg);
430 431 432 433 434 435 436 437 438 439
            return -1;
        }

        totgroups += nvars[i];
        if (nvars[i] > maxvars)
            maxvars = nvars[i];

    }

    /* Storage for matched variables */
440
    if (VIR_ALLOC_N(groups, totgroups) < 0) {
441
        virReportOOMError(conn);
442 443
        goto cleanup;
    }
444
    if (VIR_ALLOC_N(vars, maxvars+1) < 0) {
445
        virReportOOMError(conn);
446 447 448 449 450
        goto cleanup;
    }


    /* Run the program and capture its output */
451 452
    if (virExec(conn, prog, NULL, NULL,
                &child, -1, &fd, NULL, VIR_EXEC_NONE) < 0) {
453 454 455 456 457
        goto cleanup;
    }

    if ((list = fdopen(fd, "r")) == NULL) {
        virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
J
Jim Meyering 已提交
458
                              "%s", _("cannot read fd"));
459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480
        goto cleanup;
    }

    while (fgets(line, sizeof(line), list) != NULL) {
        /* Strip trailing newline */
        int len = strlen(line);
        if (len && line[len-1] == '\n')
            line[len-1] = '\0';

        for (i = 0 ; i <= maxReg && i < nregex ; i++) {
            if (regexec(&reg[i], line, nvars[i]+1, vars, 0) == 0) {
                maxReg++;

                if (i == 0)
                    ngroup = 0;

                /* NULL terminate each captured group in the line */
                for (j = 0 ; j < nvars[i] ; j++) {
                    /* NB vars[0] is the full pattern, so we offset j by 1 */
                    line[vars[j+1].rm_eo] = '\0';
                    if ((groups[ngroup++] =
                         strdup(line + vars[j+1].rm_so)) == NULL) {
481
                        virReportOOMError(conn);
482 483 484 485 486 487 488 489 490 491
                        goto cleanup;
                    }
                }

                /* We're matching on the last regex, so callback time */
                if (i == (nregex-1)) {
                    if (((*func)(conn, pool, groups, data)) < 0)
                        goto cleanup;

                    /* Release matches & restart to matching the first regex */
492
                    for (j = 0 ; j < totgroups ; j++)
493
                        VIR_FREE(groups[j]);
494 495 496 497 498 499 500 501 502 503 504 505
                    maxReg = 0;
                    ngroup = 0;
                }
            }
        }
    }

    failed = 0;

 cleanup:
    if (groups) {
        for (j = 0 ; j < totgroups ; j++)
506 507
            VIR_FREE(groups[j]);
        VIR_FREE(groups);
508
    }
509
    VIR_FREE(vars);
510 511 512 513

    for (i = 0 ; i < nregex ; i++)
        regfree(&reg[i]);

514
    VIR_FREE(reg);
515 516 517 518 519 520 521 522 523 524 525 526 527

    if (list)
        fclose(list);
    else
        close(fd);

    while ((err = waitpid(child, &exitstatus, 0) == -1) && errno == EINTR);

    /* Don't bother checking exit status if we already failed */
    if (failed)
        return -1;

    if (err == -1) {
528 529 530
        virReportSystemError(conn, errno,
                             _("failed to wait for command '%s'"),
                             prog[0]);
531 532 533
        return -1;
    } else {
        if (WIFEXITED(exitstatus)) {
534 535
            if (outexit != NULL)
                *outexit = WEXITSTATUS(exitstatus);
536 537
        } else {
            virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
J
Jim Meyering 已提交
538
                                  "%s", _("command did not exit cleanly"));
539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565
            return -1;
        }
    }

    return 0;
}

/*
 * Run an external program and read from its standard output
 * a stream of tokens from IN_STREAM, applying FUNC to
 * each successive sequence of N_COLUMNS tokens.
 * If FUNC returns < 0, stop processing input and return -1.
 * Return -1 if N_COLUMNS == 0.
 * Return -1 upon memory allocation error.
 * If the number of input tokens is not a multiple of N_COLUMNS,
 * then the final FUNC call will specify a number smaller than N_COLUMNS.
 * If there are no input tokens (empty input), call FUNC with N_COLUMNS == 0.
 */
int
virStorageBackendRunProgNul(virConnectPtr conn,
                            virStoragePoolObjPtr pool,
                            const char **prog,
                            size_t n_columns,
                            virStorageBackendListVolNulFunc func,
                            void *data)
{
    size_t n_tok = 0;
566 567
    int fd = -1, exitstatus;
    pid_t child = 0;
568 569 570 571 572 573 574 575 576
    FILE *fp = NULL;
    char **v;
    int err = -1;
    int w_err;
    int i;

    if (n_columns == 0)
        return -1;

577
    if (VIR_ALLOC_N(v, n_columns) < 0) {
578
        virReportOOMError(conn);
579 580 581 582 583 584
        return -1;
    }
    for (i = 0; i < n_columns; i++)
        v[i] = NULL;

    /* Run the program and capture its output */
585 586
    if (virExec(conn, prog, NULL, NULL,
                &child, -1, &fd, NULL, VIR_EXEC_NONE) < 0) {
587 588 589 590 591
        goto cleanup;
    }

    if ((fp = fdopen(fd, "r")) == NULL) {
        virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
J
Jim Meyering 已提交
592
                              "%s", _("cannot read fd"));
593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624
        goto cleanup;
    }

    while (1) {
        char *buf = NULL;
        size_t buf_len = 0;
        /* Be careful: even when it returns -1,
           this use of getdelim allocates memory.  */
        ssize_t tok_len = getdelim (&buf, &buf_len, 0, fp);
        v[n_tok] = buf;
        if (tok_len < 0) {
            /* Maybe EOF, maybe an error.
               If n_tok > 0, then we know it's an error.  */
            if (n_tok && func (conn, pool, n_tok, v, data) < 0)
                goto cleanup;
            break;
        }
        ++n_tok;
        if (n_tok == n_columns) {
            if (func (conn, pool, n_tok, v, data) < 0)
                goto cleanup;
            n_tok = 0;
            for (i = 0; i < n_columns; i++) {
                free (v[i]);
                v[i] = NULL;
            }
        }
    }

    if (feof (fp))
        err = 0;
    else
625 626
        virReportSystemError(conn, errno,
                             _("read error on pipe to '%s'"), prog[0]);
627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645

 cleanup:
    for (i = 0; i < n_columns; i++)
        free (v[i]);
    free (v);

    if (fp)
        fclose (fp);
    else
        close (fd);

    while ((w_err = waitpid (child, &exitstatus, 0) == -1) && errno == EINTR)
        /* empty */ ;

    /* Don't bother checking exit status if we already failed */
    if (err < 0)
        return -1;

    if (w_err == -1) {
646 647 648
        virReportSystemError(conn, errno,
                             _("failed to wait for command '%s'"),
                             prog[0]);
649 650 651 652 653 654 655 656 657 658 659
        return -1;
    } else {
        if (WIFEXITED(exitstatus)) {
            if (WEXITSTATUS(exitstatus) != 0) {
                virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
                                      _("non-zero exit status from command %d"),
                                      WEXITSTATUS(exitstatus));
                return -1;
            }
        } else {
            virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
J
Jim Meyering 已提交
660
                                  "%s", _("command did not exit cleanly"));
661 662 663 664 665 666
            return -1;
        }
    }

    return 0;
}
D
Daniel P. Berrange 已提交
667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696

#else

int
virStorageBackendRunProgRegex(virConnectPtr conn,
                              virStoragePoolObjPtr pool ATTRIBUTE_UNUSED,
                              const char *const*prog ATTRIBUTE_UNUSED,
                              int nregex ATTRIBUTE_UNUSED,
                              const char **regex ATTRIBUTE_UNUSED,
                              int *nvars ATTRIBUTE_UNUSED,
                              virStorageBackendListVolRegexFunc func ATTRIBUTE_UNUSED,
                              void *data ATTRIBUTE_UNUSED,
                              int *outexit ATTRIBUTE_UNUSED)
{
    virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, _("%s not implemented on Win32"), __FUNCTION__);
    return -1;
}

int
virStorageBackendRunProgNul(virConnectPtr conn,
                            virStoragePoolObjPtr pool ATTRIBUTE_UNUSED,
                            const char **prog ATTRIBUTE_UNUSED,
                            size_t n_columns ATTRIBUTE_UNUSED,
                            virStorageBackendListVolNulFunc func ATTRIBUTE_UNUSED,
                            void *data ATTRIBUTE_UNUSED)
{
    virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, _("%s not implemented on Win32"), __FUNCTION__);
    return -1;
}
#endif