/*! * Extracts \a elist from \a datap, replaces \a datap pointer and adjusts \a * sizep. In case \a free_data is set then data pointed by \a *datap is free'd. */ int dnet_ext_list_extract(void **datap, uint64_t *sizep, struct dnet_ext_list *elist, enum dnet_ext_free_data free_data) { struct dnet_ext_list_hdr *hdr; /* Extensions header */ uint64_t new_size; /* Size of data without extensions */ void *new_data; /* Data without extensions */ static const size_t hdr_size = sizeof(struct dnet_ext_list_hdr); /* Parameter checks */ if (datap == NULL || *datap == NULL) return -EINVAL; if (sizep == NULL || elist == NULL) return -EINVAL; /* Sanity checks */ if (*sizep < hdr_size) return -ERANGE; /* * Shortcut * * TODO: For now we account only for header size, but when we add * support additional extensions we should account for hdr_size + * hdr->size */ new_size = *sizep - hdr_size; hdr = (struct dnet_ext_list_hdr *)*datap; /* Extract payload from \a datap */ new_data = (unsigned char *)*datap + hdr_size; dnet_ext_hdr_to_list(hdr, elist); /* * Currently we do not support any extensions beyond header itself * so assert on any extensions. * * TODO: Extract all extensions */ if (elist->size != 0) return -ENOTSUP; if (elist->version <= DNET_EXT_VERSION_FIRST || elist->version >= DNET_EXT_VERSION_LAST) return -ENOTSUP; /* Save original pointer to data */ if (free_data == DNET_EXT_FREE_ON_DESTROY) elist->data = *datap; /* Swap data, adjust size */ *datap = new_data; *sizep = new_size; return 0; }
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; }