Make user.c independent of command line options.
[adu.git] / create.c
1 /*
2 * Copyright (C) 2008 Andre Noll <maan@systemlinux.org>
3 *
4 * Licensed under the GPL v2. For licencing details see COPYING.
5 */
6
7 /** \file create.c \brief The create mode of adu. */
8
9 #include <dirent.h> /* readdir() */
10 #include "format.h"
11 #include "adu.h"
12 #include "gcc-compat.h"
13 #include "cmdline.h"
14 #include "fd.h"
15 #include "string.h"
16 #include "error.h"
17 #include "user.h"
18
19 /* Id of the device containing the base dir. */
20 static dev_t device_id;
21
22 static int add_directory(char *dirname, uint64_t *dir_num, uint64_t *parent_dir_num,
23 uint64_t *dir_size, uint64_t *dir_files)
24 {
25 struct osl_object dir_objects[NUM_DT_COLUMNS];
26
27 INFO_LOG("adding #%llu: %s\n", (long long unsigned)*dir_num, dirname);
28 dir_objects[DT_NAME].data = dirname;
29 dir_objects[DT_NAME].size = strlen(dirname) + 1;
30 dir_objects[DT_NUM].data = dir_num;
31 dir_objects[DT_NUM].size = sizeof(*dir_num);
32 dir_objects[DT_PARENT_NUM].data = parent_dir_num;
33 dir_objects[DT_PARENT_NUM].size = sizeof(*parent_dir_num);
34 dir_objects[DT_BYTES].data = dir_size;
35 dir_objects[DT_BYTES].size = sizeof(*dir_size);
36 dir_objects[DT_FILES].data = dir_files;
37 dir_objects[DT_FILES].size = sizeof(*dir_files);
38 return osl(osl_add_row(dir_table, dir_objects));
39 }
40
41 static int update_user_row(struct osl_table *t, uint64_t dir_num,
42 uint64_t *add)
43 {
44 struct osl_row *row;
45 struct osl_object obj = {.data = &dir_num, .size = sizeof(dir_num)};
46
47 int ret = osl(osl_get_row(t, UT_DIR_NUM, &obj, &row));
48
49 if (ret == -E_OSL && osl_errno != E_OSL_RB_KEY_NOT_FOUND)
50 return ret;
51 if (ret < 0) { /* this is the first file we add */
52 struct osl_object objects[NUM_UT_COLUMNS];
53 uint64_t num_files = 1;
54
55 objects[UT_DIR_NUM].data = &dir_num;
56 objects[UT_DIR_NUM].size = sizeof(dir_num);
57 objects[UT_BYTES].data = add;
58 objects[UT_BYTES].size = sizeof(*add);
59 objects[UT_FILES].data = &num_files;
60 objects[UT_FILES].size = sizeof(num_files);
61 ret = osl(osl_add_row(t, objects));
62 return ret;
63 } else { /* add size and increment file count */
64 uint64_t num;
65 struct osl_object obj1, obj2 = {.data = &num, .size = sizeof(num)};
66
67 ret = osl(osl_get_object(t, row, UT_BYTES, &obj1));
68 if (ret < 0)
69 return ret;
70 num = *(uint64_t *)obj1.data + *add;
71 ret = osl(osl_update_object(t, row, UT_BYTES, &obj2));
72 if (ret < 0)
73 return ret;
74 ret = osl(osl_get_object(t, row, UT_FILES, &obj1));
75 if (ret < 0)
76 return ret;
77 num = *(uint64_t *)obj1.data + 1;
78 return osl(osl_update_object(t, row, UT_FILES, &obj2));
79 }
80 }
81
82 static int scan_dir(char *dirname, uint64_t *parent_dir_num)
83 {
84 DIR *dir;
85 struct dirent *entry;
86 int ret, cwd_fd, ret2;
87 uint64_t dir_size = 0, dir_files = 0;
88 /* dir count. */
89 static uint64_t current_dir_num;
90
91 uint64_t this_dir_num = ++current_dir_num;
92
93 check_signals();
94 DEBUG_LOG("----------------- %llu: %s\n", (long long unsigned)current_dir_num, dirname);
95 ret = adu_opendir(dirname, &dir, &cwd_fd);
96 if (ret < 0) {
97 if (ret != -ERRNO_TO_ERROR(EACCES))
98 return ret;
99 WARNING_LOG("permission denied for %s\n", dirname);
100 return 1;
101 }
102 while ((entry = readdir(dir))) {
103 mode_t m;
104 struct stat64 s;
105 uint32_t uid;
106 uint64_t size;
107 struct user_info *ui;
108
109 if (!strcmp(entry->d_name, "."))
110 continue;
111 if (!strcmp(entry->d_name, ".."))
112 continue;
113 if (lstat64(entry->d_name, &s) == -1) {
114 WARNING_LOG("lstat64 error for %s/%s (%s)\n",
115 dirname, entry->d_name, strerror(errno));
116 continue;
117 }
118 m = s.st_mode;
119 if (!S_ISREG(m) && !S_ISDIR(m))
120 continue;
121 if (S_ISDIR(m)) {
122 if (conf.one_file_system_given && s.st_dev != device_id)
123 continue;
124 ret = scan_dir(entry->d_name, &this_dir_num);
125 if (ret < 0)
126 goto out;
127 continue;
128 }
129 /* regular file */
130 size = s.st_size;
131 dir_size += size;
132 dir_files++;
133 uid = s.st_uid;
134 ret = create_user_table(conf.database_dir_arg, uid, &ui);
135 if (ret < 0)
136 goto out;
137 ret = update_user_row(ui->table, this_dir_num, &size);
138 if (ret < 0)
139 goto out;
140 }
141 ret = add_directory(dirname, &this_dir_num, parent_dir_num,
142 &dir_size, &dir_files);
143 out:
144 closedir(dir);
145 ret2 = adu_fchdir(cwd_fd);
146 if (ret2 < 0 && ret >= 0)
147 ret = ret2;
148 close(cwd_fd);
149 return ret;
150 }
151
152 /**
153 * The main function of the create mode.
154 *
155 * \return Standard.
156 */
157 int com_create(void)
158 {
159 uint64_t zero = 0ULL;
160 int ret;
161 struct stat statbuf;
162
163 if (lstat(conf.base_dir_arg, &statbuf) == -1)
164 return -ERRNO_TO_ERROR(errno);
165 if (!S_ISDIR(statbuf.st_mode))
166 return -ERRNO_TO_ERROR(ENOTDIR);
167 device_id = statbuf.st_dev;
168 create_hash_table(conf.hash_table_bits_arg);
169 ret = open_dir_table(1);
170 if (ret < 0)
171 return ret;
172 check_signals();
173 ret = scan_dir(conf.base_dir_arg, &zero);
174 if (ret < 0)
175 goto out;
176 ret = write_uid_file(conf.database_dir_arg);
177 out:
178 return ret;
179 }