|
|
c750b0 |
From 70ea76962bfe9ffed2bd604898e43e0e3b17645d Mon Sep 17 00:00:00 2001
|
|
|
c750b0 |
From: Rainer Gerhards <rgerhards@adiscon.com>
|
|
|
c750b0 |
Date: Thu, 23 Aug 2018 10:15:21 +0200
|
|
|
c750b0 |
Subject: [PATCH] omfile: implement file-id, used in state file
|
|
|
c750b0 |
|
|
|
c750b0 |
This ensures that files with the same inodes are not accidently treated
|
|
|
c750b0 |
as equal, at least within the limits of the file id hash (see doc for
|
|
|
c750b0 |
details).
|
|
|
c750b0 |
|
|
|
c750b0 |
We use the siphash reference implementation to generate our non-cryptographic
|
|
|
c750b0 |
hash.
|
|
|
c750b0 |
|
|
|
c750b0 |
State file handling was invalid. When a file was moved and re-created
|
|
|
c750b0 |
rsyslog could use the file_id if the new file to write the old files'
|
|
|
c750b0 |
state file. This could make the file reader stuck until it reached the
|
|
|
c750b0 |
previous offset. Depending on file sizes this could never happen AND
|
|
|
c750b0 |
would cause large message loss. This situation was timing dependent
|
|
|
c750b0 |
(a race) and most frequently occurred under log rotation. In polling
|
|
|
c750b0 |
mode the bug was less likely, but could also occur.
|
|
|
c750b0 |
---
|
|
|
c750b0 |
plugins/imfile/Makefile.am | 2 +-
|
|
|
c750b0 |
plugins/imfile/imfile.c | 190 ++++++++++++++++++-----
|
|
|
c750b0 |
plugins/imfile/siphash.c | 185 ++++++++++++++++++++++++++++++++
|
|
|
c750b0 |
3 files changed, 381 insertions(+), 19 deletions(-)
|
|
|
c750b0 |
create mode 100644 plugins/imfile/siphash.c
|
|
|
c750b0 |
|
|
|
c750b0 |
diff --git a/plugins/imfile/Makefile.am b/plugins/imfile/Makefile.am
|
|
|
c750b0 |
index f4df0ed687..9e137efdc8 100644
|
|
|
c750b0 |
--- a/plugins/imfile/Makefile.am
|
|
|
c750b0 |
+++ b/plugins/imfile/Makefile.am
|
|
|
c750b0 |
@@ -1,6 +1,6 @@
|
|
|
c750b0 |
pkglib_LTLIBRARIES = imfile.la
|
|
|
c750b0 |
|
|
|
c750b0 |
-imfile_la_SOURCES = imfile.c
|
|
|
c750b0 |
+imfile_la_SOURCES = imfile.c siphash.c
|
|
|
c750b0 |
imfile_la_CPPFLAGS = -I$(top_srcdir) $(PTHREADS_CFLAGS) $(RSRT_CFLAGS)
|
|
|
c750b0 |
imfile_la_LDFLAGS = -module -avoid-version
|
|
|
c750b0 |
imfile_la_LIBADD =
|
|
|
c750b0 |
diff --git a/plugins/imfile/imfile.c b/plugins/imfile/imfile.c
|
|
|
c750b0 |
index 4bc6078bda..14f4f1f495 100644
|
|
|
c750b0 |
--- a/plugins/imfile/imfile.c
|
|
|
c750b0 |
+++ b/plugins/imfile/imfile.c
|
|
|
c750b0 |
@@ -66,6 +66,8 @@
|
|
|
c750b0 |
MODULE_CNFNAME("imfile")
|
|
|
c750b0 |
|
|
|
c750b0 |
/* defines */
|
|
|
c750b0 |
+#define FILE_ID_HASH_SIZE 20 /* max size of a file_id hash */
|
|
|
c750b0 |
+#define FILE_ID_SIZE 512 /* how many bytes are used for file-id? */
|
|
|
c750b0 |
|
|
|
c750b0 |
/* Module static data */
|
|
|
c750b0 |
DEF_IMOD_STATIC_DATA /* must be present, starts static data */
|
|
|
c750b0 |
@@ -75,6 +77,9 @@
|
|
|
c750b0 |
DEFobjCurrIf(prop)
|
|
|
c750b0 |
DEFobjCurrIf(ruleset)
|
|
|
c750b0 |
|
|
|
c750b0 |
+extern int rs_siphash(const uint8_t *in, const size_t inlen, const uint8_t *k,
|
|
|
c750b0 |
+ uint8_t *out, const size_t outlen); /* see siphash.c */
|
|
|
c750b0 |
+
|
|
|
c750b0 |
static int bLegacyCnfModGlobalsPermitted;/* are legacy module-global config parameters permitted? */
|
|
|
c750b0 |
|
|
|
c750b0 |
#define NUM_MULTISUB 1024 /* default max number of submits */
|
|
|
c750b0 |
@@ -155,8 +160,10 @@
|
|
|
c750b0 |
int wd;
|
|
|
c750b0 |
time_t timeoutBase; /* what time to calculate the timeout against? */
|
|
|
c750b0 |
/* file dynamic data */
|
|
|
c750b0 |
+ char file_id[FILE_ID_HASH_SIZE]; /* file id for this entry, once we could obtain it */
|
|
|
c750b0 |
int in_move; /* workaround for inotify move: if set, state file must not be deleted */
|
|
|
c750b0 |
ino_t ino; /* current inode nbr */
|
|
|
c750b0 |
+ int fd; /* fd to file in order to obtain file_id (needs to be preserved across move) */
|
|
|
c750b0 |
strm_t *pStrm; /* its stream (NULL if not assigned) */
|
|
|
c750b0 |
int nRecords; /**< How many records did we process before persisting the stream? */
|
|
|
c750b0 |
ratelimit_t *ratelimiter;
|
|
|
c750b0 |
@@ -187,7 +194,7 @@
|
|
|
c750b0 |
static int getBasename(uchar *const __restrict__ basen, uchar *const __restrict__ path);
|
|
|
c750b0 |
static void act_obj_unlink(act_obj_t *act);
|
|
|
c750b0 |
static uchar * getStateFileName(const act_obj_t *, uchar *, const size_t);
|
|
|
c750b0 |
-static int getFullStateFileName(const uchar *const, uchar *const pszout, const size_t ilenout);
|
|
|
c750b0 |
+static int getFullStateFileName(const uchar *const, const char *const, uchar *const pszout, const size_t ilenout);
|
|
|
c750b0 |
|
|
|
c750b0 |
|
|
|
c750b0 |
#define OPMODE_POLLING 0
|
|
|
c750b0 |
@@ -328,7 +335,7 @@
|
|
|
c750b0 |
act->name, statefn);
|
|
|
c750b0 |
|
|
|
c750b0 |
/* Get full path and file name */
|
|
|
c750b0 |
- lenSFNam = getFullStateFileName(statefn, pszSFNam, sizeof(pszSFNam));
|
|
|
c750b0 |
+ lenSFNam = getFullStateFileName(statefn, "", pszSFNam, sizeof(pszSFNam));
|
|
|
c750b0 |
|
|
|
c750b0 |
/* check if the file exists */
|
|
|
c750b0 |
if(stat((char*) pszSFNam, &stat_buf) == -1) {
|
|
|
c750b0 |
@@ -561,16 +568,25 @@
|
|
|
c750b0 |
}
|
|
|
c750b0 |
}
|
|
|
c750b0 |
}
|
|
|
c750b0 |
+ DBGPRINTF("need to add new active object '%s' in '%s' - checking if accessible\n", name, edge->path);
|
|
|
c750b0 |
+ const int fd = open(name, O_RDONLY | O_CLOEXEC);
|
|
|
c750b0 |
+ if(fd < 0) {
|
|
|
c750b0 |
+ if (is_file) { LogMsg(errno, RS_RET_ERR, LOG_WARNING, "imfile: error accessing file '%s'", name);
|
|
|
c750b0 |
+ } else { DBGPRINTF("imfile: error accessing file '%s'", name); }
|
|
|
c750b0 |
+ FINALIZE;
|
|
|
c750b0 |
+ }
|
|
|
c750b0 |
DBGPRINTF("add new active object '%s' in '%s'\n", name, edge->path);
|
|
|
c750b0 |
CHKmalloc(act = calloc(sizeof(act_obj_t), 1));
|
|
|
c750b0 |
CHKmalloc(act->name = strdup(name));
|
|
|
c750b0 |
- if (-1 == getBasename((uchar*)basename, (uchar*)name)) {
|
|
|
c750b0 |
- CHKmalloc(act->basename = strdup(name)); /* assume basename is same as name */
|
|
|
c750b0 |
- } else {
|
|
|
c750b0 |
- CHKmalloc(act->basename = strdup(basename));
|
|
|
c750b0 |
- }
|
|
|
c750b0 |
+ if (-1 == getBasename((uchar*)basename, (uchar*)name)) {
|
|
|
c750b0 |
+ CHKmalloc(act->basename = strdup(name)); /* assume basename is same as name */
|
|
|
c750b0 |
+ } else {
|
|
|
c750b0 |
+ CHKmalloc(act->basename = strdup(basename));
|
|
|
c750b0 |
+ }
|
|
|
c750b0 |
act->edge = edge;
|
|
|
c750b0 |
act->ino = ino;
|
|
|
c750b0 |
+ act->fd = fd;
|
|
|
c750b0 |
+ act->file_id[0] = '\0';
|
|
|
c750b0 |
act->is_symlink = is_symlink;
|
|
|
c750b0 |
if (source) { /* we are target of symlink */
|
|
|
c750b0 |
CHKmalloc(act->source_name = strdup(source));
|
|
|
c750b0 |
@@ -813,7 +828,7 @@
|
|
|
c750b0 |
pollFile(act); /* get any left-over data */
|
|
|
c750b0 |
if(inst->bRMStateOnDel) {
|
|
|
c750b0 |
statefn = getStateFileName(act, statefile, sizeof(statefile));
|
|
|
c750b0 |
- getFullStateFileName(statefn, toDel, sizeof(toDel));
|
|
|
c750b0 |
+ getFullStateFileName(statefn, "", toDel, sizeof(toDel)); // TODO: check!
|
|
|
c750b0 |
statefn = toDel;
|
|
|
c750b0 |
}
|
|
|
c750b0 |
persistStrmState(act);
|
|
|
c750b0 |
@@ -832,6 +847,9 @@
|
|
|
c750b0 |
wdmapDel(act->wd);
|
|
|
c750b0 |
}
|
|
|
c750b0 |
#endif
|
|
|
c750b0 |
+ if(act->fd >= 0) {
|
|
|
c750b0 |
+ close(act->fd);
|
|
|
c750b0 |
+ }
|
|
|
c750b0 |
#if defined(OS_SOLARIS) && defined (HAVE_PORT_SOURCE_FILE)
|
|
|
c750b0 |
if(act->pfinf != NULL) {
|
|
|
c750b0 |
free(act->pfinf->fobj.fo_name);
|
|
|
c750b0 |
@@ -1029,7 +1047,7 @@
|
|
|
c750b0 |
* open or otherwise modify disk file state.
|
|
|
c750b0 |
*/
|
|
|
c750b0 |
static int
|
|
|
c750b0 |
-getFullStateFileName(const uchar *const pszstatefile, uchar *const pszout, const size_t ilenout)
|
|
|
c750b0 |
+getFullStateFileName(const uchar *const pszstatefile, const char *const file_id, uchar *const pszout, const size_t ilenout)
|
|
|
c750b0 |
{
|
|
|
c750b0 |
int lenout;
|
|
|
c750b0 |
const uchar* pszworkdir;
|
|
|
ff08e8 |
@@ -1038,14 +1056,69 @@
|
|
|
c750b0 |
pszworkdir = glblGetWorkDirRaw();
|
|
|
c750b0 |
|
|
|
c750b0 |
/* Construct file name */
|
|
|
c750b0 |
- lenout = snprintf((char*)pszout, ilenout, "%s/%s",
|
|
|
c750b0 |
- (char*) (pszworkdir == NULL ? "." : (char*) pszworkdir), (char*)pszstatefile);
|
|
|
c750b0 |
+ lenout = snprintf((char*)pszout, ilenout, "%s/%s%s%s",
|
|
|
c750b0 |
+ (char*) (pszworkdir == NULL ? "." : (char*) pszworkdir), (char*)pszstatefile,
|
|
|
c750b0 |
+ (*file_id == '\0') ? "" : ":", file_id);
|
|
|
c750b0 |
|
|
|
c750b0 |
/* return out length */
|
|
|
c750b0 |
return lenout;
|
|
|
c750b0 |
}
|
|
|
c750b0 |
|
|
|
c750b0 |
|
|
|
c750b0 |
+/* hash function for file-id
|
|
|
c750b0 |
+ * Takes a block of data and returns a string with the hash value.
|
|
|
c750b0 |
+ *
|
|
|
c750b0 |
+ * Currently one provided by Aaaron Wiebe based on perl's hashing algorithm
|
|
|
c750b0 |
+ * (so probably pretty generic). Not for excessively large strings!
|
|
|
c750b0 |
+ * TODO: re-think the hash function!
|
|
|
c750b0 |
+ */
|
|
|
c750b0 |
+#if defined(__clang__)
|
|
|
c750b0 |
+#pragma GCC diagnostic ignored "-Wunknown-attributes"
|
|
|
c750b0 |
+#endif
|
|
|
c750b0 |
+static void __attribute__((nonnull(1,3)))
|
|
|
c750b0 |
+#if defined(__clang__)
|
|
|
c750b0 |
+__attribute__((no_sanitize("unsigned-integer-overflow")))
|
|
|
c750b0 |
+#endif
|
|
|
c750b0 |
+get_file_id_hash(const char *data, size_t lendata,
|
|
|
c750b0 |
+ char *const hash_str, const size_t len_hash_str)
|
|
|
c750b0 |
+{
|
|
|
c750b0 |
+ assert(len_hash_str >= 17); /* we always generate 8-byte strings */
|
|
|
c750b0 |
+
|
|
|
c750b0 |
+ size_t i;
|
|
|
c750b0 |
+ uint8_t out[8], k[16];
|
|
|
c750b0 |
+ for (i = 0; i < 16; ++i)
|
|
|
c750b0 |
+ k[i] = i;
|
|
|
c750b0 |
+ memset(out, 0, sizeof(out));
|
|
|
c750b0 |
+ rs_siphash((const uint8_t *)data, lendata, k, out, 8);
|
|
|
c750b0 |
+
|
|
|
c750b0 |
+ for(i = 0 ; i < 8 ; ++i) {
|
|
|
c750b0 |
+ if(2 * i+1 >= len_hash_str)
|
|
|
c750b0 |
+ break;
|
|
|
c750b0 |
+ snprintf(hash_str+(2*i), 3, "%2.2x", out[i]);
|
|
|
c750b0 |
+ }
|
|
|
c750b0 |
+}
|
|
|
c750b0 |
+
|
|
|
c750b0 |
+
|
|
|
c750b0 |
+
|
|
|
c750b0 |
+/* this returns the file-id for a given file
|
|
|
c750b0 |
+ */
|
|
|
c750b0 |
+static void getFileID(act_obj_t *const act)
|
|
|
c750b0 |
+{
|
|
|
c750b0 |
+ /* save the old id for cleaning purposes */
|
|
|
c750b0 |
+ strncpy(act->file_id_prev, (const char*)act->file_id, FILE_ID_HASH_SIZE);
|
|
|
c750b0 |
+ act->file_id[0] = '\0';
|
|
|
c750b0 |
+ assert(act->fd >= 0); /* fd must have been opened at act_obj_t creation! */
|
|
|
c750b0 |
+ char filedata[FILE_ID_SIZE];
|
|
|
ff08e8 |
+ lseek(act->fd, 0, SEEK_SET); /* Seek to beginning of file so we have correct id */
|
|
|
c750b0 |
+ const int r = read(act->fd, filedata, FILE_ID_SIZE);
|
|
|
c750b0 |
+ if(r == FILE_ID_SIZE) {
|
|
|
c750b0 |
+ get_file_id_hash(filedata, sizeof(filedata), act->file_id, sizeof(act->file_id));
|
|
|
c750b0 |
+ } else {
|
|
|
c750b0 |
+ DBGPRINTF("getFileID partial or error read, ret %d\n", r);
|
|
|
c750b0 |
+ }
|
|
|
c750b0 |
+ DBGPRINTF("getFileID for '%s', file_id_hash '%s'\n", act->name, act->file_id);
|
|
|
c750b0 |
+}
|
|
|
c750b0 |
+
|
|
|
c750b0 |
/* this generates a state file name suitable for the given file. To avoid
|
|
|
c750b0 |
* malloc calls, it must be passed a buffer which should be MAXFNAME large.
|
|
|
c750b0 |
* Note: the buffer is not necessarily populated ... always ONLY use the
|
|
|
c750b0 |
@@ -1060,7 +1135,7 @@
|
|
|
c750b0 |
{
|
|
|
c750b0 |
DBGPRINTF("getStateFileName for '%s'\n", act->name);
|
|
|
c750b0 |
snprintf((char*)buf, lenbuf - 1, "imfile-state:%lld", (long long) act->ino);
|
|
|
c750b0 |
- DBGPRINTF("getStateFileName: stat file name now is %s\n", buf);
|
|
|
c750b0 |
+ DBGPRINTF("getStateFileName: state file name now is %s\n", buf);
|
|
|
c750b0 |
return buf;
|
|
|
c750b0 |
}
|
|
|
c750b0 |
|
|
|
c750b0 |
@@ -1136,18 +1209,45 @@
|
|
|
c750b0 |
const instanceConf_t *const inst = act->edge->instarr[0];// TODO: same file, multiple instances?
|
|
|
c750b0 |
|
|
|
c750b0 |
uchar *const statefn = getStateFileName(act, statefile, sizeof(statefile));
|
|
|
c750b0 |
+ getFileID(act);
|
|
|
c750b0 |
|
|
|
c750b0 |
- getFullStateFileName(statefn, pszSFNam, sizeof(pszSFNam));
|
|
|
c750b0 |
+ getFullStateFileName(statefn, act->file_id, pszSFNam, sizeof(pszSFNam));
|
|
|
c750b0 |
DBGPRINTF("trying to open state for '%s', state file '%s'\n", act->name, pszSFNam);
|
|
|
c750b0 |
|
|
|
c750b0 |
/* check if the file exists */
|
|
|
c750b0 |
fd = open((char*)pszSFNam, O_CLOEXEC | O_NOCTTY | O_RDONLY, 0600);
|
|
|
c750b0 |
if(fd < 0) {
|
|
|
c750b0 |
if(errno == ENOENT) {
|
|
|
c750b0 |
- DBGPRINTF("NO state file (%s) exists for '%s' - trying to see if "
|
|
|
c750b0 |
- "old-style file exists\n", pszSFNam, act->name);
|
|
|
c750b0 |
- CHKiRet(OLD_openFileWithStateFile(act));
|
|
|
c750b0 |
- FINALIZE;
|
|
|
c750b0 |
+ if(act->file_id[0] != '\0') {
|
|
|
c750b0 |
+ const char *pszSFNamHash = strdup((const char*)pszSFNam);
|
|
|
c750b0 |
+ CHKmalloc(pszSFNamHash);
|
|
|
c750b0 |
+ DBGPRINTF("state file %s for %s does not exist - trying to see if "
|
|
|
c750b0 |
+ "inode-only file exists\n", pszSFNam, act->name);
|
|
|
c750b0 |
+ getFullStateFileName(statefn, "", pszSFNam, sizeof(pszSFNam));
|
|
|
c750b0 |
+ fd = open((char*)pszSFNam, O_CLOEXEC | O_NOCTTY | O_RDONLY, 0600);
|
|
|
c750b0 |
+ if(fd >= 0) {
|
|
|
c750b0 |
+ dbgprintf("found inode-only state file, renaming it now that we "
|
|
|
c750b0 |
+ "know the file_id, new name: %s\n", pszSFNamHash);
|
|
|
c750b0 |
+ /* we now can use identify the file, so let's rename it */
|
|
|
c750b0 |
+ if(rename((const char*)pszSFNam, pszSFNamHash) != 0) {
|
|
|
c750b0 |
+ LogError(errno, RS_RET_IO_ERROR,
|
|
|
c750b0 |
+ "imfile error trying to rename state file for '%s' - "
|
|
|
c750b0 |
+ "ignoring this error, usually this means a file no "
|
|
|
c750b0 |
+ "longer file is left over, but this may also cause "
|
|
|
c750b0 |
+ "some real trouble. Still the best we can do ",
|
|
|
c750b0 |
+ act->name);
|
|
|
c750b0 |
+ free((void*) pszSFNamHash);
|
|
|
c750b0 |
+ ABORT_FINALIZE(RS_RET_IO_ERROR);
|
|
|
c750b0 |
+ }
|
|
|
c750b0 |
+ }
|
|
|
c750b0 |
+ free((void*) pszSFNamHash);
|
|
|
c750b0 |
+ }
|
|
|
c750b0 |
+ if(fd < 0) {
|
|
|
c750b0 |
+ DBGPRINTF("state file %s for %s does not exist - trying to see if "
|
|
|
c750b0 |
+ "old-style file exists\n", pszSFNam, act->name);
|
|
|
c750b0 |
+ CHKiRet(OLD_openFileWithStateFile(act));
|
|
|
c750b0 |
+ FINALIZE;
|
|
|
c750b0 |
+ }
|
|
|
c750b0 |
} else {
|
|
|
c750b0 |
LogError(errno, RS_RET_IO_ERROR,
|
|
|
c750b0 |
"imfile error trying to access state file for '%s'",
|
|
|
c750b0 |
@@ -1156,6 +1256,7 @@
|
|
|
c750b0 |
}
|
|
|
c750b0 |
}
|
|
|
c750b0 |
|
|
|
c750b0 |
+ DBGPRINTF("opened state file %s for %s\n", pszSFNam, act->name);
|
|
|
c750b0 |
CHKiRet(strm.Construct(&act->pStrm));
|
|
|
c750b0 |
|
|
|
c750b0 |
struct json_object *jval;
|
|
|
c750b0 |
@@ -1289,6 +1390,7 @@
|
|
|
c750b0 |
{
|
|
|
c750b0 |
int64 strtOffs;
|
|
|
c750b0 |
DEFiRet;
|
|
|
c750b0 |
+ int64_t startOffs = 0;
|
|
|
c750b0 |
int nProcessed = 0;
|
|
|
c750b0 |
|
|
|
c750b0 |
DBGPRINTF("pollFileReal enter, pStrm %p, name '%s'\n", act->pStrm, act->name);
|
|
|
c750b0 |
@@ -1301,6 +1403,7 @@
|
|
|
c750b0 |
CHKiRet(openFile(act)); /* open file */
|
|
|
c750b0 |
}
|
|
|
c750b0 |
|
|
|
c750b0 |
+ startOffs = act->pStrm->iCurrOffs;
|
|
|
c750b0 |
/* loop below will be exited when strmReadLine() returns EOF */
|
|
|
c750b0 |
while(glbl.GetGlobalInputTermState() == 0) {
|
|
|
c750b0 |
if(inst->maxLinesAtOnce != 0 && nProcessed >= inst->maxLinesAtOnce)
|
|
|
c750b0 |
@@ -1313,6 +1416,11 @@
|
|
|
c750b0 |
inst->escapeLF, &strtOffs));
|
|
|
c750b0 |
}
|
|
|
c750b0 |
++nProcessed;
|
|
|
c750b0 |
+ if(startOffs < FILE_ID_SIZE && act->pStrm->iCurrOffs >= FILE_ID_SIZE) {
|
|
|
c750b0 |
+ dbgprintf("initiating state file write as sufficient data is now present; file=%s\n", act->name);
|
|
|
c750b0 |
+ persistStrmState(act);
|
|
|
c750b0 |
+ startOffs = act->pStrm->iCurrOffs; /* disable check */
|
|
|
c750b0 |
+ }
|
|
|
c750b0 |
runModConf->bHadFileData = 1; /* this is just a flag, so set it and forget it */
|
|
|
c750b0 |
CHKiRet(enqLine(act, *pCStr, strtOffs)); /* process line */
|
|
|
c750b0 |
rsCStrDestruct(pCStr); /* discard string (must be done by us!) */
|
|
|
c750b0 |
@@ -2122,7 +2230,8 @@
|
|
|
c750b0 |
uchar statefname[MAXFNAME];
|
|
|
c750b0 |
|
|
|
c750b0 |
uchar *const statefn = getStateFileName(act, statefile, sizeof(statefile));
|
|
|
c750b0 |
- getFullStateFileName(statefn, statefname, sizeof(statefname));
|
|
|
c750b0 |
+ getFileID(act);
|
|
|
c750b0 |
+ getFullStateFileName(statefn, act->file_id, statefname, sizeof(statefname));
|
|
|
c750b0 |
DBGPRINTF("persisting state for '%s', state file '%s'\n", act->name, statefname);
|
|
|
c750b0 |
|
|
|
c750b0 |
struct json_object *jval = NULL;
|
|
|
c750b0 |
diff --git a/plugins/imfile/siphash.c b/plugins/imfile/siphash.c
|
|
|
c750b0 |
new file mode 100644
|
|
|
c750b0 |
index 0000000000..8d5fac7343
|
|
|
c750b0 |
--- /dev/null
|
|
|
c750b0 |
+++ b/plugins/imfile/siphash.c
|
|
|
c750b0 |
@@ -0,0 +1,185 @@
|
|
|
c750b0 |
+/* SipHash reference C implementation
|
|
|
c750b0 |
+ *
|
|
|
c750b0 |
+ * Copyright (c) 2012-2016 Jean-Philippe Aumasson
|
|
|
c750b0 |
+ * <jeanphilippe.aumasson@gmail.com>
|
|
|
c750b0 |
+ * Copyright (c) 2012-2014 Daniel J. Bernstein <djb@cr.yp.to>
|
|
|
c750b0 |
+ *
|
|
|
c750b0 |
+ * Slightly adapted by rsyslog in regard to build system and code style
|
|
|
c750b0 |
+ * check.
|
|
|
c750b0 |
+ *
|
|
|
c750b0 |
+ * To the extent possible under law, the author(s) have dedicated all copyright
|
|
|
c750b0 |
+ * and related and neighboring rights to this software to the public domain
|
|
|
c750b0 |
+ * worldwide. This software is distributed without any warranty.
|
|
|
c750b0 |
+ *
|
|
|
c750b0 |
+ * You should have received a copy of the CC0 Public Domain Dedication along
|
|
|
c750b0 |
+ * with
|
|
|
c750b0 |
+ * this software. If not, see
|
|
|
c750b0 |
+ * <http://creativecommons.org/publicdomain/zero/1.0/>.
|
|
|
c750b0 |
+ *
|
|
|
c750b0 |
+ * For details on siphash see https://131002.net/siphash/
|
|
|
c750b0 |
+ */
|
|
|
c750b0 |
+#include <assert.h>
|
|
|
c750b0 |
+#include <stdint.h>
|
|
|
c750b0 |
+#include <stdio.h>
|
|
|
c750b0 |
+#include <string.h>
|
|
|
c750b0 |
+
|
|
|
c750b0 |
+/* default: SipHash-2-4 */
|
|
|
c750b0 |
+#define cROUNDS 2
|
|
|
c750b0 |
+#define dROUNDS 4
|
|
|
c750b0 |
+
|
|
|
c750b0 |
+#define ROTL(x, b) (uint64_t)(((x) << (b)) | ((x) >> (64 - (b))))
|
|
|
c750b0 |
+
|
|
|
c750b0 |
+#define U32TO8_LE(p, v) \
|
|
|
c750b0 |
+ (p)[0] = (uint8_t)((v)); \
|
|
|
c750b0 |
+ (p)[1] = (uint8_t)((v) >> 8); \
|
|
|
c750b0 |
+ (p)[2] = (uint8_t)((v) >> 16); \
|
|
|
c750b0 |
+ (p)[3] = (uint8_t)((v) >> 24);
|
|
|
c750b0 |
+
|
|
|
c750b0 |
+#define U64TO8_LE(p, v) \
|
|
|
c750b0 |
+ U32TO8_LE((p), (uint32_t)((v))); \
|
|
|
c750b0 |
+ U32TO8_LE((p) + 4, (uint32_t)((v) >> 32));
|
|
|
c750b0 |
+
|
|
|
c750b0 |
+#define U8TO64_LE(p) \
|
|
|
c750b0 |
+ (((uint64_t)((p)[0])) | ((uint64_t)((p)[1]) << 8) | \
|
|
|
c750b0 |
+ ((uint64_t)((p)[2]) << 16) | ((uint64_t)((p)[3]) << 24) | \
|
|
|
c750b0 |
+ ((uint64_t)((p)[4]) << 32) | ((uint64_t)((p)[5]) << 40) | \
|
|
|
c750b0 |
+ ((uint64_t)((p)[6]) << 48) | ((uint64_t)((p)[7]) << 56))
|
|
|
c750b0 |
+
|
|
|
c750b0 |
+#define SIPROUND \
|
|
|
c750b0 |
+ do { \
|
|
|
c750b0 |
+ v0 += v1; \
|
|
|
c750b0 |
+ v1 = ROTL(v1, 13); \
|
|
|
c750b0 |
+ v1 ^= v0; \
|
|
|
c750b0 |
+ v0 = ROTL(v0, 32); \
|
|
|
c750b0 |
+ v2 += v3; \
|
|
|
c750b0 |
+ v3 = ROTL(v3, 16); \
|
|
|
c750b0 |
+ v3 ^= v2; \
|
|
|
c750b0 |
+ v0 += v3; \
|
|
|
c750b0 |
+ v3 = ROTL(v3, 21); \
|
|
|
c750b0 |
+ v3 ^= v0; \
|
|
|
c750b0 |
+ v2 += v1; \
|
|
|
c750b0 |
+ v1 = ROTL(v1, 17); \
|
|
|
c750b0 |
+ v1 ^= v2; \
|
|
|
c750b0 |
+ v2 = ROTL(v2, 32); \
|
|
|
c750b0 |
+ } while (0)
|
|
|
c750b0 |
+
|
|
|
c750b0 |
+#ifdef DEBUG
|
|
|
c750b0 |
+#define TRACE \
|
|
|
c750b0 |
+ do { \
|
|
|
c750b0 |
+ printf("(%3d) v0 %08x %08x\n", (int)inlen, (uint32_t)(v0 >> 32), \
|
|
|
c750b0 |
+ (uint32_t)v0); \
|
|
|
c750b0 |
+ printf("(%3d) v1 %08x %08x\n", (int)inlen, (uint32_t)(v1 >> 32), \
|
|
|
c750b0 |
+ (uint32_t)v1); \
|
|
|
c750b0 |
+ printf("(%3d) v2 %08x %08x\n", (int)inlen, (uint32_t)(v2 >> 32), \
|
|
|
c750b0 |
+ (uint32_t)v2); \
|
|
|
c750b0 |
+ printf("(%3d) v3 %08x %08x\n", (int)inlen, (uint32_t)(v3 >> 32), \
|
|
|
c750b0 |
+ (uint32_t)v3); \
|
|
|
c750b0 |
+ } while (0)
|
|
|
c750b0 |
+#else
|
|
|
c750b0 |
+#define TRACE
|
|
|
c750b0 |
+#endif
|
|
|
c750b0 |
+
|
|
|
c750b0 |
+extern int rs_siphash(const uint8_t *in, const size_t inlen, const uint8_t *k,
|
|
|
c750b0 |
+ uint8_t *out, const size_t outlen); /* avoid compiler warning */
|
|
|
c750b0 |
+#if defined(__clang__)
|
|
|
c750b0 |
+#pragma GCC diagnostic ignored "-Wunknown-attributes"
|
|
|
c750b0 |
+#endif
|
|
|
c750b0 |
+int
|
|
|
c750b0 |
+#if defined(__clang__)
|
|
|
c750b0 |
+__attribute__((no_sanitize("unsigned-integer-overflow")))
|
|
|
c750b0 |
+#endif
|
|
|
c750b0 |
+rs_siphash(const uint8_t *in, const size_t inlen, const uint8_t *k,
|
|
|
c750b0 |
+ uint8_t *out, const size_t outlen) {
|
|
|
c750b0 |
+
|
|
|
c750b0 |
+ uint64_t v0 = 0x736f6d6570736575ULL;
|
|
|
c750b0 |
+ uint64_t v1 = 0x646f72616e646f6dULL;
|
|
|
c750b0 |
+ uint64_t v2 = 0x6c7967656e657261ULL;
|
|
|
c750b0 |
+ uint64_t v3 = 0x7465646279746573ULL;
|
|
|
c750b0 |
+ uint64_t k0 = U8TO64_LE(k);
|
|
|
c750b0 |
+ uint64_t k1 = U8TO64_LE(k + 8);
|
|
|
c750b0 |
+ uint64_t m;
|
|
|
c750b0 |
+ int i;
|
|
|
c750b0 |
+ const uint8_t *end = in + inlen - (inlen % sizeof(uint64_t));
|
|
|
c750b0 |
+ const int left = inlen & 7;
|
|
|
c750b0 |
+ uint64_t b = ((uint64_t)inlen) << 56;
|
|
|
c750b0 |
+ assert((outlen == 8) || (outlen == 16));
|
|
|
c750b0 |
+ v3 ^= k1;
|
|
|
c750b0 |
+ v2 ^= k0;
|
|
|
c750b0 |
+ v1 ^= k1;
|
|
|
c750b0 |
+ v0 ^= k0;
|
|
|
c750b0 |
+
|
|
|
c750b0 |
+ if (outlen == 16)
|
|
|
c750b0 |
+ v1 ^= 0xee;
|
|
|
c750b0 |
+
|
|
|
c750b0 |
+ for (; in != end; in += 8) {
|
|
|
c750b0 |
+ m = U8TO64_LE(in);
|
|
|
c750b0 |
+ v3 ^= m;
|
|
|
c750b0 |
+
|
|
|
c750b0 |
+ TRACE;
|
|
|
c750b0 |
+ for (i = 0; i < cROUNDS; ++i)
|
|
|
c750b0 |
+ SIPROUND;
|
|
|
c750b0 |
+
|
|
|
c750b0 |
+ v0 ^= m;
|
|
|
c750b0 |
+ }
|
|
|
c750b0 |
+
|
|
|
c750b0 |
+ switch (left) {
|
|
|
c750b0 |
+ case 7:
|
|
|
c750b0 |
+ b |= ((uint64_t)in[6]) << 48;
|
|
|
c750b0 |
+ /*FALLTHROUGH*/
|
|
|
c750b0 |
+ case 6:
|
|
|
c750b0 |
+ b |= ((uint64_t)in[5]) << 40;
|
|
|
c750b0 |
+ /*FALLTHROUGH*/
|
|
|
c750b0 |
+ case 5:
|
|
|
c750b0 |
+ b |= ((uint64_t)in[4]) << 32;
|
|
|
c750b0 |
+ /*FALLTHROUGH*/
|
|
|
c750b0 |
+ case 4:
|
|
|
c750b0 |
+ b |= ((uint64_t)in[3]) << 24;
|
|
|
c750b0 |
+ /*FALLTHROUGH*/
|
|
|
c750b0 |
+ case 3:
|
|
|
c750b0 |
+ b |= ((uint64_t)in[2]) << 16;
|
|
|
c750b0 |
+ /*FALLTHROUGH*/
|
|
|
c750b0 |
+ case 2:
|
|
|
c750b0 |
+ b |= ((uint64_t)in[1]) << 8;
|
|
|
c750b0 |
+ /*FALLTHROUGH*/
|
|
|
c750b0 |
+ case 1:
|
|
|
c750b0 |
+ b |= ((uint64_t)in[0]);
|
|
|
c750b0 |
+ break;
|
|
|
c750b0 |
+ case 0:
|
|
|
c750b0 |
+ default:
|
|
|
c750b0 |
+ break;
|
|
|
c750b0 |
+ }
|
|
|
c750b0 |
+
|
|
|
c750b0 |
+ v3 ^= b;
|
|
|
c750b0 |
+
|
|
|
c750b0 |
+ TRACE;
|
|
|
c750b0 |
+ for (i = 0; i < cROUNDS; ++i)
|
|
|
c750b0 |
+ SIPROUND;
|
|
|
c750b0 |
+
|
|
|
c750b0 |
+ v0 ^= b;
|
|
|
c750b0 |
+
|
|
|
c750b0 |
+ if (outlen == 16)
|
|
|
c750b0 |
+ v2 ^= 0xee;
|
|
|
c750b0 |
+ else
|
|
|
c750b0 |
+ v2 ^= 0xff;
|
|
|
c750b0 |
+
|
|
|
c750b0 |
+ TRACE;
|
|
|
c750b0 |
+ for (i = 0; i < dROUNDS; ++i)
|
|
|
c750b0 |
+ SIPROUND;
|
|
|
c750b0 |
+
|
|
|
c750b0 |
+ b = v0 ^ v1 ^ v2 ^ v3;
|
|
|
c750b0 |
+ U64TO8_LE(out, b);
|
|
|
c750b0 |
+
|
|
|
c750b0 |
+ if (outlen == 8)
|
|
|
c750b0 |
+ return 0;
|
|
|
c750b0 |
+
|
|
|
c750b0 |
+ v1 ^= 0xdd;
|
|
|
c750b0 |
+
|
|
|
c750b0 |
+ TRACE;
|
|
|
c750b0 |
+ for (i = 0; i < dROUNDS; ++i)
|
|
|
c750b0 |
+ SIPROUND;
|
|
|
c750b0 |
+
|
|
|
c750b0 |
+ b = v0 ^ v1 ^ v2 ^ v3;
|
|
|
c750b0 |
+ U64TO8_LE(out + 8, b);
|
|
|
c750b0 |
+
|
|
|
c750b0 |
+ return 0;
|
|
|
c750b0 |
+}
|
|
|
c750b0 |
--- a/plugins/imfile/imfile.c
|
|
|
c750b0 |
+++ b/plugins/imfile/imfile.c
|
|
|
c750b0 |
@@ -182,6 +182,7 @@ struct act_obj_s {
|
|
|
c750b0 |
time_t timeoutBase; /* what time to calculate the timeout against? */
|
|
|
c750b0 |
/* file dynamic data */
|
|
|
c750b0 |
char file_id[FILE_ID_HASH_SIZE]; /* file id for this entry, once we could obtain it */
|
|
|
c750b0 |
+ char file_id_prev[FILE_ID_HASH_SIZE]; /* previous file id for this entry, set if changed */
|
|
|
c750b0 |
int in_move; /* workaround for inotify move: if set, state file must not be deleted */
|
|
|
c750b0 |
ino_t ino; /* current inode nbr */
|
|
|
c750b0 |
int fd; /* fd to file in order to obtain file_id (needs to be preserved across move) */
|
|
|
c750b0 |
@@ -727,6 +728,7 @@ act_obj_add(fs_edge_t *const edge, const char *const name, const int is_file,
|
|
|
c750b0 |
act->ino = ino;
|
|
|
c750b0 |
act->fd = fd;
|
|
|
c750b0 |
act->file_id[0] = '\0';
|
|
|
c750b0 |
+ act->file_id_prev[0] = '\0';
|
|
|
c750b0 |
act->is_symlink = is_symlink;
|
|
|
c750b0 |
if (source) { /* we are target of symlink */
|
|
|
c750b0 |
CHKmalloc(act->source_name = strdup(source));
|
|
|
c750b0 |
@@ -1378,28 +1380,13 @@ openFileWithStateFile(act_obj_t *const act)
|
|
|
c750b0 |
if(fd < 0) {
|
|
|
c750b0 |
if(errno == ENOENT) {
|
|
|
c750b0 |
if(act->file_id[0] != '\0') {
|
|
|
c750b0 |
- const char *pszSFNamHash = strdup((const char*)pszSFNam);
|
|
|
c750b0 |
- CHKmalloc(pszSFNamHash);
|
|
|
c750b0 |
DBGPRINTF("state file %s for %s does not exist - trying to see if "
|
|
|
c750b0 |
"inode-only file exists\n", pszSFNam, act->name);
|
|
|
c750b0 |
getFullStateFileName(statefn, "", pszSFNam, sizeof(pszSFNam));
|
|
|
c750b0 |
fd = open((char*)pszSFNam, O_CLOEXEC | O_NOCTTY | O_RDONLY, 0600);
|
|
|
c750b0 |
if(fd >= 0) {
|
|
|
c750b0 |
- dbgprintf("found inode-only state file, renaming it now that we "
|
|
|
c750b0 |
- "know the file_id, new name: %s\n", pszSFNamHash);
|
|
|
c750b0 |
- /* we now can use identify the file, so let's rename it */
|
|
|
c750b0 |
- if(rename((const char*)pszSFNam, pszSFNamHash) != 0) {
|
|
|
c750b0 |
- LogError(errno, RS_RET_IO_ERROR,
|
|
|
c750b0 |
- "imfile error trying to rename state file for '%s' - "
|
|
|
c750b0 |
- "ignoring this error, usually this means a file no "
|
|
|
c750b0 |
- "longer file is left over, but this may also cause "
|
|
|
c750b0 |
- "some real trouble. Still the best we can do ",
|
|
|
c750b0 |
- act->name);
|
|
|
c750b0 |
- free((void*) pszSFNamHash);
|
|
|
c750b0 |
- ABORT_FINALIZE(RS_RET_IO_ERROR);
|
|
|
c750b0 |
- }
|
|
|
c750b0 |
+ dbgprintf("found inode-only state file, will be renamed at next persist\n");
|
|
|
c750b0 |
}
|
|
|
c750b0 |
- free((void*) pszSFNamHash);
|
|
|
c750b0 |
}
|
|
|
c750b0 |
if(fd < 0) {
|
|
|
c750b0 |
DBGPRINTF("state file %s for %s does not exist - trying to see if "
|
|
|
ff08e8 |
@@ -2609,6 +2596,24 @@ atomicWriteStateFile(const char *fn, const char *content)
|
|
|
c750b0 |
RETiRet;
|
|
|
c750b0 |
}
|
|
|
c750b0 |
|
|
|
c750b0 |
+static void
|
|
|
c750b0 |
+removeOldStatefile(const uchar *statefn, const char *hashToDelete)
|
|
|
c750b0 |
+{
|
|
|
c750b0 |
+ int ret;
|
|
|
c750b0 |
+ uchar statefname[MAXFNAME];
|
|
|
c750b0 |
+
|
|
|
c750b0 |
+ getFullStateFileName(statefn, hashToDelete, statefname, sizeof(statefname));
|
|
|
ff08e8 |
+ DBGPRINTF("removing old state file: '%s'\n", statefname);
|
|
|
c750b0 |
+ ret = unlink((const char*)statefname);
|
|
|
ff08e8 |
+ if(ret != 0 && errno != ENOENT) {
|
|
|
ff08e8 |
+ LogError(errno, RS_RET_IO_ERROR,
|
|
|
ff08e8 |
+ "imfile error trying to delete old state file: '%s' - ignoring this "
|
|
|
ff08e8 |
+ "error, usually this means a file no longer file is left over, but "
|
|
|
ff08e8 |
+ "this may also cause some real trouble. Still the best we can do ",
|
|
|
ff08e8 |
+ statefname);
|
|
|
c750b0 |
+ }
|
|
|
c750b0 |
+}
|
|
|
c750b0 |
+
|
|
|
c750b0 |
|
|
|
c750b0 |
/* This function persists information for a specific file being monitored.
|
|
|
c750b0 |
* To do so, it simply persists the stream object. We do NOT abort on error
|
|
|
ff08e8 |
@@ -2660,6 +2664,10 @@ persistStrmState(act_obj_t *const act)
|
|
|
c750b0 |
CHKiRet(atomicWriteStateFile((const char*)statefname, jstr));
|
|
|
c750b0 |
json_object_put(json);
|
|
|
c750b0 |
|
|
|
c750b0 |
+ if (strncmp((const char *)act->file_id_prev, (const char *)act->file_id, FILE_ID_HASH_SIZE)) {
|
|
|
c750b0 |
+ removeOldStatefile(statefn, act->file_id_prev);
|
|
|
c750b0 |
+ }
|
|
|
c750b0 |
+
|
|
|
c750b0 |
finalize_it:
|
|
|
c750b0 |
if(iRet != RS_RET_OK) {
|
|
|
c750b0 |
errmsg.LogError(0, iRet, "imfile: could not persist state "
|