37 #define MODULE_NAME "OutputFilestore"
41 #define SHA256_STRING_LEN (SC_SHA256_LEN * 2)
42 #define LEAF_DIR_MAX_LEN 4
43 #define FILESTORE_PREFIX_MAX (PATH_MAX - SHA256_STRING_LEN - LEAF_DIR_MAX_LEN)
47 static const char *default_log_dir =
"filestore";
77 static thread_local
bool once_errs[
WOT_MAX];
79 #define WARN_ONCE(wot_type, ...) \
81 if (!once_errs[wot_type]) { \
82 once_errs[wot_type] = true; \
83 SCLogWarning(__VA_ARGS__); \
87 static uint64_t OutputFilestoreOpenFilesCounter(
void)
92 static uint32_t g_file_store_max_open_files = 0;
94 static void FileSetMaxOpenFiles(uint32_t count)
96 g_file_store_max_open_files = count;
99 static uint32_t FileGetMaxOpenFiles(
void)
101 return g_file_store_max_open_files;
111 static void OutputFilestoreUpdateFileTime(
const char *src_filename,
const char *filename)
114 if (stat(src_filename, &sb) != 0) {
115 SCLogDebug(
"Failed to stat %s: %s", filename, strerror(errno));
118 struct utimbuf utimbuf = {
119 .actime = sb.st_atime,
120 .modtime = sb.st_mtime,
122 if (utime(filename, &utimbuf) != 0) {
123 SCLogDebug(
"Failed to update file timestamps: %s: %s", filename, strerror(errno));
137 char tmp_filename[PATH_MAX] =
"";
138 char tmp_filepath[PATH_MAX] =
"";
139 snprintf(tmp_filename,
sizeof(tmp_filename),
"file.%u", ff->
file_store_id);
140 if (
PathMerge(tmp_filepath,
sizeof(tmp_filepath),
ctx->tmpdir, tmp_filename) < 0)
144 char final_sha_short[3];
145 snprintf(final_sha_short,
sizeof(final_sha_short),
"%c%c", sha256string[0], sha256string[1]);
146 char final_sha_portion[PATH_MAX];
147 if (
PathMerge(final_sha_portion,
sizeof(final_sha_portion), final_sha_short, sha256string) < 0)
149 char final_filename[PATH_MAX] =
"";
150 if (
PathMerge(final_filename,
sizeof(final_filename),
ctx->prefix, final_sha_portion) < 0)
154 OutputFilestoreUpdateFileTime(tmp_filepath, final_filename);
155 if (unlink(tmp_filepath) != 0) {
160 }
else if (rename(tmp_filepath, final_filename) != 0) {
164 if (unlink(tmp_filepath) != 0) {
173 char js_metadata_filename_suffix[PATH_MAX];
174 if (snprintf(js_metadata_filename_suffix,
sizeof(js_metadata_filename_suffix),
176 ff->
file_store_id) == (
int)
sizeof(js_metadata_filename_suffix)) {
180 char js_metadata_filename[PATH_MAX];
181 strlcpy(js_metadata_filename, final_filename,
sizeof(js_metadata_filename));
182 strlcat(js_metadata_filename, js_metadata_filename_suffix,
sizeof(js_metadata_filename));
186 if (
likely(js_fileinfo != NULL)) {
187 SCJbClose(js_fileinfo);
188 FILE *out = fopen(js_metadata_filename,
"w");
190 size_t js_len = SCJbLen(js_fileinfo);
191 fwrite(SCJbPtr(js_fileinfo), js_len, 1, out);
194 SCJbFree(js_fileinfo);
203 void *tx,
const uint64_t tx_id,
const uint8_t *data, uint32_t data_len, uint8_t
flags,
209 char filename[PATH_MAX] =
"";
211 bool close_file =
false;
213 SCLogDebug(
"ff %p, data %p, data_len %u", ff, data, data_len);
217 char tmp_filename[PATH_MAX] =
"";
218 snprintf(tmp_filename,
sizeof(tmp_filename),
"file.%u", ff->
file_store_id);
219 if (
PathMerge(filename,
sizeof(filename),
ctx->tmpdir, tmp_filename) < 0)
222 file_fd = open(filename, O_CREAT | O_TRUNC |
O_NOFOLLOW | O_WRONLY, 0644);
225 SCLogWarning(
"Filestore (v2) failed to create %s: %s", filename, strerror(errno));
230 if (FileGetMaxOpenFiles() > 0 &&
231 SC_ATOMIC_ADD(filestore_open_file_cnt, 1) >= FileGetMaxOpenFiles()) {
236 }
else if (FileGetMaxOpenFiles() == 0) {
242 }
else if (data != NULL) {
246 char tmp_filename[PATH_MAX] =
"";
247 snprintf(tmp_filename,
sizeof(tmp_filename),
"file.%u", ff->
file_store_id);
248 if (
PathMerge(filename,
sizeof(filename),
ctx->tmpdir, tmp_filename) < 0)
250 file_fd = open(filename, O_APPEND |
O_NOFOLLOW | O_WRONLY);
263 if (file_fd != -1 && data != NULL && data_len > 0) {
264 ssize_t r = write(file_fd, (
const void *)data, (
size_t)data_len);
265 if (r == -1 || (ssize_t)data_len != r) {
267 char tmp_filename[PATH_MAX] =
"";
268 snprintf(tmp_filename,
sizeof(tmp_filename),
"file.%u", ff->
file_store_id);
269 (void)
PathMerge(filename,
sizeof(filename),
ctx->tmpdir, tmp_filename);
281 if (FileGetMaxOpenFiles()) {
290 OutputFilestoreFinalizeFiles(
tv, aft,
ctx,
p, ff, tx, tx_id, dir);
296 static TmEcode OutputFilestoreLogThreadInit(
ThreadVars *t,
const void *initdata,
void **data)
302 if (initdata == NULL) {
303 SCLogDebug(
"Error getting context for LogFileStore. \"initdata\" argument NULL");
336 static void OutputFilestoreLogDeInitCtx(
OutputCtx *output_ctx)
339 if (
ctx->xff_cfg != NULL) {
346 static int GetLogDirectory(
const SCConfNode *conf,
char *out,
size_t out_size)
349 if (log_base_dir == NULL) {
350 SCLogConfig(
"Filestore (v2) default log directory %s", default_log_dir);
351 log_base_dir = default_log_dir;
354 size_t r =
strlcpy(out, log_base_dir, out_size);
359 if (
PathMerge(out, out_size, default_log_prefix, log_base_dir) < 0)
365 static bool InitFilestoreDirectory(
const char *dir)
367 const uint8_t dir_count = 0xff;
370 SCLogInfo(
"Filestore (v2) creating directory %s", dir);
372 SCLogError(
"Filestore (v2) failed to create directory %s: %s", dir, strerror(errno));
377 for (
int i = 0; i <= dir_count; i++) {
379 snprintf(hex,
sizeof(hex),
"%02x", i);
381 if (
PathMerge(leaf,
sizeof(leaf), dir, hex) < 0) {
382 SCLogError(
"Filestore (v2) failed to create leaf directory: "
387 SCLogInfo(
"Filestore (v2) creating directory %s", leaf);
390 "Filestore (v2) failed to create directory %s: %s", leaf, strerror(errno));
397 char tmpdir[PATH_MAX];
398 if (
PathMerge(tmpdir,
sizeof(tmpdir), dir,
"tmp") < 0) {
399 SCLogError(
"Filestore (v2) failed to create tmp directory: path too long");
403 SCLogInfo(
"Filestore (v2) creating directory %s", tmpdir);
405 SCLogError(
"Filestore (v2) failed to create directory %s: %s", tmpdir, strerror(errno));
423 SCLogWarning(
"File-store v1 has been removed. Please update to file-store v2.");
428 SCLogWarning(
"A file data logger is already enabled. Filestore (v2) "
429 "will not be enabled.");
433 char log_directory[PATH_MAX] =
"";
434 if (GetLogDirectory(conf, log_directory,
sizeof(log_directory)) < 0) {
435 SCLogError(
"File-store output path too long.");
438 if (!InitFilestoreDirectory(log_directory)) {
449 if (
PathMerge(
ctx->tmpdir,
sizeof(
ctx->tmpdir), log_directory,
"tmp") < 0) {
450 SCLogError(
"File-store output directory overflow.");
456 if (
ctx->xff_cfg != NULL) {
468 output_ctx->
DeInit = OutputFilestoreLogDeInitCtx;
472 SCLogConfig(
"Filestore (v2) will output fileinfo records.");
473 ctx->fileinfo =
true;
479 SCLogInfo(
"forcing filestore of all files");
485 SCLogConfig(
"Filestore (v2) forcing magic lookup for stored files");
496 if (stream_depth_str != NULL && strcmp(stream_depth_str,
"no")) {
497 uint32_t stream_depth = 0;
500 "file-store.stream-depth "
501 "from conf file - %s. Killing engine",
507 SCLogWarning(
"file-store.stream-depth value %" PRIu32
" has "
508 "no effect since it's less than stream.reassembly.depth "
518 if (file_count_str != NULL) {
519 uint32_t file_count = 0;
522 "file-store.max-open-files "
523 "from conf file - %s. Killing engine",
527 if (file_count != 0) {
528 FileSetMaxOpenFiles(file_count);
529 SCLogConfig(
"Filestore (v2) will keep a max of %d "
530 "simultaneously open files",
536 result.
ctx = output_ctx;
544 OutputFilestoreLogInitCtx, OutputFilestoreLogger, OutputFilestoreLogThreadInit,
545 OutputFilestoreLogThreadDeinit);