Output improvements.
[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 "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                 INFO_LOG("######################### ret: %d\n", ret);
62                 ret = osl(osl_add_row(t, objects));
63                 INFO_LOG("######################### ret: %d\n", ret);
64                 return ret;
65         } else { /* add size and increment file count */
66                 uint64_t num;
67                 struct osl_object obj1, obj2 = {.data = &num, .size = sizeof(num)};
68
69                 ret = osl(osl_get_object(t, row, UT_BYTES, &obj1));
70                 if (ret < 0)
71                         return ret;
72                 num = *(uint64_t *)obj1.data + *add;
73                 ret = osl(osl_update_object(t, row, UT_BYTES, &obj2));
74                 if (ret < 0)
75                         return ret;
76                 ret = osl(osl_get_object(t, row, UT_FILES, &obj1));
77                 if (ret < 0)
78                         return ret;
79                 num = *(uint64_t *)obj1.data + 1;
80                 return osl(osl_update_object(t, row, UT_FILES, &obj2));
81         }
82 }
83
84 static int scan_dir(char *dirname, uint64_t *parent_dir_num)
85 {
86         DIR *dir;
87         struct dirent *entry;
88         int ret, cwd_fd, ret2;
89         uint64_t dir_size = 0, dir_files = 0;
90         /* dir count. */
91         static uint64_t current_dir_num;
92
93         uint64_t this_dir_num = ++current_dir_num;
94
95         check_signals();
96         DEBUG_LOG("----------------- %llu: %s\n", (long long unsigned)current_dir_num, dirname);
97         ret = adu_opendir(dirname, &dir, &cwd_fd);
98         if (ret < 0) {
99                 if (ret != -ERRNO_TO_ERROR(EACCES))
100                         return ret;
101                 WARNING_LOG("permission denied for %s\n", dirname);
102                 return 1;
103         }
104         while ((entry = readdir(dir))) {
105                 mode_t m;
106                 struct stat64 s;
107                 uint32_t uid;
108                 uint64_t size;
109                 struct user_info *ui;
110
111                 if (!strcmp(entry->d_name, "."))
112                         continue;
113                 if (!strcmp(entry->d_name, ".."))
114                         continue;
115                 if (lstat64(entry->d_name, &s) == -1) {
116                         WARNING_LOG("lstat64 error for %s/%s (%s)\n",
117                                 dirname, entry->d_name, strerror(errno));
118                         continue;
119                 }
120                 m = s.st_mode;
121                 if (!S_ISREG(m) && !S_ISDIR(m))
122                         continue;
123                 if (S_ISDIR(m)) {
124                         if (conf.one_file_system_given && s.st_dev != device_id)
125                                 continue;
126                         ret = scan_dir(entry->d_name, &this_dir_num);
127                         if (ret < 0)
128                                 goto out;
129                         continue;
130                 }
131                 /* regular file */
132                 size = s.st_size;
133                 dir_size += size;
134                 dir_files++;
135                 uid = s.st_uid;
136                 ret = search_uid(uid, NULL, CREATE_USER_TABLE | OPEN_USER_TABLE, &ui);
137                 if (ret < 0)
138                         goto out;
139                 ui->bytes += size;
140                 ui->files++;
141                 ret = update_user_row(ui->table, this_dir_num, &size);
142                 if (ret < 0)
143                         goto out;
144         }
145         ret = add_directory(dirname, &this_dir_num, parent_dir_num,
146                         &dir_size, &dir_files);
147 out:
148         closedir(dir);
149         ret2 = adu_fchdir(cwd_fd);
150         if (ret2 < 0 && ret >= 0)
151                 ret = ret2;
152         close(cwd_fd);
153         return ret;
154 }
155
156 int com_create(void)
157 {
158         uint64_t zero = 0ULL;
159         int ret;
160         struct stat statbuf;
161
162         if (lstat(conf.base_dir_arg, &statbuf) == -1)
163                 return -ERRNO_TO_ERROR(errno);
164         if (!S_ISDIR(statbuf.st_mode))
165                 return -ERRNO_TO_ERROR(ENOTDIR);
166         device_id = statbuf.st_dev;
167         create_hash_table(conf.hash_table_bits_arg);
168         ret = open_dir_table(1);
169         if (ret < 0)
170                 return ret;
171         check_signals();
172         ret = scan_dir(conf.base_dir_arg, &zero);
173         if (ret < 0)
174                 goto out;
175         ret = write_uid_file();
176 out:
177         close_all_tables();
178         return ret;
179 }