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