]> git.proxmox.com Git - pve-cluster.git/blob - data/src/cfs-plug-memdb.c
901de7c3bb63d03e3d756c0247219a1ee40423de
[pve-cluster.git] / data / src / cfs-plug-memdb.c
1 /*
2 Copyright (C) 2010 - 2020 Proxmox Server Solutions GmbH
3
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU Affero General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU Affero General Public License for more details.
13
14 You should have received a copy of the GNU Affero General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>.
16
17 Author: Dietmar Maurer <dietmar@proxmox.com>
18
19 */
20
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif /* HAVE_CONFIG_H */
24
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <unistd.h>
29 #include <glib.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <sys/file.h>
33 #include <fcntl.h>
34 #include <errno.h>
35 #include <dirent.h>
36 #include <arpa/inet.h>
37
38 #include "cfs-utils.h"
39 #include "cfs-plug-memdb.h"
40 #include "dcdb.h"
41 #include "status.h"
42
43 static struct cfs_operations cfs_ops;
44
45 static gboolean
46 name_is_openvz_script(
47 const char *name,
48 guint32 *vmid_ret)
49 {
50 if (!name)
51 return FALSE;
52
53 guint32 vmid = 0;
54 char *end = NULL;
55
56 if (name[0] == 'v' && name[1] == 'p' && name[2] == 's') {
57 end = (char *)name + 3;
58 } else {
59 if (name[0] < '1' || name[0] > '9')
60 return FALSE;
61
62 vmid = strtoul(name, &end, 10);
63 }
64
65 if (!end || end[0] != '.')
66 return FALSE;
67
68 end++;
69
70 gboolean res = FALSE;
71
72 if (end[0] == 'm' && strcmp(end, "mount") == 0)
73 res = TRUE;
74
75 if (end[0] == 'u' && strcmp(end, "umount") == 0)
76 res = TRUE;
77
78 if (end[0] == 's' &&
79 (strcmp(end, "start") == 0 || strcmp(end, "stop") == 0))
80 res = TRUE;
81
82 if (end[0] == 'p' &&
83 (strcmp(end, "premount") == 0 || strcmp(end, "postumount") == 0))
84 res = TRUE;
85
86
87 if (res && vmid_ret)
88 *vmid_ret = vmid;
89
90 return res;
91 }
92
93 static void tree_entry_stat(memdb_tree_entry_t *te, struct stat *stbuf, gboolean quorate)
94 {
95 g_return_if_fail(te != NULL);
96 g_return_if_fail(stbuf != NULL);
97
98 if (te->type == DT_DIR) {
99 stbuf->st_mode = S_IFDIR | (quorate ? 0777 : 0555);
100 stbuf->st_nlink = 2;
101 } else {
102 stbuf->st_mode = S_IFREG | (quorate ? 0666 : 0444);
103 stbuf->st_nlink = 1;
104 if (name_is_openvz_script(te->name, NULL)) {
105 stbuf->st_mode |= S_IXUSR;
106 }
107 }
108
109 stbuf->st_size = te->size;
110 stbuf->st_blocks =
111 (stbuf->st_size + MEMDB_BLOCKSIZE -1)/MEMDB_BLOCKSIZE;
112 stbuf->st_atime = te->mtime;
113 stbuf->st_mtime = te->mtime;
114 stbuf->st_ctime = te->mtime;
115 }
116
117 static int cfs_plug_memdb_getattr(cfs_plug_t *plug, const char *path, struct stat *stbuf)
118 {
119 g_return_val_if_fail(plug != NULL, PARAM_CHECK_ERRNO);
120 g_return_val_if_fail(plug->ops == &cfs_ops, PARAM_CHECK_ERRNO);
121 g_return_val_if_fail(path != NULL, PARAM_CHECK_ERRNO);
122 g_return_val_if_fail(stbuf != NULL, PARAM_CHECK_ERRNO);
123
124 memset(stbuf, 0, sizeof(struct stat));
125
126 cfs_plug_memdb_t *mdb = (cfs_plug_memdb_t *)plug;
127
128 memdb_tree_entry_t *te = memdb_getattr(mdb->memdb, path);
129
130 if (te) {
131 tree_entry_stat(te, stbuf, cfs_is_quorate());
132 memdb_tree_entry_free(te);
133 return 0;
134 }
135
136 return -ENOENT;
137 }
138
139 static int cfs_plug_memdb_readdir(
140 cfs_plug_t *plug,
141 const char *path,
142 void *buf,
143 fuse_fill_dir_t filler,
144 off_t offset,
145 struct fuse_file_info *fi)
146 {
147 (void) offset;
148 (void) fi;
149
150 g_return_val_if_fail(plug != NULL, PARAM_CHECK_ERRNO);
151 g_return_val_if_fail(plug->ops == &cfs_ops, PARAM_CHECK_ERRNO);
152 g_return_val_if_fail(path != NULL, PARAM_CHECK_ERRNO);
153 g_return_val_if_fail(buf != NULL, PARAM_CHECK_ERRNO);
154 g_return_val_if_fail(filler != NULL, PARAM_CHECK_ERRNO);
155
156 cfs_plug_memdb_t *mdb = (cfs_plug_memdb_t *)plug;
157
158 GList *dirlist = memdb_readdir(mdb->memdb, path);
159
160 if (dirlist) {
161
162 filler(buf, ".", NULL, 0);
163 filler(buf, "..", NULL, 0);
164
165 struct stat stbuf;
166 memset(&stbuf, 0, sizeof(struct stat));
167
168 GList *l = dirlist;
169 while (l) {
170 memdb_tree_entry_t *te = (memdb_tree_entry_t *)l->data;
171
172 tree_entry_stat(te, &stbuf, 0);
173 filler(buf, te->name, &stbuf, 0);
174 l = g_list_next(l);
175 }
176
177 memdb_dirlist_free(dirlist);
178 }
179
180 return 0;
181 }
182
183 static int cfs_plug_memdb_open(cfs_plug_t *plug, const char *path, struct fuse_file_info *fi)
184 {
185 g_return_val_if_fail(plug != NULL, PARAM_CHECK_ERRNO);
186 g_return_val_if_fail(plug->ops == &cfs_ops, PARAM_CHECK_ERRNO);
187 g_return_val_if_fail(path != NULL, PARAM_CHECK_ERRNO);
188 g_return_val_if_fail(fi != NULL, PARAM_CHECK_ERRNO);
189
190 memdb_tree_entry_t *te;
191
192 cfs_plug_memdb_t *mdb = (cfs_plug_memdb_t *)plug;
193
194 if ((te = memdb_getattr(mdb->memdb, path))) {
195 memdb_tree_entry_free(te);
196 } else
197 return -ENOENT;
198
199 return 0;
200 }
201
202 static int cfs_plug_memdb_read(
203 cfs_plug_t *plug,
204 const char *path,
205 char *buf,
206 size_t size,
207 off_t offset,
208 struct fuse_file_info *fi)
209 {
210 (void) fi;
211
212 g_return_val_if_fail(plug != NULL, PARAM_CHECK_ERRNO);
213 g_return_val_if_fail(plug->ops == &cfs_ops, PARAM_CHECK_ERRNO);
214 g_return_val_if_fail(path != NULL, PARAM_CHECK_ERRNO);
215 g_return_val_if_fail(buf != NULL, PARAM_CHECK_ERRNO);
216
217 int len;
218 gpointer data = NULL;
219
220 cfs_plug_memdb_t *mdb = (cfs_plug_memdb_t *)plug;
221
222 len = memdb_read(mdb->memdb, path, &data);
223 if (len < 0)
224 return len;
225
226 if (offset < len) {
227 if (offset + size > len)
228 size = len - offset;
229 memcpy(buf, (uint8_t *) data + offset, size);
230 } else {
231 size = 0;
232 }
233
234 if (data)
235 g_free(data);
236
237 return size;
238 }
239
240 static int cfs_plug_memdb_write(
241 cfs_plug_t *plug,
242 const char *path,
243 const char *buf,
244 size_t size,
245 off_t offset,
246 struct fuse_file_info *fi)
247 {
248 (void) fi;
249
250 g_return_val_if_fail(plug != NULL, PARAM_CHECK_ERRNO);
251 g_return_val_if_fail(plug->ops == &cfs_ops, PARAM_CHECK_ERRNO);
252 g_return_val_if_fail(path != NULL, PARAM_CHECK_ERRNO);
253 g_return_val_if_fail(buf != NULL, PARAM_CHECK_ERRNO);
254
255 int res;
256
257 cfs_plug_memdb_t *mdb = (cfs_plug_memdb_t *)plug;
258
259 if (mdb->dfsm) {
260 res = dcdb_send_fuse_message(mdb->dfsm, DCDB_MESSAGE_CFS_WRITE, path, NULL, buf,
261 size, offset, 0);
262 } else {
263 uint32_t ctime = time(NULL);
264 res = memdb_write(mdb->memdb, path, 0, ctime, buf, size, offset, 0);
265 }
266
267 return res;
268 }
269
270 static int cfs_plug_memdb_truncate(cfs_plug_t *plug, const char *path, off_t size)
271 {
272 g_return_val_if_fail(plug != NULL, PARAM_CHECK_ERRNO);
273 g_return_val_if_fail(plug->ops == &cfs_ops, PARAM_CHECK_ERRNO);
274 g_return_val_if_fail(path != NULL, PARAM_CHECK_ERRNO);
275
276 int res;
277
278 cfs_plug_memdb_t *mdb = (cfs_plug_memdb_t *)plug;
279
280 if (mdb->dfsm) {
281 res = dcdb_send_fuse_message(mdb->dfsm, DCDB_MESSAGE_CFS_WRITE, path, NULL, NULL,
282 0, size, 1);
283 } else {
284 uint32_t ctime = time(NULL);
285 res = memdb_write(mdb->memdb, path, 0, ctime, NULL, 0, size, 1);
286 }
287
288 return res;
289 }
290
291 static int cfs_plug_memdb_create (
292 cfs_plug_t *plug,
293 const char *path,
294 mode_t mode,
295 struct fuse_file_info *fi)
296 {
297 g_return_val_if_fail(plug != NULL, PARAM_CHECK_ERRNO);
298 g_return_val_if_fail(plug->ops == &cfs_ops, PARAM_CHECK_ERRNO);
299 g_return_val_if_fail(path != NULL, PARAM_CHECK_ERRNO);
300 g_return_val_if_fail(fi != NULL, PARAM_CHECK_ERRNO);
301
302 int res;
303
304 cfs_plug_memdb_t *mdb = (cfs_plug_memdb_t *)plug;
305
306 if (mdb->dfsm) {
307 res = dcdb_send_fuse_message(mdb->dfsm, DCDB_MESSAGE_CFS_CREATE, path,
308 NULL, NULL, 0, 0, 0);
309 } else {
310 uint32_t ctime = time(NULL);
311
312 res = memdb_create(mdb->memdb, path, 0, ctime);
313 }
314
315 return res;
316 }
317
318 static int cfs_plug_memdb_mkdir(cfs_plug_t *plug, const char *path, mode_t mode)
319 {
320 g_return_val_if_fail(plug != NULL, PARAM_CHECK_ERRNO);
321 g_return_val_if_fail(plug->ops == &cfs_ops, PARAM_CHECK_ERRNO);
322 g_return_val_if_fail(path != NULL, PARAM_CHECK_ERRNO);
323
324 int res;
325
326 cfs_plug_memdb_t *mdb = (cfs_plug_memdb_t *)plug;
327
328 if (mdb->dfsm) {
329 res = dcdb_send_fuse_message(mdb->dfsm, DCDB_MESSAGE_CFS_MKDIR, path,
330 NULL, NULL, 0, 0, 0);
331 } else {
332 uint32_t ctime = time(NULL);
333 res = memdb_mkdir(mdb->memdb, path, 0, ctime);
334 }
335
336 return res;
337 }
338
339 static int cfs_plug_memdb_rmdir(cfs_plug_t *plug, const char *path)
340 {
341 g_return_val_if_fail(plug != NULL, PARAM_CHECK_ERRNO);
342 g_return_val_if_fail(plug->ops == &cfs_ops, PARAM_CHECK_ERRNO);
343 g_return_val_if_fail(path != NULL, PARAM_CHECK_ERRNO);
344
345 int res;
346
347 cfs_plug_memdb_t *mdb = (cfs_plug_memdb_t *)plug;
348
349 if (mdb->dfsm) {
350 res = dcdb_send_fuse_message(mdb->dfsm, DCDB_MESSAGE_CFS_DELETE, path,
351 NULL, NULL, 0, 0, 0);
352 } else {
353 uint32_t ctime = time(NULL);
354 res = memdb_delete(mdb->memdb, path, 0, ctime);
355 }
356
357 return res;
358 }
359
360 static int cfs_plug_memdb_rename(cfs_plug_t *plug, const char *from, const char *to)
361 {
362 g_return_val_if_fail(plug != NULL, PARAM_CHECK_ERRNO);
363 g_return_val_if_fail(plug->ops == &cfs_ops, PARAM_CHECK_ERRNO);
364 g_return_val_if_fail(from != NULL, PARAM_CHECK_ERRNO);
365 g_return_val_if_fail(to != NULL, PARAM_CHECK_ERRNO);
366
367 int res;
368
369 cfs_plug_memdb_t *mdb = (cfs_plug_memdb_t *)plug;
370
371 if (mdb->dfsm) {
372 res = dcdb_send_fuse_message(mdb->dfsm, DCDB_MESSAGE_CFS_RENAME, from, to,
373 NULL, 0, 0, 0);
374 } else {
375 uint32_t ctime = time(NULL);
376 res = memdb_rename(mdb->memdb, from, to, 0, ctime);
377 }
378
379 return res;
380 }
381
382 static int cfs_plug_memdb_unlink(cfs_plug_t *plug, const char *path)
383 {
384 g_return_val_if_fail(plug != NULL, PARAM_CHECK_ERRNO);
385 g_return_val_if_fail(plug->ops == &cfs_ops, PARAM_CHECK_ERRNO);
386 g_return_val_if_fail(path != NULL, PARAM_CHECK_ERRNO);
387
388 int res;
389
390 cfs_plug_memdb_t *mdb = (cfs_plug_memdb_t *)plug;
391
392 if (mdb->dfsm) {
393 res = dcdb_send_fuse_message(mdb->dfsm, DCDB_MESSAGE_CFS_DELETE, path,
394 NULL, NULL, 0, 0, 0);
395 } else {
396 uint32_t ctime = time(NULL);
397 res = memdb_delete(mdb->memdb, path, 0, ctime);
398 }
399
400 return res;
401 }
402
403 static int cfs_plug_memdb_utimens(
404 cfs_plug_t *plug,
405 const char *path,
406 const struct timespec tv[2])
407 {
408 g_return_val_if_fail(plug != NULL, PARAM_CHECK_ERRNO);
409 g_return_val_if_fail(plug->ops == &cfs_ops, PARAM_CHECK_ERRNO);
410 g_return_val_if_fail(path != NULL, PARAM_CHECK_ERRNO);
411 g_return_val_if_fail(tv != NULL, PARAM_CHECK_ERRNO);
412
413 int res;
414
415 cfs_plug_memdb_t *mdb = (cfs_plug_memdb_t *)plug;
416
417 res = -EIO;
418
419 memdb_tree_entry_t *te = memdb_getattr(mdb->memdb, path);
420 uint32_t mtime = tv[1].tv_sec;
421
422 gboolean unlock_req = FALSE;
423 guchar csum[32];
424
425 if (te && mtime == 0 && te->type == DT_DIR &&
426 path_is_lockdir(path)) {
427 unlock_req = TRUE;
428 }
429
430 if (mdb->dfsm) {
431 if (unlock_req && memdb_tree_entry_csum(te, csum))
432 dcdb_send_unlock(mdb->dfsm, path, csum, TRUE);
433
434 res = dcdb_send_fuse_message(mdb->dfsm, DCDB_MESSAGE_CFS_MTIME, path,
435 NULL, NULL, 0, mtime, 0);
436 } else {
437 uint32_t ctime = time(NULL);
438 if (unlock_req && memdb_tree_entry_csum(te, csum) &&
439 memdb_lock_expired(mdb->memdb, path, csum)) {
440 res = memdb_delete(mdb->memdb, path, 0, ctime);
441 } else {
442 res = memdb_mtime(mdb->memdb, path, 0, mtime);
443 }
444 }
445
446 memdb_tree_entry_free(te);
447
448 return res;
449 }
450
451 static int cfs_plug_memdb_statfs(cfs_plug_t *plug, const char *path, struct statvfs *stbuf)
452 {
453 g_return_val_if_fail(plug != NULL, PARAM_CHECK_ERRNO);
454 g_return_val_if_fail(plug->ops == &cfs_ops, PARAM_CHECK_ERRNO);
455 g_return_val_if_fail(path != NULL, PARAM_CHECK_ERRNO);
456 g_return_val_if_fail(stbuf != NULL, PARAM_CHECK_ERRNO);
457
458 cfs_plug_memdb_t *mdb = (cfs_plug_memdb_t *)plug;
459
460 return memdb_statfs(mdb->memdb, stbuf);
461 }
462
463 static void cfs_plug_memdb_destroy(cfs_plug_t *plug)
464 {
465 g_return_if_fail(plug != NULL);
466 g_return_if_fail(plug->ops == &cfs_ops);
467
468 g_free(plug->name);
469
470 g_free(plug);
471 }
472
473 static cfs_plug_t *cfs_plug_memdb_lookup_plug(cfs_plug_t *plug, char **path)
474 {
475 g_return_val_if_fail(plug != NULL, NULL);
476 g_return_val_if_fail(plug->ops == &cfs_ops, NULL);
477
478 return plug;
479 }
480
481 static struct cfs_operations cfs_ops = {
482 .getattr = cfs_plug_memdb_getattr,
483 .readdir = cfs_plug_memdb_readdir,
484 .open = cfs_plug_memdb_open,
485 .create = cfs_plug_memdb_create,
486 .read = cfs_plug_memdb_read,
487 .write = cfs_plug_memdb_write,
488 .truncate = cfs_plug_memdb_truncate,
489 .unlink = cfs_plug_memdb_unlink,
490 .mkdir = cfs_plug_memdb_mkdir,
491 .rmdir = cfs_plug_memdb_rmdir,
492 .rename = cfs_plug_memdb_rename,
493 .utimens = cfs_plug_memdb_utimens,
494 .statfs = cfs_plug_memdb_statfs,
495 #ifdef HAS_CFS_PLUG_MEMDB_LOCK
496 .lock = cfs_plug_memdb_lock,
497 #endif
498 };
499
500 cfs_plug_memdb_t *cfs_plug_memdb_new(
501 const char *name,
502 memdb_t *memdb,
503 dfsm_t *dfsm)
504 {
505 g_return_val_if_fail(name != NULL, NULL);
506 g_return_val_if_fail(memdb != NULL, NULL);
507
508 cfs_plug_memdb_t *mdb = g_new0(cfs_plug_memdb_t, 1);
509
510 g_return_val_if_fail(mdb != NULL, NULL);
511
512 mdb->plug.ops = &cfs_ops;
513
514 mdb->plug.lookup_plug = cfs_plug_memdb_lookup_plug;
515
516 mdb->plug.destroy_plug = cfs_plug_memdb_destroy;
517
518 mdb->plug.name = g_strdup(name);
519
520 mdb->memdb = memdb;
521
522 mdb->dfsm = dfsm;
523
524 return mdb;
525 }