/* * Copyright (C) 2007-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ /** \file osl_core.h Object storage layer details, not visible to users. */ #include "rbtree.h" #include "hash.h" __must_check __printf_1_2 __malloc char *make_message(const char *fmt, ...); /** Internal representation of a column of an osl table. */ struct osl_column { /** The memory mapping of this comumn (only used for mapped columns). */ struct osl_object data_map; /** The root of the rbtree (only used for columns with rbtrees). */ struct rb_root rbtree; /** The index in the array of rb nodes (only used for columns with rbtrees). */ unsigned rbtree_num; /** Index for volatile_objects of struct osl_row. */ unsigned volatile_num; /** * Starting point of the data for this column within the index * (only used for mapped columns). */ uint16_t index_offset; /** * The hash value of the name of this column. * * This is only used for mapped and disk storage columns). */ HASH_TYPE name_hash[HASH_SIZE]; }; /** Internal representation of an osl table */ struct osl_table { /** Pointer to the table description */ const struct osl_table_description *desc; /** * The CURRENT_TABLE_VERSION value of the library which created the * table. This value is stored in the index header at table creation * time. When the table is opened, the field is initialized from the * on-disk value. */ uint8_t version; /** The size of the index header of this table. */ uint16_t index_header_size; /** Contains the mapping of the table's index file */ struct osl_object index_map; /** Total number of rows, including invalid rows. */ unsigned num_rows; /** Keeps track of the number of invalid rows in the table. */ uint32_t num_invalid_rows; /** Number of columns of type \p OSL_MAPPED_STORAGE. */ unsigned num_mapped_columns; /** Number of columns of type \p OSL_NO_STORAGE. */ unsigned num_volatile_columns; /** Number of columns of type \p OSL_DISK_STORAGE. */ unsigned num_disk_storage_columns; /** Number of columns with associated rbtree. */ unsigned num_rbtrees; /** * The number of the column that determines the name of the disk * storage objects. */ unsigned disk_storage_name_column; /** The number of bytes of an index entry of a row. */ unsigned row_index_size; /** Pointer to the internal representation of the columns. */ struct osl_column *columns; }; /** Internal representation of a row of an osl table */ struct osl_row { /** * The row number. * * It is only used if there is at least one mapped column. */ off_t num; /** Array of size \a num_volatile_columns. */ struct osl_object *volatile_objects; }; int read_table_desc(struct osl_object *map, struct osl_table_description *desc); int init_table_structure(const struct osl_table_description *desc, struct osl_table **table_ptr); int row_is_invalid(struct osl_table *t, uint32_t row_num); int get_mapped_object(const struct osl_table *t, unsigned col_num, uint32_t row_num, struct osl_object *obj); int unmap_table(struct osl_table *t, enum osl_close_flags flags); int init_rbtrees(struct osl_table *t); /** * Flags to be specified for map_table(). * * \sa map_table(). */ enum map_table_flags { /** * Check whether the entries in the table index match the entries in * the table description. */ MAP_TBL_FL_VERIFY_INDEX = 1, /** Do not complain even if the dirty flag is set. */ MAP_TBL_FL_IGNORE_DIRTY = 2, /** Use read-only mappings. */ MAP_TBL_FL_MAP_RDONLY = 4 }; int map_table(struct osl_table *t, enum map_table_flags flags); void clear_rbtrees(struct osl_table *t); int mark_row_invalid(struct osl_table *t, uint32_t row_num); /** * Get the description of a column by column number * * \param d Pointer to the table description. * \param col_num The number of the column to get the description for. * * \return The table description. * * \sa struct osl_column_description. */ _static_inline_ struct osl_column_description *get_column_description( const struct osl_table_description *d, unsigned col_num) { return &d->column_descriptions[col_num]; } /** * Format of the header of the index file of an osl table. * * Bytes 16-31 are currently unused. * * \sa enum index_column_desc_offsets, HASH_SIZE, osl_table_flags. */ enum index_header_offsets { /** Bytes 0-8: PARASLASH. */ IDX_OSL_MAGIC = 0, /** Byte 9: Dirty flag (nonzero if table is mapped). */ IDX_DIRTY_FLAG = 9, /** Byte 10: osl table version number. */ IDX_VERSION = 10, /** Byte 11: Table flags.*/ IDX_TABLE_FLAGS = 11, /** Byte 12,13: Number of columns. */ IDX_NUM_COLUMNS, /** Byte 14,15 Size of the index header. */ IDX_HEADER_SIZE = 14, /** Column descriptions start here. */ IDX_COLUMN_DESCRIPTIONS = 32 }; /** * Format of the column description in the index header. * * \sa index_header_offsets. */ enum index_column_desc_offsets { /** Bytes 0,1: Storage_type. */ IDX_CD_STORAGE_TYPE = 0, /** Bytes 2,3: Storage_flags. */ IDX_CD_STORAGE_FLAGS = 2, /** Bytes 4 - 7: Data_size (only used for fixed size columns). */ IDX_CD_DATA_SIZE = 4, /** Bytes 8 - ... Name of the column. */ IDX_CD_NAME = 8, }; /** Magic string contained in the header of the index file of each osl table. */ #define OSL_MAGIC "PARASLASH" /** * The minimal number of bytes for a column in the index header. * * The column name starts at byte IDX_CD_NAME and must at least contain one * character, plus the terminating NULL byte. */ #define MIN_IDX_COLUMN_DESCRIPTION_SIZE (IDX_CD_NAME + 2) /** * Get the number of bytes used for a column in the index header. * * \param name The name of the column. * * The amount of space used for a column in the index header of a table depends * on the (length of the) name of the column. * * \return The total number of bytes needed to store one column of this name. */ _static_inline_ size_t index_column_description_size(const char *name) { return MIN_IDX_COLUMN_DESCRIPTION_SIZE + strlen(name) - 1; } /* * The version used by this instance of the library. Written to the index of * newly created tables. */ #define CURRENT_TABLE_VERSION 3 /* * The lowest table version this library understands. On open, if * current_version(table) < min_version(lib) the osl_open_table() call * fails. */ #define MIN_TABLE_VERSION 1 /** An index header must be at least that many bytes long. */ #define MIN_INDEX_HEADER_SIZE(num_cols) (MIN_IDX_COLUMN_DESCRIPTION_SIZE \ * num_cols + IDX_COLUMN_DESCRIPTIONS) /** * Get the number of rows from the size of the memory mapping. * * \param t Pointer to an open table. * * \return The number of rows, including invalid rows. */ _static_inline_ unsigned table_num_rows(const struct osl_table *t) { return (t->index_map.size - t->index_header_size) / t->row_index_size; } /** * Get the path of the index file from a table description. * * \param d The table description. * * \return The full path of the index file. Result must be freed by * the caller. */ _static_inline_ char *index_filename(const struct osl_table_description *d) { return make_message("%s/%s/index", d->dir, d->name); } /** * Get the path of storage of a column. * * \param t Pointer to an initialized table. * \param col_num The number of the column to get the path for. * * \return The full path of the mapped data file (mapped storage) or the * data directory (disk storage). Result must be freed by the caller. * * \sa osl_storage_type. */ _static_inline_ char *column_filename(const struct osl_table *t, unsigned col_num) { char asc[2 * HASH_SIZE + 1]; hash_to_asc(t->columns[col_num].name_hash, asc); return make_message("%s/%s/%s", t->desc->dir, t->desc->name, asc); } /** * Get the start of an index entry * * \param t Pointer to a table which has been mapped. * \param row_num The number of the row whose index entry should be retrieved. * \param row_index Result pointer. * * \return Positive on success, \p -E_INDEX_CORRUPTION otherwise. * * \sa get_cell_index(). */ _static_inline_ int get_row_index(const struct osl_table *t, uint32_t row_num, char **row_index) { uint32_t index_offset; index_offset = t->index_header_size + t->row_index_size * row_num; if (index_offset + 8 > t->index_map.size) { *row_index = NULL; return -E_OSL_INDEX_CORRUPTION; } *row_index = (char *)(t->index_map.data) + index_offset; return 1; } /** * Get the index entry of a row/column pair. * * \param t Pointer to a table which has been mapped. * \param row_num The number of the row whose index entry should be retrieved. * \param col_num The number of the column whose index entry should be retrieved. * \param cell_index Result pointer. * * \return Positive on success, \p -E_INDEX_CORRUPTION otherwise. * * \sa get_row_index(). */ _static_inline_ int get_cell_index(const struct osl_table *t, uint32_t row_num, uint32_t col_num, char **cell_index) { int ret = get_row_index(t, row_num, cell_index); if (ret < 0) return ret; *cell_index += t->columns[col_num].index_offset; return ret; } /** * Change an index entry of a column after object was added. * * \param row_index Pointer to the index of the row to update. * \param col Pointer to the column. * \param map_size The new size of the data file. * \param object_size The size of the object just appended to the data file. * * This is called right after an object was appended to the data file for a * mapped column. * * \sa get_row_index(). */ _static_inline_ void update_cell_index(char *row_index, struct osl_column *col, uint32_t map_size, uint32_t object_size) { write_u32(row_index + col->index_offset, map_size - object_size); write_u32(row_index + col->index_offset + 4, object_size); } /** * Get the full path of a disk storage object * * \param t Pointer to an initialized table. * \param col_num The number of the column the disk storage object belongs to. * \param ds_name The disk storage name of the object. * * \return Pointer to the full path which must be freed by the caller. * * \sa column_filename(), disk_storage_name_of_row(). */ _static_inline_ char *disk_storage_path(const struct osl_table *t, unsigned col_num, const char *ds_name) { char *filename, *dirname = column_filename(t, col_num); if (!dirname) return NULL; filename = make_message("%s/%s", dirname, ds_name); free(dirname); return filename; } /** * Get the column description of the next column of a given type. * * \param type the desired storage type. * \param col_num the column to start the search. * \param t the osl table. * \param cd result is returned here. * * \return On success, \a cd contains the column description of the next column * of type \a type, and the number of that column is returned. Otherwise, \a * cd is set to \p NULL and the function returns t->num_columns + 1. * * \sa FOR_EACH_COLUMN_OF_TYPE, FOR_EACH_MAPPED_COLUMN, FOR_EACH_RBTREE_COLUMN, * FOR_EACH_DISK_STORAGE_COLUMN, FOR_EACH_VOLATILE_COLUMN, osl_storage_type. */ _static_inline_ unsigned next_column_of_type(enum osl_storage_type type, unsigned col_num, const struct osl_table *t, const struct osl_column_description **cd) { *cd = NULL; while (col_num < t->desc->num_columns) { *cd = get_column_description(t->desc, col_num); if ((*cd)->storage_type == type) break; col_num++; } return col_num; } /** * Find the next column with an associated rbtree. * * \param col_num The column to start the search. * \param t The osl table. * \param cd Result is returned here. * \return On success, \a cd contains the column description of the next column * with associated rbtree, and the number of that column is returned. * Otherwise, \a cd is set to \p NULL and the function returns t->num_columns + * 1. * * \sa FOR_EACH_COLUMN_OF_TYPE, FOR_EACH_MAPPED_COLUMN, FOR_EACH_RBTREE_COLUMN, * FOR_EACH_DISK_STORAGE_COLUMN, FOR_EACH_VOLATILE_COLUMN, osl_storage_flags. */ _static_inline_ int next_rbtree_column(int col_num, const struct osl_table *t, const struct osl_column_description **cd) { *cd = NULL; while (col_num < t->desc->num_columns) { *cd = get_column_description(t->desc, col_num); if ((*cd)->storage_flags & OSL_RBTREE) break; col_num++; } return col_num; } /* quite some dirty hacks */ /** Round up size of struct osl_row to multiple of 8 */ #define RB_NODES_OFFSET ((sizeof(struct osl_row) + 7) / 8 * 8) /** * Allocate a new osl row. * * \param num_rbtrees The number of rbtrees for this row. * * \return A pointer to a zeroed-out area suitable for holding an osl row with * \a num_rbtrees rbtree columns or \p NULL if no memory could be allocated. */ _static_inline_ struct osl_row *allocate_row(unsigned num_rbtrees) { size_t s = RB_NODES_OFFSET + num_rbtrees * sizeof(struct rb_node); return calloc(1, s); } /** * Compute the pointer to a rbtree node embedded in a osl row. * * \param row The row to extract the rbtree pointer from. * \param rbtree_num The number of the rbtree node to extract. * * \return A pointer to the \a rbtree_num th node contained in \a row. */ _static_inline_ struct rb_node *get_rb_node_pointer(const struct osl_row *row, uint32_t rbtree_num) { return ((struct rb_node *)(((char *)row) + RB_NODES_OFFSET)) + rbtree_num; } /** * Get a pointer to the struct containing the given rbtree node. * * \param node Pointer to the rbtree node. * \param rbtree_num Number of \a node in the array of rbtree nodes. * * \return A pointer to the row containing \a node. */ _static_inline_ struct osl_row *get_row_pointer(const struct rb_node *node, unsigned rbtree_num) { node -= rbtree_num; return (struct osl_row *)(((char *)node) - RB_NODES_OFFSET); } /** * Compute a cryptographic hash of an osl object. * * \param t Determines the hash function to use. * \param obj the Object to compute the hash value from. * \param hash Result is returned here. */ _static_inline_ void hash_object(const struct osl_table *t, const struct osl_object *obj, HASH_TYPE *hash) { hash_function(t->version, obj->data, obj->size, hash); } /** * Get the relative path of an object, given the hash value. * * \param t Pointer to an initialized osl table. * \param hash An arbitrary hash value. * * This function is typically called with \a hash being the hash of the object * stored in the disk storage name column of a row. If the OSL_LARGE_TABLE * flag is set, the first two hex digits get separated with a slash from the * remaining digits. * * \return The relative path for all disk storage objects corresponding to \a * hash. * * \sa struct osl_table:disk_storage_name_column. */ _static_inline_ char *disk_storage_name_of_hash(const struct osl_table *t, HASH_TYPE *hash) { char asc[2 * HASH_SIZE + 2]; hash_to_asc(hash, asc); if (t->desc->flags & OSL_LARGE_TABLE) return make_message("%.2s/%s", asc, asc + 2); return strdup(asc); } /** * Iterate over each column of an initialized table. * * \param col A pointer to a struct osl_column. * \param desc Pointer to the table description. * \param cd Pointer to struct osl_column_description. * * On each iteration, \a col will point to the next column of the table and \a * cd will point to the column description of this column. * * \sa struct osl_column FOR_EACH_RBTREE_COLUMN, FOR_EACH_COLUMN_OF_TYPE, * FOR_EACH_MAPPED_COLUMN, FOR_EACH_DISK_STORAGE_COLUMN, * FOR_EACH_VOLATILE_COLUMN. */ #define FOR_EACH_COLUMN(col, desc, cd) \ for (col = 0; col < (desc)->num_columns && \ (cd = get_column_description(desc, col)); col++) /** * Iterate over each column with associated rbtree. * * \param col Same meaning as in FOR_EACH_COLUMN(). * \param table Same meaning as in FOR_EACH_COLUMN(). * \param cd Same meaning as in FOR_EACH_COLUMN(). * * \sa osl_storage_flags::OSL_RBTREE, FOR_EACH_COLUMN, FOR_EACH_COLUMN_OF_TYPE, * FOR_EACH_MAPPED_COLUMN, FOR_EACH_DISK_STORAGE_COLUMN, * FOR_EACH_VOLATILE_COLUMN. */ #define FOR_EACH_RBTREE_COLUMN(col, table, cd) \ for (col = next_rbtree_column(0, table, &cd); \ col < table->desc->num_columns; \ col = next_rbtree_column(++col, table, &cd)) /** * Iterate over each column of given storage type. * * \param type The osl_storage_type to iterate over. * \param col Same meaning as in FOR_EACH_COLUMN(). * \param table Same meaning as in FOR_EACH_COLUMN(). * \param cd Same meaning as in FOR_EACH_COLUMN(). * * \sa osl_storage_type, FOR_EACH_COLUMN, FOR_EACH_RBTREE_COLUMN, * FOR_EACH_MAPPED_COLUMN, FOR_EACH_DISK_STORAGE_COLUMN, * FOR_EACH_VOLATILE_COLUMN. */ #define FOR_EACH_COLUMN_OF_TYPE(type, col, table, cd) \ for (col = next_column_of_type(type, 0, table, &cd); \ col < table->desc->num_columns; \ col = next_column_of_type(type, ++col, table, &cd)) /** * Iterate over each mapped column. * * \param col Same meaning as in FOR_EACH_COLUMN(). * \param table Same meaning as in FOR_EACH_COLUMN(). * \param cd Same meaning as in FOR_EACH_COLUMN(). * * Just like FOR_EACH_COLUMN(), but skip columns which are * not of type \p OSL_MAPPED_STORAGE. * * \sa osl_storage_flags::OSL_MAPPED_STORAGE, FOR_EACH_COLUMN, * FOR_EACH_RBTREE_COLUMN, FOR_EACH_COLUMN_OF_TYPE, * FOR_EACH_DISK_STORAGE_COLUMN, FOR_EACH_VOLATILE_COLUMN. */ #define FOR_EACH_MAPPED_COLUMN(col, table, cd) \ FOR_EACH_COLUMN_OF_TYPE(OSL_MAPPED_STORAGE, col, table, cd) /** * Iterate over each disk storage column. * * \param col Same meaning as in FOR_EACH_COLUMN(). * \param table Same meaning as in FOR_EACH_COLUMN(). * \param cd Same meaning as in FOR_EACH_COLUMN(). * * Just like FOR_EACH_COLUMN(), but skip columns which are * not of type \p OSL_DISK_STORAGE. * * \sa osl_storage_flags::OSL_DISK_STORAGE, FOR_EACH_COLUMN, * FOR_EACH_RBTREE_COLUMN, FOR_EACH_COLUMN_OF_TYPE, FOR_EACH_MAPPED_COLUMN, * FOR_EACH_VOLATILE_COLUMN. */ #define FOR_EACH_DISK_STORAGE_COLUMN(col, table, cd) \ FOR_EACH_COLUMN_OF_TYPE(OSL_DISK_STORAGE, col, table, cd) /** * Iterate over each volatile column. * * \param col Same meaning as in FOR_EACH_COLUMN(). * \param table Same meaning as in FOR_EACH_COLUMN(). * \param cd Same meaning as in FOR_EACH_COLUMN(). * * Just like FOR_EACH_COLUMN(), but skip columns which are * not of type \p OSL_NO_STORAGE. * * \sa osl_storage_flags::OSL_NO_STORAGE, FOR_EACH_COLUMN, * FOR_EACH_RBTREE_COLUMN, FOR_EACH_COLUMN_OF_TYPE, FOR_EACH_MAPPED_COLUMN, * FOR_EACH_DISK_STORAGE_COLUMN. */ #define FOR_EACH_VOLATILE_COLUMN(col, table, cd) \ FOR_EACH_COLUMN_OF_TYPE(OSL_NO_STORAGE, col, table, cd)