storage_backend_fs.c 29.2 KB
Newer Older
1 2 3
/*
 * storage_backend_fs.c: storage backend for FS and directory handling
 *
4
 * Copyright (C) 2007-2010 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
static int
52
virStorageBackendProbeTarget(virStorageVolTargetPtr target,
53 54 55 56 57 58
                             char **backingStore,
                             unsigned long long *allocation,
                             unsigned long long *capacity,
                             virStorageEncryptionPtr *encryption)
{
    int fd, ret;
59
    virStorageFileMetadata meta;
60 61 62 63 64

    if (encryption)
        *encryption = NULL;

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

71
    if ((ret = virStorageBackendUpdateVolTargetInfoFD(target, fd,
72 73 74 75 76 77
                                                      allocation,
                                                      capacity)) < 0) {
        close(fd);
        return ret; /* Take care to propagate ret, it is not always -1 */
    }

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

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

    close(fd);

87 88 89 90 91 92 93 94 95 96 97 98 99
    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) {
100
        if (VIR_ALLOC(*encryption) < 0) {
101
            virReportOOMError();
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
            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
         */
    }

123 124 125 126
    return 0;
}

#if WITH_STORAGE_FS
127 128 129

#include <mntent.h>

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

typedef struct _virNetfsDiscoverState virNetfsDiscoverState;

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

    path = groups[0];

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

162
    if (!(src = virStoragePoolSourceListNewSource(&state->list)))
163
        goto cleanup;
164

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

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

180

181
static char *
182
virStorageBackendFileSystemNetFindPoolSources(virConnectPtr conn ATTRIBUTE_UNUSED,
183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
                                              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
    };
200 201 202 203 204 205 206 207
    virNetfsDiscoverState state = {
        .host = NULL,
        .list = {
            .type = VIR_STORAGE_POOL_NETFS,
            .nsources = 0,
            .sources = NULL
        }
    };
208
    const char *prog[] = { SHOWMOUNT, "--no-headers", "--exports", NULL, NULL };
209
    virStoragePoolSourcePtr source = NULL;
210 211
    int exitstatus;
    char *retval = NULL;
212
    unsigned int i;
213

214
    source = virStoragePoolDefParseSourceString(srcSpec,
215 216
                                                VIR_STORAGE_POOL_NETFS);
    if (!source)
217 218
        goto cleanup;

219 220
    state.host = source->host.name;
    prog[3] = source->host.name;
221

222
    if (virStorageBackendRunProgRegex(NULL, prog, 1, regexes, vars,
223 224 225 226
                                      virStorageBackendFileSystemNetFindPoolSourcesFunc,
                                      &state, &exitstatus) < 0)
        goto cleanup;

227
    retval = virStoragePoolSourceListFormat(&state.list);
228
    if (retval == NULL) {
229
        virReportOOMError();
230 231 232 233
        goto cleanup;
    }

 cleanup:
234 235 236
    for (i = 0; i < state.list.nsources; i++)
        virStoragePoolSourceFree(&state.list.sources[i]);

237 238
    if (source)
        virStoragePoolSourceFree(source);
239

240
    VIR_FREE(state.list.sources);
241 242 243 244 245

    return retval;
}


246 247 248 249 250 251 252 253 254
/**
 * @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
255
virStorageBackendFileSystemIsMounted(virStoragePoolObjPtr pool) {
256
    FILE *mtab;
257 258
    struct mntent ent;
    char buf[1024];
259 260

    if ((mtab = fopen(_PATH_MOUNTED, "r")) == NULL) {
261
        virReportSystemError(errno,
262 263
                             _("cannot read mount list '%s'"),
                             _PATH_MOUNTED);
264 265 266
        return -1;
    }

267 268
    while ((getmntent_r(mtab, &ent, buf, sizeof(buf))) != NULL) {
        if (STREQ(ent.mnt_dir, pool->def->target.path)) {
269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287
            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
288
virStorageBackendFileSystemMount(virStoragePoolObjPtr pool) {
289
    char *src;
290
    char *options;
291 292 293 294 295 296 297
    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);
298 299 300 301
    int glusterfs = (pool->def->type == VIR_STORAGE_POOL_NETFS &&
                 pool->def->source.format == VIR_STORAGE_POOL_NETFS_GLUSTERFS);

    int option_index;
302 303 304 305 306 307 308 309 310 311
    int source_index;

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

    const char *fs_argv[] =  {
312 313 314
        MOUNT,
        "-t",
        pool->def->type == VIR_STORAGE_POOL_FS ?
315 316
        virStoragePoolFormatFileSystemTypeToString(pool->def->source.format) :
        virStoragePoolFormatFileSystemNetTypeToString(pool->def->source.format),
317 318
        NULL, /* Fill in shortly - careful not to add extra fields
                 before this */
319 320 321
        pool->def->target.path,
        NULL,
    };
322

323 324 325 326 327 328 329 330 331 332 333 334 335
    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,
    };

336 337 338
    if (netauto) {
        mntargv = netfs_auto_argv;
        source_index = 1;
339 340 341 342
    } else if (glusterfs) {
        mntargv = glusterfs_argv;
        source_index = 3;
        option_index = 5;
343 344 345 346 347
    } else {
        mntargv = fs_argv;
        source_index = 3;
    }

348 349 350 351
    int ret;

    if (pool->def->type == VIR_STORAGE_POOL_NETFS) {
        if (pool->def->source.host.name == NULL) {
352
            virStorageReportError(VIR_ERR_INTERNAL_ERROR,
J
Jim Meyering 已提交
353
                                  "%s", _("missing source host"));
354 355 356
            return -1;
        }
        if (pool->def->source.dir == NULL) {
357
            virStorageReportError(VIR_ERR_INTERNAL_ERROR,
J
Jim Meyering 已提交
358
                                  "%s", _("missing source path"));
359 360 361 362
            return -1;
        }
    } else {
        if (pool->def->source.ndevice != 1) {
363
            virStorageReportError(VIR_ERR_INTERNAL_ERROR,
J
Jim Meyering 已提交
364
                                  "%s", _("missing source device"));
365 366 367 368
            return -1;
        }
    }

J
Jim Meyering 已提交
369
    /* Short-circuit if already mounted */
370
    if ((ret = virStorageBackendFileSystemIsMounted(pool)) != 0) {
371 372 373 374 375 376 377
        if (ret < 0)
            return -1;
        else
            return 0;
    }

    if (pool->def->type == VIR_STORAGE_POOL_NETFS) {
378
        if (pool->def->source.format == VIR_STORAGE_POOL_NETFS_GLUSTERFS) {
379
            if (virAsprintf(&options, "direct-io-mode=1") == -1) {
380
                virReportOOMError();
381 382 383
                return -1;
            }
        }
384 385 386
        if (virAsprintf(&src, "%s:%s",
                        pool->def->source.host.name,
                        pool->def->source.dir) == -1) {
387
            virReportOOMError();
388 389
            return -1;
        }
390

391
    } else {
392
        if ((src = strdup(pool->def->source.devices[0].path)) == NULL) {
393
            virReportOOMError();
394 395
            return -1;
        }
396
    }
397
    mntargv[source_index] = src;
398

399 400 401 402
    if (glusterfs) {
        mntargv[option_index] = options;
    }

403
    if (virRun(mntargv, NULL) < 0) {
404
        VIR_FREE(src);
405 406
        return -1;
    }
407
    VIR_FREE(src);
408 409 410 411 412 413 414 415 416 417 418 419 420
    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
421
virStorageBackendFileSystemUnmount(virStoragePoolObjPtr pool) {
422 423 424 425 426
    const char *mntargv[3];
    int ret;

    if (pool->def->type == VIR_STORAGE_POOL_NETFS) {
        if (pool->def->source.host.name == NULL) {
427
            virStorageReportError(VIR_ERR_INTERNAL_ERROR,
J
Jim Meyering 已提交
428
                                  "%s", _("missing source host"));
429 430 431
            return -1;
        }
        if (pool->def->source.dir == NULL) {
432
            virStorageReportError(VIR_ERR_INTERNAL_ERROR,
J
Jim Meyering 已提交
433
                                  "%s", _("missing source dir"));
434 435 436 437
            return -1;
        }
    } else {
        if (pool->def->source.ndevice != 1) {
438
            virStorageReportError(VIR_ERR_INTERNAL_ERROR,
J
Jim Meyering 已提交
439
                                  "%s", _("missing source device"));
440 441 442 443 444
            return -1;
        }
    }

    /* Short-circuit if already unmounted */
445
    if ((ret = virStorageBackendFileSystemIsMounted(pool)) != 1) {
446 447 448 449 450 451 452 453 454 455
        if (ret < 0)
            return -1;
        else
            return 0;
    }

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

456
    if (virRun(mntargv, NULL) < 0) {
457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475
        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
476
virStorageBackendFileSystemStart(virConnectPtr conn ATTRIBUTE_UNUSED,
477 478 479
                                 virStoragePoolObjPtr pool)
{
    if (pool->def->type != VIR_STORAGE_POOL_DIR &&
480
        virStorageBackendFileSystemMount(pool) < 0)
481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498
        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
499
virStorageBackendFileSystemBuild(virConnectPtr conn ATTRIBUTE_UNUSED,
500 501 502
                                 virStoragePoolObjPtr pool,
                                 unsigned int flags ATTRIBUTE_UNUSED)
{
503 504 505 506 507
    int err, ret = -1;
    char *parent;
    char *p;

    if ((parent = strdup(pool->def->target.path)) == NULL) {
508
        virReportOOMError();
509 510 511
        goto error;
    }
    if (!(p = strrchr(parent, '/'))) {
512
        virStorageReportError(VIR_ERR_INVALID_ARG,
513 514 515
                              _("path '%s' is not absolute"),
                              pool->def->target.path);
        goto error;
516 517
    }

518 519 520 521 522
    if (p != parent) {
        /* assure all directories in the path prior to the final dir
         * exist, with default uid/gid/mode. */
        *p = '\0';
        if ((err = virFileMakePath(parent)) != 0) {
523
            virReportSystemError(err, _("cannot create path '%s'"),
524 525 526 527 528 529 530 531 532 533 534 535 536 537 538
                                 parent);
            goto error;
        }
    }

    /* Now create the final dir in the path with the uid/gid/mode
     * requested in the config. If the dir already exists, just set
     * the perms. */
    if ((err = virDirCreate(pool->def->target.path,
                            pool->def->target.perms.mode,
                            pool->def->target.perms.uid,
                            pool->def->target.perms.gid,
                            VIR_FILE_CREATE_ALLOW_EXIST |
                            (pool->def->type == VIR_STORAGE_POOL_NETFS
                             ? VIR_FILE_CREATE_AS_UID : 0)) != 0)) {
539
        virReportSystemError(err, _("cannot create path '%s'"),
540 541 542 543 544 545 546
                             pool->def->target.path);
        goto error;
    }
    ret = 0;
error:
    VIR_FREE(parent);
    return ret;
547 548 549 550 551 552 553 554
}


/**
 * Iterate over the pool's directory and enumerate all disk images
 * within it. This is non-recursive.
 */
static int
555
virStorageBackendFileSystemRefresh(virConnectPtr conn ATTRIBUTE_UNUSED,
556 557 558 559 560
                                   virStoragePoolObjPtr pool)
{
    DIR *dir;
    struct dirent *ent;
    struct statvfs sb;
561
    virStorageVolDefPtr vol = NULL;
562 563

    if (!(dir = opendir(pool->def->target.path))) {
564
        virReportSystemError(errno,
565 566
                             _("cannot open path '%s'"),
                             pool->def->target.path);
567 568 569 570 571
        goto cleanup;
    }

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

574 575
        if (VIR_ALLOC(vol) < 0)
            goto no_memory;
576

577 578
        if ((vol->name = strdup(ent->d_name)) == NULL)
            goto no_memory;
579

580
        vol->type = VIR_STORAGE_VOL_FILE;
581
        vol->target.format = VIR_STORAGE_FILE_RAW; /* Real value is filled in during probe */
582 583 584
        if (virAsprintf(&vol->target.path, "%s/%s",
                        pool->def->target.path,
                        vol->name) == -1)
585 586 587 588
            goto no_memory;

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

590
        if ((ret = virStorageBackendProbeTarget(&vol->target,
591 592
                                                &backingStore,
                                                &vol->allocation,
593
                                                &vol->capacity,
594
                                                &vol->target.encryption)) < 0) {
595
            if (ret == -1)
596
                goto cleanup;
597
            else {
598 599
                /* Silently ignore non-regular files,
                 * eg '.' '..', 'lost+found' */
600 601
                virStorageVolDefFree(vol);
                vol = NULL;
602
                continue;
603
            }
604 605
        }

606
        if (backingStore != NULL) {
607
            if (vol->target.format == VIR_STORAGE_FILE_QCOW2 &&
608 609 610 611 612 613 614 615
                STRPREFIX("fmt:", backingStore)) {
                char *fmtstr = backingStore + 4;
                char *path = strchr(fmtstr, ':');
                if (!path) {
                    VIR_FREE(backingStore);
                } else {
                    *path = '\0';
                    if ((vol->backingStore.format =
616
                         virStorageFileFormatTypeFromString(fmtstr)) < 0) {
617 618 619 620 621
                        VIR_FREE(backingStore);
                    } else {
                        memmove(backingStore, path, strlen(path) + 1);
                        vol->backingStore.path = backingStore;

622
                        if (virStorageBackendUpdateVolTargetInfo(&vol->backingStore,
623 624 625 626 627 628 629 630
                                                                 NULL,
                                                                 NULL) < 0)
                            VIR_FREE(vol->backingStore);
                    }
                }
            } else {
                vol->backingStore.path = backingStore;

631
                if ((ret = virStorageBackendProbeTarget(&vol->backingStore,
632 633
                                                        NULL, NULL, NULL,
                                                        NULL)) < 0) {
634
                    if (ret == -1)
635
                        goto cleanup;
636 637 638 639 640 641 642 643 644 645 646
                    else {
                        /* Silently ignore non-regular files,
                         * eg '.' '..', 'lost+found' */
                        VIR_FREE(vol->backingStore);
                    }
                }
            }
        }



647 648 649 650 651
        if (VIR_REALLOC_N(pool->volumes.objs,
                          pool->volumes.count+1) < 0)
            goto no_memory;
        pool->volumes.objs[pool->volumes.count++] = vol;
        vol = NULL;
652 653 654 655 656
    }
    closedir(dir);


    if (statvfs(pool->def->target.path, &sb) < 0) {
657
        virReportSystemError(errno,
658 659
                             _("cannot statvfs path '%s'"),
                             pool->def->target.path);
660 661 662 663 664 665 666 667 668 669
        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;

670
no_memory:
671
    virReportOOMError();
672 673
    /* fallthrough */

674
 cleanup:
675 676
    if (dir)
        closedir(dir);
677
    virStorageVolDefFree(vol);
678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693
    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
694
virStorageBackendFileSystemStop(virConnectPtr conn ATTRIBUTE_UNUSED,
695 696 697
                                virStoragePoolObjPtr pool)
{
    if (pool->def->type != VIR_STORAGE_POOL_DIR &&
698
        virStorageBackendFileSystemUnmount(pool) < 0)
699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716
        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
717
virStorageBackendFileSystemDelete(virConnectPtr conn ATTRIBUTE_UNUSED,
718 719 720 721 722
                                  virStoragePoolObjPtr pool,
                                  unsigned int flags ATTRIBUTE_UNUSED)
{
    /* XXX delete all vols first ? */

723
    if (rmdir(pool->def->target.path) < 0) {
724
        virReportSystemError(errno,
725
                             _("failed to remove pool '%s'"),
726
                             pool->def->target.path);
727 728 729 730 731 732 733 734
        return -1;
    }

    return 0;
}


/**
735 736 737 738
 * 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.
739 740
 */
static int
741
virStorageBackendFileSystemVolCreate(virConnectPtr conn ATTRIBUTE_UNUSED,
742 743 744 745
                                     virStoragePoolObjPtr pool,
                                     virStorageVolDefPtr vol)
{

746 747
    vol->type = VIR_STORAGE_VOL_FILE;

R
Ryota Ozaki 已提交
748
    VIR_FREE(vol->target.path);
749 750 751
    if (virAsprintf(&vol->target.path, "%s/%s",
                    pool->def->target.path,
                    vol->name) == -1) {
752
        virReportOOMError();
753 754
        return -1;
    }
755

R
Ryota Ozaki 已提交
756
    VIR_FREE(vol->key);
757 758
    vol->key = strdup(vol->target.path);
    if (vol->key == NULL) {
759
        virReportOOMError();
760 761 762
        return -1;
    }

763 764 765
    return 0;
}

766
static int createFileDir(virConnectPtr conn ATTRIBUTE_UNUSED,
767
                         virStoragePoolObjPtr pool,
768
                         virStorageVolDefPtr vol,
769 770
                         virStorageVolDefPtr inputvol,
                         unsigned int flags ATTRIBUTE_UNUSED) {
771 772
    int err;

773
    if (inputvol) {
774
        virStorageReportError(VIR_ERR_INTERNAL_ERROR,
775 776 777 778 779
                              "%s",
                              _("cannot copy from volume to a directory volume"));
        return -1;
    }

780 781 782 783
    if ((err = virDirCreate(vol->target.path, vol->target.perms.mode,
                            vol->target.perms.uid, vol->target.perms.gid,
                            (pool->def->type == VIR_STORAGE_POOL_NETFS
                             ? VIR_FILE_CREATE_AS_UID : 0))) != 0) {
784
        virReportSystemError(err, _("cannot create path '%s'"),
785 786 787
                             vol->target.path);
        return -1;
    }
788

789 790
    return 0;
}
791

792
static int
793
_virStorageBackendFileSystemVolBuild(virConnectPtr conn,
794
                                     virStoragePoolObjPtr pool,
795 796
                                     virStorageVolDefPtr vol,
                                     virStorageVolDefPtr inputvol)
797
{
798
    virStorageBackendBuildVolFrom create_func;
799
    int tool_type;
800

801
    if (inputvol) {
802
        if (vol->target.encryption != NULL) {
803
            virStorageReportError(VIR_ERR_NO_SUPPORT,
804 805 806 807 808
                                  "%s", _("storage pool does not support "
                                          "building encrypted volumes from "
                                          "other volumes"));
            return -1;
        }
809
        create_func = virStorageBackendGetBuildVolFromFunction(vol,
810
                                                               inputvol);
811 812
        if (!create_func)
            return -1;
813
    } else if (vol->target.format == VIR_STORAGE_FILE_RAW) {
814
        create_func = virStorageBackendCreateRaw;
815
    } else if (vol->target.format == VIR_STORAGE_FILE_DIR) {
816
        create_func = createFileDir;
817
    } else if ((tool_type = virStorageBackendFindFSImageTool(NULL)) != -1) {
818
        create_func = virStorageBackendFSImageToolTypeToFunc(tool_type);
819 820

        if (!create_func)
821
            return -1;
822
    } else {
823
        virStorageReportError(VIR_ERR_INTERNAL_ERROR,
J
Jim Meyering 已提交
824 825
                              "%s", _("creation of non-raw images "
                                      "is not supported without qemu-img"));
826 827 828
        return -1;
    }

829
    if (create_func(conn, pool, vol, inputvol, 0) < 0)
830
        return -1;
831 832 833
    return 0;
}

834 835 836 837 838 839 840
/**
 * 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,
841
                                    virStoragePoolObjPtr pool,
842
                                    virStorageVolDefPtr vol) {
843
    return _virStorageBackendFileSystemVolBuild(conn, pool, vol, NULL);
844 845 846 847 848 849 850
}

/*
 * Create a storage vol using 'inputvol' as input
 */
static int
virStorageBackendFileSystemVolBuildFrom(virConnectPtr conn,
851
                                        virStoragePoolObjPtr pool,
852 853 854
                                        virStorageVolDefPtr vol,
                                        virStorageVolDefPtr inputvol,
                                        unsigned int flags ATTRIBUTE_UNUSED) {
855
    return _virStorageBackendFileSystemVolBuild(conn, pool, vol, inputvol);
856
}
857 858 859 860 861

/**
 * Remove a volume - just unlinks for now
 */
static int
862
virStorageBackendFileSystemVolDelete(virConnectPtr conn ATTRIBUTE_UNUSED,
863 864 865 866 867 868 869
                                     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) {
870
            virReportSystemError(errno,
871 872
                                 _("cannot unlink file '%s'"),
                                 vol->target.path);
873 874 875 876 877 878 879 880 881 882 883 884 885 886 887
            return -1;
        }
    }
    return 0;
}


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

890
    /* Refresh allocation / permissions info in case its changed */
891
    ret = virStorageBackendUpdateVolInfo(vol, 0);
892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908
    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);
909
                virReportOOMError();
910 911 912 913 914 915 916 917 918 919 920 921 922 923
                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;
924 925 926 927 928 929 930 931
}

virStorageBackend virStorageBackendDirectory = {
    .type = VIR_STORAGE_POOL_DIR,

    .buildPool = virStorageBackendFileSystemBuild,
    .refreshPool = virStorageBackendFileSystemRefresh,
    .deletePool = virStorageBackendFileSystemDelete,
932
    .buildVol = virStorageBackendFileSystemVolBuild,
933
    .buildVolFrom = virStorageBackendFileSystemVolBuildFrom,
934 935 936 937 938 939 940 941 942 943 944 945 946 947
    .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,
948
    .buildVol = virStorageBackendFileSystemVolBuild,
949
    .buildVolFrom = virStorageBackendFileSystemVolBuildFrom,
950 951 952 953 954 955 956 957 958
    .createVol = virStorageBackendFileSystemVolCreate,
    .refreshVol = virStorageBackendFileSystemVolRefresh,
    .deleteVol = virStorageBackendFileSystemVolDelete,
};
virStorageBackend virStorageBackendNetFileSystem = {
    .type = VIR_STORAGE_POOL_NETFS,

    .buildPool = virStorageBackendFileSystemBuild,
    .startPool = virStorageBackendFileSystemStart,
959
    .findPoolSources = virStorageBackendFileSystemNetFindPoolSources,
960 961 962
    .refreshPool = virStorageBackendFileSystemRefresh,
    .stopPool = virStorageBackendFileSystemStop,
    .deletePool = virStorageBackendFileSystemDelete,
963
    .buildVol = virStorageBackendFileSystemVolBuild,
964
    .buildVolFrom = virStorageBackendFileSystemVolBuildFrom,
965 966 967 968 969
    .createVol = virStorageBackendFileSystemVolCreate,
    .refreshVol = virStorageBackendFileSystemVolRefresh,
    .deleteVol = virStorageBackendFileSystemVolDelete,
};
#endif /* WITH_STORAGE_FS */