Line data Source code
1 : /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2 : /*
3 : * file_backend.c
4 : * This file is part of "Sauvegarde" project.
5 : *
6 : * (C) Copyright 2015 - 2016 Olivier Delhomme
7 : * e-mail : olivier.delhomme@free.fr
8 : *
9 : * "Sauvegarde" is free software: you can redistribute it and/or modify
10 : * it under the terms of the GNU General Public License as published by
11 : * the Free Software Foundation, either version 3 of the License, or
12 : * (at your option) any later version.
13 : *
14 : * "Sauvegarde" is distributed in the hope that it will be useful,
15 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : * GNU General Public License for more details.
18 : *
19 : * You should have received a copy of the GNU General Public License
20 : * along with "Sauvegarde". If not, see <http://www.gnu.org/licenses/>
21 : */
22 : /**
23 : * @file server/file_backend.c
24 : *
25 : * This file contains all the functions for the file backend that saves
26 : * everything to some flat files somewhere into the filesystem.
27 : *
28 : * @note to translators: file_backend is the name of the backend please
29 : * do not translate this. Thanks.
30 : */
31 :
32 : #include "server.h"
33 :
34 :
35 : static void file_create_directory(gchar *save_dir, gchar *sub_dir);
36 : static void make_all_subdirectories(file_backend_t *file_backend);
37 : static buffer_t *init_buffer_structure(GFileInputStream *stream);
38 : static void free_buffer_t(buffer_t *a_buffer);
39 : static void read_one_buffer(buffer_t *a_buffer);
40 : static gchar *extract_one_line_from_buffer(buffer_t *a_buffer);
41 : static guint64 get_guint64_from_string(gchar *string);
42 : static uint get_uint_from_string(gchar *string);
43 : static gboolean compare_mtime_to_date(guint64 mtime, gchar *date);
44 : static meta_data_t *extract_from_line(gchar *line, GRegex *a_regex, query_t *query);
45 :
46 :
47 : /**
48 : * Stores meta data into a flat file. A file is created for each host that
49 : * sends meta data. This code is not thread safe (it means that this is
50 : * not safe to call it from different threads unless some mechanism
51 : * garantees that a write will never occur in the same file at the same
52 : * time.
53 : * @param server_struct is the server main structure where all
54 : * informations needed by the program are stored.
55 : * @param smeta the server's structure for file meta data. It contains the
56 : * hostname that sent it. This structure IS FREED by this
57 : * function.
58 : * @todo prefix should be set as a configuration's option.
59 : */
60 49745 : void file_store_smeta(server_struct_t *server_struct, server_meta_data_t *smeta)
61 : {
62 49745 : GFile *meta_file = NULL;
63 49745 : gchar *filename = NULL;
64 49745 : GFileOutputStream *stream = NULL;
65 49745 : GError *error = NULL;
66 49745 : gsize count = 0;
67 49745 : gssize written = 0;
68 49745 : gchar *string_written = NULL;
69 49745 : gchar *buffer = NULL;
70 49745 : gchar *hash_list = NULL;
71 49745 : meta_data_t *meta = NULL;
72 49745 : gchar *prefix = NULL;
73 49745 : file_backend_t *file_backend = NULL;
74 :
75 :
76 49745 : if (server_struct != NULL && server_struct->backend != NULL && server_struct->backend->user_data != NULL && smeta != NULL)
77 : {
78 49745 : meta = smeta->meta;
79 49745 : file_backend = server_struct->backend->user_data;
80 49745 : prefix = g_build_filename((gchar *) file_backend->prefix, "meta", NULL);
81 :
82 49745 : if (smeta->hostname != NULL && meta != NULL)
83 : {
84 49745 : filename = g_build_filename(prefix, smeta->hostname, NULL);
85 :
86 49745 : meta_file = g_file_new_for_path(filename);
87 :
88 49745 : stream = g_file_append_to(meta_file, G_FILE_CREATE_NONE, NULL, &error);
89 :
90 49745 : if (stream != NULL)
91 : {
92 49745 : hash_list = convert_hash_data_list_to_gchar(meta->hash_data_list);
93 :
94 49745 : if (hash_list != NULL)
95 : {
96 48763 : buffer = g_strdup_printf("%d, %" G_GUINT64_FORMAT ", %d, %" G_GUINT64_FORMAT ", %" G_GUINT64_FORMAT ", %" G_GUINT64_FORMAT ", %" G_GUINT64_FORMAT ", \"%s\", \"%s\", %d, %d, \"%s\", \"%s\", %s\n", meta->file_type, meta->inode, meta->mode, meta->atime, meta->ctime, meta->mtime, meta->size, meta->owner, meta->group, meta->uid, meta->gid, meta->name, meta->link, hash_list);
97 48763 : free_variable(hash_list);
98 : }
99 : else
100 : {
101 982 : buffer = g_strdup_printf("%d, %" G_GUINT64_FORMAT ", %d, %" G_GUINT64_FORMAT ", %" G_GUINT64_FORMAT ", %" G_GUINT64_FORMAT ", %" G_GUINT64_FORMAT ", \"%s\", \"%s\", %d, %d, \"%s\", \"%s\"\n", meta->file_type, meta->inode, meta->mode, meta->atime, meta->ctime, meta->mtime, meta->size, meta->owner, meta->group, meta->uid, meta->gid, meta->name, meta->link);
102 : }
103 :
104 49745 : count = strlen(buffer);
105 49745 : written = g_output_stream_write((GOutputStream *) stream, buffer, count, NULL, &error);
106 :
107 49745 : if (error != NULL)
108 : {
109 0 : string_written = g_strdup_printf("%"G_GSSIZE_FORMAT, written);
110 0 : print_error(__FILE__, __LINE__, _("Error: unable to write to file %s (%s bytes written).\n"), filename, string_written);
111 : }
112 :
113 49745 : g_output_stream_close((GOutputStream *) stream, NULL, &error);
114 49745 : free_variable(buffer);
115 : }
116 : else
117 : {
118 0 : print_error(__FILE__, __LINE__, _("Error: unable to open file %s to append meta-data in it.\n"), filename);
119 : }
120 :
121 49745 : free_object(meta_file);
122 49745 : free_variable(filename);
123 : }
124 : else
125 : {
126 0 : print_error(__FILE__, __LINE__, _("Error: no server_meta_data_t structure or missing hostname or missing meta_data_t * structure.\n"));
127 : }
128 :
129 49745 : free_variable(prefix);
130 : }
131 49745 : }
132 :
133 :
134 : /**
135 : * Stores data into a flat file. The file is named by its hash in hex
136 : * representation (one should easily check that the sha256sum of such a
137 : * file gives its name !).
138 : * @param server_struct is the server's main structure where all
139 : * informations needed by the program are stored.
140 : * @param hash_data is a hash_data_t * structure that contains the hash and
141 : * the corresponding data in a binary form and a 'read' field that
142 : * contains the number of bytes in 'data' field.
143 : * @todo return errors when they occurs
144 : */
145 229178 : void file_store_data(server_struct_t *server_struct, hash_data_t *hash_data)
146 : {
147 229178 : GFile *data_file = NULL;
148 229178 : gchar *filename = NULL;
149 229178 : GFileOutputStream *stream = NULL;
150 229178 : GError *error = NULL;
151 229178 : gssize written = 0;
152 229178 : gchar *string_written = NULL;
153 229178 : gchar *hex_hash = NULL;
154 229178 : gchar *path = NULL;
155 229178 : gchar *prefix = NULL;
156 229178 : file_backend_t *file_backend = NULL;
157 :
158 229178 : if (server_struct != NULL && server_struct->backend != NULL && server_struct->backend->user_data != NULL)
159 : {
160 229178 : file_backend = server_struct->backend->user_data;
161 229178 : prefix = g_build_filename((gchar *) file_backend->prefix, "data", NULL);
162 :
163 229178 : if (hash_data != NULL && hash_data->hash != NULL && hash_data->data != NULL)
164 : {
165 229178 : path = make_path_from_hash(prefix, hash_data->hash, file_backend->level);
166 229178 : hex_hash = hash_to_string(hash_data->hash);
167 229178 : filename = g_build_filename(path, hex_hash, NULL);
168 :
169 229178 : data_file = g_file_new_for_path(filename);
170 229178 : stream = g_file_replace(data_file, NULL, FALSE, G_FILE_CREATE_NONE, NULL, &error);
171 :
172 229178 : if (stream != NULL)
173 : {
174 229178 : written = g_output_stream_write((GOutputStream *) stream, hash_data->data, hash_data->read, NULL, &error);
175 :
176 229178 : if (error != NULL)
177 : {
178 0 : string_written = g_strdup_printf("%"G_GSSIZE_FORMAT, written);
179 0 : print_error(__FILE__, __LINE__, _("Error: unable to write to file %s (%s bytes written).\n"), filename, string_written);
180 0 : free_variable(string_written);
181 : }
182 :
183 229178 : g_output_stream_close((GOutputStream *) stream, NULL, &error);
184 :
185 229178 : g_object_unref(stream);
186 229178 : free_variable(hash_data->data);
187 229178 : free_variable(hash_data->hash);
188 229178 : free_variable(hash_data);
189 : }
190 : else
191 : {
192 0 : print_error(__FILE__, __LINE__, _("Error: unable to open file %s to write data in it.\n"), filename);
193 : }
194 :
195 229178 : free_object(data_file);
196 229178 : free_variable(filename);
197 229178 : free_variable(hex_hash);
198 229178 : free_variable(path);
199 : }
200 : else
201 : {
202 0 : print_error(__FILE__, __LINE__, _("Error: no hash_data_t structure or hash in it or missing data in it.\n"));
203 : }
204 :
205 229178 : free_variable(prefix);
206 : }
207 229178 : }
208 :
209 :
210 : /**
211 : * Builds a list of hashs that cdpfglerver's server needs.
212 : * @param server_struct is the server's main structure where all
213 : * informations needed by the program are stored.
214 : * @param hash_list is the list of hashs that we have to check for.
215 : * @returns to the client a list of hashs in no specific order for which
216 : * the server needs the data.
217 : */
218 49742 : GList *file_build_needed_hash_list(server_struct_t *server_struct, GList *hash_data_list)
219 : {
220 49742 : GFile *data_file = NULL;
221 49742 : GList *head = hash_data_list;
222 49742 : GList *needed = NULL;
223 49742 : gchar *hex_hash = NULL;
224 49742 : gchar *filename = NULL;
225 49742 : gchar *path = NULL;
226 49742 : gchar *prefix = NULL;
227 49742 : file_backend_t *file_backend = NULL;
228 49742 : guint8 *a_hash = NULL;
229 49742 : hash_data_t *hash_data = NULL;
230 49742 : hash_data_t *needed_hash_data = NULL;
231 :
232 :
233 49742 : if (server_struct != NULL && server_struct->backend != NULL && server_struct->backend->user_data != NULL)
234 : {
235 49742 : file_backend = server_struct->backend->user_data;
236 :
237 49742 : prefix = g_build_filename((gchar *) file_backend->prefix, "data", NULL);
238 :
239 624695 : while (head != NULL)
240 : {
241 525211 : hash_data = head->data;
242 525211 : path = make_path_from_hash(prefix, hash_data->hash, file_backend->level);
243 525211 : hex_hash = hash_to_string(hash_data->hash);
244 :
245 525211 : filename = g_build_filename(path, hex_hash, NULL);
246 525211 : data_file = g_file_new_for_path(filename);
247 :
248 525211 : if (g_file_query_exists(data_file, NULL) == FALSE && hash_data_is_in_list(hash_data, needed) == FALSE)
249 : {
250 : /* file does not exists and is not in the needed list so we need it!
251 : * thus putting it it the needed list
252 : */
253 223915 : a_hash = (guint8 *) g_malloc(sizeof(guint8) * HASH_LEN); /* No need to do g_malloc0 here as we store binary data */
254 223915 : memcpy(a_hash, hash_data->hash, HASH_LEN);
255 223915 : needed_hash_data = new_hash_data_t(NULL, 0, a_hash);
256 223915 : needed = g_list_prepend(needed, needed_hash_data);
257 : }
258 :
259 525211 : free_object(data_file);
260 525211 : free_variable(filename);
261 525211 : free_variable(hex_hash);
262 525211 : free_variable(path);
263 :
264 525211 : head = g_list_next(head);
265 : }
266 :
267 49742 : needed = g_list_reverse(needed);
268 49742 : free_variable(prefix);
269 : }
270 :
271 49742 : return needed;
272 : }
273 :
274 :
275 : /**
276 : * Creates sub_dir subdirectory into save_dir path
277 : * @param save_dir prefix directory where to create sub_dir
278 : * @param sub_dir name of the sub directory to be created under save_dir
279 : */
280 2 : static void file_create_directory(gchar *save_dir, gchar *sub_dir)
281 : {
282 2 : gchar *a_directory = NULL;
283 :
284 2 : a_directory = g_build_filename(save_dir, sub_dir, NULL);
285 2 : create_directory(a_directory);
286 2 : free_variable(a_directory);
287 2 : }
288 :
289 :
290 : /**
291 : * Makes all subdirectories into the "data" directory.
292 : * @note creating subdirectories for a level of 2 will take some time and
293 : * the empty directories will consume at least 256 Mb of space (ext4
294 : * filesystem). A level of 3 will take a long time and will consume
295 : * at least like 64 Gb of space (ext4 filesystem). Expect 16 Tb with
296 : * level 4 and a very very long time to complete.
297 : * @param file_backend the structure that contains the prefix path and the
298 : * level in which we want to create the subdirectories.
299 : */
300 1 : static void make_all_subdirectories(file_backend_t *file_backend)
301 : {
302 1 : gchar *path = NULL;
303 1 : gchar *path2 = NULL;
304 1 : gchar *octet = NULL;
305 1 : guint number = 0;
306 1 : double i = 0;
307 1 : double p = 0;
308 1 : double total = 0;
309 :
310 1 : if (file_backend != NULL && file_backend->level < 5 && file_backend->level > 1)
311 : {
312 1 : total = pow(256, file_backend->level);
313 :
314 65537 : for (i = 0; i < total; i++)
315 : {
316 65536 : path = g_strdup("");
317 :
318 196608 : for (p = file_backend->level-1; p >= 0; p--)
319 : {
320 131072 : number = i / (pow(256, p));
321 :
322 :
323 131072 : if (number > 255)
324 : {
325 65280 : number = fmod(number, 256);
326 : }
327 :
328 :
329 131072 : octet = g_strdup_printf("%02x", number);
330 131072 : path2 = g_strconcat(path, octet, "/", NULL);
331 131072 : free_variable(path);
332 131072 : path = path2;
333 131072 : free_variable(octet);
334 : }
335 :
336 65536 : path[strlen(path)-1] = '\0';
337 65536 : path2 = g_build_filename(file_backend->prefix, "data", path, NULL);
338 :
339 65536 : create_directory(path2);
340 :
341 65536 : free_variable(path);
342 65536 : free_variable(path2);
343 :
344 : }
345 :
346 : /* Creates a directory named .done in prefix/data in order
347 : * to tell that we already have created all direcrories an subdirectories
348 : */
349 1 : path = g_build_filename(file_backend->prefix, "data", ".done", NULL);
350 1 : create_directory(path);
351 1 : free_variable(path);
352 : }
353 0 : else if (file_backend != NULL)
354 : {
355 0 : print_error(__FILE__, __LINE__, _("dir-level (%d) should be > 1 and < 5\n"), file_backend->level);
356 : }
357 1 : }
358 :
359 :
360 : /**
361 : * Reads keys in keyfile if groupname is in that keyfile and fills
362 : * file_backend structure accordingly.
363 : * @param[in,out] file_backend: file_backend_t * structure to store
364 : * options read from the configuration file "filename".
365 : * @param filename : the filename of the configuration file to read from
366 : */
367 1 : static void read_from_group_file_backend(file_backend_t *file_backend, gchar *filename)
368 : {
369 1 : GKeyFile *keyfile = NULL; /** Configuration file parser */
370 1 : GError *error = NULL; /** Glib error handling */
371 1 : gchar *prefix = NULL;
372 1 : guint level = 0;
373 :
374 1 : keyfile = g_key_file_new();
375 :
376 1 : if (g_key_file_load_from_file(keyfile, filename, G_KEY_FILE_KEEP_COMMENTS, &error))
377 : {
378 1 : if (keyfile != NULL && filename != NULL && g_key_file_has_group(keyfile, GN_FILE_BACKEND) == TRUE)
379 : {
380 1 : prefix = read_string_from_file(keyfile, filename, GN_FILE_BACKEND, KN_FILE_DIRECTORY, _("Could not load [file_backend] file-directory from file."));
381 1 : level = read_int_from_file(keyfile, filename, GN_FILE_BACKEND, KN_DIR_LEVEL, _("Could not load [file_backend] dir-level from file."), FILE_BACKEND_LEVEL);
382 : }
383 : }
384 0 : else if (error != NULL)
385 : {
386 0 : print_error(__FILE__, __LINE__, _("Failed to open %s configuration file: %s\n"), filename, error->message);
387 0 : error = free_error(error);
388 : }
389 :
390 1 : if (prefix != NULL && file_backend != NULL)
391 : {
392 1 : file_backend->prefix = free_variable(file_backend->prefix);
393 1 : file_backend->prefix = normalize_directory(prefix);
394 : }
395 :
396 1 : free_variable(prefix);
397 :
398 1 : if (level > 0 && level < 6)
399 : { /* Will anyone need more than 1 099 511 627 776 directories to
400 : * store data? with 16k blocs and 256 block files per leafs
401 : * it represents 4 exabytes !
402 : */
403 1 : file_backend->level = level;
404 : }
405 :
406 1 : g_key_file_free(keyfile);
407 1 : }
408 :
409 :
410 : /**
411 : * Inits the backend : takes care of the directories we want to write to.
412 : * user_data of the backend structure is a file_backend_t structure that
413 : * contains the prefix path where to store data and the level of
414 : * indirections
415 : * @param server_struct is the server's main structure where all
416 : * informations needed by the program are stored.
417 : */
418 1 : void file_init_backend(server_struct_t *server_struct)
419 : {
420 1 : file_backend_t *file_backend = NULL;
421 1 : gchar *path = NULL;
422 :
423 1 : if (server_struct != NULL && server_struct->backend != NULL)
424 : {
425 1 : file_backend = (file_backend_t *) g_malloc0(sizeof(file_backend_t));
426 :
427 : /* default values */
428 1 : file_backend->prefix = g_strdup("/var/tmp/cdpfgl/server");
429 1 : file_backend->level = FILE_BACKEND_LEVEL;
430 :
431 1 : if (server_struct->opt != NULL && server_struct->opt->configfile != NULL)
432 : {
433 : /* Values from the config file */
434 1 : read_from_group_file_backend(file_backend, server_struct->opt->configfile);
435 : }
436 :
437 1 : server_struct->backend->user_data = file_backend;
438 :
439 1 : file_create_directory(file_backend->prefix, "meta");
440 1 : file_create_directory(file_backend->prefix, "data");
441 :
442 1 : path = g_build_filename(file_backend->prefix, "data", ".done", NULL);
443 1 : if (file_exists(path) == FALSE)
444 : {
445 1 : fprintf(stdout, _("Please wait while creating directories\n"));
446 1 : make_all_subdirectories(file_backend);
447 1 : fprintf(stdout, _("Finished !\n"));
448 : }
449 1 : free_variable(path);
450 :
451 : }
452 : else
453 : {
454 0 : print_error(__FILE__, __LINE__, _("Error: no server structure or no backend structure.\n"));
455 : }
456 1 : }
457 :
458 :
459 : /**
460 : * Allocates a newly buffer_t structure and fills it with the corresponding
461 : * values.
462 : * @param stream is the GFileInputStream stream from which we want to read things
463 : * @returns a newly allocated buffer_t structure with buf, size and pos
464 : * filled accordingly. It may be freed when no longer needed.
465 : */
466 0 : static buffer_t *init_buffer_structure(GFileInputStream *stream)
467 : {
468 0 : buffer_t *a_buffer = NULL;
469 0 : gchar *buf = NULL;
470 :
471 0 : a_buffer = (buffer_t *) g_malloc0(sizeof(buffer_t));
472 :
473 0 : buf =(gchar *) g_malloc0(FILE_BACKEND_BUFFER_SIZE + 1); /* to store the \0 at the end ! */
474 :
475 0 : a_buffer->buf = buf;
476 0 : a_buffer->size = 0;
477 0 : a_buffer->pos = 0;
478 0 : a_buffer->stream = stream;
479 :
480 0 : return a_buffer;
481 : }
482 :
483 :
484 : /**
485 : * Frees the buffer structrure
486 : * @param a_buffer is the buffer structure to be freed
487 : */
488 0 : static void free_buffer_t(buffer_t *a_buffer)
489 : {
490 0 : free_variable(a_buffer->buf);
491 0 : g_object_unref(a_buffer->stream);
492 0 : free_variable(a_buffer);
493 0 : }
494 :
495 :
496 : /**
497 : * Reads one entire buffer
498 : * @param[in,out] a_buffer is a buffer_t * structure containing all that is
499 : * needed to read a buffer and to know where we are in it
500 : * when parsing it. It fills the structure with the bytes
501 : * read, the number of bytes read and puts pos at 0.
502 : */
503 0 : static void read_one_buffer(buffer_t *a_buffer)
504 : {
505 0 : GError *error = NULL;
506 :
507 0 : if (a_buffer != NULL && a_buffer->stream != NULL)
508 : {
509 0 : a_buffer->size = g_input_stream_read((GInputStream *) a_buffer->stream, a_buffer->buf, FILE_BACKEND_BUFFER_SIZE, NULL, &error);
510 0 : a_buffer->pos = 0;
511 :
512 0 : if (a_buffer->size < 0 && error != NULL)
513 : {
514 0 : print_error(__FILE__, __LINE__, _("Error while reading the file: %s\n"), error->message);
515 0 : error = free_error(error);
516 : }
517 : }
518 0 : }
519 :
520 :
521 : /**
522 : * This function extracts one line from the buffer by searching the end of
523 : * line (assuming unix style '\n' end of lines).
524 : * @param[in,out] a_buffer contains the buffer the total number of bytes read
525 : * and the actual position
526 : * @returns a gchar * representing a whole line that may be freed when no
527 : * longer needed.
528 : */
529 0 : static gchar *extract_one_line_from_buffer(buffer_t *a_buffer)
530 : {
531 0 : gchar *line = NULL;
532 0 : gchar *a_line = NULL;
533 0 : gchar *whole_line = NULL;
534 0 : gssize i = 0;
535 :
536 0 : if (a_buffer != NULL && a_buffer->buf != NULL)
537 : {
538 0 : i = a_buffer->pos;
539 :
540 0 : while (a_buffer->buf[i] != '\n' && a_buffer->size != 0)
541 : {
542 0 : if (i < a_buffer->size)
543 : {
544 0 : i++;
545 : }
546 : else
547 : {
548 : /* The line is stored in more than one buffer */
549 :
550 0 : line = g_strndup(a_buffer->buf + a_buffer->pos, i - a_buffer->pos);
551 :
552 0 : if (whole_line != NULL)
553 : {
554 : /* the line is stored in more than 2 buffers */
555 0 : a_line = g_strconcat(whole_line, line, NULL);
556 0 : free_variable(whole_line);
557 0 : free_variable(line);
558 0 : whole_line = a_line;
559 : }
560 : else
561 : {
562 : /* second buffer */
563 : whole_line = line;
564 : }
565 :
566 0 : read_one_buffer(a_buffer);
567 0 : i = 0;
568 : }
569 : }
570 :
571 :
572 0 : line = g_strndup(a_buffer->buf + a_buffer->pos, i - a_buffer->pos);
573 0 : a_buffer->pos = i + 1; /* the new position is right next '\n' ! */
574 :
575 0 : if (whole_line != NULL)
576 : {
577 0 : a_line = g_strconcat(whole_line, line, NULL);
578 0 : free_variable(whole_line);
579 0 : free_variable(line);
580 0 : whole_line = a_line;
581 : }
582 : else
583 : {
584 : whole_line = line;
585 : }
586 : }
587 :
588 0 : return whole_line;
589 : }
590 :
591 :
592 : /**
593 : * @param string a gchar * string containing a number coded at most in 64
594 : * bits.
595 : * @returns a guint64 from the gchar * string that may contain such a
596 : * number.
597 : */
598 : static guint64 get_guint64_from_string(gchar *string)
599 : {
600 0 : guint64 guess_64 = 0;
601 :
602 0 : if (string != NULL)
603 : {
604 0 : sscanf(string, "%" G_GUINT64_FORMAT "", &guess_64);
605 : }
606 :
607 0 : return guess_64;
608 : }
609 :
610 :
611 : /**
612 : * @param string a gchar * string containing a number that should be
613 : * 32 bits at most.
614 : * @returns a uint from the gchar * string that may contain such a number.
615 : */
616 : static uint get_uint_from_string(gchar *string)
617 : {
618 0 : uint guess = 0;
619 :
620 0 : if (string != NULL)
621 : {
622 0 : sscanf(string, "%d", &guess);
623 : }
624 :
625 0 : return guess;
626 : }
627 :
628 :
629 : /**
630 : * Compares mtime to a YYYY-MM-DD HH:MM:SS gchar * string formated date
631 : * @param mtime the time in unix time
632 : * @param date the date in YYYY-MM-DD HH:MM:SS format - it may lack
633 : * things from the end ie: YYYY-MM-DD HH: for instance.
634 : * @returns TRUE if 'mtime' has 'date' as prefix and TRUE if 'date' is NULL
635 : */
636 0 : static gboolean compare_mtime_to_date(guint64 mtime, gchar *date)
637 : {
638 0 : GDateTime *la_date = NULL;
639 0 : gchar *the_date = NULL;
640 0 : gboolean result = TRUE;
641 :
642 0 : if (date != NULL)
643 : {
644 0 : la_date = g_date_time_new_from_unix_local(mtime);
645 0 : the_date = g_date_time_format(la_date, "%F %T %z");
646 :
647 0 : result = g_str_has_prefix(the_date, date);
648 :
649 0 : free_variable(the_date);
650 0 : g_date_time_unref(la_date);
651 : }
652 :
653 0 : return result;
654 : }
655 :
656 :
657 : /**
658 : * Gets digit values at a given place into a gchar YYYY-MM-DD HH:MM:SS
659 : * formatted string
660 : * @param date the string to be parsed
661 : * @param i the offset where to start
662 : * @param size the size to be parsed
663 : * @returns a gint that is supposed to be the value read into 'date' at
664 : * 'i' position ('size' long).
665 : */
666 0 : static gint get_digit_value(gchar *date, guint i, guint size)
667 : {
668 0 : gchar *value = NULL;
669 0 : gint digit_value = NULL;
670 0 : guint j = 0;
671 0 : gint ret = 0;
672 :
673 0 : value = (gchar *) g_malloc0(size + 1);
674 :
675 0 : while (isdigit(date[i]) && j < size)
676 : {
677 0 : value[j] = date[i];
678 0 : i++;
679 0 : j++;
680 : }
681 :
682 0 : ret = sscanf(value, "%d", &digit_value);
683 :
684 0 : if (ret != 1)
685 : {
686 0 : digit_value = 0;
687 : }
688 :
689 0 : free_variable(value);
690 :
691 0 : return digit_value;
692 : }
693 :
694 :
695 : /**
696 : * Analyses a gchar string that may contain YYYY-MM-DD HH:MM:SS
697 : * @param date is the gchar * string that may contain a date at the given
698 : * format.
699 : * @returns a GDateTime * that may represents the date.
700 : */
701 0 : static GDateTime *convert_gchar_date_to_gdatetime(gchar *date)
702 : {
703 0 : gint year = 0;
704 0 : gint month = 0;
705 0 : gint day = 0;
706 0 : gint hour = 0;
707 0 : gint minute = 0;
708 0 : gint second = 0;
709 0 : GDateTime *datetime = NULL;
710 0 : GTimeZone *tz = NULL;
711 :
712 0 : year = get_digit_value(date, 0, 4);
713 0 : month = get_digit_value(date, 5, 2);
714 0 : day = get_digit_value(date, 8, 2);
715 0 : hour = get_digit_value(date, 11, 2);
716 0 : minute = get_digit_value(date, 14, 2);
717 0 : second = get_digit_value(date, 17, 2);
718 :
719 0 : tz = g_time_zone_new_local();
720 0 : datetime = g_date_time_new(tz, year, month, day, hour, minute, second);
721 0 : g_time_zone_unref(tz);
722 :
723 0 : return datetime;
724 : }
725 :
726 :
727 : /**
728 : * Compares mtime to a YYYY-MM-DD HH:MM:SS gchar * string formated date
729 : * @param mtime the time in unix time
730 : * @param date the date in YYYY-MM-DD HH:MM:SS format - it may lack
731 : * things from the end ie: YYYY-MM-DD HH: for instance.
732 : * @returns -1, 0 or 1 if mtime is less than, equal to or greater than
733 : * 'date'.
734 : */
735 0 : static gint compare_mtime_to_gchar_date(guint64 mtime, gchar *date)
736 : {
737 0 : GDateTime *mtime_date = NULL;
738 0 : GDateTime *date_date = NULL;
739 0 : gint result = 0;
740 :
741 0 : date_date = convert_gchar_date_to_gdatetime(date);
742 0 : mtime_date = g_date_time_new_from_unix_local(mtime);
743 :
744 0 : result = g_date_time_compare(mtime_date, date_date);
745 :
746 0 : g_date_time_unref(mtime_date);
747 0 : g_date_time_unref(date_date);
748 :
749 0 : return result;
750 : }
751 :
752 :
753 : /**
754 : * returns true or false
755 : * @param mtime the time in unix time
756 : * @param date the date in YYYY-MM-DD HH:MM:SS format - it may lack
757 : * things from the end ie: YYYY-MM-DD HH: for instance.
758 : * @param after is TRUE if we are to compare and after date and false
759 : * if its a before date.
760 : */
761 : static gboolean compare_after_before_date(guint64 mtime, gchar *date, gboolean after)
762 : {
763 0 : gint cmp_date = 0;
764 :
765 0 : cmp_date = compare_mtime_to_gchar_date(mtime, date);
766 :
767 0 : if (cmp_date >= 0)
768 : {
769 : return after;
770 : }
771 : else
772 : {
773 : return !after;
774 : }
775 : }
776 :
777 :
778 : /**
779 : * Extracts all meta data from one line.
780 : * @param line is the line that has been read.
781 : * @param a_regex is the regular expression to filter upon the filename
782 : * @param query is the structure that contains everything about the
783 : * requested filename.
784 : * @returns a newly allocated gchar * string containing the filename that
785 : * may be freed when no longer needed
786 : */
787 0 : static meta_data_t *extract_from_line(gchar *line, GRegex *a_regex, query_t *query)
788 : {
789 0 : gchar **params = NULL;
790 0 : gchar *filename = NULL;
791 0 : meta_data_t *meta = NULL;
792 0 : guint32 q_uid = 0;
793 0 : guint32 q_gid = 0;
794 :
795 0 : gboolean res = FALSE;
796 :
797 0 : if (line != NULL && strlen(line) > 16)
798 : {
799 : /**
800 : * line example : 1, 1049893, 33261, 1432131763, 1432129404, 1425592185, 38680, "root", "root", 0, 0, "/bin/locale", "eAAAdPN/AAAQFgB0838AAFNKQtsAlNrU4QHmJlkxiKA=",
801 : * "IBUAdPN/AADgCQB0838AACuk6dHfqsfcXvECD/HXSbU=", "4AwAdPN/AAAQFgB0838AAPJ18vuZ+mHsaFOztwu6IWw="
802 : */
803 :
804 0 : params = g_strsplit(line, ",", 14);
805 : /* we have a leading space before " and a trailing space after " so begins at + 2 and length is - 3 less */
806 0 : filename = g_strndup(params[11]+2, strlen(params[11])-3);
807 :
808 0 : if (g_regex_match(a_regex, filename, 0, NULL))
809 : {
810 0 : meta = new_meta_data_t();
811 :
812 0 : meta->name = filename;
813 :
814 0 : meta->file_type = get_uint_from_string(params[0]);
815 0 : meta->inode = get_guint64_from_string(params[1]);
816 :
817 0 : meta->mode = get_uint_from_string(params[2]);
818 :
819 0 : meta->atime = get_guint64_from_string(params[3]);
820 0 : meta->ctime = get_guint64_from_string(params[4]);
821 0 : meta->mtime = get_guint64_from_string(params[5]);
822 :
823 0 : res = compare_mtime_to_date(meta->mtime, query->date);
824 :
825 : /** @todo gain speed in comparison by transforming query->afterdate and query->beforedate only once
826 : * in the calling function
827 : */
828 0 : if (query->afterdate != NULL)
829 : {
830 0 : res = res && compare_after_before_date(meta->mtime, query->afterdate, TRUE);
831 : }
832 :
833 0 : if (query->beforedate != NULL)
834 : {
835 0 : res = res && compare_after_before_date(meta->mtime, query->beforedate, FALSE);
836 : }
837 :
838 0 : if (res == TRUE)
839 : {
840 :
841 0 : meta->size = get_guint64_from_string(params[6]);
842 :
843 0 : meta->owner = g_strndup(params[7]+2, strlen(params[7])-3);
844 0 : meta->group = g_strndup(params[8]+2, strlen(params[8])-3);
845 0 : meta->link = g_strndup(params[12]+2, strlen(params[12])-3);
846 :
847 0 : meta->uid = get_uint_from_string(params[9]);
848 0 : meta->gid = get_uint_from_string(params[10]);
849 0 : q_uid = get_uint_from_string(query->uid);
850 0 : q_gid = get_uint_from_string(query->gid);
851 :
852 0 : if (strcmp(meta->owner, query->owner) == 0 && strcmp(meta->group, query->group) == 0 && (meta->uid == q_uid) && (meta->gid == q_gid))
853 : {
854 0 : meta->hash_data_list = make_hash_data_list_from_string(params[13]);
855 :
856 0 : print_debug("file_backend: --> type %d, inode: %"G_GUINT64_FORMAT", mode: %d, atime: %"G_GUINT64_FORMAT", ctime: %"G_GUINT64_FORMAT", mtime: %"G_GUINT64_FORMAT", size: %"G_GUINT64_FORMAT", filename: %s, owner: %s, group: %s, uid: %d, gid: %d, link: %s\n", meta->file_type, meta->inode, meta->mode, meta->atime, meta->ctime, meta->mtime, meta->size, meta->name, meta->owner, meta->group, meta->uid, meta->gid, meta->link);
857 : }
858 : else
859 : {
860 0 : meta = free_meta_data_t(meta, TRUE);
861 : }
862 : }
863 : else
864 : {
865 0 : meta = free_meta_data_t(meta, TRUE);
866 : }
867 : }
868 : else
869 : {
870 0 : free_variable(filename);
871 : }
872 :
873 0 : g_strfreev(params);
874 : }
875 :
876 0 : return meta;
877 : }
878 :
879 :
880 : /**
881 : * Gets the list of all saved files.
882 : * @param server_struct is the structure that contains all data for the
883 : * server.
884 : * @param query is the structure that contains everything about the
885 : * requested query.
886 : * @returns a JSON string containing all filenames requested
887 : * @note g_str_match_string is only available since glib 2.40 and
888 : * travis-ci.org has an older version of it!
889 : */
890 0 : gchar *file_get_list_of_files(server_struct_t *server_struct, query_t *query)
891 : {
892 0 : gchar *filename = NULL;
893 0 : file_backend_t *file_backend = NULL;
894 0 : GFile *the_file = NULL;
895 0 : GFileInputStream *stream = NULL;
896 0 : GError *error = NULL;
897 0 : buffer_t *a_buffer = NULL;
898 0 : gchar *line = NULL;
899 0 : json_t *array = NULL;
900 0 : json_t *root = NULL;
901 0 : gchar *json_string = NULL;
902 0 : GRegex *a_regex = NULL;
903 0 : meta_data_t *meta = NULL;
904 0 : json_t *meta_json = NULL;
905 :
906 :
907 0 : array = json_array();
908 :
909 0 : if (server_struct != NULL && server_struct->backend != NULL && server_struct->backend->user_data != NULL && query != NULL)
910 : {
911 0 : print_debug(_("file_backend: filter is: %s && %s && %s && %s\n"), \
912 : query->filename, query->date, query->afterdate, query->beforedate);
913 :
914 0 : a_regex = g_regex_new(query->filename, G_REGEX_CASELESS, 0, &error);
915 :
916 0 : file_backend = server_struct->backend->user_data;
917 0 : filename = g_build_filename(file_backend->prefix, "meta", query->hostname, NULL);
918 0 : the_file = g_file_new_for_path(filename);
919 :
920 0 : stream = g_file_read(the_file, NULL, &error);
921 :
922 0 : if (stream != NULL)
923 : {
924 0 : a_buffer = init_buffer_structure(stream);
925 0 : read_one_buffer(a_buffer);
926 : do
927 : {
928 0 : line = extract_one_line_from_buffer(a_buffer);
929 :
930 0 : if (a_buffer->size != 0)
931 : {
932 0 : meta = extract_from_line(line, a_regex, query);
933 :
934 0 : if (meta != NULL && meta->name != NULL)
935 : {
936 0 : meta_json = convert_meta_data_to_json(meta, query->hostname, FALSE);
937 0 : json_array_append_new(array, meta_json);
938 : }
939 :
940 0 : free_meta_data_t(meta, TRUE);
941 : }
942 :
943 0 : free_variable(line);
944 :
945 : }
946 0 : while (a_buffer->size != 0);
947 :
948 0 : g_input_stream_close((GInputStream *) stream, NULL, &error);
949 :
950 0 : free_buffer_t(a_buffer);
951 : }
952 : else
953 : {
954 0 : print_error(__FILE__, __LINE__, _("Error: unable to open file %s to read data from it.\n"), filename);
955 : }
956 :
957 0 : free_variable(filename);
958 0 : g_regex_unref(a_regex);
959 : }
960 : else
961 : {
962 0 : print_debug(_("file_backend: Something is wrong with backend initialization!\n"));
963 : }
964 :
965 0 : root = json_object();
966 0 : insert_json_value_into_json_root(root, "file_list", array);
967 0 : json_string = json_dumps(root, 0);
968 :
969 0 : json_decref(array);
970 0 : json_decref(root);
971 :
972 0 : return json_string;
973 : }
974 :
975 :
976 : /**
977 : * Retrieves data from a flat file. The file is named by its hash in hex
978 : * representation (one should easily check that the sha256sum of such a
979 : * file gives its name !).
980 : * @param server_struct is the server's main structure where all
981 : * informations needed by the program are stored.
982 : * @param hex_hash is a gchar * hash in hexadecimal format as retrieved
983 : * from the url.
984 : */
985 0 : hash_data_t *file_retrieve_data(server_struct_t *server_struct, gchar *hex_hash)
986 : {
987 0 : GFile *data_file = NULL;
988 0 : gchar *filename = NULL;
989 0 : GFileInputStream *stream = NULL;
990 0 : GError *error = NULL;
991 0 : gssize read = 0;
992 0 : gchar *string_read = NULL;
993 0 : gchar *path = NULL;
994 0 : gchar *prefix = NULL;
995 0 : file_backend_t *file_backend = NULL;
996 0 : hash_data_t *hash_data = NULL;
997 0 : guchar *data = NULL;
998 0 : guint8 *hash = NULL;
999 0 : guint64 filesize = 0;
1000 :
1001 :
1002 0 : if (server_struct != NULL && server_struct->backend != NULL && server_struct->backend->user_data != NULL)
1003 : {
1004 0 : file_backend = server_struct->backend->user_data;
1005 0 : prefix = g_build_filename((gchar *) file_backend->prefix, "data", NULL);
1006 0 : hash = string_to_hash(hex_hash);
1007 0 : path = make_path_from_hash(prefix, hash, file_backend->level);
1008 0 : filename = g_build_filename(path, hex_hash, NULL);
1009 0 : data_file = g_file_new_for_path(filename);
1010 0 : stream = g_file_read(data_file, NULL, &error);
1011 :
1012 0 : print_debug(_("file_backend: path: %s, filename: %s\n"), path, filename);
1013 :
1014 0 : if (stream != NULL)
1015 : {
1016 0 : filesize = get_file_size(data_file);
1017 : /* we can do this because files may not be too big: as large as the biggest CLIENT_BUFFER_SIZE. */
1018 0 : data = (guchar *) g_malloc(filesize + 1); /* No need to do g_malloc0 because data is binary data */
1019 :
1020 0 : read = g_input_stream_read((GInputStream *) stream, data, filesize, NULL, &error);
1021 :
1022 0 : if (error != NULL)
1023 : {
1024 0 : string_read = g_strdup_printf("%"G_GSSIZE_FORMAT, read);
1025 0 : print_error(__FILE__, __LINE__, _("Error: unable to read from file %s (%s bytes read): %s.\n"), filename, string_read, error->message);
1026 0 : free_variable(string_read);
1027 0 : free_error(error);
1028 : }
1029 : else
1030 : {
1031 0 : hash_data = new_hash_data_t(data, read, hash);
1032 : }
1033 :
1034 0 : g_input_stream_close((GInputStream *) stream, NULL, &error);
1035 0 : g_object_unref(stream);
1036 : }
1037 : else
1038 : {
1039 0 : print_error(__FILE__, __LINE__, _("Error: unable to open file %s to read data from it.\n"), filename);
1040 : }
1041 :
1042 0 : free_object(data_file);
1043 0 : free_variable(filename);
1044 0 : free_variable(path);
1045 0 : free_variable(prefix);
1046 : }
1047 :
1048 0 : return hash_data;
1049 : }
|