Line data Source code
1 : /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2 : /*
3 : * client.c
4 : * This file is part of "Sauvegarde" project.
5 : *
6 : * (C) Copyright 2014 - 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 : /**
24 : * @file client.c
25 : *
26 : * This file is the main file for the client program. This program has
27 : * to monitor file changes onto filesystems. It should notice
28 : * when a file is created, deleted or changed
29 : */
30 :
31 : #include "client.h"
32 :
33 : static GSList *make_regex_exclude_list(GSList *exclude_list);
34 : static gboolean exclude_file(GSList *regex_exclude_list, gchar *filename);
35 : static main_struct_t *init_main_structure(options_t *opt);
36 : static GList *calculate_hash_data_list_for_file(GFile *a_file, gint64 blocksize);
37 : static meta_data_t *get_meta_data_from_fileinfo(file_event_t *file_event, filter_file_t *filter, options_t *opt);
38 : static gchar *send_meta_data_to_server(main_struct_t *main_struct, meta_data_t *meta, gboolean data_sent);
39 : static GList *find_hash_in_list(GList *hash_data_list, guint8 *hash);
40 : static void send_data_to_server(main_struct_t *main_struct, meta_data_t *meta, gchar *answer);
41 : static void send_all_data_to_server(main_struct_t *main_struct, meta_data_t *meta, gchar *answer);
42 : static void iterate_over_enum(main_struct_t *main_struct, gchar *directory, GFileEnumerator *file_enum);
43 : static void carve_one_directory(gpointer data, gpointer user_data);
44 : static gpointer carve_all_directories(gpointer data);
45 : static gpointer save_one_file_threaded(gpointer data);
46 : static gpointer free_file_event_t(file_event_t *file_event);
47 : static gint insert_array_in_root_and_send(main_struct_t *main_struct, json_t *array);
48 : static void process_small_file_not_in_cache(main_struct_t *main_struct, meta_data_t *meta);
49 : static gint64 calculate_file_blocksize(options_t *opt, gint64 size);
50 : static gpointer reconnected(gpointer data);
51 :
52 : /**
53 : * Make a list of precompiled GRegex to be used to filter out directories
54 : * and filenames from being saved.
55 : * @param exclude_list is the list of gchar * string that should contain
56 : * filenames and/or dirnames with basic regex notation.
57 : * @returns A newly allocated list of compiled GRegex * pointers.
58 : */
59 1 : static GSList *make_regex_exclude_list(GSList *exclude_list)
60 : {
61 1 : GRegex *a_regex = NULL;
62 1 : GError *error = NULL;
63 1 : GSList *regex_exclude_list = NULL;
64 :
65 6 : while (exclude_list != NULL)
66 : {
67 4 : a_regex = g_regex_new(exclude_list->data, G_REGEX_CASELESS, 0, &error);
68 :
69 4 : if (error != NULL)
70 : {
71 0 : print_error(__FILE__, __LINE__, _("Error while compiling %s to a regular expression: %s"), exclude_list->data, error->message);
72 : }
73 4 : else if (a_regex != NULL)
74 : {
75 4 : regex_exclude_list = g_slist_prepend(regex_exclude_list, a_regex);
76 : }
77 :
78 4 : exclude_list = g_slist_next(exclude_list);
79 : }
80 :
81 1 : return regex_exclude_list;
82 : }
83 :
84 :
85 : /**
86 : * Says if 'filename' may correspond to one of the regex of the list.
87 : * @param regex_exclude_list is the list of precompiled Regex of files
88 : * to be excluded from being saved.
89 : * @param filename is the name pf the file that we want to know if we
90 : * have to exclude it or not.
91 : * @returns TRUE if the file has to be excluded, FALSE otherwise
92 : */
93 51772 : static gboolean exclude_file(GSList *regex_exclude_list, gchar *filename)
94 : {
95 51772 : gboolean result = FALSE;
96 :
97 304603 : while (regex_exclude_list != NULL && result == FALSE)
98 : {
99 201059 : result = g_regex_match(regex_exclude_list->data, filename, 0, NULL);
100 201059 : regex_exclude_list = g_slist_next(regex_exclude_list);
101 : }
102 :
103 51772 : return result;
104 : }
105 :
106 :
107 : /**
108 : * Inits the main structure.
109 : * @note With sqlite version > 3.7.7 we should use URI filename.
110 : * @param opt : a filled options_t * structure that contains all options
111 : * by default, read into the file or selected in the command line.
112 : * @returns a main_struct_t * pointer to the main structure
113 : */
114 1 : static main_struct_t *init_main_structure(options_t *opt)
115 : {
116 1 : main_struct_t *main_struct = NULL;
117 1 : gchar *db_uri = NULL;
118 1 : gchar *conn = NULL;
119 :
120 1 : if (opt != NULL)
121 : {
122 :
123 1 : print_debug(_("Please wait while initializing main structure...\n"));
124 :
125 1 : main_struct = (main_struct_t *) g_malloc0(sizeof(main_struct_t));
126 :
127 1 : create_directory(opt->dircache);
128 1 : db_uri = g_build_filename(opt->dircache, opt->dbname , NULL);
129 1 : main_struct->database = open_database(db_uri);
130 1 : db_uri = free_variable(db_uri);
131 :
132 1 : main_struct->opt = opt;
133 1 : main_struct->hostname = g_get_host_name();
134 :
135 1 : if (opt != NULL && opt->ip != NULL)
136 : {
137 1 : conn = make_connexion_string(opt->ip, opt->port);
138 1 : main_struct->comm = init_comm_struct(conn);
139 1 : main_struct->reconnected = init_comm_struct(conn);
140 1 : free_variable(conn);
141 : }
142 : else
143 : {
144 : /* This should never happen because we have default values */
145 0 : main_struct->comm = NULL;
146 : }
147 :
148 1 : main_struct->signal_fd = start_signals();
149 1 : main_struct->fanotify_fd = start_fanotify(opt);
150 :
151 : /* inits the queue that will wait for events on files */
152 1 : main_struct->save_queue = g_async_queue_new();
153 1 : main_struct->dir_queue = g_async_queue_new();
154 1 : main_struct->regex_exclude_list = make_regex_exclude_list(opt->exclude_list);
155 :
156 : /* Thread initialization */
157 1 : main_struct->save_one_file = g_thread_new("save_one_file", save_one_file_threaded, main_struct);
158 1 : main_struct->carve_all_directories = g_thread_new("carve_all_directories", carve_all_directories, main_struct);
159 1 : main_struct->reconn_thread = g_thread_new("reconnected", reconnected, main_struct);
160 :
161 1 : print_debug(_("Main structure initialized !\n"));
162 :
163 : }
164 :
165 1 : return main_struct;
166 : }
167 :
168 :
169 : /**
170 : * Calculates hashs for each block of blocksize bytes long on the file
171 : * and returns a list of all hashs in correct order stored in a binary
172 : * form to save space.
173 : * @note This technique has some limits in term of memory footprint
174 : * because one file is entirely in memory at a time. Saving huge
175 : * files may not be possible with this, depending on the size of
176 : * the file and the size of the memory.
177 : * @todo Imagine a new way to checksum huge files because of limitations.
178 : * May be with the local sqlite database ?
179 : * @param a_file is the file from which we want the hashs.
180 : * @param blocksize is the blocksize to be used to calculate hashs upon.
181 : * @returns a GSList * list of hashs stored in a binary form.
182 : */
183 48779 : static GList *calculate_hash_data_list_for_file(GFile *a_file, gint64 blocksize)
184 : {
185 48779 : GFileInputStream *stream = NULL;
186 48779 : GError *error = NULL;
187 48779 : GList *hash_data_list = NULL;
188 48779 : hash_data_t *hash_data = NULL;
189 48779 : gssize read = 0;
190 48779 : guchar *buffer = NULL;
191 48779 : GChecksum *checksum = NULL;
192 48779 : guint8 *a_hash = NULL;
193 48779 : gsize digest_len = HASH_LEN;
194 :
195 48779 : if (a_file != NULL)
196 : {
197 48779 : stream = g_file_read(a_file, NULL, &error);
198 :
199 48779 : if (stream != NULL && error == NULL)
200 : {
201 :
202 48779 : checksum = g_checksum_new(G_CHECKSUM_SHA256);
203 48779 : buffer = (guchar *) g_malloc(blocksize);
204 48779 : a_hash = (guint8 *) g_malloc(digest_len);
205 :
206 48779 : read = g_input_stream_read((GInputStream *) stream, buffer, blocksize, NULL, &error);
207 :
208 622787 : while (read != 0 && error == NULL)
209 : {
210 525229 : g_checksum_update(checksum, buffer, read);
211 525229 : g_checksum_get_digest(checksum, a_hash, &digest_len);
212 :
213 : /* Need to save data and read in hash_data_t structure */
214 525229 : hash_data = new_hash_data_t(buffer, read, a_hash);
215 :
216 525229 : hash_data_list = g_list_prepend(hash_data_list, hash_data);
217 525229 : g_checksum_reset(checksum);
218 525229 : digest_len = HASH_LEN;
219 :
220 525229 : buffer = (guchar *) g_malloc(blocksize);
221 525229 : a_hash = (guint8 *) g_malloc(digest_len);
222 :
223 525229 : read = g_input_stream_read((GInputStream *) stream, buffer, blocksize, NULL, &error);
224 : }
225 :
226 48779 : if (error != NULL)
227 : {
228 10 : print_error(__FILE__, __LINE__, _("Error while reading file: %s\n"), error->message);
229 10 : error = free_error(error);
230 10 : g_list_free_full(hash_data_list, free_hdt_struct);
231 10 : hash_data_list = NULL;
232 : }
233 : else
234 : {
235 : /* get the list in correct order (because we prepended the hashs to get speed when inserting hashs in the list) */
236 48769 : hash_data_list = g_list_reverse(hash_data_list);
237 : }
238 :
239 48779 : free_variable(buffer);
240 48779 : free_variable(a_hash);
241 :
242 48779 : g_checksum_free(checksum);
243 48779 : g_input_stream_close((GInputStream *) stream, NULL, NULL);
244 48779 : free_object(stream);
245 : }
246 : else
247 : {
248 0 : print_error(__FILE__, __LINE__, _("Unable to open file for reading: %s\n"), error->message);
249 0 : error = free_error(error);
250 : }
251 : }
252 :
253 48779 : return hash_data_list;
254 : }
255 :
256 :
257 : /**
258 : * Gets the attributes of a file
259 : * @param meta is the structure where to store meta data (attributes) of
260 : * the file
261 : * @param fileinfo is a glib structure that contains a lot of informations
262 : * about the file and from which we want to keep a few.
263 : */
264 49745 : static void get_file_attributes(meta_data_t *meta, GFileInfo *fileinfo)
265 : {
266 49745 : if (meta != NULL)
267 : {
268 49745 : meta->inode = g_file_info_get_attribute_uint64(fileinfo, G_FILE_ATTRIBUTE_UNIX_INODE);
269 49745 : meta->owner = g_file_info_get_attribute_as_string(fileinfo, G_FILE_ATTRIBUTE_OWNER_USER);
270 49745 : meta->group = g_file_info_get_attribute_as_string(fileinfo, G_FILE_ATTRIBUTE_OWNER_GROUP);
271 49745 : meta->uid = g_file_info_get_attribute_uint32(fileinfo, G_FILE_ATTRIBUTE_UNIX_UID);
272 49745 : meta->gid = g_file_info_get_attribute_uint32(fileinfo, G_FILE_ATTRIBUTE_UNIX_GID);
273 49745 : meta->atime = g_file_info_get_attribute_uint64(fileinfo, G_FILE_ATTRIBUTE_TIME_ACCESS);
274 49745 : meta->ctime = g_file_info_get_attribute_uint64(fileinfo, G_FILE_ATTRIBUTE_TIME_CHANGED);
275 49745 : meta->mtime = g_file_info_get_attribute_uint64(fileinfo, G_FILE_ATTRIBUTE_TIME_MODIFIED);
276 49745 : meta->mode = g_file_info_get_attribute_uint32(fileinfo, G_FILE_ATTRIBUTE_UNIX_MODE);
277 49745 : meta->size = g_file_info_get_attribute_uint64(fileinfo, G_FILE_ATTRIBUTE_STANDARD_SIZE);
278 :
279 : /* Do the right things with specific cases */
280 49745 : if (meta->file_type == G_FILE_TYPE_SYMBOLIC_LINK)
281 : {
282 634 : meta->link = (gchar *) g_file_info_get_attribute_byte_string(fileinfo, G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET);
283 : }
284 : else
285 : {
286 49111 : meta->link = g_strdup("");
287 : }
288 : }
289 49745 : }
290 :
291 :
292 : /**
293 : * Gets all meta data for a file and returns a filled meta_data_t *
294 : * structure.
295 : * @param file_event is the structure that contains directory and fileinfo
296 : * structures. directory is the directory we are iterating over. It
297 : * is used here to build the filename name. fileinfo is a glib
298 : * structure that contains all meta data and more for a file.
299 : * @param filter is a structure containing all structures needed to filter
300 : * files as database, regex_exclude_list and contains a boolean
301 : * that is set to TRUE if the file has been filtered out and FALSE
302 : * otherwise.
303 : * @param opt are the selected options for the program.
304 : * @returns a newly allocated and filled meta_data_t * structure.
305 : */
306 51772 : static meta_data_t *get_meta_data_from_fileinfo(file_event_t *file_event, filter_file_t *filter, options_t *opt)
307 : {
308 51772 : meta_data_t *meta = NULL;
309 51772 : gchar *directory = NULL;
310 51772 : GFileInfo *fileinfo = NULL;
311 :
312 51772 : if (file_event != NULL)
313 : {
314 51772 : directory = file_event->directory;
315 51772 : fileinfo = file_event->fileinfo;
316 : }
317 :
318 51772 : if (directory != NULL && fileinfo != NULL && filter != NULL && filter->database != NULL)
319 : {
320 : /* filling meta data for the file represented by fileinfo */
321 51772 : meta = new_meta_data_t();
322 :
323 51772 : meta->file_type = g_file_info_get_file_type(fileinfo);
324 51772 : meta->name = g_build_path(G_DIR_SEPARATOR_S, directory, g_file_info_get_name(fileinfo), NULL);
325 :
326 51772 : if (exclude_file(filter->regex_exclude_list, meta->name) == FALSE)
327 : {
328 : /* fills meta_data_t *meta structure */
329 49745 : get_file_attributes(meta, fileinfo);
330 49745 : meta->blocksize = calculate_file_blocksize(opt, meta->size);
331 :
332 : /* We need to determine if the file has already been saved by looking into the local database
333 : * This is usefull only when carving directories at the begining of the process as when called
334 : * by m_fanotify we already know that the file was written and that something changed.
335 : */
336 49745 : meta->in_cache = is_file_in_cache(filter->database, meta);
337 49745 : filter->excluded = FALSE;
338 : }
339 : else
340 : {
341 2027 : filter->excluded = TRUE;
342 : }
343 : }
344 :
345 51772 : return meta;
346 : }
347 :
348 :
349 : /**
350 : * Sends meta data to the server and returns it's answer or NULL in
351 : * case of an error.
352 : * @param main_struct : main structure of the program (contains pointers
353 : * to the communication socket.
354 : * @param meta : the meta_data_t * structure to be saved.
355 : * @returns a newly allocated gchar * string that may be freed when no
356 : * longer needed.
357 : */
358 49745 : static gchar *send_meta_data_to_server(main_struct_t *main_struct, meta_data_t *meta, gboolean data_sent)
359 : {
360 49745 : gchar *json_str = NULL;
361 49745 : gchar *answer = NULL;
362 49745 : gint success = CURLE_FAILED_INIT;
363 49745 : json_t *root = NULL;
364 49745 : json_t *array = NULL;
365 :
366 49745 : if (main_struct != NULL && meta != NULL && main_struct->hostname != NULL)
367 : {
368 49745 : json_str = convert_meta_data_to_json_string(meta, main_struct->hostname, data_sent);
369 :
370 : /* Sends meta data here: readbuffer is the buffer sent to server */
371 49745 : print_debug(_("Sending meta data: %s\n"), json_str);
372 49745 : main_struct->comm->readbuffer = json_str;
373 49745 : success = post_url(main_struct->comm, "/Meta.json");
374 :
375 49745 : if (success == CURLE_OK)
376 : {
377 49745 : answer = g_strdup(main_struct->comm->buffer);
378 49745 : main_struct->comm->buffer = free_variable(main_struct->comm->buffer);
379 : }
380 : else
381 : {
382 : /* Need to manage HTTP errors ? */
383 : /* Saving meta data that should have been sent to sqlite database */
384 0 : db_save_buffer(main_struct->database, "/Meta.json", main_struct->comm->readbuffer);
385 :
386 : /* An error occured -> we need the whole hash list to be saved
387 : * we are building a 'fake' answer with the whole hash list.
388 : */
389 0 : array = convert_hash_list_to_json(meta->hash_data_list);
390 0 : root = json_object();
391 0 : insert_json_value_into_json_root(root, "hash_list", array);
392 0 : answer = json_dumps(root, 0);
393 0 : json_decref(root);
394 : }
395 :
396 49745 : free_variable(main_struct->comm->readbuffer);
397 : }
398 :
399 49745 : return answer;
400 : }
401 :
402 :
403 : /**
404 : * Finds a hash in the hash data list and returns the hash_data_t that
405 : * corresponds to it. In normal operations it should always find
406 : * something.
407 : * @param hash_data_list is the list to look into for the hash 'hash'
408 : * @param hash is the hash to look for.
409 : * @returns the GList * pointer that contains hash_data_t structure that
410 : * corresponds to the hash 'hash'.
411 : */
412 223915 : static GList *find_hash_in_list(GList *hash_data_list, guint8 *hash)
413 : {
414 223915 : GList *iter = hash_data_list;
415 223915 : hash_data_t *found = NULL;
416 223915 : gboolean ok = FALSE;
417 :
418 858875 : while (iter != NULL && ok == FALSE)
419 : {
420 411045 : found = iter->data;
421 :
422 411045 : if (compare_two_hashs(hash, found->hash) == 0)
423 : {
424 : ok = TRUE;
425 : }
426 : else
427 : {
428 187130 : iter = g_list_next(iter);
429 : }
430 : }
431 :
432 223915 : if (ok == TRUE)
433 : {
434 223915 : return iter;
435 : }
436 : else
437 : {
438 : return NULL;
439 : }
440 : }
441 :
442 :
443 : /**
444 : * Inserts the array into a root json_t * structure and dumps it into a
445 : * buffer that is send to the server and then freed.
446 : * @param main_struct : main structure of the program.
447 : * @param array is the json_t * array to be sent to the server
448 : */
449 44123 : static gint insert_array_in_root_and_send(main_struct_t *main_struct, json_t *array)
450 : {
451 44123 : json_t *root = NULL;
452 44123 : gint success = CURLE_FAILED_INIT;
453 :
454 :
455 44123 : if (main_struct != NULL && main_struct->comm != NULL && array != NULL)
456 : {
457 :
458 44123 : root = json_object();
459 44123 : insert_json_value_into_json_root(root, "data_array", array);
460 :
461 : /* readbuffer is the buffer sent to server */
462 44123 : main_struct->comm->readbuffer = json_dumps(root, 0);
463 :
464 44123 : success = post_url(main_struct->comm, "/Data_Array.json");
465 :
466 44123 : if (success != CURLE_OK)
467 : {
468 0 : db_save_buffer(main_struct->database, "/Data_Array.json", main_struct->comm->readbuffer);
469 : }
470 :
471 44123 : free_variable(main_struct->comm->readbuffer);
472 44123 : json_decref(root);
473 44123 : main_struct->comm->buffer = free_variable(main_struct->comm->buffer);
474 :
475 : }
476 :
477 44123 : return success;
478 : }
479 :
480 :
481 : /**
482 : * Sends data as requested by the server 'cdpfglserver' in a buffered way.
483 : * @param main_struct : main structure of the program.
484 : * @param meta : meta is a pointer to meta_data_t strcuture that contains
485 : * hash_data_list the list of hash_data_t * pointers
486 : * containing all all the data to be saved.
487 : * @param answer is the request sent back by server when we had send
488 : * meta data.
489 : * @note using directly main_struct->comm->buffer -> not threadable as is.
490 : */
491 45022 : static void send_all_data_to_server(main_struct_t *main_struct, meta_data_t *meta, gchar *answer)
492 : {
493 45022 : json_t *root = NULL;
494 45022 : json_t *array = NULL;
495 45022 : GList *hash_list = NULL; /** hash_list is local to this function and contains the needed hashs as answer by server */
496 45022 : GList *head = NULL;
497 45022 : GList *iter = NULL;
498 45022 : hash_data_t *found = NULL;
499 45022 : hash_data_t *hash_data = NULL;
500 45022 : gint bytes = 0;
501 45022 : json_t *to_insert = NULL;
502 45022 : gint64 limit = 0;
503 45022 : a_clock_t *elapsed = NULL;
504 :
505 45022 : if (answer != NULL && meta != NULL && meta->hash_data_list != NULL && main_struct != NULL && main_struct->opt != NULL)
506 : {
507 44683 : root = load_json(answer);
508 :
509 44683 : limit = main_struct->opt->buffersize;
510 :
511 44683 : if (root != NULL)
512 : {
513 : /* This hash_list is the needed hashs from server */
514 44683 : hash_list = extract_glist_from_array(root, "hash_list", TRUE);
515 44683 : json_decref(root);
516 :
517 44683 : array = json_array();
518 :
519 44683 : head = hash_list;
520 :
521 310264 : while (hash_list != NULL)
522 : {
523 220898 : hash_data = hash_list->data;
524 : /* hash_data_list contains all hashs and their associated data for the file
525 : * being processed */
526 220898 : iter = find_hash_in_list(meta->hash_data_list, hash_data->hash);
527 220898 : found = iter->data;
528 :
529 220898 : to_insert = convert_hash_data_t_to_json(found);
530 220898 : json_array_append_new(array, to_insert);
531 :
532 220898 : bytes = bytes + found->read;
533 :
534 220898 : meta->hash_data_list = g_list_remove_link(meta->hash_data_list, iter);
535 220898 : g_list_free_full(iter, free_hdt_struct);
536 :
537 220898 : if (bytes >= limit)
538 : {
539 : /* when we've got opt->buffersize bytes of data send them ! */
540 :
541 175 : elapsed = new_clock_t();
542 175 : insert_array_in_root_and_send(main_struct, array);
543 175 : array = json_array();
544 175 : bytes = 0;
545 175 : end_clock(elapsed, "insert_array_in_root_and_send");
546 : }
547 :
548 220898 : hash_list = g_list_next(hash_list);
549 : }
550 :
551 44683 : if (bytes > 0)
552 : {
553 : /* Send the rest of the data (less than opt->buffersize bytes) */
554 43618 : elapsed = new_clock_t();
555 43618 : insert_array_in_root_and_send(main_struct, array);
556 43618 : end_clock(elapsed, "insert_array_in_root_and_send");
557 : }
558 : else
559 : {
560 1065 : json_decref(array);
561 : }
562 :
563 44683 : if (head != NULL)
564 : {
565 43618 : g_list_free_full(head, free_hdt_struct);
566 : }
567 : }
568 : else
569 : {
570 0 : print_error(__FILE__, __LINE__, _("Error while loading JSON answer from server\n"));
571 : }
572 : }
573 45022 : }
574 :
575 :
576 : /**
577 : * Sends data as requested by the server 'cdpfglserver'.
578 : * @param main_struct : main structure of the program.
579 : * @param hash_data_list : list of hash_data_t * pointers containing
580 : * all the data to be saved.
581 : * @param answer is the request sent back by server when we had send
582 : * meta data.
583 : * @note using directly main_struct->comm->buffer -> not threadable as is.
584 : */
585 4720 : static void send_data_to_server(main_struct_t *main_struct, meta_data_t *meta, gchar *answer)
586 : {
587 4720 : json_t *root = NULL;
588 4720 : GList *hash_list = NULL; /** hash_list is local to this function */
589 4720 : GList *head = NULL;
590 4720 : gint success = CURLE_FAILED_INIT;
591 4720 : GList *iter = NULL;
592 4720 : hash_data_t *found = NULL;
593 4720 : hash_data_t *hash_data = NULL;
594 :
595 4720 : if (main_struct != NULL && main_struct->comm != NULL && answer != NULL && meta != NULL && meta->hash_data_list!= NULL)
596 : {
597 4077 : root = load_json(answer);
598 :
599 4077 : if (root != NULL)
600 : {
601 : /* This hash_list is the needed hashs from server */
602 4077 : hash_list = extract_glist_from_array(root, "hash_list", TRUE);
603 4077 : json_decref(root);
604 4077 : head = hash_list;
605 :
606 11171 : while (hash_list != NULL)
607 : {
608 3017 : hash_data = hash_list->data;
609 : /* hash_data_list contains all hashs and their associated data */
610 3017 : iter = find_hash_in_list(meta->hash_data_list, hash_data->hash);
611 3017 : found = iter->data;
612 :
613 : /* readbuffer is the buffer sent to server */
614 3017 : main_struct->comm->readbuffer = convert_hash_data_t_to_string(found);
615 3017 : success = post_url(main_struct->comm, "/Data.json");
616 :
617 3017 : if (success != CURLE_OK)
618 : {
619 0 : db_save_buffer(main_struct->database, "/Data.json", main_struct->comm->readbuffer);
620 : }
621 :
622 3017 : free_variable(main_struct->comm->readbuffer);
623 :
624 3017 : meta->hash_data_list = g_list_remove_link(meta->hash_data_list, iter);
625 3017 : g_list_free_full(iter, free_hdt_struct);
626 :
627 3017 : main_struct->comm->buffer = free_variable(main_struct->comm->buffer);
628 3017 : hash_list = g_list_next(hash_list);
629 : }
630 :
631 4077 : if (head != NULL)
632 : {
633 3017 : g_list_free_full(head, free_hdt_struct);
634 : }
635 : }
636 : else
637 : {
638 0 : print_error(__FILE__, __LINE__, _("Error while loading JSON answer from server\n"));
639 : }
640 : }
641 4720 : }
642 :
643 :
644 : /**
645 : * @returns a newly allocated file_event_t * structure that must be freed
646 : * with free_file_event_t() when no longer needed
647 : * @param directory is the directory where the event occured
648 : * @param fileinfo is fileinfo of the file on which the event occured
649 : */
650 51772 : file_event_t *new_file_event_t(gchar *directory, GFileInfo *fileinfo)
651 : {
652 51772 : file_event_t *file_event = NULL;
653 :
654 :
655 51772 : file_event = (file_event_t *) g_malloc(sizeof(file_event_t));
656 :
657 51772 : file_event->directory = g_strdup(directory);
658 51772 : file_event->fileinfo = g_file_info_dup(fileinfo);
659 :
660 51772 : return file_event;
661 : }
662 :
663 :
664 : /**
665 : * Frees file_event_t's memory
666 : * @param file_event is the event to be freed
667 : * @returns NULL
668 : */
669 51772 : static gpointer free_file_event_t(file_event_t *file_event)
670 : {
671 51772 : if (file_event != NULL)
672 : {
673 51772 : free_variable(file_event->directory);
674 51772 : free_object(file_event->fileinfo);
675 51772 : free_variable(file_event);
676 : }
677 :
678 51772 : return NULL;
679 : }
680 :
681 :
682 : /**
683 : * @returns a newly allocated filter_file_t * structure that must be freed
684 : * with free_filter_t() when no longer needed.
685 : * @param database is a pointer to the database.
686 : * @param regex_exclude_list is the list of regular expressions used to
687 : * filter our files
688 : * @param excluded is a gboolean that is used to say whether a file has
689 : * been excluded or not.
690 : */
691 : static filter_file_t *new_filter_t(db_t *database, GSList *regex_exclude_list, gboolean excluded)
692 : {
693 51772 : filter_file_t *filter = NULL;
694 :
695 :
696 51772 : filter = (filter_file_t *) g_malloc(sizeof(filter_file_t));
697 :
698 51772 : filter->database = database;
699 51772 : filter->regex_exclude_list = regex_exclude_list;
700 51772 : filter->excluded = excluded;
701 :
702 : return filter;
703 : }
704 :
705 :
706 : /**
707 : * frees filter_file_t *filter's memory
708 : * @param filter the filter to be freed
709 : * @returns NULL
710 : */
711 : static gpointer free_filter_file_t(filter_file_t *filter)
712 : {
713 : /* Beware not to free database and regex_exclude_list that are
714 : * used elsewhere in the program
715 : */
716 51772 : if (filter != NULL)
717 : {
718 51772 : g_free(filter);
719 : }
720 : return NULL;
721 : }
722 :
723 :
724 : /**
725 : * Threaded function that saves one file by getting it's meta-data and
726 : * it's data and sends them to the server in order to be saved.
727 : * @param data must be main_struct_t * pointer.
728 : */
729 1 : static gpointer save_one_file_threaded(gpointer data)
730 : {
731 1 : main_struct_t *main_struct = (main_struct_t *) data;
732 1 : file_event_t *file_event = NULL;
733 :
734 1 : if (main_struct != NULL && main_struct->save_queue != NULL)
735 : {
736 : while (1)
737 : {
738 51773 : file_event = g_async_queue_pop(main_struct->save_queue);
739 51772 : save_one_file(main_struct, file_event);
740 51772 : free_file_event_t(file_event);
741 51772 : }
742 : }
743 :
744 0 : return NULL;
745 : }
746 :
747 :
748 : /**
749 : * Calculates the block size to be used upon a file
750 : * @param opt are the selected options for the program.
751 : * @param size is the size of the considered file.
752 : */
753 49745 : static gint64 calculate_file_blocksize(options_t *opt, gint64 size)
754 : {
755 :
756 49745 : if (opt != NULL && opt->adaptive == TRUE)
757 : {
758 49745 : if (size < 32768) /* max 64 blocks */
759 : {
760 : return 512;
761 : }
762 1174 : else if (size < 262144) /* max 128 blocks */
763 : {
764 : return 2048;
765 : }
766 242 : else if (size < 1048576) /* max 128 blocks */
767 : {
768 : return 8192;
769 : }
770 65 : else if (size < 8388608) /* max 512 blocks */
771 : {
772 : return 16384;
773 : }
774 10 : else if (size < 67108864) /* max 1024 blocks */
775 : {
776 : return 65536;
777 : }
778 3 : else if (size < 134217728) /* max 1024 blocks */
779 : {
780 0 : opt->buffersize = (CLIENT_MIN_BUFFER) * 2;
781 0 : return 131072;
782 : }
783 : else /* at least 512 blocks */
784 : {
785 3 : opt->buffersize = (CLIENT_MIN_BUFFER) * 4;
786 3 : return 262144;
787 : }
788 : }
789 0 : else if (opt != NULL)
790 : {
791 : /* default case */
792 0 : return opt->blocksize;
793 : }
794 : else
795 : {
796 : /* default case */
797 : return CLIENT_BLOCK_SIZE;
798 : }
799 : }
800 :
801 :
802 : /**
803 : * Process the file that is not already in our local cache
804 : * @param main_struct : main structure of the program
805 : * @param meta is the meta data of the file to be processed (it does
806 : * not contain any hashs at that point).
807 : */
808 49742 : static void process_small_file_not_in_cache(main_struct_t *main_struct, meta_data_t *meta)
809 : {
810 49742 : GFile *a_file = NULL;
811 49742 : gchar *answer = NULL;
812 49742 : gint success = 0; /** success returns a CURL Error status such as CURLE_OK for instance */
813 49742 : a_clock_t *mesure_time = NULL;
814 :
815 49742 : if (main_struct != NULL && main_struct->opt != NULL && meta != NULL)
816 : {
817 :
818 49742 : print_debug(_("Processing small file: %s\n"), meta->name);
819 :
820 49742 : if (meta->file_type == G_FILE_TYPE_REGULAR)
821 : {
822 48779 : mesure_time = new_clock_t();
823 :
824 : /* Calculates hashs and takes care of data */
825 48779 : a_file = g_file_new_for_path(meta->name);
826 48779 : meta->hash_data_list = calculate_hash_data_list_for_file(a_file, meta->blocksize);
827 48779 : a_file = free_object(a_file);
828 :
829 48779 : end_clock(mesure_time, "calculate_hash_data_list");
830 : }
831 :
832 49742 : mesure_time = new_clock_t();
833 49742 : answer = send_meta_data_to_server(main_struct, meta, FALSE);
834 49742 : end_clock(mesure_time, "send_meta_data_to_server");
835 :
836 49742 : mesure_time = new_clock_t();
837 49742 : if (meta->size < meta->blocksize)
838 : {
839 : /* Only one block to send (size is less than blocksize's value) */
840 4720 : send_data_to_server(main_struct, meta, answer);
841 : }
842 : else
843 : {
844 : /* A least 2 blocks to send */
845 45022 : send_all_data_to_server(main_struct, meta, answer);
846 : }
847 49742 : end_clock(mesure_time, "send_(all)_data_to_server");
848 :
849 49742 : free_variable(answer); /* Not used by now */
850 :
851 : if (success == CURLE_OK)
852 : {
853 : /* Everything has been transmitted so we can save meta data into the local db cache */
854 : /* This is usefull for file carving to avoid sending too much things to the server */
855 49742 : mesure_time = new_clock_t();
856 49742 : db_save_meta_data(main_struct->database, meta, TRUE);
857 49742 : end_clock(mesure_time, "db_save_meta_data");
858 : }
859 : }
860 49742 : }
861 :
862 :
863 : /**
864 : * Process the file that is not already in our local cache
865 : * @param main_struct : main structure of the program
866 : * @param meta is the meta data of the file to be processed (it does
867 : * not contain any hashs at that point).
868 : */
869 3 : static void process_big_file_not_in_cache(main_struct_t *main_struct, meta_data_t *meta)
870 : {
871 3 : GFile *a_file = NULL;
872 3 : gchar *answer = NULL;
873 3 : GFileInputStream *stream = NULL;
874 3 : GError *error = NULL;
875 3 : GList *hash_data_list = NULL;
876 3 : hash_data_t *hash_data = NULL;
877 3 : gssize read = 0;
878 3 : guchar *buffer = NULL;
879 3 : GChecksum *checksum = NULL;
880 3 : guint8 *a_hash = NULL;
881 3 : gsize digest_len = HASH_LEN;
882 3 : gsize read_bytes = 0;
883 3 : json_t *array = NULL;
884 3 : json_t *to_insert = NULL;
885 3 : a_clock_t *elapsed = NULL;
886 :
887 3 : if (main_struct != NULL && main_struct->opt != NULL && meta != NULL)
888 : {
889 3 : a_file = g_file_new_for_path(meta->name);
890 3 : print_debug(_("Processing file: %s\n"), meta->name);
891 :
892 3 : if (a_file != NULL)
893 : {
894 3 : stream = g_file_read(a_file, NULL, &error);
895 :
896 3 : if (stream != NULL && error == NULL)
897 : {
898 :
899 3 : checksum = g_checksum_new(G_CHECKSUM_SHA256);
900 3 : buffer = (guchar *) g_malloc(meta->blocksize);
901 3 : a_hash = (guint8 *) g_malloc(digest_len);
902 :
903 3 : read = g_input_stream_read((GInputStream *) stream, buffer, meta->blocksize, NULL, &error);
904 3 : read_bytes = read_bytes + read;
905 3 : array = json_array();
906 :
907 5269 : while (read != 0 && error == NULL)
908 : {
909 5263 : g_checksum_update(checksum, buffer, read);
910 5263 : g_checksum_get_digest(checksum, a_hash, &digest_len);
911 :
912 : /* Need to save data and read in hash_data_t structure */
913 5263 : hash_data = new_hash_data_t(buffer, read, a_hash);
914 :
915 5263 : to_insert = convert_hash_data_t_to_json(hash_data);
916 5263 : json_array_append_new(array, to_insert);
917 :
918 : /* Only keeping hashs in the list */
919 5263 : hash_data->data = free_variable(hash_data->data);
920 5263 : hash_data_list = g_list_prepend(hash_data_list, hash_data);
921 :
922 5263 : g_checksum_reset(checksum);
923 5263 : digest_len = HASH_LEN;
924 :
925 5263 : if (read_bytes >= main_struct->opt->buffersize)
926 : {
927 : /* sending datas naïvely */
928 327 : elapsed = new_clock_t();
929 327 : print_debug(_("Sending data: %d bytes\n"), read_bytes);
930 327 : insert_array_in_root_and_send(main_struct, array);
931 327 : array = json_array();
932 327 : read_bytes = 0;
933 327 : end_clock(elapsed, "insert_array_in_root_and_send");
934 : }
935 :
936 5263 : buffer = (guchar *) g_malloc(meta->blocksize);
937 5263 : a_hash = (guint8 *) g_malloc(digest_len);
938 5263 : read = g_input_stream_read((GInputStream *) stream, buffer, meta->blocksize, NULL, &error);
939 5263 : read_bytes = read_bytes + read;
940 : }
941 :
942 3 : if (error != NULL)
943 : {
944 0 : print_error(__FILE__, __LINE__, _("Error while reading file: %s\n"), error->message);
945 0 : error = free_error(error);
946 0 : g_list_free_full(hash_data_list, free_hdt_struct);
947 0 : hash_data_list = NULL;
948 : }
949 : else
950 : {
951 :
952 : /* sending datas naïvely */
953 3 : if (read_bytes > 0)
954 : {
955 3 : elapsed = new_clock_t();
956 3 : print_debug(_("Sending data: %d bytes\n"), read_bytes);
957 3 : insert_array_in_root_and_send(main_struct, array);
958 3 : end_clock(elapsed, "insert_array_in_root_and_send");
959 : }
960 :
961 : /* get the list in correct order (because we prepended the hashs to get speed when inserting hashs in the list) */
962 3 : hash_data_list = g_list_reverse(hash_data_list);
963 : }
964 :
965 3 : free_variable(buffer);
966 3 : free_variable(a_hash);
967 3 : g_checksum_free(checksum);
968 3 : g_input_stream_close((GInputStream *) stream, NULL, NULL);
969 3 : free_object(stream);
970 : }
971 : else
972 : {
973 0 : print_error(__FILE__, __LINE__, _("Unable to open file for reading: %s\n"), error->message);
974 0 : error = free_error(error);
975 : }
976 :
977 3 : meta->hash_data_list = hash_data_list;
978 3 : answer = send_meta_data_to_server(main_struct, meta, TRUE);
979 :
980 3 : if (answer != NULL)
981 : { /** @todo may be we should check that answer is something that tells that everything went Ok. */
982 : /* Everything has been transmitted so we can save meta data into the local db cache */
983 : /* This is usefull for file carving to avoid sending too much things to the server */
984 3 : elapsed = new_clock_t();
985 3 : db_save_meta_data(main_struct->database, meta, TRUE);
986 3 : end_clock(elapsed, "db_save_meta_data");
987 : }
988 :
989 3 : a_file = free_object(a_file);
990 : }
991 : }
992 3 : }
993 :
994 :
995 : /**
996 : * This function gets meta data and data from a file and sends them
997 : * to the server in order to save the file located in the directory
998 : * 'directory' and represented by 'fileinfo' variable.
999 : * @param main_struct : main structure of the program
1000 : * @param directory is the directory we are iterating over
1001 : * @param fileinfo is a glib structure that contains all meta data and
1002 : * more for a file.
1003 : * @note This function is not threadable as is. One may have problems
1004 : * when writing to the database for instance.
1005 : */
1006 51772 : void save_one_file(main_struct_t *main_struct, file_event_t *file_event)
1007 : {
1008 51772 : meta_data_t *meta = NULL;
1009 51772 : a_clock_t *my_clock = NULL;
1010 51772 : gchar *message = NULL;
1011 51772 : gchar *another_dir = NULL;
1012 51772 : filter_file_t *filter = NULL;
1013 :
1014 51772 : if (main_struct != NULL && file_event != NULL)
1015 : {
1016 51772 : my_clock = new_clock_t();
1017 :
1018 : /* Get data and meta_data for a file. */
1019 103544 : filter = new_filter_t(main_struct->database, main_struct->regex_exclude_list, FALSE);
1020 51772 : meta = get_meta_data_from_fileinfo(file_event, filter, main_struct->opt);
1021 :
1022 : /* We want to save all files that are not excluded ie filter->excluded not TRUE */
1023 51772 : if (meta != NULL && filter != NULL && filter->excluded == FALSE)
1024 : {
1025 49745 : if (meta->in_cache == FALSE)
1026 : {
1027 : /* File is not in cache thus unknown thus we need to save it */
1028 49745 : if (meta->size < CLIENT_SMALL_FILE_SIZE)
1029 : {
1030 49742 : process_small_file_not_in_cache(main_struct, meta);
1031 : }
1032 : else
1033 : {
1034 3 : process_big_file_not_in_cache(main_struct, meta);
1035 : }
1036 : }
1037 :
1038 49745 : if (meta->file_type == G_FILE_TYPE_DIRECTORY)
1039 : {
1040 : /* This is a recursive call */
1041 329 : another_dir = g_strdup(meta->name);
1042 329 : g_async_queue_push(main_struct->dir_queue, another_dir);
1043 :
1044 : }
1045 49745 : message = g_strdup_printf(_("processing file %s"), meta->name);
1046 49745 : free_meta_data_t(meta, FALSE);
1047 : free_filter_file_t(filter);
1048 : }
1049 2027 : else if (meta != NULL && filter != NULL && filter->excluded == TRUE)
1050 : {
1051 2027 : message = g_strdup_printf(_("processing excluded file %s"), meta->name);
1052 2027 : free_meta_data_t(meta, FALSE);
1053 : free_filter_file_t(filter);
1054 : }
1055 : else
1056 : {
1057 0 : message = g_strdup_printf(_("Error with meta (%p) or filter (%p) structures\n"), meta, filter);
1058 0 : print_error(__FILE__, __LINE__, message);
1059 : }
1060 :
1061 51772 : end_clock(my_clock, message);
1062 51772 : free_variable(message);
1063 : }
1064 51772 : }
1065 :
1066 :
1067 : /**
1068 : * Iterates over an enumerator obtained from a directory.
1069 : * @param main_struct : main structure of the program
1070 : * @param directory is the directory we are iterating over
1071 : * @param file_enum is the enumerator obtained when opening a directory
1072 : * to carve it.
1073 : */
1074 333 : static void iterate_over_enum(main_struct_t *main_struct, gchar *directory, GFileEnumerator *file_enum)
1075 : {
1076 333 : GError *error = NULL;
1077 333 : GFileInfo *fileinfo = NULL;
1078 333 : file_event_t *file_event = NULL;
1079 :
1080 333 : if (main_struct != NULL && file_enum != NULL)
1081 : {
1082 333 : fileinfo = g_file_enumerator_next_file(file_enum, NULL, &error);
1083 :
1084 52430 : while (error == NULL && fileinfo != NULL)
1085 : {
1086 : /* file_event is used and freed in the thread
1087 : * save_one_file_threaded where the queue save_queue
1088 : * is used
1089 : */
1090 51764 : file_event = new_file_event_t(directory, fileinfo);
1091 51764 : g_async_queue_push(main_struct->save_queue, file_event);
1092 :
1093 51764 : fileinfo = free_object(fileinfo);
1094 :
1095 51764 : fileinfo = g_file_enumerator_next_file(file_enum, NULL, &error);
1096 : }
1097 : }
1098 333 : }
1099 :
1100 :
1101 : /**
1102 : * Call back for the g_slist_foreach function that carves one directory
1103 : * and sub directories in a recursive way.
1104 : * @param data is an element of opt->list ie: a gchar * that represents
1105 : * a directory name
1106 : * @param user_data is the main_struct_t * pointer to the main structure.
1107 : */
1108 333 : static void carve_one_directory(gpointer data, gpointer user_data)
1109 : {
1110 333 : gchar *directory = (gchar *) data;
1111 333 : main_struct_t *main_struct = (main_struct_t *) user_data;
1112 :
1113 333 : GFile *a_dir = NULL;
1114 333 : GFileEnumerator *file_enum = NULL;
1115 333 : GError *error = NULL;
1116 :
1117 333 : if (directory != NULL && main_struct != NULL)
1118 : {
1119 333 : a_dir = g_file_new_for_path(directory);
1120 333 : file_enum = g_file_enumerate_children(a_dir, "*", G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, &error);
1121 :
1122 333 : if (error == NULL && file_enum != NULL)
1123 : {
1124 333 : iterate_over_enum(main_struct, directory, file_enum);
1125 333 : g_file_enumerator_close(file_enum, NULL, NULL);
1126 333 : file_enum = free_object(file_enum);
1127 : }
1128 : else
1129 : {
1130 0 : print_error(__FILE__, __LINE__, _("Unable to enumerate directory %s: %s\n"), directory, error->message);
1131 0 : error = free_error(error);
1132 : }
1133 :
1134 333 : a_dir = free_object(a_dir);
1135 : }
1136 333 : }
1137 :
1138 :
1139 : /**
1140 : * Does carve all directories from the list in the option list.
1141 : * This function is a thread that is run at the end of the initialisation
1142 : * of main_struct structure.
1143 : * @param data: main structure of the program that contains also
1144 : * the options structure that should have a list of directories
1145 : * to save.
1146 : */
1147 1 : static gpointer carve_all_directories(gpointer data)
1148 : {
1149 1 : main_struct_t *main_struct = (main_struct_t *) data;
1150 1 : gchar *directory = NULL;
1151 :
1152 1 : if (main_struct != NULL && main_struct->opt != NULL)
1153 : {
1154 1 : g_slist_foreach(main_struct->opt->dirname_list, carve_one_directory, main_struct);
1155 :
1156 1 : directory = g_async_queue_pop(main_struct->dir_queue);
1157 :
1158 330 : while (directory != NULL)
1159 : {
1160 329 : carve_one_directory(directory, main_struct);
1161 329 : free_variable(directory);
1162 329 : directory = g_async_queue_pop(main_struct->dir_queue);
1163 : }
1164 : }
1165 :
1166 0 : return NULL;
1167 : }
1168 :
1169 :
1170 : /**
1171 : * Manages reconnections to the server and the data that may have been
1172 : * saved in local buffers while the server was unreachable.
1173 : * @param data: main structure of the program that contains also
1174 : * the options structure.
1175 : */
1176 1 : static gpointer reconnected(gpointer data)
1177 : {
1178 1 : main_struct_t *main_struct = (main_struct_t *) data;
1179 :
1180 :
1181 41 : while (main_struct != NULL)
1182 : {
1183 40 : if (db_is_there_buffers_to_transmit(main_struct->database))
1184 : {
1185 0 : if (is_server_alive(main_struct->reconnected))
1186 : {
1187 0 : print_debug(_("We have data and meta data to transmit to server\n"));
1188 : /* we need to do the job: transmit again the data
1189 : * sleep until we'll implement something
1190 : */
1191 0 : db_transmit_buffers(main_struct->database, main_struct->reconnected);
1192 0 : sleep(CLIENT_RECONNECT_SLEEP_TIME);
1193 : }
1194 : else
1195 : {
1196 0 : sleep(CLIENT_RECONNECT_SLEEP_TIME);
1197 : }
1198 : }
1199 : else
1200 : {
1201 40 : sleep(CLIENT_RECONNECT_SLEEP_TIME);
1202 : }
1203 : }
1204 :
1205 0 : return NULL;
1206 : }
1207 :
1208 :
1209 :
1210 : /**
1211 : * Main function
1212 : * @param argc : number of arguments given on the command line.
1213 : * @param argv : an array of strings that contains command line arguments.
1214 : * @returns always 0
1215 : */
1216 3 : int main(int argc, char **argv)
1217 : {
1218 3 : options_t *opt = NULL; /** Structure to manage options from the command line can be freed when no longer needed */
1219 3 : main_struct_t *main_struct = NULL;
1220 :
1221 : #if !GLIB_CHECK_VERSION(2, 36, 0)
1222 : g_type_init(); /** g_type_init() is deprecated since glib 2.36 */
1223 : #endif
1224 :
1225 3 : init_international_languages();
1226 :
1227 : /* Global curl initialisation to avoid curl_easy_init() calls to call it. */
1228 3 : curl_global_init(CURL_GLOBAL_ALL);
1229 :
1230 3 : opt = do_what_is_needed_from_command_line_options(argc, argv);
1231 :
1232 1 : if (opt != NULL)
1233 : {
1234 : /**
1235 : * Inits the main structure and launches two threads one that
1236 : * will save the files with an asynchronous queue and a second
1237 : * that will do directory carving. Threads communicates with
1238 : * the asynchronous queue.
1239 : */
1240 1 : main_struct = init_main_structure(opt);
1241 :
1242 : /** Launching an infinite loop to get modifications done on
1243 : * the filesystem (on directories we watch).
1244 : * @note fanotify's kernel interface does not provide the events
1245 : * needed to know if a file has been deleted or it's attributes
1246 : * changed. Enabling this feature even if we know that files
1247 : * will never get deleted in our database.
1248 : */
1249 1 : fanotify_loop(main_struct);
1250 :
1251 1 : free_options_t(main_struct->opt);
1252 : }
1253 :
1254 1 : return 0;
1255 : }
|