storage_backend_fs.c 29.9 KB
Newer Older
1 2 3
/*
 * storage_backend_fs.c: storage backend for FS and directory handling
 *
4
 * Copyright (C) 2007-2009 Red Hat, Inc.
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
 * 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 <sys/statvfs.h>
#include <sys/types.h>
#include <sys/stat.h>
29
#include <stdbool.h>
30 31 32 33 34 35 36
#include <stdio.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

37 38 39 40
#include <libxml/parser.h>
#include <libxml/tree.h>
#include <libxml/xpath.h>

41
#include "virterror_internal.h"
42 43
#include "storage_backend_fs.h"
#include "storage_conf.h"
44
#include "storage_file.h"
45
#include "util.h"
46
#include "memory.h"
47
#include "xml.h"
48

49
#define VIR_FROM_THIS VIR_FROM_STORAGE
50

51 52 53 54 55 56 57 58 59
static int
virStorageBackendProbeTarget(virConnectPtr conn,
                             virStorageVolTargetPtr target,
                             char **backingStore,
                             unsigned long long *allocation,
                             unsigned long long *capacity,
                             virStorageEncryptionPtr *encryption)
{
    int fd, ret;
60
    virStorageFileMetadata meta;
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78

    if (encryption)
        *encryption = NULL;

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

    if ((ret = virStorageBackendUpdateVolTargetInfoFD(conn, target, fd,
                                                      allocation,
                                                      capacity)) < 0) {
        close(fd);
        return ret; /* Take care to propagate ret, it is not always -1 */
    }

79 80
    memset(&meta, 0, sizeof(meta));

81
    if (virStorageFileGetMetadataFromFD(conn, target->path, fd, &meta) < 0) {
82 83 84 85 86 87
        close(fd);
        return -1;
    }

    close(fd);

88 89 90 91 92 93 94 95 96 97 98 99 100
    target->format = meta.format;

    if (backingStore) {
        *backingStore = meta.backingStore;
        meta.backingStore = NULL;
    }

    VIR_FREE(meta.backingStore);

    if (capacity && meta.capacity)
        *capacity = meta.capacity;

    if (encryption != NULL && meta.encrypted) {
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
        if (VIR_ALLOC(*encryption) < 0) {
            virReportOOMError(conn);
            if (backingStore)
                VIR_FREE(*backingStore);
            return -1;
        }

        switch (target->format) {
        case VIR_STORAGE_FILE_QCOW:
        case VIR_STORAGE_FILE_QCOW2:
            (*encryption)->format = VIR_STORAGE_ENCRYPTION_FORMAT_QCOW;
            break;
        default:
            break;
        }

        /* XXX ideally we'd fill in secret UUID here
         * but we cannot guarentee 'conn' is non-NULL
         * at this point in time :-(  So we only fill
         * in secrets when someone first queries a vol
         */
    }

124 125 126 127
    return 0;
}

#if WITH_STORAGE_FS
128 129 130

#include <mntent.h>

131 132
struct _virNetfsDiscoverState {
    const char *host;
133
    virStoragePoolSourceList list;
134 135 136 137 138
};

typedef struct _virNetfsDiscoverState virNetfsDiscoverState;

static int
139
virStorageBackendFileSystemNetFindPoolSourcesFunc(virConnectPtr conn,
140 141 142 143 144 145
                                                  virStoragePoolObjPtr pool ATTRIBUTE_UNUSED,
                                                  char **const groups,
                                                  void *data)
{
    virNetfsDiscoverState *state = data;
    const char *name, *path;
146 147
    virStoragePoolSource *src = NULL;
    int ret = -1;
148 149 150 151 152 153 154

    path = groups[0];

    name = strrchr(path, '/');
    if (name == NULL) {
        virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
                              _("invalid netfs path (no /): %s"), path);
155
        goto cleanup;
156 157 158 159 160
    }
    name += 1;
    if (*name == '\0') {
        virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
                              _("invalid netfs path (ends in /): %s"), path);
161
        goto cleanup;
162 163
    }

164 165
    if (!(src = virStoragePoolSourceListNewSource(conn, &state->list)))
        goto cleanup;
166

167
    if (!(src->host.name = strdup(state->host)) ||
168 169 170 171
        !(src->dir = strdup(path))) {
        virReportOOMError(conn);
        goto cleanup;
    }
172
    src->format = VIR_STORAGE_POOL_NETFS_NFS;
173

174 175 176 177 178 179
    src = NULL;
    ret = 0;
cleanup:
    if (src)
        virStoragePoolSourceFree(src);
    return ret;
180 181
}

182

183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
static char *
virStorageBackendFileSystemNetFindPoolSources(virConnectPtr conn,
                                              const char *srcSpec,
                                              unsigned int flags ATTRIBUTE_UNUSED)
{
    /*
     *  # showmount --no-headers -e HOSTNAME
     *  /tmp   *
     *  /A dir demo1.foo.bar,demo2.foo.bar
     *
     * Extract directory name (including possible interior spaces ...).
     */

    const char *regexes[] = {
        "^(/.*\\S) +\\S+$"
    };
    int vars[] = {
        1
    };
    xmlDocPtr doc = NULL;
    xmlXPathContextPtr xpath_ctxt = NULL;
204 205 206 207 208 209 210 211
    virNetfsDiscoverState state = {
        .host = NULL,
        .list = {
            .type = VIR_STORAGE_POOL_NETFS,
            .nsources = 0,
            .sources = NULL
        }
    };
212 213 214
    const char *prog[] = { SHOWMOUNT, "--no-headers", "--exports", NULL, NULL };
    int exitstatus;
    char *retval = NULL;
215
    unsigned int i;
216 217 218 219 220 221 222 223 224 225 226

    doc = xmlReadDoc((const xmlChar *)srcSpec, "srcSpec.xml", NULL,
                     XML_PARSE_NOENT | XML_PARSE_NONET |
                     XML_PARSE_NOERROR | XML_PARSE_NOWARNING);
    if (doc == NULL) {
        virStorageReportError(conn, VIR_ERR_XML_ERROR, "%s", _("bad <source> spec"));
        goto cleanup;
    }

    xpath_ctxt = xmlXPathNewContext(doc);
    if (xpath_ctxt == NULL) {
227
        virReportOOMError(conn);
228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243
        goto cleanup;
    }

    state.host = virXPathString(conn, "string(/source/host/@name)", xpath_ctxt);
    if (!state.host || !state.host[0]) {
        virStorageReportError(conn, VIR_ERR_XML_ERROR, "%s",
                              _("missing <host> in <source> spec"));
        goto cleanup;
    }
    prog[3] = state.host;

    if (virStorageBackendRunProgRegex(conn, NULL, prog, 1, regexes, vars,
                                      virStorageBackendFileSystemNetFindPoolSourcesFunc,
                                      &state, &exitstatus) < 0)
        goto cleanup;

244
    retval = virStoragePoolSourceListFormat(conn, &state.list);
245
    if (retval == NULL) {
246
        virReportOOMError(conn);
247 248 249 250
        goto cleanup;
    }

 cleanup:
251 252 253 254 255 256
    for (i = 0; i < state.list.nsources; i++)
        virStoragePoolSourceFree(&state.list.sources[i]);

    VIR_FREE(state.list.sources);
    VIR_FREE(state.host);

257 258 259 260 261 262 263
    xmlFreeDoc(doc);
    xmlXPathFreeContext(xpath_ctxt);

    return retval;
}


264 265 266 267 268 269 270 271 272 273 274 275
/**
 * @conn connection to report errors against
 * @pool storage pool to check for status
 *
 * Determine if a storage pool is already mounted
 *
 * Return 0 if not mounted, 1 if mounted, -1 on error
 */
static int
virStorageBackendFileSystemIsMounted(virConnectPtr conn,
                                     virStoragePoolObjPtr pool) {
    FILE *mtab;
276 277
    struct mntent ent;
    char buf[1024];
278 279

    if ((mtab = fopen(_PATH_MOUNTED, "r")) == NULL) {
280 281 282
        virReportSystemError(conn, errno,
                             _("cannot read mount list '%s'"),
                             _PATH_MOUNTED);
283 284 285
        return -1;
    }

286 287
    while ((getmntent_r(mtab, &ent, buf, sizeof(buf))) != NULL) {
        if (STREQ(ent.mnt_dir, pool->def->target.path)) {
288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309
            fclose(mtab);
            return 1;
        }
    }

    fclose(mtab);
    return 0;
}

/**
 * @conn connection to report errors against
 * @pool storage pool to mount
 *
 * Ensure that a FS storage pool is mounted on its target location.
 * If already mounted, this is a no-op
 *
 * Returns 0 if successfully mounted, -1 on error
 */
static int
virStorageBackendFileSystemMount(virConnectPtr conn,
                                 virStoragePoolObjPtr pool) {
    char *src;
310
    char *options;
311 312 313 314 315 316 317
    const char **mntargv;

    /* 'mount -t auto' doesn't seem to auto determine nfs (or cifs),
     *  while plain 'mount' does. We have to craft separate argvs to
     *  accommodate this */
    int netauto = (pool->def->type == VIR_STORAGE_POOL_NETFS &&
                   pool->def->source.format == VIR_STORAGE_POOL_NETFS_AUTO);
318 319 320 321
    int glusterfs = (pool->def->type == VIR_STORAGE_POOL_NETFS &&
                 pool->def->source.format == VIR_STORAGE_POOL_NETFS_GLUSTERFS);

    int option_index;
322 323 324 325 326 327 328 329 330 331
    int source_index;

    const char *netfs_auto_argv[] = {
        MOUNT,
        NULL, /* source path */
        pool->def->target.path,
        NULL,
    };

    const char *fs_argv[] =  {
332 333 334
        MOUNT,
        "-t",
        pool->def->type == VIR_STORAGE_POOL_FS ?
335 336
        virStoragePoolFormatFileSystemTypeToString(pool->def->source.format) :
        virStoragePoolFormatFileSystemNetTypeToString(pool->def->source.format),
337 338
        NULL, /* Fill in shortly - careful not to add extra fields
                 before this */
339 340 341
        pool->def->target.path,
        NULL,
    };
342

343 344 345 346 347 348 349 350 351 352 353 354 355
    const char *glusterfs_argv[] = {
        MOUNT,
        "-t",
        pool->def->type == VIR_STORAGE_POOL_FS ?
        virStoragePoolFormatFileSystemTypeToString(pool->def->source.format) :
        virStoragePoolFormatFileSystemNetTypeToString(pool->def->source.format),
        NULL,
        "-o",
        NULL,
        pool->def->target.path,
        NULL,
    };

356 357 358
    if (netauto) {
        mntargv = netfs_auto_argv;
        source_index = 1;
359 360 361 362
    } else if (glusterfs) {
        mntargv = glusterfs_argv;
        source_index = 3;
        option_index = 5;
363 364 365 366 367
    } else {
        mntargv = fs_argv;
        source_index = 3;
    }

368 369 370 371 372
    int ret;

    if (pool->def->type == VIR_STORAGE_POOL_NETFS) {
        if (pool->def->source.host.name == NULL) {
            virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
J
Jim Meyering 已提交
373
                                  "%s", _("missing source host"));
374 375 376 377
            return -1;
        }
        if (pool->def->source.dir == NULL) {
            virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
J
Jim Meyering 已提交
378
                                  "%s", _("missing source path"));
379 380 381 382 383
            return -1;
        }
    } else {
        if (pool->def->source.ndevice != 1) {
            virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
J
Jim Meyering 已提交
384
                                  "%s", _("missing source device"));
385 386 387 388
            return -1;
        }
    }

J
Jim Meyering 已提交
389
    /* Short-circuit if already mounted */
390 391 392 393 394 395 396 397
    if ((ret = virStorageBackendFileSystemIsMounted(conn, pool)) != 0) {
        if (ret < 0)
            return -1;
        else
            return 0;
    }

    if (pool->def->type == VIR_STORAGE_POOL_NETFS) {
398
        if (pool->def->source.format == VIR_STORAGE_POOL_NETFS_GLUSTERFS) {
399 400 401 402 403
            if (virAsprintf(&options, "direct-io-mode=1") == -1) {
                virReportOOMError(conn);
                return -1;
            }
        }
404 405 406
        if (virAsprintf(&src, "%s:%s",
                        pool->def->source.host.name,
                        pool->def->source.dir) == -1) {
407
            virReportOOMError(conn);
408 409
            return -1;
        }
410

411
    } else {
412
        if ((src = strdup(pool->def->source.devices[0].path)) == NULL) {
413
            virReportOOMError(conn);
414 415
            return -1;
        }
416
    }
417
    mntargv[source_index] = src;
418

419 420 421 422
    if (glusterfs) {
        mntargv[option_index] = options;
    }

423
    if (virRun(conn, mntargv, NULL) < 0) {
424
        VIR_FREE(src);
425 426
        return -1;
    }
427
    VIR_FREE(src);
428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448
    return 0;
}

/**
 * @conn connection to report errors against
 * @pool storage pool to unmount
 *
 * Ensure that a FS storage pool is not mounted on its target location.
 * If already unmounted, this is a no-op
 *
 * Returns 0 if successfully unmounted, -1 on error
 */
static int
virStorageBackendFileSystemUnmount(virConnectPtr conn,
                                   virStoragePoolObjPtr pool) {
    const char *mntargv[3];
    int ret;

    if (pool->def->type == VIR_STORAGE_POOL_NETFS) {
        if (pool->def->source.host.name == NULL) {
            virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
J
Jim Meyering 已提交
449
                                  "%s", _("missing source host"));
450 451 452 453
            return -1;
        }
        if (pool->def->source.dir == NULL) {
            virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
J
Jim Meyering 已提交
454
                                  "%s", _("missing source dir"));
455 456 457 458 459
            return -1;
        }
    } else {
        if (pool->def->source.ndevice != 1) {
            virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
J
Jim Meyering 已提交
460
                                  "%s", _("missing source device"));
461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476
            return -1;
        }
    }

    /* Short-circuit if already unmounted */
    if ((ret = virStorageBackendFileSystemIsMounted(conn, pool)) != 1) {
        if (ret < 0)
            return -1;
        else
            return 0;
    }

    mntargv[0] = UMOUNT;
    mntargv[1] = pool->def->target.path;
    mntargv[2] = NULL;

477
    if (virRun(conn, mntargv, NULL) < 0) {
478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523
        return -1;
    }
    return 0;
}
#endif /* WITH_STORAGE_FS */


/**
 * @conn connection to report errors against
 * @pool storage pool to start
 *
 * Starts a directory or FS based storage pool.
 *
 *  - If it is a FS based pool, mounts the unlying source device on the pool
 *
 * Returns 0 on success, -1 on error
 */
#if WITH_STORAGE_FS
static int
virStorageBackendFileSystemStart(virConnectPtr conn,
                                 virStoragePoolObjPtr pool)
{
    if (pool->def->type != VIR_STORAGE_POOL_DIR &&
        virStorageBackendFileSystemMount(conn, pool) < 0)
        return -1;

    return 0;
}
#endif /* WITH_STORAGE_FS */


/**
 * @conn connection to report errors against
 * @pool storage pool to build
 *
 * Build a directory or FS based storage pool.
 *
 *  - If it is a FS based pool, mounts the unlying source device on the pool
 *
 * Returns 0 on success, -1 on error
 */
static int
virStorageBackendFileSystemBuild(virConnectPtr conn,
                                 virStoragePoolObjPtr pool,
                                 unsigned int flags ATTRIBUTE_UNUSED)
{
524 525 526 527 528
    int err;
    if ((err = virFileMakePath(pool->def->target.path)) < 0) {
        virReportSystemError(conn, err,
                             _("cannot create path '%s'"),
                             pool->def->target.path);
529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546
        return -1;
    }

    return 0;
}


/**
 * Iterate over the pool's directory and enumerate all disk images
 * within it. This is non-recursive.
 */
static int
virStorageBackendFileSystemRefresh(virConnectPtr conn,
                                   virStoragePoolObjPtr pool)
{
    DIR *dir;
    struct dirent *ent;
    struct statvfs sb;
547
    virStorageVolDefPtr vol = NULL;
548 549

    if (!(dir = opendir(pool->def->target.path))) {
550 551 552
        virReportSystemError(conn, errno,
                             _("cannot open path '%s'"),
                             pool->def->target.path);
553 554 555 556 557
        goto cleanup;
    }

    while ((ent = readdir(dir)) != NULL) {
        int ret;
558
        char *backingStore;
559

560 561
        if (VIR_ALLOC(vol) < 0)
            goto no_memory;
562

563 564
        if ((vol->name = strdup(ent->d_name)) == NULL)
            goto no_memory;
565

566
        vol->type = VIR_STORAGE_VOL_FILE;
567
        vol->target.format = VIR_STORAGE_FILE_RAW; /* Real value is filled in during probe */
568 569 570
        if (virAsprintf(&vol->target.path, "%s/%s",
                        pool->def->target.path,
                        vol->name) == -1)
571 572 573 574
            goto no_memory;

        if ((vol->key = strdup(vol->target.path)) == NULL)
            goto no_memory;
575

576 577 578 579
        if ((ret = virStorageBackendProbeTarget(conn,
                                                &vol->target,
                                                &backingStore,
                                                &vol->allocation,
580 581
                                                &vol->capacity,
                                                &vol->target.encryption) < 0)) {
582
            if (ret == -1)
583
                goto cleanup;
584
            else {
585 586
                /* Silently ignore non-regular files,
                 * eg '.' '..', 'lost+found' */
587 588
                virStorageVolDefFree(vol);
                vol = NULL;
589
                continue;
590
            }
591 592
        }

593
        if (backingStore != NULL) {
594
            if (vol->target.format == VIR_STORAGE_FILE_QCOW2 &&
595 596 597 598 599 600 601 602
                STRPREFIX("fmt:", backingStore)) {
                char *fmtstr = backingStore + 4;
                char *path = strchr(fmtstr, ':');
                if (!path) {
                    VIR_FREE(backingStore);
                } else {
                    *path = '\0';
                    if ((vol->backingStore.format =
603
                         virStorageFileFormatTypeFromString(fmtstr)) < 0) {
604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620
                        VIR_FREE(backingStore);
                    } else {
                        memmove(backingStore, path, strlen(path) + 1);
                        vol->backingStore.path = backingStore;

                        if (virStorageBackendUpdateVolTargetInfo(conn,
                                                                 &vol->backingStore,
                                                                 NULL,
                                                                 NULL) < 0)
                            VIR_FREE(vol->backingStore);
                    }
                }
            } else {
                vol->backingStore.path = backingStore;

                if ((ret = virStorageBackendProbeTarget(conn,
                                                        &vol->backingStore,
621 622
                                                        NULL, NULL, NULL,
                                                        NULL)) < 0) {
623
                    if (ret == -1)
624
                        goto cleanup;
625 626 627 628 629 630 631 632 633 634 635
                    else {
                        /* Silently ignore non-regular files,
                         * eg '.' '..', 'lost+found' */
                        VIR_FREE(vol->backingStore);
                    }
                }
            }
        }



636 637 638 639 640
        if (VIR_REALLOC_N(pool->volumes.objs,
                          pool->volumes.count+1) < 0)
            goto no_memory;
        pool->volumes.objs[pool->volumes.count++] = vol;
        vol = NULL;
641 642 643 644 645
    }
    closedir(dir);


    if (statvfs(pool->def->target.path, &sb) < 0) {
646 647 648
        virReportSystemError(conn, errno,
                             _("cannot statvfs path '%s'"),
                             pool->def->target.path);
649 650 651 652 653 654 655 656 657 658
        return -1;
    }
    pool->def->capacity = ((unsigned long long)sb.f_frsize *
                           (unsigned long long)sb.f_blocks);
    pool->def->available = ((unsigned long long)sb.f_bfree *
                            (unsigned long long)sb.f_bsize);
    pool->def->allocation = pool->def->capacity - pool->def->available;

    return 0;

659
no_memory:
660
    virReportOOMError(conn);
661 662
    /* fallthrough */

663
 cleanup:
664 665
    if (dir)
        closedir(dir);
666
    virStorageVolDefFree(vol);
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 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712
    virStoragePoolObjClearVols(pool);
    return -1;
}


/**
 * @conn connection to report errors against
 * @pool storage pool to start
 *
 * Stops a directory or FS based storage pool.
 *
 *  - If it is a FS based pool, unmounts the unlying source device on the pool
 *  - Releases all cached data about volumes
 */
#if WITH_STORAGE_FS
static int
virStorageBackendFileSystemStop(virConnectPtr conn,
                                virStoragePoolObjPtr pool)
{
    if (pool->def->type != VIR_STORAGE_POOL_DIR &&
        virStorageBackendFileSystemUnmount(conn, pool) < 0)
        return -1;

    return 0;
}
#endif /* WITH_STORAGE_FS */


/**
 * @conn connection to report errors against
 * @pool storage pool to build
 *
 * Build a directory or FS based storage pool.
 *
 *  - If it is a FS based pool, mounts the unlying source device on the pool
 *
 * Returns 0 on success, -1 on error
 */
static int
virStorageBackendFileSystemDelete(virConnectPtr conn,
                                  virStoragePoolObjPtr pool,
                                  unsigned int flags ATTRIBUTE_UNUSED)
{
    /* XXX delete all vols first ? */

    if (unlink(pool->def->target.path) < 0) {
713 714 715
        virReportSystemError(conn, errno,
                             _("cannot unlink path '%s'"),
                             pool->def->target.path);
716 717 718 719 720 721 722 723
        return -1;
    }

    return 0;
}


/**
724 725 726 727
 * Set up a volume definition to be added to a pool's volume list, but
 * don't do any file creation or allocation. By separating the two processes,
 * we allow allocation progress reporting (by polling the volume's 'info'
 * function), and can drop the parent pool lock during the (slow) allocation.
728 729 730 731 732 733 734
 */
static int
virStorageBackendFileSystemVolCreate(virConnectPtr conn,
                                     virStoragePoolObjPtr pool,
                                     virStorageVolDefPtr vol)
{

735 736
    vol->type = VIR_STORAGE_VOL_FILE;

R
Ryota Ozaki 已提交
737
    VIR_FREE(vol->target.path);
738 739 740
    if (virAsprintf(&vol->target.path, "%s/%s",
                    pool->def->target.path,
                    vol->name) == -1) {
741
        virReportOOMError(conn);
742 743
        return -1;
    }
744

R
Ryota Ozaki 已提交
745
    VIR_FREE(vol->key);
746 747
    vol->key = strdup(vol->target.path);
    if (vol->key == NULL) {
748
        virReportOOMError(conn);
749 750 751
        return -1;
    }

752 753 754
    return 0;
}

755
static int createFileDir(virConnectPtr conn,
756
                         virStorageVolDefPtr vol,
757 758
                         virStorageVolDefPtr inputvol,
                         unsigned int flags ATTRIBUTE_UNUSED) {
759 760 761 762 763 764 765
    if (inputvol) {
        virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
                              "%s",
                              _("cannot copy from volume to a directory volume"));
        return -1;
    }

766 767 768 769 770 771
    if (mkdir(vol->target.path, vol->target.perms.mode) < 0) {
        virReportSystemError(conn, errno,
                             _("cannot create path '%s'"),
                             vol->target.path);
        return -1;
    }
772

773 774
    return 0;
}
775

776
static int
777 778 779
_virStorageBackendFileSystemVolBuild(virConnectPtr conn,
                                     virStorageVolDefPtr vol,
                                     virStorageVolDefPtr inputvol)
780 781
{
    int fd;
782
    virStorageBackendBuildVolFrom create_func;
783
    int tool_type;
784

785
    if (inputvol) {
786 787 788 789 790 791 792
        if (vol->target.encryption != NULL) {
            virStorageReportError(conn, VIR_ERR_NO_SUPPORT,
                                  "%s", _("storage pool does not support "
                                          "building encrypted volumes from "
                                          "other volumes"));
            return -1;
        }
793 794
        create_func = virStorageBackendGetBuildVolFromFunction(conn, vol,
                                                               inputvol);
795 796
        if (!create_func)
            return -1;
797
    } else if (vol->target.format == VIR_STORAGE_FILE_RAW) {
798
        create_func = virStorageBackendCreateRaw;
799
    } else if (vol->target.format == VIR_STORAGE_FILE_DIR) {
800
        create_func = createFileDir;
801 802 803 804
    } else if ((tool_type = virStorageBackendFindFSImageTool(NULL)) != -1) {
        create_func = virStorageBackendFSImageToolTypeToFunc(conn, tool_type);

        if (!create_func)
805
            return -1;
806
    } else {
807
        virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
J
Jim Meyering 已提交
808 809
                              "%s", _("creation of non-raw images "
                                      "is not supported without qemu-img"));
810 811 812
        return -1;
    }

813
    if (create_func(conn, vol, inputvol, 0) < 0)
814 815 816 817 818 819 820 821 822
        return -1;

    if ((fd = open(vol->target.path, O_RDONLY)) < 0) {
        virReportSystemError(conn, errno,
                             _("cannot read path '%s'"),
                             vol->target.path);
        return -1;
    }

823 824 825
    /* We can only chown/grp if root */
    if (getuid() == 0) {
        if (fchown(fd, vol->target.perms.uid, vol->target.perms.gid) < 0) {
826 827 828
            virReportSystemError(conn, errno,
                                 _("cannot set file owner '%s'"),
                                 vol->target.path);
829 830 831 832 833
            close(fd);
            return -1;
        }
    }
    if (fchmod(fd, vol->target.perms.mode) < 0) {
834 835 836
        virReportSystemError(conn, errno,
                             _("cannot set file mode '%s'"),
                             vol->target.path);
837 838 839 840 841
        close(fd);
        return -1;
    }

    /* Refresh allocation / permissions info, but not capacity */
842 843 844
    if (virStorageBackendUpdateVolTargetInfoFD(conn, &vol->target, fd,
                                               &vol->allocation,
                                               NULL) < 0) {
845 846 847 848 849
        close(fd);
        return -1;
    }

    if (close(fd) < 0) {
850 851 852
        virReportSystemError(conn, errno,
                             _("cannot close file '%s'"),
                             vol->target.path);
853 854 855 856 857 858
        return -1;
    }

    return 0;
}

859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879
/**
 * Allocate a new file as a volume. This is either done directly
 * for raw/sparse files, or by calling qemu-img/qcow-create for
 * special kinds of files
 */
static int
virStorageBackendFileSystemVolBuild(virConnectPtr conn,
                                    virStorageVolDefPtr vol) {
    return _virStorageBackendFileSystemVolBuild(conn, vol, NULL);
}

/*
 * Create a storage vol using 'inputvol' as input
 */
static int
virStorageBackendFileSystemVolBuildFrom(virConnectPtr conn,
                                        virStorageVolDefPtr vol,
                                        virStorageVolDefPtr inputvol,
                                        unsigned int flags ATTRIBUTE_UNUSED) {
    return _virStorageBackendFileSystemVolBuild(conn, vol, inputvol);
}
880 881 882 883 884 885 886 887 888 889 890 891 892

/**
 * Remove a volume - just unlinks for now
 */
static int
virStorageBackendFileSystemVolDelete(virConnectPtr conn,
                                     virStoragePoolObjPtr pool ATTRIBUTE_UNUSED,
                                     virStorageVolDefPtr vol,
                                     unsigned int flags ATTRIBUTE_UNUSED)
{
    if (unlink(vol->target.path) < 0) {
        /* Silently ignore failures where the vol has already gone away */
        if (errno != ENOENT) {
893 894 895
            virReportSystemError(conn, errno,
                                 _("cannot unlink file '%s'"),
                                 vol->target.path);
896 897 898 899 900 901 902 903 904 905 906 907 908 909 910
            return -1;
        }
    }
    return 0;
}


/**
 * Update info about a volume's capacity/allocation
 */
static int
virStorageBackendFileSystemVolRefresh(virConnectPtr conn,
                                      virStoragePoolObjPtr pool ATTRIBUTE_UNUSED,
                                      virStorageVolDefPtr vol)
{
911 912
    int ret;

913
    /* Refresh allocation / permissions info in case its changed */
914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946
    ret = virStorageBackendUpdateVolInfo(conn, vol, 0);
    if (ret < 0)
        return ret;

    /* Load any secrets if posible */
    if (vol->target.encryption &&
        vol->target.encryption->format == VIR_STORAGE_ENCRYPTION_FORMAT_QCOW &&
        vol->target.encryption->nsecrets == 0) {
        virSecretPtr sec;
        virStorageEncryptionSecretPtr encsec = NULL;

        sec = virSecretLookupByUsage(conn,
                                     VIR_SECRET_USAGE_TYPE_VOLUME,
                                     vol->target.path);
        if (sec) {
            if (VIR_ALLOC_N(vol->target.encryption->secrets, 1) < 0 ||
                VIR_ALLOC(encsec) < 0) {
                VIR_FREE(vol->target.encryption->secrets);
                virReportOOMError(conn);
                virSecretFree(sec);
                return -1;
            }

            vol->target.encryption->nsecrets = 1;
            vol->target.encryption->secrets[0] = encsec;

            encsec->type = VIR_STORAGE_ENCRYPTION_SECRET_TYPE_PASSPHRASE;
            virSecretGetUUID(sec, encsec->uuid);
            virSecretFree(sec);
        }
    }

    return 0;
947 948 949 950 951 952 953 954
}

virStorageBackend virStorageBackendDirectory = {
    .type = VIR_STORAGE_POOL_DIR,

    .buildPool = virStorageBackendFileSystemBuild,
    .refreshPool = virStorageBackendFileSystemRefresh,
    .deletePool = virStorageBackendFileSystemDelete,
955
    .buildVol = virStorageBackendFileSystemVolBuild,
956
    .buildVolFrom = virStorageBackendFileSystemVolBuildFrom,
957 958 959 960 961 962 963 964 965 966 967 968 969 970
    .createVol = virStorageBackendFileSystemVolCreate,
    .refreshVol = virStorageBackendFileSystemVolRefresh,
    .deleteVol = virStorageBackendFileSystemVolDelete,
};

#if WITH_STORAGE_FS
virStorageBackend virStorageBackendFileSystem = {
    .type = VIR_STORAGE_POOL_FS,

    .buildPool = virStorageBackendFileSystemBuild,
    .startPool = virStorageBackendFileSystemStart,
    .refreshPool = virStorageBackendFileSystemRefresh,
    .stopPool = virStorageBackendFileSystemStop,
    .deletePool = virStorageBackendFileSystemDelete,
971
    .buildVol = virStorageBackendFileSystemVolBuild,
972
    .buildVolFrom = virStorageBackendFileSystemVolBuildFrom,
973 974 975 976 977 978 979 980 981
    .createVol = virStorageBackendFileSystemVolCreate,
    .refreshVol = virStorageBackendFileSystemVolRefresh,
    .deleteVol = virStorageBackendFileSystemVolDelete,
};
virStorageBackend virStorageBackendNetFileSystem = {
    .type = VIR_STORAGE_POOL_NETFS,

    .buildPool = virStorageBackendFileSystemBuild,
    .startPool = virStorageBackendFileSystemStart,
982
    .findPoolSources = virStorageBackendFileSystemNetFindPoolSources,
983 984 985
    .refreshPool = virStorageBackendFileSystemRefresh,
    .stopPool = virStorageBackendFileSystemStop,
    .deletePool = virStorageBackendFileSystemDelete,
986
    .buildVol = virStorageBackendFileSystemVolBuild,
987
    .buildVolFrom = virStorageBackendFileSystemVolBuildFrom,
988 989 990 991 992
    .createVol = virStorageBackendFileSystemVolCreate,
    .refreshVol = virStorageBackendFileSystemVolRefresh,
    .deleteVol = virStorageBackendFileSystemVolDelete,
};
#endif /* WITH_STORAGE_FS */