]> git.tuebingen.mpg.de Git - paraslash.git/blob - upgrade_db.c
Consult $HOME rather than calling getpwuid(),
[paraslash.git] / upgrade_db.c
1 /* Copyright (C) 2020 Andre Noll <maan@tuebingen.mpg.de>, see file COPYING. */
2
3 /** \file upgrade_db.c Prepare the paraslash database for paraslash-0.7. */
4
5 #include <osl.h>
6 #include <lopsub.h>
7 #include <regex.h>
8
9 #include "upgrade_db.lsg.h"
10 #include "para.h"
11 #include "error.h"
12 #include "string.h"
13 #include "fd.h"
14 #include "crypt.h"
15 #include "version.h"
16
17 #define CMD_PTR (lls_cmd(0, upgrade_db_suite))
18 #define OPT_RESULT(_name, _lpr) \
19         (lls_opt_result(LSG_UPGRADE_DB_PARA_UPGRADE_DB_OPT_ ## _name, lpr))
20 #define OPT_GIVEN(_name, _lpr) (lls_opt_given(OPT_RESULT(_name, _lpr)))
21 #define OPT_UINT32_VAL(_name, _lpr) (lls_uint32_val(0, OPT_RESULT(_name, _lpr)))
22 #define OPT_STRING_VAL(_name, _lpr) (lls_string_val(0, OPT_RESULT(_name, _lpr)))
23
24 static int loglevel;
25 INIT_STDERR_LOGGING(loglevel);
26
27 /** Array of error strings. */
28 DEFINE_PARA_ERRLIST;
29
30 static void handle_help_flag(struct lls_parse_result *lpr)
31 {
32         char *help;
33
34         if (OPT_GIVEN(DETAILED_HELP, lpr))
35                 help = lls_long_help(CMD_PTR);
36         else if (OPT_GIVEN(HELP, lpr))
37                 help = lls_short_help(CMD_PTR);
38         else
39                 return;
40         printf("%s\n", help);
41         free(help);
42         exit(EXIT_SUCCESS);
43 }
44
45 static struct stat *path_exists(const char *path)
46 {
47         static struct stat sb;
48
49         if (stat(path, &sb) < 0)
50                 return NULL;
51         return &sb;
52 }
53
54 static bool is_dir(const char *path)
55 {
56         struct stat *sb = path_exists(path);
57         if (!sb)
58                 return false;
59         return (sb->st_mode & S_IFMT) == S_IFDIR;
60 }
61
62 __noreturn static void die(const char *msg)
63 {
64         PARA_EMERG_LOG("%s\n", msg);
65         exit(EXIT_FAILURE);
66 }
67
68 static int string_compare(const struct osl_object *obj1, const struct osl_object *obj2)
69 {
70         const char *str1 = obj1->data;
71         const char *str2 = obj2->data;
72         return strncmp(str1, str2, PARA_MIN(obj1->size, obj2->size));
73 }
74
75 static char *src_db_dir, *dst_db_dir, *src_aft_dir, *dst_aft_dir;
76
77 static void set_paths(const struct lls_parse_result *lpr)
78 {
79         const char *home = get_homedir();
80
81         if (OPT_GIVEN(SRC_DATABASE_DIR, lpr))
82                 src_db_dir = para_strdup(OPT_STRING_VAL(SRC_DATABASE_DIR,
83                         lpr));
84         else
85                 src_db_dir = make_message(
86                         "%s/.paraslash/afs_database-0.4", home);
87         if (OPT_GIVEN(DST_DATABASE_DIR, lpr))
88                 dst_db_dir = para_strdup(OPT_STRING_VAL(DST_DATABASE_DIR,
89                         lpr));
90         else
91                 dst_db_dir = make_message(
92                         "%s/.paraslash/afs_database-0.7", home);
93         src_aft_dir = make_message("%s/audio_files", src_db_dir);
94         dst_aft_dir = make_message("%s/audio-files", src_db_dir);
95         PARA_NOTICE_LOG("source aft dir: %s\n", src_aft_dir);
96         PARA_NOTICE_LOG("destination aft dir: %s\n", dst_aft_dir);
97 }
98
99 static void check_sanity(void)
100 {
101         PARA_INFO_LOG("checking source and destination directories\n");
102         if (!is_dir(src_db_dir))
103                 die("source db directory does not exist");
104         if (path_exists(dst_db_dir))
105                 die("destination db already exists");
106         if (!is_dir(src_aft_dir))
107                 die("source audio file table does not exist");
108         if (path_exists(dst_aft_dir))
109                 die("destination audio file table already exists");
110 }
111
112 /** The columns of the audio file table (both old and new). */
113 enum audio_file_table_columns {
114         /** The hash on the content of the audio file. */
115         AFTCOL_HASH,
116         /** The full path in the filesystem. */
117         AFTCOL_PATH,
118         /** The audio file selector info. */
119         AFTCOL_AFSI,
120         /** The audio format handler info. */
121         AFTCOL_AFHI,
122         /** The chunk table info and the chunk table of the audio file. */
123         AFTCOL_CHUNKS,
124         /** The number of columns of this table. */
125         NUM_AFT_COLUMNS
126 };
127
128 #define AFSI_SIZE 32
129
130 static int src_aft_hash_compare(const struct osl_object *obj1,
131                 const struct osl_object *obj2)
132 {
133         return hash_compare((unsigned char *)obj1->data,
134                 (unsigned char *)obj2->data);
135 }
136
137 static struct osl_column_description src_aft_cols[] = {
138         [AFTCOL_HASH] = {
139                 .storage_type = OSL_MAPPED_STORAGE,
140                 .storage_flags = OSL_RBTREE | OSL_FIXED_SIZE | OSL_UNIQUE,
141                 .name = "hash",
142                 .compare_function = src_aft_hash_compare,
143                 .data_size = HASH_SIZE
144         },
145         [AFTCOL_PATH] = {
146                 .storage_type = OSL_MAPPED_STORAGE,
147                 .storage_flags = OSL_RBTREE | OSL_UNIQUE,
148                 .name = "path",
149                 .compare_function = string_compare,
150         },
151         [AFTCOL_AFSI] = {
152                 .storage_type = OSL_MAPPED_STORAGE,
153                 .storage_flags = OSL_FIXED_SIZE,
154                 .name = "afs_info",
155                 .data_size = AFSI_SIZE
156         },
157         [AFTCOL_AFHI] = {
158                 .storage_type = OSL_MAPPED_STORAGE,
159                 .name = "afh_info",
160         },
161         [AFTCOL_CHUNKS] = {
162                 .storage_type = OSL_DISK_STORAGE,
163                 .name = "chunks",
164         }
165 };
166
167 static struct osl_table_description src_aft_desc = {
168         .name = "audio_files",
169         .num_columns = NUM_AFT_COLUMNS,
170         .flags = OSL_LARGE_TABLE,
171         .column_descriptions = src_aft_cols
172 };
173
174 static struct osl_table *src_aft, *dst_aft;
175
176 static void open_src_aft(void)
177 {
178         int ret;
179
180         PARA_NOTICE_LOG("opening: %s\n", src_aft_dir);
181         src_aft_desc.dir = src_db_dir;
182         ret = osl(osl_open_table(&src_aft_desc, &src_aft));
183         if (ret < 0) {
184                 PARA_EMERG_LOG("can not open source audio file table: %s\n",
185                          para_strerror(-ret));
186                 exit(EXIT_FAILURE);
187         }
188         PARA_INFO_LOG("successfully opened source audio file table\n");
189 }
190
191 static int dst_aft_hash_compare(const struct osl_object *obj1,
192                 const struct osl_object *obj2)
193 {
194         return hash2_compare((unsigned char *)obj1->data,
195                 (unsigned char *)obj2->data);
196 }
197
198 /* identical to src_aft_cols except the comparator and the hash size. */
199 static struct osl_column_description dst_aft_cols[] = {
200         [AFTCOL_HASH] = {
201                 .storage_type = OSL_MAPPED_STORAGE,
202                 .storage_flags = OSL_RBTREE | OSL_FIXED_SIZE | OSL_UNIQUE,
203                 .name = "hash",
204                 .compare_function = dst_aft_hash_compare,
205                 .data_size = HASH2_SIZE
206         },
207         [AFTCOL_PATH] = {
208                 .storage_type = OSL_MAPPED_STORAGE,
209                 .storage_flags = OSL_RBTREE | OSL_UNIQUE,
210                 .name = "path",
211                 .compare_function = string_compare,
212         },
213         [AFTCOL_AFSI] = {
214                 .storage_type = OSL_MAPPED_STORAGE,
215                 .storage_flags = OSL_FIXED_SIZE,
216                 .name = "afs_info",
217                 .data_size = AFSI_SIZE
218         },
219         [AFTCOL_AFHI] = {
220                 .storage_type = OSL_MAPPED_STORAGE,
221                 .name = "afh_info",
222         },
223         [AFTCOL_CHUNKS] = {
224                 .storage_type = OSL_DISK_STORAGE,
225                 .name = "chunks",
226         }
227 };
228
229 static struct osl_table_description dst_aft_desc = {
230         .name = "audio-files",
231         .num_columns = NUM_AFT_COLUMNS,
232         .flags = OSL_LARGE_TABLE,
233         .column_descriptions = dst_aft_cols
234 };
235
236 static int create_and_open_dst_aft(void)
237 {
238         int ret;
239
240         PARA_NOTICE_LOG("creating %s\n", dst_aft_dir);
241         dst_aft_desc.dir = src_db_dir;
242         ret = osl(osl_create_table(&dst_aft_desc));
243         if (ret < 0) {
244                 PARA_EMERG_LOG("could not create destination audio file table\n");
245                 return ret;
246         }
247         ret = osl(osl_open_table(&dst_aft_desc, &dst_aft));
248         if (ret < 0) {
249                 PARA_EMERG_LOG("could not open destination audio file table: %s\n",
250                          para_strerror(-ret));
251                 exit(EXIT_FAILURE);
252         }
253         PARA_INFO_LOG("successfully opened destination audio file table\n");
254         return 0;
255 }
256
257 static int copy_aft_row(struct osl_row *row, void *data)
258 {
259         unsigned *n = data;
260         int i, ret;
261         unsigned char hash2[HASH2_SIZE] = "\0";
262         struct osl_object objs[NUM_AFT_COLUMNS] = {
263                 [AFTCOL_HASH] = {.data = hash2, .size = HASH2_SIZE}
264         };
265
266         ret = osl(osl_open_disk_object(src_aft, row, AFTCOL_CHUNKS,
267                 objs + AFTCOL_CHUNKS));
268         if (ret < 0) {
269                 PARA_ERROR_LOG("can not open disk object: %s\n",
270                         para_strerror(-ret));
271                 return ret;
272         }
273         for (i = 0; i < NUM_AFT_COLUMNS; i++) {
274                 if (i == AFTCOL_HASH) /* never assign to this index */
275                         continue;
276                 if (i == AFTCOL_CHUNKS) /* disk storage object handled above */
277                         continue;
278                 /* mapped storage */
279                 ret = osl(osl_get_object(src_aft, row, i, objs + i));
280                 if (ret < 0) {
281                         PARA_ERROR_LOG("get_object (col = %d): %s\n",
282                                 i, para_strerror(-ret));
283                         return ret;
284                 }
285                 if (i == AFTCOL_PATH)
286                         PARA_DEBUG_LOG("copying %s\n", (char *)objs[i].data);
287         }
288         (*n)++;
289         memcpy(hash2, n, sizeof(*n));
290         ret = osl(osl_add_row(dst_aft, objs));
291         if (ret < 0)
292                 PARA_ERROR_LOG("failed to add row: %s\n", para_strerror(-ret));
293         osl_close_disk_object(objs + AFTCOL_CHUNKS);
294         return ret;
295 }
296
297 static int convert_aft(void)
298 {
299         unsigned n;
300         int ret;
301
302         osl_get_num_rows(src_aft, &n);
303         PARA_NOTICE_LOG("converting hash of %u rows to sha256\n", n);
304         n = 0;
305         ret = osl(osl_rbtree_loop(src_aft, AFTCOL_HASH, &n, copy_aft_row));
306         if (ret < 0)
307                 PARA_ERROR_LOG("osl_rbtree_loop failed\n");
308         return ret;
309 }
310
311 static int remove_source_aft(void)
312 {
313         pid_t pid;
314         int fds[3] = {-1, -1, -1}; /* no redirection of stdin/stdout/stderr */
315         int ret, wstatus;
316         char *cmdline = make_message("rm -rf %s", src_aft_dir);
317
318         PARA_NOTICE_LOG("removing %s\n", src_aft_dir);
319         ret = para_exec_cmdline_pid(&pid, cmdline, fds);
320         if (ret < 0) {
321                 PARA_ERROR_LOG("exec failure\n");
322                 goto out;
323         }
324         do {
325                 ret = waitpid(pid, &wstatus, 0);
326         } while (ret < 0 && errno == EINTR);
327         if (ret < 0)
328                 PARA_ERROR_LOG("waitpid failure\n");
329 out:
330         return ret;
331 }
332
333 static int rename_db(void)
334 {
335         PARA_NOTICE_LOG("renaming %s -> %s\n", src_db_dir, dst_db_dir);
336         if (rename(src_db_dir, dst_db_dir) < 0) {
337                 int ret = -ERRNO_TO_PARA_ERROR(errno);
338                 PARA_ERROR_LOG("rename failed\n");
339                 return ret;
340         }
341         return 1;
342 }
343
344 int main(int argc, char *argv[])
345 {
346         struct lls_parse_result *lpr; /* command line */
347         char *errctx;
348         int ret;
349
350         ret = lls(lls_parse(argc, argv, CMD_PTR, &lpr, &errctx));
351         if (ret < 0)
352                 goto out;
353         loglevel = OPT_UINT32_VAL(LOGLEVEL, lpr);
354         version_handle_flag("recv", OPT_GIVEN(VERSION, lpr));
355         handle_help_flag(lpr);
356         set_paths(lpr);
357         check_sanity();
358         open_src_aft();
359         ret = create_and_open_dst_aft();
360         if (ret < 0)
361                 goto close_src_aft;
362         ret = convert_aft();
363         if (ret < 0)
364                 goto close_dst_aft;
365         ret = remove_source_aft();
366         if (ret < 0)
367                 goto close_dst_aft;
368         ret = rename_db();
369 close_dst_aft:
370         osl_close_table(dst_aft, OSL_MARK_CLEAN);
371 close_src_aft:
372         PARA_INFO_LOG("closing audio file tables\n");
373         osl_close_table(src_aft, OSL_MARK_CLEAN);
374 out:
375         if (ret < 0) {
376                 if (errctx)
377                         PARA_ERROR_LOG("%s\n", errctx);
378                 free(errctx);
379                 PARA_ERROR_LOG("%s\n", para_strerror(-ret));
380         } else {
381                 PARA_WARNING_LOG("success. Now start para_server and force-add"
382                         " all audio files.\n");
383         }
384         return ret < 0? EXIT_FAILURE : EXIT_SUCCESS;
385 }