LCOV - code coverage report
Current view: top level - server - file_backend.c (source / functions) Hit Total Coverage
Test: coverage-server.info Lines: 177 436 40.6 %
Date: 2016-02-03 22:31:45 Functions: 7 18 38.9 %

          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             : }

Generated by: LCOV version 1.11