storage_backend_fs.c 30.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
#include "files.h"
49

50
#define VIR_FROM_THIS VIR_FROM_STORAGE
51

E
Eric Blake 已提交
52
static int ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3)
53
virStorageBackendProbeTarget(virStorageVolTargetPtr target,
54
                             char **backingStore,
55
                             int *backingStoreFormat,
56 57 58 59 60
                             unsigned long long *allocation,
                             unsigned long long *capacity,
                             virStorageEncryptionPtr *encryption)
{
    int fd, ret;
61
    virStorageFileMetadata meta;
62

E
Eric Blake 已提交
63 64
    *backingStore = NULL;
    *backingStoreFormat = VIR_STORAGE_FILE_AUTO;
65 66 67
    if (encryption)
        *encryption = NULL;

68 69 70 71
    if ((ret = virStorageBackendVolOpenCheckMode(target->path,
                                                 (VIR_STORAGE_VOL_OPEN_DEFAULT & ~VIR_STORAGE_VOL_OPEN_ERROR))) < 0)
        return ret; /* Take care to propagate ret, it is not always -1 */
    fd = ret;
72

73
    if ((ret = virStorageBackendUpdateVolTargetInfoFD(target, fd,
74 75
                                                      allocation,
                                                      capacity)) < 0) {
76
        VIR_FORCE_CLOSE(fd);
E
Eric Blake 已提交
77
        return ret;
78 79
    }

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

82
    if ((target->format = virStorageFileProbeFormatFromFD(target->path, fd)) < 0) {
83
        VIR_FORCE_CLOSE(fd);
84 85 86
        return -1;
    }

87 88 89
    if (virStorageFileGetMetadataFromFD(target->path, fd,
                                        target->format,
                                        &meta) < 0) {
90
        VIR_FORCE_CLOSE(fd);
91 92
        return -1;
    }
93

94
    VIR_FORCE_CLOSE(fd);
95

96
    if (meta.backingStore) {
E
Eric Blake 已提交
97 98 99 100 101
        *backingStore = meta.backingStore;
        meta.backingStore = NULL;
        if (meta.backingStoreFormat == VIR_STORAGE_FILE_AUTO) {
            if ((*backingStoreFormat
                 = virStorageFileProbeFormat(*backingStore)) < 0) {
102
                VIR_FORCE_CLOSE(fd);
E
Eric Blake 已提交
103
                goto cleanup;
104 105
            }
        } else {
E
Eric Blake 已提交
106
            *backingStoreFormat = meta.backingStoreFormat;
107
        }
E
Eric Blake 已提交
108 109
    } else {
        VIR_FREE(meta.backingStore);
110 111 112 113 114 115
    }

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

    if (encryption != NULL && meta.encrypted) {
116
        if (VIR_ALLOC(*encryption) < 0) {
117
            virReportOOMError();
118
            goto cleanup;
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
        }

        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
         */
    }

137
    return 0;
138 139

cleanup:
E
Eric Blake 已提交
140
    VIR_FREE(*backingStore);
141
    return -1;
142 143 144
}

#if WITH_STORAGE_FS
145

146
# include <mntent.h>
147

148 149
struct _virNetfsDiscoverState {
    const char *host;
150
    virStoragePoolSourceList list;
151 152 153 154 155
};

typedef struct _virNetfsDiscoverState virNetfsDiscoverState;

static int
156
virStorageBackendFileSystemNetFindPoolSourcesFunc(virStoragePoolObjPtr pool ATTRIBUTE_UNUSED,
157 158 159 160 161
                                                  char **const groups,
                                                  void *data)
{
    virNetfsDiscoverState *state = data;
    const char *name, *path;
162 163
    virStoragePoolSource *src = NULL;
    int ret = -1;
164 165 166 167 168

    path = groups[0];

    name = strrchr(path, '/');
    if (name == NULL) {
169
        virStorageReportError(VIR_ERR_INTERNAL_ERROR,
170
                              _("invalid netfs path (no /): %s"), path);
171
        goto cleanup;
172 173 174
    }
    name += 1;
    if (*name == '\0') {
175
        virStorageReportError(VIR_ERR_INTERNAL_ERROR,
176
                              _("invalid netfs path (ends in /): %s"), path);
177
        goto cleanup;
178 179
    }

180
    if (!(src = virStoragePoolSourceListNewSource(&state->list)))
181
        goto cleanup;
182

183
    if (!(src->host.name = strdup(state->host)) ||
184
        !(src->dir = strdup(path))) {
185
        virReportOOMError();
186 187
        goto cleanup;
    }
188
    src->format = VIR_STORAGE_POOL_NETFS_NFS;
189

190 191 192
    src = NULL;
    ret = 0;
cleanup:
193 194
    virStoragePoolSourceFree(src);
    VIR_FREE(src);
195
    return ret;
196 197
}

198

199
static char *
200
virStorageBackendFileSystemNetFindPoolSources(virConnectPtr conn ATTRIBUTE_UNUSED,
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217
                                              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
    };
218 219 220 221 222 223 224 225
    virNetfsDiscoverState state = {
        .host = NULL,
        .list = {
            .type = VIR_STORAGE_POOL_NETFS,
            .nsources = 0,
            .sources = NULL
        }
    };
226
    const char *prog[] = { SHOWMOUNT, "--no-headers", "--exports", NULL, NULL };
227
    virStoragePoolSourcePtr source = NULL;
228 229
    int exitstatus;
    char *retval = NULL;
230
    unsigned int i;
231

232
    source = virStoragePoolDefParseSourceString(srcSpec,
233 234
                                                VIR_STORAGE_POOL_NETFS);
    if (!source)
235 236
        goto cleanup;

237 238
    state.host = source->host.name;
    prog[3] = source->host.name;
239

240
    if (virStorageBackendRunProgRegex(NULL, prog, 1, regexes, vars,
241 242 243 244
                                      virStorageBackendFileSystemNetFindPoolSourcesFunc,
                                      &state, &exitstatus) < 0)
        goto cleanup;

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

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

255 256
    virStoragePoolSourceFree(source);
    VIR_FREE(source);
257

258
    VIR_FREE(state.list.sources);
259 260 261 262 263

    return retval;
}


264 265 266 267 268 269 270 271 272
/**
 * @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
273
virStorageBackendFileSystemIsMounted(virStoragePoolObjPtr pool) {
274
    FILE *mtab;
275 276
    struct mntent ent;
    char buf[1024];
277 278

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

285 286
    while ((getmntent_r(mtab, &ent, buf, sizeof(buf))) != NULL) {
        if (STREQ(ent.mnt_dir, pool->def->target.path)) {
287
            VIR_FORCE_FCLOSE(mtab);
288 289 290 291
            return 1;
        }
    }

292
    VIR_FORCE_FCLOSE(mtab);
293 294 295 296 297 298 299 300 301 302 303 304 305
    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
306
virStorageBackendFileSystemMount(virStoragePoolObjPtr pool) {
307
    char *src;
308
    char *options = NULL;
309 310 311 312 313 314 315
    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);
316 317 318 319
    int glusterfs = (pool->def->type == VIR_STORAGE_POOL_NETFS &&
                 pool->def->source.format == VIR_STORAGE_POOL_NETFS_GLUSTERFS);

    int option_index;
320 321 322 323 324 325 326 327 328 329
    int source_index;

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

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

341 342 343 344 345 346 347 348 349 350 351 352 353
    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,
    };

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

366 367 368 369
    int ret;

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

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

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

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

417 418 419 420
    if (glusterfs) {
        mntargv[option_index] = options;
    }

421
    if (virRun(mntargv, NULL) < 0) {
422
        VIR_FREE(src);
423 424
        return -1;
    }
425
    VIR_FREE(src);
426 427 428 429 430 431 432 433 434 435 436 437 438
    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
439
virStorageBackendFileSystemUnmount(virStoragePoolObjPtr pool) {
440 441 442 443 444
    const char *mntargv[3];
    int ret;

    if (pool->def->type == VIR_STORAGE_POOL_NETFS) {
        if (pool->def->source.host.name == NULL) {
445
            virStorageReportError(VIR_ERR_INTERNAL_ERROR,
J
Jim Meyering 已提交
446
                                  "%s", _("missing source host"));
447 448 449
            return -1;
        }
        if (pool->def->source.dir == NULL) {
450
            virStorageReportError(VIR_ERR_INTERNAL_ERROR,
J
Jim Meyering 已提交
451
                                  "%s", _("missing source dir"));
452 453 454 455
            return -1;
        }
    } else {
        if (pool->def->source.ndevice != 1) {
456
            virStorageReportError(VIR_ERR_INTERNAL_ERROR,
J
Jim Meyering 已提交
457
                                  "%s", _("missing source device"));
458 459 460 461 462
            return -1;
        }
    }

    /* Short-circuit if already unmounted */
463
    if ((ret = virStorageBackendFileSystemIsMounted(pool)) != 1) {
464 465 466 467 468 469 470 471 472 473
        if (ret < 0)
            return -1;
        else
            return 0;
    }

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

474
    if (virRun(mntargv, NULL) < 0) {
475 476 477 478 479 480 481
        return -1;
    }
    return 0;
}
#endif /* WITH_STORAGE_FS */


482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505
static int
virStorageBackendFileSystemCheck(virConnectPtr conn ATTRIBUTE_UNUSED,
                                 virStoragePoolObjPtr pool,
                                 bool *isActive)
{
    *isActive = false;
    if (pool->def->type == VIR_STORAGE_POOL_DIR) {
        if (access(pool->def->target.path, F_OK) == 0)
            *isActive = true;
#if WITH_STORAGE_FS
    } else {
        int ret;
        if ((ret = virStorageBackendFileSystemIsMounted(pool)) != 0) {
            if (ret < 0)
                return -1;
            *isActive = true;
        }
#endif /* WITH_STORAGE_FS */
    }

    return 0;
}

#if WITH_STORAGE_FS
506 507 508 509 510 511 512 513 514 515 516
/**
 * @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
 */
static int
517
virStorageBackendFileSystemStart(virConnectPtr conn ATTRIBUTE_UNUSED,
518 519 520
                                 virStoragePoolObjPtr pool)
{
    if (pool->def->type != VIR_STORAGE_POOL_DIR &&
521
        virStorageBackendFileSystemMount(pool) < 0)
522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539
        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
540
virStorageBackendFileSystemBuild(virConnectPtr conn ATTRIBUTE_UNUSED,
541 542 543
                                 virStoragePoolObjPtr pool,
                                 unsigned int flags ATTRIBUTE_UNUSED)
{
544 545 546 547 548
    int err, ret = -1;
    char *parent;
    char *p;

    if ((parent = strdup(pool->def->target.path)) == NULL) {
549
        virReportOOMError();
550 551 552
        goto error;
    }
    if (!(p = strrchr(parent, '/'))) {
553
        virStorageReportError(VIR_ERR_INVALID_ARG,
554 555 556
                              _("path '%s' is not absolute"),
                              pool->def->target.path);
        goto error;
557 558
    }

559 560 561 562 563
    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) {
564
            virReportSystemError(err, _("cannot create path '%s'"),
565 566 567 568 569 570 571 572
                                 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. */
573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589

    struct stat st;

    if ((stat(pool->def->target.path, &st) < 0)
        || (pool->def->target.perms.uid != -1)) {

        uid_t uid = (pool->def->target.perms.uid == -1)
            ? getuid() : pool->def->target.perms.uid;
        gid_t gid = (pool->def->target.perms.gid == -1)
            ? getgid() : pool->def->target.perms.gid;

        if ((err = virDirCreate(pool->def->target.path,
                                pool->def->target.perms.mode,
                                uid, gid,
                                VIR_DIR_CREATE_FORCE_PERMS |
                                VIR_DIR_CREATE_ALLOW_EXIST |
                                (pool->def->type == VIR_STORAGE_POOL_NETFS
590 591
                                 ? VIR_DIR_CREATE_AS_UID : 0)) < 0)) {
            virReportSystemError(-err, _("cannot create path '%s'"),
592 593 594
                                 pool->def->target.path);
            goto error;
        }
595 596 597 598 599
    }
    ret = 0;
error:
    VIR_FREE(parent);
    return ret;
600 601 602 603 604 605 606 607
}


/**
 * Iterate over the pool's directory and enumerate all disk images
 * within it. This is non-recursive.
 */
static int
608
virStorageBackendFileSystemRefresh(virConnectPtr conn ATTRIBUTE_UNUSED,
609 610 611 612 613
                                   virStoragePoolObjPtr pool)
{
    DIR *dir;
    struct dirent *ent;
    struct statvfs sb;
614
    virStorageVolDefPtr vol = NULL;
615 616

    if (!(dir = opendir(pool->def->target.path))) {
617
        virReportSystemError(errno,
618 619
                             _("cannot open path '%s'"),
                             pool->def->target.path);
620 621 622 623 624
        goto cleanup;
    }

    while ((ent = readdir(dir)) != NULL) {
        int ret;
625
        char *backingStore;
626
        int backingStoreFormat;
627

628 629
        if (VIR_ALLOC(vol) < 0)
            goto no_memory;
630

631 632
        if ((vol->name = strdup(ent->d_name)) == NULL)
            goto no_memory;
633

634
        vol->type = VIR_STORAGE_VOL_FILE;
635
        vol->target.format = VIR_STORAGE_FILE_RAW; /* Real value is filled in during probe */
636 637 638
        if (virAsprintf(&vol->target.path, "%s/%s",
                        pool->def->target.path,
                        vol->name) == -1)
639 640 641 642
            goto no_memory;

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

644
        if ((ret = virStorageBackendProbeTarget(&vol->target,
645
                                                &backingStore,
646
                                                &backingStoreFormat,
647
                                                &vol->allocation,
648
                                                &vol->capacity,
649
                                                &vol->target.encryption)) < 0) {
650
            if (ret == -1)
651
                goto cleanup;
652
            else {
653 654
                /* Silently ignore non-regular files,
                 * eg '.' '..', 'lost+found' */
655 656
                virStorageVolDefFree(vol);
                vol = NULL;
657
                continue;
658
            }
659 660
        }

661
        if (backingStore != NULL) {
662 663 664 665 666 667 668 669
            vol->backingStore.path = backingStore;
            vol->backingStore.format = backingStoreFormat;

            if (virStorageBackendUpdateVolTargetInfo(&vol->backingStore,
                                                     NULL,
                                                     NULL) < 0) {
                VIR_FREE(vol->backingStore.path);
                goto cleanup;
670 671 672 673
            }
        }


674 675 676 677 678
        if (VIR_REALLOC_N(pool->volumes.objs,
                          pool->volumes.count+1) < 0)
            goto no_memory;
        pool->volumes.objs[pool->volumes.count++] = vol;
        vol = NULL;
679 680 681 682 683
    }
    closedir(dir);


    if (statvfs(pool->def->target.path, &sb) < 0) {
684
        virReportSystemError(errno,
685 686
                             _("cannot statvfs path '%s'"),
                             pool->def->target.path);
687 688 689 690 691 692 693 694 695 696
        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;

697
no_memory:
698
    virReportOOMError();
699 700
    /* fallthrough */

701
 cleanup:
702 703
    if (dir)
        closedir(dir);
704
    virStorageVolDefFree(vol);
705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720
    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
721
virStorageBackendFileSystemStop(virConnectPtr conn ATTRIBUTE_UNUSED,
722 723 724
                                virStoragePoolObjPtr pool)
{
    if (pool->def->type != VIR_STORAGE_POOL_DIR &&
725
        virStorageBackendFileSystemUnmount(pool) < 0)
726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743
        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
744
virStorageBackendFileSystemDelete(virConnectPtr conn ATTRIBUTE_UNUSED,
745 746 747 748 749
                                  virStoragePoolObjPtr pool,
                                  unsigned int flags ATTRIBUTE_UNUSED)
{
    /* XXX delete all vols first ? */

750
    if (rmdir(pool->def->target.path) < 0) {
751
        virReportSystemError(errno,
752
                             _("failed to remove pool '%s'"),
753
                             pool->def->target.path);
754 755 756 757 758 759 760 761
        return -1;
    }

    return 0;
}


/**
762 763 764 765
 * 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.
766 767
 */
static int
768
virStorageBackendFileSystemVolCreate(virConnectPtr conn ATTRIBUTE_UNUSED,
769 770 771 772
                                     virStoragePoolObjPtr pool,
                                     virStorageVolDefPtr vol)
{

773 774
    vol->type = VIR_STORAGE_VOL_FILE;

R
Ryota Ozaki 已提交
775
    VIR_FREE(vol->target.path);
776 777 778
    if (virAsprintf(&vol->target.path, "%s/%s",
                    pool->def->target.path,
                    vol->name) == -1) {
779
        virReportOOMError();
780 781
        return -1;
    }
782

R
Ryota Ozaki 已提交
783
    VIR_FREE(vol->key);
784 785
    vol->key = strdup(vol->target.path);
    if (vol->key == NULL) {
786
        virReportOOMError();
787 788 789
        return -1;
    }

790 791 792
    return 0;
}

793
static int createFileDir(virConnectPtr conn ATTRIBUTE_UNUSED,
794
                         virStoragePoolObjPtr pool,
795
                         virStorageVolDefPtr vol,
796 797
                         virStorageVolDefPtr inputvol,
                         unsigned int flags ATTRIBUTE_UNUSED) {
798 799
    int err;

800
    if (inputvol) {
801
        virStorageReportError(VIR_ERR_INTERNAL_ERROR,
802 803 804 805 806
                              "%s",
                              _("cannot copy from volume to a directory volume"));
        return -1;
    }

807 808 809 810 811
    uid_t uid = (vol->target.perms.uid == -1)
        ? getuid() : vol->target.perms.uid;
    gid_t gid = (vol->target.perms.gid == -1)
        ? getgid() : vol->target.perms.gid;

812
    if ((err = virDirCreate(vol->target.path, vol->target.perms.mode,
813
                            uid, gid,
814
                            VIR_DIR_CREATE_FORCE_PERMS |
815
                            (pool->def->type == VIR_STORAGE_POOL_NETFS
816 817
                             ? VIR_DIR_CREATE_AS_UID : 0))) < 0) {
        virReportSystemError(-err, _("cannot create path '%s'"),
818 819 820
                             vol->target.path);
        return -1;
    }
821

822 823
    return 0;
}
824

825
static int
826
_virStorageBackendFileSystemVolBuild(virConnectPtr conn,
827
                                     virStoragePoolObjPtr pool,
828 829
                                     virStorageVolDefPtr vol,
                                     virStorageVolDefPtr inputvol)
830
{
831
    virStorageBackendBuildVolFrom create_func;
832
    int tool_type;
833

834
    if (inputvol) {
835
        if (vol->target.encryption != NULL) {
836
            virStorageReportError(VIR_ERR_NO_SUPPORT,
837 838 839 840 841
                                  "%s", _("storage pool does not support "
                                          "building encrypted volumes from "
                                          "other volumes"));
            return -1;
        }
842
        create_func = virStorageBackendGetBuildVolFromFunction(vol,
843
                                                               inputvol);
844 845
        if (!create_func)
            return -1;
846
    } else if (vol->target.format == VIR_STORAGE_FILE_RAW) {
847
        create_func = virStorageBackendCreateRaw;
848
    } else if (vol->target.format == VIR_STORAGE_FILE_DIR) {
849
        create_func = createFileDir;
850
    } else if ((tool_type = virStorageBackendFindFSImageTool(NULL)) != -1) {
851
        create_func = virStorageBackendFSImageToolTypeToFunc(tool_type);
852 853

        if (!create_func)
854
            return -1;
855
    } else {
856
        virStorageReportError(VIR_ERR_INTERNAL_ERROR,
J
Jim Meyering 已提交
857 858
                              "%s", _("creation of non-raw images "
                                      "is not supported without qemu-img"));
859 860 861
        return -1;
    }

862
    if (create_func(conn, pool, vol, inputvol, 0) < 0)
863
        return -1;
864 865 866
    return 0;
}

867 868 869 870 871 872 873
/**
 * 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,
874
                                    virStoragePoolObjPtr pool,
875
                                    virStorageVolDefPtr vol) {
876
    return _virStorageBackendFileSystemVolBuild(conn, pool, vol, NULL);
877 878 879 880 881 882 883
}

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

/**
 * Remove a volume - just unlinks for now
 */
static int
895
virStorageBackendFileSystemVolDelete(virConnectPtr conn ATTRIBUTE_UNUSED,
896 897 898 899 900 901 902
                                     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) {
903
            virReportSystemError(errno,
904 905
                                 _("cannot unlink file '%s'"),
                                 vol->target.path);
906 907 908 909 910 911 912 913 914 915 916 917 918 919 920
            return -1;
        }
    }
    return 0;
}


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

923
    /* Refresh allocation / permissions info in case its changed */
924
    ret = virStorageBackendUpdateVolInfo(vol, 0);
925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941
    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);
942
                virReportOOMError();
943 944 945 946 947 948 949 950 951 952 953 954 955 956
                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;
957 958 959 960 961 962
}

virStorageBackend virStorageBackendDirectory = {
    .type = VIR_STORAGE_POOL_DIR,

    .buildPool = virStorageBackendFileSystemBuild,
963
    .checkPool = virStorageBackendFileSystemCheck,
964 965
    .refreshPool = virStorageBackendFileSystemRefresh,
    .deletePool = virStorageBackendFileSystemDelete,
966
    .buildVol = virStorageBackendFileSystemVolBuild,
967
    .buildVolFrom = virStorageBackendFileSystemVolBuildFrom,
968 969 970 971 972 973 974 975 976 977
    .createVol = virStorageBackendFileSystemVolCreate,
    .refreshVol = virStorageBackendFileSystemVolRefresh,
    .deleteVol = virStorageBackendFileSystemVolDelete,
};

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

    .buildPool = virStorageBackendFileSystemBuild,
978
    .checkPool = virStorageBackendFileSystemCheck,
979 980 981 982
    .startPool = virStorageBackendFileSystemStart,
    .refreshPool = virStorageBackendFileSystemRefresh,
    .stopPool = virStorageBackendFileSystemStop,
    .deletePool = virStorageBackendFileSystemDelete,
983
    .buildVol = virStorageBackendFileSystemVolBuild,
984
    .buildVolFrom = virStorageBackendFileSystemVolBuildFrom,
985 986 987 988 989 990 991 992
    .createVol = virStorageBackendFileSystemVolCreate,
    .refreshVol = virStorageBackendFileSystemVolRefresh,
    .deleteVol = virStorageBackendFileSystemVolDelete,
};
virStorageBackend virStorageBackendNetFileSystem = {
    .type = VIR_STORAGE_POOL_NETFS,

    .buildPool = virStorageBackendFileSystemBuild,
993
    .checkPool = virStorageBackendFileSystemCheck,
994
    .startPool = virStorageBackendFileSystemStart,
995
    .findPoolSources = virStorageBackendFileSystemNetFindPoolSources,
996 997 998
    .refreshPool = virStorageBackendFileSystemRefresh,
    .stopPool = virStorageBackendFileSystemStop,
    .deletePool = virStorageBackendFileSystemDelete,
999
    .buildVol = virStorageBackendFileSystemVolBuild,
1000
    .buildVolFrom = virStorageBackendFileSystemVolBuildFrom,
1001 1002 1003 1004 1005
    .createVol = virStorageBackendFileSystemVolCreate,
    .refreshVol = virStorageBackendFileSystemVolRefresh,
    .deleteVol = virStorageBackendFileSystemVolDelete,
};
#endif /* WITH_STORAGE_FS */