代码拉取完成,页面将自动刷新
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Copyright by The HDF Group. *
* All rights reserved. *
* *
* This file is part of HDF5. The full HDF5 copyright notice, including *
* terms governing use, modification, and redistribution, is contained in *
* the COPYING file, which can be found at the root of the source code *
* distribution tree, or in https://www.hdfgroup.org/licenses. *
* If you do not have access to either file, you may request a copy from *
* help@hdfgroup.org. *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*
* Onion Virtual File Driver (VFD)
*
* Purpose: Provide in-file provenance and revision/version control.
*/
/* This source code file is part of the H5FD driver module */
#include "H5FDdrvr_module.h"
#include "H5private.h" /* Generic Functions */
#include "H5Eprivate.h" /* Error handling */
#include "H5Fprivate.h" /* Files */
#include "H5FDprivate.h" /* File drivers */
#include "H5FDonion.h" /* Onion file driver */
#include "H5FDonion_priv.h" /* Onion file driver internals */
#include "H5FDsec2.h" /* Sec2 file driver */
#include "H5FLprivate.h" /* Free Lists */
#include "H5Iprivate.h" /* IDs */
#include "H5MMprivate.h" /* Memory management */
/* The driver identification number, initialized at runtime */
static hid_t H5FD_ONION_g = 0;
/******************************************************************************
*
* Structure: H5FD_onion_t
*
* Purpose: Store information required to manage an onionized file.
* This structure is created when such a file is "opened" and
* discarded when it is "closed".
*
* pu
*
* Instance of H5FD_t which contains fields common to all VFDs.
* It must be the first item in this structure, since at higher levels,
* this structure will be treated as an instance of H5FD_t.
*
* fa
*
* Instance of `H5FD_onion_fapl_info_t` containing the configuration data
* needed to "open" the HDF5 file.
*
* original_file
*
* VFD handle for the original HDF5 file.
*
* onion_file
*
* VFD handle for the onion file.
* NULL if not set to use the single, separate storage target.
*
* recovery_file
*
* VFD handle for the history recovery file. This file is a backup of
* the existing history when an existing onion file is opened in RW mode.
*
* recovery_file_name
*
* String allocated and populated on file-open in write mode and freed on
* file-close, stores the path/name of the 'recovery' file. The file
* created at this location is to be removed upon successful file-close
* from write mode.
*
* is_open_rw
*
* Remember whether the file was opened in a read-write mode.
*
* align_history_on_pages
*
* Remember whether onion-writes must be aligned to page boundaries.
*
* header
*
* In-memory copy of the onion history data header.
*
* history
*
* In-memory copy of the onion history.
*
* curr_rev_record
*
* Record for the currently open revision.
*
* rev_index
*
* Index for maintaining modified pages (RW mode only).
* Pointer is NULL when the file is not opened in write mode.
* Pointer is allocated on open and must be freed on close.
* Contents must be merged with the revision record's archival index prior
* to commitment of history to backing store.
*
* onion_eof
*
* Last byte in the onion file.
*
* origin_eof
*
* Size of the original HDF5 file.
*
* logical_eoa
*
* Address of first byte past addressed space in the logical 'file'
* presented by this VFD.
*
* logical_eof
*
* Address of first byte past last byte in the logical 'file' presented
* by this VFD.
* Must be copied into the revision record on close to write onion data.
*
******************************************************************************
*/
typedef struct H5FD_onion_t {
H5FD_t pub;
H5FD_onion_fapl_info_t fa;
hbool_t is_open_rw;
hbool_t align_history_on_pages;
/* Onion-related files */
H5FD_t *original_file;
H5FD_t *onion_file;
H5FD_t *recovery_file;
char *recovery_file_name;
/* Onion data structures */
H5FD_onion_header_t header;
H5FD_onion_history_t history;
H5FD_onion_revision_record_t curr_rev_record;
H5FD_onion_revision_index_t *rev_index;
/* End of addresses and files */
haddr_t onion_eof;
haddr_t origin_eof;
haddr_t logical_eoa;
haddr_t logical_eof;
} H5FD_onion_t;
H5FL_DEFINE_STATIC(H5FD_onion_t);
#define MAXADDR (((haddr_t)1 << (8 * sizeof(HDoff_t) - 1)) - 1)
#define H5FD_CTL_GET_NUM_REVISIONS 20001
/* Prototypes */
static herr_t H5FD__onion_close(H5FD_t *);
static haddr_t H5FD__onion_get_eoa(const H5FD_t *, H5FD_mem_t);
static haddr_t H5FD__onion_get_eof(const H5FD_t *, H5FD_mem_t);
static H5FD_t *H5FD__onion_open(const char *, unsigned int, hid_t, haddr_t);
static herr_t H5FD__onion_read(H5FD_t *, H5FD_mem_t, hid_t, haddr_t, size_t, void *);
static herr_t H5FD__onion_set_eoa(H5FD_t *, H5FD_mem_t, haddr_t);
static herr_t H5FD__onion_term(void);
static herr_t H5FD__onion_write(H5FD_t *, H5FD_mem_t, hid_t, haddr_t, size_t, const void *);
static herr_t H5FD__onion_open_rw(H5FD_onion_t *, unsigned int, haddr_t, bool new_open);
static herr_t H5FD__onion_sb_encode(H5FD_t *_file, char *name /*out*/, unsigned char *buf /*out*/);
static herr_t H5FD__onion_sb_decode(H5FD_t *_file, const char *name, const unsigned char *buf);
static hsize_t H5FD__onion_sb_size(H5FD_t *_file);
static herr_t H5FD__onion_ctl(H5FD_t *_file, uint64_t op_code, uint64_t flags,
const void H5_ATTR_UNUSED *input, void H5_ATTR_UNUSED **output);
static herr_t H5FD__get_onion_revision_count(H5FD_t *file, uint64_t *revision_count);
/* Temporary */
H5_DLL herr_t H5FD__onion_write_final_history(H5FD_onion_t *file);
static const H5FD_class_t H5FD_onion_g = {
H5FD_CLASS_VERSION, /* struct version */
H5FD_ONION_VALUE, /* value */
"onion", /* name */
MAXADDR, /* maxaddr */
H5F_CLOSE_WEAK, /* fc_degree */
H5FD__onion_term, /* terminate */
H5FD__onion_sb_size, /* sb_size */
H5FD__onion_sb_encode, /* sb_encode */
H5FD__onion_sb_decode, /* sb_decode */
sizeof(H5FD_onion_fapl_info_t), /* fapl_size */
NULL, /* fapl_get */
NULL, /* fapl_copy */
NULL, /* fapl_free */
0, /* dxpl_size */
NULL, /* dxpl_copy */
NULL, /* dxpl_free */
H5FD__onion_open, /* open */
H5FD__onion_close, /* close */
NULL, /* cmp */
NULL, /* query */
NULL, /* get_type_map */
NULL, /* alloc */
NULL, /* free */
H5FD__onion_get_eoa, /* get_eoa */
H5FD__onion_set_eoa, /* set_eoa */
H5FD__onion_get_eof, /* get_eof */
NULL, /* get_handle */
H5FD__onion_read, /* read */
H5FD__onion_write, /* write */
NULL, /* read_vector */
NULL, /* write_vector */
NULL, /* read_selection */
NULL, /* write_selection */
NULL, /* flush */
NULL, /* truncate */
NULL, /* lock */
NULL, /* unlock */
NULL, /* del */
H5FD__onion_ctl, /* ctl */
H5FD_FLMAP_DICHOTOMY /* fl_map */
};
/*-----------------------------------------------------------------------------
* Function: H5FD_onion_init
*
* Purpose: Initialize this driver by registering the driver with the
* library.
*
* Return: Success: The driver ID for the onion driver.
* Failure: Negative
*-----------------------------------------------------------------------------
*/
hid_t
H5FD_onion_init(void)
{
hid_t ret_value = H5I_INVALID_HID;
FUNC_ENTER_NOAPI_NOERR
if (H5I_VFL != H5I_get_type(H5FD_ONION_g))
H5FD_ONION_g = H5FD_register(&H5FD_onion_g, sizeof(H5FD_class_t), FALSE);
/* Set return value */
ret_value = H5FD_ONION_g;
FUNC_LEAVE_NOAPI(ret_value)
} /* end H5FD_onion_init() */
/*-----------------------------------------------------------------------------
* Function: H5FD__onion_term
*
* Purpose: Shut down the Onion VFD.
*
* Returns: SUCCEED (Can't fail)
*-----------------------------------------------------------------------------
*/
static herr_t
H5FD__onion_term(void)
{
FUNC_ENTER_PACKAGE_NOERR
/* Reset VFL ID */
H5FD_ONION_g = 0;
FUNC_LEAVE_NOAPI(SUCCEED);
} /* end H5FD__onion_term() */
/*-----------------------------------------------------------------------------
* Function: H5Pget_fapl_onion
*
* Purpose: Copy the Onion configuration information from the FAPL at
* `fapl_id` to the destination pointer `fa_out`.
*
* Return: Success: Non-negative value (SUCCEED).
* Failure: Negative value (FAIL).
*-----------------------------------------------------------------------------
*/
herr_t
H5Pget_fapl_onion(hid_t fapl_id, H5FD_onion_fapl_info_t *fa_out)
{
const H5FD_onion_fapl_info_t *info_ptr = NULL;
H5P_genplist_t *plist = NULL;
herr_t ret_value = SUCCEED;
FUNC_ENTER_API(FAIL)
H5TRACE2("e", "i*!", fapl_id, fa_out);
if (NULL == fa_out)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "NULL info-out pointer")
if (NULL == (plist = H5P_object_verify(fapl_id, H5P_FILE_ACCESS)))
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "Not a valid FAPL ID")
if (H5FD_ONION != H5P_peek_driver(plist))
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "Incorrect VFL driver")
if (NULL == (info_ptr = (const H5FD_onion_fapl_info_t *)H5P_peek_driver_info(plist)))
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "bad VFL driver info")
HDmemcpy(fa_out, info_ptr, sizeof(H5FD_onion_fapl_info_t));
done:
FUNC_LEAVE_API(ret_value)
} /* end H5Pget_fapl_onion() */
/*-----------------------------------------------------------------------------
* Function: H5Pset_fapl_onion
*
* Purpose Set the file access property list at `fapl_id` to use the
* Onion virtual file driver with the given configuration.
* The info structure may be modified or deleted after this call,
* as its contents are copied into the FAPL.
*
* Return: Success: Non-negative value (SUCCEED).
* Failure: Negative value (FAIL).
*-----------------------------------------------------------------------------
*/
herr_t
H5Pset_fapl_onion(hid_t fapl_id, const H5FD_onion_fapl_info_t *fa)
{
H5P_genplist_t *fapl = NULL;
H5P_genplist_t *backing_fapl = NULL;
hid_t backing_vfd_id = H5I_INVALID_HID;
herr_t ret_value = SUCCEED;
FUNC_ENTER_API(FAIL)
H5TRACE2("e", "i*!", fapl_id, fa);
if (NULL == (fapl = H5P_object_verify(fapl_id, H5P_FILE_ACCESS)))
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "Not a valid FAPL ID");
if (NULL == fa)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "NULL info pointer");
if (H5FD_ONION_FAPL_INFO_VERSION_CURR != fa->version)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid info version");
if (!POWER_OF_TWO(fa->page_size))
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid info page size");
if (fa->page_size < 1)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid info page size");
if (H5P_DEFAULT == fa->backing_fapl_id) {
if (NULL == (backing_fapl = H5P_object_verify(H5P_FILE_ACCESS_DEFAULT, H5P_FILE_ACCESS)))
HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, FAIL, "invalid backing fapl id");
}
else {
if (NULL == (backing_fapl = H5P_object_verify(fa->backing_fapl_id, H5P_FILE_ACCESS)))
HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, FAIL, "invalid backing fapl id");
}
/* The only backing fapl that is currently supported is sec2 */
if ((backing_vfd_id = H5P_peek_driver(backing_fapl)) < 0)
HGOTO_ERROR(H5E_VFL, H5E_CANTGET, FAIL, "Can't get VFD from fapl");
if (backing_vfd_id != H5FD_SEC2)
HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, FAIL, "Onion VFD only supports sec2 backing store");
if (H5P_set_driver(fapl, H5FD_ONION, (const void *)fa, NULL) < 0)
HGOTO_ERROR(H5E_VFL, H5E_CANTSET, FAIL, "Can't set the onion VFD");
done:
FUNC_LEAVE_API(ret_value)
} /* end H5Pset_fapl_onion() */
/*-------------------------------------------------------------------------
* Function: H5FD__onion_sb_size
*
* Purpose: Returns the size of the private information to be stored in
* the superblock.
*
* Return: Success: The super block driver data size
* Failure: never fails
*-------------------------------------------------------------------------
*/
static hsize_t
H5FD__onion_sb_size(H5FD_t *_file)
{
H5FD_onion_t *file = (H5FD_onion_t *)_file;
hsize_t ret_value = 0;
FUNC_ENTER_PACKAGE_NOERR
/* Sanity check */
HDassert(file);
HDassert(file->original_file);
if (file->original_file)
ret_value = H5FD_sb_size(file->original_file);
FUNC_LEAVE_NOAPI(ret_value)
} /* end H5FD__onion_sb_size */
/*-------------------------------------------------------------------------
* Function: H5FD__onion_sb_encode
*
* Purpose: Encodes the superblock information for this driver
*
* Return: SUCCEED/FAIL
*-------------------------------------------------------------------------
*/
static herr_t
H5FD__onion_sb_encode(H5FD_t *_file, char *name /*out*/, unsigned char *buf /*out*/)
{
H5FD_onion_t *file = (H5FD_onion_t *)_file;
herr_t ret_value = SUCCEED; /* Return value */
FUNC_ENTER_PACKAGE
/* Sanity check */
HDassert(file);
HDassert(file->original_file);
if (file->original_file && H5FD_sb_encode(file->original_file, name, buf) < 0)
HGOTO_ERROR(H5E_VFL, H5E_CANTENCODE, FAIL, "unable to encode the superblock in R/W file")
done:
FUNC_LEAVE_NOAPI(ret_value)
} /* end H5FD__onion_sb_encode */
/*-------------------------------------------------------------------------
* Function: H5FD__onion_sb_decode
*
* Purpose: Decodes the superblock information for this driver
*
* Return: SUCCEED/FAIL
*-------------------------------------------------------------------------
*/
static herr_t
H5FD__onion_sb_decode(H5FD_t *_file, const char *name, const unsigned char *buf)
{
H5FD_onion_t *file = (H5FD_onion_t *)_file;
herr_t ret_value = SUCCEED; /* Return value */
FUNC_ENTER_PACKAGE
/* Sanity check */
HDassert(file);
HDassert(file->original_file);
if (H5FD_sb_load(file->original_file, name, buf) < 0)
HGOTO_ERROR(H5E_VFL, H5E_CANTDECODE, FAIL, "unable to decode the superblock in R/W file")
done:
FUNC_LEAVE_NOAPI(ret_value)
} /* end H5FD__onion_sb_decode */
/*-----------------------------------------------------------------------------
* Write in-memory revision record to appropriate backing file.
* Update information in other in-memory components.
*-----------------------------------------------------------------------------
*/
static herr_t
H5FD__onion_commit_new_revision_record(H5FD_onion_t *file)
{
uint32_t checksum = 0; /* required */
size_t size = 0;
haddr_t phys_addr = 0; /* offset in history file to record start */
unsigned char *buf = NULL;
herr_t ret_value = SUCCEED;
H5FD_onion_revision_record_t *rec = &file->curr_rev_record;
H5FD_onion_history_t *history = &file->history;
H5FD_onion_record_loc_t *new_list = NULL;
time_t rawtime;
struct tm *info;
FUNC_ENTER_PACKAGE
HDtime(&rawtime);
info = HDgmtime(&rawtime);
HDstrftime(rec->time_of_creation, sizeof(rec->time_of_creation), "%Y%m%dT%H%M%SZ", info);
rec->logical_eof = file->logical_eof;
if ((TRUE == file->is_open_rw) && (H5FD__onion_merge_revision_index_into_archival_index(
file->rev_index, &file->curr_rev_record.archival_index) < 0))
HGOTO_ERROR(H5E_VFL, H5E_INTERNAL, FAIL, "unable to update index to write")
if (NULL == (buf = H5MM_malloc(H5FD_ONION_ENCODED_SIZE_REVISION_RECORD + (size_t)rec->comment_size +
(H5FD_ONION_ENCODED_SIZE_INDEX_ENTRY * rec->archival_index.n_entries))))
HGOTO_ERROR(H5E_VFL, H5E_CANTALLOC, FAIL, "can't allocate buffer for encoded revision record")
if (0 == (size = H5FD__onion_revision_record_encode(rec, buf, &checksum)))
HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, FAIL, "problem encoding revision record")
phys_addr = file->onion_eof;
if (H5FD_set_eoa(file->onion_file, H5FD_MEM_DRAW, phys_addr + size) < 0)
HGOTO_ERROR(H5E_VFL, H5E_CANTSET, FAIL, "can't modify EOA for new revision record")
if (H5FD_write(file->onion_file, H5FD_MEM_DRAW, phys_addr, size, buf) < 0)
HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, FAIL, "can't write new revision record")
file->onion_eof = phys_addr + size;
if (TRUE == file->align_history_on_pages)
file->onion_eof = (file->onion_eof + (file->header.page_size - 1)) & (~(file->header.page_size - 1));
/* Update history info to accommodate new revision */
if (history->n_revisions == 0) {
unsigned char *ptr = buf; /* re-use buffer space to compute checksum */
HDassert(history->record_locs == NULL);
history->n_revisions = 1;
if (NULL == (history->record_locs = H5MM_calloc(sizeof(H5FD_onion_record_loc_t))))
HGOTO_ERROR(H5E_VFL, H5E_CANTALLOC, FAIL, "can't allocate temporary record pointer list")
history->record_locs[0].phys_addr = phys_addr;
history->record_locs[0].record_size = size;
UINT64ENCODE(ptr, phys_addr);
UINT64ENCODE(ptr, size);
history->record_locs[0].checksum = H5_checksum_fletcher32(buf, (size_t)(ptr - buf));
/* TODO: size-reset belongs where? */
file->header.history_size += H5FD_ONION_ENCODED_SIZE_RECORD_POINTER;
} /* end if no extant revisions in history */
else {
unsigned char *ptr = buf; /* re-use buffer space to compute checksum */
HDassert(history->record_locs != NULL);
if (NULL == (new_list = H5MM_calloc((history->n_revisions + 1) * sizeof(H5FD_onion_record_loc_t))))
HGOTO_ERROR(H5E_VFL, H5E_CANTALLOC, FAIL, "unable to resize record pointer list")
HDmemcpy(new_list, history->record_locs, sizeof(H5FD_onion_record_loc_t) * history->n_revisions);
H5MM_xfree(history->record_locs);
history->record_locs = new_list;
new_list = NULL;
history->record_locs[history->n_revisions].phys_addr = phys_addr;
history->record_locs[history->n_revisions].record_size = size;
UINT64ENCODE(ptr, phys_addr);
UINT64ENCODE(ptr, size);
history->record_locs[history->n_revisions].checksum =
H5_checksum_fletcher32(buf, (size_t)(ptr - buf));
file->header.history_size += H5FD_ONION_ENCODED_SIZE_RECORD_POINTER;
history->n_revisions += 1;
} /* end if one or more revisions present in history */
file->header.history_addr = file->onion_eof;
done:
H5MM_xfree(buf);
H5MM_xfree(new_list);
FUNC_LEAVE_NOAPI(ret_value);
} /* end H5FD__onion_commit_new_revision_record() */
/*-----------------------------------------------------------------------------
* Function: H5FD__onion_close
*
* Purpose: Close an onionized file
*
* Return: SUCCEED/FAIL
*-----------------------------------------------------------------------------
*/
static herr_t
H5FD__onion_close(H5FD_t *_file)
{
H5FD_onion_t *file = (H5FD_onion_t *)_file;
herr_t ret_value = SUCCEED;
FUNC_ENTER_PACKAGE
HDassert(file);
if (H5FD_ONION_STORE_TARGET_ONION == file->fa.store_target) {
HDassert(file->onion_file);
if (file->is_open_rw) {
HDassert(file->recovery_file);
if (H5FD__onion_commit_new_revision_record(file) < 0)
HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, FAIL, "Can't write revision record to backing store")
if (H5FD__onion_write_final_history(file) < 0)
HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, FAIL, "Can't write history to backing store")
/* Unset write-lock flag and write header */
if (file->is_open_rw)
file->header.flags &= (uint32_t)~H5FD_ONION_HEADER_FLAG_WRITE_LOCK;
if (H5FD__onion_write_header(&(file->header), file->onion_file) < 0)
HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, FAIL, "Can't write updated header to backing store")
}
}
else
HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, FAIL, "invalid history target")
done:
/* Destroy things as best we can, even if there were earlier errors */
if (file->original_file)
if (H5FD_close(file->original_file) < 0)
HDONE_ERROR(H5E_VFL, H5E_CANTRELEASE, FAIL, "can't close backing canon file")
if (file->onion_file)
if (H5FD_close(file->onion_file) < 0)
HDONE_ERROR(H5E_VFL, H5E_CANTRELEASE, FAIL, "can't close backing onion file")
if (file->recovery_file) {
if (H5FD_close(file->recovery_file) < 0)
HDONE_ERROR(H5E_VFL, H5E_CANTRELEASE, FAIL, "can't close backing recovery file")
/* TODO: Use the VFD's del callback instead of remove (this requires
* storing a copy of the fapl that was used to open it)
*/
HDremove(file->recovery_file_name);
}
if (file->rev_index)
if (H5FD__onion_revision_index_destroy(file->rev_index) < 0)
HDONE_ERROR(H5E_VFL, H5E_CANTRELEASE, FAIL, "can't close revision index")
H5MM_xfree(file->recovery_file_name);
H5MM_xfree(file->history.record_locs);
H5MM_xfree(file->curr_rev_record.comment);
H5MM_xfree(file->curr_rev_record.archival_index.list);
file = H5FL_FREE(H5FD_onion_t, file);
FUNC_LEAVE_NOAPI(ret_value)
} /* end H5FD__onion_close() */
/*-----------------------------------------------------------------------------
* Function: H5FD__onion_get_eoa
*
* Purpose: Get end-of-address address.
*
* Return: Address of first byte past the addressed space
*-----------------------------------------------------------------------------
*/
static haddr_t
H5FD__onion_get_eoa(const H5FD_t *_file, H5FD_mem_t H5_ATTR_UNUSED type)
{
const H5FD_onion_t *file = (const H5FD_onion_t *)_file;
FUNC_ENTER_PACKAGE_NOERR;
FUNC_LEAVE_NOAPI(file->logical_eoa)
} /* end H5FD__onion_get_eoa() */
/*-----------------------------------------------------------------------------
* Function: H5FD__onion_get_eof
*
* Purpose: Get end-of-file address.
*
* Return: Address of first byte past the file-end.
*-----------------------------------------------------------------------------
*/
static haddr_t
H5FD__onion_get_eof(const H5FD_t *_file, H5FD_mem_t H5_ATTR_UNUSED type)
{
const H5FD_onion_t *file = (const H5FD_onion_t *)_file;
FUNC_ENTER_PACKAGE_NOERR;
FUNC_LEAVE_NOAPI(file->logical_eof)
} /* end H5FD__onion_get_eof() */
/*-----------------------------------------------------------------------------
* Sanitize the backing FAPL ID
*-----------------------------------------------------------------------------
*/
static inline hid_t
H5FD__onion_get_legit_fapl_id(hid_t fapl_id)
{
if (H5P_DEFAULT == fapl_id)
return H5P_FILE_ACCESS_DEFAULT;
else if (TRUE == H5P_isa_class(fapl_id, H5P_FILE_ACCESS))
return fapl_id;
else
return H5I_INVALID_HID;
}
/*-----------------------------------------------------------------------------
* Function: H5FD_onion_create_truncate_onion
*
* Purpose: Create/truncate HDF5 and onion data for a fresh file
*
* Special open operation required to instantiate the canonical file and
* history simultaneously. If successful, the required backing files are
* craeated and given initial population on the backing store, and the Onion
* virtual file handle is set; open effects a write-mode open.
*
* Cannot create 'template' history and proceed with normal write-mode open,
* as this would in effect create an empty first revision, making the history
* unintuitive. (create file -> initialize and commit empty first revision
* (revision 0); any data written to file during the 'create' open, as seen by
* the user, would be in the second revision (revision 1).)
*
* Return: SUCCEED/FAIL
*-----------------------------------------------------------------------------
*/
static herr_t
H5FD__onion_create_truncate_onion(H5FD_onion_t *file, const char *filename, const char *name_onion,
const char *recovery_file_nameery, unsigned int flags, haddr_t maxaddr)
{
hid_t backing_fapl_id = H5I_INVALID_HID;
H5FD_onion_header_t *hdr = NULL;
H5FD_onion_history_t *history = NULL;
H5FD_onion_revision_record_t *rec = NULL;
unsigned char *buf = NULL;
size_t size = 0;
herr_t ret_value = SUCCEED;
FUNC_ENTER_PACKAGE;
HDassert(file != NULL);
hdr = &file->header;
history = &file->history;
rec = &file->curr_rev_record;
hdr->flags = H5FD_ONION_HEADER_FLAG_WRITE_LOCK;
if (H5FD_ONION_FAPL_INFO_CREATE_FLAG_ENABLE_PAGE_ALIGNMENT & file->fa.creation_flags)
hdr->flags |= H5FD_ONION_HEADER_FLAG_PAGE_ALIGNMENT;
hdr->origin_eof = 0;
backing_fapl_id = H5FD__onion_get_legit_fapl_id(file->fa.backing_fapl_id);
if (H5I_INVALID_HID == backing_fapl_id)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid backing FAPL ID")
/* Create backing files for onion history */
if (NULL == (file->original_file = H5FD_open(filename, flags, backing_fapl_id, maxaddr)))
HGOTO_ERROR(H5E_VFL, H5E_CANTOPENFILE, FAIL, "cannot open the backing file")
if (NULL == (file->onion_file = H5FD_open(name_onion, flags, backing_fapl_id, maxaddr)))
HGOTO_ERROR(H5E_VFL, H5E_CANTOPENFILE, FAIL, "cannot open the backing onion file")
if (NULL == (file->recovery_file = H5FD_open(recovery_file_nameery, flags, backing_fapl_id, maxaddr)))
HGOTO_ERROR(H5E_VFL, H5E_CANTOPENFILE, FAIL, "cannot open the backing file")
/* Write "empty" .h5 file contents (signature ONIONEOF) */
if (H5FD_set_eoa(file->original_file, H5FD_MEM_DRAW, 8) < 0)
HGOTO_ERROR(H5E_VFL, H5E_CANTSET, FAIL, "can't extend EOA")
if (H5FD_write(file->original_file, H5FD_MEM_DRAW, 0, 8, "ONIONEOF") < 0)
HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, FAIL, "cannot write header to the backing h5 file")
/* Write nascent history (with no revisions) to "recovery" */
if (NULL == (buf = H5MM_malloc(H5FD_ONION_ENCODED_SIZE_HISTORY)))
HGOTO_ERROR(H5E_VFL, H5E_CANTALLOC, FAIL, "can't allocate buffer")
size = H5FD__onion_history_encode(history, buf, &history->checksum);
if (H5FD_ONION_ENCODED_SIZE_HISTORY != size)
HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, FAIL, "can't encode history")
if (H5FD_set_eoa(file->recovery_file, H5FD_MEM_DRAW, size) < 0)
HGOTO_ERROR(H5E_VFL, H5E_CANTSET, FAIL, "can't extend EOA")
if (H5FD_write(file->recovery_file, H5FD_MEM_DRAW, 0, size, buf) < 0)
HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, FAIL, "cannot write history to the backing recovery file")
hdr->history_size = size; /* record for later use */
H5MM_xfree(buf);
buf = NULL;
/* Write history header with "no" history.
* Size of the "recovery" history recorded for later use on close.
*/
if (NULL == (buf = H5MM_malloc(H5FD_ONION_ENCODED_SIZE_HEADER)))
HGOTO_ERROR(H5E_VFL, H5E_CANTALLOC, FAIL, "can't allocate buffer")
size = H5FD__onion_header_encode(hdr, buf, &hdr->checksum);
if (H5FD_ONION_ENCODED_SIZE_HEADER != size)
HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, FAIL, "can't encode history header")
if (H5FD_set_eoa(file->onion_file, H5FD_MEM_DRAW, size) < 0)
HGOTO_ERROR(H5E_VFL, H5E_CANTSET, FAIL, "can't extend EOA")
if (H5FD_write(file->onion_file, H5FD_MEM_DRAW, 0, size, buf) < 0)
HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, FAIL, "cannot write header to the backing onion file")
file->onion_eof = (haddr_t)size;
if (TRUE == file->align_history_on_pages)
file->onion_eof = (file->onion_eof + (hdr->page_size - 1)) & (~(hdr->page_size - 1));
rec->archival_index.list = NULL;
if (NULL == (file->rev_index = H5FD__onion_revision_index_init(file->fa.page_size)))
HGOTO_ERROR(H5E_VFL, H5E_CANTINIT, FAIL, "can't initialize revision index")
done:
H5MM_xfree(buf);
if (FAIL == ret_value)
HDremove(recovery_file_nameery); /* destroy new temp file, if 'twas created */
FUNC_LEAVE_NOAPI(ret_value);
} /* end H5FD__onion_create_truncate_onion() */
static herr_t
H5FD__onion_remove_unused_symbols(char *s)
{
char *d = s;
FUNC_ENTER_PACKAGE_NOERR;
do {
while (*d == '{' || *d == '}' || *d == ' ') {
++d;
}
} while ((*s++ = *d++));
FUNC_LEAVE_NOAPI(SUCCEED);
}
static herr_t
H5FD__onion_parse_config_str(const char *config_str, H5FD_onion_fapl_info_t *fa)
{
char *config_str_copy = NULL;
herr_t ret_value = SUCCEED;
FUNC_ENTER_PACKAGE;
if (!HDstrcmp(config_str, ""))
HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, FAIL, "configure string can't be empty")
/* Initialize to the default values */
fa->version = H5FD_ONION_FAPL_INFO_VERSION_CURR;
fa->backing_fapl_id = H5P_DEFAULT;
fa->page_size = 4;
fa->store_target = H5FD_ONION_STORE_TARGET_ONION;
fa->revision_num = H5FD_ONION_FAPL_INFO_REVISION_ID_LATEST;
fa->force_write_open = 0;
fa->creation_flags = 0;
HDstrcpy(fa->comment, "initial comment");
/* If a single integer is passed in as a string, it's a shortcut for the tools
* (h5repack, h5diff, h5dump). Otherwise, the string should have curly brackets,
* e.g. {revision_num: 2; page_size: 4;}
*/
if (config_str[0] != '{')
fa->revision_num = (uint64_t)HDstrtoull(config_str, NULL, 10);
else {
char *token1 = NULL, *token2 = NULL;
/* Duplicate the configure string since strtok will mess with it */
if (NULL == (config_str_copy = H5MM_strdup(config_str)))
HGOTO_ERROR(H5E_VFL, H5E_CANTALLOC, FAIL, "can't duplicate configure string")
/* Remove the curly brackets and space from the configure string */
H5FD__onion_remove_unused_symbols(config_str_copy);
/* The configure string can't be empty after removing the curly brackets */
if (!HDstrcmp(config_str_copy, ""))
HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, FAIL, "configure string can't be empty")
token1 = HDstrtok(config_str_copy, ":");
token2 = HDstrtok(NULL, ";");
do {
if (token1 && token2) {
if (!HDstrcmp(token1, "version")) {
if (!HDstrcmp(token2, "H5FD_ONION_FAPL_INFO_VERSION_CURR"))
fa->version = H5FD_ONION_FAPL_INFO_VERSION_CURR;
}
else if (!HDstrcmp(token1, "backing_fapl_id")) {
if (!HDstrcmp(token2, "H5P_DEFAULT"))
fa->backing_fapl_id = H5P_DEFAULT;
else if (!strcmp(token2, "H5I_INVALID_HID"))
fa->backing_fapl_id = H5I_INVALID_HID;
else
fa->backing_fapl_id = HDstrtoll(token2, NULL, 10);
}
else if (!HDstrcmp(token1, "page_size")) {
fa->page_size = (uint32_t)HDstrtoul(token2, NULL, 10);
}
else if (!HDstrcmp(token1, "revision_num")) {
if (!HDstrcmp(token2, "H5FD_ONION_FAPL_INFO_REVISION_ID_LATEST"))
fa->revision_num = H5FD_ONION_FAPL_INFO_REVISION_ID_LATEST;
else
fa->revision_num = (uint64_t)HDstrtoull(token2, NULL, 10);
}
else if (!HDstrcmp(token1, "force_write_open")) {
fa->force_write_open = (uint8_t)HDstrtoul(token2, NULL, 10);
}
else if (!HDstrcmp(token1, "creation_flags")) {
fa->creation_flags = (uint8_t)HDstrtoul(token2, NULL, 10);
}
else if (!HDstrcmp(token1, "comment")) {
HDstrcpy(fa->comment, token2);
}
else
HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, FAIL, "unknown token in the configure string: %s",
token1)
}
token1 = HDstrtok(NULL, ":");
token2 = HDstrtok(NULL, ";");
} while (token1);
}
if (H5P_DEFAULT == fa->backing_fapl_id || H5I_INVALID_HID == fa->backing_fapl_id) {
H5P_genclass_t *pclass; /* Property list class to modify */
if (NULL == (pclass = (H5P_genclass_t *)H5I_object_verify(H5P_FILE_ACCESS, H5I_GENPROP_CLS)))
HGOTO_ERROR(H5E_VFL, H5E_BADTYPE, FAIL, "not a property list class");
/* Create the new property list */
if ((fa->backing_fapl_id = H5P_create_id(pclass, TRUE)) < 0)
HGOTO_ERROR(H5E_VFL, H5E_CANTCREATE, FAIL, "unable to create property list");
}
done:
H5MM_free(config_str_copy);
FUNC_LEAVE_NOAPI(ret_value);
}
/*-----------------------------------------------------------------------------
* Function: H5FD__onion_open
*
* Purpose: Open an onionized file
*
* Return: Success: A pointer to a new file data structure
* Failure: NULL
*-----------------------------------------------------------------------------
*/
static H5FD_t *
H5FD__onion_open(const char *filename, unsigned flags, hid_t fapl_id, haddr_t maxaddr)
{
H5P_genplist_t *plist = NULL;
H5FD_onion_t *file = NULL;
const H5FD_onion_fapl_info_t *fa = NULL;
H5FD_onion_fapl_info_t *new_fa = NULL;
const char *config_str = NULL;
hid_t backing_fapl_id = H5I_INVALID_HID;
char *name_onion = NULL;
char *recovery_file_nameery = NULL;
bool new_open = false;
haddr_t canon_eof = 0;
H5FD_t *ret_value = NULL;
FUNC_ENTER_PACKAGE
/* Check arguments */
if (!filename || !*filename)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, NULL, "invalid file name")
if (0 == maxaddr || HADDR_UNDEF == maxaddr)
HGOTO_ERROR(H5E_ARGS, H5E_BADRANGE, NULL, "bogus maxaddr")
HDassert(H5P_DEFAULT != fapl_id);
if (NULL == (plist = (H5P_genplist_t *)H5I_object(fapl_id)))
HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, NULL, "not a file access property list")
/* This VFD can be invoked by either H5Pset_fapl_onion() or
* H5Pset_driver_by_name(). When invoked by the former, there will be
* driver info to peek at.
*/
fa = (const H5FD_onion_fapl_info_t *)H5P_peek_driver_info(plist);
if (NULL == fa) {
if (NULL == (config_str = H5P_peek_driver_config_str(plist)))
HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, NULL, "missing VFL driver configure string")
/* Allocate a new onion fapl info struct and fill it from the
* configuration string
*/
if (NULL == (new_fa = H5MM_calloc(sizeof(H5FD_onion_fapl_info_t))))
HGOTO_ERROR(H5E_VFL, H5E_CANTALLOC, NULL, "can't allocate memory for onion fapl info struct")
if (H5FD__onion_parse_config_str(config_str, new_fa) < 0)
HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, NULL, "failed to parse configure string")
fa = new_fa;
}
/* Check for unsupported target values */
if (H5FD_ONION_STORE_TARGET_ONION != fa->store_target)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, NULL, "invalid store target")
/* Allocate space for the file struct */
if (NULL == (file = H5FL_CALLOC(H5FD_onion_t)))
HGOTO_ERROR(H5E_VFL, H5E_CANTALLOC, NULL, "unable to allocate file struct")
/* Allocate space for onion VFD file names */
if (NULL == (name_onion = H5MM_malloc(sizeof(char) * (HDstrlen(filename) + 7))))
HGOTO_ERROR(H5E_VFL, H5E_CANTALLOC, NULL, "unable to allocate onion name string")
HDsnprintf(name_onion, HDstrlen(filename) + 7, "%s.onion", filename);
if (NULL == (recovery_file_nameery = H5MM_malloc(sizeof(char) * (HDstrlen(name_onion) + 10))))
HGOTO_ERROR(H5E_VFL, H5E_CANTALLOC, NULL, "unable to allocate recovery name string")
HDsnprintf(recovery_file_nameery, HDstrlen(name_onion) + 10, "%s.recovery", name_onion);
file->recovery_file_name = recovery_file_nameery;
if (NULL == (file->recovery_file_name = H5MM_malloc(sizeof(char) * (HDstrlen(name_onion) + 10))))
HGOTO_ERROR(H5E_VFL, H5E_CANTALLOC, NULL, "unable to allocate recovery name string")
HDsnprintf(file->recovery_file_name, HDstrlen(name_onion) + 10, "%s.recovery", name_onion);
/* Translate H5P_DEFAULT to a real fapl ID, if necessary */
backing_fapl_id = H5FD__onion_get_legit_fapl_id(file->fa.backing_fapl_id);
if (H5I_INVALID_HID == backing_fapl_id)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, NULL, "invalid backing FAPL ID");
/* Initialize file structure fields */
HDmemcpy(&(file->fa), fa, sizeof(H5FD_onion_fapl_info_t));
file->header.version = H5FD_ONION_HEADER_VERSION_CURR;
file->header.page_size = file->fa.page_size; /* guarded on FAPL-set */
file->history.version = H5FD_ONION_HISTORY_VERSION_CURR;
file->curr_rev_record.version = H5FD_ONION_REVISION_RECORD_VERSION_CURR;
file->curr_rev_record.archival_index.version = H5FD_ONION_ARCHIVAL_INDEX_VERSION_CURR;
/* Check that the page size is a power of two */
if ((fa->page_size == 0) || ((fa->page_size & (fa->page_size - 1)) != 0))
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, NULL, "page size is not a power of two")
/* Assign the page size */
double log2_page_size = HDlog2((double)(fa->page_size));
file->curr_rev_record.archival_index.page_size_log2 = (uint32_t)log2_page_size;
/* Proceed with open. */
if ((H5F_ACC_CREAT | H5F_ACC_TRUNC) & flags) {
/* Create a new onion file from scratch */
/* Set flags */
if (fa->creation_flags & H5FD_ONION_FAPL_INFO_CREATE_FLAG_ENABLE_PAGE_ALIGNMENT) {
file->header.flags |= H5FD_ONION_HEADER_FLAG_PAGE_ALIGNMENT;
file->align_history_on_pages = TRUE;
}
/* Truncate and create everything as necessary */
if (H5FD__onion_create_truncate_onion(file, filename, name_onion, file->recovery_file_name, flags,
maxaddr) < 0)
HGOTO_ERROR(H5E_VFL, H5E_CANTCREATE, NULL, "unable to create/truncate onionized files")
file->is_open_rw = TRUE;
}
else {
/* Opening an existing onion file */
/* Open the existing file using the specified fapl */
if (NULL == (file->original_file = H5FD_open(filename, flags, backing_fapl_id, maxaddr)))
HGOTO_ERROR(H5E_VFL, H5E_CANTOPENFILE, NULL, "unable to open canonical file (does not exist?)")
/* Try to open any existing onion file */
H5E_BEGIN_TRY
{
file->onion_file = H5FD_open(name_onion, flags, backing_fapl_id, maxaddr);
}
H5E_END_TRY;
/* If that didn't work, create a new onion file */
/* TODO: Move to a new function */
if (NULL == file->onion_file) {
if (H5F_ACC_RDWR & flags) {
H5FD_onion_header_t *hdr = NULL;
H5FD_onion_history_t *history = NULL;
H5FD_onion_revision_record_t *rec = NULL;
unsigned char *head_buf = NULL;
unsigned char *hist_buf = NULL;
size_t size = 0;
size_t saved_size = 0;
HDassert(file != NULL);
hdr = &file->header;
history = &file->history;
rec = &file->curr_rev_record;
new_open = true;
if (H5FD_ONION_FAPL_INFO_CREATE_FLAG_ENABLE_PAGE_ALIGNMENT & file->fa.creation_flags) {
hdr->flags |= H5FD_ONION_HEADER_FLAG_PAGE_ALIGNMENT;
file->align_history_on_pages = TRUE;
}
if (HADDR_UNDEF == (canon_eof = H5FD_get_eof(file->original_file, H5FD_MEM_DEFAULT))) {
HGOTO_ERROR(H5E_VFL, H5E_CANTINIT, NULL, "cannot get size of canonical file")
}
if (H5FD_set_eoa(file->original_file, H5FD_MEM_DRAW, canon_eof) < 0)
HGOTO_ERROR(H5E_VFL, H5E_CANTSET, NULL, "can't extend EOA")
hdr->origin_eof = canon_eof;
file->logical_eof = canon_eof;
backing_fapl_id = H5FD__onion_get_legit_fapl_id(file->fa.backing_fapl_id);
if (H5I_INVALID_HID == backing_fapl_id)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, NULL, "invalid backing FAPL ID")
/* Create backing files for onion history */
if ((file->onion_file = H5FD_open(name_onion, (H5F_ACC_RDWR | H5F_ACC_CREAT | H5F_ACC_TRUNC),
backing_fapl_id, maxaddr)) == NULL) {
HGOTO_ERROR(H5E_VFL, H5E_CANTOPENFILE, NULL, "cannot open the backing onion file")
}
/* Write history header with "no" history */
hdr->history_size = H5FD_ONION_ENCODED_SIZE_HISTORY; /* record for later use */
hdr->history_addr =
H5FD_ONION_ENCODED_SIZE_HEADER + 1; /* TODO: comment these 2 or do some other way */
head_buf = H5MM_malloc(H5FD_ONION_ENCODED_SIZE_HEADER);
if (NULL == head_buf)
HGOTO_ERROR(H5E_VFL, H5E_CANTALLOC, NULL, "can't allocate buffer")
size = H5FD__onion_header_encode(hdr, head_buf, &hdr->checksum);
if (H5FD_ONION_ENCODED_SIZE_HEADER != size)
HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, NULL, "can't encode history header")
hist_buf = H5MM_malloc(H5FD_ONION_ENCODED_SIZE_HISTORY);
if (NULL == hist_buf)
HGOTO_ERROR(H5E_VFL, H5E_CANTALLOC, NULL, "can't allocate buffer")
saved_size = size;
history->n_revisions = 0;
size = H5FD__onion_history_encode(history, hist_buf, &history->checksum);
file->header.history_size = size; /* record for later use */
if (H5FD_ONION_ENCODED_SIZE_HISTORY != size) {
HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, NULL, "can't encode history")
}
if (H5FD_set_eoa(file->onion_file, H5FD_MEM_DRAW, saved_size + size + 1) < 0)
HGOTO_ERROR(H5E_VFL, H5E_CANTSET, NULL, "can't extend EOA")
if (H5FD_write(file->onion_file, H5FD_MEM_DRAW, 0, saved_size, head_buf) < 0) {
HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, NULL,
"cannot write header to the backing onion file")
}
file->onion_eof = (haddr_t)saved_size;
if (TRUE == file->align_history_on_pages)
file->onion_eof = (file->onion_eof + (hdr->page_size - 1)) & (~(hdr->page_size - 1));
rec->archival_index.list = NULL;
file->header.history_addr = file->onion_eof;
/* Write nascent history (with no revisions) to the backing onion file */
if (H5FD_write(file->onion_file, H5FD_MEM_DRAW, saved_size + 1, size, hist_buf) < 0) {
HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, NULL,
"cannot write history to the backing onion file")
}
file->header.history_size = size; /* record for later use */
H5MM_xfree(head_buf);
H5MM_xfree(hist_buf);
}
else {
HGOTO_ERROR(H5E_VFL, H5E_CANTOPENFILE, NULL, "unable to open onion file (does not exist?).")
}
}
if (HADDR_UNDEF == (canon_eof = H5FD_get_eof(file->original_file, H5FD_MEM_DEFAULT))) {
HGOTO_ERROR(H5E_VFL, H5E_CANTINIT, NULL, "cannot get size of canonical file")
}
if (H5FD_set_eoa(file->original_file, H5FD_MEM_DRAW, canon_eof) < 0)
HGOTO_ERROR(H5E_VFL, H5E_CANTSET, NULL, "can't extend EOA")
/* Get the history header from the onion file */
if (H5FD__onion_ingest_header(&file->header, file->onion_file, 0) < 0)
HGOTO_ERROR(H5E_VFL, H5E_CANTDECODE, NULL, "can't get history header from backing store")
file->align_history_on_pages =
(file->header.flags & H5FD_ONION_HEADER_FLAG_PAGE_ALIGNMENT) ? TRUE : FALSE;
if (H5FD_ONION_HEADER_FLAG_WRITE_LOCK & file->header.flags) {
/* Opening a file twice in write mode is an error */
HGOTO_ERROR(H5E_VFL, H5E_UNSUPPORTED, NULL, "Can't open file already opened in write-mode")
}
else {
/* Read in the history from the onion file */
if (H5FD__onion_ingest_history(&file->history, file->onion_file, file->header.history_addr,
file->header.history_size) < 0)
HGOTO_ERROR(H5E_VFL, H5E_CANTDECODE, NULL, "can't get history from backing store")
/* Sanity check on revision ID */
if (fa->revision_num > file->history.n_revisions &&
fa->revision_num != H5FD_ONION_FAPL_INFO_REVISION_ID_LATEST)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, NULL, "target revision ID out of range")
if (fa->revision_num == 0) {
file->curr_rev_record.logical_eof = canon_eof;
}
else if (file->history.n_revisions > 0 &&
H5FD__onion_ingest_revision_record(
&file->curr_rev_record, file->onion_file, &file->history,
MIN(fa->revision_num - 1, (file->history.n_revisions - 1))) < 0) {
HGOTO_ERROR(H5E_VFL, H5E_CANTDECODE, NULL, "can't get revision record from backing store")
}
if (H5F_ACC_RDWR & flags)
if (H5FD__onion_open_rw(file, flags, maxaddr, new_open) < 0)
HGOTO_ERROR(H5E_VFL, H5E_CANTOPENFILE, NULL, "can't write-open write-locked file")
}
} /* End if opening existing file */
/* Copy comment from FAPL info, if one is given */
if ((H5F_ACC_RDWR | H5F_ACC_CREAT | H5F_ACC_TRUNC) & flags) {
/* Free the old comment */
file->curr_rev_record.comment = H5MM_xfree(file->curr_rev_record.comment);
/* The buffer is of size H5FD_ONION_FAPL_INFO_COMMENT_MAX_LEN + 1
*
* We're getting this buffer from a fixed-size array in a struct, which
* will be garbage and not null-terminated if the user isn't careful.
* Be careful of this and do strndup first to ensure strdup gets a
* null-termianted string (HDF5 doesn't provide a strnlen call if you
* don't have one).
*/
if (NULL ==
(file->curr_rev_record.comment = H5MM_strndup(fa->comment, H5FD_ONION_FAPL_INFO_COMMENT_MAX_LEN)))
HGOTO_ERROR(H5E_VFL, H5E_CANTALLOC, NULL, "unable to duplicate comment string")
/* TODO: Lengths of strings should be size_t */
file->curr_rev_record.comment_size = (uint32_t)HDstrlen(fa->comment) + 1;
}
file->origin_eof = file->header.origin_eof;
file->logical_eof = MAX(file->curr_rev_record.logical_eof, file->logical_eof);
file->logical_eoa = 0;
file->onion_eof = H5FD_get_eoa(file->onion_file, H5FD_MEM_DRAW);
if (TRUE == file->align_history_on_pages)
file->onion_eof = (file->onion_eof + (file->header.page_size - 1)) & (~(file->header.page_size - 1));
ret_value = (H5FD_t *)file;
done:
H5MM_xfree(name_onion);
H5MM_xfree(recovery_file_nameery);
if (config_str && new_fa)
if (fa && fa->backing_fapl_id)
if (H5I_GENPROP_LST == H5I_get_type(fa->backing_fapl_id))
H5I_dec_app_ref(fa->backing_fapl_id);
if ((NULL == ret_value) && file) {
if (file->original_file)
if (H5FD_close(file->original_file) < 0)
HDONE_ERROR(H5E_VFL, H5E_CANTRELEASE, NULL, "can't destroy backing canon")
if (file->onion_file)
if (H5FD_close(file->onion_file) < 0)
HDONE_ERROR(H5E_VFL, H5E_CANTRELEASE, NULL, "can't destroy backing onion")
if (file->recovery_file)
if (H5FD_close(file->recovery_file) < 0)
HDONE_ERROR(H5E_VFL, H5E_CANTRELEASE, NULL, "can't destroy backing recov")
if (file->rev_index)
if (H5FD__onion_revision_index_destroy(file->rev_index) < 0)
HDONE_ERROR(H5E_VFL, H5E_CANTRELEASE, NULL, "can't destroy revision index")
H5MM_xfree(file->history.record_locs);
H5MM_xfree(file->recovery_file_name);
H5MM_xfree(file->curr_rev_record.comment);
H5FL_FREE(H5FD_onion_t, file);
}
H5MM_xfree(new_fa);
FUNC_LEAVE_NOAPI(ret_value);
} /* end H5FD__onion_open() */
/*-----------------------------------------------------------------------------
* Function: H5FD__onion_open_rw
*
* Purpose: Complete onion file-open, handling process for write mode.
*
* Creates recovery file if one does not exist.
* Initializes 'live' revision index.
* Force write-open is not yet supported (recovery provision) TODO
* Establishes write-lock in history header (sets lock flag).
*
* Return: SUCCEED/FAIL
*-----------------------------------------------------------------------------
*/
static herr_t
H5FD__onion_open_rw(H5FD_onion_t *file, unsigned int flags, haddr_t maxaddr, bool new_open)
{
unsigned char *buf = NULL;
size_t size = 0;
uint32_t checksum = 0;
herr_t ret_value = SUCCEED;
FUNC_ENTER_PACKAGE;
/* Guard against simultaneous write-open.
* TODO: support recovery open with force-write-open flag in FAPL info.
*/
if (file->header.flags & H5FD_ONION_HEADER_FLAG_WRITE_LOCK)
HGOTO_ERROR(H5E_VFL, H5E_UNSUPPORTED, FAIL, "can't write-open write-locked file")
/* Copy history to recovery file */
if (NULL ==
(file->recovery_file = H5FD_open(file->recovery_file_name, (flags | H5F_ACC_CREAT | H5F_ACC_TRUNC),
file->fa.backing_fapl_id, maxaddr)))
HGOTO_ERROR(H5E_VFL, H5E_CANTOPENFILE, FAIL, "unable to create recovery file")
if (0 == (size = H5FD__onion_write_history(&file->history, file->recovery_file, 0, 0)))
HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, FAIL, "can't write history to recovery file")
if (size != file->header.history_size)
HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, FAIL, "written history differed from expected size")
/* Set write-lock flag in onion header */
if (NULL == (buf = H5MM_malloc(H5FD_ONION_ENCODED_SIZE_HEADER)))
HGOTO_ERROR(H5E_VFL, H5E_CANTALLOC, FAIL, "can't allocate space for encoded buffer")
file->header.flags |= H5FD_ONION_HEADER_FLAG_WRITE_LOCK;
if (0 == (size = H5FD__onion_header_encode(&file->header, buf, &checksum)))
HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, FAIL, "problem encoding history header")
if (H5FD_write(file->onion_file, H5FD_MEM_DRAW, 0, size, buf) < 0)
HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, FAIL, "can't write updated history header")
/* Prepare revision index and finalize write-mode open */
if (NULL == (file->rev_index = H5FD__onion_revision_index_init(file->fa.page_size)))
HGOTO_ERROR(H5E_VFL, H5E_CANTINIT, FAIL, "can't initialize revision index")
file->curr_rev_record.parent_revision_num = file->curr_rev_record.revision_num;
if (!new_open)
file->curr_rev_record.revision_num += 1;
file->is_open_rw = TRUE;
done:
if (FAIL == ret_value) {
if (file->recovery_file != NULL) {
if (H5FD_close(file->recovery_file) < 0)
HDONE_ERROR(H5E_VFL, H5E_CANTCLOSEFILE, FAIL, "can't close recovery file")
file->recovery_file = NULL;
}
if (file->rev_index != NULL) {
if (H5FD__onion_revision_index_destroy(file->rev_index) < 0)
HDONE_ERROR(H5E_VFL, H5E_CANTRELEASE, FAIL, "can't destroy revision index")
file->rev_index = NULL;
}
}
H5MM_xfree(buf);
FUNC_LEAVE_NOAPI(ret_value);
} /* end H5FD__onion_open_rw() */
/*-----------------------------------------------------------------------------
* Function: H5FD__onion_read
*
* Purpose: Read bytes from an onionized file
*
* Return: SUCCEED/FAIL
*-----------------------------------------------------------------------------
*/
static herr_t
H5FD__onion_read(H5FD_t *_file, H5FD_mem_t type, hid_t H5_ATTR_UNUSED dxpl_id, haddr_t offset, size_t len,
void *_buf_out)
{
H5FD_onion_t *file = (H5FD_onion_t *)_file;
uint64_t page_0 = 0;
size_t n_pages = 0;
uint32_t page_size = 0;
uint32_t page_size_log2 = 0;
size_t bytes_to_read = len;
unsigned char *buf_out = (unsigned char *)_buf_out;
herr_t ret_value = SUCCEED;
FUNC_ENTER_PACKAGE
HDassert(file != NULL);
HDassert(buf_out != NULL);
if ((uint64_t)(offset + len) > file->logical_eoa)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "Read extends beyond addressed space")
if (0 == len)
goto done;
page_size = file->header.page_size;
page_size_log2 = file->curr_rev_record.archival_index.page_size_log2;
page_0 = offset >> page_size_log2;
n_pages = (len + page_size - 1) >> page_size_log2;
/* Read, page-by-page */
for (size_t i = 0; i < n_pages; i++) {
const H5FD_onion_index_entry_t *entry_out = NULL;
haddr_t page_gap_head = 0; /* start of page to start of buffer */
haddr_t page_gap_tail = 0; /* end of buffer to end of page */
size_t page_readsize = 0;
uint64_t page_i = page_0 + i;
if (0 == i) {
page_gap_head = offset & (((uint32_t)1 << page_size_log2) - 1);
/* Check if we need to add an additional page to make up for the page_gap_head */
if (page_gap_head > 0 &&
(page_gap_head + (bytes_to_read % page_size) > page_size || bytes_to_read % page_size == 0)) {
n_pages++;
}
}
if (n_pages - 1 == i)
page_gap_tail = page_size - bytes_to_read - page_gap_head;
page_readsize = (size_t)page_size - page_gap_head - page_gap_tail;
if (TRUE == file->is_open_rw && file->fa.revision_num != 0 &&
H5FD__onion_revision_index_find(file->rev_index, page_i, &entry_out)) {
/* Page exists in 'live' revision index */
if (H5FD_read(file->onion_file, H5FD_MEM_DRAW, entry_out->phys_addr + page_gap_head,
page_readsize, buf_out) < 0)
HGOTO_ERROR(H5E_VFL, H5E_READERROR, FAIL, "can't get working file data")
}
else if (file->fa.revision_num != 0 &&
H5FD__onion_archival_index_find(&file->curr_rev_record.archival_index, page_i, &entry_out)) {
/* Page exists in archival index */
if (H5FD_read(file->onion_file, H5FD_MEM_DRAW, entry_out->phys_addr + page_gap_head,
page_readsize, buf_out) < 0)
HGOTO_ERROR(H5E_VFL, H5E_READERROR, FAIL, "can't get previously-amended file data")
}
else {
/* Page does not exist in either index */
/* Casts prevent truncation */
haddr_t addr_start = (haddr_t)page_i * (haddr_t)page_size + (haddr_t)page_gap_head;
haddr_t overlap_size = (addr_start > file->origin_eof) ? 0 : file->origin_eof - addr_start;
haddr_t read_size = MIN(overlap_size, page_readsize);
/* Get all original bytes in page range */
if ((read_size > 0) && H5FD_read(file->original_file, type, addr_start, read_size, buf_out) < 0) {
HGOTO_ERROR(H5E_VFL, H5E_READERROR, FAIL, "can't get original file data")
}
/* Fill with 0s any gaps after end of original bytes
* and before end of page.
*/
for (size_t j = read_size; j < page_readsize; j++)
buf_out[j] = 0;
}
buf_out += page_readsize;
bytes_to_read -= page_readsize;
} /* end for each page in range */
HDassert(0 == bytes_to_read);
done:
FUNC_LEAVE_NOAPI(ret_value);
} /* end H5FD__onion_read() */
/*-----------------------------------------------------------------------------
* Function: H5FD__onion_set_eoa
*
* Purpose: Set end-of-address marker of the logical file.
*
* Return: SUCCEED/FAIL
*-----------------------------------------------------------------------------
*/
static herr_t
H5FD__onion_set_eoa(H5FD_t *_file, H5FD_mem_t H5_ATTR_UNUSED type, haddr_t addr)
{
H5FD_onion_t *file = (H5FD_onion_t *)_file;
FUNC_ENTER_PACKAGE_NOERR;
file->logical_eoa = addr;
FUNC_LEAVE_NOAPI(SUCCEED);
} /* end H5FD__onion_set_eoa() */
/*-----------------------------------------------------------------------------
* Function: H5FD__onion_write
*
* Purpose: Write bytes to an onionized file
*
* Return: SUCCEED/FAIL
*-----------------------------------------------------------------------------
*/
static herr_t
H5FD__onion_write(H5FD_t *_file, H5FD_mem_t type, hid_t H5_ATTR_UNUSED dxpl_id, haddr_t offset, size_t len,
const void *_buf)
{
H5FD_onion_t *file = (H5FD_onion_t *)_file;
uint64_t page_0 = 0;
size_t n_pages = 0;
unsigned char *page_buf = NULL;
uint32_t page_size = 0;
uint32_t page_size_log2 = 0;
size_t bytes_to_write = len;
const unsigned char *buf = (const unsigned char *)_buf;
herr_t ret_value = SUCCEED;
FUNC_ENTER_PACKAGE
HDassert(file != NULL);
HDassert(buf != NULL);
HDassert(file->rev_index != NULL);
HDassert((uint64_t)(offset + len) <= file->logical_eoa);
if (FALSE == file->is_open_rw)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "Write not allowed if file not opened in write mode")
if (0 == len)
goto done;
page_size = file->header.page_size;
page_size_log2 = file->curr_rev_record.archival_index.page_size_log2;
page_0 = offset >> page_size_log2;
n_pages = (len + page_size - 1) >> page_size_log2;
if (NULL == (page_buf = H5MM_calloc(page_size * sizeof(unsigned char))))
HGOTO_ERROR(H5E_VFL, H5E_CANTALLOC, FAIL, "cannot allocate temporary buffer")
/* Write, page-by-page */
for (size_t i = 0; i < n_pages; i++) {
const unsigned char *write_buf = buf;
H5FD_onion_index_entry_t new_entry;
const H5FD_onion_index_entry_t *entry_out = NULL;
haddr_t page_gap_head = 0; /* start of page to start of buffer */
haddr_t page_gap_tail = 0; /* end of buffer to end of page */
size_t page_n_used = 0; /* nbytes from buffer for this page-write */
uint64_t page_i = page_0 + i;
if (0 == i) {
page_gap_head = offset & (((uint32_t)1 << page_size_log2) - 1);
/* If we have a page_gap_head and the number of bytes to write is
* evenly divisible by the page size we need to add an additional
* page to make up for the page_gap_head
*/
if (page_gap_head > 0 && (page_gap_head + (bytes_to_write % page_size) > page_size ||
bytes_to_write % page_size == 0)) {
n_pages++;
}
}
if (n_pages - 1 == i)
page_gap_tail = page_size - bytes_to_write - page_gap_head;
page_n_used = page_size - page_gap_head - page_gap_tail;
/* Modify page in revision index, if present */
if (H5FD__onion_revision_index_find(file->rev_index, page_i, &entry_out)) {
if (page_gap_head | page_gap_tail) {
/* Copy existing page verbatim. */
if (H5FD_read(file->onion_file, H5FD_MEM_DRAW, entry_out->phys_addr, page_size, page_buf) < 0)
HGOTO_ERROR(H5E_VFL, H5E_READERROR, FAIL, "can't get working file data")
/* Overlay delta from input buffer onto page buffer. */
HDmemcpy(page_buf + page_gap_head, buf, page_n_used);
write_buf = page_buf;
} /* end if partial page */
if (H5FD_write(file->onion_file, H5FD_MEM_DRAW, entry_out->phys_addr, page_size, write_buf) < 0)
HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, FAIL, "write amended page data to backing file")
buf += page_n_used; /* overflow never touched */
bytes_to_write -= page_n_used;
continue;
} /* end if page exists in 'live' revision index */
if (page_gap_head || page_gap_tail) {
/* Fill gaps with existing data or zeroes. */
if (H5FD__onion_archival_index_find(&file->curr_rev_record.archival_index, page_i, &entry_out)) {
/* Page exists in archival index */
/* Copy existing page verbatim */
if (H5FD_read(file->onion_file, H5FD_MEM_DRAW, entry_out->phys_addr, page_size, page_buf) < 0)
HGOTO_ERROR(H5E_VFL, H5E_READERROR, FAIL, "can't get previously-amended data")
}
else {
haddr_t addr_start = (haddr_t)(page_i * page_size);
haddr_t overlap_size = (addr_start > file->origin_eof) ? 0 : file->origin_eof - addr_start;
haddr_t read_size = MIN(overlap_size, page_size);
/* Get all original bytes in page range */
if ((read_size > 0) &&
H5FD_read(file->original_file, type, addr_start, read_size, page_buf) < 0) {
HGOTO_ERROR(H5E_VFL, H5E_READERROR, FAIL, "can't get original file data")
}
/* Fill with 0s any gaps after end of original bytes
* or start of page and before start of new data.
*/
for (size_t j = read_size; j < page_gap_head; j++)
page_buf[j] = 0;
/* Fill with 0s any gaps after end of original bytes
* or end of new data and before end of page.
*/
for (size_t j = MAX(read_size, page_size - page_gap_tail); j < page_size; j++)
page_buf[j] = 0;
} /* end if page exists in neither index */
/* Copy input buffer to temporary page buffer */
HDassert((page_size - page_gap_head) >= page_n_used);
HDmemcpy(page_buf + page_gap_head, buf, page_n_used);
write_buf = page_buf;
} /* end if data range does not span entire page */
new_entry.logical_page = page_i;
new_entry.phys_addr = file->onion_eof;
if (H5FD_set_eoa(file->onion_file, H5FD_MEM_DRAW, file->onion_eof + page_size) < 0)
HGOTO_ERROR(H5E_VFL, H5E_CANTSET, FAIL, "can't modify EOA for new page amendment")
if (H5FD_write(file->onion_file, H5FD_MEM_DRAW, file->onion_eof, page_size, write_buf) < 0)
HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, FAIL, "write amended page data to backing file")
if (H5FD__onion_revision_index_insert(file->rev_index, &new_entry) < 0)
HGOTO_ERROR(H5E_VFL, H5E_CANTINSERT, FAIL, "can't insert new index entry into revision index")
file->onion_eof += page_size;
buf += page_n_used; /* possible overflow never touched */
bytes_to_write -= page_n_used;
} /* end for each page to write */
HDassert(0 == bytes_to_write);
file->logical_eof = MAX(file->logical_eof, (offset + len));
done:
H5MM_xfree(page_buf);
FUNC_LEAVE_NOAPI(ret_value);
} /* end H5FD__onion_write() */
/*-------------------------------------------------------------------------
* Function: H5FD__onion_ctl
*
* Purpose: Onion VFD version of the ctl callback.
*
* The desired operation is specified by the op_code
* parameter.
*
* The flags parameter controls management of op_codes that
* are unknown to the callback
*
* The input and output parameters allow op_code specific
* input and output
*
* Return: SUCCEED/FAIL
*-------------------------------------------------------------------------
*/
static herr_t
H5FD__onion_ctl(H5FD_t *_file, uint64_t op_code, uint64_t flags, const void H5_ATTR_UNUSED *input,
void **output)
{
H5FD_onion_t *file = (H5FD_onion_t *)_file;
herr_t ret_value = SUCCEED;
FUNC_ENTER_PACKAGE
/* Sanity checks */
HDassert(file);
switch (op_code) {
case H5FD_CTL_GET_NUM_REVISIONS:
if (!output || !*output)
HGOTO_ERROR(H5E_VFL, H5E_FCNTL, FAIL, "the output parameter is null")
**((uint64_t **)output) = file->history.n_revisions;
break;
/* Unknown op code */
default:
if (flags & H5FD_CTL_FAIL_IF_UNKNOWN_FLAG)
HGOTO_ERROR(H5E_VFL, H5E_FCNTL, FAIL, "unknown op_code and fail if unknown flag is set")
break;
}
done:
FUNC_LEAVE_NOAPI(ret_value)
} /* end H5FD__onion_ctl() */
/*-------------------------------------------------------------------------
* Function: H5FDget_onion_revision_count
*
* Purpose: Get the number of revisions in an onion file
*
* Return: SUCCEED/FAIL
*-------------------------------------------------------------------------
*/
herr_t
H5FDonion_get_revision_count(const char *filename, hid_t fapl_id, uint64_t *revision_count /*out*/)
{
H5P_genplist_t *plist = NULL;
H5FD_t *file = NULL;
herr_t ret_value = SUCCEED;
FUNC_ENTER_API(FAIL)
H5TRACE3("e", "*six", filename, fapl_id, revision_count);
/* Check args */
if (!filename || !HDstrcmp(filename, ""))
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "not a valid file name")
if (!revision_count)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "revision count can't be null")
/* Make sure using the correct driver */
if (NULL == (plist = H5P_object_verify(fapl_id, H5P_FILE_ACCESS)))
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "not a valid FAPL ID")
if (H5FD_ONION != H5P_peek_driver(plist))
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "not a Onion VFL driver")
/* Open the file with the driver */
if (NULL == (file = H5FD_open(filename, H5F_ACC_RDONLY, fapl_id, HADDR_UNDEF)))
HGOTO_ERROR(H5E_VFL, H5E_CANTOPENFILE, FAIL, "unable to open file with onion driver")
/* Call the private function */
if (H5FD__get_onion_revision_count(file, revision_count) < 0)
HGOTO_ERROR(H5E_VFL, H5E_CANTGET, FAIL, "failed to get the number of revisions")
done:
/* Close H5FD_t structure pointer */
if (file && H5FD_close(file) < 0)
HGOTO_ERROR(H5E_VFL, H5E_CANTCLOSEFILE, FAIL, "unable to close file")
FUNC_LEAVE_API(ret_value)
}
/*-------------------------------------------------------------------------
* Function: H5FD__get_onion_revision_count
*
* Purpose: Private version of H5FDget_onion_revision_count()
*
* Return: SUCCEED/FAIL
*-------------------------------------------------------------------------
*/
static herr_t
H5FD__get_onion_revision_count(H5FD_t *file, uint64_t *revision_count)
{
uint64_t op_code;
uint64_t flags;
herr_t ret_value = SUCCEED;
FUNC_ENTER_PACKAGE
HDassert(file);
HDassert(revision_count);
op_code = H5FD_CTL_GET_NUM_REVISIONS;
flags = H5FD_CTL_FAIL_IF_UNKNOWN_FLAG;
/* Get the number of revisions via the ctl callback */
if (H5FD_ctl(file, op_code, flags, NULL, (void **)&revision_count) < 0)
HGOTO_ERROR(H5E_VFL, H5E_FCNTL, FAIL, "VFD ctl request failed")
done:
FUNC_LEAVE_NOAPI(ret_value)
}
/*-----------------------------------------------------------------------------
* Function: H5FD__onion_write_final_history
*
* Purpose: Write final history to appropriate backing file on file close
*
* Return: SUCCEED/FAIL
*-----------------------------------------------------------------------------
*/
herr_t
H5FD__onion_write_final_history(H5FD_onion_t *file)
{
size_t size = 0;
herr_t ret_value = SUCCEED;
FUNC_ENTER_PACKAGE;
/* TODO: history EOF may not be correct (under what circumstances?) */
if (0 == (size = H5FD__onion_write_history(&(file->history), file->onion_file, file->onion_eof,
file->onion_eof)))
HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, FAIL, "can't write final history")
if (size != file->header.history_size)
HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, FAIL, "written history differed from expected size")
/* Is last write operation to history file; no need to extend to page
* boundary if set to page-align.
*/
file->onion_eof += size;
done:
FUNC_LEAVE_NOAPI(ret_value);
} /* end H5FD__onion_write_final_history() */
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。