/* * storage_file_gluster.c: storage file backend for Gluster handling * * Copyright (C) 2013-2018 Red Hat, Inc. * * 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 * . * */ #include #include #include "storage_file_gluster.h" #include "viralloc.h" #include "virerror.h" #include "virlog.h" #include "virstoragefilebackend.h" #include "virstring.h" #define VIR_FROM_THIS VIR_FROM_STORAGE VIR_LOG_INIT("storage.storage_file_gluster"); typedef struct _virStorageFileBackendGlusterPriv virStorageFileBackendGlusterPriv; typedef virStorageFileBackendGlusterPriv *virStorageFileBackendGlusterPrivPtr; struct _virStorageFileBackendGlusterPriv { glfs_t *vol; char *canonpath; }; static void virStorageFileBackendGlusterDeinit(virStorageSourcePtr src) { virStorageFileBackendGlusterPrivPtr priv = src->drv->priv; VIR_DEBUG("deinitializing gluster storage file %p (gluster://%s:%u/%s%s)", src, src->hosts->name, src->hosts->port, src->volume, src->path); if (priv->vol) glfs_fini(priv->vol); VIR_FREE(priv->canonpath); VIR_FREE(priv); src->drv->priv = NULL; } static int virStorageFileBackendGlusterInitServer(virStorageFileBackendGlusterPrivPtr priv, virStorageNetHostDefPtr host) { const char *transport = virStorageNetHostTransportTypeToString(host->transport); const char *hoststr = NULL; int port = 0; switch ((virStorageNetHostTransport) host->transport) { case VIR_STORAGE_NET_HOST_TRANS_RDMA: case VIR_STORAGE_NET_HOST_TRANS_TCP: hoststr = host->name; port = host->port; 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); return -1; } return 0; } static int virStorageFileBackendGlusterInit(virStorageSourcePtr src) { virStorageFileBackendGlusterPrivPtr priv = NULL; size_t i; if (!src->volume) { virReportError(VIR_ERR_INTERNAL_ERROR, _("missing gluster volume name for path '%s'"), src->path); return -1; } if (VIR_ALLOC(priv) < 0) return -1; 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); if (!(priv->vol = glfs_new(src->volume))) { virReportOOMError(); goto error; } for (i = 0; i < src->nhosts; i++) { if (virStorageFileBackendGlusterInitServer(priv, src->hosts + i) < 0) goto error; } if (glfs_init(priv->vol) < 0) { virReportSystemError(errno, _("failed to initialize gluster connection " "(src=%p priv=%p)"), src, priv); goto error; } src->drv->priv = priv; return 0; error: if (priv->vol) glfs_fini(priv->vol); VIR_FREE(priv); return -1; } static int virStorageFileBackendGlusterCreate(virStorageSourcePtr src) { virStorageFileBackendGlusterPrivPtr priv = src->drv->priv; glfs_fd_t *fd = NULL; mode_t mode = S_IRUSR; if (!src->readonly) mode |= S_IWUSR; if (!(fd = glfs_creat(priv->vol, src->path, O_CREAT | O_TRUNC | O_WRONLY, mode))) return -1; ignore_value(glfs_close(fd)); return 0; } static int virStorageFileBackendGlusterUnlink(virStorageSourcePtr src) { virStorageFileBackendGlusterPrivPtr priv = src->drv->priv; return glfs_unlink(priv->vol, src->path); } static int virStorageFileBackendGlusterStat(virStorageSourcePtr src, struct stat *st) { virStorageFileBackendGlusterPrivPtr priv = src->drv->priv; return glfs_stat(priv->vol, src->path, st); } static ssize_t virStorageFileBackendGlusterRead(virStorageSourcePtr src, size_t offset, size_t len, char **buf) { virStorageFileBackendGlusterPrivPtr priv = src->drv->priv; glfs_fd_t *fd = NULL; ssize_t ret = -1; char *s; size_t nread = 0; *buf = NULL; if (!(fd = glfs_open(priv->vol, src->path, O_RDONLY))) { virReportSystemError(errno, _("Failed to open file '%s'"), src->path); return -1; } if (offset > 0) { if (glfs_lseek(fd, offset, SEEK_SET) == (off_t) -1) { virReportSystemError(errno, _("cannot seek into '%s'"), src->path); goto cleanup; } } if (VIR_ALLOC_N(*buf, len) < 0) return -1; s = *buf; while (len) { ssize_t r = glfs_read(fd, s, len, 0); if (r < 0 && errno == EINTR) continue; if (r < 0) { VIR_FREE(*buf); virReportSystemError(errno, _("unable to read '%s'"), src->path); return r; } if (r == 0) return nread; s += r; len -= r; nread += r; } ret = nread; cleanup: if (fd) glfs_close(fd); return ret; } static int virStorageFileBackendGlusterAccess(virStorageSourcePtr src, int mode) { virStorageFileBackendGlusterPrivPtr priv = src->drv->priv; return glfs_access(priv->vol, src->path, mode); } static int virStorageFileBackendGlusterReadlinkCallback(const char *path, char **linkpath, void *data) { virStorageFileBackendGlusterPrivPtr priv = data; char *buf = NULL; size_t bufsiz = 0; ssize_t ret; struct stat st; *linkpath = NULL; 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'; *linkpath = buf; 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:%u/%s/%s", src->hosts->name, src->hosts->port, src->volume, filePath)); VIR_FREE(filePath); return priv->canonpath; } static int virStorageFileBackendGlusterChown(const virStorageSource *src, uid_t uid, gid_t gid) { virStorageFileBackendGlusterPrivPtr priv = src->drv->priv; return glfs_chown(priv->vol, src->path, uid, gid); } virStorageFileBackend virStorageFileBackendGluster = { .type = VIR_STORAGE_TYPE_NETWORK, .protocol = VIR_STORAGE_NET_PROTOCOL_GLUSTER, .backendInit = virStorageFileBackendGlusterInit, .backendDeinit = virStorageFileBackendGlusterDeinit, .storageFileCreate = virStorageFileBackendGlusterCreate, .storageFileUnlink = virStorageFileBackendGlusterUnlink, .storageFileStat = virStorageFileBackendGlusterStat, .storageFileRead = virStorageFileBackendGlusterRead, .storageFileAccess = virStorageFileBackendGlusterAccess, .storageFileChown = virStorageFileBackendGlusterChown, .storageFileGetUniqueIdentifier = virStorageFileBackendGlusterGetUniqueIdentifier, }; int virStorageFileGlusterRegister(void) { if (virStorageFileBackendRegister(&virStorageFileBackendGluster) < 0) return -1; return 0; }