static int leveldb_backend_read(struct leveldb_backend *s, void *state, struct dnet_cmd *cmd, void *iodata, int last) { struct dnet_io_attr *io = iodata; struct dnet_ext_list elist; char *data; size_t data_size; int err = -EINVAL; char *error_string = NULL; dnet_ext_list_init(&elist); dnet_convert_io_attr(io); if (io->size || io->offset) { err = -ERANGE; goto err_out_exit; } data = leveldb_get(s->db, s->roptions, (const char *)io->id, DNET_ID_SIZE, &data_size, &error_string); if (error_string || !data) { if (!data) err = -ENOENT; goto err_out_exit; } /* Extract original data and extension list from &data */ err = dnet_ext_list_extract((void *)&data, (uint64_t *)&data_size, &elist, DNET_EXT_FREE_ON_DESTROY); if (err != 0) goto err_out_exit; dnet_ext_list_to_io(&elist, io); io->size = data_size; if (data_size && data && last) cmd->flags &= ~DNET_FLAGS_NEED_ACK; err = dnet_send_read_data(state, cmd, io, data, -1, io->offset, 0); if (err < 0) goto err_out_exit; dnet_backend_log(DNET_LOG_NOTICE, "%s: leveldb: : READ: Ok: size: %llu.\n", dnet_dump_id(&cmd->id), (unsigned long long)io->size); err_out_exit: dnet_ext_list_destroy(&elist); if (err < 0) dnet_backend_log(DNET_LOG_ERROR, "%s: leveldb: READ: error: %s: %d\n", dnet_dump_id(&cmd->id), error_string, err); free(error_string); return err; }
static int leveldb_backend_lookup(struct leveldb_backend *s, void *state, struct dnet_cmd *cmd) { char *data = NULL; size_t data_size; int err = -EINVAL; char *error_string = NULL; struct dnet_ext_list elist; static const size_t ehdr_size = sizeof(struct dnet_ext_list_hdr); struct dnet_ext_list_hdr *ehdr = NULL; dnet_ext_list_init(&elist); data = leveldb_get(s->db, s->roptions, (const char *)cmd->id.id, DNET_ID_SIZE, &data_size, &error_string); if (error_string || !data) { if (!data) err = -ENOENT; goto err_out_exit; } if (data_size < ehdr_size) { err = -ERANGE; goto err_out_exit; } ehdr = (struct dnet_ext_list_hdr *)data; dnet_ext_hdr_to_list(ehdr, &elist); data_size -= ehdr_size; data += ehdr_size; err = dnet_send_file_info_ts_without_fd(state, cmd, data, data_size, &elist.timestamp); if (err < 0) goto err_out_free; err_out_free: free(data); err_out_exit: free(error_string); dnet_ext_list_destroy(&elist); return err; }
/* Pre-callback that formats arguments and calls ictl->callback */ static int blob_iterate_callback_common(struct eblob_disk_control *dc, int fd, uint64_t data_offset, void *priv, int no_meta) { struct dnet_iterator_ctl *ictl = priv; struct dnet_ext_list_hdr ehdr; struct dnet_ext_list elist; struct eblob_backend_config *c = ictl->iterate_private; uint64_t size; int err; assert(dc != NULL); size = dc->data_size; dnet_ext_list_init(&elist); /* If it's an extended record - extract header, move data pointer */ if (dc->flags & BLOB_DISK_CTL_EXTHDR) { /* * Skip reading/extracting header of the committed records if iterator runs with no_meta. * Header of uncommitted records should be read in any cases for correct recovery. */ if (!no_meta || (dc->flags & BLOB_DISK_CTL_UNCOMMITTED)) { err = dnet_ext_hdr_read(&ehdr, fd, data_offset); if (!err) { dnet_ext_hdr_to_list(&ehdr, &elist); } else { /* If extended header couldn't be extracted reset elist, * call callback for key with empty elist * and continue iteration because the rest records can be ok. * We need to reset the error to make iteration continue. */ char buffer[2*DNET_ID_SIZE + 1] = {0}; dnet_backend_log(c->blog, DNET_LOG_ERROR, "blob: iter: %s: dnet_ext_hdr_read failed: %d. Use empty extended header for this key\n", dnet_dump_id_len_raw((const unsigned char*)&dc->key, DNET_ID_SIZE, buffer), err); err = 0; } } data_offset += sizeof(struct dnet_ext_list_hdr); /* * When record has not been committed (no matter whether data has been written or not) * its @data_size is zero and removing ext header size ends up with * negative size converted back to very large positive number (0xffffffffffffffd0). * * It is possible that iterator will catch this key before commit time, * we have to be ready and do not provide invalid size. * * For more details, see blob_write() function below and prepare section comments. * * @data_header is safe, since we have preallocated all needed space for ext header * it just hasn't yet been committed to disk and thus @data_size hasn't yet been updated. */ if (size >= sizeof(struct dnet_ext_list_hdr)) { size -= sizeof(struct dnet_ext_list_hdr); } } err = ictl->callback(ictl->callback_private, (struct dnet_raw_id *)&dc->key, dc->flags, fd, data_offset, size, &elist); dnet_ext_list_destroy(&elist); return err; }
static int leveldb_backend_write(struct leveldb_backend *s, void *state, struct dnet_cmd *cmd, void *data) { struct dnet_ext_list elist; int err = -EINVAL; char *error_string = NULL; struct dnet_io_attr *io = data; void *read_data = NULL; dnet_ext_list_init(&elist); dnet_convert_io_attr(io); data += sizeof(struct dnet_io_attr); /* Combine data with empty extension list header */ err = dnet_ext_list_combine(&data, &io->size, &elist); if (err != 0) goto err_out_exit; /* * key should be locked by elliptics here, so it is safe to run read-modify-write cycle * if one performs write without lock we do not really care that one write may overwrite another one */ if (io->offset || (io->flags & DNET_IO_FLAGS_APPEND)) { size_t data_size; size_t offset = io->offset; read_data = leveldb_get(s->db, s->roptions, (const char *)io->id, DNET_ID_SIZE, &data_size, &error_string); if (error_string || !read_data) { free(error_string); error_string = NULL; goto plain_write; } if (io->flags & DNET_IO_FLAGS_APPEND) { io->offset = 0; offset = data_size; } /* * XXX: Account for extended header */ if (io->offset > data_size) { err = -ERANGE; goto err_out_exit; } if (offset + io->size > data_size) { read_data = realloc(read_data, data_size + io->size); if (!read_data) { err = -ENOMEM; goto err_out_exit; } } memcpy(read_data + offset, data, io->size); data = read_data; if (offset + io->size > data_size) io->size = offset + io->size; else io->size = data_size; } plain_write: leveldb_put(s->db, s->woptions, (const char *)cmd->id.id, DNET_ID_SIZE, data, io->size, &error_string); if (error_string) goto err_out_free; if (io->flags & DNET_IO_FLAGS_WRITE_NO_FILE_INFO) { cmd->flags |= DNET_FLAGS_NEED_ACK; err = 0; goto err_out_exit; } err = dnet_send_file_info_ts_without_fd(state, cmd, 0, io->size, &elist.timestamp); if (err < 0) goto err_out_free; dnet_backend_log(DNET_LOG_NOTICE, "%s: leveldb: : WRITE: Ok: offset: %llu, size: %llu, ioflags: %x.\n", dnet_dump_id(&cmd->id), (unsigned long long)io->offset, (unsigned long long)io->size, io->flags); err_out_free: free(data); err_out_exit: dnet_ext_list_destroy(&elist); if (err < 0) dnet_backend_log(DNET_LOG_ERROR, "%s: leveldb: : WRITE: error: %s: %d.\n", dnet_dump_id(&cmd->id), error_string, err); free(read_data); free(error_string); return err; }