aboutsummaryrefslogtreecommitdiffstats
path: root/framework/src/suricata/src/util-file.c
diff options
context:
space:
mode:
Diffstat (limited to 'framework/src/suricata/src/util-file.c')
-rw-r--r--framework/src/suricata/src/util-file.c932
1 files changed, 932 insertions, 0 deletions
diff --git a/framework/src/suricata/src/util-file.c b/framework/src/suricata/src/util-file.c
new file mode 100644
index 00000000..bfb68e1e
--- /dev/null
+++ b/framework/src/suricata/src/util-file.c
@@ -0,0 +1,932 @@
+/* Copyright (C) 2007-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ * \author Pablo Rincon <pablo.rincon.crespo@gmail.com>
+ *
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+#include "debug.h"
+#include "flow.h"
+#include "stream.h"
+#include "runmodes.h"
+#include "util-hash.h"
+#include "util-debug.h"
+#include "util-memcmp.h"
+#include "util-print.h"
+#include "app-layer-parser.h"
+#include "util-validate.h"
+
+/** \brief switch to force magic checks on all files
+ * regardless of the rules.
+ */
+static int g_file_force_magic = 0;
+
+/** \brief switch to force md5 calculation on all files
+ * regardless of the rules.
+ */
+static int g_file_force_md5 = 0;
+
+/** \brief switch to force tracking off all files
+ * regardless of the rules.
+ */
+static int g_file_force_tracking = 0;
+
+/* prototypes */
+static void FileFree(File *);
+static void FileDataFree(FileData *);
+
+void FileForceMagicEnable(void)
+{
+ g_file_force_magic = 1;
+}
+
+void FileForceMd5Enable(void)
+{
+ g_file_force_md5 = 1;
+}
+
+int FileForceMagic(void)
+{
+ return g_file_force_magic;
+}
+
+int FileForceMd5(void)
+{
+ return g_file_force_md5;
+}
+
+void FileForceTrackingEnable(void)
+{
+ g_file_force_tracking = 1;
+}
+
+int FileMagicSize(void)
+{
+ /** \todo make this size configurable */
+ return 512;
+}
+
+static int FileAppendFileDataFilePtr(File *ff, FileData *ffd)
+{
+ SCEnter();
+
+ if (ff == NULL) {
+ SCReturnInt(-1);
+ }
+
+ if (ff->chunks_tail == NULL) {
+ ff->chunks_head = ffd;
+ ff->chunks_tail = ffd;
+ ff->content_len_so_far = ffd->len;
+ } else {
+ ff->chunks_tail->next = ffd;
+ ff->chunks_tail = ffd;
+ ff->content_len_so_far += ffd->len;
+ }
+
+#ifdef DEBUG
+ ff->chunks_cnt++;
+ if (ff->chunks_cnt > ff->chunks_cnt_max)
+ ff->chunks_cnt_max = ff->chunks_cnt;
+#endif
+
+#ifdef HAVE_NSS
+ if (ff->md5_ctx)
+ HASH_Update(ff->md5_ctx, ffd->data, ffd->len);
+#endif
+ SCReturnInt(0);
+}
+
+static int FileAppendFileData(FileContainer *ffc, FileData *ffd)
+{
+ SCEnter();
+
+ if (ffc == NULL) {
+ SCReturnInt(-1);
+ }
+
+ if (FileAppendFileDataFilePtr(ffc->tail, ffd) == -1)
+ {
+ SCReturnInt(-1);
+ }
+
+ SCReturnInt(0);
+}
+
+
+
+static int FilePruneFile(File *file)
+{
+ SCEnter();
+
+ SCLogDebug("file %p, file->chunks_cnt %"PRIu64, file, file->chunks_cnt);
+
+ if (!(file->flags & FILE_NOMAGIC)) {
+ /* need magic but haven't set it yet, bail out */
+ if (file->magic == NULL)
+ SCReturnInt(0);
+ else
+ SCLogDebug("file->magic %s", file->magic);
+ } else {
+ SCLogDebug("file->flags & FILE_NOMAGIC == true");
+ }
+
+ /* okay, we now know we can prune */
+ FileData *fd = file->chunks_head;
+
+ while (fd != NULL) {
+ SCLogDebug("fd %p", fd);
+
+ if (file->flags & FILE_NOSTORE || fd->stored == 1) {
+ file->chunks_head = fd->next;
+ if (file->chunks_tail == fd)
+ file->chunks_tail = fd->next;
+
+ FileDataFree(fd);
+
+ fd = file->chunks_head;
+#ifdef DEBUG
+ file->chunks_cnt--;
+ SCLogDebug("file->chunks_cnt %"PRIu64, file->chunks_cnt);
+#endif
+ } else if (fd->stored == 0) {
+ fd = NULL;
+ SCReturnInt(0);
+ break;
+ }
+ }
+
+ /* file is done when state is closed+, logging/storing is done (if any) */
+ if (file->state >= FILE_STATE_CLOSED &&
+ (!RunModeOutputFileEnabled() || (file->flags & FILE_LOGGED)) &&
+ (!RunModeOutputFiledataEnabled() || (file->flags & FILE_STORED)))
+ {
+ SCReturnInt(1);
+ } else {
+ SCReturnInt(0);
+ }
+}
+
+void FilePrune(FileContainer *ffc)
+{
+ File *file = ffc->head;
+
+ while (file) {
+ if (FilePruneFile(file) == 0)
+ break;
+
+ BUG_ON(file != ffc->head);
+
+ File *file_next = file->next;
+
+ /* update head and tail */
+ ffc->head = file_next;
+ if (file == ffc->tail)
+ ffc->tail = NULL;
+
+ FileFree(file);
+ file = file_next;
+ }
+}
+
+/**
+ * \brief allocate a FileContainer
+ *
+ * \retval new newly allocated FileContainer
+ * \retval NULL error
+ */
+FileContainer *FileContainerAlloc(void)
+{
+ FileContainer *new = SCMalloc(sizeof(FileContainer));
+ if (unlikely(new == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating mem");
+ return NULL;
+ }
+ memset(new, 0, sizeof(FileContainer));
+ new->head = new->tail = NULL;
+ return new;
+}
+
+/**
+ * \brief Recycle a FileContainer
+ *
+ * \param ffc FileContainer
+ */
+void FileContainerRecycle(FileContainer *ffc)
+{
+ if (ffc == NULL)
+ return;
+
+ File *cur = ffc->head;
+ File *next = NULL;
+ for (;cur != NULL; cur = next) {
+ next = cur->next;
+ FileFree(cur);
+ }
+ ffc->head = ffc->tail = NULL;
+}
+
+/**
+ * \brief Free a FileContainer
+ *
+ * \param ffc FileContainer
+ */
+void FileContainerFree(FileContainer *ffc)
+{
+ if (ffc == NULL)
+ return;
+
+ File *ptr = ffc->head;
+ File *next = NULL;
+ for (;ptr != NULL; ptr = next) {
+ next = ptr->next;
+ FileFree(ptr);
+ }
+ ffc->head = ffc->tail = NULL;
+ SCFree(ffc);
+}
+
+/**
+ * \internal
+ *
+ * \brief allocate a FileData chunk and set it up
+ *
+ * \param data data chunk to store in the FileData
+ * \param data_len lenght of the data
+ *
+ * \retval new FileData object
+ */
+static FileData *FileDataAlloc(uint8_t *data, uint32_t data_len)
+{
+ FileData *new = SCMalloc(sizeof(FileData));
+ if (unlikely(new == NULL)) {
+ return NULL;
+ }
+ memset(new, 0, sizeof(FileData));
+
+ new->data = SCMalloc(data_len);
+ if (new->data == NULL) {
+ SCFree(new);
+ return NULL;
+ }
+
+ new->len = data_len;
+ memcpy(new->data, data, data_len);
+
+ new->next = NULL;
+ return new;
+}
+
+/**
+ * \internal
+ *
+ * \brief free a FileData object
+ *
+ * \param ffd the flow file data object to free
+ */
+static void FileDataFree(FileData *ffd)
+{
+ if (ffd == NULL)
+ return;
+
+ if (ffd->data != NULL) {
+ SCFree(ffd->data);
+ }
+
+ SCFree(ffd);
+}
+
+/**
+ * \brief Alloc a new File
+ *
+ * \param name character array containing the name (not a string)
+ * \param name_len length in bytes of the name
+ *
+ * \retval new File object or NULL on error
+ */
+static File *FileAlloc(uint8_t *name, uint16_t name_len)
+{
+ File *new = SCMalloc(sizeof(File));
+ if (unlikely(new == NULL)) {
+ SCLogError(SC_ERR_MEM_ALLOC, "Error allocating mem");
+ return NULL;
+ }
+ memset(new, 0, sizeof(File));
+
+ new->name = SCMalloc(name_len);
+ if (new->name == NULL) {
+ SCFree(new);
+ return NULL;
+ }
+
+ new->name_len = name_len;
+ memcpy(new->name, name, name_len);
+
+ return new;
+}
+
+static void FileFree(File *ff)
+{
+ if (ff == NULL)
+ return;
+
+ if (ff->name != NULL)
+ SCFree(ff->name);
+
+ /* magic returned by libmagic is strdup'd by MagicLookup. */
+ if (ff->magic != NULL)
+ SCFree(ff->magic);
+
+ if (ff->chunks_head != NULL) {
+ FileData *ffd = ff->chunks_head;
+
+ while (ffd != NULL) {
+ FileData *next_ffd = ffd->next;
+ FileDataFree(ffd);
+ ffd = next_ffd;
+ }
+ }
+
+#ifdef HAVE_NSS
+ if (ff->md5_ctx)
+ HASH_Destroy(ff->md5_ctx);
+#endif
+ SCLogDebug("ff chunks_cnt %"PRIu64", chunks_cnt_max %"PRIu64,
+ ff->chunks_cnt, ff->chunks_cnt_max);
+ SCFree(ff);
+}
+
+void FileContainerAdd(FileContainer *ffc, File *ff)
+{
+ if (ffc->head == NULL || ffc->tail == NULL) {
+ ffc->head = ffc->tail = ff;
+ } else {
+ ffc->tail->next = ff;
+ ffc->tail = ff;
+ }
+}
+
+/**
+ * \brief Tag a file for storing
+ *
+ * \param ff The file to store
+ */
+int FileStore(File *ff)
+{
+ ff->flags |= FILE_STORE;
+ SCReturnInt(0);
+}
+
+/**
+ * \brief Set the TX id for a file
+ *
+ * \param ff The file to store
+ * \param txid the tx id
+ */
+int FileSetTx(File *ff, uint64_t txid)
+{
+ SCLogDebug("ff %p txid %"PRIu64, ff, txid);
+ if (ff != NULL)
+ ff->txid = txid;
+ SCReturnInt(0);
+}
+
+/**
+ * \brief check if we have stored enough
+ *
+ * \param ff file
+ *
+ * \retval 0 limit not reached yet
+ * \retval 1 limit reached
+ */
+static int FileStoreNoStoreCheck(File *ff)
+{
+ SCEnter();
+
+ if (ff == NULL) {
+ SCReturnInt(0);
+ }
+
+ if (ff->flags & FILE_NOSTORE) {
+ if (ff->state == FILE_STATE_OPENED &&
+ ff->size >= (uint64_t)FileMagicSize())
+ {
+ SCReturnInt(1);
+ }
+ }
+
+ SCReturnInt(0);
+}
+
+/**
+ * \brief Store a chunk of file data in the flow. The open "flowfile"
+ * will be used.
+ *
+ * \param ffc the container
+ * \param data data chunk
+ * \param data_len data chunk len
+ *
+ * \retval 0 ok
+ * \retval -1 error
+ * \retval -2 no store for this file
+ */
+int FileAppendData(FileContainer *ffc, uint8_t *data, uint32_t data_len)
+{
+ SCEnter();
+
+ if (ffc == NULL || ffc->tail == NULL || data == NULL || data_len == 0) {
+ SCReturnInt(-1);
+ }
+
+ if (ffc->tail->state != FILE_STATE_OPENED) {
+ if (ffc->tail->flags & FILE_NOSTORE) {
+ SCReturnInt(-2);
+ }
+ SCReturnInt(-1);
+ }
+
+ ffc->tail->size += data_len;
+ SCLogDebug("file size is now %"PRIu64, ffc->tail->size);
+
+ if (FileStoreNoStoreCheck(ffc->tail) == 1) {
+#ifdef HAVE_NSS
+ /* no storage but forced md5 */
+ if (ffc->tail->md5_ctx) {
+ if (ffc->tail->md5_ctx)
+ HASH_Update(ffc->tail->md5_ctx, data, data_len);
+
+ SCReturnInt(0);
+ }
+#endif
+ if (g_file_force_tracking || (!(ffc->tail->flags & FILE_NOTRACK)))
+ SCReturnInt(0);
+
+ ffc->tail->state = FILE_STATE_TRUNCATED;
+ SCLogDebug("flowfile state transitioned to FILE_STATE_TRUNCATED");
+ SCReturnInt(-2);
+ }
+
+ SCLogDebug("appending %"PRIu32" bytes", data_len);
+
+ FileData *ffd = FileDataAlloc(data, data_len);
+ if (ffd == NULL) {
+ ffc->tail->state = FILE_STATE_ERROR;
+ SCReturnInt(-1);
+ }
+
+ if (ffc->tail->chunks_head == NULL)
+ ffd->stream_offset = 0;
+ else
+ ffd->stream_offset = ffc->tail->size;
+
+ /* append the data */
+ if (FileAppendFileData(ffc, ffd) < 0) {
+ ffc->tail->state = FILE_STATE_ERROR;
+ FileDataFree(ffd);
+ SCReturnInt(-1);
+ }
+ SCReturnInt(0);
+}
+
+/**
+ * \brief Open a new File
+ *
+ * \param ffc flow container
+ * \param name filename character array
+ * \param name_len filename len
+ * \param data initial data
+ * \param data_len initial data len
+ * \param flags open flags
+ *
+ * \retval ff flowfile object
+ *
+ * \note filename is not a string, so it's not nul terminated.
+ */
+File *FileOpenFile(FileContainer *ffc, uint8_t *name,
+ uint16_t name_len, uint8_t *data, uint32_t data_len, uint8_t flags)
+{
+ SCEnter();
+
+ //PrintRawDataFp(stdout, name, name_len);
+
+ File *ff = FileAlloc(name, name_len);
+ if (ff == NULL) {
+ SCReturnPtr(NULL, "File");
+ }
+
+ if (flags & FILE_STORE) {
+ ff->flags |= FILE_STORE;
+ } else if (flags & FILE_NOSTORE) {
+ SCLogDebug("not storing this file");
+ ff->flags |= FILE_NOSTORE;
+ }
+ if (flags & FILE_NOMAGIC) {
+ SCLogDebug("not doing magic for this file");
+ ff->flags |= FILE_NOMAGIC;
+ }
+ if (flags & FILE_NOMD5) {
+ SCLogDebug("not doing md5 for this file");
+ ff->flags |= FILE_NOMD5;
+ }
+
+#ifdef HAVE_NSS
+ if (!(ff->flags & FILE_NOMD5) || g_file_force_md5) {
+ ff->md5_ctx = HASH_Create(HASH_AlgMD5);
+ if (ff->md5_ctx != NULL) {
+ HASH_Begin(ff->md5_ctx);
+ }
+ }
+#endif
+
+ ff->state = FILE_STATE_OPENED;
+ SCLogDebug("flowfile state transitioned to FILE_STATE_OPENED");
+
+ FileContainerAdd(ffc, ff);
+
+ if (data != NULL) {
+ //PrintRawDataFp(stdout, data, data_len);
+ ff->size += data_len;
+ SCLogDebug("file size is now %"PRIu64, ff->size);
+
+ FileData *ffd = FileDataAlloc(data, data_len);
+ if (ffd == NULL) {
+ ff->state = FILE_STATE_ERROR;
+ SCReturnPtr(NULL, "File");
+ }
+
+ /* append the data */
+ if (FileAppendFileData(ffc, ffd) < 0) {
+ ff->state = FILE_STATE_ERROR;
+ FileDataFree(ffd);
+ SCReturnPtr(NULL, "File");
+ }
+ }
+
+ SCReturnPtr(ff, "File");
+}
+
+static int FileCloseFilePtr(File *ff, uint8_t *data,
+ uint32_t data_len, uint8_t flags)
+{
+ SCEnter();
+
+ if (ff == NULL) {
+ SCReturnInt(-1);
+ }
+
+ if (ff->state != FILE_STATE_OPENED) {
+ SCReturnInt(-1);
+ }
+
+ ff->size += data_len;
+ SCLogDebug("file size is now %"PRIu64, ff->size);
+
+ if (data != NULL) {
+ //PrintRawDataFp(stdout, data, data_len);
+
+ if (ff->flags & FILE_NOSTORE) {
+#ifdef HAVE_NSS
+ /* no storage but md5 */
+ if (ff->md5_ctx)
+ HASH_Update(ff->md5_ctx, data, data_len);
+#endif
+ } else {
+ FileData *ffd = FileDataAlloc(data, data_len);
+ if (ffd == NULL) {
+ ff->state = FILE_STATE_ERROR;
+ SCReturnInt(-1);
+ }
+
+ /* append the data */
+ if (FileAppendFileDataFilePtr(ff, ffd) < 0) {
+ ff->state = FILE_STATE_ERROR;
+ FileDataFree(ffd);
+ SCReturnInt(-1);
+ }
+ }
+ }
+
+ if (flags & FILE_TRUNCATED) {
+ ff->state = FILE_STATE_TRUNCATED;
+ SCLogDebug("flowfile state transitioned to FILE_STATE_TRUNCATED");
+
+ if (flags & FILE_NOSTORE) {
+ SCLogDebug("not storing this file");
+ ff->flags |= FILE_NOSTORE;
+ }
+ } else {
+ ff->state = FILE_STATE_CLOSED;
+ SCLogDebug("flowfile state transitioned to FILE_STATE_CLOSED");
+
+#ifdef HAVE_NSS
+ if (ff->md5_ctx) {
+ unsigned int len = 0;
+ HASH_End(ff->md5_ctx, ff->md5, &len, sizeof(ff->md5));
+ ff->flags |= FILE_MD5;
+ }
+#endif
+ }
+
+ SCReturnInt(0);
+}
+
+/**
+ * \brief Close a File
+ *
+ * \param ffc the container
+ * \param data final data if any
+ * \param data_len data len if any
+ * \param flags flags
+ *
+ * \retval 0 ok
+ * \retval -1 error
+ */
+int FileCloseFile(FileContainer *ffc, uint8_t *data,
+ uint32_t data_len, uint8_t flags)
+{
+ SCEnter();
+
+ if (ffc == NULL || ffc->tail == NULL) {
+ SCReturnInt(-1);
+ }
+
+ if (FileCloseFilePtr(ffc->tail, data, data_len, flags) == -1) {
+ SCReturnInt(-1);
+ }
+
+ SCReturnInt(0);
+}
+
+/**
+ * \brief disable file storage for a flow
+ *
+ * \param f *LOCKED* flow
+ * \param direction flow direction
+ */
+void FileDisableStoring(Flow *f, uint8_t direction)
+{
+ File *ptr = NULL;
+
+ SCEnter();
+
+ DEBUG_ASSERT_FLOW_LOCKED(f);
+
+ if (direction == STREAM_TOSERVER)
+ f->flags |= FLOW_FILE_NO_STORE_TS;
+ else
+ f->flags |= FLOW_FILE_NO_STORE_TC;
+
+ FileContainer *ffc = AppLayerParserGetFiles(f->proto, f->alproto, f->alstate, direction);
+ if (ffc != NULL) {
+ for (ptr = ffc->head; ptr != NULL; ptr = ptr->next) {
+ /* if we're already storing, we'll continue */
+ if (!(ptr->flags & FILE_STORE)) {
+ SCLogDebug("not storing this file");
+ ptr->flags |= FILE_NOSTORE;
+ }
+ }
+ }
+ SCReturn;
+}
+
+/**
+ * \brief disable file magic lookups for this flow
+ *
+ * \param f *LOCKED* flow
+ * \param direction flow direction
+ */
+void FileDisableMagic(Flow *f, uint8_t direction)
+{
+ File *ptr = NULL;
+
+ SCEnter();
+
+ DEBUG_ASSERT_FLOW_LOCKED(f);
+
+ if (direction == STREAM_TOSERVER)
+ f->flags |= FLOW_FILE_NO_MAGIC_TS;
+ else
+ f->flags |= FLOW_FILE_NO_MAGIC_TC;
+
+ FileContainer *ffc = AppLayerParserGetFiles(f->proto, f->alproto, f->alstate, direction);
+ if (ffc != NULL) {
+ for (ptr = ffc->head; ptr != NULL; ptr = ptr->next) {
+ SCLogDebug("disabling magic for file %p from direction %s",
+ ptr, direction == STREAM_TOSERVER ? "toserver":"toclient");
+ ptr->flags |= FILE_NOMAGIC;
+ }
+ }
+
+ SCReturn;
+}
+
+/**
+ * \brief disable file md5 calc for this flow
+ *
+ * \param f *LOCKED* flow
+ * \param direction flow direction
+ */
+void FileDisableMd5(Flow *f, uint8_t direction)
+{
+ File *ptr = NULL;
+
+ SCEnter();
+
+ DEBUG_ASSERT_FLOW_LOCKED(f);
+
+ if (direction == STREAM_TOSERVER)
+ f->flags |= FLOW_FILE_NO_MD5_TS;
+ else
+ f->flags |= FLOW_FILE_NO_MD5_TC;
+
+ FileContainer *ffc = AppLayerParserGetFiles(f->proto, f->alproto, f->alstate, direction);
+ if (ffc != NULL) {
+ for (ptr = ffc->head; ptr != NULL; ptr = ptr->next) {
+ SCLogDebug("disabling md5 for file %p from direction %s",
+ ptr, direction == STREAM_TOSERVER ? "toserver":"toclient");
+ ptr->flags |= FILE_NOMD5;
+
+#ifdef HAVE_NSS
+ /* destroy any ctx we may have so far */
+ if (ptr->md5_ctx != NULL) {
+ HASH_Destroy(ptr->md5_ctx);
+ ptr->md5_ctx = NULL;
+ }
+#endif
+ }
+ }
+
+ SCReturn;
+}
+
+/**
+ * \brief disable file size tracking for this flow
+ *
+ * \param f *LOCKED* flow
+ * \param direction flow direction
+ */
+void FileDisableFilesize(Flow *f, uint8_t direction)
+{
+ File *ptr = NULL;
+
+ SCEnter();
+
+ DEBUG_ASSERT_FLOW_LOCKED(f);
+
+ if (direction == STREAM_TOSERVER)
+ f->flags |= FLOW_FILE_NO_SIZE_TS;
+ else
+ f->flags |= FLOW_FILE_NO_SIZE_TC;
+
+ FileContainer *ffc = AppLayerParserGetFiles(f->proto, f->alproto, f->alstate, direction);
+ if (ffc != NULL) {
+ for (ptr = ffc->head; ptr != NULL; ptr = ptr->next) {
+ SCLogDebug("disabling size tracking for file %p from direction %s",
+ ptr, direction == STREAM_TOSERVER ? "toserver":"toclient");
+ ptr->flags |= FILE_NOTRACK;
+ }
+ }
+
+ SCReturn;
+}
+
+
+/**
+ * \brief set no store flag, close file if needed
+ *
+ * \param ff file
+ */
+void FileDisableStoringForFile(File *ff)
+{
+ SCEnter();
+
+ if (ff == NULL) {
+ SCReturn;
+ }
+
+ SCLogDebug("not storing this file");
+ ff->flags |= FILE_NOSTORE;
+
+ if (ff->state == FILE_STATE_OPENED && ff->size >= (uint64_t)FileMagicSize()) {
+ if (g_file_force_md5 == 0 && g_file_force_tracking == 0) {
+ (void)FileCloseFilePtr(ff, NULL, 0,
+ (FILE_TRUNCATED|FILE_NOSTORE));
+ }
+ }
+}
+
+/**
+ * \brief disable file storing for files in a transaction
+ *
+ * \param f *LOCKED* flow
+ * \param direction flow direction
+ * \param tx_id transaction id
+ */
+void FileDisableStoringForTransaction(Flow *f, uint8_t direction, uint64_t tx_id)
+{
+ File *ptr = NULL;
+
+ DEBUG_ASSERT_FLOW_LOCKED(f);
+
+ SCEnter();
+
+ FileContainer *ffc = AppLayerParserGetFiles(f->proto, f->alproto, f->alstate, direction);
+ if (ffc != NULL) {
+ for (ptr = ffc->head; ptr != NULL; ptr = ptr->next) {
+ if (ptr->txid == tx_id) {
+ if (ptr->flags & FILE_STORE) {
+ /* weird, already storing -- let it continue*/
+ SCLogDebug("file is already being stored");
+ } else {
+ FileDisableStoringForFile(ptr);
+ }
+ }
+ }
+ }
+
+ SCReturn;
+}
+
+/**
+ * \brief flag a file with id "file_id" to be stored.
+ *
+ * \param fc file store
+ * \param file_id the file's id
+ */
+void FileStoreFileById(FileContainer *fc, uint16_t file_id)
+{
+ File *ptr = NULL;
+
+ SCEnter();
+
+ if (fc != NULL) {
+ for (ptr = fc->head; ptr != NULL; ptr = ptr->next) {
+ if (ptr->file_id == file_id) {
+ ptr->flags |= FILE_STORE;
+ }
+ }
+ }
+}
+
+void FileStoreAllFilesForTx(FileContainer *fc, uint16_t tx_id)
+{
+ File *ptr = NULL;
+
+ SCEnter();
+
+ if (fc != NULL) {
+ for (ptr = fc->head; ptr != NULL; ptr = ptr->next) {
+ if (ptr->txid == tx_id) {
+ ptr->flags |= FILE_STORE;
+ }
+ }
+ }
+}
+
+void FileStoreAllFiles(FileContainer *fc)
+{
+ File *ptr = NULL;
+
+ SCEnter();
+
+ if (fc != NULL) {
+ for (ptr = fc->head; ptr != NULL; ptr = ptr->next) {
+ ptr->flags |= FILE_STORE;
+ }
+ }
+}
+
+void FileTruncateAllOpenFiles(FileContainer *fc)
+{
+ File *ptr = NULL;
+
+ SCEnter();
+
+ if (fc != NULL) {
+ for (ptr = fc->head; ptr != NULL; ptr = ptr->next) {
+ if (ptr->state == FILE_STATE_OPENED) {
+ FileCloseFilePtr(ptr, NULL, 0, FILE_TRUNCATED);
+ }
+ }
+ }
+}