Elliptics version update: 2.19.2.7
[elliptics.git] / example / file_backend.c
blob7dfc63314f74a37a816afb1c342c4f64894d3f6f
1 /*
2 * 2008+ Copyright (c) Evgeniy Polyakov <zbr@ioremap.net>
3 * All rights reserved.
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
16 #include <sys/types.h>
17 #include <sys/stat.h>
18 #include <sys/socket.h>
19 #include <sys/mman.h>
20 #include <sys/wait.h>
22 #include <errno.h>
23 #include <ctype.h>
24 #include <dirent.h>
25 #include <fcntl.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <unistd.h>
31 #include "elliptics/packet.h"
32 #include "elliptics/interface.h"
34 #include "backends.h"
35 #include "common.h"
37 #ifndef __unused
38 #define __unused __attribute__ ((unused))
39 #endif
41 struct file_backend_root
43 char *root;
44 int root_len;
45 int rootfd;
46 int sync;
47 int bit_num;
49 uint64_t records_in_blob;
50 uint64_t blob_size;
51 int defrag_percentage;
52 int defrag_timeout;
54 struct eblob_log log;
55 struct eblob_backend *meta;
58 static inline void file_backend_setup_file(struct file_backend_root *r, char *file,
59 unsigned int size, const unsigned char *id)
61 char dir[2*DNET_ID_SIZE+1];
62 char id_str[2*DNET_ID_SIZE+1];
64 file_backend_get_dir(id, r->bit_num, dir);
65 snprintf(file, size, "%s/%s", dir, dnet_dump_id_len_raw(id, DNET_ID_SIZE, id_str));
68 static inline uint64_t file_backend_get_dir_bits(const unsigned char *id, int bit_num)
70 #if 0
71 uint64_t res = *(uint64_t *)id;
73 bit_num = 64 - bit_num;
75 res <<= bit_num;
76 res >>= bit_num;
78 return res;
79 #else
80 char sub[DNET_ID_SIZE*2+1];
81 char *res = file_backend_get_dir(id, bit_num, sub);
82 char hex[DNET_ID_SIZE*2 + 1 + 2];
84 snprintf(hex, sizeof(hex), "0x%s", res);
85 return strtoull(hex, NULL, 16);
86 #endif
89 static void dnet_remove_file_if_empty_raw(char *file)
91 struct stat st;
92 int err;
94 err = stat(file, &st);
95 if (!err && !st.st_size)
96 remove(file);
99 static void dnet_remove_file_if_empty(struct file_backend_root *r, struct dnet_io_attr *io)
101 char file[DNET_ID_SIZE * 2 + 8 + 8 + 2];
103 file_backend_setup_file(r, file, sizeof(file), io->id);
104 dnet_remove_file_if_empty_raw(file);
107 static int file_write_raw(struct file_backend_root *r, struct dnet_io_attr *io)
109 /* null byte + maximum directory length (32 bits in hex) + '/' directory prefix */
110 char file[DNET_ID_SIZE * 2 + 8 + 8 + 2];
111 int oflags = O_RDWR | O_CREAT | O_LARGEFILE | O_CLOEXEC;
112 void *data = io + 1;
113 int fd;
114 ssize_t err;
116 file_backend_setup_file(r, file, sizeof(file), io->id);
118 if (io->flags & DNET_IO_FLAGS_APPEND)
119 oflags |= O_APPEND;
120 else if (!io->offset)
121 oflags |= O_TRUNC;
123 fd = open(file, oflags, 0644);
124 if (fd < 0) {
125 err = -errno;
126 dnet_backend_log(DNET_LOG_ERROR, "%s: FILE: %s: OPEN: %zd: %s.\n",
127 dnet_dump_id_str(io->id), file, err, strerror(-err));
128 goto err_out_exit;
131 err = pwrite(fd, data, io->size, io->offset);
132 if (err != (ssize_t)io->size) {
133 err = -errno;
134 dnet_backend_log(DNET_LOG_ERROR, "%s: FILE: %s: WRITE: %zd: offset: %llu, size: %llu: %s.\n",
135 dnet_dump_id_str(io->id), file, err,
136 (unsigned long long)io->offset, (unsigned long long)io->size,
137 strerror(-err));
138 goto err_out_close;
141 if (!r->sync)
142 fsync(fd);
144 return fd;
146 err_out_close:
147 dnet_remove_file_if_empty_raw(file);
148 close(fd);
149 err_out_exit:
150 return err;
153 static int file_write(struct file_backend_root *r, void *state __unused, struct dnet_cmd *cmd, void *data)
155 int err, fd;
156 char dir[2*DNET_ID_SIZE+1];
157 struct dnet_io_attr *io = data;
159 dnet_convert_io_attr(io);
161 data += sizeof(struct dnet_io_attr);
163 file_backend_get_dir(io->id, r->bit_num, dir);
165 err = mkdir(dir, 0755);
166 if (err < 0) {
167 if (errno != EEXIST) {
168 err = -errno;
169 dnet_backend_log(DNET_LOG_ERROR, "%s: FILE: %s: dir-create: %d: %s.\n",
170 dnet_dump_id(&cmd->id), dir, err, strerror(-err));
171 goto err_out_exit;
175 err = file_write_raw(r, io);
176 if (err < 0)
177 goto err_out_check_remove;
179 fd = err;
181 dnet_backend_log(DNET_LOG_INFO, "%s: FILE: %s: WRITE: Ok: offset: %llu, size: %llu.\n",
182 dnet_dump_id(&cmd->id), dir, (unsigned long long)io->offset, (unsigned long long)io->size);
183 err = dnet_send_file_info(state, cmd, fd, 0, -1);
184 if (err)
185 goto err_out_close;
187 close(fd);
189 return 0;
191 err_out_close:
192 close(fd);
193 err_out_check_remove:
194 dnet_remove_file_if_empty(r, io);
195 err_out_exit:
196 return err;
199 static int file_read(struct file_backend_root *r, void *state, struct dnet_cmd *cmd, void *data)
201 struct dnet_io_attr *io = data;
202 int fd, err;
203 ssize_t size;
204 char file[DNET_ID_SIZE * 2 + 8 + 8 + 2];
205 struct stat st;
207 data += sizeof(struct dnet_io_attr);
209 dnet_convert_io_attr(io);
211 file_backend_setup_file(r, file, sizeof(file), io->id);
213 fd = open(file, O_RDONLY | O_CLOEXEC, 0644);
214 if (fd < 0) {
215 err = -errno;
216 dnet_backend_log(DNET_LOG_ERROR, "%s: FILE: %s: READ: %d: %s.\n",
217 dnet_dump_id(&cmd->id), file, err, strerror(-err));
218 goto err_out_exit;
221 size = io->size;
223 err = fstat(fd, &st);
224 if (err) {
225 err = -errno;
226 dnet_backend_log(DNET_LOG_ERROR, "%s: FILE: %s: read-stat: %d: %s.\n",
227 dnet_dump_id(&cmd->id), file, err, strerror(-err));
228 goto err_out_close_fd;
231 size = dnet_backend_check_get_size(io, st.st_size);
232 if (size <= 0) {
233 err = size;
234 goto err_out_close_fd;
237 io->size = size;
238 err = dnet_send_read_data(state, cmd, io, NULL, fd, io->offset, 1);
239 if (err)
240 goto err_out_close_fd;
241 return 0;
243 err_out_close_fd:
244 close(fd);
245 err_out_exit:
246 return err;
249 static int file_del(struct file_backend_root *r, void *state __unused, struct dnet_cmd *cmd, void *data __unused)
251 char file[DNET_ID_SIZE * 2 + 2*DNET_ID_SIZE + 2]; /* file + dir + suffix + slash + 0-byte */
252 char dir[2*DNET_ID_SIZE+1];
253 char id[2*DNET_ID_SIZE+1];
255 file_backend_get_dir(cmd->id.id, r->bit_num, dir);
257 snprintf(file, sizeof(file), "%s/%s",
258 dir, dnet_dump_id_len_raw(cmd->id.id, DNET_ID_SIZE, id));
259 remove(file);
261 return 0;
264 static int file_info(struct file_backend_root *r, void *state, struct dnet_cmd *cmd)
266 char file[DNET_ID_SIZE * 2 + 2*DNET_ID_SIZE + 2]; /* file + dir + suffix + slash + 0-byte */
267 char dir[2*DNET_ID_SIZE+1];
268 char id[2*DNET_ID_SIZE+1];
269 int fd, err;
271 file_backend_get_dir(cmd->id.id, r->bit_num, dir);
273 snprintf(file, sizeof(file), "%s/%s",
274 dir, dnet_dump_id_len_raw(cmd->id.id, DNET_ID_SIZE, id));
276 err = open(file, O_RDONLY | O_CLOEXEC);
277 if (err < 0) {
278 err = -errno;
279 dnet_backend_log(DNET_LOG_ERROR, "%s: FILE: %s: info-stat-open-csum: %d: %s.\n",
280 dnet_dump_id(&cmd->id), file, err, strerror(-err));
281 goto err_out_exit;
283 fd = err;
285 err = dnet_send_file_info(state, cmd, fd, 0, -1);
286 if (err)
287 goto err_out_close;
289 err = 0;
291 err_out_close:
292 close(fd);
293 err_out_exit:
294 return err;
297 static int file_bulk_read(struct file_backend_root *r, void *state, struct dnet_cmd *cmd, void *data)
299 int err = -1, ret;
300 struct dnet_io_attr *io = data;
301 struct dnet_io_attr *ios = io+1;
302 uint64_t count = 0;
303 uint64_t i;
305 dnet_convert_io_attr(io);
306 count = io->size / sizeof(struct dnet_io_attr);
308 for (i = 0; i < count; i++) {
309 ret = file_read(r, state, cmd, &ios[i]);
310 if (!ret)
311 err = 0;
312 else if (err == -1)
313 err = ret;
316 return err;
318 static int file_backend_command_handler(void *state, void *priv, struct dnet_cmd *cmd,void *data)
320 int err;
321 struct file_backend_root *r = priv;
323 switch (cmd->cmd) {
324 case DNET_CMD_LOOKUP:
325 err = file_info(r, state, cmd);
326 break;
327 case DNET_CMD_WRITE:
328 err = file_write(r, state, cmd, data);
329 break;
330 case DNET_CMD_READ:
331 err = file_read(r, state, cmd, data);
332 break;
333 case DNET_CMD_STAT:
334 err = backend_stat(state, r->root, cmd);
335 break;
336 case DNET_CMD_DEL:
337 err = file_del(r, state, cmd, data);
338 break;
339 case DNET_CMD_BULK_READ:
340 err = file_bulk_read(r, state, cmd, data);
341 break;
342 case DNET_CMD_READ_RANGE:
343 err = -ENOTSUP;
344 break;
345 default:
346 err = -EINVAL;
347 break;
350 return err;
353 static int dnet_file_set_bit_number(struct dnet_config_backend *b, char *key __unused, char *value)
355 struct file_backend_root *r = b->data;
357 r->bit_num = ALIGN(atoi(value), 4);
358 return 0;
361 static int dnet_file_set_records_in_blob(struct dnet_config_backend *b, char *key __unused, char *value)
363 struct file_backend_root *r = b->data;
365 r->records_in_blob = (unsigned int)strtoull(value, NULL, 0);
366 return 0;
369 static int dnet_file_set_blob_size(struct dnet_config_backend *b, char *key __unused, char *value)
371 struct file_backend_root *r = b->data;
372 uint64_t val = strtoul(value, NULL, 0);
374 if (strchr(value, 'T'))
375 val *= 1024*1024*1024*1024ULL;
376 else if (strchr(value, 'G'))
377 val *= 1024*1024*1024ULL;
378 else if (strchr(value, 'M'))
379 val *= 1024*1024;
380 else if (strchr(value, 'K'))
381 val *= 1024;
383 r->blob_size = val;
384 return 0;
387 static int dnet_file_set_defrag_timeout(struct dnet_config_backend *b, char *key __unused, char *value)
389 struct file_backend_root *r = b->data;
391 r->defrag_timeout = strtoul(value, NULL, 0);
392 return 0;
395 static int dnet_file_set_defrag_percentage(struct dnet_config_backend *b, char *key __unused, char *value)
397 struct file_backend_root *r = b->data;
399 r->defrag_percentage = strtoul(value, NULL, 0);
400 return 0;
403 static int dnet_file_set_sync(struct dnet_config_backend *b, char *key __unused, char *value)
405 struct file_backend_root *r = b->data;
407 r->sync = atoi(value);
408 return 0;
411 static int dnet_file_set_root(struct dnet_config_backend *b, char *key __unused, char *root)
413 struct file_backend_root *r = b->data;
414 int err;
416 err = backend_storage_size(b, root);
417 if (err)
418 goto err_out_exit;
420 r->root = strdup(root);
421 if (!r->root) {
422 err = -ENOMEM;
423 goto err_out_exit;
426 r->rootfd = open(r->root, O_RDONLY | O_CLOEXEC);
427 if (r->rootfd < 0) {
428 err = -errno;
429 dnet_backend_log(DNET_LOG_ERROR, "Failed to open root '%s': %s.\n", root, strerror(-err));
430 goto err_out_free;
432 r->root_len = strlen(r->root);
434 err = fchdir(r->rootfd);
435 if (err) {
436 err = -errno;
437 dnet_backend_log(DNET_LOG_ERROR, "Failed to change current dir to root '%s' directory: %s.\n",
438 root, strerror(-err));
439 goto err_out_close;
442 return 0;
444 err_out_close:
445 close(r->rootfd);
446 r->rootfd = -1;
447 err_out_free:
448 free(r->root);
449 r->root = NULL;
450 err_out_exit:
451 return err;
454 static int file_backend_send(void *state, void *priv, struct dnet_id *id)
456 struct dnet_node *n = dnet_get_node_from_state(state);
457 struct file_backend_root *r = priv;
458 char file[DNET_ID_SIZE * 2 + 2*DNET_ID_SIZE + 2]; /* file + dir + suffix + slash + 0-byte */
459 int err = -ENOENT;
461 file_backend_setup_file(r, file, sizeof(file), id->id);
463 if (!access(file, R_OK)) {
464 struct dnet_session *s = dnet_session_create(n);
465 dnet_session_set_groups(s, (int *)&id->group_id, 1);
466 err = dnet_write_file_id(s, file, id, 0, 0, 0, 0, 0);
467 if (err < 0) {
468 goto err_out_exit;
472 err_out_exit:
473 return err;
476 int file_backend_storage_stat(void *priv, struct dnet_stat *st)
478 int err;
479 struct file_backend_root *r = priv;
481 memset(st, 0, sizeof(struct dnet_stat));
483 err = backend_stat_low_level(r->root?r->root:".", st);
484 if (err)
485 return err;
487 return 0;
490 static void dnet_file_db_cleanup(struct file_backend_root *r)
492 eblob_cleanup(r->meta);
495 static int dnet_file_db_init(struct file_backend_root *r, struct dnet_config *c, const char *path)
497 static char meta_path[300];
498 struct eblob_config ecfg;
499 int err = 0;
501 snprintf(meta_path, sizeof(meta_path), "%s/meta", path);
503 memset(&ecfg, 0, sizeof(ecfg));
504 ecfg.file = meta_path;
505 ecfg.sync = r->sync;
506 ecfg.blob_flags = EBLOB_TRY_OVERWRITE | EBLOB_OVERWRITE_COMMITS | EBLOB_NO_FREE_SPACE_CHECK;
507 ecfg.records_in_blob = r->records_in_blob;
508 ecfg.blob_size = r->blob_size;
509 ecfg.defrag_percentage = r->defrag_percentage;
510 ecfg.defrag_timeout = r->defrag_timeout;
511 ecfg.log = (struct eblob_log *)c->log;
513 r->meta = eblob_init(&ecfg);
514 if (!r->meta) {
515 err = -EINVAL;
516 dnet_backend_log(DNET_LOG_ERROR, "Failed to initialize metadata eblob\n");
519 return err;
522 static void file_backend_cleanup(void *priv)
524 struct file_backend_root *r = priv;
526 dnet_file_db_cleanup(r);
527 close(r->rootfd);
528 free(r->root);
531 static ssize_t dnet_file_db_read(void *priv, struct dnet_raw_id *id, void **datap)
533 struct file_backend_root *r = priv;
534 return dnet_db_read_raw(r->meta, id, datap);
537 static int dnet_file_db_write(void *priv, struct dnet_raw_id *id, void *data, size_t size)
539 struct file_backend_root *r = priv;
540 return dnet_db_write_raw(r->meta, id, data, size);
543 static int dnet_file_db_remove(void *priv, struct dnet_raw_id *id, int real_del)
545 struct file_backend_root *r = priv;
546 return dnet_db_remove_raw(r->meta, id, real_del);
549 static long long dnet_file_db_total_elements(void *priv)
551 struct file_backend_root *r = priv;
552 return eblob_total_elements(r->meta);
555 static int dnet_file_db_iterate(struct dnet_iterate_ctl *ctl)
557 struct file_backend_root *r = ctl->iterate_private;
558 return dnet_db_iterate(r->meta, ctl);
561 static int file_backend_checksum(struct dnet_node *n, void *priv, struct dnet_id *id, void *csum, int *csize)
563 struct file_backend_root *r = priv;
564 char file[DNET_ID_SIZE * 2 + 2*DNET_ID_SIZE + 2];
565 /* file + dir + suffix + slash + 0-byte */
567 file_backend_setup_file(r, file, sizeof(file), id->id);
568 return dnet_checksum_file(n, csum, csize, file, 0, 0);
571 static int dnet_file_config_init(struct dnet_config_backend *b, struct dnet_config *c)
573 struct file_backend_root *r = b->data;
574 int err;
576 c->cb = &b->cb;
578 b->cb.command_private = r;
580 b->cb.command_handler = file_backend_command_handler;
581 b->cb.send = file_backend_send;
582 b->cb.checksum = file_backend_checksum;
584 c->storage_size = b->storage_size;
585 c->storage_free = b->storage_free;
587 b->cb.storage_stat = file_backend_storage_stat;
588 b->cb.backend_cleanup = file_backend_cleanup;
590 b->cb.meta_read = dnet_file_db_read;
591 b->cb.meta_write = dnet_file_db_write;
592 b->cb.meta_remove = dnet_file_db_remove;
593 b->cb.meta_total_elements = dnet_file_db_total_elements;
594 b->cb.meta_iterate = dnet_file_db_iterate;
596 mkdir("history", 0755);
597 err = dnet_file_db_init(r, c, "history");
598 if (err)
599 return err;
601 return 0;
604 static void dnet_file_config_cleanup(struct dnet_config_backend *b)
606 struct file_backend_root *r = b->data;
608 file_backend_cleanup(r);
611 static struct dnet_config_entry dnet_cfg_entries_filesystem[] = {
612 {"directory_bit_number", dnet_file_set_bit_number},
613 {"sync", dnet_file_set_sync},
614 {"root", dnet_file_set_root},
615 {"records_in_blob", dnet_file_set_records_in_blob},
616 {"blob_size", dnet_file_set_blob_size},
617 {"defrag_timeout", dnet_file_set_defrag_timeout},
618 {"defrag_percentage", dnet_file_set_defrag_percentage},
621 static struct dnet_config_backend dnet_file_backend = {
622 .name = "filesystem",
623 .ent = dnet_cfg_entries_filesystem,
624 .num = ARRAY_SIZE(dnet_cfg_entries_filesystem),
625 .size = sizeof(struct file_backend_root),
626 .init = dnet_file_config_init,
627 .cleanup = dnet_file_config_cleanup,
630 int dnet_file_backend_init(void)
632 return dnet_backend_register(&dnet_file_backend);
635 void dnet_file_backend_exit(void)
637 /* cleanup routing will be called explicitly through backend->cleanup() callback */