*/
/** \file osl.c Object storage layer functions. */
-#include "para.h"
-#include "error.h"
-#include "list.h"
-#include "osl_core.h"
#include <dirent.h> /* readdir() */
#include <assert.h>
-//#define FMT_OFF_T "%li"
-
+#include "para.h"
+#include "error.h"
+#include "fd.h"
+#include "list.h"
+#include "osl_core.h"
/**
* A wrapper for lseek(2).
*
- * \param fd The filedescriptor whose offset is to be to repositioned.
+ * \param fd The file descriptor whose offset is to be to repositioned.
* \param offset A value-result parameter.
* \param whence Usual repositioning directive.
*
}
/**
- * Waraper for the write system call.
+ * Wrapper for the write system call.
*
* \param fd The file descriptor to write to.
* \param buf The buffer to write.
* \param size The length of \a buf in bytes.
*
- * This function writes out the given bufffer and retries if an interrupt
- * occured during the write.
+ * This function writes out the given buffer and retries if an interrupt
+ * occurred during the write.
*
* \return On success, the number of bytes written is returned, otherwise, the
* function returns \p -E_WRITE.
}
return 1;
}
-/**
- * Wrapper for the open(2) system call.
- *
- * \param path The filename.
- * \param flags The usual open(2) flags.
- * \param mode Specifies the permissions to use.
- *
- * The mode parameter must be specified when O_CREAT is in the flags, and is ignored
- * otherwise.
- *
- * \return Positive on success, negative on errors. Possible errors: \p
- * E_EXIST, \p E_ISDIR, \p E_NOENT, \p E_OSL_PERM.
- *
- * \sa open(2).
- */
-int para_open(const char *path, int flags, mode_t mode)
-{
- PARA_DEBUG_LOG("opening %s\n", path);
- int ret = open(path, flags, mode);
-
- if (ret >= 0)
- return ret;
- switch (errno) {
- case EEXIST:
- ret = -E_EXIST;
- break;
- case EISDIR:
- ret = -E_ISDIR;
- break;
- case ENOENT:
- ret = -E_NOENT;
- break;
- case EPERM:
- ret = -E_OSL_PERM;
- break;
- };
- PARA_ERROR_LOG("failed to open %s: %s\n", path, strerror(errno));
- return ret;
-}
-
/**
* Open a file, write the given buffer and close the file.
*
* \param func The function to call for each entry.
* \param private_data Pointer to an arbitrary data structure.
*
- * For each regular file in \a dirname, the supplied function \a func is
+ * For each regular file under \a dirname, the supplied function \a func is
* called. The full path of the regular file and the \a private_data pointer
- * are passed to \a func.
+ * are passed to \a func. Directories for which the calling process has no
+ * permissions to change to are silently ignored.
*
* \return On success, 1 is returned. Otherwise, this function returns a
* negative value which indicates the kind of the error.
int for_each_file_in_dir(const char *dirname,
int (*func)(const char *, const void *), const void *private_data)
{
- DIR *dir = NULL;
+ DIR *dir;
struct dirent *entry;
- /*
- * Opening the current directory (".") and calling fchdir() to return
- * is usually faster and more reliable than saving cwd in some buffer
- * and calling chdir() afterwards (see man 3 getcwd).
- */
- int cwd_fd = open(".", O_RDONLY);
- struct stat s;
- int ret = -1;
-
-// PARA_DEBUG_LOG("dirname: %s\n", dirname);
- if (cwd_fd < 0)
- return -E_OSL_GETCWD;
- ret = -E_OSL_CHDIR;
- if (chdir(dirname) < 0)
- goto out;
- ret = -E_OSL_OPENDIR;
- dir = opendir(".");
- if (!dir)
- goto out;
+ int cwd_fd, ret2, ret = para_opendir(dirname, &dir, &cwd_fd);
+
+ if (ret < 0)
+ return ret == -E_CHDIR_PERM? 1 : ret;
/* scan cwd recursively */
while ((entry = readdir(dir))) {
mode_t m;
char *tmp;
+ struct stat s;
if (!strcmp(entry->d_name, "."))
continue;
if (!strcmp(entry->d_name, ".."))
continue;
- ret = -E_OSL_LSTAT;
if (lstat(entry->d_name, &s) == -1)
continue;
m = s.st_mode;
}
ret = 1;
out:
- if (dir)
- closedir(dir);
- if (fchdir(cwd_fd) < 0 && ret >= 0)
- ret = -E_OSL_CHDIR;
+ closedir(dir);
+ ret2 = para_fchdir(cwd_fd);
+ if (ret2 < 0 && ret >= 0)
+ ret = ret2;
close(cwd_fd);
return ret;
}
-int para_mkdir(const char *path, mode_t mode)
-{
- if (!mkdir(path, mode))
- return 1;
- if (errno == EEXIST)
- return -E_EXIST;
- if (errno == ENOSPC)
- return -E_NOSPC;
- if (errno == ENOTDIR)
- return -E_NOTDIR;
- if (errno == EPERM)
- return E_OSL_PERM;
- return -E_MKDIR;
-}
-
-static int verify_basename(const char *name)
+static int verify_name(const char *name)
{
if (!name)
- return -E_BAD_BASENAME;
+ return -E_BAD_NAME;
if (!*name)
- return -E_BAD_BASENAME;
+ return -E_BAD_NAME;
if (strchr(name, '/'))
- return -E_BAD_BASENAME;
+ return -E_BAD_NAME;
if (!strcmp(name, ".."))
- return -E_BAD_BASENAME;
+ return -E_BAD_NAME;
if (!strcmp(name, "."))
- return -E_BAD_BASENAME;
+ return -E_BAD_NAME;
return 1;
}
const struct osl_column_description *cd;
ret = -E_BAD_TABLE_DESC;
- ret = verify_basename(t->desc->name);
+ ret = verify_name(t->desc->name);
if (ret < 0)
goto err;
ret = -E_BAD_DB_DIR;
ret = -E_NO_COLUMN_NAME;
if (!cd->name || !cd->name[0])
goto err;
- ret = verify_basename(cd->name);
+ ret = verify_name(cd->name);
if (ret < 0)
goto err;
t->index_header_size += index_column_description_size(cd->name);
break;
case OSL_MAPPED_STORAGE:
t->num_mapped_columns++;
- col->index_offset = t->index_entry_size;
- t->index_entry_size += 8;
+ col->index_offset = t->row_index_size;
+ t->row_index_size += 8;
break;
case OSL_NO_STORAGE:
col->volatile_num = t->num_volatile_columns;
if (!t->num_rbtrees)
goto err;
/* success */
- PARA_DEBUG_LOG("OK. Index entry size: %u\n", t->index_entry_size);
+ PARA_DEBUG_LOG("OK. Index entry size: %u\n", t->row_index_size);
ret = init_column_descriptions(t);
if (ret < 0)
goto err;
ret = -E_BAD_TABLE_FLAGS;
if (desc.flags != t->desc->flags)
goto out;
- ret = E_BAD_COLUMN_NUM;
+ ret = -E_BAD_COLUMN_NUM;
if (desc.num_columns != t->desc->num_columns)
goto out;
FOR_EACH_COLUMN(i, t->desc, cd1) {
* \param desc Pointer to the table description.
*
* \return Positive on success, negative on errors. Possible errors include: \p
- * E_BAD_TABLE_DESC, \p E_BAD_DB_DIR, \p E_BAD_BASENAME, \p E_NO_COMPARE_FUNC, \p
+ * E_BAD_TABLE_DESC, \p E_BAD_DB_DIR, \p E_BAD_NAME, \p E_NO_COMPARE_FUNC, \p
* E_NO_COLUMN_NAME, \p E_DUPLICATE_COL_NAME, \p E_MKDIR, any errors returned
* by para_open().
*/
* \param t Pointer to a mapped table.
* \param flags Options for unmapping.
*
- * \return Positive on success, negative on errors. Possible errors include:
- * E_NOT_MAPPED, E_MUNMAP.
+ * \return Positive on success, negative on errors.
*
* \sa map_table(), enum osl_close_flags, para_munmap().
*/
* of storage type \p OSL_MAPPED_STORAGE.
*
* \return Positive on success, negative on errors. Possible errors include:
- * \p E_BAD_ID, \p E_INVALID_OBJECT.
+ * \p E_BAD_ROW_NUM, \p E_INVALID_OBJECT.
*
* \sa osl_storage_type.
*/
struct osl_column *col = &t->columns[col_num];
uint32_t offset;
char *header;
- char *index_entry;
+ char *cell_index;
int ret;
if (t->num_rows <= row_num)
- return -E_BAD_ID;
- ret = get_index_entry(t, row_num, col_num, &index_entry);
+ return -E_BAD_ROW_NUM;
+ ret = get_cell_index(t, row_num, col_num, &cell_index);
if (ret < 0)
return ret;
- offset = read_u32(index_entry);
- obj->size = read_u32(index_entry + 4) - 1;
+ offset = read_u32(cell_index);
+ obj->size = read_u32(cell_index + 4) - 1;
header = col->data_map.data + offset;
obj->data = header + 1;
if (read_u8(header) == 0xff) {
struct osl_object this_obj;
parent = *new;
if (st == OSL_MAPPED_STORAGE) {
- ret = get_mapped_object(t, col_num, this_row->id,
+ ret = get_mapped_object(t, col_num, this_row->num,
&this_obj);
if (ret < 0)
return ret;
rb_erase(victim, &col->rbtree);
}
-static int add_row_to_rbtrees(struct osl_table *t, uint32_t id,
+static int add_row_to_rbtrees(struct osl_table *t, uint32_t row_num,
struct osl_object *volatile_objs, struct osl_row **row_ptr)
{
unsigned i;
struct osl_row *row = allocate_row(t->num_rbtrees);
const struct osl_column_description *cd;
- row->id = id;
+ row->num = row_num;
row->volatile_objects = volatile_objs;
FOR_EACH_RBTREE_COLUMN(i, t, cd) {
if (cd->storage_type == OSL_MAPPED_STORAGE) {
struct osl_object obj;
- ret = get_mapped_object(t, i, id, &obj);
+ ret = get_mapped_object(t, i, row_num, &obj);
if (ret < 0)
goto err;
ret = insert_rbtree(t, i, row, &obj);
* \param flags Options for what should be cleaned up.
*
* If osl_open_table() succeeds, the resulting table pointer must later be
- * passed to this function in order to flush all changes to the filesystem and
+ * passed to this function in order to flush all changes to the file system and
* to free the resources that were allocated by osl_open_table().
*
* \return Positive on success, negative on errors. Possible errors: \p E_BAD_TABLE,
*/
int row_is_invalid(struct osl_table *t, uint32_t row_num)
{
- char *index_entry;
- int i, ret = get_index_entry_start(t, row_num, &index_entry);
+ char *row_index;
+ int i, ret = get_row_index(t, row_num, &row_index);
if (ret < 0)
return ret;
- for (i = 0; i < t->index_entry_size; i++) {
- if ((unsigned char)index_entry[i] != 0xff)
+ for (i = 0; i < t->row_index_size; i++) {
+ if ((unsigned char)row_index[i] != 0xff)
return 0;
}
PARA_INFO_LOG("row %d is invalid\n", row_num);
*/
int mark_row_invalid(struct osl_table *t, uint32_t row_num)
{
- char *index_entry;
- int i, ret = get_index_entry_start(t, row_num, &index_entry);
+ char *row_index;
+ int ret = get_row_index(t, row_num, &row_index);
- PARA_INFO_LOG("marking row %d as invalid\n", row_num);
if (ret < 0)
return ret;
- for (i = 0; i < t->index_entry_size; i++)
- index_entry[i] = 0xff;
+ PARA_INFO_LOG("marking row %d as invalid\n", row_num);
+ memset(row_index, 0xff, t->row_index_size);
return 1;
}
return ret;
}
-static int append_index_entry(const struct osl_table *t, char *new_index_entry)
+static int append_row_index(const struct osl_table *t, char *row_index)
{
char *filename;
int ret;
if (!t->num_mapped_columns)
return 1;
filename = index_filename(t->desc);
-// PARA_DEBUG_LOG("appending %u bytes\n", t->index_entry_size);
- ret = append_file(filename, NULL, 0, new_index_entry,
- t->index_entry_size, NULL);
+ ret = append_file(filename, NULL, 0, row_index,
+ t->row_index_size, NULL);
free(filename);
return ret;
}
int i, ret;
char *ds_name = NULL;
struct rb_node **rb_parents = NULL, ***rb_links = NULL;
- char *new_index_entry = NULL;
+ char *new_row_index = NULL;
struct osl_object *volatile_objs = NULL;
const struct osl_column_description *cd;
rb_parents = para_malloc(t->num_rbtrees * sizeof(struct rn_node*));
rb_links = para_malloc(t->num_rbtrees * sizeof(struct rn_node**));
if (t->num_mapped_columns)
- new_index_entry = para_malloc(t->index_entry_size);
+ new_row_index = para_malloc(t->row_index_size);
/* pass 1: sanity checks */
// PARA_DEBUG_LOG("sanity tests: %p:%p\n", objects[0].data,
// objects[1].data);
ret = append_map_file(t, i, objects + i, &new_size);
if (ret < 0)
goto rollback;
- update_index_entry(new_index_entry, col, new_size,
+ update_cell_index(new_row_index, col, new_size,
objects[i].size);
continue;
}
if (ret < 0)
goto rollback;
}
- ret = append_index_entry(t, new_index_entry);
+ ret = append_row_index(t, new_row_index);
if (ret < 0)
goto rollback;
ret = map_table(t, MAP_TBL_FL_VERIFY_INDEX);
if (ret < 0) { /* truncate index and rollback changes */
char *filename = index_filename(t->desc);
- para_truncate(filename, t->index_entry_size);
+ para_truncate(filename, t->row_index_size);
free(filename);
goto rollback;
}
/* ignore error and return previous error value */
map_table(t, MAP_TBL_FL_VERIFY_INDEX);
out:
- free(new_index_entry);
+ free(new_row_index);
free(ds_name);
free(rb_parents);
free(rb_links);
if (cd->storage_type == OSL_DISK_STORAGE)
return -E_BAD_STORAGE_TYPE;
if (cd->storage_type == OSL_MAPPED_STORAGE)
- return get_mapped_object(t, col_num, r->id, object);
+ return get_mapped_object(t, col_num, r->num, object);
/* volatile */
*object = r->volatile_objects[t->columns[col_num].volatile_num];
return 1;
}
-static int mark_mapped_object_invalid(const struct osl_table *t, uint32_t id,
- unsigned col_num)
+static int mark_mapped_object_invalid(const struct osl_table *t,
+ uint32_t row_num, unsigned col_num)
{
struct osl_object obj;
char *p;
- int ret = get_mapped_object(t, col_num, id, &obj);
+ int ret = get_mapped_object(t, col_num, row_num, &obj);
if (ret < 0)
return ret;
enum osl_storage_type st = cd->storage_type;
remove_rb_node(t, i, r);
if (st == OSL_MAPPED_STORAGE) {
- mark_mapped_object_invalid(t, r->id, i);
+ mark_mapped_object_invalid(t, r->num, i);
continue;
}
if (st == OSL_NO_STORAGE)
free(r->volatile_objects[col->volatile_num].data);
}
if (t->num_mapped_columns) {
- ret = mark_row_invalid(t, r->id);
+ ret = mark_row_invalid(t, r->num);
if (ret < 0)
goto out;
t->num_invalid_rows++;
* Lookup \a obj in \a t and return the row containing \a obj. The column
* specified by \a col_num must have an associated rbtree.
*
- * \return Positive on success, negative on errors. If an error occured, \a
+ * \return Positive on success, negative on errors. If an error occurred, \a
* result is set to \p NULL. Possible errors include: \p E_BAD_TABLE, \p
* E_BAD_STORAGE_FLAGS, errors returned by get_mapped_object(), \p
* E_RB_KEY_NOT_FOUND.
static int rbtree_loop(struct osl_column *col, void *private_data,
osl_rbtree_loop_func *func)
{
- struct rb_node *n;
+ struct rb_node *n, *tmp;
- for (n = rb_first(&col->rbtree); n; n = rb_next(n)) {
+ /* this for-loop is safe against removal of an entry */
+ for (n = rb_first(&col->rbtree), tmp = n? rb_next(n) : NULL;
+ n;
+ n = tmp, tmp = tmp? rb_next(tmp) : NULL) {
struct osl_row *r = get_row_pointer(n, col->rbtree_num);
int ret = func(r, private_data);
if (ret < 0)
static int rbtree_loop_reverse(struct osl_column *col, void *private_data,
osl_rbtree_loop_func *func)
{
- struct rb_node *n;
+ struct rb_node *n, *tmp;
- for (n = rb_last(&col->rbtree); n; n = rb_prev(n)) {
+ /* safe against removal of an entry */
+ for (n = rb_last(&col->rbtree), tmp = n? rb_prev(n) : NULL;
+ n;
+ n = tmp, tmp = tmp? rb_prev(tmp) : NULL) {
struct osl_row *r = get_row_pointer(n, col->rbtree_num);
int ret = func(r, private_data);
if (ret < 0)
* This function does an in-order walk of the rbtree associated with \a
* col_num. It is an error if the \p OSL_RBTREE flag is not set for this
* column. For each node in the rbtree, the given function \a func is called
- * with two \p void* pointers as arguments: The first argument points to the
+ * with two pointers as arguments: The first osl_row* argument points to the
* row that contains the object corresponding to the rbtree node currently
- * traversed, and the \a private_data pointer is passed to \a func as the
+ * traversed, and the \a private_data pointer is passed verbatim to \a func as the
* second argument. The loop terminates either if \a func returns a negative
* value, or if all nodes of the tree have been visited.
*
*
* This function gets rid of all references to the old object. This includes
* removal of the rbtree node in case there is an rbtree associated with \a
- * col_num. It then inserts \a obj into the table and the rbtree if neccessary.
+ * col_num. It then inserts \a obj into the table and the rbtree if necessary.
*
* If the \p OSL_RBTREE flag is set for \a col_num, you \b MUST call this
* function in order to change the contents of an object, even for volatile or
}
if (cd->storage_flags & OSL_FIXED_SIZE) {
if (obj->size != cd->data_size)
- return -E_BAD_SIZE;
+ return -E_BAD_DATA_SIZE;
}
remove_rb_node(t, col_num, r);
if (cd->storage_type == OSL_NO_STORAGE) { /* TODO: If fixed size, reuse object? */
return ret;
} else { /* mapped storage */
struct osl_object old_obj;
- ret = get_mapped_object(t, col_num, r->id, &old_obj);
+ ret = get_mapped_object(t, col_num, r->num, &old_obj);
if (ret < 0)
return ret;
/*
memcpy(old_obj.data, obj->data, cd->data_size);
else { /* TODO: if the size doesn't change, use old space */
uint32_t new_data_map_size;
- char *index_entry;
- ret = get_index_entry_start(t, r->id, &index_entry);
+ char *row_index;
+ ret = get_row_index(t, r->num, &row_index);
if (ret < 0)
return ret;
- ret = mark_mapped_object_invalid(t, r->id, col_num);
+ ret = mark_mapped_object_invalid(t, r->num, col_num);
if (ret < 0)
return ret;
unmap_column(t, col_num);
ret = map_column(t, col_num);
if (ret < 0)
return ret;
- update_index_entry(index_entry, col, new_data_map_size,
+ update_cell_index(row_index, col, new_data_map_size,
obj->size);
}
}