1 /*
2  * storage_backend_gluster.c: storage backend for Gluster handling
3  *
4  * Copyright (C) 2013-2018 Red Hat, Inc.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library.  If not, see
18  * <http://www.gnu.org/licenses/>.
19  *
20  */
21 
22 #include <config.h>
23 
24 #include <glusterfs/api/glfs.h>
25 
26 #include "storage_backend_gluster.h"
27 #include "storage_conf.h"
28 #include "viralloc.h"
29 #include "virerror.h"
30 #include "virlog.h"
31 #include "virstring.h"
32 #include "viruri.h"
33 #include "storage_file_probe.h"
34 #include "storage_util.h"
35 #include "storage_source.h"
36 
37 #define VIR_FROM_THIS VIR_FROM_STORAGE
38 
39 VIR_LOG_INIT("storage.storage_backend_gluster");
40 
41 struct _virStorageBackendGlusterState {
42     glfs_t *vol;
43 
44     /* Accept the same URIs as qemu's block/gluster.c:
45      * gluster[+transport]://[server[:port]]/vol/[dir/]image[?socket=...] */
46     virURI *uri;
47 
48     char *volname; /* vol from URI, no '/' */
49     char *dir; /* dir from URI, or "/"; always starts and ends in '/' */
50 };
51 
52 typedef struct _virStorageBackendGlusterState virStorageBackendGlusterState;
53 
54 static void
virStorageBackendGlusterClose(virStorageBackendGlusterState * state)55 virStorageBackendGlusterClose(virStorageBackendGlusterState *state)
56 {
57     if (!state)
58         return;
59 
60     /* Yuck - glusterfs-api-3.4.1 appears to always return -1 for
61      * glfs_fini, with errno containing random data, so there's no way
62      * to tell if it succeeded. 3.4.2 is supposed to fix this.*/
63     if (state->vol && glfs_fini(state->vol) < 0)
64         VIR_DEBUG("shutdown of gluster volume %s failed with errno %d",
65                   state->volname, errno);
66 
67     virURIFree(state->uri);
68     VIR_FREE(state->volname);
69     VIR_FREE(state->dir);
70     VIR_FREE(state);
71 }
72 
73 static virStorageBackendGlusterState *
virStorageBackendGlusterOpen(virStoragePoolObj * pool)74 virStorageBackendGlusterOpen(virStoragePoolObj *pool)
75 {
76     virStorageBackendGlusterState *ret = NULL;
77     virStoragePoolDef *def = virStoragePoolObjGetDef(pool);
78     const char *name = def->source.name;
79     const char *dir = def->source.dir;
80     bool trailing_slash = true;
81 
82     /* Volume name must not contain '/'; optional path allows use of a
83      * subdirectory within the volume name.  */
84     if (strchr(name, '/')) {
85         virReportError(VIR_ERR_XML_ERROR,
86                        _("gluster pool name '%s' must not contain /"),
87                        name);
88         return NULL;
89     }
90     if (dir) {
91         if (*dir != '/') {
92             virReportError(VIR_ERR_XML_ERROR,
93                            _("gluster pool path '%s' must start with /"),
94                            dir);
95             return NULL;
96         }
97         if (strchr(dir, '\0')[-1] != '/')
98             trailing_slash = false;
99     }
100 
101     ret = g_new0(virStorageBackendGlusterState, 1);
102 
103     ret->volname = g_strdup(name);
104     ret->dir = g_strdup_printf("%s%s", dir ? dir : "/", trailing_slash ? "" : "/");
105 
106     /* FIXME: Currently hard-coded to tcp transport; XML needs to be
107      * extended to allow alternate transport */
108     ret->uri = g_new0(virURI, 1);
109     ret->uri->scheme = g_strdup("gluster");
110     ret->uri->server = g_strdup(def->source.hosts[0].name);
111     ret->uri->path = g_strdup_printf("/%s%s", ret->volname, ret->dir);
112     ret->uri->port = def->source.hosts[0].port;
113 
114     /* Actually connect to glfs */
115     if (!(ret->vol = glfs_new(ret->volname))) {
116         virReportError(VIR_ERR_OPERATION_FAILED,
117                        _("failed to create glfs object for '%s'"), ret->volname);
118         goto error;
119     }
120 
121     if (glfs_set_volfile_server(ret->vol, "tcp",
122                                 ret->uri->server, ret->uri->port) < 0 ||
123         glfs_init(ret->vol) < 0) {
124         g_autofree char *uri = NULL;
125         uri = virURIFormat(ret->uri);
126         virReportSystemError(errno, _("failed to connect to %s"), NULLSTR(uri));
127         goto error;
128     }
129 
130     if (glfs_chdir(ret->vol, ret->dir) < 0) {
131         virReportSystemError(errno,
132                              _("failed to change to directory '%s' in '%s'"),
133                              ret->dir, ret->volname);
134         goto error;
135     }
136 
137     return ret;
138 
139  error:
140     virStorageBackendGlusterClose(ret);
141     return NULL;
142 }
143 
144 
145 static ssize_t
virStorageBackendGlusterRead(glfs_fd_t * fd,const char * name,size_t len,char ** buf)146 virStorageBackendGlusterRead(glfs_fd_t *fd,
147                              const char *name,
148                              size_t len,
149                              char **buf)
150 {
151     char *s;
152     size_t nread = 0;
153 
154     *buf = g_new0(char, len);
155 
156     s = *buf;
157     while (len) {
158         ssize_t r = glfs_read(fd, s, len, 0);
159         if (r < 0 && errno == EINTR)
160             continue;
161         if (r < 0) {
162             VIR_FREE(*buf);
163             virReportSystemError(errno, _("unable to read '%s'"), name);
164             return r;
165         }
166         if (r == 0)
167             return nread;
168         s += r;
169         len -= r;
170         nread += r;
171     }
172     return nread;
173 }
174 
175 
176 static int
virStorageBackendGlusterSetMetadata(virStorageBackendGlusterState * state,virStorageVolDef * vol,const char * name)177 virStorageBackendGlusterSetMetadata(virStorageBackendGlusterState *state,
178                                     virStorageVolDef *vol,
179                                     const char *name)
180 {
181     char *tmp;
182     g_autofree char *path = NULL;
183 
184     VIR_FREE(vol->key);
185     VIR_FREE(vol->target.path);
186 
187     vol->type = VIR_STORAGE_VOL_NETWORK;
188     vol->target.format = VIR_STORAGE_FILE_RAW;
189 
190     if (name) {
191         VIR_FREE(vol->name);
192         vol->name = g_strdup(name);
193     }
194 
195     path = g_strdup_printf("%s%s%s", state->volname, state->dir, vol->name);
196 
197     tmp = state->uri->path;
198     state->uri->path = g_strdup_printf("/%s", path);
199     if (!(vol->target.path = virURIFormat(state->uri))) {
200         VIR_FREE(state->uri->path);
201         state->uri->path = tmp;
202         return -1;
203     }
204     VIR_FREE(state->uri->path);
205     state->uri->path = tmp;
206 
207     /* the path is unique enough to serve as a volume key */
208     vol->key = g_strdup(vol->target.path);
209 
210     return 0;
211 }
212 
213 
214 /* Populate *volptr for the given name and stat information, or leave
215  * it NULL if the entry should be skipped (such as ".").  Return 0 on
216  * success, -1 on failure. */
217 static int
virStorageBackendGlusterRefreshVol(virStorageBackendGlusterState * state,const char * name,struct stat * st,virStorageVolDef ** volptr)218 virStorageBackendGlusterRefreshVol(virStorageBackendGlusterState *state,
219                                    const char *name,
220                                    struct stat *st,
221                                    virStorageVolDef **volptr)
222 {
223     int ret = -1;
224     glfs_fd_t *fd = NULL;
225     ssize_t len;
226     g_autoptr(virStorageVolDef) vol = NULL;
227     g_autoptr(virStorageSource) meta = NULL;
228     g_autofree char *header = NULL;
229 
230     *volptr = NULL;
231 
232     /* Silently skip '.' and '..'.  */
233     if (STREQ(name, ".") || STREQ(name, ".."))
234         return 0;
235 
236     /* Follow symlinks; silently skip broken links and loops.  */
237     if (S_ISLNK(st->st_mode) && glfs_stat(state->vol, name, st) < 0) {
238         if (errno == ENOENT || errno == ELOOP) {
239             VIR_WARN("ignoring dangling symlink '%s'", name);
240             ret = 0;
241         } else {
242             virReportSystemError(errno, _("cannot stat '%s'"), name);
243         }
244         return ret;
245     }
246 
247     vol = g_new0(virStorageVolDef, 1);
248 
249     if (virStorageBackendUpdateVolTargetInfoFD(&vol->target, -1, st) < 0)
250         goto cleanup;
251 
252     if (virStorageBackendGlusterSetMetadata(state, vol, name) < 0)
253         goto cleanup;
254 
255     if (S_ISDIR(st->st_mode)) {
256         vol->type = VIR_STORAGE_VOL_NETDIR;
257         vol->target.format = VIR_STORAGE_FILE_DIR;
258         *volptr = g_steal_pointer(&vol);
259         ret = 0;
260         goto cleanup;
261     }
262 
263     /* No need to worry about O_NONBLOCK - gluster doesn't allow creation
264      * of fifos, so there's nothing it would protect us from. */
265     if (!(fd = glfs_open(state->vol, name, O_RDONLY | O_NOCTTY))) {
266         /* A dangling symlink now implies a TOCTTOU race; report it.  */
267         virReportSystemError(errno, _("cannot open volume '%s'"), name);
268         goto cleanup;
269     }
270 
271     if ((len = virStorageBackendGlusterRead(fd, name, VIR_STORAGE_MAX_HEADER,
272                                             &header)) < 0)
273         goto cleanup;
274 
275     if (!(meta = virStorageSourceGetMetadataFromBuf(name, header, len,
276                                                     VIR_STORAGE_FILE_AUTO)))
277         goto cleanup;
278 
279     if (meta->backingStoreRaw) {
280         vol->target.backingStore = virStorageSourceNew();
281         vol->target.backingStore->type = VIR_STORAGE_TYPE_NETWORK;
282         vol->target.backingStore->path = g_steal_pointer(&meta->backingStoreRaw);
283         vol->target.backingStore->format = meta->backingStoreRawFormat;
284 
285         if (vol->target.backingStore->format < 0)
286             vol->target.backingStore->format = VIR_STORAGE_FILE_RAW;
287     }
288 
289     vol->target.format = meta->format;
290     if (meta->capacity)
291         vol->target.capacity = meta->capacity;
292     if (meta->encryption) {
293         vol->target.encryption = meta->encryption;
294         meta->encryption = NULL;
295     }
296     vol->target.features = meta->features;
297     meta->features = NULL;
298     vol->target.compat = meta->compat;
299     meta->compat = NULL;
300 
301     *volptr = g_steal_pointer(&vol);
302     ret = 0;
303  cleanup:
304     if (fd)
305         glfs_close(fd);
306     return ret;
307 }
308 
309 static int
virStorageBackendGlusterRefreshPool(virStoragePoolObj * pool)310 virStorageBackendGlusterRefreshPool(virStoragePoolObj *pool)
311 {
312     int ret = -1;
313     virStoragePoolDef *def = virStoragePoolObjGetDef(pool);
314     virStorageBackendGlusterState *state = NULL;
315     struct {
316         struct dirent ent;
317         /* See comment below about readdir_r needing padding */
318         char padding[MAX(1, 256 - (int)(sizeof(struct dirent)
319                                          - offsetof(struct dirent, d_name)))];
320     } de;
321     struct dirent *ent;
322     glfs_fd_t *dir = NULL;
323     struct stat st;
324     struct statvfs sb;
325 
326     if (!(state = virStorageBackendGlusterOpen(pool)))
327         goto cleanup;
328 
329     /* Why oh why did glfs 3.4 decide to expose only readdir_r rather
330      * than readdir?  POSIX admits that readdir_r is inherently a
331      * flawed design, because systems are not required to define
332      * NAME_MAX: https://austingroupbugs.net/view.php?id=696
333      * https://womble.decadent.org.uk/readdir_r-advisory.html
334      *
335      * Fortunately, gluster appears to limit its underlying bricks to
336      * only use file systems such as XFS that have a NAME_MAX of 255;
337      * so we are currently guaranteed that if we provide 256 bytes of
338      * tail padding, then we should have enough space to avoid buffer
339      * overflow no matter whether the OS used d_name[], d_name[1], or
340      * d_name[256] in its 'struct dirent'.
341      * https://lists.gnu.org/archive/html/gluster-devel/2013-10/msg00083.html
342      */
343 
344     if (!(dir = glfs_opendir(state->vol, state->dir))) {
345         virReportSystemError(errno, _("cannot open path '%s' in '%s'"),
346                              state->dir, state->volname);
347         goto cleanup;
348     }
349     while (!(errno = glfs_readdirplus_r(dir, &st, &de.ent, &ent)) && ent) {
350         virStorageVolDef *vol;
351         int okay = virStorageBackendGlusterRefreshVol(state,
352                                                       ent->d_name, &st,
353                                                       &vol);
354 
355         if (okay < 0)
356             goto cleanup;
357         if (vol && virStoragePoolObjAddVol(pool, vol) < 0)
358             goto cleanup;
359     }
360     if (errno) {
361         virReportSystemError(errno, _("failed to read directory '%s' in '%s'"),
362                              state->dir, state->volname);
363         goto cleanup;
364     }
365 
366     if (glfs_statvfs(state->vol, state->dir, &sb) < 0) {
367         virReportSystemError(errno, _("cannot statvfs path '%s' in '%s'"),
368                              state->dir, state->volname);
369         goto cleanup;
370     }
371 
372     def->capacity = ((unsigned long long)sb.f_frsize *
373                      (unsigned long long)sb.f_blocks);
374     def->available = ((unsigned long long)sb.f_bfree *
375                       (unsigned long long)sb.f_frsize);
376     def->allocation = def->capacity - def->available;
377 
378     ret = 0;
379  cleanup:
380     if (dir)
381         glfs_closedir(dir);
382     virStorageBackendGlusterClose(state);
383     return ret;
384 }
385 
386 
387 static int
virStorageBackendGlusterVolDelete(virStoragePoolObj * pool,virStorageVolDef * vol,unsigned int flags)388 virStorageBackendGlusterVolDelete(virStoragePoolObj *pool,
389                                   virStorageVolDef *vol,
390                                   unsigned int flags)
391 {
392     virStorageBackendGlusterState *state = NULL;
393     int ret = -1;
394 
395     virCheckFlags(0, -1);
396 
397     switch ((virStorageVolType)vol->type) {
398     case VIR_STORAGE_VOL_FILE:
399     case VIR_STORAGE_VOL_DIR:
400     case VIR_STORAGE_VOL_BLOCK:
401     case VIR_STORAGE_VOL_PLOOP:
402     case VIR_STORAGE_VOL_LAST:
403         virReportError(VIR_ERR_NO_SUPPORT,
404                        _("removing of '%s' volumes is not supported "
405                          "by the gluster backend: %s"),
406                        virStorageVolTypeToString(vol->type),
407                        vol->target.path);
408         goto cleanup;
409         break;
410 
411     case VIR_STORAGE_VOL_NETWORK:
412         if (!(state = virStorageBackendGlusterOpen(pool)))
413             goto cleanup;
414 
415         if (glfs_unlink(state->vol, vol->name) < 0) {
416             if (errno != ENOENT) {
417                 virReportSystemError(errno,
418                                      _("cannot remove gluster volume file '%s'"),
419                                      vol->target.path);
420                 goto cleanup;
421             }
422         }
423         break;
424 
425     case VIR_STORAGE_VOL_NETDIR:
426         if (!(state = virStorageBackendGlusterOpen(pool)))
427             goto cleanup;
428 
429         if (glfs_rmdir(state->vol, vol->target.path) < 0) {
430             if (errno != ENOENT) {
431                 virReportSystemError(errno,
432                                      _("cannot remove gluster volume dir '%s'"),
433                                      vol->target.path);
434                 goto cleanup;
435             }
436         }
437         break;
438     }
439 
440     ret = 0;
441 
442  cleanup:
443     virStorageBackendGlusterClose(state);
444     return ret;
445 }
446 
447 
448 static char *
virStorageBackendGlusterFindPoolSources(const char * srcSpec,unsigned int flags)449 virStorageBackendGlusterFindPoolSources(const char *srcSpec,
450                                         unsigned int flags)
451 {
452     virStoragePoolSourceList list = { .type = VIR_STORAGE_POOL_GLUSTER,
453                                       .nsources = 0,
454                                       .sources = NULL
455                                     };
456     char *ret = NULL;
457     int rc;
458     size_t i;
459     g_autoptr(virStoragePoolSource) source = NULL;
460 
461     virCheckFlags(0, NULL);
462 
463     if (!srcSpec) {
464         virReportError(VIR_ERR_INVALID_ARG, "%s",
465                        _("hostname must be specified for gluster sources"));
466         return NULL;
467     }
468 
469     if (!(source = virStoragePoolDefParseSourceString(srcSpec,
470                                                       VIR_STORAGE_POOL_GLUSTER)))
471         return NULL;
472 
473     if (source->nhost != 1) {
474         virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
475                        _("Expected exactly 1 host for the storage pool"));
476         goto cleanup;
477     }
478 
479     if ((rc = virStorageBackendFindGlusterPoolSources(source->hosts[0].name,
480                                                       VIR_STORAGE_POOL_GLUSTER,
481                                                       &list, true)) < 0)
482         goto cleanup;
483 
484     if (rc == 0) {
485         virReportError(VIR_ERR_OPERATION_FAILED,
486                        _("no storage pools were found on host '%s'"),
487                        source->hosts[0].name);
488         goto cleanup;
489     }
490 
491     if (!(ret = virStoragePoolSourceListFormat(&list)))
492         goto cleanup;
493 
494  cleanup:
495     for (i = 0; i < list.nsources; i++)
496         virStoragePoolSourceClear(&list.sources[i]);
497     VIR_FREE(list.sources);
498 
499     return ret;
500 }
501 
502 
503 static int
virStorageBackendGlusterCheckPool(virStoragePoolObj * pool,bool * active)504 virStorageBackendGlusterCheckPool(virStoragePoolObj *pool,
505                                   bool *active)
506 {
507     /* Return previous state remembered by the status XML. If the pool is not
508      * available we will fail to refresh it and end up in the same situation.
509      * This will save one attempt to open the connection to the remote server */
510     *active = virStoragePoolObjIsActive(pool);
511     return 0;
512 }
513 
514 
515 virStorageBackend virStorageBackendGluster = {
516     .type = VIR_STORAGE_POOL_GLUSTER,
517 
518     .checkPool = virStorageBackendGlusterCheckPool,
519     .refreshPool = virStorageBackendGlusterRefreshPool,
520     .findPoolSources = virStorageBackendGlusterFindPoolSources,
521 
522     .deleteVol = virStorageBackendGlusterVolDelete,
523 };
524 
525 
526 int
virStorageBackendGlusterRegister(void)527 virStorageBackendGlusterRegister(void)
528 {
529     if (virStorageBackendRegister(&virStorageBackendGluster) < 0)
530         return -1;
531 
532     return 0;
533 }
534