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