Fix prototype of com_create().
[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 The create mode of adu. */
8
9 #include <dirent.h> /* readdir() */
10 #include "adu.h"
11 #include "gcc-compat.h"
12 #include "cmdline.h"
13 #include "fd.h"
14 #include "string.h"
15 #include "error.h"
16 #include "portable_io.h"
17
18 /* Id of the device containing the base dir. */
19 static dev_t device_id;
20
21 static int write_uid(struct user_info *ui, void *data)
22 {
23 char **p = data;
24
25 write_u32(*p, ui->uid);
26 *p += sizeof(uint32_t);
27 return 1;
28 }
29
30 static int write_uid_list(void)
31 {
32 char *buf, *p, *filename;
33 size_t size = num_uids * sizeof(uint32_t);
34 int ret;
35
36 if (!num_uids)
37 return 0;
38 buf = p = adu_malloc(size);
39 ret = for_each_admissible_user(write_uid, &p);
40 if (ret < 0)
41 goto out;
42 filename = get_uid_list_name();
43 ret = adu_write_file(filename, buf, size);
44 free(filename);
45 out:
46 free(buf);
47 return ret;
48 }
49
50 static int add_directory(char *dirname, uint64_t *dir_num, uint64_t *parent_dir_num,
51 uint64_t *dir_size, uint64_t *dir_files)
52 {
53 struct osl_object dir_objects[NUM_DT_COLUMNS];
54
55 INFO_LOG("adding #%llu: %s\n", (long long unsigned)*dir_num, dirname);
56 dir_objects[DT_NAME].data = dirname;
57 dir_objects[DT_NAME].size = strlen(dirname) + 1;
58 dir_objects[DT_NUM].data = dir_num;
59 dir_objects[DT_NUM].size = sizeof(*dir_num);
60 dir_objects[DT_PARENT_NUM].data = parent_dir_num;
61 dir_objects[DT_PARENT_NUM].size = sizeof(*parent_dir_num);
62 dir_objects[DT_BYTES].data = dir_size;
63 dir_objects[DT_BYTES].size = sizeof(*dir_size);
64 dir_objects[DT_FILES].data = dir_files;
65 dir_objects[DT_FILES].size = sizeof(*dir_files);
66 return osl(osl_add_row(dir_table, dir_objects));
67 }
68
69 static int update_user_row(struct osl_table *t, uint64_t dir_num,
70 uint64_t *add)
71 {
72 struct osl_row *row;
73 struct osl_object obj = {.data = &dir_num, .size = sizeof(dir_num)};
74
75 int ret = osl(osl_get_row(t, UT_DIR_NUM, &obj, &row));
76
77 if (ret == -E_OSL && osl_errno != E_OSL_RB_KEY_NOT_FOUND)
78 return ret;
79 if (ret < 0) { /* this is the first file we add */
80 struct osl_object objects[NUM_UT_COLUMNS];
81 uint64_t num_files = 1;
82
83 objects[UT_DIR_NUM].data = &dir_num;
84 objects[UT_DIR_NUM].size = sizeof(dir_num);
85 objects[UT_BYTES].data = add;
86 objects[UT_BYTES].size = sizeof(*add);
87 objects[UT_FILES].data = &num_files;
88 objects[UT_FILES].size = sizeof(num_files);
89 INFO_LOG("######################### ret: %d\n", ret);
90 ret = osl(osl_add_row(t, objects));
91 INFO_LOG("######################### ret: %d\n", ret);
92 return ret;
93 } else { /* add size and increment file count */
94 uint64_t num;
95 struct osl_object obj1, obj2 = {.data = &num, .size = sizeof(num)};
96
97 ret = osl(osl_get_object(t, row, UT_BYTES, &obj1));
98 if (ret < 0)
99 return ret;
100 num = *(uint64_t *)obj1.data + *add;
101 ret = osl(osl_update_object(t, row, UT_BYTES, &obj2));
102 if (ret < 0)
103 return ret;
104 ret = osl(osl_get_object(t, row, UT_FILES, &obj1));
105 if (ret < 0)
106 return ret;
107 num = *(uint64_t *)obj1.data + 1;
108 return osl(osl_update_object(t, row, UT_FILES, &obj2));
109 }
110 }
111
112 static int scan_dir(char *dirname, uint64_t *parent_dir_num)
113 {
114 DIR *dir;
115 struct dirent *entry;
116 int ret, cwd_fd, ret2;
117 uint64_t dir_size = 0, dir_files = 0;
118 /* dir count. */
119 static uint64_t current_dir_num;
120
121 uint64_t this_dir_num = ++current_dir_num;
122
123 check_signals();
124 DEBUG_LOG("----------------- %llu: %s\n", (long long unsigned)current_dir_num, dirname);
125 ret = adu_opendir(dirname, &dir, &cwd_fd);
126 if (ret < 0) {
127 if (ret != -ERRNO_TO_ERROR(EACCES))
128 return ret;
129 WARNING_LOG("permission denied for %s\n", dirname);
130 return 1;
131 }
132 while ((entry = readdir(dir))) {
133 mode_t m;
134 struct stat64 s;
135 uint32_t uid;
136 uint64_t size;
137 struct user_info *ui;
138
139 if (!strcmp(entry->d_name, "."))
140 continue;
141 if (!strcmp(entry->d_name, ".."))
142 continue;
143 if (lstat64(entry->d_name, &s) == -1) {
144 WARNING_LOG("lstat64 error for %s/%s (%s)\n",
145 dirname, entry->d_name, strerror(errno));
146 continue;
147 }
148 m = s.st_mode;
149 if (!S_ISREG(m) && !S_ISDIR(m))
150 continue;
151 if (S_ISDIR(m)) {
152 if (conf.one_file_system_given && s.st_dev != device_id)
153 continue;
154 ret = scan_dir(entry->d_name, &this_dir_num);
155 if (ret < 0)
156 goto out;
157 continue;
158 }
159 /* regular file */
160 size = s.st_size;
161 dir_size += size;
162 dir_files++;
163 uid = s.st_uid;
164 ret = search_uid(uid, CREATE_USER_TABLE | OPEN_USER_TABLE, &ui);
165 if (ret < 0)
166 goto out;
167 ui->bytes += size;
168 ui->files++;
169 ret = update_user_row(ui->table, this_dir_num, &size);
170 if (ret < 0)
171 goto out;
172 }
173 ret = add_directory(dirname, &this_dir_num, parent_dir_num,
174 &dir_size, &dir_files);
175 out:
176 closedir(dir);
177 ret2 = adu_fchdir(cwd_fd);
178 if (ret2 < 0 && ret >= 0)
179 ret = ret2;
180 close(cwd_fd);
181 return ret;
182 }
183
184 int com_create(void)
185 {
186 uint64_t zero = 0ULL;
187 int ret;
188 struct stat statbuf;
189
190 if (lstat(conf.base_dir_arg, &statbuf) == -1)
191 return -ERRNO_TO_ERROR(errno);
192 if (!S_ISDIR(statbuf.st_mode))
193 return -ERRNO_TO_ERROR(ENOTDIR);
194 device_id = statbuf.st_dev;
195 create_hash_table(conf.hash_table_bits_arg);
196 ret = open_dir_table(1);
197 if (ret < 0)
198 return ret;
199 check_signals();
200 ret = scan_dir(conf.base_dir_arg, &zero);
201 if (ret < 0)
202 goto out;
203 ret = write_uid_list();
204 out:
205 close_all_tables();
206 return ret;
207 }