windows: open log files with shared delete access

On Windows, fopen() does not open files with FILE_SHARE_DELETE, so a log file
can not be renamed or deleted while Suricata has it open. This prevents
external tools from rotating log files.

Add SCWin32Fopen(), an fopen() replacement for the "a" and "w" modes that
opens the file with CreateFile() and FILE_SHARE_DELETE, and use it for log
files, including suricata.log.

Ticket: 7757
pull/16512/head
Jason Ish 2 weeks ago committed by Victor Julien
parent f303d50441
commit ca93c76ca1

@ -35,6 +35,10 @@
#include "util-path.h"
#include "util-syslog.h"
#ifdef OS_WIN32
#include "win32-misc.h"
#endif /* OS_WIN32 */
// clang-format off
/* holds the string-enum mapping for the enums held in the table SCLogLevel */
SCEnumCharMap sc_log_level_map[] = {
@ -631,7 +635,7 @@ static int SCLogReopen(SCLogOPIfaceCtx *op_iface_ctx)
if (op_iface_ctx->file_d != NULL) {
fclose(op_iface_ctx->file_d);
}
op_iface_ctx->file_d = fopen(op_iface_ctx->file, "a");
op_iface_ctx->file_d = SCFopenLogFile(op_iface_ctx->file, "a");
if (op_iface_ctx->file_d == NULL) {
return -1;
}
@ -846,7 +850,8 @@ static inline SCLogOPIfaceCtx *SCLogInitFileOPIface(const char *file, uint32_t u
iface_ctx->iface = SC_LOG_OP_IFACE_FILE;
iface_ctx->type = type;
if ( (iface_ctx->file_d = fopen(file, "a")) == NULL) {
iface_ctx->file_d = SCFopenLogFile(file, "a");
if (iface_ctx->file_d == NULL) {
SCLogWarning("error opening file %s: %s", file, strerror(errno));
goto error;
}

@ -518,11 +518,8 @@ static FILE *SCLogOpenFileFp(
return NULL;
}
if (SCConfValIsTrue(append_setting)) {
ret = fopen(filename, "a");
} else {
ret = fopen(filename, "w");
}
const char *fmode = SCConfValIsTrue(append_setting) ? "a" : "w";
ret = SCFopenLogFile(filename, fmode);
if (ret == NULL) {
SCLogError("Error opening file: \"%s\": %s", filename, strerror(errno));

@ -28,6 +28,7 @@
#include "util-path.h"
#ifdef OS_WIN32
#include "win32-misc.h"
#define DIRECTORY_SEPARATOR '\\'
#else
#define DIRECTORY_SEPARATOR '/'
@ -275,6 +276,32 @@ bool SCPathContainsTraversal(const char *path)
#endif
}
/**
* \brief Open a text log file for append or truncate.
*
* On Windows, allows the file to be renamed or deleted while open so that
* external tools can rotate it. Only modes "a" and "w" are supported on
* all platforms; other modes fail with errno set to EINVAL.
*
* \param path The file path to open
* \param mode "a" to append or "w" to truncate
* \retval FILE* on success
* \retval NULL on error with errno set
*/
FILE *SCFopenLogFile(const char *path, const char *mode)
{
if (strcmp(mode, "a") != 0 && strcmp(mode, "w") != 0) {
errno = EINVAL;
return NULL;
}
#ifdef OS_WIN32
return SCWin32Fopen(path, mode);
#else
return fopen(path, mode);
#endif
}
/**
* \brief Update access and modification time of an existing file to 'now'.
* \param path The file path to touch

@ -60,5 +60,6 @@ char *SCRealPath(const char *path, char *resolved_path);
const char *SCBasename(const char *path);
bool SCPathContainsTraversal(const char *path);
int SCTouchFile(const char *path);
FILE *SCFopenLogFile(const char *path, const char *mode);
#endif /* SURICATA_UTIL_PATH_H */

@ -30,6 +30,9 @@
#include "direct.h"
#include "util-ip.h"
#include <fcntl.h>
#include <io.h>
void setenv(const char *name, const char *value, int overwrite)
{
if (overwrite || NULL == getenv(name)) {
@ -120,4 +123,84 @@ int inet_pton(int af, const char *src, void *dst)
}
#endif
/**
* \brief Open a file for writing.
*
* Like fopen() with a mode of "a" or "w", but also opens the file with
* FILE_SHARE_DELETE, which fopen() does not. This allows the file to be
* renamed or deleted while open, for example so log files can be rotated by
* external tools, then reopened by Suricata.
*
* \param path The file path to open
* \param mode "a" to append or "w" to truncate
* \retval FILE* on success
* \retval NULL on error with errno set
*/
FILE *SCWin32Fopen(const char *path, const char *mode)
{
bool append;
if (strcmp(mode, "a") == 0) {
append = true;
} else if (strcmp(mode, "w") == 0) {
append = false;
} else {
errno = EINVAL;
return NULL;
}
HANDLE h =
CreateFileA(path, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL, append ? OPEN_ALWAYS : CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (h == INVALID_HANDLE_VALUE) {
switch (GetLastError()) {
case ERROR_FILE_NOT_FOUND:
case ERROR_PATH_NOT_FOUND:
errno = ENOENT;
break;
case ERROR_ACCESS_DENIED:
case ERROR_SHARING_VIOLATION:
case ERROR_LOCK_VIOLATION:
errno = EACCES;
break;
case ERROR_INVALID_NAME:
case ERROR_BAD_PATHNAME:
case ERROR_DIRECTORY:
errno = EINVAL;
break;
case ERROR_FILENAME_EXCED_RANGE:
errno = ENAMETOOLONG;
break;
case ERROR_TOO_MANY_OPEN_FILES:
errno = EMFILE;
break;
case ERROR_DISK_FULL:
case ERROR_HANDLE_DISK_FULL:
errno = ENOSPC;
break;
default:
errno = EIO;
break;
}
return NULL;
}
/* Text mode, to match fopen() without a 'b' mode. */
int fd = _open_osfhandle((intptr_t)h, _O_TEXT | (append ? _O_APPEND : 0));
if (fd == -1) {
CloseHandle(h);
return NULL;
}
FILE *fp = _fdopen(fd, mode);
if (fp == NULL) {
/* Also closes the underlying handle. */
int err = errno;
_close(fd);
errno = err;
return NULL;
}
return fp;
}
#endif /* OS_WIN32 */

@ -40,4 +40,6 @@ int inet_pton(int af, const char *src, void *dst);
#define geteuid() (0)
FILE *SCWin32Fopen(const char *path, const char *mode);
#endif

Loading…
Cancel
Save