"Fossies" - the Fresh Open Source Software Archive

Member "snort-2.9.17/src/file-process/file_segment_process.c" (16 Oct 2020, 23604 Bytes) of package /linux/misc/snort-2.9.17.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "file_segment_process.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 2.9.16.1_vs_2.9.17.

    1 /****************************************************************************
    2  * Copyright (C) 2014-2020 Cisco and/or its affiliates. All rights reserved.
    3  * Copyright (C) 2008-2013 Sourcefire, Inc.
    4  *
    5  * This program is free software; you can redistribute it and/or modify
    6  * it under the terms of the GNU General Public License Version 2 as
    7  * published by the Free Software Foundation.  You may not use, modify or
    8  * distribute this program under any other version of the GNU General
    9  * Public License.
   10  *
   11  * This program is distributed in the hope that it will be useful,
   12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   14  * GNU General Public License for more details.
   15  *
   16  * You should have received a copy of the GNU General Public License
   17  * along with this program; if not, write to the Free Software
   18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
   19  *
   20  ****************************************************************************
   21  *  Author(s):  Hui Cao <huica@cisco.com>
   22  ****************************************************************************/
   23 
   24 #include "file_segment_process.h"
   25 #include "parser.h"
   26 #include "file_resume_block.h"
   27 #include "file_config.h"
   28 
   29 #ifdef REG_TEST
   30 #include "file_stats.h"
   31 #include "reg_test.h"
   32 #include <stdio.h>
   33 #endif
   34 
   35 #define   UNKNOWN_FILE_SIZE           ~0
   36 
   37 extern FileSession* get_file_session(void *ssnptr);
   38 
   39 static inline void file_segment_free(FileCache *fileCache, FileSegment* file_segment)
   40 {
   41     if (!file_segment)
   42         return;
   43 
   44     if (fileCache)
   45     {
   46         if(fileCache->status.segment_mem_in_use >= file_segment->segment_size)
   47             fileCache->status.segment_mem_in_use -= file_segment->segment_size;
   48         else
   49             fileCache->status.segment_mem_in_use = 0;
   50     }
   51 
   52     free(file_segment);
   53 }
   54 
   55 static inline void file_segments_free (FileEntry *file_entry)
   56 {
   57     FileSegment *current_segment;
   58     current_segment = file_entry->segments;
   59     while (current_segment)
   60     {
   61         FileSegment *previous_segment = current_segment;
   62         current_segment = current_segment->next;
   63         file_segment_free(file_entry->file_cache, previous_segment);
   64     }
   65 
   66     file_entry->segments = NULL;
   67     file_entry->offset = 0;
   68 }
   69 
   70 static inline void file_entry_free(FileEntry *file_entry)
   71 {
   72 
   73     if (!file_entry)
   74         return;
   75 
   76     if (file_entry->file_name)
   77     {
   78         DEBUG_WRAP(DebugMessage(DEBUG_FILE,
   79                               "File name: %s released (%p)\n", file_entry->file_name, file_entry->file_name));
   80         free(file_entry->file_name);
   81         file_entry->file_name = NULL;
   82         file_entry->file_name_size = 0;
   83     }
   84 
   85     if (file_entry->context)
   86     {
   87         file_entry->context->attached_file_entry = NULL;
   88         file_context_free(file_entry->context);
   89         file_entry->context = NULL;
   90     }
   91 
   92     file_entry->file_size = 0;
   93 
   94     file_segments_free(file_entry);
   95 
   96 }
   97 
   98 static int file_entry_free_func(void *option_key, void *data)
   99 {
  100     FileEntry *file_entry = ( FileEntry *)data;
  101     file_entry_free(file_entry);
  102     return 0;
  103 }
  104 
  105 /* Prune file entries based on LRU
  106  */
  107 static int  pruneFileCache(FileCache *fileCache, FileEntry *file)
  108 {
  109     SFXHASH_NODE  *lru_node = NULL;
  110     int pruned = 0;
  111     int mustdie = fileCache->cleanup_files;
  112 
  113     while (pruned < mustdie &&
  114             (sfxhash_count(fileCache->hashTable) > 0))
  115     {
  116         if ((lru_node =  sfxhash_lru_node(fileCache->hashTable)) != NULL)
  117         {
  118             if (lru_node->data == file)
  119                 break;
  120             if (sfxhash_free_node(fileCache->hashTable, lru_node) != SFXHASH_OK)
  121             {
  122                 LogMessage("WARNING: failed to remove file entry from hash.\n");
  123                 break;
  124             }
  125             pruned++;
  126         }
  127     }
  128 
  129     fileCache->status.prunes += pruned;
  130     return pruned;
  131 }
  132 
  133 FileEntry *file_cache_get(FileCache *fileCache, void* p, uint64_t file_id,
  134     bool can_create)
  135 {
  136     SFXHASH_NODE *hnode;
  137     FileKey fileKey;
  138     Packet *pkt = (Packet *)p;
  139     sfaddr_t* srcIP;
  140     sfaddr_t* dstIP;
  141     SAVE_DAQ_PKT_HDR(p);
  142 
  143     if ((fileCache == NULL) || (fileCache->hashTable == NULL))
  144     {
  145         FILE_WARNING("Failed to get file cache info");
  146         return NULL;
  147     }
  148 
  149     if ((pkt->packet_flags & PKT_FROM_CLIENT))
  150     {
  151         srcIP = GET_SRC_IP(pkt);
  152         dstIP = GET_DST_IP(pkt);
  153     }
  154     else
  155     {
  156         srcIP = GET_DST_IP(pkt);
  157         dstIP = GET_SRC_IP(pkt);
  158     }
  159 
  160     sfaddr_copy_to_raw(&fileKey.dip, dstIP);
  161     sfaddr_copy_to_raw(&fileKey.sip, srcIP);
  162     fileKey.file_id = file_id;
  163 
  164     if (!can_create)
  165     {
  166         hnode = sfxhash_find_node(fileCache->hashTable, &fileKey);
  167     }
  168     else
  169     {
  170 
  171         hnode = sfxhash_get_node(fileCache->hashTable, &fileKey);
  172 
  173         if (!hnode)
  174         {
  175             /*No more file entries, free up some old ones*/
  176             if(pruneFileCache(fileCache, NULL) == 0)
  177             {
  178                 FILE_WARNING("No free node available");
  179                 return NULL;
  180             }
  181 
  182             /* Should have some freed nodes now */
  183             hnode = sfxhash_get_node(fileCache->hashTable, &fileKey);
  184 
  185 #ifdef DEBUG_MSGS
  186             if (!hnode)
  187                 LogMessage("%s(%d) Problem, no freed nodes\n", __FILE__, __LINE__);
  188 #endif
  189         }
  190     }
  191 
  192     if (hnode && hnode->data)
  193     {
  194         FileEntry *file_entry = (FileEntry *)hnode->data;
  195 
  196         return file_entry;
  197     }
  198     else
  199     {
  200         FILE_WARNING("No free node available");
  201         return NULL;
  202     }
  203 }
  204 
  205 /* Initialize file cache based on memcap
  206  * File cache cost includes three part:
  207  * 1) file hash table
  208  * 2) file context
  209  * 3) file segment
  210  *
  211  * Both 1) and 2) can be limited by maximal files, 3) can be limited by memcap.
  212  */
  213 static inline uint32_t get_max_files_from_memcap (uint64_t memcap)
  214 {
  215     /* Per file cost*/
  216     uint32_t per_file_cost = sizeof(FileContext) + sizeof(FileKey) + sizeof(FileEntry);
  217 
  218     return (memcap/per_file_cost);
  219 }
  220 
  221 static inline FileSegment* file_segment_alloc (FileCache *fileCache,
  222         const uint8_t* file_data, int data_size, uint64_t offset, FileEntry *file)
  223 {
  224     FileSegment* ss;
  225     unsigned int size = sizeof(*ss);
  226 
  227     if ( data_size > 0 )
  228         size += (uint64_t)data_size - 1;  /* ss contains 1st byte */
  229     else
  230         return NULL;
  231 
  232     /* Check against memcap here*/
  233     if ((fileCache->status.segment_mem_in_use + size) > fileCache->file_segment_memcap)
  234     {
  235         /* make more memory available by pruning. Return NULL if nothing is pruned*/
  236         if(pruneFileCache(fileCache, file) == 0)
  237             return NULL;
  238         else if ((fileCache->status.segment_mem_in_use + size) > fileCache->file_segment_memcap)
  239             return NULL;
  240     }
  241 
  242     fileCache->status.segment_mem_in_use += size;
  243 
  244     ss = (FileSegment*) SnortAlloc(size);
  245     ss->segment_size = size;
  246     ss->size = data_size;
  247     ss->offset = offset;
  248     memcpy(ss->data, file_data, data_size);
  249 
  250     if (fileCache->status.segment_mem_in_use_max < fileCache->status.segment_mem_in_use)
  251     {
  252         fileCache->status.segment_mem_in_use_max = fileCache->status.segment_mem_in_use;
  253     }
  254 
  255     return ss;
  256 }
  257 
  258 /* Update the segment list based on new data
  259  * Use the original data if possible
  260  * Input:
  261  *    offset: offset in file for the new segment
  262  *    data_size: size of new segment
  263  *    file: the file entry
  264  */
  265 static inline int _file_segments_update( FileCache *fileCache,
  266         const uint8_t* file_data, uint64_t offset,
  267         int data_size, FileEntry *file)
  268 {
  269     FileSegment *current_segment = file->segments;
  270     uint64_t start = offset;
  271     uint64_t end = offset + data_size;
  272     FileSegment *new_segment;
  273     /* left points to segment that "next" pointer needs to be updated */
  274     FileSegment *left = NULL;
  275     FileSegment *previous = NULL;
  276     bool find_left = false;
  277 
  278     /* Find left boundary, left points to segment that needs update*/
  279     while (current_segment)
  280     {
  281         if (current_segment->offset > start)
  282         {
  283             find_left = true;
  284             left = previous;
  285             break;
  286         }
  287 
  288         previous = current_segment;
  289         current_segment = current_segment->next;
  290     }
  291 
  292     if (find_left)
  293     {
  294         if (!left)
  295         {   
  296             /* Need to insert at begining */
  297             if (end > file->segments->offset)
  298             {   
  299                 /* Overlap, trim off exrta data from end */
  300                 data_size = file->segments->offset - offset;
  301             }
  302         }
  303         else
  304         {   
  305             /* Need to insert in the middle */
  306             if (left->offset + left->size > start)
  307             {   
  308                 /* Overlap, trim begining */
  309                 offset = left->offset +left->size;
  310                 data_size = end - offset;
  311                 file_data = file_data + offset - start;
  312             }
  313             if (left->next->offset < end)
  314             {   
  315                 /* Overlap, trim end of data */
  316                 data_size = left->next->offset - offset;
  317             }
  318         }
  319     }
  320     else if (previous)
  321     {   
  322         /* Need to insert at end */
  323         left = previous;
  324         if (left->offset + left->size > start)
  325         {   
  326             /* Overlap, trim begining */
  327             offset = left->offset + left->size;
  328             data_size = end - offset;
  329             file_data = file_data + offset - start;
  330         }
  331     }
  332     if (data_size > 0)
  333     {
  334         new_segment = file_segment_alloc(fileCache, file_data, data_size, offset, file);
  335         if (!new_segment)
  336             return 0;
  337     }
  338     else
  339     {   
  340         /* data_size <= 0 means a complete coverlapped segment we have already seen */
  341         FILE_DEBUG("Complete overlapped segment, discarding.");
  342         return 0;
  343     }
  344     
  345     if (!left)
  346     {   
  347         /* inserting at begining or list is empty */
  348         new_segment->next = file->segments;
  349         file->segments = new_segment;
  350     }
  351     else
  352     {   
  353         /* inserting at end or middle */
  354         new_segment->next = left->next;
  355         left->next = new_segment;
  356     }
  357     return 1;
  358 }
  359 
  360 static inline FilePosition get_file_position(uint64_t file_size, int data_size,
  361         uint64_t offset)
  362 {
  363     if (offset == 0)
  364     {
  365         if (file_size == (uint64_t) data_size)
  366             return SNORT_FILE_FULL;
  367         else
  368             return SNORT_FILE_START;
  369     }
  370 
  371     if (file_size <= data_size + offset)
  372         return SNORT_FILE_END;
  373 
  374     return SNORT_FILE_MIDDLE;
  375 }
  376 
  377 static inline int _process_one_file_segment (void* p, FileEntry *fileEntry,
  378         const uint8_t* file_data, int *data_size, uint64_t file_size)
  379 {
  380     int ret;
  381 
  382     if(fileEntry->offset < file_size && fileEntry->offset + *data_size > file_size)
  383         *data_size = file_size - fileEntry->offset;
  384 
  385     FilePosition position = get_file_position(file_size, *data_size, fileEntry->offset);
  386     FILE_DEBUG("Processing segment, File size: %u, data size: %d, File position: %d",file_size,*data_size,position);
  387 
  388     if(fileEntry->file_size != UNKNOWN_FILE_SIZE)
  389     {
  390         if (position == SNORT_FILE_END && fileEntry->context && fileEntry->context->smb_unknown_file_size)
  391         {
  392             if((fileEntry->context->file_state.sig_state == FILE_SIG_FLUSH) && fileEntry->context->sha256)
  393             {
  394                 free(fileEntry->context->sha256);
  395                 fileEntry->context->sha256 = NULL;
  396             } 
  397             fileEntry->context->smb_unknown_file_size = false;
  398         }
  399         ret = file_api->process_file(fileEntry->context, p, (uint8_t *)file_data, *data_size, position, false);
  400     }
  401     else
  402     {
  403         Packet *pkt = (Packet *)p;
  404         if((fileEntry->context->file_state.sig_state == FILE_SIG_FLUSH) && fileEntry->context && fileEntry->context->sha256)
  405         {
  406             free(fileEntry->context->sha256);
  407             fileEntry->context->sha256 = NULL;
  408         }
  409         if(pkt->packet_flags & PKT_PDU_TAIL)
  410         {
  411             fileEntry->context->file_state.sig_state = FILE_SIG_FLUSH;
  412             fileEntry->context->smb_unknown_file_size = true;
  413         }
  414         else
  415             fileEntry->context->file_state.sig_state = FILE_SIG_PROCESSING;
  416 
  417         ret = file_api->process_file(fileEntry->context, p, (uint8_t *)file_data, *data_size, position, false);
  418     }
  419     return ret;
  420 }
  421 
  422 static inline int _process_file_segments(FileCache *fileCache, void* p, FileEntry *fileEntry,
  423         uint64_t file_size)
  424 {
  425     int ret = 1;
  426     /*Process the packet update the offset */
  427     FileSegment *current_segment = fileEntry->segments;
  428     while (current_segment && (fileEntry->offset == current_segment->offset))
  429     {
  430         ret = _process_one_file_segment(p, fileEntry, current_segment->data,
  431                 (int *)&current_segment->size, file_size);
  432 
  433         if (!ret)
  434         {
  435             file_segments_free(fileEntry);
  436             break;
  437         }
  438 
  439         fileEntry->offset += current_segment->size;
  440         fileEntry->segments = current_segment->next;
  441         file_segment_free(fileCache, current_segment);
  442         current_segment = fileEntry->segments;
  443     }
  444 
  445     return ret;
  446 }
  447 
  448 /* Create file cache */
  449 FileCache *file_cache_create(uint64_t memcap, uint32_t cleanup_files)
  450 {
  451     FileCache *fileCache = NULL;
  452     int  max_files = 0;
  453     uint64_t file_segment_memcap = memcap/2;
  454 
  455     if( !memcap )
  456     {
  457         WarningMessage("%s(%d) File cache memory unlimited!\n",
  458                 file_name, file_line);
  459     }
  460 
  461     /* Half for file segment, half for file context tracking*/
  462     max_files = get_max_files_from_memcap(memcap - file_segment_memcap);
  463 
  464     fileCache = SnortAlloc( sizeof( *fileCache ) );
  465     if( fileCache )
  466     {
  467         fileCache->max_files = max_files;
  468         /* Okay, now create the table */
  469         fileCache->hashTable = sfxhash_new(max_files, sizeof(FileKey), sizeof(FileEntry),
  470                 0, 0, NULL, file_entry_free_func, 1 );
  471 
  472         if (!fileCache->hashTable)
  473         {
  474             FatalError( "%s(%d) Unable to create a file cache.\n", file_name, file_line);
  475         }
  476 
  477         sfxhash_set_max_nodes( fileCache->hashTable, max_files );
  478         fileCache->file_segment_memcap = file_segment_memcap;
  479         fileCache->cleanup_files = cleanup_files;
  480     }
  481     else
  482     {
  483         FatalError( "%s(%d) Unable to create a file cache.\n",
  484                 file_name, file_line);
  485     }
  486 
  487     return fileCache;
  488 }
  489 
  490 /* Release file cache */
  491 void file_cache_free( FileCache *fileCache )
  492 {
  493     if (fileCache)
  494     {
  495         sfxhash_delete(fileCache->hashTable);
  496         free(fileCache);
  497     }
  498 }
  499 
  500 /* Add/update a file entry specified by file_id in the file cache*/
  501 void *file_cache_update_entry (FileCache *fileCache, void* p, uint64_t file_id,
  502         uint8_t *file_name, uint32_t file_name_size, uint64_t file_size, bool reset, bool no_update_size)
  503 {
  504     FileEntry *fileEntry;
  505 
  506     fileEntry = file_cache_get(fileCache, p, file_id, true);
  507 
  508     if (!fileEntry)
  509     {
  510         FILE_DEBUG("Failed to add file entry: file_id %d not found in cache",file_id);
  511         return NULL;
  512     }
  513 
  514     if(fileEntry->context && reset)
  515         file_entry_free(fileEntry);
  516 
  517     /* If no context, set resume check */
  518     if(!fileEntry->context)
  519         fileEntry->file_resume_check = true;
  520 
  521     if (file_name)
  522     {
  523         FILE_DEBUG("Add file: %s (%p) with file id %d", file_name,file_name,file_id);
  524         if (fileEntry->file_name && fileEntry->file_name != file_name)
  525         {
  526             FILE_DEBUG("File name: %s released (%p)", fileEntry->file_name,fileEntry->file_name);
  527             free(fileEntry->file_name);
  528         }
  529         fileEntry->file_name = file_name;
  530         fileEntry->file_name_size = file_name_size;
  531     }
  532 
  533     if (file_size)
  534     {
  535         if(no_update_size)
  536         {
  537             if(!fileEntry->file_size)
  538                 fileEntry->file_size = file_size;
  539         }
  540         else 
  541         {
  542             fileEntry->file_size = file_size;
  543         }
  544     }
  545 
  546     return fileEntry;
  547 }
  548 
  549 static inline void update_file_session(void *ssnptr, FileCache *fileCache,
  550     uint64_t file_id, FileContext *context)
  551 {
  552     FileSession *file_session;
  553 
  554     if (!file_api->set_current_file_context(ssnptr, context))
  555         return;
  556 
  557     file_session = get_file_session (ssnptr);
  558 
  559     if (!file_session->file_cache)
  560         file_session->file_cache = fileCache;
  561 
  562     file_session->file_id = file_id;
  563     FILE_DEBUG("Updated file_id: %u, file cache %p in file session %p",file_id, fileCache, file_session);
  564 }
  565 
  566 /*
  567  * Process file segment, do file segment reassemble if the file segment is
  568  * out of order. file_id is unique, used as a key to find the file entity.
  569  * Return:
  570  *    1: continue processing/log/block this file
  571  *    0: ignore this file segment
  572  */
  573 int file_segment_process( FileCache *fileCache, void* p, uint64_t file_id,
  574         uint64_t file_size, const uint8_t* file_data, int data_size, uint64_t offset,
  575         bool upload)
  576 {
  577     FileEntry *fileEntry;
  578     int ret = 0;
  579     Packet *pkt = (Packet *)p;
  580     void *ssnptr = pkt->ssnptr;
  581     File_Verdict verdict = FILE_VERDICT_UNKNOWN;
  582     uint32_t file_sig = 0;
  583     SAVE_DAQ_PKT_HDR(p);
  584     FILE_DEBUG("Processing segment: file_id: %u, file_size: %u, data_size: %d, offset: %u, direction: %d",file_id, file_size, data_size, offset, upload);
  585 
  586     fileEntry = file_cache_get(fileCache, p, file_id, true);
  587 
  588     if (fileEntry == NULL)
  589     {
  590         FILE_ERROR("Processing segment failed: no file entry in cache");
  591         return 0;
  592     }
  593 
  594     if (!fileEntry->context)
  595         fileEntry->file_resume_check = true;
  596 
  597     if(fileEntry->file_resume_check)
  598     {
  599         if(fileEntry->file_name_size > 0)
  600         {
  601             file_sig = file_api->str_to_hash(fileEntry->file_name, fileEntry->file_name_size);
  602         }
  603         else if(fileEntry->context && fileEntry->context->file_name_size > 0 && fileEntry->context->file_name)
  604         {
  605             file_sig = file_api->str_to_hash(fileEntry->context->file_name, fileEntry->context->file_name_size);
  606         }
  607 
  608         if(file_sig)
  609             verdict = file_resume_block_check(p, file_sig);
  610 
  611         if (verdict == FILE_VERDICT_BLOCK || verdict == FILE_VERDICT_REJECT || verdict == FILE_VERDICT_PENDING)
  612         {
  613 #ifdef HAVE_DAQ_DP_ADD_DC
  614             DAQ_DC_Params params;
  615             sfaddr_t *srcIP = GET_SRC_IP(pkt);
  616             sfaddr_t *dstIP = GET_DST_IP(pkt);
  617             memset(&params, 0, sizeof(params));
  618             params.flags = DAQ_DC_ALLOW_MULTIPLE;
  619             params.timeout_ms = 15 * 60 * 1000; /* 15 minutes */
  620             if (pkt->packet_flags & PKT_FROM_CLIENT)
  621                 DAQ_Add_Dynamic_Protocol_Channel(pkt, srcIP, 0, dstIP,  pkt->dp, GET_IPH_PROTO(pkt),
  622                         &params);
  623             else if (pkt->packet_flags & PKT_FROM_SERVER)
  624                 DAQ_Add_Dynamic_Protocol_Channel(pkt, dstIP, 0, srcIP, pkt->sp, GET_IPH_PROTO(pkt),
  625                         &params);
  626 #endif
  627             FILE_INFO("Processing segment stopped, verdict: %d\n",verdict);
  628             return 1;
  629         }
  630         fileEntry->file_resume_check = false;
  631     }
  632 
  633     if (fileEntry->file_size)
  634         file_size = fileEntry->file_size;
  635     else
  636     {
  637         FILE_ERROR("Processing segment failed: file size 0");
  638         return 0;
  639     }
  640 
  641     if (!fileEntry->file_cache)
  642         fileEntry->file_cache = fileCache;
  643 
  644     if (!fileEntry->context)
  645     {
  646         fileEntry->context = file_api->create_file_context(ssnptr);
  647         file_api->init_file_context(ssnptr, upload, fileEntry->context);
  648         fileEntry->context->file_id = (uint32_t)file_id;
  649         fileEntry->context->attached_file_entry = fileEntry;
  650         update_file_session(ssnptr, fileCache, file_id, fileEntry->context);
  651         file_name_set(fileEntry->context, fileEntry->file_name, fileEntry->file_name_size, true);
  652     }
  653     else if (fileEntry->context->verdict != FILE_VERDICT_UNKNOWN && !fileEntry->context->smb_unknown_file_size)
  654     {
  655         /*A new file session, but policy might be different*/
  656         if (((fileEntry->context->sha256))
  657                 || !fileEntry->context->file_signature_enabled )
  658         {
  659             /* Just check file type and signature */
  660             return _process_one_file_segment(p, fileEntry, file_data, &data_size, file_size);
  661         }
  662     }
  663 
  664     if(offset < fileEntry->offset && offset + data_size > fileEntry->offset)
  665     {
  666         file_data += (fileEntry->offset - offset);
  667         data_size = (offset + data_size)-fileEntry->offset;
  668         offset = fileEntry->offset;
  669     }
  670 
  671     /* Walk through the segments that can be flushed*/
  672     if (fileEntry->offset == offset)
  673     {
  674         /*Process the packet update the offset */
  675         ret = _process_one_file_segment(p, fileEntry, file_data, &data_size, file_size);
  676         fileEntry->offset += data_size;
  677         if (!ret)
  678         {
  679             file_segments_free(fileEntry);
  680             return 0;
  681         }
  682 
  683         ret = _process_file_segments(fileCache, p, fileEntry, file_size);
  684     }
  685     else if ((fileEntry->offset < file_size) && (fileEntry->offset < offset))
  686     {
  687         ret = _file_segments_update(fileCache, file_data, offset, data_size, fileEntry);
  688     }
  689 #ifdef REG_TEST
  690     if(ret && fileEntry->file_name_size)
  691     {
  692         FILE_REG_DEBUG_WRAP(printFileContext(fileEntry->context));
  693         fileEntry->file_name_size = 0;
  694     }
  695 #endif
  696 
  697     return ret;
  698 }
  699 
  700 /* Return the status of file cache */
  701 FileCacheStatus *file_cache_status(FileCache *fileCache)
  702 {
  703     return (&(fileCache->status));
  704 }
  705 
  706 static bool file_cache_prune_files(FileCache *fileCache, uint8_t *pWork)
  707 {
  708 #ifdef REG_TEST
  709     if (REG_TEST_FLAG_FILE_CACHE & getRegTestFlags())
  710         printf("file-cache prunefiles-before %u \n", sfxhash_count(fileCache->hashTable));
  711 #endif
  712     for (; *pWork > 0 && sfxhash_count(fileCache->hashTable) > fileCache->hashTable->max_nodes; (*pWork)--)
  713         pruneFileCache(fileCache,NULL);
  714 #ifdef REG_TEST
  715     if (REG_TEST_FLAG_FILE_CACHE & getRegTestFlags())
  716         printf("file-cache prunefiles-after %u \n", sfxhash_count(fileCache->hashTable));
  717 #endif
  718     return sfxhash_count(fileCache->hashTable) <= fileCache->hashTable->max_nodes;
  719 }
  720 
  721 static bool file_cache_prune_segment(FileCache *fileCache, uint8_t *pWork)
  722 {
  723 #ifdef REG_TEST
  724     if (REG_TEST_FLAG_FILE_CACHE & getRegTestFlags())
  725         printf("file-cache prunesegment-before %"PRIu64" \n",
  726             fileCache->status.segment_mem_in_use);
  727 #endif
  728     for (; *pWork > 0 && fileCache->status.segment_mem_in_use > fileCache->file_segment_memcap; (*pWork)--)
  729         pruneFileCache(fileCache,NULL);
  730 #ifdef REG_TEST
  731     if (REG_TEST_FLAG_FILE_CACHE & getRegTestFlags())
  732         printf("file-cache prunesegment-after %"PRIu64" \n",
  733             fileCache->status.segment_mem_in_use);
  734 #endif
  735     return fileCache->status.segment_mem_in_use <= fileCache->file_segment_memcap;
  736 }
  737 
  738 bool file_cache_shrink_to_memcap(FileCache *fileCache, uint8_t *pWork)
  739 {
  740     if (fileCache == NULL)
  741         return true;
  742 
  743     bool cache_shrunk = file_cache_prune_files(fileCache, pWork) &&
  744                         file_cache_prune_segment(fileCache, pWork);
  745 #ifdef REG_TEST
  746     if (cache_shrunk && REG_TEST_FLAG_FILE_CACHE & getRegTestFlags())
  747        printf("file-cache done 1\n");
  748 #endif
  749     return cache_shrunk;
  750 }
  751 
  752 void file_cache_set_memcap(FileCache *fileCache, uint64_t memcap)
  753 {
  754     if (fileCache == NULL)
  755         return;
  756     sfxhash_set_max_nodes(fileCache->hashTable, get_max_files_from_memcap(memcap/2));
  757     fileCache->file_segment_memcap = memcap/2;
  758 #ifdef REG_TEST
  759     if (REG_TEST_FLAG_FILE_CACHE & getRegTestFlags())
  760     {
  761         printf("file-cache mem-files %"PRIu32" \n", get_max_files_from_memcap(memcap/2));
  762         printf("file-cache mem-segment %"PRIu64" \n", memcap/2);
  763     }
  764 #endif
  765 }
  766