From b26c3f9cbd784a5f7db68049fd9c49a0a2077734 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Thu, 14 Mar 2019 14:45:08 +0000 Subject: tests: refactor file monitor test to make it more understandable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The current file monitor unit tests are too clever for their own good making it hard to understand the desired output. Instead of trying to infer the expected events, explicitly list the events we expect in the operation sequence. Instead of dynamically building a matrix of tests, just have one giant operation sequence that validates all scenarios in a single test. Reviewed-by: Marc-André Lureau Signed-off-by: Daniel P. Berrangé --- tests/test-util-filemonitor.c | 534 ++++++++++++++++-------------------------- 1 file changed, 208 insertions(+), 326 deletions(-) (limited to 'tests') diff --git a/tests/test-util-filemonitor.c b/tests/test-util-filemonitor.c index 5d95cea5ee..ea3715a8f4 100644 --- a/tests/test-util-filemonitor.c +++ b/tests/test-util-filemonitor.c @@ -26,6 +26,9 @@ #include enum { + QFILE_MONITOR_TEST_OP_ADD_WATCH, + QFILE_MONITOR_TEST_OP_DEL_WATCH, + QFILE_MONITOR_TEST_OP_EVENT, QFILE_MONITOR_TEST_OP_CREATE, QFILE_MONITOR_TEST_OP_APPEND, QFILE_MONITOR_TEST_OP_TRUNC, @@ -38,20 +41,10 @@ typedef struct { int type; const char *filesrc; const char *filedst; + int watchid; + int eventid; } QFileMonitorTestOp; -typedef struct { - const char *file; -} QFileMonitorTestWatch; - -typedef struct { - gsize nwatches; - const QFileMonitorTestWatch *watches; - - gsize nops; - const QFileMonitorTestOp *ops; -} QFileMonitorTestPlan; - typedef struct { int id; QFileMonitorEvent event; @@ -67,6 +60,7 @@ typedef struct { static QemuMutex evlock; static bool evstopping; static bool evrunning; +static bool debug; /* * Main function for a background thread that is @@ -200,9 +194,125 @@ qemu_file_monitor_test_expect(QFileMonitorTestData *data, static void -test_file_monitor_events(const void *opaque) +test_file_monitor_events(void) { - const QFileMonitorTestPlan *plan = opaque; + QFileMonitorTestOp ops[] = { + { .type = QFILE_MONITOR_TEST_OP_ADD_WATCH, + .filesrc = NULL, .watchid = 0 }, + { .type = QFILE_MONITOR_TEST_OP_ADD_WATCH, + .filesrc = "one.txt", .watchid = 1 }, + { .type = QFILE_MONITOR_TEST_OP_ADD_WATCH, + .filesrc = "two.txt", .watchid = 2 }, + + + { .type = QFILE_MONITOR_TEST_OP_CREATE, + .filesrc = "one.txt", }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "one.txt", .watchid = 0, + .eventid = QFILE_MONITOR_EVENT_CREATED }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "one.txt", .watchid = 1, + .eventid = QFILE_MONITOR_EVENT_CREATED }, + + + { .type = QFILE_MONITOR_TEST_OP_CREATE, + .filesrc = "two.txt", }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "two.txt", .watchid = 0, + .eventid = QFILE_MONITOR_EVENT_CREATED }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "two.txt", .watchid = 2, + .eventid = QFILE_MONITOR_EVENT_CREATED }, + + + { .type = QFILE_MONITOR_TEST_OP_CREATE, + .filesrc = "three.txt", }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "three.txt", .watchid = 0, + .eventid = QFILE_MONITOR_EVENT_CREATED }, + + + { .type = QFILE_MONITOR_TEST_OP_UNLINK, + .filesrc = "three.txt", }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "three.txt", .watchid = 0, + .eventid = QFILE_MONITOR_EVENT_DELETED }, + + + { .type = QFILE_MONITOR_TEST_OP_RENAME, + .filesrc = "one.txt", .filedst = "two.txt" }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "one.txt", .watchid = 0, + .eventid = QFILE_MONITOR_EVENT_DELETED }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "one.txt", .watchid = 1, + .eventid = QFILE_MONITOR_EVENT_DELETED }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "two.txt", .watchid = 0, + .eventid = QFILE_MONITOR_EVENT_CREATED }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "two.txt", .watchid = 2, + .eventid = QFILE_MONITOR_EVENT_CREATED }, + + + { .type = QFILE_MONITOR_TEST_OP_APPEND, + .filesrc = "two.txt", }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "two.txt", .watchid = 0, + .eventid = QFILE_MONITOR_EVENT_MODIFIED }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "two.txt", .watchid = 2, + .eventid = QFILE_MONITOR_EVENT_MODIFIED }, + + + { .type = QFILE_MONITOR_TEST_OP_TOUCH, + .filesrc = "two.txt", }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "two.txt", .watchid = 0, + .eventid = QFILE_MONITOR_EVENT_ATTRIBUTES }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "two.txt", .watchid = 2, + .eventid = QFILE_MONITOR_EVENT_ATTRIBUTES }, + + + { .type = QFILE_MONITOR_TEST_OP_DEL_WATCH, + .filesrc = "one.txt", .watchid = 1 }, + { .type = QFILE_MONITOR_TEST_OP_ADD_WATCH, + .filesrc = "one.txt", .watchid = 3 }, + { .type = QFILE_MONITOR_TEST_OP_CREATE, + .filesrc = "one.txt", }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "one.txt", .watchid = 0, + .eventid = QFILE_MONITOR_EVENT_CREATED }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "one.txt", .watchid = 3, + .eventid = QFILE_MONITOR_EVENT_CREATED }, + + + { .type = QFILE_MONITOR_TEST_OP_DEL_WATCH, + .filesrc = "one.txt", .watchid = 3 }, + { .type = QFILE_MONITOR_TEST_OP_UNLINK, + .filesrc = "one.txt", }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "one.txt", .watchid = 0, + .eventid = QFILE_MONITOR_EVENT_DELETED }, + + + { .type = QFILE_MONITOR_TEST_OP_UNLINK, + .filesrc = "two.txt", }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "two.txt", .watchid = 0, + .eventid = QFILE_MONITOR_EVENT_DELETED }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "two.txt", .watchid = 2, + .eventid = QFILE_MONITOR_EVENT_DELETED }, + + + { .type = QFILE_MONITOR_TEST_OP_DEL_WATCH, + .filesrc = "two.txt", .watchid = 2 }, + { .type = QFILE_MONITOR_TEST_OP_DEL_WATCH, + .filesrc = NULL, .watchid = 0 }, + }; Error *local_err = NULL; GError *gerr = NULL; QFileMonitor *mon = qemu_file_monitor_new(&local_err); @@ -210,7 +320,7 @@ test_file_monitor_events(const void *opaque) GTimer *timer; gchar *dir = NULL; int err = -1; - gsize i, j; + gsize i; char *pathsrc = NULL; char *pathdst = NULL; QFileMonitorTestData data; @@ -248,33 +358,13 @@ test_file_monitor_events(const void *opaque) } /* - * First register all the directory / file watches - * we're interested in seeing events against + * Run through the operation sequence validating events + * as we go */ - for (i = 0; i < plan->nwatches; i++) { - int watchid; - watchid = qemu_file_monitor_add_watch(mon, - dir, - plan->watches[i].file, - qemu_file_monitor_test_handler, - &data, - &local_err); - if (watchid < 0) { - g_printerr("Unable to add watch %s", - error_get_pretty(local_err)); - goto cleanup; - } - } - - - /* - * Now invoke all the file operations (create, - * delete, rename, chmod, etc). These operations - * will trigger the various file monitor events - */ - for (i = 0; i < plan->nops; i++) { - const QFileMonitorTestOp *op = &(plan->ops[i]); + for (i = 0; i < G_N_ELEMENTS(ops); i++) { + const QFileMonitorTestOp *op = &(ops[i]); int fd; + int watchid; struct utimbuf ubuf; pathsrc = g_strdup_printf("%s/%s", dir, op->filesrc); @@ -283,7 +373,50 @@ test_file_monitor_events(const void *opaque) } switch (op->type) { + case QFILE_MONITOR_TEST_OP_ADD_WATCH: + if (debug) { + g_printerr("Add watch %s %s %d\n", + dir, op->filesrc, op->watchid); + } + watchid = + qemu_file_monitor_add_watch(mon, + dir, + op->filesrc, + qemu_file_monitor_test_handler, + &data, + &local_err); + if (watchid < 0) { + g_printerr("Unable to add watch %s", + error_get_pretty(local_err)); + goto cleanup; + } + if (watchid != op->watchid) { + g_printerr("Unexpected watch ID %d, wanted %d\n", + watchid, op->watchid); + goto cleanup; + } + break; + case QFILE_MONITOR_TEST_OP_DEL_WATCH: + if (debug) { + g_printerr("Del watch %s %d\n", dir, op->watchid); + } + qemu_file_monitor_remove_watch(mon, + dir, + op->watchid); + break; + case QFILE_MONITOR_TEST_OP_EVENT: + if (debug) { + g_printerr("Event id=%d event=%d file=%s\n", + op->watchid, op->eventid, op->filesrc); + } + if (!qemu_file_monitor_test_expect( + &data, op->watchid, op->eventid, op->filesrc)) + goto cleanup; + break; case QFILE_MONITOR_TEST_OP_CREATE: + if (debug) { + g_printerr("Create %s\n", pathsrc); + } fd = open(pathsrc, O_WRONLY | O_CREAT, 0700); if (fd < 0) { g_printerr("Unable to create %s: %s", @@ -294,6 +427,9 @@ test_file_monitor_events(const void *opaque) break; case QFILE_MONITOR_TEST_OP_APPEND: + if (debug) { + g_printerr("Append %s\n", pathsrc); + } fd = open(pathsrc, O_WRONLY | O_APPEND, 0700); if (fd < 0) { g_printerr("Unable to open %s: %s", @@ -311,6 +447,9 @@ test_file_monitor_events(const void *opaque) break; case QFILE_MONITOR_TEST_OP_TRUNC: + if (debug) { + g_printerr("Truncate %s\n", pathsrc); + } if (truncate(pathsrc, 4) < 0) { g_printerr("Unable to truncate %s: %s", pathsrc, strerror(errno)); @@ -319,6 +458,9 @@ test_file_monitor_events(const void *opaque) break; case QFILE_MONITOR_TEST_OP_RENAME: + if (debug) { + g_printerr("Rename %s -> %s\n", pathsrc, pathdst); + } if (rename(pathsrc, pathdst) < 0) { g_printerr("Unable to rename %s to %s: %s", pathsrc, pathdst, strerror(errno)); @@ -327,6 +469,9 @@ test_file_monitor_events(const void *opaque) break; case QFILE_MONITOR_TEST_OP_UNLINK: + if (debug) { + g_printerr("Unlink %s\n", pathsrc); + } if (unlink(pathsrc) < 0) { g_printerr("Unable to unlink %s: %s", pathsrc, strerror(errno)); @@ -335,6 +480,9 @@ test_file_monitor_events(const void *opaque) break; case QFILE_MONITOR_TEST_OP_TOUCH: + if (debug) { + g_printerr("Touch %s\n", pathsrc); + } ubuf.actime = 1024; ubuf.modtime = 1025; if (utime(pathsrc, &ubuf) < 0) { @@ -353,92 +501,6 @@ test_file_monitor_events(const void *opaque) pathsrc = pathdst = NULL; } - - /* - * Finally validate that we have received all the events - * we expect to see for the combination of watches and - * file operations - */ - for (i = 0; i < plan->nops; i++) { - const QFileMonitorTestOp *op = &(plan->ops[i]); - - switch (op->type) { - case QFILE_MONITOR_TEST_OP_CREATE: - for (j = 0; j < plan->nwatches; j++) { - if (plan->watches[j].file && - !g_str_equal(plan->watches[j].file, op->filesrc)) - continue; - - if (!qemu_file_monitor_test_expect( - &data, j, QFILE_MONITOR_EVENT_CREATED, op->filesrc)) - goto cleanup; - } - break; - - case QFILE_MONITOR_TEST_OP_APPEND: - case QFILE_MONITOR_TEST_OP_TRUNC: - for (j = 0; j < plan->nwatches; j++) { - if (plan->watches[j].file && - !g_str_equal(plan->watches[j].file, op->filesrc)) - continue; - - if (!qemu_file_monitor_test_expect( - &data, j, QFILE_MONITOR_EVENT_MODIFIED, op->filesrc)) - goto cleanup; - } - break; - - case QFILE_MONITOR_TEST_OP_RENAME: - for (j = 0; j < plan->nwatches; j++) { - if (plan->watches[j].file && - !g_str_equal(plan->watches[j].file, op->filesrc)) - continue; - - if (!qemu_file_monitor_test_expect( - &data, j, QFILE_MONITOR_EVENT_DELETED, op->filesrc)) - goto cleanup; - } - - for (j = 0; j < plan->nwatches; j++) { - if (plan->watches[j].file && - !g_str_equal(plan->watches[j].file, op->filedst)) - continue; - - if (!qemu_file_monitor_test_expect( - &data, j, QFILE_MONITOR_EVENT_CREATED, op->filedst)) - goto cleanup; - } - break; - - case QFILE_MONITOR_TEST_OP_TOUCH: - for (j = 0; j < plan->nwatches; j++) { - if (plan->watches[j].file && - !g_str_equal(plan->watches[j].file, op->filesrc)) - continue; - - if (!qemu_file_monitor_test_expect( - &data, j, QFILE_MONITOR_EVENT_ATTRIBUTES, op->filesrc)) - goto cleanup; - } - break; - - case QFILE_MONITOR_TEST_OP_UNLINK: - for (j = 0; j < plan->nwatches; j++) { - if (plan->watches[j].file && - !g_str_equal(plan->watches[j].file, op->filesrc)) - continue; - - if (!qemu_file_monitor_test_expect( - &data, j, QFILE_MONITOR_EVENT_DELETED, op->filesrc)) - goto cleanup; - } - break; - - default: - g_assert_not_reached(); - } - } - err = 0; cleanup: @@ -460,171 +522,36 @@ test_file_monitor_events(const void *opaque) } g_timer_destroy(timer); - for (i = 0; i < plan->nops; i++) { - const QFileMonitorTestOp *op = &(plan->ops[i]); - pathsrc = g_strdup_printf("%s/%s", dir, op->filesrc); - unlink(pathsrc); - g_free(pathsrc); - if (op->filedst) { - pathdst = g_strdup_printf("%s/%s", dir, op->filedst); - unlink(pathdst); - g_free(pathdst); - } - } - qemu_file_monitor_free(mon); g_list_foreach(data.records, (GFunc)qemu_file_monitor_test_record_free, NULL); g_list_free(data.records); qemu_mutex_destroy(&data.lock); if (dir) { - rmdir(dir); + for (i = 0; i < G_N_ELEMENTS(ops); i++) { + const QFileMonitorTestOp *op = &(ops[i]); + char *path = g_strdup_printf("%s/%s", + dir, op->filesrc); + unlink(path); + g_free(path); + if (op->filedst) { + path = g_strdup_printf("%s/%s", + dir, op->filedst); + unlink(path); + g_free(path); + } + } + if (rmdir(dir) < 0) { + g_printerr("Failed to remove %s: %s\n", + dir, strerror(errno)); + abort(); + } } g_free(dir); g_assert(err == 0); } -/* - * Set of structs which define which file name patterns - * we're trying to watch against. NULL, means all files - * in the directory - */ -static const QFileMonitorTestWatch watches_any[] = { - { NULL }, -}; - -static const QFileMonitorTestWatch watches_one[] = { - { "one.txt" }, -}; - -static const QFileMonitorTestWatch watches_two[] = { - { "two.txt" }, -}; - -static const QFileMonitorTestWatch watches_many[] = { - { NULL }, - { "one.txt" }, - { "two.txt" }, -}; - - -/* - * Various sets of file operations we're going to - * trigger and validate events for - */ -static const QFileMonitorTestOp ops_create_one[] = { - { .type = QFILE_MONITOR_TEST_OP_CREATE, - .filesrc = "one.txt", } -}; - -static const QFileMonitorTestOp ops_delete_one[] = { - { .type = QFILE_MONITOR_TEST_OP_CREATE, - .filesrc = "one.txt", }, - { .type = QFILE_MONITOR_TEST_OP_UNLINK, - .filesrc = "one.txt", } -}; - -static const QFileMonitorTestOp ops_create_many[] = { - { .type = QFILE_MONITOR_TEST_OP_CREATE, - .filesrc = "one.txt", }, - { .type = QFILE_MONITOR_TEST_OP_CREATE, - .filesrc = "two.txt", }, - { .type = QFILE_MONITOR_TEST_OP_CREATE, - .filesrc = "three.txt", } -}; - -static const QFileMonitorTestOp ops_rename_one[] = { - { .type = QFILE_MONITOR_TEST_OP_CREATE, - .filesrc = "one.txt", }, - { .type = QFILE_MONITOR_TEST_OP_RENAME, - .filesrc = "one.txt", .filedst = "two.txt" } -}; - -static const QFileMonitorTestOp ops_rename_many[] = { - { .type = QFILE_MONITOR_TEST_OP_CREATE, - .filesrc = "one.txt", }, - { .type = QFILE_MONITOR_TEST_OP_CREATE, - .filesrc = "two.txt", }, - { .type = QFILE_MONITOR_TEST_OP_RENAME, - .filesrc = "one.txt", .filedst = "two.txt" } -}; - -static const QFileMonitorTestOp ops_append_one[] = { - { .type = QFILE_MONITOR_TEST_OP_CREATE, - .filesrc = "one.txt", }, - { .type = QFILE_MONITOR_TEST_OP_APPEND, - .filesrc = "one.txt", }, -}; - -static const QFileMonitorTestOp ops_trunc_one[] = { - { .type = QFILE_MONITOR_TEST_OP_CREATE, - .filesrc = "one.txt", }, - { .type = QFILE_MONITOR_TEST_OP_TRUNC, - .filesrc = "one.txt", }, -}; - -static const QFileMonitorTestOp ops_touch_one[] = { - { .type = QFILE_MONITOR_TEST_OP_CREATE, - .filesrc = "one.txt", }, - { .type = QFILE_MONITOR_TEST_OP_TOUCH, - .filesrc = "one.txt", }, -}; - - -/* - * No we define data sets for the combinatorial - * expansion of file watches and operation sets - */ -#define PLAN_DATA(o, w) \ - static const QFileMonitorTestPlan plan_ ## o ## _ ## w = { \ - .nops = G_N_ELEMENTS(ops_ ##o), \ - .ops = ops_ ##o, \ - .nwatches = G_N_ELEMENTS(watches_ ##w), \ - .watches = watches_ ## w, \ - } - -PLAN_DATA(create_one, any); -PLAN_DATA(create_one, one); -PLAN_DATA(create_one, two); -PLAN_DATA(create_one, many); - -PLAN_DATA(delete_one, any); -PLAN_DATA(delete_one, one); -PLAN_DATA(delete_one, two); -PLAN_DATA(delete_one, many); - -PLAN_DATA(create_many, any); -PLAN_DATA(create_many, one); -PLAN_DATA(create_many, two); -PLAN_DATA(create_many, many); - -PLAN_DATA(rename_one, any); -PLAN_DATA(rename_one, one); -PLAN_DATA(rename_one, two); -PLAN_DATA(rename_one, many); - -PLAN_DATA(rename_many, any); -PLAN_DATA(rename_many, one); -PLAN_DATA(rename_many, two); -PLAN_DATA(rename_many, many); - -PLAN_DATA(append_one, any); -PLAN_DATA(append_one, one); -PLAN_DATA(append_one, two); -PLAN_DATA(append_one, many); - -PLAN_DATA(trunc_one, any); -PLAN_DATA(trunc_one, one); -PLAN_DATA(trunc_one, two); -PLAN_DATA(trunc_one, many); - -PLAN_DATA(touch_one, any); -PLAN_DATA(touch_one, one); -PLAN_DATA(touch_one, two); -PLAN_DATA(touch_one, many); - - int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); @@ -633,53 +560,8 @@ int main(int argc, char **argv) qemu_mutex_init(&evlock); - /* - * Register test cases for the combinatorial - * expansion of file watches and operation sets - */ - #define PLAN_REGISTER(o, w) \ - g_test_add_data_func("/util/filemonitor/" # o "/" # w, \ - &plan_ ## o ## _ ## w, test_file_monitor_events) - - PLAN_REGISTER(create_one, any); - PLAN_REGISTER(create_one, one); - PLAN_REGISTER(create_one, two); - PLAN_REGISTER(create_one, many); - - PLAN_REGISTER(delete_one, any); - PLAN_REGISTER(delete_one, one); - PLAN_REGISTER(delete_one, two); - PLAN_REGISTER(delete_one, many); - - PLAN_REGISTER(create_many, any); - PLAN_REGISTER(create_many, one); - PLAN_REGISTER(create_many, two); - PLAN_REGISTER(create_many, many); - - PLAN_REGISTER(rename_one, any); - PLAN_REGISTER(rename_one, one); - PLAN_REGISTER(rename_one, two); - PLAN_REGISTER(rename_one, many); - - PLAN_REGISTER(rename_many, any); - PLAN_REGISTER(rename_many, one); - PLAN_REGISTER(rename_many, two); - PLAN_REGISTER(rename_many, many); - - PLAN_REGISTER(append_one, any); - PLAN_REGISTER(append_one, one); - PLAN_REGISTER(append_one, two); - PLAN_REGISTER(append_one, many); - - PLAN_REGISTER(trunc_one, any); - PLAN_REGISTER(trunc_one, one); - PLAN_REGISTER(trunc_one, two); - PLAN_REGISTER(trunc_one, many); - - PLAN_REGISTER(touch_one, any); - PLAN_REGISTER(touch_one, one); - PLAN_REGISTER(touch_one, two); - PLAN_REGISTER(touch_one, many); + debug = getenv("FILEMONITOR_DEBUG") != NULL; + g_test_add_func("/util/filemonitor", test_file_monitor_events); return g_test_run(); } -- cgit v1.2.3 From ff3dc8fefe953fd3650279e064bf63b212c5699a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Wed, 13 Mar 2019 17:36:18 +0000 Subject: filemon: ensure watch IDs are unique to QFileMonitor scope MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The watch IDs are mistakenly only unique within the scope of the directory being monitored. This is not useful for clients which are monitoring multiple directories. They require watch IDs to be unique globally within the QFileMonitor scope. Reviewed-by: Marc-André Lureau Tested-by: Bandan Das Reviewed-by: Bandan Das Signed-off-by: Daniel P. Berrangé --- tests/test-util-filemonitor.c | 116 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 108 insertions(+), 8 deletions(-) (limited to 'tests') diff --git a/tests/test-util-filemonitor.c b/tests/test-util-filemonitor.c index ea3715a8f4..71a7cf5de0 100644 --- a/tests/test-util-filemonitor.c +++ b/tests/test-util-filemonitor.c @@ -35,6 +35,8 @@ enum { QFILE_MONITOR_TEST_OP_RENAME, QFILE_MONITOR_TEST_OP_TOUCH, QFILE_MONITOR_TEST_OP_UNLINK, + QFILE_MONITOR_TEST_OP_MKDIR, + QFILE_MONITOR_TEST_OP_RMDIR, }; typedef struct { @@ -298,6 +300,54 @@ test_file_monitor_events(void) .eventid = QFILE_MONITOR_EVENT_DELETED }, + { .type = QFILE_MONITOR_TEST_OP_MKDIR, + .filesrc = "fish", }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "fish", .watchid = 0, + .eventid = QFILE_MONITOR_EVENT_CREATED }, + + + { .type = QFILE_MONITOR_TEST_OP_ADD_WATCH, + .filesrc = "fish/", .watchid = 4 }, + { .type = QFILE_MONITOR_TEST_OP_ADD_WATCH, + .filesrc = "fish/one.txt", .watchid = 5 }, + { .type = QFILE_MONITOR_TEST_OP_CREATE, + .filesrc = "fish/one.txt", }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "one.txt", .watchid = 4, + .eventid = QFILE_MONITOR_EVENT_CREATED }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "one.txt", .watchid = 5, + .eventid = QFILE_MONITOR_EVENT_CREATED }, + + + { .type = QFILE_MONITOR_TEST_OP_DEL_WATCH, + .filesrc = "fish/one.txt", .watchid = 5 }, + { .type = QFILE_MONITOR_TEST_OP_RENAME, + .filesrc = "fish/one.txt", .filedst = "two.txt", }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "one.txt", .watchid = 4, + .eventid = QFILE_MONITOR_EVENT_DELETED }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "two.txt", .watchid = 0, + .eventid = QFILE_MONITOR_EVENT_CREATED }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "two.txt", .watchid = 2, + .eventid = QFILE_MONITOR_EVENT_CREATED }, + + + { .type = QFILE_MONITOR_TEST_OP_RMDIR, + .filesrc = "fish", }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "", .watchid = 4, + .eventid = QFILE_MONITOR_EVENT_IGNORED }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "fish", .watchid = 0, + .eventid = QFILE_MONITOR_EVENT_DELETED }, + { .type = QFILE_MONITOR_TEST_OP_DEL_WATCH, + .filesrc = "fish", .watchid = 4 }, + + { .type = QFILE_MONITOR_TEST_OP_UNLINK, .filesrc = "two.txt", }, { .type = QFILE_MONITOR_TEST_OP_EVENT, @@ -366,6 +416,8 @@ test_file_monitor_events(void) int fd; int watchid; struct utimbuf ubuf; + char *watchdir; + const char *watchfile; pathsrc = g_strdup_printf("%s/%s", dir, op->filesrc); if (op->filedst) { @@ -378,13 +430,26 @@ test_file_monitor_events(void) g_printerr("Add watch %s %s %d\n", dir, op->filesrc, op->watchid); } + if (op->filesrc && strchr(op->filesrc, '/')) { + watchdir = g_strdup_printf("%s/%s", dir, op->filesrc); + watchfile = strrchr(watchdir, '/'); + *(char *)watchfile = '\0'; + watchfile++; + if (*watchfile == '\0') { + watchfile = NULL; + } + } else { + watchdir = g_strdup(dir); + watchfile = op->filesrc; + } watchid = qemu_file_monitor_add_watch(mon, - dir, - op->filesrc, + watchdir, + watchfile, qemu_file_monitor_test_handler, &data, &local_err); + g_free(watchdir); if (watchid < 0) { g_printerr("Unable to add watch %s", error_get_pretty(local_err)); @@ -400,9 +465,17 @@ test_file_monitor_events(void) if (debug) { g_printerr("Del watch %s %d\n", dir, op->watchid); } + if (op->filesrc && strchr(op->filesrc, '/')) { + watchdir = g_strdup_printf("%s/%s", dir, op->filesrc); + watchfile = strrchr(watchdir, '/'); + *(char *)watchfile = '\0'; + } else { + watchdir = g_strdup(dir); + } qemu_file_monitor_remove_watch(mon, - dir, + watchdir, op->watchid); + g_free(watchdir); break; case QFILE_MONITOR_TEST_OP_EVENT: if (debug) { @@ -492,6 +565,28 @@ test_file_monitor_events(void) } break; + case QFILE_MONITOR_TEST_OP_MKDIR: + if (debug) { + g_printerr("Mkdir %s\n", pathsrc); + } + if (mkdir(pathsrc, 0700) < 0) { + g_printerr("Unable to mkdir %s: %s", + pathsrc, strerror(errno)); + goto cleanup; + } + break; + + case QFILE_MONITOR_TEST_OP_RMDIR: + if (debug) { + g_printerr("Rmdir %s\n", pathsrc); + } + if (rmdir(pathsrc) < 0) { + g_printerr("Unable to rmdir %s: %s", + pathsrc, strerror(errno)); + goto cleanup; + } + break; + default: g_assert_not_reached(); } @@ -532,13 +627,18 @@ test_file_monitor_events(void) const QFileMonitorTestOp *op = &(ops[i]); char *path = g_strdup_printf("%s/%s", dir, op->filesrc); - unlink(path); - g_free(path); - if (op->filedst) { - path = g_strdup_printf("%s/%s", - dir, op->filedst); + if (op->type == QFILE_MONITOR_TEST_OP_MKDIR) { + rmdir(path); + g_free(path); + } else { unlink(path); g_free(path); + if (op->filedst) { + path = g_strdup_printf("%s/%s", + dir, op->filedst); + unlink(path); + g_free(path); + } } } if (rmdir(dir) < 0) { -- cgit v1.2.3 From b4682a63f86ed81abcaa543ea6135e17f9e99d01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 19 Mar 2019 15:47:47 +0000 Subject: filemon: fix watch IDs to avoid potential wraparound issues MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Watch IDs are allocated from incrementing a int counter against the QFileMonitor object. In very long life QEMU processes with a huge amount of USB MTP activity creating & deleting directories it is just about conceivable that the int counter can wrap around. This would result in incorrect behaviour of the file monitor watch APIs due to clashing watch IDs. Instead of trying to detect this situation, this patch changes the way watch IDs are allocated. It is turned into an int64_t variable where the high 32 bits are set from the underlying inotify "int" ID. This gives an ID that is guaranteed unique for the directory as a whole, and we can rely on the kernel to enforce this. QFileMonitor then sets the low 32 bits from a per-directory counter. The USB MTP device only sets watches on the directory as a whole, not files within, so there is no risk of guest triggered wrap around on the low 32 bits. Reviewed-by: Marc-André Lureau Signed-off-by: Daniel P. Berrangé --- tests/test-util-filemonitor.c | 130 +++++++++++++++++++++++------------------- 1 file changed, 72 insertions(+), 58 deletions(-) (limited to 'tests') diff --git a/tests/test-util-filemonitor.c b/tests/test-util-filemonitor.c index 71a7cf5de0..46e781c022 100644 --- a/tests/test-util-filemonitor.c +++ b/tests/test-util-filemonitor.c @@ -43,12 +43,12 @@ typedef struct { int type; const char *filesrc; const char *filedst; - int watchid; + int64_t *watchid; int eventid; } QFileMonitorTestOp; typedef struct { - int id; + int64_t id; QFileMonitorEvent event; char *filename; } QFileMonitorTestRecord; @@ -90,7 +90,7 @@ qemu_file_monitor_test_event_loop(void *opaque G_GNUC_UNUSED) * an ordered list of all events that it receives */ static void -qemu_file_monitor_test_handler(int id, +qemu_file_monitor_test_handler(int64_t id, QFileMonitorEvent event, const char *filename, void *opaque) @@ -156,7 +156,7 @@ qemu_file_monitor_test_next_record(QFileMonitorTestData *data) */ static bool qemu_file_monitor_test_expect(QFileMonitorTestData *data, - int id, + int64_t id, QFileMonitorEvent event, const char *filename) { @@ -166,13 +166,14 @@ qemu_file_monitor_test_expect(QFileMonitorTestData *data, rec = qemu_file_monitor_test_next_record(data); if (!rec) { - g_printerr("Missing event watch id %d event %d file %s\n", + g_printerr("Missing event watch id %" PRIx64 " event %d file %s\n", id, event, filename); return false; } if (id != rec->id) { - g_printerr("Expected watch id %d but got %d\n", id, rec->id); + g_printerr("Expected watch id %" PRIx64 " but got %" PRIx64 "\n", + id, rec->id); goto cleanup; } @@ -198,170 +199,176 @@ qemu_file_monitor_test_expect(QFileMonitorTestData *data, static void test_file_monitor_events(void) { + int64_t watch0 = 0; + int64_t watch1 = 0; + int64_t watch2 = 0; + int64_t watch3 = 0; + int64_t watch4 = 0; + int64_t watch5 = 0; QFileMonitorTestOp ops[] = { { .type = QFILE_MONITOR_TEST_OP_ADD_WATCH, - .filesrc = NULL, .watchid = 0 }, + .filesrc = NULL, .watchid = &watch0 }, { .type = QFILE_MONITOR_TEST_OP_ADD_WATCH, - .filesrc = "one.txt", .watchid = 1 }, + .filesrc = "one.txt", .watchid = &watch1 }, { .type = QFILE_MONITOR_TEST_OP_ADD_WATCH, - .filesrc = "two.txt", .watchid = 2 }, + .filesrc = "two.txt", .watchid = &watch2 }, { .type = QFILE_MONITOR_TEST_OP_CREATE, .filesrc = "one.txt", }, { .type = QFILE_MONITOR_TEST_OP_EVENT, - .filesrc = "one.txt", .watchid = 0, + .filesrc = "one.txt", .watchid = &watch0, .eventid = QFILE_MONITOR_EVENT_CREATED }, { .type = QFILE_MONITOR_TEST_OP_EVENT, - .filesrc = "one.txt", .watchid = 1, + .filesrc = "one.txt", .watchid = &watch1, .eventid = QFILE_MONITOR_EVENT_CREATED }, { .type = QFILE_MONITOR_TEST_OP_CREATE, .filesrc = "two.txt", }, { .type = QFILE_MONITOR_TEST_OP_EVENT, - .filesrc = "two.txt", .watchid = 0, + .filesrc = "two.txt", .watchid = &watch0, .eventid = QFILE_MONITOR_EVENT_CREATED }, { .type = QFILE_MONITOR_TEST_OP_EVENT, - .filesrc = "two.txt", .watchid = 2, + .filesrc = "two.txt", .watchid = &watch2, .eventid = QFILE_MONITOR_EVENT_CREATED }, { .type = QFILE_MONITOR_TEST_OP_CREATE, .filesrc = "three.txt", }, { .type = QFILE_MONITOR_TEST_OP_EVENT, - .filesrc = "three.txt", .watchid = 0, + .filesrc = "three.txt", .watchid = &watch0, .eventid = QFILE_MONITOR_EVENT_CREATED }, { .type = QFILE_MONITOR_TEST_OP_UNLINK, .filesrc = "three.txt", }, { .type = QFILE_MONITOR_TEST_OP_EVENT, - .filesrc = "three.txt", .watchid = 0, + .filesrc = "three.txt", .watchid = &watch0, .eventid = QFILE_MONITOR_EVENT_DELETED }, { .type = QFILE_MONITOR_TEST_OP_RENAME, .filesrc = "one.txt", .filedst = "two.txt" }, { .type = QFILE_MONITOR_TEST_OP_EVENT, - .filesrc = "one.txt", .watchid = 0, + .filesrc = "one.txt", .watchid = &watch0, .eventid = QFILE_MONITOR_EVENT_DELETED }, { .type = QFILE_MONITOR_TEST_OP_EVENT, - .filesrc = "one.txt", .watchid = 1, + .filesrc = "one.txt", .watchid = &watch1, .eventid = QFILE_MONITOR_EVENT_DELETED }, { .type = QFILE_MONITOR_TEST_OP_EVENT, - .filesrc = "two.txt", .watchid = 0, + .filesrc = "two.txt", .watchid = &watch0, .eventid = QFILE_MONITOR_EVENT_CREATED }, { .type = QFILE_MONITOR_TEST_OP_EVENT, - .filesrc = "two.txt", .watchid = 2, + .filesrc = "two.txt", .watchid = &watch2, .eventid = QFILE_MONITOR_EVENT_CREATED }, { .type = QFILE_MONITOR_TEST_OP_APPEND, .filesrc = "two.txt", }, { .type = QFILE_MONITOR_TEST_OP_EVENT, - .filesrc = "two.txt", .watchid = 0, + .filesrc = "two.txt", .watchid = &watch0, .eventid = QFILE_MONITOR_EVENT_MODIFIED }, { .type = QFILE_MONITOR_TEST_OP_EVENT, - .filesrc = "two.txt", .watchid = 2, + .filesrc = "two.txt", .watchid = &watch2, .eventid = QFILE_MONITOR_EVENT_MODIFIED }, { .type = QFILE_MONITOR_TEST_OP_TOUCH, .filesrc = "two.txt", }, { .type = QFILE_MONITOR_TEST_OP_EVENT, - .filesrc = "two.txt", .watchid = 0, + .filesrc = "two.txt", .watchid = &watch0, .eventid = QFILE_MONITOR_EVENT_ATTRIBUTES }, { .type = QFILE_MONITOR_TEST_OP_EVENT, - .filesrc = "two.txt", .watchid = 2, + .filesrc = "two.txt", .watchid = &watch2, .eventid = QFILE_MONITOR_EVENT_ATTRIBUTES }, { .type = QFILE_MONITOR_TEST_OP_DEL_WATCH, - .filesrc = "one.txt", .watchid = 1 }, + .filesrc = "one.txt", .watchid = &watch1 }, { .type = QFILE_MONITOR_TEST_OP_ADD_WATCH, - .filesrc = "one.txt", .watchid = 3 }, + .filesrc = "one.txt", .watchid = &watch3 }, { .type = QFILE_MONITOR_TEST_OP_CREATE, .filesrc = "one.txt", }, { .type = QFILE_MONITOR_TEST_OP_EVENT, - .filesrc = "one.txt", .watchid = 0, + .filesrc = "one.txt", .watchid = &watch0, .eventid = QFILE_MONITOR_EVENT_CREATED }, { .type = QFILE_MONITOR_TEST_OP_EVENT, - .filesrc = "one.txt", .watchid = 3, + .filesrc = "one.txt", .watchid = &watch3, .eventid = QFILE_MONITOR_EVENT_CREATED }, { .type = QFILE_MONITOR_TEST_OP_DEL_WATCH, - .filesrc = "one.txt", .watchid = 3 }, + .filesrc = "one.txt", .watchid = &watch3 }, { .type = QFILE_MONITOR_TEST_OP_UNLINK, .filesrc = "one.txt", }, { .type = QFILE_MONITOR_TEST_OP_EVENT, - .filesrc = "one.txt", .watchid = 0, + .filesrc = "one.txt", .watchid = &watch0, .eventid = QFILE_MONITOR_EVENT_DELETED }, { .type = QFILE_MONITOR_TEST_OP_MKDIR, .filesrc = "fish", }, { .type = QFILE_MONITOR_TEST_OP_EVENT, - .filesrc = "fish", .watchid = 0, + .filesrc = "fish", .watchid = &watch0, .eventid = QFILE_MONITOR_EVENT_CREATED }, { .type = QFILE_MONITOR_TEST_OP_ADD_WATCH, - .filesrc = "fish/", .watchid = 4 }, + .filesrc = "fish/", .watchid = &watch4 }, { .type = QFILE_MONITOR_TEST_OP_ADD_WATCH, - .filesrc = "fish/one.txt", .watchid = 5 }, + .filesrc = "fish/one.txt", .watchid = &watch5 }, { .type = QFILE_MONITOR_TEST_OP_CREATE, .filesrc = "fish/one.txt", }, { .type = QFILE_MONITOR_TEST_OP_EVENT, - .filesrc = "one.txt", .watchid = 4, + .filesrc = "one.txt", .watchid = &watch4, .eventid = QFILE_MONITOR_EVENT_CREATED }, { .type = QFILE_MONITOR_TEST_OP_EVENT, - .filesrc = "one.txt", .watchid = 5, + .filesrc = "one.txt", .watchid = &watch5, .eventid = QFILE_MONITOR_EVENT_CREATED }, { .type = QFILE_MONITOR_TEST_OP_DEL_WATCH, - .filesrc = "fish/one.txt", .watchid = 5 }, + .filesrc = "fish/one.txt", .watchid = &watch5 }, { .type = QFILE_MONITOR_TEST_OP_RENAME, .filesrc = "fish/one.txt", .filedst = "two.txt", }, { .type = QFILE_MONITOR_TEST_OP_EVENT, - .filesrc = "one.txt", .watchid = 4, + .filesrc = "one.txt", .watchid = &watch4, .eventid = QFILE_MONITOR_EVENT_DELETED }, { .type = QFILE_MONITOR_TEST_OP_EVENT, - .filesrc = "two.txt", .watchid = 0, + .filesrc = "two.txt", .watchid = &watch0, .eventid = QFILE_MONITOR_EVENT_CREATED }, { .type = QFILE_MONITOR_TEST_OP_EVENT, - .filesrc = "two.txt", .watchid = 2, + .filesrc = "two.txt", .watchid = &watch2, .eventid = QFILE_MONITOR_EVENT_CREATED }, { .type = QFILE_MONITOR_TEST_OP_RMDIR, .filesrc = "fish", }, { .type = QFILE_MONITOR_TEST_OP_EVENT, - .filesrc = "", .watchid = 4, + .filesrc = "", .watchid = &watch4, .eventid = QFILE_MONITOR_EVENT_IGNORED }, { .type = QFILE_MONITOR_TEST_OP_EVENT, - .filesrc = "fish", .watchid = 0, + .filesrc = "fish", .watchid = &watch0, .eventid = QFILE_MONITOR_EVENT_DELETED }, { .type = QFILE_MONITOR_TEST_OP_DEL_WATCH, - .filesrc = "fish", .watchid = 4 }, + .filesrc = "fish", .watchid = &watch4 }, { .type = QFILE_MONITOR_TEST_OP_UNLINK, .filesrc = "two.txt", }, { .type = QFILE_MONITOR_TEST_OP_EVENT, - .filesrc = "two.txt", .watchid = 0, + .filesrc = "two.txt", .watchid = &watch0, .eventid = QFILE_MONITOR_EVENT_DELETED }, { .type = QFILE_MONITOR_TEST_OP_EVENT, - .filesrc = "two.txt", .watchid = 2, + .filesrc = "two.txt", .watchid = &watch2, .eventid = QFILE_MONITOR_EVENT_DELETED }, { .type = QFILE_MONITOR_TEST_OP_DEL_WATCH, - .filesrc = "two.txt", .watchid = 2 }, + .filesrc = "two.txt", .watchid = &watch2 }, { .type = QFILE_MONITOR_TEST_OP_DEL_WATCH, - .filesrc = NULL, .watchid = 0 }, + .filesrc = NULL, .watchid = &watch0 }, }; Error *local_err = NULL; GError *gerr = NULL; @@ -374,6 +381,7 @@ test_file_monitor_events(void) char *pathsrc = NULL; char *pathdst = NULL; QFileMonitorTestData data; + GHashTable *ids = g_hash_table_new(g_int64_hash, g_int64_equal); qemu_mutex_init(&data.lock); data.records = NULL; @@ -414,7 +422,6 @@ test_file_monitor_events(void) for (i = 0; i < G_N_ELEMENTS(ops); i++) { const QFileMonitorTestOp *op = &(ops[i]); int fd; - int watchid; struct utimbuf ubuf; char *watchdir; const char *watchfile; @@ -427,8 +434,8 @@ test_file_monitor_events(void) switch (op->type) { case QFILE_MONITOR_TEST_OP_ADD_WATCH: if (debug) { - g_printerr("Add watch %s %s %d\n", - dir, op->filesrc, op->watchid); + g_printerr("Add watch %s %s\n", + dir, op->filesrc); } if (op->filesrc && strchr(op->filesrc, '/')) { watchdir = g_strdup_printf("%s/%s", dir, op->filesrc); @@ -442,7 +449,7 @@ test_file_monitor_events(void) watchdir = g_strdup(dir); watchfile = op->filesrc; } - watchid = + *op->watchid = qemu_file_monitor_add_watch(mon, watchdir, watchfile, @@ -450,20 +457,23 @@ test_file_monitor_events(void) &data, &local_err); g_free(watchdir); - if (watchid < 0) { + if (*op->watchid < 0) { g_printerr("Unable to add watch %s", error_get_pretty(local_err)); goto cleanup; } - if (watchid != op->watchid) { - g_printerr("Unexpected watch ID %d, wanted %d\n", - watchid, op->watchid); + if (debug) { + g_printerr("Watch ID %" PRIx64 "\n", *op->watchid); + } + if (g_hash_table_contains(ids, op->watchid)) { + g_printerr("Watch ID %" PRIx64 "already exists", *op->watchid); goto cleanup; } + g_hash_table_add(ids, op->watchid); break; case QFILE_MONITOR_TEST_OP_DEL_WATCH: if (debug) { - g_printerr("Del watch %s %d\n", dir, op->watchid); + g_printerr("Del watch %s %" PRIx64 "\n", dir, *op->watchid); } if (op->filesrc && strchr(op->filesrc, '/')) { watchdir = g_strdup_printf("%s/%s", dir, op->filesrc); @@ -472,18 +482,19 @@ test_file_monitor_events(void) } else { watchdir = g_strdup(dir); } + g_hash_table_remove(ids, op->watchid); qemu_file_monitor_remove_watch(mon, watchdir, - op->watchid); + *op->watchid); g_free(watchdir); break; case QFILE_MONITOR_TEST_OP_EVENT: if (debug) { - g_printerr("Event id=%d event=%d file=%s\n", - op->watchid, op->eventid, op->filesrc); + g_printerr("Event id=%" PRIx64 " event=%d file=%s\n", + *op->watchid, op->eventid, op->filesrc); } if (!qemu_file_monitor_test_expect( - &data, op->watchid, op->eventid, op->filesrc)) + &data, *op->watchid, op->eventid, op->filesrc)) goto cleanup; break; case QFILE_MONITOR_TEST_OP_CREATE: @@ -596,6 +607,8 @@ test_file_monitor_events(void) pathsrc = pathdst = NULL; } + g_assert_cmpint(g_hash_table_size(ids), ==, 0); + err = 0; cleanup: @@ -647,6 +660,7 @@ test_file_monitor_events(void) abort(); } } + g_hash_table_unref(ids); g_free(dir); g_assert(err == 0); } -- cgit v1.2.3