--- /dev/null
+/*
+ * Copyright (C) 2009 Andre Noll <maan@systemlinux.org>
+ *
+ * Licensed under the GPL v2. For licencing details see COPYING.
+ */
+
+/* A simple program that all hard links of a given directory. */
+
+#include <inttypes.h>
+#include <osl.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <errno.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+
+/**
+ * We are lazy here and use an osl table with only a single column of type
+ * OSL_NO_STORAGE. This is possible because we need only one red black tree
+ * for hlt. So we pack everything into one structure and use a fixed-size
+ * column for the data of each (device, inode) pair.
+ *
+ * This approach keeps things simple but is a bit dangerous: As the compare
+ * function for the red black tree, hld_compare, uses only the dev_id and the
+ * inode fields of struct hard_link_data it's OK to modify the other fields of
+ * the struct in-place.
+ */
+enum hlt_columns {
+ COL_HLD,
+ NUM_HLT_COLUMNS
+};
+
+/**
+ * The data of one "row" of the table.
+ */
+struct hard_link_data {
+ /** The st_dev field from struct stat. */
+ dev_t dev_id;
+ /** The st_ino field from struct stat. */
+ ino_t inode;
+ /** The st_nlink field from struct stat. */
+ nlink_t num_links;
+ /** The array of pathnames. */
+ char **paths;
+ /** The size of \a paths. */
+ unsigned array_size;
+ /** The number of hard links found so far. */
+ unsigned num_paths;
+};
+
+/**
+ * Compare two hard_link_data structs.
+ *
+ * \param obj1 Pointer to the first object.
+ * \param obj2 Pointer to the second object.
+ *
+ * \return It returns an integer less than, equal to, or greater than zero if
+ * \a obj1 is found, respectively, to be less than, to match, or be greater than
+ * obj2.
+ *
+ * \sa strcmp(3), strncmp(3), osl_compare_func.
+ */
+static int hld_compare(const struct osl_object *obj1, const struct osl_object *obj2)
+{
+ const struct hard_link_data *hld1 = obj1->data;
+ const struct hard_link_data *hld2 = obj2->data;
+
+ if (hld1->inode < hld2->inode)
+ return -1;
+ if (hld1->inode > hld2->inode)
+ return 1;
+ if (hld1->dev_id < hld2->dev_id)
+ return -1;
+ if (hld1->dev_id > hld2->dev_id)
+ return 1;
+ return 0;
+}
+
+static struct osl_column_description hlt_table_cols[] = {
+ [COL_HLD] = {
+ .storage_type = OSL_NO_STORAGE,
+ .storage_flags = OSL_RBTREE | OSL_UNIQUE | OSL_FIXED_SIZE,
+ .data_size = sizeof(struct hard_link_data),
+ .name = "hard link data",
+ .compare_function = hld_compare,
+ },
+};
+
+static struct osl_table *table;
+
+static struct osl_table_description hlt_table_desc = {
+ /* dir not needed because all cols have storage type NO_STORAGE */
+ .dir = NULL,
+ .name = "hlt",
+ .num_columns = NUM_HLT_COLUMNS,
+ .flags = 0,
+ .column_descriptions = hlt_table_cols,
+};
+
+static void print_usage_and_die(void)
+{
+ fprintf(stderr, "usage:\n\thlt <dirname>\n");
+ exit(EXIT_FAILURE);
+}
+
+/* either succeeds or exits */
+static void create_table_or_die(int argc, char **argv)
+{
+ struct stat statbuf;
+ int ret;
+
+ /* some sanity checks */
+ if (argc != 2)
+ print_usage_and_die();
+ if (lstat(argv[1], &statbuf) == -1) {
+ fprintf(stderr, "no such dir: %s\n", argv[1]);
+ exit(EXIT_FAILURE);
+ }
+ if (!S_ISDIR(statbuf.st_mode)) {
+ fprintf(stderr, "not a dir: %s\n", argv[1]);
+ exit(EXIT_FAILURE);
+ }
+
+ //fprintf(stderr, "col_desc: %p\n", hlt_table_desc.column_descriptions);
+ /* create the osl table... */
+ ret = osl_create_table(&hlt_table_desc);
+ if (ret < 0) {
+ fprintf(stderr, "%s\n", osl_strerror(-ret));
+ exit(EXIT_FAILURE);
+ }
+ /* ...and open it. */
+ ret = osl_open_table(&hlt_table_desc, &table);
+ if (ret < 0) {
+ fprintf(stderr, "osl_open_table: %s\n", osl_strerror(-ret));
+ exit(EXIT_FAILURE);
+ }
+}
+
+static void *malloc_or_die(size_t size)
+{
+ void *ret = malloc(size);
+
+ if (ret)
+ return ret;
+ fprintf(stderr, "out of memory\n");
+ exit(EXIT_FAILURE);
+}
+
+static char *make_name(const char *dirname, const char *subdirname)
+{
+ size_t len = strlen(dirname) + strlen(subdirname) + 2;
+ char *name = malloc_or_die(len);
+
+ sprintf(name, "%s/%s", dirname, subdirname);
+ return name;
+}
+
+static int add_hard_link(char *path, struct stat *s)
+{
+ int ret;
+ struct osl_row *row;
+ struct hard_link_data *entry;
+
+ /*
+ * We need to find out whether a row containing the (dev_t, inode) pair
+ * given by \a s already exists in the osl table. Therefore we init a
+ * hard_link_data structure with only these two fields initialized...
+ */
+ struct hard_link_data hld = {
+ .dev_id = s->st_dev,
+ .inode = s->st_ino
+ };
+
+ /* ... and make an osl object out of it. */
+ struct osl_object obj = {
+ .data = &hld,
+ .size = sizeof(hld)
+ };
+
+ /*
+ * check whether there is a row with the same dev_id and inode values.
+ */
+ ret = osl_get_row(table, COL_HLD, &obj, &row);
+
+ /* Abort on real errors. */
+ if (ret < 0 && ret != -E_OSL_RB_KEY_NOT_FOUND)
+ return ret;
+
+ if (ret < 0) { /* key not found, add a new row to the table. */
+ struct osl_object objs[NUM_HLT_COLUMNS];
+
+ entry = malloc_or_die(sizeof(*entry));
+ memset(entry, 0, sizeof(*entry));
+
+ /* copy relevant data from struct stat */
+ entry->dev_id = s->st_dev;
+ entry->inode = s->st_ino;
+ entry->num_links = s->st_nlink;
+
+ entry->array_size = s->st_nlink; /* that's usually enough */
+ entry->paths = malloc_or_die(entry->array_size * sizeof(char *));
+
+ /* Add the new row */
+ objs[COL_HLD].data = entry;
+ objs[COL_HLD].size = sizeof(*entry);
+ ret = osl_add_row(table, objs);
+ if (ret < 0) {
+ free(entry);
+ return ret;
+ }
+ } else { /* OK, we have a row, retrieve the hld struct */
+ ret = osl_get_object(table, row, COL_HLD, &obj);
+ if (ret < 0)
+ return ret;
+ entry = obj.data;
+ }
+
+ /*
+ * Now 'entry' points to a struct hard_link_data and we may modify
+ * everything in-place except dev_id and inode (these two fields must
+ * not be changed directly because that would mess up the rbtree).
+ *
+ * So increase the counter and add the new path to the entry->paths
+ * array.
+ */
+
+ if (entry->num_paths >= entry->array_size) {
+ /*
+ * New hard links have been created by someone and the
+ * entry->paths array is too small. Double its size.
+ */
+ entry->array_size *= 2;
+ entry->paths = realloc(entry->paths,
+ entry->array_size * sizeof(char *));
+ if (!entry->paths) {
+ fprintf(stderr, "realloc error. array size: %u\n",
+ entry->array_size);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ entry->paths[entry->num_paths] = path;
+ entry->num_paths++;
+ return 1;
+}
+
+/**
+ * Traverse the given directory recursively. Each regular file
+ * with hard link count > 1 is going to be added to the table.
+ */
+static int populate_table(char *dirname)
+{
+ struct dirent *entry;
+ int ret;
+ DIR *dir = opendir(dirname);
+
+ if (!dir) {
+ fprintf(stderr, "failed to open dir %s\n", dirname);
+ /* Ignore this error by returning success. */
+ return 1;
+ }
+ while ((entry = readdir(dir))) {
+ mode_t m;
+ struct stat s;
+ char *new_name;
+
+ if (!strcmp(entry->d_name, "."))
+ continue;
+ if (!strcmp(entry->d_name, ".."))
+ continue;
+ new_name = make_name(dirname, entry->d_name);
+ if (lstat(new_name, &s) == -1) {
+ fprintf(stderr, "lstat error for %s (%s)\n",
+ new_name, strerror(errno));
+ free(new_name);
+ continue;
+ }
+ m = s.st_mode;
+ if (S_ISDIR(m)) {
+ /* recurse and pass the name of the subdir */
+ ret = populate_table(new_name);
+ free(new_name);
+ if (ret < 0) /* fatal error */
+ goto out;
+ continue;
+ }
+ if (!S_ISREG(m) || s.st_nlink < 2) {
+ free(new_name);
+ continue;
+ }
+ /* regular file with hard links, add it to the table */
+ add_hard_link(new_name, &s);
+ }
+ ret = 1; /* success */
+out:
+ closedir(dir);
+ return ret;
+}
+
+static int print_row(struct osl_row *row, void *data)
+{
+ struct hard_link_data *hld;
+ struct osl_object obj;
+ int i, ret = osl_get_object(table, row, COL_HLD, &obj);
+
+ if (ret < 0)
+ return ret;
+ hld = obj.data;
+ printf("%d/%d: Found %u/%u hard links:\n", (int)hld->dev_id,
+ (int)hld->inode, hld->num_paths, hld->num_links);
+ for (i = 0; i < hld->num_paths; i++) {
+ printf("\t%s\n", hld->paths[i]);
+ free(hld->paths[i]);
+ }
+ free(hld->paths);
+ return 1;
+}
+
+int main(int argc, char **argv)
+{
+ int ret;
+
+ create_table_or_die(argc, argv);
+ ret = populate_table(argv[1]);
+ if (ret < 0)
+ goto out;
+ /*
+ * Print results by iterating over all rows and calling print_row() for
+ * each row.
+ */
+ ret = osl_rbtree_loop(table, COL_HLD, NULL, print_row);
+out:
+ /* This frees the memory pointed to by the osl objects. */
+ osl_close_table(table, OSL_FREE_VOLATILE);
+ return ret < 0? EXIT_FAILURE : EXIT_SUCCESS;
+}
--- /dev/null
+/*
+ * Copyright (C) 2009 Andre Noll <maan@systemlinux.org>
+ *
+ * Licensed under the GPL v2. For licencing details see COPYING.
+ */
+
+/*
+ * A simple file archiver with many limitations. Do not use it. It is just an
+ * example to illustrate programming with libosl.
+ */
+
+#include <inttypes.h>
+#include <osl.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <errno.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+
+enum osltar_columns {
+ OTC_NAME,
+ OTC_DATA,
+ NUM_OT_COLUMNS
+};
+
+/**
+ * Compare two osl objects of string type.
+ *
+ * \param obj1 Pointer to the first object.
+ * \param obj2 Pointer to the second object.
+ *
+ * In any case, only \p MIN(obj1->size, obj2->size) characters of each string
+ * are taken into account.
+ *
+ * \return It returns an integer less than, equal to, or greater than zero if
+ * \a obj1 is found, respectively, to be less than, to match, or be greater than
+ * obj2.
+ *
+ * \sa strcmp(3), strncmp(3), osl_compare_func.
+ */
+int string_compare(const struct osl_object *obj1, const struct osl_object *obj2)
+{
+ const char *str1 = (const char *)obj1->data;
+ const char *str2 = (const char *)obj2->data;
+ return strcmp(str1, str2);
+}
+
+static struct osl_column_description tar_table_cols[] = {
+ [OTC_NAME] = {
+ .storage_type = OSL_MAPPED_STORAGE,
+ .storage_flags = OSL_RBTREE | OSL_UNIQUE,
+ .name = "filename",
+ .compare_function = string_compare,
+ },
+ [OTC_DATA] = {
+ .storage_type = OSL_MAPPED_STORAGE,
+ .storage_flags = 0,
+ .name = "data",
+ },
+};
+
+static struct osl_table *table;
+
+static struct osl_table_description tar_table_desc = {
+ .name = "tar_table",
+ .num_columns = NUM_OT_COLUMNS,
+ .flags = 0,
+ .column_descriptions = tar_table_cols,
+};
+
+static void print_usage_and_die(void)
+{
+ fprintf(stderr, "usage:\n\tosltar c <db_dir> <data_dir>\n");
+ fprintf(stderr, "\tosltar x <db_dir> file1 [file2...]\n");
+ fprintf(stderr, "\tosltar t <db_dir>\n");
+ exit(EXIT_FAILURE);
+}
+
+/**
+ * Open a file and map it into memory.
+ */
+static int mmap_full_file(const char *path, void **map,
+ size_t *size)
+{
+ int fd, ret, mmap_prot, mmap_flags;
+ struct stat file_status;
+
+ mmap_prot = PROT_READ;
+ mmap_flags = MAP_PRIVATE;
+ ret = open(path, O_RDONLY, 0);
+ if (ret < 0) {
+ fprintf(stderr, "open: %s\n", strerror(errno));
+ return ret;
+ }
+ fd = ret;
+ if (fstat(fd, &file_status) < 0) {
+ ret = -1;
+ fprintf(stderr, "fstat: %s\n", strerror(errno));
+ goto out;
+ }
+ *size = file_status.st_size;
+ if (!*size) {
+ fprintf(stderr, "can not add empty file: %s\n", path);
+ goto out;
+ }
+ *map = mmap(NULL, *size, mmap_prot, mmap_flags, fd, 0);
+ if (*map == MAP_FAILED) {
+ fprintf(stderr, "map failed: %s\n", path);
+ *map = NULL;
+ goto out;
+ }
+ ret = 1;
+out:
+ close(fd);
+ return ret;
+}
+
+
+static int add_file(char *name)
+{
+ int ret;
+ struct osl_object objs[NUM_OT_COLUMNS] = {
+ [OTC_NAME] = {
+ .data = name,
+ .size = strlen(name) + 1
+ },
+ };
+ printf("%s\n", name);
+ ret = mmap_full_file(name, &objs[OTC_DATA].data,
+ &objs[OTC_DATA].size);
+ if (ret < 0)
+ return ret;
+ return osl_add_row(table, objs);
+}
+
+static int populate_table(const char *dirname)
+{
+ struct dirent *entry;
+ DIR *dir;
+ int ret = chdir(dirname);
+
+ if (ret < 0) {
+ fprintf(stderr, "chdir: %s\n", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ dir = opendir(".");
+ if (!dir) {
+ fprintf(stderr, "opendir: %s\n", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ while ((entry = readdir(dir))) {
+ mode_t m;
+ struct stat s;
+
+ if (!strcmp(entry->d_name, "."))
+ continue;
+ if (!strcmp(entry->d_name, ".."))
+ continue;
+ if (lstat(entry->d_name, &s) == -1)
+ continue;
+ m = s.st_mode;
+ if (!S_ISREG(m))
+ continue;
+ ret = add_file(entry->d_name);
+ if (ret < 0)
+ goto out;
+ }
+ ret = 1;
+out:
+ closedir(dir);
+ return ret;
+}
+
+static int com_create(int argc, char **argv)
+{
+ struct stat statbuf;
+ int ret;
+
+ if (argc != 3)
+ print_usage_and_die();
+ if (lstat(argv[2], &statbuf) == -1) {
+ fprintf(stderr, "no such dir: %s\n", argv[2]);
+ exit(EXIT_FAILURE);
+ }
+ if (!S_ISDIR(statbuf.st_mode)) {
+ fprintf(stderr, "not a dir: %s\n", argv[2]);
+ exit(EXIT_FAILURE);
+ }
+ tar_table_desc.dir = argv[1];
+ ret = osl_create_table(&tar_table_desc);
+ if (ret < 0) {
+ fprintf(stderr, "failed to create table\n");
+ exit(EXIT_FAILURE);
+
+ }
+ ret = osl_open_table(&tar_table_desc, &table);
+ if (ret < 0) {
+ fprintf(stderr, "osl_open_table: %s\n", osl_strerror(-ret));
+ exit(EXIT_FAILURE);
+ }
+ ret = populate_table(argv[2]);
+ osl_close_table(table, OSL_MARK_CLEAN);
+ return ret < 0? EXIT_FAILURE : EXIT_SUCCESS;
+}
+
+/**
+ * Write the whole buffer to a file descriptor.
+ */
+ssize_t write_all(int fd, const void *buf, size_t size)
+{
+ const char *b = buf;
+ while (size) {
+ ssize_t ret = write(fd, b, size);
+ if (ret < 0) {
+ fprintf(stderr, "open: %s\n", strerror(errno));
+ return ret;
+
+ }
+ b += ret;
+ size -= ret;
+ }
+ return 1;
+}
+
+/**
+ * Open a file, write the given buffer and close the file.
+ */
+static int write_file(const char *filename, const void *buf, size_t size)
+{
+ int ret, fd;
+
+ ret = open(filename, O_WRONLY | O_CREAT | O_EXCL, 0644);
+ if (ret < 0) {
+ fprintf(stderr, "open: %s\n", strerror(errno));
+ return ret;
+
+ }
+ fd = ret;
+ printf("%s\n", filename);
+ ret = write_all(fd, buf, size);
+ if (ret < 0)
+ goto out;
+ ret = 1;
+out:
+ close(fd);
+ return ret;
+}
+
+static int extract_file(char *name)
+{
+ int ret;
+ struct osl_row *row;
+ struct osl_object obj = {.data = name, .size = strlen(name) + 1};
+
+ ret = osl_get_row(table, OTC_NAME, &obj, &row);
+ if (ret < 0) {
+ fprintf(stderr, "osl_get_row(%s): %s\n", name,
+ osl_strerror(-ret));
+ return ret;
+ }
+ ret = osl_get_object(table, row, OTC_DATA, &obj);
+ if (ret < 0) {
+ fprintf(stderr, "osl_get_object: %s\n", osl_strerror(-ret));
+ return ret;
+ }
+ return write_file(name, obj.data, obj.size);
+}
+
+static int com_extract(int argc, char **argv)
+{
+ int i, ret;
+
+ if (argc < 3)
+ print_usage_and_die();
+ tar_table_desc.dir = argv[1];
+ ret = osl_open_table(&tar_table_desc, &table);
+ if (ret < 0) {
+ fprintf(stderr, "osl_open_table: %s\n", osl_strerror(-ret));
+ exit(EXIT_FAILURE);
+
+ }
+ for (i = 2; i < argc; i++) {
+ ret = extract_file(argv[i]);
+ if (ret < 0)
+ goto out;
+ }
+out:
+ osl_close_table(table, OSL_MARK_CLEAN);
+ return ret;
+}
+
+static int list_entry(struct osl_row *row, void *data)
+{
+ struct osl_object obj;
+ int ret = osl_get_object(table, row, OTC_NAME, &obj);
+ if (ret < 0) {
+ fprintf(stderr, "osl_get_object: %s\n", osl_strerror(-ret));
+ return ret;
+ }
+ printf("%s\n", (char *)obj.data);
+ return 1;
+}
+
+int com_list(int argc, char **argv)
+{
+ int ret;
+
+ if (argc != 2)
+ print_usage_and_die();
+ tar_table_desc.dir = argv[1];
+ ret = osl_open_table(&tar_table_desc, &table);
+ if (ret < 0) {
+ fprintf(stderr, "osl_open_table: %s\n", osl_strerror(-ret));
+ exit(EXIT_FAILURE);
+
+ }
+ ret = osl_rbtree_loop(table, OTC_NAME, NULL, list_entry);
+ osl_close_table(table, OSL_MARK_CLEAN);
+ return ret;
+}
+
+int main(int argc, char **argv)
+{
+ if (argc < 2)
+ print_usage_and_die();
+ if (!strcmp(argv[1], "c"))
+ return com_create(argc - 1, argv + 1);
+ if (!strcmp(argv[1], "x"))
+ return com_extract(argc - 1, argv + 1);
+ if (!strcmp(argv[1], "t"))
+ return com_list(argc - 1, argv + 1);
+ print_usage_and_die();
+ exit(EXIT_FAILURE);
+
+}