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