storage_backend_gluster.c 24.3 KB
Newer Older
1 2 3
/*
 * storage_backend_gluster.c: storage backend for Gluster handling
 *
4
 * Copyright (C) 2013-2014 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
 *
 * 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, see
 * <http://www.gnu.org/licenses/>.
 *
 */

#include <config.h>

#include <glusterfs/api/glfs.h>

#include "storage_backend_gluster.h"
#include "storage_conf.h"
28 29 30 31 32 33
#include "viralloc.h"
#include "virerror.h"
#include "virlog.h"
#include "virstoragefile.h"
#include "virstring.h"
#include "viruri.h"
34
#include "storage_util.h"
35 36 37

#define VIR_FROM_THIS VIR_FROM_STORAGE

38 39
VIR_LOG_INIT("storage.storage_backend_gluster");

40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
struct _virStorageBackendGlusterState {
    glfs_t *vol;

    /* Accept the same URIs as qemu's block/gluster.c:
     * gluster[+transport]://[server[:port]]/vol/[dir/]image[?socket=...] */
    virURI *uri;

    char *volname; /* vol from URI, no '/' */
    char *dir; /* dir from URI, or "/"; always starts and ends in '/' */
};

typedef struct _virStorageBackendGlusterState virStorageBackendGlusterState;
typedef virStorageBackendGlusterState *virStorageBackendGlusterStatePtr;

static void
virStorageBackendGlusterClose(virStorageBackendGlusterStatePtr state)
{
    if (!state)
        return;

    /* Yuck - glusterfs-api-3.4.1 appears to always return -1 for
     * glfs_fini, with errno containing random data, so there's no way
     * to tell if it succeeded. 3.4.2 is supposed to fix this.*/
    if (state->vol && glfs_fini(state->vol) < 0)
        VIR_DEBUG("shutdown of gluster volume %s failed with errno %d",
                  state->volname, errno);

    virURIFree(state->uri);
    VIR_FREE(state->volname);
    VIR_FREE(state->dir);
    VIR_FREE(state);
}

static virStorageBackendGlusterStatePtr
virStorageBackendGlusterOpen(virStoragePoolObjPtr pool)
{
    virStorageBackendGlusterStatePtr ret = NULL;
    const char *name = pool->def->source.name;
    const char *dir = pool->def->source.dir;
    bool trailing_slash = true;

    /* Volume name must not contain '/'; optional path allows use of a
     * subdirectory within the volume name.  */
    if (strchr(name, '/')) {
        virReportError(VIR_ERR_XML_ERROR,
                       _("gluster pool name '%s' must not contain /"),
                       name);
        return NULL;
    }
    if (dir) {
        if (*dir != '/') {
            virReportError(VIR_ERR_XML_ERROR,
                           _("gluster pool path '%s' must start with /"),
                           dir);
            return NULL;
        }
        if (strchr(dir, '\0')[-1] != '/')
            trailing_slash = false;
    }

    if (VIR_ALLOC(ret) < 0)
        return NULL;

    if (VIR_STRDUP(ret->volname, name) < 0)
        goto error;
    if (virAsprintf(&ret->dir, "%s%s", dir ? dir : "/",
                    trailing_slash ? "" : "/") < 0)
        goto error;

    /* FIXME: Currently hard-coded to tcp transport; XML needs to be
     * extended to allow alternate transport */
    if (VIR_ALLOC(ret->uri) < 0)
        goto error;
    if (VIR_STRDUP(ret->uri->scheme, "gluster") < 0)
        goto error;
    if (VIR_STRDUP(ret->uri->server, pool->def->source.hosts[0].name) < 0)
        goto error;
    if (virAsprintf(&ret->uri->path, "/%s%s", ret->volname, ret->dir) < 0)
        goto error;
    ret->uri->port = pool->def->source.hosts[0].port;

    /* Actually connect to glfs */
    if (!(ret->vol = glfs_new(ret->volname))) {
        virReportOOMError();
        goto error;
    }

    if (glfs_set_volfile_server(ret->vol, "tcp",
                                ret->uri->server, ret->uri->port) < 0 ||
        glfs_init(ret->vol) < 0) {
        char *uri = virURIFormat(ret->uri);

        virReportSystemError(errno, _("failed to connect to %s"),
                             NULLSTR(uri));
        VIR_FREE(uri);
        goto error;
    }

    if (glfs_chdir(ret->vol, ret->dir) < 0) {
        virReportSystemError(errno,
                             _("failed to change to directory '%s' in '%s'"),
                             ret->dir, ret->volname);
        goto error;
    }

    return ret;

147
 error:
148 149 150 151 152
    virStorageBackendGlusterClose(ret);
    return NULL;
}


153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
static ssize_t
virStorageBackendGlusterReadHeader(glfs_fd_t *fd,
                                   const char *name,
                                   ssize_t maxlen,
                                   char **buf)
{
    char *s;
    size_t nread = 0;

    if (VIR_ALLOC_N(*buf, maxlen) < 0)
        return -1;

    s = *buf;
    while (maxlen) {
        ssize_t r = glfs_read(fd, s, maxlen, 0);
        if (r < 0 && errno == EINTR)
            continue;
        if (r < 0) {
            VIR_FREE(*buf);
            virReportSystemError(errno, _("unable to read '%s'"), name);
            return r;
        }
        if (r == 0)
            return nread;
177
        s += r;
178 179 180 181 182 183
        maxlen -= r;
        nread += r;
    }
    return nread;
}

184 185 186 187 188 189 190

static int
virStorageBackendGlusterSetMetadata(virStorageBackendGlusterStatePtr state,
                                    virStorageVolDefPtr vol,
                                    const char *name)
{
    int ret = -1;
191
    char *path = NULL;
192 193 194 195 196 197 198 199 200 201 202 203 204 205
    char *tmp;

    VIR_FREE(vol->key);
    VIR_FREE(vol->target.path);

    vol->type = VIR_STORAGE_VOL_NETWORK;
    vol->target.format = VIR_STORAGE_FILE_RAW;

    if (name) {
        VIR_FREE(vol->name);
        if (VIR_STRDUP(vol->name, name) < 0)
            goto cleanup;
    }

206
    if (virAsprintf(&path, "%s%s%s", state->volname, state->dir,
207 208 209 210
                    vol->name) < 0)
        goto cleanup;

    tmp = state->uri->path;
211
    if (virAsprintf(&state->uri->path, "/%s", path) < 0) {
212 213 214 215 216 217 218 219 220 221 222
        state->uri->path = tmp;
        goto cleanup;
    }
    if (!(vol->target.path = virURIFormat(state->uri))) {
        VIR_FREE(state->uri->path);
        state->uri->path = tmp;
        goto cleanup;
    }
    VIR_FREE(state->uri->path);
    state->uri->path = tmp;

223 224 225 226
    /* the path is unique enough to serve as a volume key */
    if (VIR_STRDUP(vol->key, vol->target.path) < 0)
        goto cleanup;

227 228
    ret = 0;

229
 cleanup:
230
    VIR_FREE(path);
231 232 233 234
    return ret;
}


235 236 237 238 239 240 241 242 243 244 245
/* Populate *volptr for the given name and stat information, or leave
 * it NULL if the entry should be skipped (such as ".").  Return 0 on
 * success, -1 on failure. */
static int
virStorageBackendGlusterRefreshVol(virStorageBackendGlusterStatePtr state,
                                   const char *name,
                                   struct stat *st,
                                   virStorageVolDefPtr *volptr)
{
    int ret = -1;
    virStorageVolDefPtr vol = NULL;
246
    glfs_fd_t *fd = NULL;
247
    virStorageSourcePtr meta = NULL;
248 249
    char *header = NULL;
    ssize_t len = VIR_STORAGE_MAX_HEADER;
250
    int backingFormat;
251 252 253 254 255 256 257

    *volptr = NULL;

    /* Silently skip '.' and '..'.  */
    if (STREQ(name, ".") || STREQ(name, ".."))
        return 0;

258 259 260 261 262 263 264 265 266 267 268
    /* Follow symlinks; silently skip broken links and loops.  */
    if (S_ISLNK(st->st_mode) && glfs_stat(state->vol, name, st) < 0) {
        if (errno == ENOENT || errno == ELOOP) {
            VIR_WARN("ignoring dangling symlink '%s'", name);
            ret = 0;
        } else {
            virReportSystemError(errno, _("cannot stat '%s'"), name);
        }
        return ret;
    }

269 270
    if (VIR_ALLOC(vol) < 0)
        goto cleanup;
271

272
    if (virStorageBackendUpdateVolTargetInfoFD(&vol->target, -1, st) < 0)
273 274
        goto cleanup;

275
    if (virStorageBackendGlusterSetMetadata(state, vol, name) < 0)
276
        goto cleanup;
277

278 279 280 281 282 283 284 285 286
    if (S_ISDIR(st->st_mode)) {
        vol->type = VIR_STORAGE_VOL_NETDIR;
        vol->target.format = VIR_STORAGE_FILE_DIR;
        *volptr = vol;
        vol = NULL;
        ret = 0;
        goto cleanup;
    }

287 288 289 290 291 292 293 294 295 296 297 298
    /* No need to worry about O_NONBLOCK - gluster doesn't allow creation
     * of fifos, so there's nothing it would protect us from. */
    if (!(fd = glfs_open(state->vol, name, O_RDONLY | O_NOCTTY))) {
        /* A dangling symlink now implies a TOCTTOU race; report it.  */
        virReportSystemError(errno, _("cannot open volume '%s'"), name);
        goto cleanup;
    }

    if ((len = virStorageBackendGlusterReadHeader(fd, name, len, &header)) < 0)
        goto cleanup;

    if (!(meta = virStorageFileGetMetadataFromBuf(name, header, len,
299
                                                  VIR_STORAGE_FILE_AUTO,
300
                                                  &backingFormat)))
301 302
        goto cleanup;

303 304 305 306 307 308 309 310 311 312 313 314
    if (meta->backingStoreRaw) {
        if (VIR_ALLOC(vol->target.backingStore) < 0)
            goto cleanup;

        vol->target.backingStore->path = meta->backingStoreRaw;

        if (backingFormat < 0)
            vol->target.backingStore->format = VIR_STORAGE_FILE_RAW;
        else
            vol->target.backingStore->format = backingFormat;
        meta->backingStoreRaw = NULL;
    }
315

J
Ján Tomko 已提交
316
    vol->target.format = meta->format;
317
    if (meta->capacity)
318
        vol->target.capacity = meta->capacity;
319 320 321
    if (meta->encryption) {
        vol->target.encryption = meta->encryption;
        meta->encryption = NULL;
322 323 324 325 326
    }
    vol->target.features = meta->features;
    meta->features = NULL;
    vol->target.compat = meta->compat;
    meta->compat = NULL;
327 328 329 330

    *volptr = vol;
    vol = NULL;
    ret = 0;
331
 cleanup:
332
    virStorageSourceFree(meta);
333
    virStorageVolDefFree(vol);
334 335 336
    if (fd)
        glfs_close(fd);
    VIR_FREE(header);
337 338
    return ret;
}
339 340 341

static int
virStorageBackendGlusterRefreshPool(virConnectPtr conn ATTRIBUTE_UNUSED,
342
                                    virStoragePoolObjPtr pool)
343
{
344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410
    int ret = -1;
    virStorageBackendGlusterStatePtr state = NULL;
    struct {
        struct dirent ent;
        /* See comment below about readdir_r needing padding */
        char padding[MAX(1, 256 - (int) (sizeof(struct dirent)
                                         - offsetof(struct dirent, d_name)))];
    } de;
    struct dirent *ent;
    glfs_fd_t *dir = NULL;
    struct stat st;
    struct statvfs sb;

    if (!(state = virStorageBackendGlusterOpen(pool)))
        goto cleanup;

    /* Why oh why did glfs 3.4 decide to expose only readdir_r rather
     * than readdir?  POSIX admits that readdir_r is inherently a
     * flawed design, because systems are not required to define
     * NAME_MAX: http://austingroupbugs.net/view.php?id=696
     * http://womble.decadent.org.uk/readdir_r-advisory.html
     *
     * Fortunately, gluster appears to limit its underlying bricks to
     * only use file systems such as XFS that have a NAME_MAX of 255;
     * so we are currently guaranteed that if we provide 256 bytes of
     * tail padding, then we should have enough space to avoid buffer
     * overflow no matter whether the OS used d_name[], d_name[1], or
     * d_name[256] in its 'struct dirent'.
     * http://lists.gnu.org/archive/html/gluster-devel/2013-10/msg00083.html
     */

    if (!(dir = glfs_opendir(state->vol, state->dir))) {
        virReportSystemError(errno, _("cannot open path '%s' in '%s'"),
                             state->dir, state->volname);
        goto cleanup;
    }
    while (!(errno = glfs_readdirplus_r(dir, &st, &de.ent, &ent)) && ent) {
        virStorageVolDefPtr vol;
        int okay = virStorageBackendGlusterRefreshVol(state,
                                                      ent->d_name, &st,
                                                      &vol);

        if (okay < 0)
            goto cleanup;
        if (vol && VIR_APPEND_ELEMENT(pool->volumes.objs, pool->volumes.count,
                                      vol) < 0)
            goto cleanup;
    }
    if (errno) {
        virReportSystemError(errno, _("failed to read directory '%s' in '%s'"),
                             state->dir, state->volname);
        goto cleanup;
    }

    if (glfs_statvfs(state->vol, state->dir, &sb) < 0) {
        virReportSystemError(errno, _("cannot statvfs path '%s' in '%s'"),
                             state->dir, state->volname);
        goto cleanup;
    }

    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_frsize);
    pool->def->allocation = pool->def->capacity - pool->def->available;

    ret = 0;
411
 cleanup:
412 413 414 415 416 417
    if (dir)
        glfs_closedir(dir);
    virStorageBackendGlusterClose(state);
    if (ret < 0)
        virStoragePoolObjClearVols(pool);
    return ret;
418 419
}

420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435

static int
virStorageBackendGlusterVolDelete(virConnectPtr conn ATTRIBUTE_UNUSED,
                                  virStoragePoolObjPtr pool,
                                  virStorageVolDefPtr vol,
                                  unsigned int flags)
{
    virStorageBackendGlusterStatePtr state = NULL;
    int ret = -1;

    virCheckFlags(0, -1);

    switch ((virStorageVolType) vol->type) {
    case VIR_STORAGE_VOL_FILE:
    case VIR_STORAGE_VOL_DIR:
    case VIR_STORAGE_VOL_BLOCK:
J
Jiri Denemark 已提交
436
    case VIR_STORAGE_VOL_PLOOP:
437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476
    case VIR_STORAGE_VOL_LAST:
        virReportError(VIR_ERR_NO_SUPPORT,
                       _("removing of '%s' volumes is not supported "
                         "by the gluster backend: %s"),
                       virStorageVolTypeToString(vol->type),
                       vol->target.path);
        goto cleanup;
        break;

    case VIR_STORAGE_VOL_NETWORK:
        if (!(state = virStorageBackendGlusterOpen(pool)))
            goto cleanup;

        if (glfs_unlink(state->vol, vol->name) < 0) {
            if (errno != ENOENT) {
                virReportSystemError(errno,
                                     _("cannot remove gluster volume file '%s'"),
                                     vol->target.path);
                goto cleanup;
            }
        }
        break;

    case VIR_STORAGE_VOL_NETDIR:
        if (!(state = virStorageBackendGlusterOpen(pool)))
            goto cleanup;

        if (glfs_rmdir(state->vol, vol->target.path) < 0) {
            if (errno != ENOENT) {
                virReportSystemError(errno,
                                     _("cannot remove gluster volume dir '%s'"),
                                     vol->target.path);
                goto cleanup;
            }
        }
        break;
    }

    ret = 0;

477
 cleanup:
478 479 480 481 482
    virStorageBackendGlusterClose(state);
    return ret;
}


483 484 485 486 487 488 489 490 491 492 493
static char *
virStorageBackendGlusterFindPoolSources(virConnectPtr conn ATTRIBUTE_UNUSED,
                                        const char *srcSpec,
                                        unsigned int flags)
{
    virStoragePoolSourceList list = { .type = VIR_STORAGE_POOL_GLUSTER,
                                      .nsources = 0,
                                      .sources = NULL
                                    };
    virStoragePoolSourcePtr source = NULL;
    char *ret = NULL;
494
    int rc;
495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514
    size_t i;

    virCheckFlags(0, NULL);

    if (!srcSpec) {
        virReportError(VIR_ERR_INVALID_ARG, "%s",
                       _("hostname must be specified for gluster sources"));
        return NULL;
    }

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

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

515 516
    if ((rc = virStorageBackendFindGlusterPoolSources(source->hosts[0].name,
                                                      0, /* currently ignored */
517
                                                      &list, true)) < 0)
518 519
        goto cleanup;

520 521 522 523 524 525 526
    if (rc == 0) {
        virReportError(VIR_ERR_OPERATION_FAILED,
                       _("no storage pools were found on host '%s'"),
                       source->hosts[0].name);
        goto cleanup;
    }

527 528 529 530 531 532 533 534 535 536 537 538 539
    if (!(ret = virStoragePoolSourceListFormat(&list)))
        goto cleanup;

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

    virStoragePoolSourceFree(source);
    return ret;
}


540 541 542 543
virStorageBackend virStorageBackendGluster = {
    .type = VIR_STORAGE_POOL_GLUSTER,

    .refreshPool = virStorageBackendGlusterRefreshPool,
544
    .findPoolSources = virStorageBackendGlusterFindPoolSources,
545 546

    .deleteVol = virStorageBackendGlusterVolDelete,
547
};
548 549 550 551 552 553 554


typedef struct _virStorageFileBackendGlusterPriv virStorageFileBackendGlusterPriv;
typedef virStorageFileBackendGlusterPriv *virStorageFileBackendGlusterPrivPtr;

struct _virStorageFileBackendGlusterPriv {
    glfs_t *vol;
555
    char *canonpath;
556 557 558 559
};


static void
560
virStorageFileBackendGlusterDeinit(virStorageSourcePtr src)
561
{
562
    virStorageFileBackendGlusterPrivPtr priv = src->drv->priv;
563

564 565 566 567
    VIR_DEBUG("deinitializing gluster storage file %p (gluster://%s:%s/%s%s)",
              src, src->hosts->name, src->hosts->port ? src->hosts->port : "0",
              src->volume, src->path);

568 569
    if (priv->vol)
        glfs_fini(priv->vol);
570
    VIR_FREE(priv->canonpath);
571 572

    VIR_FREE(priv);
573
    src->drv->priv = NULL;
574 575 576
}

static int
577 578
virStorageFileBackendGlusterInitServer(virStorageFileBackendGlusterPrivPtr priv,
                                       virStorageNetHostDefPtr host)
579
{
580 581
    const char *transport = virStorageNetHostTransportTypeToString(host->transport);
    const char *hoststr = NULL;
582 583
    int port = 0;

584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613
    switch ((virStorageNetHostTransport) host->transport) {
    case VIR_STORAGE_NET_HOST_TRANS_RDMA:
    case VIR_STORAGE_NET_HOST_TRANS_TCP:
        hoststr = host->name;

        if (host->port &&
            virStrToLong_i(host->port, NULL, 10, &port) < 0) {
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           _("failed to parse port number '%s'"),
                           host->port);
            return -1;
        }

        break;

    case VIR_STORAGE_NET_HOST_TRANS_UNIX:
        hoststr = host->socket;
        break;

    case VIR_STORAGE_NET_HOST_TRANS_LAST:
        break;
    }

    VIR_DEBUG("adding gluster host for %p: transport=%s host=%s port=%d",
              priv, transport, hoststr, port);

    if (glfs_set_volfile_server(priv->vol, transport, hoststr, port) < 0) {
        virReportSystemError(errno,
                             _("failed to set gluster volfile server '%s'"),
                             hoststr);
614 615 616
        return -1;
    }

617 618
    return 0;
}
619

620 621 622 623 624 625

static int
virStorageFileBackendGlusterInit(virStorageSourcePtr src)
{
    virStorageFileBackendGlusterPrivPtr priv = NULL;
    size_t i;
626

627
    if (!src->volume) {
628
        virReportError(VIR_ERR_INTERNAL_ERROR,
629 630
                       _("missing gluster volume name for path '%s'"),
                       src->path);
631
        return -1;
632 633
    }

634 635
    if (VIR_ALLOC(priv) < 0)
        return -1;
636

637 638 639 640
    VIR_DEBUG("initializing gluster storage file %p "
              "(priv='%p' volume='%s' path='%s') as [%u:%u]",
              src, priv, src->volume, src->path,
              (unsigned int)src->drv->uid, (unsigned int)src->drv->gid);
641

642
    if (!(priv->vol = glfs_new(src->volume))) {
643 644 645 646
        virReportOOMError();
        goto error;
    }

647 648 649
    for (i = 0; i < src->nhosts; i++) {
        if (virStorageFileBackendGlusterInitServer(priv, src->hosts + i) < 0)
            goto error;
650 651 652 653
    }

    if (glfs_init(priv->vol) < 0) {
        virReportSystemError(errno,
654 655
                             _("failed to initialize gluster connection "
                               "(src=%p priv=%p)"), src, priv);
656 657 658
        goto error;
    }

659
    src->drv->priv = priv;
660 661 662

    return 0;

663
 error:
664 665
    if (priv->vol)
        glfs_fini(priv->vol);
666
    VIR_FREE(priv);
667 668 669 670 671

    return -1;
}


672 673 674 675 676
static int
virStorageFileBackendGlusterCreate(virStorageSourcePtr src)
{
    virStorageFileBackendGlusterPrivPtr priv = src->drv->priv;
    glfs_fd_t *fd = NULL;
677
    mode_t mode = S_IRUSR;
678

679 680 681 682 683
    if (!src->readonly)
        mode |= S_IWUSR;

    if (!(fd = glfs_creat(priv->vol, src->path,
                          O_CREAT | O_TRUNC | O_WRONLY, mode)))
684 685 686 687 688 689 690
        return -1;

    ignore_value(glfs_close(fd));
    return 0;
}


691
static int
692
virStorageFileBackendGlusterUnlink(virStorageSourcePtr src)
693
{
694
    virStorageFileBackendGlusterPrivPtr priv = src->drv->priv;
695

696
    return glfs_unlink(priv->vol, src->path);
697 698 699 700
}


static int
701
virStorageFileBackendGlusterStat(virStorageSourcePtr src,
702 703
                                 struct stat *st)
{
704
    virStorageFileBackendGlusterPrivPtr priv = src->drv->priv;
705

706
    return glfs_stat(priv->vol, src->path, st);
707 708 709
}


710 711 712 713 714 715 716 717 718 719 720 721 722 723
static ssize_t
virStorageFileBackendGlusterReadHeader(virStorageSourcePtr src,
                                       ssize_t max_len,
                                       char **buf)
{
    virStorageFileBackendGlusterPrivPtr priv = src->drv->priv;
    glfs_fd_t *fd = NULL;
    ssize_t ret = -1;

    *buf = NULL;

    if (!(fd = glfs_open(priv->vol, src->path, O_RDONLY))) {
        virReportSystemError(errno, _("Failed to open file '%s'"),
                             src->path);
724
        return -1;
725 726
    }

727
    ret = virStorageBackendGlusterReadHeader(fd, src->path, max_len, buf);
728 729 730 731 732 733 734 735

    if (fd)
        glfs_close(fd);

    return ret;
}


736 737 738 739 740 741 742 743 744
static int
virStorageFileBackendGlusterAccess(virStorageSourcePtr src,
                                   int mode)
{
    virStorageFileBackendGlusterPrivPtr priv = src->drv->priv;

    return glfs_access(priv->vol, src->path, mode);
}

745 746
static int
virStorageFileBackendGlusterReadlinkCallback(const char *path,
747
                                             char **linkpath,
748 749 750 751 752 753 754 755
                                             void *data)
{
    virStorageFileBackendGlusterPrivPtr priv = data;
    char *buf = NULL;
    size_t bufsiz = 0;
    ssize_t ret;
    struct stat st;

756
    *linkpath = NULL;
757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783

    if (glfs_stat(priv->vol, path, &st) < 0) {
        virReportSystemError(errno,
                             _("failed to stat gluster path '%s'"),
                             path);
        return -1;
    }

    if (!S_ISLNK(st.st_mode))
        return 1;

 realloc:
    if (VIR_EXPAND_N(buf, bufsiz, 256) < 0)
        goto error;

    if ((ret = glfs_readlink(priv->vol, path, buf, bufsiz)) < 0) {
        virReportSystemError(errno,
                             _("failed to read link of gluster file '%s'"),
                             path);
        goto error;
    }

    if (ret == bufsiz)
        goto realloc;

    buf[ret] = '\0';

784
    *linkpath = buf;
785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818

    return 0;

 error:
    VIR_FREE(buf);
    return -1;
}


static const char *
virStorageFileBackendGlusterGetUniqueIdentifier(virStorageSourcePtr src)
{
    virStorageFileBackendGlusterPrivPtr priv = src->drv->priv;
    char *filePath = NULL;

    if (priv->canonpath)
        return priv->canonpath;

    if (!(filePath = virStorageFileCanonicalizePath(src->path,
                                                    virStorageFileBackendGlusterReadlinkCallback,
                                                    priv)))
        return NULL;

    ignore_value(virAsprintf(&priv->canonpath, "gluster://%s:%s/%s/%s",
                             src->hosts->name,
                             src->hosts->port,
                             src->volume,
                             filePath));

    VIR_FREE(filePath);

    return priv->canonpath;
}

819

820
static int
821
virStorageFileBackendGlusterChown(const virStorageSource *src,
822 823 824 825 826 827 828 829 830
                                  uid_t uid,
                                  gid_t gid)
{
    virStorageFileBackendGlusterPrivPtr priv = src->drv->priv;

    return glfs_chown(priv->vol, src->path, uid, gid);
}


831
virStorageFileBackend virStorageFileBackendGluster = {
E
Eric Blake 已提交
832
    .type = VIR_STORAGE_TYPE_NETWORK,
833
    .protocol = VIR_STORAGE_NET_PROTOCOL_GLUSTER,
834 835 836 837

    .backendInit = virStorageFileBackendGlusterInit,
    .backendDeinit = virStorageFileBackendGlusterDeinit,

838
    .storageFileCreate = virStorageFileBackendGlusterCreate,
839 840
    .storageFileUnlink = virStorageFileBackendGlusterUnlink,
    .storageFileStat = virStorageFileBackendGlusterStat,
841
    .storageFileReadHeader = virStorageFileBackendGlusterReadHeader,
842
    .storageFileAccess = virStorageFileBackendGlusterAccess,
843
    .storageFileChown = virStorageFileBackendGlusterChown,
844 845 846 847

    .storageFileGetUniqueIdentifier = virStorageFileBackendGlusterGetUniqueIdentifier,


848
};