storage_backend_fs.c 17.9 KB
Newer Older
1 2 3
/*
 * storage_backend_fs.c: storage backend for FS and directory handling
 *
4
 * Copyright (C) 2007-2015 Red Hat, Inc.
5 6 7 8 9 10 11 12 13 14 15 16 17
 * 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
18
 * License along with this library.  If not, see
O
Osier Yang 已提交
19
 * <http://www.gnu.org/licenses/>.
20 21 22 23 24 25 26 27
 */

#include <config.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

28
#include "virerror.h"
29
#include "storage_backend_fs.h"
30
#include "storage_util.h"
31
#include "storage_conf.h"
32
#include "vircommand.h"
33
#include "viralloc.h"
E
Eric Blake 已提交
34
#include "virfile.h"
35
#include "virlog.h"
36
#include "virstring.h"
37

38
#define VIR_FROM_THIS VIR_FROM_STORAGE
39

40 41
VIR_LOG_INIT("storage.storage_backend_fs");

42
#if WITH_STORAGE_FS
43

44
# include <mntent.h>
45

46 47
struct _virNetfsDiscoverState {
    const char *host;
48
    virStoragePoolSourceList list;
49 50 51 52 53
};

typedef struct _virNetfsDiscoverState virNetfsDiscoverState;

static int
54
virStorageBackendFileSystemNetFindPoolSourcesFunc(char **const groups,
55 56 57 58
                                                  void *data)
{
    virNetfsDiscoverState *state = data;
    const char *name, *path;
59 60
    virStoragePoolSource *src = NULL;
    int ret = -1;
61 62 63

    path = groups[0];

64
    if (!(name = strrchr(path, '/'))) {
65 66
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("invalid netfs path (no /): %s"), path);
67
        goto cleanup;
68 69 70
    }
    name += 1;
    if (*name == '\0') {
71 72
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("invalid netfs path (ends in /): %s"), path);
73
        goto cleanup;
74 75
    }

76
    if (!(src = virStoragePoolSourceListNewSource(&state->list)))
77
        goto cleanup;
78

79
    if (VIR_ALLOC_N(src->hosts, 1) < 0)
80
        goto cleanup;
81
    src->nhost = 1;
82

83 84
    if (VIR_STRDUP(src->hosts[0].name, state->host) < 0 ||
        VIR_STRDUP(src->dir, path) < 0)
85
        goto cleanup;
86
    src->format = VIR_STORAGE_POOL_NETFS_NFS;
87

88
    ret = 0;
89
 cleanup:
90
    return ret;
91 92
}

93

94
static int
95
virStorageBackendFileSystemNetFindNFSPoolSources(virNetfsDiscoverState *state)
96
{
97 98
    int ret = -1;

99 100 101 102 103 104 105 106 107 108 109 110 111 112
    /*
     *  # 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
    };
113 114 115 116 117 118 119 120 121 122 123

    virCommandPtr cmd = NULL;

    cmd = virCommandNewArgList(SHOWMOUNT,
                               "--no-headers",
                               "--exports",
                               state->host,
                               NULL);

    if (virCommandRunRegex(cmd, 1, regexes, vars,
                           virStorageBackendFileSystemNetFindPoolSourcesFunc,
124
                           state, NULL, NULL) < 0)
125
        goto cleanup;
126

127 128 129
    ret = 0;

 cleanup:
130
    virCommandFree(cmd);
131
    return ret;
132 133 134 135
}


static char *
136
virStorageBackendFileSystemNetFindPoolSources(const char *srcSpec,
137 138
                                              unsigned int flags)
{
139 140 141 142 143 144 145 146
    virNetfsDiscoverState state = {
        .host = NULL,
        .list = {
            .type = VIR_STORAGE_POOL_NETFS,
            .nsources = 0,
            .sources = NULL
        }
    };
147
    virStoragePoolSourcePtr source = NULL;
148
    char *ret = NULL;
149
    size_t i;
150 151
    int retNFS = -1;
    int retGluster = 0;
152

E
Eric Blake 已提交
153 154
    virCheckFlags(0, NULL);

155
    if (!srcSpec) {
156 157
        virReportError(VIR_ERR_INVALID_ARG, "%s",
                       _("hostname must be specified for netfs sources"));
158 159 160 161 162 163
        return NULL;
    }

    if (!(source = virStoragePoolDefParseSourceString(srcSpec,
                                                      VIR_STORAGE_POOL_NETFS)))
        return NULL;
164

165
    if (source->nhost != 1) {
166 167
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                       _("Expected exactly 1 host for the storage pool"));
168 169 170 171
        goto cleanup;
    }

    state.host = source->hosts[0].name;
172

173
    retNFS = virStorageBackendFileSystemNetFindNFSPoolSources(&state);
174

175
    retGluster = virStorageBackendFindGlusterPoolSources(state.host,
176
                                                         VIR_STORAGE_POOL_NETFS,
177
                                                         &state.list, false);
178 179 180 181

    if (retGluster < 0)
        goto cleanup;

182
    /* If both fail, then we won't return an empty list - return an error */
183 184 185 186
    if (retNFS < 0 && retGluster == 0) {
        virReportError(VIR_ERR_OPERATION_FAILED,
                       _("no storage pools were found on host '%s'"),
                       state.host);
187
        goto cleanup;
188
    }
189

190
    if (!(ret = virStoragePoolSourceListFormat(&state.list)))
191 192 193
        goto cleanup;

 cleanup:
194
    for (i = 0; i < state.list.nsources; i++)
195 196
        virStoragePoolSourceClear(&state.list.sources[i]);
    VIR_FREE(state.list.sources);
197

198
    virStoragePoolSourceFree(source);
199
    return ret;
200 201
}

202 203 204 205 206 207 208 209 210 211
/**
 * @pool storage pool to check FS types
 *
 * Determine if storage pool FS types are properly set up
 *
 * Return 0 if everything's OK, -1 on error
 */
static int
virStorageBackendFileSystemIsValid(virStoragePoolObjPtr pool)
{
212 213 214 215
    virStoragePoolDefPtr def = virStoragePoolObjGetDef(pool);

    if (def->type == VIR_STORAGE_POOL_NETFS) {
        if (def->source.nhost != 1) {
216 217 218 219
            virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                           _("expected exactly 1 host for the storage pool"));
            return -1;
        }
220
        if (def->source.hosts[0].name == NULL) {
221 222 223 224
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           "%s", _("missing source host"));
            return -1;
        }
225
        if (def->source.dir == NULL) {
226 227 228 229 230
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           "%s", _("missing source path"));
            return -1;
        }
    } else {
231 232
        if (def->source.ndevice != 1) {
            if (def->source.ndevice == 0)
233 234 235 236 237 238
                virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                               _("missing source device"));
            else
                virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                               _("expected exactly 1 device for the "
                                 "storage pool"));
239 240 241 242 243
            return -1;
        }
    }
    return 0;
}
244

245

246 247 248 249 250 251 252 253
/**
 * @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
254 255
virStorageBackendFileSystemIsMounted(virStoragePoolObjPtr pool)
{
256
    int ret = -1;
257
    virStoragePoolDefPtr def = virStoragePoolObjGetDef(pool);
258
    char *src = NULL;
259
    FILE *mtab;
260 261
    struct mntent ent;
    char buf[1024];
262
    int rc1, rc2;
263 264

    if ((mtab = fopen(_PATH_MOUNTED, "r")) == NULL) {
265
        virReportSystemError(errno,
266 267
                             _("cannot read mount list '%s'"),
                             _PATH_MOUNTED);
268
        goto cleanup;
269 270
    }

271
    while ((getmntent_r(mtab, &ent, buf, sizeof(buf))) != NULL) {
272 273 274
        if (!(src = virStorageBackendFileSystemGetPoolSource(pool)))
            goto cleanup;

275 276 277
        /* compare both mount destinations and sources to be sure the mounted
         * FS pool is really the one we're looking for
         */
278
        if ((rc1 = virFileComparePaths(ent.mnt_dir, def->target.path)) < 0 ||
279 280 281 282
            (rc2 = virFileComparePaths(ent.mnt_fsname, src)) < 0)
            goto cleanup;

        if (rc1 && rc2) {
283 284
            ret = 1;
            goto cleanup;
285
        }
286 287

        VIR_FREE(src);
288 289
    }

290 291 292
    ret = 0;

 cleanup:
293
    VIR_FORCE_FCLOSE(mtab);
294
    VIR_FREE(src);
295
    return ret;
296 297
}

298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330

/**
 * @pool storage pool to mount
 *
 * Ensure that a FS storage pool is mounted on its target location.
 * If already mounted, this is a no-op
 *
 * Returns 0 if successfully mounted, -1 on error
 */
static int
virStorageBackendFileSystemMount(virStoragePoolObjPtr pool)
{
    virStoragePoolDefPtr def = virStoragePoolObjGetDef(pool);
    char *src = NULL;
    virCommandPtr cmd = NULL;
    int ret = -1;
    int rc;

    if (virStorageBackendFileSystemIsValid(pool) < 0)
        return -1;

    if ((rc = virStorageBackendFileSystemIsMounted(pool)) < 0)
        return -1;

    /* Short-circuit if already mounted */
    if (rc == 1) {
        VIR_INFO("Target '%s' is already mounted", def->target.path);
        return 0;
    }

    if (!(src = virStorageBackendFileSystemGetPoolSource(pool)))
        return -1;

331
    cmd = virStorageBackendFileSystemMountCmd(MOUNT, def, src);
332 333 334 335
    if (virCommandRun(cmd, NULL) < 0)
        goto cleanup;

    ret = 0;
336
 cleanup:
337
    virCommandFree(cmd);
338
    VIR_FREE(src);
339
    return ret;
340 341
}

342

343
/**
344 345 346 347 348 349 350 351
 * @pool storage pool to start
 *
 * Starts a directory or FS based storage pool.  The underlying source
 * device will be mounted for FS based pools.
 *
 * Returns 0 on success, -1 on error
 */
static int
352
virStorageBackendFileSystemStart(virStoragePoolObjPtr pool)
353
{
354 355 356
    virStoragePoolDefPtr def = virStoragePoolObjGetDef(pool);

    if (def->type != VIR_STORAGE_POOL_DIR &&
357 358 359 360 361 362 363 364
        virStorageBackendFileSystemMount(pool) < 0)
        return -1;

    return 0;
}


/**
365 366
 * @pool storage pool to unmount
 *
367 368 369
 * Stops a file storage pool.  The underlying source device is unmounted
 * for FS based pools.  Any cached data about volumes is released.
 *
370
 * Ensure that a FS storage pool is not mounted on its target location.
371
 * If already unmounted, this is a no-op.
372 373 374 375
 *
 * Returns 0 if successfully unmounted, -1 on error
 */
static int
376
virStorageBackendFileSystemStop(virStoragePoolObjPtr pool)
377
{
378
    virStoragePoolDefPtr def = virStoragePoolObjGetDef(pool);
379 380
    virCommandPtr cmd = NULL;
    int ret = -1;
381
    int rc;
382

383 384
    if (virStorageBackendFileSystemIsValid(pool) < 0)
        return -1;
385 386

    /* Short-circuit if already unmounted */
387 388
    if ((rc = virStorageBackendFileSystemIsMounted(pool)) != 1)
        return rc;
389

390
    cmd = virCommandNewArgList(UMOUNT, def->target.path, NULL);
391 392 393 394 395 396 397
    if (virCommandRun(cmd, NULL) < 0)
        goto cleanup;

    ret = 0;
 cleanup:
    virCommandFree(cmd);
    return ret;
398 399 400 401
}
#endif /* WITH_STORAGE_FS */


402
static int
403
virStorageBackendFileSystemCheck(virStoragePoolObjPtr pool,
404 405
                                 bool *isActive)
{
406 407 408 409
    virStoragePoolDefPtr def = virStoragePoolObjGetDef(pool);

    if (def->type == VIR_STORAGE_POOL_DIR) {
        *isActive = virFileExists(def->target.path);
410 411 412
#if WITH_STORAGE_FS
    } else {
        int ret;
413
        *isActive = false;
414 415 416 417

        if (virStorageBackendFileSystemIsValid(pool) < 0)
            return -1;

418 419 420 421 422 423 424 425 426 427 428
        if ((ret = virStorageBackendFileSystemIsMounted(pool)) != 0) {
            if (ret < 0)
                return -1;
            *isActive = true;
        }
#endif /* WITH_STORAGE_FS */
    }

    return 0;
}

429 430
/* some platforms don't support mkfs */
#ifdef MKFS
O
Osier Yang 已提交
431 432 433 434 435 436 437
static int
virStorageBackendExecuteMKFS(const char *device,
                             const char *format)
{
    int ret = 0;
    virCommandPtr cmd = NULL;

J
Ján Tomko 已提交
438 439
    cmd = virCommandNewArgList(MKFS, "-t", format, NULL);

440 441 442
    /* use the force, otherwise mkfs.xfs won't overwrite existing fs.
     * Similarly mkfs.ext2, mkfs.ext3, and mkfs.ext4 require supplying -F
     * and mkfs.vfat uses -I */
J
Ján Tomko 已提交
443 444
    if (STREQ(format, "xfs"))
        virCommandAddArg(cmd, "-f");
445 446 447 448 449 450
    else if (STREQ(format, "ext2") ||
             STREQ(format, "ext3") ||
             STREQ(format, "ext4"))
        virCommandAddArg(cmd, "-F");
    else if (STREQ(format, "vfat"))
        virCommandAddArg(cmd, "-I");
J
Ján Tomko 已提交
451 452

    virCommandAddArg(cmd, device);
O
Osier Yang 已提交
453 454 455 456 457 458 459 460

    if (virCommandRun(cmd, NULL) < 0) {
        virReportSystemError(errno,
                             _("Failed to make filesystem of "
                               "type '%s' on device '%s'"),
                             format, device);
        ret = -1;
    }
461 462

    virCommandFree(cmd);
O
Osier Yang 已提交
463 464
    return ret;
}
465 466 467 468 469
#else /* #ifdef MKFS */
static int
virStorageBackendExecuteMKFS(const char *device ATTRIBUTE_UNUSED,
                             const char *format ATTRIBUTE_UNUSED)
{
470 471 472 473 474
    virReportError(VIR_ERR_INTERNAL_ERROR,
                   _("mkfs is not supported on this platform: "
                     "Failed to make filesystem of "
                     "type '%s' on device '%s'"),
                   format, device);
475 476 477
    return -1;
}
#endif /* #ifdef MKFS */
O
Osier Yang 已提交
478 479 480 481 482

static int
virStorageBackendMakeFileSystem(virStoragePoolObjPtr pool,
                                unsigned int flags)
{
483
    virStoragePoolDefPtr def = virStoragePoolObjGetDef(pool);
O
Osier Yang 已提交
484 485 486 487
    const char *device = NULL, *format = NULL;
    bool ok_to_mkfs = false;
    int ret = -1;

488
    if (def->source.devices == NULL) {
489 490
        virReportError(VIR_ERR_OPERATION_INVALID,
                       _("No source device specified when formatting pool '%s'"),
491
                       def->name);
O
Osier Yang 已提交
492 493 494
        goto error;
    }

495 496
    device = def->source.devices[0].path;
    format = virStoragePoolFormatFileSystemTypeToString(def->source.format);
O
Osier Yang 已提交
497 498 499
    VIR_DEBUG("source device: '%s' format: '%s'", device, format);

    if (!virFileExists(device)) {
500 501
        virReportError(VIR_ERR_OPERATION_INVALID,
                       _("Source device does not exist when formatting pool '%s'"),
502
                       def->name);
O
Osier Yang 已提交
503 504 505 506 507 508
        goto error;
    }

    if (flags & VIR_STORAGE_POOL_BUILD_OVERWRITE) {
        ok_to_mkfs = true;
    } else if (flags & VIR_STORAGE_POOL_BUILD_NO_OVERWRITE &&
509
               virStorageBackendDeviceIsEmpty(device, format, true)) {
O
Osier Yang 已提交
510 511 512
        ok_to_mkfs = true;
    }

513
    if (ok_to_mkfs)
O
Osier Yang 已提交
514 515
        ret = virStorageBackendExecuteMKFS(device, format);

516
 error:
O
Osier Yang 已提交
517 518 519
    return ret;
}

520 521 522

/**
 * @pool storage pool to build
E
Eric Blake 已提交
523
 * @flags controls the pool formatting behaviour
524 525 526
 *
 * Build a directory or FS based storage pool.
 *
527 528 529 530 531 532 533 534 535
 * If no flag is set, it only makes the directory.
 *
 * If VIR_STORAGE_POOL_BUILD_NO_OVERWRITE set, it probes to determine if
 * any filesystem already exists on the target device, returning an error
 * if one exists. If no filesystem already exists, use mkfs to format the
 * target device.
 *
 * If VIR_STORAGE_POOL_BUILD_OVERWRITE is set, mkfs is always executed and
 * any existing data on the target device is overwritten unconditionally.
O
Osier Yang 已提交
536
 *
537
 * The underlying source device is mounted for FS based pools.
538 539 540 541
 *
 * Returns 0 on success, -1 on error
 */
static int
542
virStorageBackendFileSystemBuild(virStoragePoolObjPtr pool,
E
Eric Blake 已提交
543
                                 unsigned int flags)
544
{
O
Osier Yang 已提交
545
    virCheckFlags(VIR_STORAGE_POOL_BUILD_OVERWRITE |
546
                  VIR_STORAGE_POOL_BUILD_NO_OVERWRITE, -1);
O
Osier Yang 已提交
547

548 549 550
    VIR_EXCLUSIVE_FLAGS_RET(VIR_STORAGE_POOL_BUILD_OVERWRITE,
                            VIR_STORAGE_POOL_BUILD_NO_OVERWRITE,
                            -1);
551

552
    if (virStorageBackendBuildLocal(pool) < 0)
553
        return -1;
554 555 556

    if (flags != 0)
        return virStorageBackendMakeFileSystem(pool, flags);
557 558 559 560 561 562 563 564 565

    return 0;
}


virStorageBackend virStorageBackendDirectory = {
    .type = VIR_STORAGE_POOL_DIR,

    .buildPool = virStorageBackendFileSystemBuild,
566
    .checkPool = virStorageBackendFileSystemCheck,
567 568
    .refreshPool = virStorageBackendRefreshLocal,
    .deletePool = virStorageBackendDeleteLocal,
569 570 571 572 573 574
    .buildVol = virStorageBackendVolBuildLocal,
    .buildVolFrom = virStorageBackendVolBuildFromLocal,
    .createVol = virStorageBackendVolCreateLocal,
    .refreshVol = virStorageBackendVolRefreshLocal,
    .deleteVol = virStorageBackendVolDeleteLocal,
    .resizeVol = virStorageBackendVolResizeLocal,
575 576
    .uploadVol = virStorageBackendVolUploadLocal,
    .downloadVol = virStorageBackendVolDownloadLocal,
577
    .wipeVol = virStorageBackendVolWipeLocal,
578 579 580 581 582 583 584
};

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

    .buildPool = virStorageBackendFileSystemBuild,
585
    .checkPool = virStorageBackendFileSystemCheck,
586
    .startPool = virStorageBackendFileSystemStart,
587
    .refreshPool = virStorageBackendRefreshLocal,
588
    .stopPool = virStorageBackendFileSystemStop,
589
    .deletePool = virStorageBackendDeleteLocal,
590 591 592 593 594 595
    .buildVol = virStorageBackendVolBuildLocal,
    .buildVolFrom = virStorageBackendVolBuildFromLocal,
    .createVol = virStorageBackendVolCreateLocal,
    .refreshVol = virStorageBackendVolRefreshLocal,
    .deleteVol = virStorageBackendVolDeleteLocal,
    .resizeVol = virStorageBackendVolResizeLocal,
596 597
    .uploadVol = virStorageBackendVolUploadLocal,
    .downloadVol = virStorageBackendVolDownloadLocal,
598
    .wipeVol = virStorageBackendVolWipeLocal,
599 600 601 602 603
};
virStorageBackend virStorageBackendNetFileSystem = {
    .type = VIR_STORAGE_POOL_NETFS,

    .buildPool = virStorageBackendFileSystemBuild,
604
    .checkPool = virStorageBackendFileSystemCheck,
605
    .startPool = virStorageBackendFileSystemStart,
606
    .findPoolSources = virStorageBackendFileSystemNetFindPoolSources,
607
    .refreshPool = virStorageBackendRefreshLocal,
608
    .stopPool = virStorageBackendFileSystemStop,
609
    .deletePool = virStorageBackendDeleteLocal,
610 611 612 613 614 615
    .buildVol = virStorageBackendVolBuildLocal,
    .buildVolFrom = virStorageBackendVolBuildFromLocal,
    .createVol = virStorageBackendVolCreateLocal,
    .refreshVol = virStorageBackendVolRefreshLocal,
    .deleteVol = virStorageBackendVolDeleteLocal,
    .resizeVol = virStorageBackendVolResizeLocal,
616 617
    .uploadVol = virStorageBackendVolUploadLocal,
    .downloadVol = virStorageBackendVolDownloadLocal,
618
    .wipeVol = virStorageBackendVolWipeLocal,
619
};
620
#endif /* WITH_STORAGE_FS */
621 622


623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638
int
virStorageBackendFsRegister(void)
{
    if (virStorageBackendRegister(&virStorageBackendDirectory) < 0)
        return -1;

#if WITH_STORAGE_FS
    if (virStorageBackendRegister(&virStorageBackendFileSystem) < 0)
        return -1;

    if (virStorageBackendRegister(&virStorageBackendNetFileSystem) < 0)
        return -1;
#endif /* WITH_STORAGE_FS */

    return 0;
}