*/
/** \file osl.c Object storage layer functions. */
+#include <dirent.h> /* readdir() */
+#include <assert.h>
+
+
#include "para.h"
#include "error.h"
+#include "fd.h"
#include "list.h"
#include "osl_core.h"
-#include <dirent.h> /* readdir() */
-#include <assert.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.
*
* \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.
*
return ret;
}
-/**
- * Map a file into memory.
- *
- * \param path Name of the regular file to map.
- * \param open_mode Either \p O_RDONLY or \p O_RDWR.
- * \param obj On success, the mapping is returned here.
- *
- * \return Positive on success, negative on errors. Possible errors include: \p
- * E_FSTAT, any errors returned by para_open(), \p E_EMPTY, \p E_MMAP.
- *
- * \sa para_open(), mmap(2).
- */
-int mmap_full_file(const char *path, int open_mode, struct osl_object *obj)
-{
- int fd, ret, mmap_prot, mmap_flags;
- struct stat file_status;
-
- if (open_mode == O_RDONLY) {
- mmap_prot = PROT_READ;
- mmap_flags = MAP_PRIVATE;
- } else {
- mmap_prot = PROT_READ | PROT_WRITE;
- mmap_flags = MAP_SHARED;
- }
- ret = para_open(path, open_mode, 0);
- if (ret < 0)
- return ret;
- fd = ret;
- ret = -E_FSTAT;
- if (fstat(fd, &file_status) < 0)
- goto out;
- obj->size = file_status.st_size;
- ret = -E_EMPTY;
- PARA_DEBUG_LOG("%s: size %zu\n", path, obj->size);
- if (!obj->size)
- goto out;
- obj->data = mmap(NULL, obj->size, mmap_prot, mmap_flags, fd, 0);
- if (obj->data == MAP_FAILED) {
- obj->data = NULL;
- ret = -E_MMAP;
- goto out;
- }
- ret = 1;
-out:
- close(fd);
- return ret;
-}
-
/**
* Traverse the given directory recursively.
*
* \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);
- 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;
}
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_name(const char *name)
{
if (!name)
if (ret < 0)
goto err;
ret = -E_BAD_DB_DIR;
- if (!t->desc->dir)
+ if (!t->desc->dir && (t->num_disk_storage_columns || t->num_mapped_columns))
goto err;
/* the size of the index header without column descriptions */
t->index_header_size = IDX_COLUMN_DESCRIPTIONS;
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_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().
+ * \return Standard.
*/
int osl_create_table(const struct osl_table_description *desc)
{
continue;
if (!table_dir) {
ret = para_mkdir(desc->dir, 0777);
- if (ret < 0 && ret != -E_EXIST)
+ if (ret < 0 && !is_errno(-ret, EEXIST))
goto out;
table_dir = make_message("%s/%s", desc->dir,
desc->name);
* \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().
*/
return ret;
}
ret = mmap_full_file(filename, O_RDWR,
- &t->columns[col_num].data_map);
+ &t->columns[col_num].data_map.data,
+ &t->columns[col_num].data_map.size,
+ NULL);
free(filename);
return ret;
}
filename = index_filename(t->desc);
PARA_DEBUG_LOG("mapping table '%s' (index: %s)\n", t->desc->name, filename);
ret = mmap_full_file(filename, flags & MAP_TBL_FL_MAP_RDONLY?
- O_RDONLY : O_RDWR, &t->index_map);
+ O_RDONLY : O_RDWR, &t->index_map.data, &t->index_map.size, NULL);
free(filename);
if (ret < 0)
return ret;
for (n = rb_first(&rb_col->rbtree); n; n = rb_next(n)) {
struct osl_row *r = get_row_pointer(n, rb_col->rbtree_num);
if (flags & OSL_FREE_VOLATILE)
- for (j = 0; j < t->num_volatile_columns; j++)
- free(r->volatile_objects[j].data);
+ FOR_EACH_VOLATILE_COLUMN(j, t, cd) {
+ if (cd->storage_flags & OSL_DONT_FREE)
+ continue;
+ free(r->volatile_objects[
+ t->columns[j].volatile_num].data);
+ }
+// for (j = 0; j < t->num_volatile_columns; j++)
+// free(r->volatile_objects[j].data);
free(r->volatile_objects);
}
}
* \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,
* The table description given by \a desc should coincide with the
* description used at creation time.
*
- * \return Positive on success, negative on errors. Possible errors include:
- * errors returned by init_table_structure(), \p E_NOENT, \p E_STAT, \p \p
- * E_NOTDIR, \p E_BAD_TABLE_DESC, \p E_BAD_DB_DIR, \p E_NO_COMPARE_FUNC, \p
- * E_NO_COLUMN_NAME, errors returned by init_rbtrees().
+ * \return Standard.
*/
int osl_open_table(const struct osl_table_description *table_desc,
struct osl_table **result)
ret = stat(dirname, &statbuf);
free(dirname);
if (ret < 0) {
- if (errno == ENOENT)
- ret = -E_NOENT;
- else
- ret = -E_STAT;
+ ret = -ERRNO_TO_PARA_ERROR(errno);
goto err;
}
- ret = -E_NOTDIR;
+ ret = -ERRNO_TO_PARA_ERROR(ENOTDIR);
if (!S_ISDIR(statbuf.st_mode))
goto err;
}
dirname = disk_storage_dirname(t, col_num, ds_name);
ret = para_mkdir(dirname, 0777);
free(dirname);
- if (ret < 0 && ret != -E_EXIST)
+ if (ret < 0 && !is_errno(-ret, EEXIST))
return ret;
return 1;
}
const char *ds_name)
{
char *dirname, *filename = disk_storage_path(t, col_num, ds_name);
- int ret = unlink(filename);
+ int ret = unlink(filename), err = errno;
- PARA_DEBUG_LOG("deleted %s\n", filename);
free(filename);
- if (ret < 0) {
- if (errno == ENOENT)
- return -E_NOENT;
- return -E_UNLINK;
- }
+ if (ret < 0)
+ return -ERRNO_TO_PARA_ERROR(err);
if (!(t->desc->flags & OSL_LARGE_TABLE))
return 1;
dirname = disk_storage_dirname(t, col_num, ds_name);
* 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
* mapped columns of constant size (which may be updated directly if \p
* OSL_RBTREE is not set). Otherwise the rbtree might become corrupted.
*
- * \return Positive on success, negative on errors. Possible errors include: \p
- * E_BAD_TABLE, \p E_RB_KEY_EXISTS, \p E_BAD_SIZE, \p E_NOENT, \p E_UNLINK,
- * errors returned by para_write_file(), \p E_MKDIR.
+ * \return Standard
*/
int osl_update_object(struct osl_table *t, const struct osl_row *r,
unsigned col_num, struct osl_object *obj)
if (ret < 0)
return ret;
ret = delete_disk_storage_file(t, col_num, ds_name);
- if (ret < 0 && ret != -E_NOENT) {
+ if (ret < 0 && !is_errno(-ret, ENOENT)) {
free(ds_name);
return ret;
}
filename = disk_storage_path(t, col_num, ds_name);
free(ds_name);
PARA_DEBUG_LOG("filename: %s\n", filename);
- ret = mmap_full_file(filename, O_RDONLY, obj);
+ ret = mmap_full_file(filename, O_RDONLY, &obj->data, &obj->size, NULL);
free(filename);
return ret;
}
* \param rank Result pointer.
*
* The rank is, by definition, the position of the row in the linear order
- * determined by an inorder tree walk of the rbtree associated with column
+ * determined by an in-order tree walk of the rbtree associated with column
* number \a col_num of \a table.
*
* \return Positive on success, negative on errors.