Add create.c.
authorAndre Noll <maan@systemlinux.org>
Sun, 1 Jun 2008 17:52:17 +0000 (19:52 +0200)
committerAndre Noll <maan@systemlinux.org>
Sun, 1 Jun 2008 17:52:17 +0000 (19:52 +0200)
Oops, forgot to check it in..

create.c [new file with mode: 0644]

diff --git a/create.c b/create.c
new file mode 100644 (file)
index 0000000..1ce2e54
--- /dev/null
+++ b/create.c
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2008 Andre Noll <maan@systemlinux.org>
+ *
+ * Licensed under the GPL v2. For licencing details see COPYING.
+ */
+
+/** \file create.c The create mode of adu. */
+
+#include <dirent.h> /* readdir() */
+#include "adu.h"
+#include "gcc-compat.h"
+#include "cmdline.h"
+#include "fd.h"
+#include "string.h"
+#include "error.h"
+#include "portable_io.h"
+
+/* Id of the device containing the base dir. */
+static dev_t device_id;
+
+static int write_uid(struct user_info *ui, void *data)
+{
+       char **p = data;
+
+       write_u32(*p, ui->uid);
+       *p += sizeof(uint32_t);
+       return 1;
+}
+
+static int write_uid_list(void)
+{
+       char *buf, *p, *filename;
+       uint32_t count = 0;
+       struct user_info *ui;
+       size_t size = num_uids * sizeof(uint32_t);
+       int ret;
+
+       if (!num_uids)
+               return 0;
+       buf = p = adu_malloc(size);
+       ret = for_each_admissible_user(write_uid, &p);
+       if (ret < 0)
+               goto out;
+       filename = get_uid_list_name();
+       ret = adu_write_file(filename, buf, size);
+       free(filename);
+out:
+       free(buf);
+       return ret;
+}
+
+static int add_directory(char *dirname, uint64_t *dir_num, uint64_t *parent_dir_num,
+               uint64_t *dir_size, uint64_t *dir_files)
+{
+       struct osl_object dir_objects[NUM_DT_COLUMNS];
+
+       INFO_LOG("adding #%llu: %s\n", (long long unsigned)*dir_num, dirname);
+       dir_objects[DT_NAME].data = dirname;
+       dir_objects[DT_NAME].size = strlen(dirname) + 1;
+       dir_objects[DT_NUM].data = dir_num;
+       dir_objects[DT_NUM].size = sizeof(*dir_num);
+       dir_objects[DT_PARENT_NUM].data = parent_dir_num;
+       dir_objects[DT_PARENT_NUM].size = sizeof(*parent_dir_num);
+       dir_objects[DT_BYTES].data = dir_size;
+       dir_objects[DT_BYTES].size = sizeof(*dir_size);
+       dir_objects[DT_FILES].data = dir_files;
+       dir_objects[DT_FILES].size = sizeof(*dir_files);
+       return osl(osl_add_row(dir_table, dir_objects));
+}
+
+static int update_user_row(struct osl_table *t, uint64_t dir_num,
+               uint64_t *add)
+{
+       struct osl_row *row;
+       struct osl_object obj = {.data = &dir_num, .size = sizeof(dir_num)};
+
+       int ret = osl(osl_get_row(t, UT_DIR_NUM, &obj, &row));
+
+       if (ret == -E_OSL && osl_errno != E_OSL_RB_KEY_NOT_FOUND)
+               return ret;
+       if (ret < 0) { /* this is the first file we add */
+               struct osl_object objects[NUM_UT_COLUMNS];
+               uint64_t num_files = 1;
+
+               objects[UT_DIR_NUM].data = &dir_num;
+               objects[UT_DIR_NUM].size = sizeof(dir_num);
+               objects[UT_BYTES].data = add;
+               objects[UT_BYTES].size = sizeof(*add);
+               objects[UT_FILES].data = &num_files;
+               objects[UT_FILES].size = sizeof(num_files);
+               INFO_LOG("######################### ret: %d\n", ret);
+               ret = osl(osl_add_row(t, objects));
+               INFO_LOG("######################### ret: %d\n", ret);
+               return ret;
+       } else { /* add size and increment file count */
+               uint64_t num;
+               struct osl_object obj1, obj2 = {.data = &num, .size = sizeof(num)};
+
+               ret = osl(osl_get_object(t, row, UT_BYTES, &obj1));
+               if (ret < 0)
+                       return ret;
+               num = *(uint64_t *)obj1.data + *add;
+               ret = osl(osl_update_object(t, row, UT_BYTES, &obj2));
+               if (ret < 0)
+                       return ret;
+               ret = osl(osl_get_object(t, row, UT_FILES, &obj1));
+               if (ret < 0)
+                       return ret;
+               num = *(uint64_t *)obj1.data + 1;
+               return osl(osl_update_object(t, row, UT_FILES, &obj2));
+       }
+}
+
+static int scan_dir(char *dirname, uint64_t *parent_dir_num)
+{
+       DIR *dir;
+       struct dirent *entry;
+       int ret, cwd_fd, ret2;
+       uint64_t dir_size = 0, dir_files = 0;
+       uint64_t this_dir_num = ++num_dirs;
+
+       check_signals();
+       DEBUG_LOG("----------------- %llu: %s\n", (long long unsigned)num_dirs, dirname);
+       ret = adu_opendir(dirname, &dir, &cwd_fd);
+       if (ret < 0) {
+               if (ret != -ERRNO_TO_ERROR(EACCES))
+                       return ret;
+               WARNING_LOG("permission denied for %s\n", dirname);
+               return 1;
+       }
+       while ((entry = readdir(dir))) {
+               mode_t m;
+               struct stat s;
+               uint32_t uid;
+               uint64_t size;
+               struct user_info *ui;
+
+               if (!strcmp(entry->d_name, "."))
+                       continue;
+               if (!strcmp(entry->d_name, ".."))
+                       continue;
+               if (lstat(entry->d_name, &s) == -1) {
+                       WARNING_LOG("lstat error for %s/%s\n", dirname,
+                               entry->d_name);
+                       continue;
+               }
+               m = s.st_mode;
+               if (!S_ISREG(m) && !S_ISDIR(m))
+                       continue;
+               if (S_ISDIR(m)) {
+                       if (conf.one_file_system_given && s.st_dev != device_id)
+                               continue;
+                       ret = scan_dir(entry->d_name, &this_dir_num);
+                       if (ret < 0)
+                               goto out;
+                       continue;
+               }
+               /* regular file */
+               size = s.st_size;
+               dir_size += size;
+               num_bytes += size;
+               dir_files++;
+               num_files++;
+               uid = s.st_uid;
+               ret = search_uid(uid, CREATE_USER_TABLE | OPEN_USER_TABLE, &ui);
+               if (ret < 0)
+                       goto out;
+               ui->bytes += size;
+               ui->files++;
+               ret = update_user_row(ui->table, this_dir_num, &size);
+               if (ret < 0)
+                       goto out;
+       }
+       ret = add_directory(dirname, &this_dir_num, parent_dir_num,
+                       &dir_size, &dir_files);
+out:
+       closedir(dir);
+       ret2 = adu_fchdir(cwd_fd);
+       if (ret2 < 0 && ret >= 0)
+               ret = ret2;
+       close(cwd_fd);
+       return ret;
+}
+
+int com_create()
+{
+       uint64_t zero = 0ULL;
+       int ret;
+       struct stat statbuf;
+
+       if (lstat(conf.base_dir_arg, &statbuf) == -1)
+               return -ERRNO_TO_ERROR(errno);
+       if (!S_ISDIR(statbuf.st_mode))
+               return -ERRNO_TO_ERROR(ENOTDIR);
+       device_id = statbuf.st_dev;
+       create_hash_table(conf.hash_table_bits_arg);
+       ret = open_dir_table(1);
+       if (ret < 0)
+               return ret;
+       check_signals();
+       ret = scan_dir(conf.base_dir_arg, &zero);
+       if (ret < 0)
+               goto out;
+       ret = write_uid_list();
+out:
+       close_all_tables();
+       return ret;
+}