+# Implicit rules are implemented in make as suffix rules. The following rule
+# empties the suffix list to disable the predefined implicit rules. This
+# increases performance and avoids hard-to-debug behaviour.
+.SUFFIXES:
+MAKEFLAGS += -Rr
+CC := $(CC)
+ifeq ($(strip $(CC)),)
+ CC := cc
+endif
+
# where to install
PREFIX ?= /usr/local
libdir := $(PREFIX)/lib
bindir := $(PREFIX)/bin
mandir := $(PREFIX)/man/man1
- objects := osl.o util.o rbtree.o sha1.o
- fsck_objects := fsck.o osl.o util.o rbtree.o sha1.o oslfsck.lsg.o
+ objects := osl.o util.o rbtree.o sha1.o sha3.o sha256.o
+ fsck_objects := fsck.o osl.o util.o rbtree.o sha1.o sha3.o sha256.o oslfsck.lsg.o
deps := $(sort $(objects:.o=.d) $(fsck_objects:.o=.d))
headers := osl.h
executables := oslfsck
man_pages := oslfsck.1
INSTALL := install
-ifeq "$(origin CC)" "default"
- CC := cc
-endif
MKDIR := mkdir -p
RM := rm -f
LN := ln
LD := ld
+M4 := m4 -g
OBJCOPY := objcopy
# libosl's versioning consists of three numbers. Let's call them x, y and z.
OSL_CPPFLAGS += -DOSL_VERSION='"$(VERSION)"'
-OSL_CFLAGS += -Wno-sign-compare -g -Wunused -Wundef -W
+OSL_CFLAGS += -g -Wunused -Wundef -W
OSL_CFLAGS += -Wredundant-decls
OSL_CFLAGS += -Os
OSL_CFLAGS += -Wall
ifeq ($(findstring clean, $(MAKECMDGOALS)),)
-include $(deps)
endif
-%.o: %.c Makefile errtab.h
+
+# The files to generate before attempting to run the compiler. These
+# are the order-only prerequisites in the rule below.
+pre_deps := osl.h errtab.h oslfsck.lsg.h
+
+# The .d and .o files are both created from a single cc invocation.
+define CC_CMD
$(CC) $(OSL_CPPFLAGS) $(CPPFLAGS) \
- -c -MMD -MF $(*F).d -MT $@ \
+ -c -MMD -MF $(*F).d -MT $(*F).o \
$(OSL_CFLAGS) $(CFLAGS) $<
+endef
+%.o: %.c Makefile | $(pre_deps)
+ $(CC_CMD)
+%.d: %.c Makefile | $(pre_deps)
+ $(CC_CMD)
fsck.o: oslfsck.lsg.h
oslfsck: $(fsck_objects)
$(CC) -o $@ $(fsck_objects) $(LDFLAGS) -llopsub
+.PRECIOUS: %.lsg.h %.lsg.c
%.lsg.c: %.suite
lopsubgen --gen-c < $<
$(realname): $(objects)
$(CC) $(OSL_LDFLAGS) $(LDFLAGS) -o $@ $(objects)
-$(libname).sym: osl.h.in
- sed -Ene '/^int|^const/{s/.*(osl_.*)\(.*/\1/; p;}' $< > $@
-$(libname).ga: $(objects)
- $(LD) -r -o $@ $(objects)
-lib$(libname).a: $(libname).ga $(libname).sym
- $(OBJCOPY) --keep-global-symbols $(libname).sym $(libname).ga $@
-
-osl_errors.h: errlist
- echo '/** public error codes of the osl library. */' > $@
- sed -e 's/\([A-Z_]*\) .*/ E_OSL_\1/' \
- -e '1s/^/enum osl_errors {/1' \
- -e '1s/$$/=1/1' \
- -e '$$!s/$$/,/g' \
- -e '$$s/$$/};/1' $< >> $@
-
errtab.h: errlist
sed -e 's/^\([A-Z_]*\)\s*\(.*\)/OSL_ERROR(E_OSL_\1, \2)/g' $< > $@
-osl.h: osl.h.in osl_errors.h Makefile
+osl.h: osl.h.m4 errlist Makefile
echo '#ifndef _OSL_H' > $@
echo '#define _OSL_H' >> $@
- cat osl.h.in osl_errors.h >> $@
+ $(M4) -DOUTPUT_MODE=C gendoc.m4 $< >> $@
+ echo '/** public error codes of the osl library. */' >> $@
+ sed -e 's/\([A-Z_]*\) .*/ E_OSL_\1/' \
+ -e '1s/^/enum osl_errors {/1' \
+ -e '1s/$$/=1/1' \
+ -e '$$!s/$$/,/g' \
+ -e '$$s/$$/};/1' errlist >> $@
echo '#endif /* _OSL_H */' >> $@
clean:
- rm -f *.o $(realname) osl.h osl_errors.h errtab.h \
- oslfsck *.a *.ga *.sym *.lsg.* *.d
+ rm -f *.o $(realname) osl.h errtab.h oslfsck *.lsg.* *.d
distclean: clean
rm -f web/index.html web/oslfsck.1.html web/osl.png
- rm -rf web/doxygen
install-bin: $(executables)
$(MKDIR) $(bindir)
$(INSTALL) -m 644 $(headers) $(includedir)
install: all install-bin install-man install-lib
-www: web/index.html web/osl.png web/doxygen/index.html
+www: web/index.html web/osl.png web/api.html
.PHONY: all shared clean install install-bin install-man install-lib www
web/osl.png: web/osl.pdf Makefile
convert -scale 200x200 $< $@
-web/index.html: oslfsck.1 web/index.html.in INSTALL README
- sed -e '/@README@/,$$d' web/index.html.in > $@
+web/index.html: oslfsck.1 web/header.html web/index.html.in INSTALL README QUICK_START
+ cat web/header.html > $@
+ sed -e '/@README@/,$$d' web/index.html.in >> $@
markdown < README >> $@
sed -e '1,/@README@/d' -e '/@INSTALL@/,$$d' web/index.html.in >> $@
markdown < INSTALL >> $@
- sed -e '1,/@INSTALL@/d' -e '/@MAN_PAGE@/,$$d' web/index.html.in >> $@
+ sed -e '1,/@INSTALL@/d' -e '/@QUICK_START@/,$$d' web/index.html.in >> $@
+ markdown < QUICK_START >> $@
+ sed -e '1,/@QUICK_START@/d' -e '/@MAN_PAGE@/,$$d' web/index.html.in >> $@
groff -m man -Thtml -P -l oslfsck.1 | sed -e '1,/^<body>/d; /^<\/body>/,$$d' >> $@
sed -e '1,/@MAN_PAGE@/d' web/index.html.in >> $@
-web/doxygen/index.html: $(wildcard *.c *.h) web/Doxyfile web/header.html \
- web/footer.html
- doxygen web/Doxyfile
+web/api.html: osl.h.m4 web/header.html web/footer.html
+ cat web/header.html > $@
+ $(M4) -DOUTPUT_MODE=HTML gendoc.m4 $< >> $@
+ cat web/footer.html >> $@
*/
static int check_index_ranges(struct osl_table *t)
{
- int i, j, ret;
+ int ret;
+ unsigned k, n;
INFO_LOG("checking for range violations in index\n");
//DEBUG_LOG("%d rows. %d columns\n", t->num_rows, t->desc->num_columns);
t->num_invalid_rows = 0;
- for (i = 0; i < t->num_rows; i++) {
+ for (n = 0; n < t->num_rows; n++) {
const struct osl_column_description *cd;
- if (row_is_invalid(t, i)) {
+ if (row_is_invalid(t, n)) {
t->num_invalid_rows++;
continue;
}
- FOR_EACH_MAPPED_COLUMN(j, t, cd) {
- ret = check_range(t, i, j);
+ FOR_EACH_MAPPED_COLUMN(k, t, cd) {
+ ret = check_range(t, n, k);
if (ret < 0) {
if (ret != -E_FSCK_RANGE_VIOLATION)
goto err;
- ret = fsck_mark_row_invalid(t, i);
+ ret = fsck_mark_row_invalid(t, n);
if (ret < 0)
goto err;
t->num_invalid_rows++;
static int check_for_invalid_objects(struct osl_table *t, uint32_t **lost_bytes)
{
- int i, j, ret;
+ int ret;
+ unsigned k, n;
const struct osl_column_description *cd;
uint32_t *loss = fsck_malloc(sizeof(uint32_t) * t->desc->num_columns);
INFO_LOG("looking for mapped objects not contained in index\n");
/* first count used bytes */
- FOR_EACH_MAPPED_COLUMN(i, t, cd) {
- loss[i] = t->columns[i].data_map.size;
- DEBUG_LOG("column %i data map: %zu bytes\n", i,
- t->columns[i].data_map.size);
- for (j = 0; j < t->num_rows; j++) {
+ FOR_EACH_MAPPED_COLUMN(k, t, cd) {
+ loss[k] = t->columns[k].data_map.size;
+ DEBUG_LOG("column %i data map: %zu bytes\n", k,
+ t->columns[k].data_map.size);
+ for (n = 0; n < t->num_rows; n++) {
struct osl_object obj;
- ret = get_mapped_object(t, i, j, &obj);
+ ret = get_mapped_object(t, k, n, &obj);
if (ret < 0)
goto err;
- loss[i] -= obj.size;
+ loss[k] -= obj.size;
}
}
ret = 0;
- FOR_EACH_MAPPED_COLUMN(i, t, cd) {
- if (loss[i]) {
+ FOR_EACH_MAPPED_COLUMN(k, t, cd) {
+ if (loss[k]) {
NOTICE_LOG("column %u contains %u lost bytes\n",
- i, loss[i]);
+ k, loss[k]);
ret = 1;
}
}
/* prune_invalid_rows() must be run on the table before calling this */
static int prune_mapped_column(struct osl_table *t, uint32_t col_num, int fd)
{
- int i, ret;
+ int ret;
+ unsigned n;
uint32_t written = 0;
struct osl_column *col = t->columns + col_num;
INFO_LOG("pruning col %u\n", col_num);
- for (i = 0; i < t->num_rows; i++) {
+ for (n = 0; n < t->num_rows; n++) {
struct osl_object obj;
char *index_entry;
- DEBUG_LOG("checking row %u/%u\n", i, t->num_rows);
- ret = get_mapped_object(t, col_num, i, &obj);
+ DEBUG_LOG("checking row %u/%u\n", n, t->num_rows);
+ ret = get_mapped_object(t, col_num, n, &obj);
if (ret < 0)
return ret;
ret = _write_all(fd, (char *)(obj.data), obj.size);
if (ret < 0)
return ret;
written += obj.size;
- ret = get_row_index(t, i, &index_entry);
+ ret = get_row_index(t, n, &index_entry);
if (ret < 0)
return ret;
update_cell_index(index_entry, col, written, obj.size);
static int check_disk_storage_presence(struct osl_table *t)
{
- int ret, i, j;
+ int ret;
+ unsigned k, n;
struct osl_object obj, hash_obj = {.size = HASH_SIZE};
char *ds_name;
const struct osl_column_description *cd;
return 1;
hashes = fsck_malloc(t->num_rows * HASH_SIZE);
INFO_LOG("looking for missing disk storage objects\n");
- for (i = 0; i < t->num_rows; i++) {
- if (row_is_invalid(t, i))
+ for (k = 0; k < t->num_rows; k++) {
+ if (row_is_invalid(t, k))
continue;
- ret = get_mapped_object(t, dsnc, i, &obj);
+ ret = get_mapped_object(t, dsnc, k, &obj);
if (ret < 0)
return ret;
- hash_object(&obj, hashes + k * HASH_SIZE);
- hash_object(t, &obj, hashes + i * HASH_SIZE);
- hash_obj.data = hashes + i * HASH_SIZE;
++ hash_object(t, &obj, hashes + k * HASH_SIZE);
+ hash_obj.data = hashes + k * HASH_SIZE;
osl_add_row(hash_tree_table, &hash_obj);
- ds_name = disk_storage_name_of_hash(t, hashes + i * HASH_SIZE);
- FOR_EACH_DISK_STORAGE_COLUMN(j, t, cd) {
- ret = check_disk_storage_column(t, i, j, ds_name,
+ ds_name = disk_storage_name_of_hash(t, hashes + k * HASH_SIZE);
+ FOR_EACH_DISK_STORAGE_COLUMN(n, t, cd) {
+ ret = check_disk_storage_column(t, k, n, ds_name,
&missing_objects);
if (ret < 0)
goto err;
static int fsck_init(struct osl_table_description *desc, struct osl_table **t)
{
struct osl_object map;
- int ret = map_index(desc, &map);
+ int version, ret = map_index(desc, &map);
if (ret < 0)
goto out;
- ret = read_table_desc(&map, desc);
+ ret = read_table_desc(&map, desc); /* checks table version */
if (ret < 0) {
osl_munmap(map.data, map.size);
goto out;
}
+ version = ret;
set_dummy_contents(desc);
ret = init_table_structure(desc, t);
if (ret < 0) {
osl_munmap(map.data, map.size);
goto out;
}
+ (*t)->version = version;
DEBUG_LOG("unmapping index\n");
osl_munmap(map.data, map.size);
if (OPT_GIVEN(FORCE))
ret = get_mapped_object(t, dsnc, row_num, &obj);
if (ret < 0)
return ret;
- hash_object(&obj, hash);
+ hash_object(t, &obj, hash);
ds_name = disk_storage_name_of_hash(t, hash);
FOR_EACH_DISK_STORAGE_COLUMN(i, t, cd) {
filename = disk_storage_path(t, i, ds_name);
int main(int argc, char **argv)
{
- int i, ret;
+ int ret;
+ unsigned n;
char *errctx = NULL;
const char *dd;
ret = check_all_tables(dd);
goto out;
}
- for (i = 0; i < lls_num_inputs(lpr); i++) {
- ret = check_table(dd, lls_input(i, lpr));
+ for (n = 0; n < lls_num_inputs(lpr); n++) {
+ ret = check_table(dd, lls_input(n, lpr));
if (ret < 0)
break;
}
const struct osl_object *obj)
{
HASH_TYPE hash[HASH_SIZE];
- hash_object(obj, hash);
+ hash_object(t, obj, hash);
return disk_storage_name_of_hash(t, hash);
}
return -E_OSL_NOMEM;
}
- static void column_name_hash(const char *col_name, HASH_TYPE *hash)
+ static void column_name_hash(const struct osl_table *t, const char *col_name,
+ HASH_TYPE *hash)
{
- hash_function(col_name, strlen(col_name), hash);
+ hash_function(t->version, col_name, strlen(col_name), hash);
}
static int init_column_descriptions(struct osl_table *t)
/* the size of the index header without column descriptions */
t->index_header_size = IDX_COLUMN_DESCRIPTIONS;
FOR_EACH_COLUMN(i, t->desc, cd) {
- struct osl_column *col = t->columns + i;
if (cd->storage_flags & OSL_RBTREE) {
if (!cd->compare_function)
return -E_OSL_NO_COMPARE_FUNC;
if (ret < 0)
goto err;
t->index_header_size += index_column_description_size(cd->name);
- column_name_hash(cd->name, col->name_hash);
ret = -E_OSL_DUPLICATE_COL_NAME;
for (j = i + 1; j < t->desc->num_columns; j++) {
const char *name2 = get_column_description(t->desc,
* Read the index header, check for the osl magic string and the table version
* number. Read all information stored in the index header into \a desc.
*
- * \return Standard.
+ * \return The on-disk table version number on success, negative on errors.
*
* \sa struct osl_table_description, osl_create_table.
*/
int read_table_desc(struct osl_object *map, struct osl_table_description *desc)
{
char *buf = map->data;
- uint8_t version, compat_version, create_version;
+ uint8_t table_version;
uint16_t header_size;
int ret, i;
unsigned offset;
return -E_OSL_SHORT_TABLE;
if (strncmp(buf + IDX_OSL_MAGIC, OSL_MAGIC, strlen(OSL_MAGIC)))
return -E_OSL_NO_MAGIC;
- version = read_u8(buf + IDX_VERSION);
- /*
- * The on-disk version consists of two version numbers: the
- * create_version (low 4 bits) is the CURRENT_TABLE_VERSION version
- * number of the library that created the table, and compat_version
- * (high 4 bits) tells us the lowest version of the library that can
- * still read this table.
- */
- create_version = version & 0xf;
- compat_version = version >> 4;
- INFO_LOG("create_version: %u, compat_version: %u\n", create_version,
- compat_version);
- if (create_version < MIN_TABLE_VERSION /* table too old */
- || compat_version > CURRENT_TABLE_VERSION) /* libosl too old */
+ table_version = read_u8(buf + IDX_VERSION);
+ INFO_LOG("osl versions (table/min/current): %u/%u/%u\n",
+ table_version, MIN_TABLE_VERSION, CURRENT_TABLE_VERSION);
+ if (table_version < MIN_TABLE_VERSION /* table too old */
+ || table_version > CURRENT_TABLE_VERSION) /* libosl too old */
return -E_OSL_VERSION_MISMATCH;
desc->flags = read_u8(buf + IDX_TABLE_FLAGS);
desc->num_columns = read_u16(buf + IDX_NUM_COLUMNS);
offset, header_size);
goto err;
}
- return 1;
+ return table_version;
err:
FOR_EACH_COLUMN(i, desc, cd)
free(cd->name);
ret = read_table_desc(&t->index_map, &desc);
if (ret < 0)
return ret;
+ t->version = ret;
ret = -E_OSL_BAD_TABLE_FLAGS;
if (desc.flags != t->desc->flags)
goto out;
sprintf(buf + IDX_OSL_MAGIC, "%s", OSL_MAGIC);
write_u8(buf + IDX_TABLE_FLAGS, t->desc->flags);
write_u8(buf + IDX_DIRTY_FLAG, 0);
- write_u8(buf + IDX_VERSION, CURRENT_TABLE_VERSION
- + (COMPAT_TABLE_VERSION << 4));
+ write_u8(buf + IDX_VERSION, t->version);
write_u16(buf + IDX_NUM_COLUMNS, t->num_mapped_columns + t->num_disk_storage_columns);
write_u16(buf + IDX_HEADER_SIZE, t->index_header_size);
offset = IDX_COLUMN_DESCRIPTIONS;
if (ret < 0)
return ret;
- INFO_LOG("creating %s\n", desc->name);
+ t->version = CURRENT_TABLE_VERSION;
+ INFO_LOG("creating version %u table %s\n", t->version, desc->name);
FOR_EACH_COLUMN(i, t->desc, cd) {
if (cd->storage_type == OSL_NO_STORAGE)
continue;
if (ret < 0)
goto out;
}
+ column_name_hash(t, cd->name, t->columns[i].name_hash);
ret = -E_OSL_NOMEM;
filename = column_filename(t, i);
if (!filename)
}
mark_table_dirty(t);
num_rows = table_num_rows(t);
- if (!num_rows)
- return num_rows;
/* map data files */
- FOR_EACH_MAPPED_COLUMN(i, t, cd) {
- ret = map_column(t, i);
- if (ret < 0)
- goto err;
+ FOR_EACH_COLUMN(i, t->desc, cd) {
+ if (cd->storage_type == OSL_NO_STORAGE)
+ continue;
+ column_name_hash(t, cd->name, t->columns[i].name_hash);
+ if (num_rows > 0 && cd->storage_type == OSL_MAPPED_STORAGE) {
+ ret = map_column(t, i);
+ if (ret < 0)
+ goto err;
+ }
}
return num_rows;
err: /* unmap what is already mapped */
int row_is_invalid(struct osl_table *t, uint32_t row_num)
{
char *row_index;
- int i, ret = get_row_index(t, row_num, &row_index);
+ int ret = get_row_index(t, row_num, &row_index);
+ unsigned n;
if (ret < 0)
return ret;
- for (i = 0; i < t->row_index_size; i++) {
- if ((unsigned char)row_index[i] != 0xff)
+ for (n = 0; n < t->row_index_size; n++) {
+ if ((unsigned char)row_index[n] != 0xff)
return 0;
}
INFO_LOG("row %d is invalid\n", row_num);
*/
int init_rbtrees(struct osl_table *t)
{
- int i, ret;
+ int ret;
+ unsigned n;
const struct osl_column_description *cd;
/* create rbtrees */
- FOR_EACH_RBTREE_COLUMN(i, t, cd)
- t->columns[i].rbtree = RB_ROOT;
+ FOR_EACH_RBTREE_COLUMN(n, t, cd)
+ t->columns[n].rbtree = RB_ROOT;
/* add valid rows to rbtrees */
t->num_invalid_rows = 0;
- for (i = 0; i < t->num_rows; i++) {
+ for (n = 0; n < t->num_rows; n++) {
struct osl_object *volatile_objs;
- ret = row_is_invalid(t, i);
+ ret = row_is_invalid(t, n);
if (ret < 0)
return ret;
if (ret) {
return -E_OSL_NOMEM;
} else
volatile_objs = NULL;
- ret = add_row_to_rbtrees(t, i, volatile_objs, NULL);
+ ret = add_row_to_rbtrees(t, n, volatile_objs, NULL);
if (ret < 0)
return ret;
}
__export int osl_open_table(const struct osl_table_description *table_desc,
struct osl_table **result)
{
- int i, ret;
+ int ret;
struct osl_table *t;
- const struct osl_column_description *cd;
NOTICE_LOG("opening table %s\n", table_desc->name);
ret = init_table_structure(table_desc, &t);
if (ret < 0)
return ret;
- FOR_EACH_DISK_STORAGE_COLUMN(i, t, cd) {
- struct stat statbuf;
- char *dirname = column_filename(t, i);
-
- ret = -E_OSL_NOMEM;
- if (!dirname)
- goto err;
- /* check if directory exists */
- ret = osl_stat(dirname, &statbuf);
- free(dirname);
- if (ret < 0)
- goto err;
- ret = -E_OSL_NOTDIR;
- if (!S_ISDIR(statbuf.st_mode))
- goto err;
- }
ret = map_table(t, MAP_TBL_FL_VERIFY_INDEX);
if (ret < 0)
goto err;
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 */
* The version used by this instance of the library. Written to the index of
* newly created tables.
*/
- #define CURRENT_TABLE_VERSION 1
-
- /*
- * The lowest library version that is able to use tables of version
- * CURRENT_TABLE_VERSION. Also written to the index of new tables. If
- * compat_version(table) > current_version(lib) the table can not be opened.
- */
- #define COMPAT_TABLE_VERSION 0
+ #define CURRENT_TABLE_VERSION 3
/*
* The lowest table version this library understands. On open, if
* \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_ int next_column_of_type(enum osl_storage_type type, int col_num,
- const struct osl_table *t,
+_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;
/**
* 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_object *obj, HASH_TYPE *hash)
+ _static_inline_ void hash_object(const struct osl_table *t,
+ const struct osl_object *obj, HASH_TYPE *hash)
{
- hash_function(obj->data, obj->size, hash);
+ hash_function(t->version, obj->data, obj->size, hash);
}
/**