Added startup stack size check
[elliptics.git] / example / file_backend.c
blobf1c96e6421eda48fc88e8d3abaaa31e2302c54f8
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 err = dnet_write_file_id(n, file, id, 0, 0, 0, 0, 0);
465 if (err < 0) {
466 goto err_out_exit;
470 err_out_exit:
471 return err;
474 int file_backend_storage_stat(void *priv, struct dnet_stat *st)
476 int err;
477 struct file_backend_root *r = priv;
479 memset(st, 0, sizeof(struct dnet_stat));
481 err = backend_stat_low_level(r->root?r->root:".", st);
482 if (err)
483 return err;
485 return 0;
488 static void dnet_file_db_cleanup(struct file_backend_root *r)
490 eblob_cleanup(r->meta);
493 static int dnet_file_db_init(struct file_backend_root *r, struct dnet_config *c, const char *path)
495 static char meta_path[300];
496 struct eblob_config ecfg;
497 int err = 0;
499 snprintf(meta_path, sizeof(meta_path), "%s/meta", path);
501 memset(&ecfg, 0, sizeof(ecfg));
502 ecfg.file = meta_path;
503 ecfg.sync = r->sync;
504 ecfg.records_in_blob = r->records_in_blob;
505 ecfg.blob_size = r->blob_size;
506 ecfg.defrag_percentage = r->defrag_percentage;
507 ecfg.defrag_timeout = r->defrag_timeout;
508 ecfg.log = (struct eblob_log *)c->log;
510 r->meta = eblob_init(&ecfg);
511 if (!r->meta) {
512 err = -EINVAL;
513 dnet_backend_log(DNET_LOG_ERROR, "Failed to initialize metadata eblob\n");
516 return err;
519 static void file_backend_cleanup(void *priv)
521 struct file_backend_root *r = priv;
523 dnet_file_db_cleanup(r);
524 close(r->rootfd);
525 free(r->root);
528 static ssize_t dnet_file_db_read(void *priv, struct dnet_raw_id *id, void **datap)
530 struct file_backend_root *r = priv;
531 return dnet_db_read_raw(r->meta, id, datap);
534 static int dnet_file_db_write(void *priv, struct dnet_raw_id *id, void *data, size_t size)
536 struct file_backend_root *r = priv;
537 return dnet_db_write_raw(r->meta, id, data, size);
540 static int dnet_file_db_remove(void *priv, struct dnet_raw_id *id, int real_del)
542 struct file_backend_root *r = priv;
543 return dnet_db_remove_raw(r->meta, id, real_del);
546 static long long dnet_file_db_total_elements(void *priv)
548 struct file_backend_root *r = priv;
549 return eblob_total_elements(r->meta);
552 static int dnet_file_db_iterate(struct dnet_iterate_ctl *ctl)
554 struct file_backend_root *r = ctl->iterate_private;
555 return dnet_db_iterate(r->meta, ctl);
558 static int dnet_file_config_init(struct dnet_config_backend *b, struct dnet_config *c)
560 struct file_backend_root *r = b->data;
561 int err;
563 c->cb = &b->cb;
565 b->cb.command_private = r;
567 b->cb.command_handler = file_backend_command_handler;
568 b->cb.send = file_backend_send;
570 c->storage_size = b->storage_size;
571 c->storage_free = b->storage_free;
573 b->cb.storage_stat = file_backend_storage_stat;
574 b->cb.backend_cleanup = file_backend_cleanup;
576 b->cb.meta_read = dnet_file_db_read;
577 b->cb.meta_write = dnet_file_db_write;
578 b->cb.meta_remove = dnet_file_db_remove;
579 b->cb.meta_total_elements = dnet_file_db_total_elements;
580 b->cb.meta_iterate = dnet_file_db_iterate;
582 mkdir("history", 0755);
583 err = dnet_file_db_init(r, c, "history");
584 if (err)
585 return err;
587 return 0;
590 static void dnet_file_config_cleanup(struct dnet_config_backend *b)
592 struct file_backend_root *r = b->data;
594 file_backend_cleanup(r);
597 static struct dnet_config_entry dnet_cfg_entries_filesystem[] = {
598 {"directory_bit_number", dnet_file_set_bit_number},
599 {"sync", dnet_file_set_sync},
600 {"root", dnet_file_set_root},
601 {"records_in_blob", dnet_file_set_records_in_blob},
602 {"blob_size", dnet_file_set_blob_size},
603 {"defrag_timeout", dnet_file_set_defrag_timeout},
604 {"defrag_percentage", dnet_file_set_defrag_percentage},
607 static struct dnet_config_backend dnet_file_backend = {
608 .name = "filesystem",
609 .ent = dnet_cfg_entries_filesystem,
610 .num = ARRAY_SIZE(dnet_cfg_entries_filesystem),
611 .size = sizeof(struct file_backend_root),
612 .init = dnet_file_config_init,
613 .cleanup = dnet_file_config_cleanup,
616 int dnet_file_backend_init(void)
618 return dnet_backend_register(&dnet_file_backend);
621 void dnet_file_backend_exit(void)
623 /* cleanup routing will be called explicitly through backend->cleanup() callback */