/**************************************************************************** * OBIDMS_column functions * ****************************************************************************/ /** * @file OBIDMS_column.h * @author Celine Mercier * @date 22 May 2015 * @brief Functions for the shared elements of all the OBIColumn structures. */ #include #include #include #include #include #include #include #include /* mmap() is defined in this header */ #include "obidmscolumn.h" #include "obidmscolumngroup.h" #include "obidms.h" #include "obitypes.h" #include "obierrno.h" #include "obilittlebigman.h" #include "private_at_functions.h" /************************************************************************** * * D E C L A R A T I O N O F T H E P R I V A T E F U N C T I O N S * **************************************************************************/ /** * @brief Internal function building the file name for a column. * * The function builds the file name corresponding to a column of an OBIDMS. * * @warning The returned pointer has to be freed by the caller. * * @param column_name the name of the OBIDMS column. * * @return a pointer to the column file name * @retvalue NULL if an error occurs * * ###Error values * - OBIDMS_MEMORY_ERROR : something wrong occurred during memory allocation. * * @since May 2015 * @author Eric Coissac (eric.coissac@metabarcoding.org) */ static char *build_column_file_name(const char *column_name, obiversion_t version_number); /** * @brief Internal function building the file name for a column version file. * * The column version file indicates the latest version number for a column. * This function returns the name of the file storing this information. * * @warning The returned pointer has to be freed by the caller. * * @param column_name the name of the OBIDMS column. * * @return a pointer to the version file name * @retvalue NULL if an error occurs * * ###Error values * - OBIDMS_MEMORY_ERROR : something wrong occurs during memory allocation. * * @since May 2015 * @author Eric Coissac (eric.coissac@metabarcoding.org) */ static char *build_version_file_name(const char *column_name); /** * @brief Internal function returning a new column version number * in the `dms` database * * @param dms a pointer as returned by obi_create_dms() or obi_open_dms() * @param column_name the name of the column * @param block is the call is blocking or not * - `true` the call is blocking * - `false` the call is not blocking * * @return the bigger version number used for this column * @retvalue -1 if the column does not exist * * @since May 2015 * @author Eric Coissac (eric.coissac@metabarcoding.org) */ static obiversion_t obi_get_new_version_number(OBIDMS_column_group_p column_group, bool block); /** * @brief Internal function creating a new column version file * in the `dms` database * * The new file is initialized with the minimum version number `0`. * * @param dms a pointer as returned by obi_create_dms() or obi_open_dms() * @param column_name the name of the column * * @return the next usable version number for this column : `0` * @retvalue -1 if the column does not exist * * @since May 2015 * @author Eric Coissac (eric.coissac@metabarcoding.org) */ static int create_version_file(OBIDMS_column_group_p column_group); /************************************************************************ * * D E F I N I T I O N O F T H E P R I V A T E F U N C T I O N S * ************************************************************************/ static char *build_column_file_name(const char *column_name, obiversion_t version_number) { char *filename; // Build the database directory name if (asprintf(&filename,"%s@%d.odc", column_name, version_number) < 0) { obi_set_errno(OBICOL_MEMORY_ERROR); return NULL; } return filename; } static char *build_version_file_name(const char *column_name) { char *filename; // Build the database directory name if (asprintf(&filename,"%s.odv", column_name) < 0) { obi_set_errno(OBICOL_MEMORY_ERROR); return NULL; } return filename; } static obiversion_t obi_get_new_version_number(OBIDMS_column_group_p column_group, bool block) { off_t loc_size; obiversion_t new_version_number; char* version_file_name; int column_dir_file_descriptor; int version_file_descriptor; bool little_endian; int lock_mode; new_version_number = 0; loc_size = sizeof(bool) + sizeof(obiversion_t); // Select the correct lockf operation according to the blocking mode if (block) lock_mode=F_LOCK; else lock_mode=F_TLOCK; // Build the version file name version_file_name = build_version_file_name(column_group->column_name); if (version_file_name == NULL) return -1; // Get the file descriptor associated to the column group directory column_dir_file_descriptor = dirfd(column_group->directory); if (column_dir_file_descriptor < 0) { obi_set_errno(OBICOL_UNKNOWN_ERROR); free(version_file_name); return -1; } // Open the version file version_file_descriptor = private_openat(column_dir_file_descriptor, version_file_name, O_RDWR); if (version_file_descriptor < 0) { free(version_file_name); //close(column_dir_file_descriptor); if (errno == ENOENT) return create_version_file(column_group); else { obi_set_errno(OBICOL_UNKNOWN_ERROR); return -1; } } // Test if the version file size is ok if (lseek(version_file_descriptor, 0, SEEK_END) < loc_size) { obi_set_errno(OBICOL_UNKNOWN_ERROR); close(version_file_descriptor); close(column_dir_file_descriptor); free(version_file_name); return -1; } // Prepare the file for locking if (lseek(version_file_descriptor, 0, SEEK_SET) != 0) { obi_set_errno(OBICOL_UNKNOWN_ERROR); close(version_file_descriptor); close(column_dir_file_descriptor); free(version_file_name); return -1; } // Lock the file if (lockf(version_file_descriptor, lock_mode, loc_size) < 0) { obi_set_errno(OBICOL_UNKNOWN_ERROR); close(version_file_descriptor); close(column_dir_file_descriptor); free(version_file_name); return -1; } // Read the endianness of the file if (read(version_file_descriptor, &little_endian, sizeof(bool)) < sizeof(bool)) { obi_set_errno(OBICOL_UNKNOWN_ERROR); close(version_file_descriptor); close(column_dir_file_descriptor); free(version_file_name); return -1; } // Check if endianness is correct if (little_endian != obi_is_little_endian()) { obi_set_errno(OBICOL_BAD_ENDIAN_ERROR); close(version_file_descriptor); close(column_dir_file_descriptor); free(version_file_name); return -1; } // Read the current version number if (read(version_file_descriptor, &new_version_number, sizeof(obiversion_t)) < sizeof(obiversion_t)) { obi_set_errno(OBICOL_UNKNOWN_ERROR); close(version_file_descriptor); close(column_dir_file_descriptor); free(version_file_name); return -1; } new_version_number++; // Write the new version number if (lseek(version_file_descriptor, sizeof(bool), SEEK_SET) != sizeof(bool)) { obi_set_errno(OBICOL_UNKNOWN_ERROR); close(version_file_descriptor); close(column_dir_file_descriptor); free(version_file_name); return -1; } if (write(version_file_descriptor, &new_version_number, sizeof(obiversion_t)) < sizeof(obiversion_t)) { obi_set_errno(OBICOL_UNKNOWN_ERROR); close(version_file_descriptor); close(column_dir_file_descriptor); free(version_file_name); return -1; } // Prepare for unlocking if (lseek(version_file_descriptor, 0, SEEK_SET) != 0) { obi_set_errno(OBICOL_UNKNOWN_ERROR); close(version_file_descriptor); close(column_dir_file_descriptor); free(version_file_name); return -1; } // Unlock the file if (lockf(version_file_descriptor, F_ULOCK, loc_size) < 0) { obi_set_errno(OBICOL_UNKNOWN_ERROR); close(version_file_descriptor); close(column_dir_file_descriptor); free(version_file_name); return -1; } close(version_file_descriptor); //close(column_dir_file_descriptor); free(version_file_name); return new_version_number; } static int create_version_file(OBIDMS_column_group_p column_group) { off_t loc_size; obiversion_t version_number; char* version_file_name; int column_dir_file_descriptor; int version_file_descriptor; bool little_endian; loc_size = sizeof(bool) + sizeof(obiversion_t); version_number = 0; version_file_name = build_version_file_name(column_group->column_name); if (version_file_name == NULL) return -1; // Get the file descriptor associated to the column group directory column_dir_file_descriptor = dirfd(column_group->directory); if (column_dir_file_descriptor < 0) { obi_set_errno(OBICOL_UNKNOWN_ERROR); free(version_file_name); return -1; } // Get the file descriptor associated to the version file version_file_descriptor = private_openat(column_dir_file_descriptor, version_file_name, O_RDWR | O_CREAT); if (version_file_descriptor < 0) { obi_set_errno(OBICOL_UNKNOWN_ERROR); free(version_file_name); close(column_dir_file_descriptor); return -1; } // Lock the file if (lockf(version_file_descriptor, F_LOCK, loc_size) < 0) { obi_set_errno(OBICOL_UNKNOWN_ERROR); free(version_file_name); close(version_file_descriptor); close(column_dir_file_descriptor); return -1; } // Truncate the version file to the right size if (ftruncate(version_file_descriptor, loc_size) < 0) { obi_set_errno(OBICOL_UNKNOWN_ERROR); free(version_file_name); close(version_file_descriptor); close(column_dir_file_descriptor); return -1; } // Position offset to 0 to prepare for writing if (lseek(version_file_descriptor, 0, SEEK_SET) != 0) { obi_set_errno(OBICOL_UNKNOWN_ERROR); free(version_file_name); close(version_file_descriptor); close(column_dir_file_descriptor); return -1; } little_endian = obi_is_little_endian(); // Write endianness if (write(version_file_descriptor, &little_endian, sizeof(bool)) < sizeof(bool)) { obi_set_errno(OBICOL_UNKNOWN_ERROR); free(version_file_name); close(version_file_descriptor); close(column_dir_file_descriptor); return -1; } // Write version number if (write(version_file_descriptor, &version_number, sizeof(obiversion_t)) < sizeof(obiversion_t)) { obi_set_errno(OBICOL_UNKNOWN_ERROR); free(version_file_name); close(version_file_descriptor); close(column_dir_file_descriptor); return -1; } // Prepare for unlocking if (lseek(version_file_descriptor, 0, SEEK_SET) != 0) { obi_set_errno(OBICOL_UNKNOWN_ERROR); free(version_file_name); close(version_file_descriptor); close(column_dir_file_descriptor); return -1; } // Unlock the file if (lockf(version_file_descriptor, F_ULOCK, loc_size) < 0) { obi_set_errno(OBICOL_UNKNOWN_ERROR); free(version_file_name); close(version_file_descriptor); close(column_dir_file_descriptor); return -1; } close(version_file_descriptor); //close(column_dir_file_descriptor); free(version_file_name); return version_number; } /********************************************************************** * * D E F I N I T I O N O F T H E P U B L I C F U N C T I O N S * **********************************************************************/ obiversion_t obi_get_latest_version_number(OBIDMS_column_group_p column_group) { off_t loc_size; obiversion_t latest_version_number; char * version_file_name; int column_dir_file_descriptor; int version_file_descriptor; bool little_endian; loc_size = sizeof(bool) + sizeof(obiversion_t); latest_version_number = 0; version_file_name = build_version_file_name(column_group->column_name); if (version_file_name==NULL) return -1; // Get the file descriptor associated to the column group directory column_dir_file_descriptor = dirfd(column_group->directory); if (column_dir_file_descriptor < 0) { obi_set_errno(OBICOL_UNKNOWN_ERROR); free(version_file_name); return -1; } // Get the file descriptor associated to the version file version_file_descriptor = private_openat(column_dir_file_descriptor, version_file_name, O_RDONLY); if (version_file_descriptor < 0) { obi_set_errno(OBICOL_UNKNOWN_ERROR); free(version_file_name); close(column_dir_file_descriptor); return -1; } // Check that the version file size is ok if (lseek(version_file_descriptor, 0, SEEK_END) < loc_size) { obi_set_errno(OBICOL_UNKNOWN_ERROR); free(version_file_name); close(version_file_descriptor); close(column_dir_file_descriptor); return -1; } // Set the offset to 0 in the version file if (lseek(version_file_descriptor, 0, SEEK_SET) != 0) { obi_set_errno(OBICOL_UNKNOWN_ERROR); free(version_file_name); close(version_file_descriptor); close(column_dir_file_descriptor); return -1; } // Read the endianness if (read(version_file_descriptor, &little_endian, sizeof(bool)) < sizeof(bool)) { obi_set_errno(OBICOL_UNKNOWN_ERROR); free(version_file_name); close(version_file_descriptor); close(column_dir_file_descriptor); return -1; } // Verify the endianness if (little_endian != obi_is_little_endian()) { obi_set_errno(OBICOL_BAD_ENDIAN_ERROR); free(version_file_name); close(version_file_descriptor); close(column_dir_file_descriptor); return -1; } // Read the latest version number if (read(version_file_descriptor, &latest_version_number, sizeof(obiversion_t)) < sizeof(obiversion_t)) { obi_set_errno(OBICOL_UNKNOWN_ERROR); free(version_file_name); close(version_file_descriptor); close(column_dir_file_descriptor); return -1; } free(version_file_name); close(version_file_descriptor); //close(column_dir_file_descriptor); return latest_version_number; } size_t obi_get_platform_header_size() { return getpagesize() * 1; } OBIDMS_column_p obi_create_column(OBIDMS_p dms, const char *column_name, OBIType_t type, size_t nb_elements) { OBIDMS_column_p new_column; OBIDMS_column_group_p column_group; OBIDMS_column_header_p header; size_t file_size; obiversion_t version_number; char* column_file_name; int column_file_descriptor; int column_dir_file_descriptor; size_t header_size; size_t data_size; new_column = NULL; // Get the column group structure associated to the column column_group = obi_column_group(dms, column_name); if (column_group == NULL) return NULL; // Get the file descriptor associated to the column group directory column_dir_file_descriptor = dirfd(column_group->directory); if (column_dir_file_descriptor < 0) { obi_set_errno(OBICOLDIR_UNKNOWN_ERROR); obi_close_column_group(column_group); return NULL; } // Calculate the size needed header_size = obi_get_platform_header_size(); data_size = obi_array_sizeof(type, nb_elements); file_size = header_size + data_size; // Get the latest version number version_number = obi_get_new_version_number(column_group, true); if (version_number < 0) { obi_close_column_group(column_group); close(column_dir_file_descriptor); return NULL; } // Get the column file name column_file_name = build_column_file_name(column_name, version_number); if (column_file_name == NULL) { obi_close_column_group(column_group); close(column_dir_file_descriptor); return NULL; } // Open the column file column_file_descriptor = private_openat(column_dir_file_descriptor, column_file_name, O_RDWR | O_CREAT); if (column_file_descriptor < 0) { obi_set_errno(OBICOL_UNKNOWN_ERROR); obi_close_column_group(column_group); close(column_dir_file_descriptor); free(column_file_name); return NULL; } // Truncate the column file to the right size if (ftruncate(column_file_descriptor, file_size) < 0) { obi_set_errno(OBICOL_UNKNOWN_ERROR); obi_close_column_group(column_group); close(column_dir_file_descriptor); close(column_file_descriptor); free(column_file_name); return NULL; } // Allocate the memory for the column structure new_column = (OBIDMS_column_p) malloc(sizeof(OBIDMS_column_t)); if (new_column == NULL) { obi_set_errno(OBICOL_UNKNOWN_ERROR); obi_close_column_group(column_group); close(column_dir_file_descriptor); close(column_file_descriptor); free(column_file_name); return NULL; } // Fill the column structure new_column->dms = dms; new_column->column_group = column_group; new_column->header = mmap(NULL, header_size, PROT_READ | PROT_WRITE, MAP_SHARED, column_file_descriptor, 0 ); if (new_column->header == MAP_FAILED) { obi_set_errno(OBICOL_UNKNOWN_ERROR); obi_close_column_group(column_group); close(column_dir_file_descriptor); close(column_file_descriptor); free(column_file_name); free(new_column); return NULL; } new_column->data = mmap(NULL, data_size, PROT_READ | PROT_WRITE, MAP_SHARED, column_file_descriptor, header_size ); if (new_column->data == MAP_FAILED) { munmap(new_column->header, header_size); obi_set_errno(OBICOL_UNKNOWN_ERROR); obi_close_column_group(column_group); close(column_dir_file_descriptor); close(column_file_descriptor); free(column_file_name); free(new_column); return NULL; } new_column->writable = true; header = new_column->header; header->little_endian = obi_is_little_endian(); header->header_size = header_size; header->line_count = nb_elements; header->lines_used = 0; header->data_type = type; header->creation_date = time(NULL); header->version = version_number; header->comments[0] = 0x0; strncpy(header->name, column_name, OBIDMS_MAX_COLNAME); free(column_file_name); //close(column_dir_file_descriptor); close(column_file_descriptor); return new_column; }