2 * 2008+ Copyright (c) Evgeniy Polyakov <zbr@ioremap.net>
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>
18 #include <sys/socket.h>
31 #include "elliptics/packet.h"
32 #include "elliptics/interface.h"
38 #define __unused __attribute__ ((unused))
41 struct file_backend_root
49 uint64_t records_in_blob
;
51 int defrag_percentage
;
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
)
71 uint64_t res
= *(uint64_t *)id
;
73 bit_num
= 64 - bit_num
;
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);
89 static void dnet_remove_file_if_empty_raw(char *file
)
94 err
= stat(file
, &st
);
95 if (!err
&& !st
.st_size
)
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
;
116 file_backend_setup_file(r
, file
, sizeof(file
), io
->id
);
118 if (io
->flags
& DNET_IO_FLAGS_APPEND
)
120 else if (!io
->offset
)
123 fd
= open(file
, oflags
, 0644);
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
));
131 err
= pwrite(fd
, data
, io
->size
, io
->offset
);
132 if (err
!= (ssize_t
)io
->size
) {
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
,
147 dnet_remove_file_if_empty_raw(file
);
153 static int file_write(struct file_backend_root
*r
, void *state __unused
, struct dnet_cmd
*cmd
, void *data
)
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);
167 if (errno
!= EEXIST
) {
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
));
175 err
= file_write_raw(r
, io
);
177 goto err_out_check_remove
;
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);
193 err_out_check_remove
:
194 dnet_remove_file_if_empty(r
, io
);
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
;
204 char file
[DNET_ID_SIZE
* 2 + 8 + 8 + 2];
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);
216 dnet_backend_log(DNET_LOG_ERROR
, "%s: FILE: %s: READ: %d: %s.\n",
217 dnet_dump_id(&cmd
->id
), file
, err
, strerror(-err
));
223 err
= fstat(fd
, &st
);
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
);
234 goto err_out_close_fd
;
238 err
= dnet_send_read_data(state
, cmd
, io
, NULL
, fd
, io
->offset
, 1);
240 goto err_out_close_fd
;
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
));
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];
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
);
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
));
285 err
= dnet_send_file_info(state
, cmd
, fd
, 0, -1);
297 static int file_bulk_read(struct file_backend_root
*r
, void *state
, struct dnet_cmd
*cmd
, void *data
)
300 struct dnet_io_attr
*io
= data
;
301 struct dnet_io_attr
*ios
= io
+1;
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
]);
318 static int file_backend_command_handler(void *state
, void *priv
, struct dnet_cmd
*cmd
,void *data
)
321 struct file_backend_root
*r
= priv
;
324 case DNET_CMD_LOOKUP
:
325 err
= file_info(r
, state
, cmd
);
328 err
= file_write(r
, state
, cmd
, data
);
331 err
= file_read(r
, state
, cmd
, data
);
334 err
= backend_stat(state
, r
->root
, cmd
);
337 err
= file_del(r
, state
, cmd
, data
);
339 case DNET_CMD_BULK_READ
:
340 err
= file_bulk_read(r
, state
, cmd
, data
);
342 case DNET_CMD_READ_RANGE
:
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);
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);
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'))
380 else if (strchr(value
, 'K'))
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);
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);
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
);
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
;
416 err
= backend_storage_size(b
, root
);
420 r
->root
= strdup(root
);
426 r
->rootfd
= open(r
->root
, O_RDONLY
| O_CLOEXEC
);
429 dnet_backend_log(DNET_LOG_ERROR
, "Failed to open root '%s': %s.\n", root
, strerror(-err
));
432 r
->root_len
= strlen(r
->root
);
434 err
= fchdir(r
->rootfd
);
437 dnet_backend_log(DNET_LOG_ERROR
, "Failed to change current dir to root '%s' directory: %s.\n",
438 root
, strerror(-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 */
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);
474 int file_backend_storage_stat(void *priv
, struct dnet_stat
*st
)
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
);
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
;
499 snprintf(meta_path
, sizeof(meta_path
), "%s/meta", path
);
501 memset(&ecfg
, 0, sizeof(ecfg
));
502 ecfg
.file
= meta_path
;
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
);
513 dnet_backend_log(DNET_LOG_ERROR
, "Failed to initialize metadata eblob\n");
519 static void file_backend_cleanup(void *priv
)
521 struct file_backend_root
*r
= priv
;
523 dnet_file_db_cleanup(r
);
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
;
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");
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 */