|
|
65ca46 |
From ac30968b7858d4ca3743d2b4d296eca543864fe2 Mon Sep 17 00:00:00 2001
|
|
|
65ca46 |
From: Jiri Vymazal <jvymazal@redhat.com>
|
|
|
65ca46 |
Date: Fri, 22 Nov 2019 14:25:59 +0100
|
|
|
65ca46 |
Subject: [PATCH] Thorougher state-file renaming and cleaning
|
|
|
65ca46 |
|
|
|
65ca46 |
Now checking if file-id changes and reanming - cleaning state file
|
|
|
65ca46 |
accordingly and always checking and cleaning old inode-only style
|
|
|
65ca46 |
state files.
|
|
|
65ca46 |
---
|
|
|
65ca46 |
plugins/imfile/imfile.c | 66 +++++++++++++++++++++++++++--------------
|
|
|
65ca46 |
1 file changed, 43 insertions(+), 23 deletions(-)
|
|
|
65ca46 |
|
|
|
65ca46 |
diff --git a/plugins/imfile/imfile.c b/plugins/imfile/imfile.c
|
|
|
65ca46 |
index d9bf0fbb6d..9db2b47ac9 100644
|
|
|
65ca46 |
--- a/plugins/imfile/imfile.c
|
|
|
65ca46 |
+++ b/plugins/imfile/imfile.c
|
|
|
65ca46 |
@@ -182,6 +182,7 @@ struct act_obj_s {
|
|
|
65ca46 |
time_t timeoutBase; /* what time to calculate the timeout against? */
|
|
|
65ca46 |
/* file dynamic data */
|
|
|
65ca46 |
char file_id[FILE_ID_HASH_SIZE]; /* file id for this entry, once we could obtain it */
|
|
|
65ca46 |
+ char file_id_prev[FILE_ID_HASH_SIZE]; /* previous file id for this entry, set if changed */
|
|
|
65ca46 |
int in_move; /* workaround for inotify move: if set, state file must not be deleted */
|
|
|
65ca46 |
ino_t ino; /* current inode nbr */
|
|
|
65ca46 |
int fd; /* fd to file in order to obtain file_id (needs to be preserved across move) */
|
|
|
65ca46 |
@@ -711,7 +712,7 @@ act_obj_add(fs_edge_t *const edge, const char *const name, const int is_file,
|
|
|
65ca46 |
if (is_file) {
|
|
|
65ca46 |
LogError(errno, RS_RET_ERR, "imfile: error accessing file '%s'", name);
|
|
|
65ca46 |
} else { /* reporting only in debug for dirs as higher lvl paths are likely blocked by selinux */
|
|
|
65ca46 |
- DBGPRINTF("imfile: error accessing file '%s'", name);
|
|
|
65ca46 |
+ DBGPRINTF("imfile: error accessing directory '%s'", name);
|
|
|
65ca46 |
}
|
|
|
65ca46 |
FINALIZE;
|
|
|
65ca46 |
}
|
|
|
65ca46 |
@@ -727,6 +728,7 @@ act_obj_add(fs_edge_t *const edge, const char *const name, const int is_file,
|
|
|
65ca46 |
act->ino = ino;
|
|
|
65ca46 |
act->fd = fd;
|
|
|
65ca46 |
act->file_id[0] = '\0';
|
|
|
65ca46 |
+ act->file_id_prev[0] = '\0';
|
|
|
65ca46 |
act->is_symlink = is_symlink;
|
|
|
65ca46 |
if (source) { /* we are target of symlink */
|
|
|
65ca46 |
CHKmalloc(act->source_name = strdup(source));
|
|
|
65ca46 |
@@ -1256,17 +1258,15 @@ get_file_id_hash(const char *data, size_t lendata,
|
|
|
65ca46 |
static void ATTR_NONNULL(1)
|
|
|
65ca46 |
getFileID(act_obj_t *const act)
|
|
|
65ca46 |
{
|
|
|
65ca46 |
- if(act->file_id[0] != '\0') {
|
|
|
65ca46 |
- return; /* everything already done */
|
|
|
65ca46 |
- }
|
|
|
65ca46 |
+ /* save the old id for cleaning purposes */
|
|
|
65ca46 |
+ strncpy(act->file_id_prev, (const char*)act->file_id, FILE_ID_HASH_SIZE);
|
|
|
65ca46 |
+ act->file_id[0] = '\0';
|
|
|
65ca46 |
assert(act->fd >= 0); /* fd must have been opened at act_obj_t creation! */
|
|
|
65ca46 |
char filedata[FILE_ID_SIZE];
|
|
|
65ca46 |
+ lseek(act->fd, 0, SEEK_SET); /* Seek to beginning of file so we have correct id */
|
|
|
65ca46 |
const int r = read(act->fd, filedata, FILE_ID_SIZE);
|
|
|
65ca46 |
if(r == FILE_ID_SIZE) {
|
|
|
65ca46 |
get_file_id_hash(filedata, sizeof(filedata), act->file_id, sizeof(act->file_id));
|
|
|
65ca46 |
- dbgprintf("file_id '%s' obtained, closing monitoring file handle\n", act->file_id);
|
|
|
65ca46 |
- close(act->fd); /* we will never go here! */
|
|
|
65ca46 |
- act->fd = -1;
|
|
|
65ca46 |
} else {
|
|
|
65ca46 |
DBGPRINTF("getFileID partial or error read, ret %d\n", r);
|
|
|
65ca46 |
}
|
|
|
65ca46 |
@@ -1378,28 +1378,13 @@ openFileWithStateFile(act_obj_t *const act)
|
|
|
65ca46 |
if(fd < 0) {
|
|
|
65ca46 |
if(errno == ENOENT) {
|
|
|
65ca46 |
if(act->file_id[0] != '\0') {
|
|
|
65ca46 |
- const char *pszSFNamHash = strdup((const char*)pszSFNam);
|
|
|
65ca46 |
- CHKmalloc(pszSFNamHash);
|
|
|
65ca46 |
DBGPRINTF("state file %s for %s does not exist - trying to see if "
|
|
|
65ca46 |
"inode-only file exists\n", pszSFNam, act->name);
|
|
|
65ca46 |
getFullStateFileName(statefn, "", pszSFNam, sizeof(pszSFNam));
|
|
|
65ca46 |
fd = open((char*)pszSFNam, O_CLOEXEC | O_NOCTTY | O_RDONLY, 0600);
|
|
|
65ca46 |
if(fd >= 0) {
|
|
|
65ca46 |
- dbgprintf("found inode-only state file, renaming it now that we "
|
|
|
65ca46 |
- "know the file_id, new name: %s\n", pszSFNamHash);
|
|
|
65ca46 |
- /* we now can use identify the file, so let's rename it */
|
|
|
65ca46 |
- if(rename((const char*)pszSFNam, pszSFNamHash) != 0) {
|
|
|
65ca46 |
- LogError(errno, RS_RET_IO_ERROR,
|
|
|
65ca46 |
- "imfile error trying to rename state file for '%s' - "
|
|
|
65ca46 |
- "ignoring this error, usually this means a file no "
|
|
|
65ca46 |
- "longer file is left over, but this may also cause "
|
|
|
65ca46 |
- "some real trouble. Still the best we can do ",
|
|
|
65ca46 |
- act->name);
|
|
|
65ca46 |
- free((void*) pszSFNamHash);
|
|
|
65ca46 |
- ABORT_FINALIZE(RS_RET_IO_ERROR);
|
|
|
65ca46 |
- }
|
|
|
65ca46 |
+ dbgprintf("found inode-only state file, will be renamed at next persist\n");
|
|
|
65ca46 |
}
|
|
|
65ca46 |
- free((void*) pszSFNamHash);
|
|
|
65ca46 |
}
|
|
|
65ca46 |
if(fd < 0) {
|
|
|
65ca46 |
DBGPRINTF("state file %s for %s does not exist - trying to see if "
|
|
|
65ca46 |
@@ -2609,6 +2594,36 @@ atomicWriteStateFile(const char *fn, const char *content)
|
|
|
65ca46 |
RETiRet;
|
|
|
65ca46 |
}
|
|
|
65ca46 |
|
|
|
65ca46 |
+/* This function should be called after any file ID change - that is if
|
|
|
65ca46 |
+ * file grown from hash-only statefile, or was truncated, this will ensure
|
|
|
65ca46 |
+ * we delete the old file so we do not make garbage in our working dir and
|
|
|
65ca46 |
+ * there are no leftover statefiles which can in theory later bind to something
|
|
|
65ca46 |
+ * and cause data loss.
|
|
|
65ca46 |
+ * jvymazal 2019-11-27
|
|
|
65ca46 |
+ */
|
|
|
65ca46 |
+static void
|
|
|
65ca46 |
+removeOldStatefile(const uchar *statefn, const char *hashToDelete)
|
|
|
65ca46 |
+{
|
|
|
65ca46 |
+ int ret;
|
|
|
65ca46 |
+ uchar statefname[MAXFNAME];
|
|
|
65ca46 |
+
|
|
|
65ca46 |
+ getFullStateFileName(statefn, hashToDelete, statefname, sizeof(statefname));
|
|
|
65ca46 |
+ DBGPRINTF("removing old state file: '%s'\n", statefname);
|
|
|
65ca46 |
+ ret = unlink((const char*)statefname);
|
|
|
65ca46 |
+ if(ret != 0) {
|
|
|
65ca46 |
+ if (errno != ENOENT) {
|
|
|
65ca46 |
+ LogError(errno, RS_RET_IO_ERROR,
|
|
|
65ca46 |
+ "imfile error trying to delete old state file: '%s' - ignoring this "
|
|
|
65ca46 |
+ "error, usually this means a file no longer file is left over, but "
|
|
|
65ca46 |
+ "this may also cause some real trouble. Still the best we can do ",
|
|
|
65ca46 |
+ statefname);
|
|
|
65ca46 |
+ } else {
|
|
|
65ca46 |
+ DBGPRINTF("trying to delete no longer valid statefile '%s' which no "
|
|
|
65ca46 |
+ "longer exists (probably already deleted)\n", statefname);
|
|
|
65ca46 |
+ }
|
|
|
65ca46 |
+ }
|
|
|
65ca46 |
+}
|
|
|
65ca46 |
+
|
|
|
65ca46 |
|
|
|
65ca46 |
/* This function persists information for a specific file being monitored.
|
|
|
65ca46 |
* To do so, it simply persists the stream object. We do NOT abort on error
|
|
|
65ca46 |
@@ -2660,6 +2675,11 @@ persistStrmState(act_obj_t *const act)
|
|
|
65ca46 |
CHKiRet(atomicWriteStateFile((const char*)statefname, jstr));
|
|
|
65ca46 |
json_object_put(json);
|
|
|
65ca46 |
|
|
|
65ca46 |
+ /* file-id changed remove the old statefile */
|
|
|
65ca46 |
+ if (strncmp((const char *)act->file_id_prev, (const char *)act->file_id, FILE_ID_HASH_SIZE)) {
|
|
|
65ca46 |
+ removeOldStatefile(statefn, act->file_id_prev);
|
|
|
65ca46 |
+ }
|
|
|
65ca46 |
+
|
|
|
65ca46 |
finalize_it:
|
|
|
65ca46 |
if(iRet != RS_RET_OK) {
|
|
|
65ca46 |
LogError(0, iRet, "imfile: could not persist state "
|