storage_backend_fs.c 31.5 KB
Newer Older
1 2 3
/*
 * storage_backend_fs.c: storage backend for FS and directory handling
 *
4
 * Copyright (C) 2007-2011 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 29 30 31 32 33 34 35
 * 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>
#include <stdio.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

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

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

49
#define VIR_FROM_THIS VIR_FROM_STORAGE
50

51 52 53 54 55
#define VIR_STORAGE_VOL_FS_OPEN_FLAGS       (VIR_STORAGE_VOL_OPEN_DEFAULT   |\
                                             VIR_STORAGE_VOL_OPEN_DIR)
#define VIR_STORAGE_VOL_FS_REFRESH_FLAGS    (VIR_STORAGE_VOL_FS_OPEN_FLAGS  &\
                                             ~VIR_STORAGE_VOL_OPEN_ERROR)

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

E
Eric Blake 已提交
67 68
    *backingStore = NULL;
    *backingStoreFormat = VIR_STORAGE_FILE_AUTO;
69 70 71
    if (encryption)
        *encryption = NULL;

72
    if ((ret = virStorageBackendVolOpenCheckMode(target->path,
73
                                        VIR_STORAGE_VOL_FS_REFRESH_FLAGS)) < 0)
74 75
        return ret; /* Take care to propagate ret, it is not always -1 */
    fd = ret;
76

77
    if ((ret = virStorageBackendUpdateVolTargetInfoFD(target, fd,
78 79
                                                      allocation,
                                                      capacity)) < 0) {
80
        VIR_FORCE_CLOSE(fd);
E
Eric Blake 已提交
81
        return ret;
82 83
    }

84 85
    memset(&meta, 0, sizeof(meta));

86
    if ((target->format = virStorageFileProbeFormatFromFD(target->path, fd)) < 0) {
87
        VIR_FORCE_CLOSE(fd);
88 89 90
        return -1;
    }

91 92 93
    if (virStorageFileGetMetadataFromFD(target->path, fd,
                                        target->format,
                                        &meta) < 0) {
94
        VIR_FORCE_CLOSE(fd);
95 96
        return -1;
    }
97

98
    VIR_FORCE_CLOSE(fd);
99

100
    if (meta.backingStore) {
E
Eric Blake 已提交
101 102 103
        *backingStore = meta.backingStore;
        meta.backingStore = NULL;
        if (meta.backingStoreFormat == VIR_STORAGE_FILE_AUTO) {
104 105 106 107 108 109 110 111 112 113 114
            if ((ret = virStorageFileProbeFormat(*backingStore)) < 0) {
                /* If the backing file is currently unavailable, only log an error,
                 * but continue. Returning -1 here would disable the whole storage
                 * pool, making it unavailable for even maintenance. */
                virStorageReportError(VIR_ERR_INTERNAL_ERROR,
                                      _("cannot probe backing volume format: %s"),
                                      *backingStore);
                ret = -3;
            } else {
                *backingStoreFormat = ret;
                ret = 0;
115 116
            }
        } else {
E
Eric Blake 已提交
117
            *backingStoreFormat = meta.backingStoreFormat;
118
            ret = 0;
119
        }
E
Eric Blake 已提交
120 121
    } else {
        VIR_FREE(meta.backingStore);
122
        ret = 0;
123 124 125 126 127 128
    }

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

    if (encryption != NULL && meta.encrypted) {
129
        if (VIR_ALLOC(*encryption) < 0) {
130
            virReportOOMError();
131
            goto cleanup;
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
        }

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

150
    return ret;
151 152

cleanup:
E
Eric Blake 已提交
153
    VIR_FREE(*backingStore);
154
    return -1;
155 156 157
}

#if WITH_STORAGE_FS
158

159
# include <mntent.h>
160

161 162
struct _virNetfsDiscoverState {
    const char *host;
163
    virStoragePoolSourceList list;
164 165 166 167 168
};

typedef struct _virNetfsDiscoverState virNetfsDiscoverState;

static int
169
virStorageBackendFileSystemNetFindPoolSourcesFunc(virStoragePoolObjPtr pool ATTRIBUTE_UNUSED,
170 171 172 173 174
                                                  char **const groups,
                                                  void *data)
{
    virNetfsDiscoverState *state = data;
    const char *name, *path;
175 176
    virStoragePoolSource *src = NULL;
    int ret = -1;
177 178 179 180 181

    path = groups[0];

    name = strrchr(path, '/');
    if (name == NULL) {
182
        virStorageReportError(VIR_ERR_INTERNAL_ERROR,
183
                              _("invalid netfs path (no /): %s"), path);
184
        goto cleanup;
185 186 187
    }
    name += 1;
    if (*name == '\0') {
188
        virStorageReportError(VIR_ERR_INTERNAL_ERROR,
189
                              _("invalid netfs path (ends in /): %s"), path);
190
        goto cleanup;
191 192
    }

193
    if (!(src = virStoragePoolSourceListNewSource(&state->list)))
194
        goto cleanup;
195

196
    if (!(src->host.name = strdup(state->host)) ||
197
        !(src->dir = strdup(path))) {
198
        virReportOOMError();
199 200
        goto cleanup;
    }
201
    src->format = VIR_STORAGE_POOL_NETFS_NFS;
202

203 204 205
    src = NULL;
    ret = 0;
cleanup:
206 207
    virStoragePoolSourceFree(src);
    VIR_FREE(src);
208
    return ret;
209 210
}

211

212
static char *
213
virStorageBackendFileSystemNetFindPoolSources(virConnectPtr conn ATTRIBUTE_UNUSED,
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230
                                              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
    };
231 232 233 234 235 236 237 238
    virNetfsDiscoverState state = {
        .host = NULL,
        .list = {
            .type = VIR_STORAGE_POOL_NETFS,
            .nsources = 0,
            .sources = NULL
        }
    };
239
    const char *prog[] = { SHOWMOUNT, "--no-headers", "--exports", NULL, NULL };
240
    virStoragePoolSourcePtr source = NULL;
241
    char *retval = NULL;
242
    unsigned int i;
243

244
    source = virStoragePoolDefParseSourceString(srcSpec,
245 246
                                                VIR_STORAGE_POOL_NETFS);
    if (!source)
247 248
        goto cleanup;

249 250
    state.host = source->host.name;
    prog[3] = source->host.name;
251

252
    if (virStorageBackendRunProgRegex(NULL, prog, 1, regexes, vars,
253 254
                            virStorageBackendFileSystemNetFindPoolSourcesFunc,
                            &state) < 0)
255 256
        goto cleanup;

257
    retval = virStoragePoolSourceListFormat(&state.list);
258
    if (retval == NULL) {
259
        virReportOOMError();
260 261 262 263
        goto cleanup;
    }

 cleanup:
264 265 266
    for (i = 0; i < state.list.nsources; i++)
        virStoragePoolSourceFree(&state.list.sources[i]);

267 268
    virStoragePoolSourceFree(source);
    VIR_FREE(source);
269

270
    VIR_FREE(state.list.sources);
271 272 273 274 275

    return retval;
}


276 277 278 279 280 281 282 283 284
/**
 * @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
285
virStorageBackendFileSystemIsMounted(virStoragePoolObjPtr pool) {
286
    FILE *mtab;
287 288
    struct mntent ent;
    char buf[1024];
289 290

    if ((mtab = fopen(_PATH_MOUNTED, "r")) == NULL) {
291
        virReportSystemError(errno,
292 293
                             _("cannot read mount list '%s'"),
                             _PATH_MOUNTED);
294 295 296
        return -1;
    }

297 298
    while ((getmntent_r(mtab, &ent, buf, sizeof(buf))) != NULL) {
        if (STREQ(ent.mnt_dir, pool->def->target.path)) {
299
            VIR_FORCE_FCLOSE(mtab);
300 301 302 303
            return 1;
        }
    }

304
    VIR_FORCE_FCLOSE(mtab);
305 306 307 308 309 310 311 312 313 314 315 316 317
    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
318
virStorageBackendFileSystemMount(virStoragePoolObjPtr pool) {
319
    char *src;
320 321 322 323 324 325 326
    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);
327 328 329
    int glusterfs = (pool->def->type == VIR_STORAGE_POOL_NETFS &&
                 pool->def->source.format == VIR_STORAGE_POOL_NETFS_GLUSTERFS);

330 331 332 333 334 335 336 337 338 339
    int source_index;

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

    const char *fs_argv[] =  {
340 341 342
        MOUNT,
        "-t",
        pool->def->type == VIR_STORAGE_POOL_FS ?
343 344
        virStoragePoolFormatFileSystemTypeToString(pool->def->source.format) :
        virStoragePoolFormatFileSystemNetTypeToString(pool->def->source.format),
345 346
        NULL, /* Fill in shortly - careful not to add extra fields
                 before this */
347 348 349
        pool->def->target.path,
        NULL,
    };
350

351 352 353 354 355 356 357 358
    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",
E
Eric Blake 已提交
359
        "direct-io-mode=1",
360 361 362 363
        pool->def->target.path,
        NULL,
    };

364 365 366
    if (netauto) {
        mntargv = netfs_auto_argv;
        source_index = 1;
367 368 369
    } else if (glusterfs) {
        mntargv = glusterfs_argv;
        source_index = 3;
370 371 372 373 374
    } else {
        mntargv = fs_argv;
        source_index = 3;
    }

375 376 377 378
    int ret;

    if (pool->def->type == VIR_STORAGE_POOL_NETFS) {
        if (pool->def->source.host.name == NULL) {
379
            virStorageReportError(VIR_ERR_INTERNAL_ERROR,
J
Jim Meyering 已提交
380
                                  "%s", _("missing source host"));
381 382 383
            return -1;
        }
        if (pool->def->source.dir == NULL) {
384
            virStorageReportError(VIR_ERR_INTERNAL_ERROR,
J
Jim Meyering 已提交
385
                                  "%s", _("missing source path"));
386 387 388 389
            return -1;
        }
    } else {
        if (pool->def->source.ndevice != 1) {
390
            virStorageReportError(VIR_ERR_INTERNAL_ERROR,
J
Jim Meyering 已提交
391
                                  "%s", _("missing source device"));
392 393 394 395
            return -1;
        }
    }

J
Jim Meyering 已提交
396
    /* Short-circuit if already mounted */
397
    if ((ret = virStorageBackendFileSystemIsMounted(pool)) != 0) {
398 399 400 401 402 403 404
        if (ret < 0)
            return -1;
        else
            return 0;
    }

    if (pool->def->type == VIR_STORAGE_POOL_NETFS) {
405 406 407
        if (virAsprintf(&src, "%s:%s",
                        pool->def->source.host.name,
                        pool->def->source.dir) == -1) {
408
            virReportOOMError();
409 410
            return -1;
        }
411

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

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

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

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

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

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


481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504
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
505 506 507 508 509 510 511 512 513 514 515
/**
 * @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
516
virStorageBackendFileSystemStart(virConnectPtr conn ATTRIBUTE_UNUSED,
517 518 519
                                 virStoragePoolObjPtr pool)
{
    if (pool->def->type != VIR_STORAGE_POOL_DIR &&
520
        virStorageBackendFileSystemMount(pool) < 0)
521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538
        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
539
virStorageBackendFileSystemBuild(virConnectPtr conn ATTRIBUTE_UNUSED,
540 541 542
                                 virStoragePoolObjPtr pool,
                                 unsigned int flags ATTRIBUTE_UNUSED)
{
543 544 545 546 547
    int err, ret = -1;
    char *parent;
    char *p;

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

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

    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
589 590
                                 ? VIR_DIR_CREATE_AS_UID : 0)) < 0)) {
            virReportSystemError(-err, _("cannot create path '%s'"),
591 592 593
                                 pool->def->target.path);
            goto error;
        }
594 595 596 597 598
    }
    ret = 0;
error:
    VIR_FREE(parent);
    return ret;
599 600 601 602 603 604 605 606
}


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

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

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

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

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

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

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

643
        if ((ret = virStorageBackendProbeTarget(&vol->target,
644
                                                &backingStore,
645
                                                &backingStoreFormat,
646
                                                &vol->allocation,
647
                                                &vol->capacity,
648
                                                &vol->target.encryption)) < 0) {
649
            if (ret == -2) {
650
                /* Silently ignore non-regular files,
651
                 * eg '.' '..', 'lost+found', dangling symbolic link */
652 653
                virStorageVolDefFree(vol);
                vol = NULL;
654
                continue;
655 656 657 658 659 660 661 662 663
            } else if (ret == -3) {
                /* The backing file is currently unavailable, its format is not
                 * explicitly specified, the probe to auto detect the format
                 * failed: continue with faked RAW format, since AUTO will
                 * break virStorageVolTargetDefFormat() generating the line
                 * <format type='...'/>. */
                backingStoreFormat = VIR_STORAGE_FILE_RAW;
            } else
                goto cleanup;
664 665
        }

666
        if (backingStore != NULL) {
667 668 669 670
            vol->backingStore.path = backingStore;
            vol->backingStore.format = backingStoreFormat;

            if (virStorageBackendUpdateVolTargetInfo(&vol->backingStore,
671 672
                                        NULL, NULL,
                                        VIR_STORAGE_VOL_OPEN_DEFAULT) < 0) {
673 674 675 676 677 678 679 680 681
                /* The backing file is currently unavailable, the capacity,
                 * allocation, owner, group and mode are unknown. Just log the
                 * error an continue.
                 * Unfortunately virStorageBackendProbeTarget() might already
                 * have logged a similar message for the same problem, but only
                 * if AUTO format detection was used. */
                virStorageReportError(VIR_ERR_INTERNAL_ERROR,
                                      _("cannot probe backing volume info: %s"),
                                      vol->backingStore.path);
682 683 684 685
            }
        }


686 687 688 689 690
        if (VIR_REALLOC_N(pool->volumes.objs,
                          pool->volumes.count+1) < 0)
            goto no_memory;
        pool->volumes.objs[pool->volumes.count++] = vol;
        vol = NULL;
691 692 693 694 695
    }
    closedir(dir);


    if (statvfs(pool->def->target.path, &sb) < 0) {
696
        virReportSystemError(errno,
697 698
                             _("cannot statvfs path '%s'"),
                             pool->def->target.path);
699 700 701 702 703 704 705 706 707 708
        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;

709
no_memory:
710
    virReportOOMError();
711 712
    /* fallthrough */

713
 cleanup:
714 715
    if (dir)
        closedir(dir);
716
    virStorageVolDefFree(vol);
717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732
    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
733
virStorageBackendFileSystemStop(virConnectPtr conn ATTRIBUTE_UNUSED,
734 735 736
                                virStoragePoolObjPtr pool)
{
    if (pool->def->type != VIR_STORAGE_POOL_DIR &&
737
        virStorageBackendFileSystemUnmount(pool) < 0)
738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755
        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
756
virStorageBackendFileSystemDelete(virConnectPtr conn ATTRIBUTE_UNUSED,
757 758 759 760 761
                                  virStoragePoolObjPtr pool,
                                  unsigned int flags ATTRIBUTE_UNUSED)
{
    /* XXX delete all vols first ? */

762
    if (rmdir(pool->def->target.path) < 0) {
763
        virReportSystemError(errno,
764
                             _("failed to remove pool '%s'"),
765
                             pool->def->target.path);
766 767 768 769 770 771 772 773
        return -1;
    }

    return 0;
}


/**
774 775 776 777
 * 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.
778 779
 */
static int
780
virStorageBackendFileSystemVolCreate(virConnectPtr conn ATTRIBUTE_UNUSED,
781 782 783 784
                                     virStoragePoolObjPtr pool,
                                     virStorageVolDefPtr vol)
{

785 786
    vol->type = VIR_STORAGE_VOL_FILE;

R
Ryota Ozaki 已提交
787
    VIR_FREE(vol->target.path);
788 789 790
    if (virAsprintf(&vol->target.path, "%s/%s",
                    pool->def->target.path,
                    vol->name) == -1) {
791
        virReportOOMError();
792 793
        return -1;
    }
794

R
Ryota Ozaki 已提交
795
    VIR_FREE(vol->key);
796 797
    vol->key = strdup(vol->target.path);
    if (vol->key == NULL) {
798
        virReportOOMError();
799 800 801
        return -1;
    }

802 803 804
    return 0;
}

805
static int createFileDir(virConnectPtr conn ATTRIBUTE_UNUSED,
806
                         virStoragePoolObjPtr pool,
807
                         virStorageVolDefPtr vol,
808 809
                         virStorageVolDefPtr inputvol,
                         unsigned int flags ATTRIBUTE_UNUSED) {
810 811
    int err;

812
    if (inputvol) {
813
        virStorageReportError(VIR_ERR_INTERNAL_ERROR,
814 815 816 817 818
                              "%s",
                              _("cannot copy from volume to a directory volume"));
        return -1;
    }

819 820 821 822 823
    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;

824
    if ((err = virDirCreate(vol->target.path, vol->target.perms.mode,
825
                            uid, gid,
826
                            VIR_DIR_CREATE_FORCE_PERMS |
827
                            (pool->def->type == VIR_STORAGE_POOL_NETFS
828 829
                             ? VIR_DIR_CREATE_AS_UID : 0))) < 0) {
        virReportSystemError(-err, _("cannot create path '%s'"),
830 831 832
                             vol->target.path);
        return -1;
    }
833

834 835
    return 0;
}
836

837
static int
838
_virStorageBackendFileSystemVolBuild(virConnectPtr conn,
839
                                     virStoragePoolObjPtr pool,
840 841
                                     virStorageVolDefPtr vol,
                                     virStorageVolDefPtr inputvol)
842
{
843
    virStorageBackendBuildVolFrom create_func;
844
    int tool_type;
845

846
    if (inputvol) {
847
        if (vol->target.encryption != NULL) {
848
            virStorageReportError(VIR_ERR_NO_SUPPORT,
849 850 851 852 853
                                  "%s", _("storage pool does not support "
                                          "building encrypted volumes from "
                                          "other volumes"));
            return -1;
        }
854
        create_func = virStorageBackendGetBuildVolFromFunction(vol,
855
                                                               inputvol);
856 857
        if (!create_func)
            return -1;
858
    } else if (vol->target.format == VIR_STORAGE_FILE_RAW) {
859
        create_func = virStorageBackendCreateRaw;
860
    } else if (vol->target.format == VIR_STORAGE_FILE_DIR) {
861
        create_func = createFileDir;
862
    } else if ((tool_type = virStorageBackendFindFSImageTool(NULL)) != -1) {
863
        create_func = virStorageBackendFSImageToolTypeToFunc(tool_type);
864 865

        if (!create_func)
866
            return -1;
867
    } else {
868
        virStorageReportError(VIR_ERR_INTERNAL_ERROR,
J
Jim Meyering 已提交
869 870
                              "%s", _("creation of non-raw images "
                                      "is not supported without qemu-img"));
871 872 873
        return -1;
    }

874
    if (create_func(conn, pool, vol, inputvol, 0) < 0)
875
        return -1;
876 877 878
    return 0;
}

879 880 881 882 883 884 885
/**
 * 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,
886
                                    virStoragePoolObjPtr pool,
887
                                    virStorageVolDefPtr vol) {
888
    return _virStorageBackendFileSystemVolBuild(conn, pool, vol, NULL);
889 890 891 892 893 894 895
}

/*
 * Create a storage vol using 'inputvol' as input
 */
static int
virStorageBackendFileSystemVolBuildFrom(virConnectPtr conn,
896
                                        virStoragePoolObjPtr pool,
897 898 899
                                        virStorageVolDefPtr vol,
                                        virStorageVolDefPtr inputvol,
                                        unsigned int flags ATTRIBUTE_UNUSED) {
900
    return _virStorageBackendFileSystemVolBuild(conn, pool, vol, inputvol);
901
}
902 903 904 905 906

/**
 * Remove a volume - just unlinks for now
 */
static int
907
virStorageBackendFileSystemVolDelete(virConnectPtr conn ATTRIBUTE_UNUSED,
908 909 910 911 912 913 914
                                     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) {
915
            virReportSystemError(errno,
916 917
                                 _("cannot unlink file '%s'"),
                                 vol->target.path);
918 919 920 921 922 923 924 925 926 927 928 929 930 931 932
            return -1;
        }
    }
    return 0;
}


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

935
    /* Refresh allocation / permissions info in case its changed */
936 937
    ret = virStorageBackendUpdateVolInfoFlags(vol, 0,
                                              VIR_STORAGE_VOL_FS_OPEN_FLAGS);
938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954
    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);
955
                virReportOOMError();
956 957 958 959 960 961 962 963 964 965 966 967 968 969
                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;
970 971 972 973 974 975
}

virStorageBackend virStorageBackendDirectory = {
    .type = VIR_STORAGE_POOL_DIR,

    .buildPool = virStorageBackendFileSystemBuild,
976
    .checkPool = virStorageBackendFileSystemCheck,
977 978
    .refreshPool = virStorageBackendFileSystemRefresh,
    .deletePool = virStorageBackendFileSystemDelete,
979
    .buildVol = virStorageBackendFileSystemVolBuild,
980
    .buildVolFrom = virStorageBackendFileSystemVolBuildFrom,
981 982 983 984 985 986 987 988 989 990
    .createVol = virStorageBackendFileSystemVolCreate,
    .refreshVol = virStorageBackendFileSystemVolRefresh,
    .deleteVol = virStorageBackendFileSystemVolDelete,
};

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

    .buildPool = virStorageBackendFileSystemBuild,
991
    .checkPool = virStorageBackendFileSystemCheck,
992 993 994 995
    .startPool = virStorageBackendFileSystemStart,
    .refreshPool = virStorageBackendFileSystemRefresh,
    .stopPool = virStorageBackendFileSystemStop,
    .deletePool = virStorageBackendFileSystemDelete,
996
    .buildVol = virStorageBackendFileSystemVolBuild,
997
    .buildVolFrom = virStorageBackendFileSystemVolBuildFrom,
998 999 1000 1001 1002 1003 1004 1005
    .createVol = virStorageBackendFileSystemVolCreate,
    .refreshVol = virStorageBackendFileSystemVolRefresh,
    .deleteVol = virStorageBackendFileSystemVolDelete,
};
virStorageBackend virStorageBackendNetFileSystem = {
    .type = VIR_STORAGE_POOL_NETFS,

    .buildPool = virStorageBackendFileSystemBuild,
1006
    .checkPool = virStorageBackendFileSystemCheck,
1007
    .startPool = virStorageBackendFileSystemStart,
1008
    .findPoolSources = virStorageBackendFileSystemNetFindPoolSources,
1009 1010 1011
    .refreshPool = virStorageBackendFileSystemRefresh,
    .stopPool = virStorageBackendFileSystemStop,
    .deletePool = virStorageBackendFileSystemDelete,
1012
    .buildVol = virStorageBackendFileSystemVolBuild,
1013
    .buildVolFrom = virStorageBackendFileSystemVolBuildFrom,
1014 1015 1016 1017 1018
    .createVol = virStorageBackendFileSystemVolCreate,
    .refreshVol = virStorageBackendFileSystemVolRefresh,
    .deleteVol = virStorageBackendFileSystemVolDelete,
};
#endif /* WITH_STORAGE_FS */