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