3faebbcd64
Unlike on Linux, on FreeBSD renaming a file when the destination already exists results in an IN_DELETE event for that existing file: $ FILEMONITOR_DEBUG=1 build/tests/unit/test-util-filemonitor Rename /tmp/test-util-filemonitor-K13LI2/fish/one.txt -> /tmp/test-util-filemonitor-K13LI2/two.txt Event id=200000000 event=2 file=one.txt Queue event id 200000000 event 2 file one.txt Queue event id 100000000 event 2 file two.txt Queue event id 100000002 event 2 file two.txt Queue event id 100000000 event 0 file two.txt Queue event id 100000002 event 0 file two.txt Event id=100000000 event=0 file=two.txt Expected event 0 but got 2 This difference in behavior is not expected to break the real users, so teach the test to accept it. Suggested-by: "Daniel P. Berrange" <berrange@redhat.com> Signed-off-by: Ilya Leoshkevich <iii@linux.ibm.com> Message-ID: <20240206002344.12372-4-iii@linux.ibm.com> Signed-off-by: Thomas Huth <thuth@redhat.com>
729 lines
23 KiB
C
729 lines
23 KiB
C
/*
|
|
* Tests for util/filemonitor-*.c
|
|
*
|
|
* Copyright 2018 Red Hat, Inc.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this library; if not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#include "qemu/osdep.h"
|
|
#include "qemu/main-loop.h"
|
|
#include "qapi/error.h"
|
|
#include "qemu/filemonitor.h"
|
|
|
|
#include <glib/gstdio.h>
|
|
|
|
#include <utime.h>
|
|
|
|
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,
|
|
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 {
|
|
int type;
|
|
const char *filesrc;
|
|
const char *filedst;
|
|
int64_t *watchid;
|
|
int eventid;
|
|
/*
|
|
* Only valid with OP_EVENT - this event might be
|
|
* swapped with the next OP_EVENT
|
|
*/
|
|
bool swapnext;
|
|
} QFileMonitorTestOp;
|
|
|
|
typedef struct {
|
|
int64_t id;
|
|
QFileMonitorEvent event;
|
|
char *filename;
|
|
} QFileMonitorTestRecord;
|
|
|
|
|
|
typedef struct {
|
|
QemuMutex lock;
|
|
GList *records;
|
|
} QFileMonitorTestData;
|
|
|
|
static QemuMutex evlock;
|
|
static bool evstopping;
|
|
static bool evrunning;
|
|
static bool debug;
|
|
|
|
/*
|
|
* Main function for a background thread that is
|
|
* running the event loop during the test
|
|
*/
|
|
static void *
|
|
qemu_file_monitor_test_event_loop(void *opaque G_GNUC_UNUSED)
|
|
{
|
|
qemu_mutex_lock(&evlock);
|
|
|
|
while (!evstopping) {
|
|
qemu_mutex_unlock(&evlock);
|
|
main_loop_wait(true);
|
|
qemu_mutex_lock(&evlock);
|
|
}
|
|
|
|
evrunning = false;
|
|
qemu_mutex_unlock(&evlock);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
* File monitor event handler which simply maintains
|
|
* an ordered list of all events that it receives
|
|
*/
|
|
static void
|
|
qemu_file_monitor_test_handler(int64_t id,
|
|
QFileMonitorEvent event,
|
|
const char *filename,
|
|
void *opaque)
|
|
{
|
|
QFileMonitorTestData *data = opaque;
|
|
QFileMonitorTestRecord *rec = g_new0(QFileMonitorTestRecord, 1);
|
|
|
|
if (debug) {
|
|
g_printerr("Queue event id %" PRIx64 " event %d file %s\n",
|
|
id, event, filename);
|
|
}
|
|
rec->id = id;
|
|
rec->event = event;
|
|
rec->filename = g_strdup(filename);
|
|
|
|
qemu_mutex_lock(&data->lock);
|
|
data->records = g_list_append(data->records, rec);
|
|
qemu_mutex_unlock(&data->lock);
|
|
}
|
|
|
|
|
|
static void
|
|
qemu_file_monitor_test_record_free(QFileMonitorTestRecord *rec)
|
|
{
|
|
g_free(rec->filename);
|
|
g_free(rec);
|
|
}
|
|
|
|
|
|
/*
|
|
* Get the next event record that has been received by
|
|
* the file monitor event handler. Since events are
|
|
* emitted in the background thread running the event
|
|
* loop, we can't assume there is a record available
|
|
* immediately. Thus we will sleep for up to 5 seconds
|
|
* to wait for the event to be queued for us.
|
|
*/
|
|
static QFileMonitorTestRecord *
|
|
qemu_file_monitor_test_next_record(QFileMonitorTestData *data,
|
|
QFileMonitorTestRecord *pushback)
|
|
{
|
|
GTimer *timer = g_timer_new();
|
|
QFileMonitorTestRecord *record = NULL;
|
|
GList *tmp;
|
|
|
|
qemu_mutex_lock(&data->lock);
|
|
while (!data->records && g_timer_elapsed(timer, NULL) < 5) {
|
|
qemu_mutex_unlock(&data->lock);
|
|
usleep(10 * 1000);
|
|
qemu_mutex_lock(&data->lock);
|
|
}
|
|
if (data->records) {
|
|
record = data->records->data;
|
|
if (pushback) {
|
|
data->records->data = pushback;
|
|
} else {
|
|
tmp = data->records;
|
|
data->records = g_list_remove_link(data->records, tmp);
|
|
g_list_free(tmp);
|
|
}
|
|
} else if (pushback) {
|
|
qemu_file_monitor_test_record_free(pushback);
|
|
}
|
|
qemu_mutex_unlock(&data->lock);
|
|
|
|
g_timer_destroy(timer);
|
|
return record;
|
|
}
|
|
|
|
|
|
/*
|
|
* Check whether the event record we retrieved matches
|
|
* data we were expecting to see for the event
|
|
*/
|
|
static bool
|
|
qemu_file_monitor_test_expect(QFileMonitorTestData *data,
|
|
int64_t id,
|
|
QFileMonitorEvent event,
|
|
const char *filename,
|
|
bool swapnext)
|
|
{
|
|
QFileMonitorTestRecord *rec;
|
|
bool ret = false;
|
|
|
|
rec = qemu_file_monitor_test_next_record(data, NULL);
|
|
|
|
retry:
|
|
if (!rec) {
|
|
g_printerr("Missing event watch id %" PRIx64 " event %d file %s\n",
|
|
id, event, filename);
|
|
return false;
|
|
}
|
|
|
|
if (id != rec->id) {
|
|
if (swapnext) {
|
|
rec = qemu_file_monitor_test_next_record(data, rec);
|
|
swapnext = false;
|
|
goto retry;
|
|
}
|
|
g_printerr("Expected watch id %" PRIx64 " but got %" PRIx64 "\n",
|
|
id, rec->id);
|
|
goto cleanup;
|
|
}
|
|
|
|
if (event != rec->event) {
|
|
g_printerr("Expected event %d but got %d\n", event, rec->event);
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!g_str_equal(filename, rec->filename)) {
|
|
g_printerr("Expected filename %s but got %s\n",
|
|
filename, rec->filename);
|
|
goto cleanup;
|
|
}
|
|
|
|
ret = true;
|
|
|
|
cleanup:
|
|
qemu_file_monitor_test_record_free(rec);
|
|
return ret;
|
|
}
|
|
|
|
|
|
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 = &watch0 },
|
|
{ .type = QFILE_MONITOR_TEST_OP_ADD_WATCH,
|
|
.filesrc = "one.txt", .watchid = &watch1 },
|
|
{ .type = QFILE_MONITOR_TEST_OP_ADD_WATCH,
|
|
.filesrc = "two.txt", .watchid = &watch2 },
|
|
|
|
|
|
{ .type = QFILE_MONITOR_TEST_OP_CREATE,
|
|
.filesrc = "one.txt", },
|
|
{ .type = QFILE_MONITOR_TEST_OP_EVENT,
|
|
.filesrc = "one.txt", .watchid = &watch0,
|
|
.eventid = QFILE_MONITOR_EVENT_CREATED },
|
|
{ .type = QFILE_MONITOR_TEST_OP_EVENT,
|
|
.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 = &watch0,
|
|
.eventid = QFILE_MONITOR_EVENT_CREATED },
|
|
{ .type = QFILE_MONITOR_TEST_OP_EVENT,
|
|
.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 = &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 = &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 = &watch0,
|
|
.eventid = QFILE_MONITOR_EVENT_DELETED },
|
|
{ .type = QFILE_MONITOR_TEST_OP_EVENT,
|
|
.filesrc = "one.txt", .watchid = &watch1,
|
|
.eventid = QFILE_MONITOR_EVENT_DELETED },
|
|
{ .type = QFILE_MONITOR_TEST_OP_EVENT,
|
|
.filesrc = "two.txt", .watchid = &watch0,
|
|
.eventid = QFILE_MONITOR_EVENT_CREATED },
|
|
{ .type = QFILE_MONITOR_TEST_OP_EVENT,
|
|
.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 = &watch0,
|
|
.eventid = QFILE_MONITOR_EVENT_MODIFIED },
|
|
{ .type = QFILE_MONITOR_TEST_OP_EVENT,
|
|
.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 = &watch0,
|
|
.eventid = QFILE_MONITOR_EVENT_ATTRIBUTES },
|
|
{ .type = QFILE_MONITOR_TEST_OP_EVENT,
|
|
.filesrc = "two.txt", .watchid = &watch2,
|
|
.eventid = QFILE_MONITOR_EVENT_ATTRIBUTES },
|
|
|
|
|
|
{ .type = QFILE_MONITOR_TEST_OP_DEL_WATCH,
|
|
.filesrc = "one.txt", .watchid = &watch1 },
|
|
{ .type = QFILE_MONITOR_TEST_OP_ADD_WATCH,
|
|
.filesrc = "one.txt", .watchid = &watch3 },
|
|
{ .type = QFILE_MONITOR_TEST_OP_CREATE,
|
|
.filesrc = "one.txt", },
|
|
{ .type = QFILE_MONITOR_TEST_OP_EVENT,
|
|
.filesrc = "one.txt", .watchid = &watch0,
|
|
.eventid = QFILE_MONITOR_EVENT_CREATED },
|
|
{ .type = QFILE_MONITOR_TEST_OP_EVENT,
|
|
.filesrc = "one.txt", .watchid = &watch3,
|
|
.eventid = QFILE_MONITOR_EVENT_CREATED },
|
|
|
|
|
|
{ .type = QFILE_MONITOR_TEST_OP_DEL_WATCH,
|
|
.filesrc = "one.txt", .watchid = &watch3 },
|
|
{ .type = QFILE_MONITOR_TEST_OP_UNLINK,
|
|
.filesrc = "one.txt", },
|
|
{ .type = QFILE_MONITOR_TEST_OP_EVENT,
|
|
.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 = &watch0,
|
|
.eventid = QFILE_MONITOR_EVENT_CREATED },
|
|
|
|
|
|
{ .type = QFILE_MONITOR_TEST_OP_ADD_WATCH,
|
|
.filesrc = "fish/", .watchid = &watch4 },
|
|
{ .type = QFILE_MONITOR_TEST_OP_ADD_WATCH,
|
|
.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 = &watch4,
|
|
.eventid = QFILE_MONITOR_EVENT_CREATED },
|
|
{ .type = QFILE_MONITOR_TEST_OP_EVENT,
|
|
.filesrc = "one.txt", .watchid = &watch5,
|
|
.eventid = QFILE_MONITOR_EVENT_CREATED },
|
|
|
|
|
|
{ .type = QFILE_MONITOR_TEST_OP_DEL_WATCH,
|
|
.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 = &watch4,
|
|
.eventid = QFILE_MONITOR_EVENT_DELETED },
|
|
#ifdef __FreeBSD__
|
|
{ .type = QFILE_MONITOR_TEST_OP_EVENT,
|
|
.filesrc = "two.txt", .watchid = &watch0,
|
|
.eventid = QFILE_MONITOR_EVENT_DELETED },
|
|
{ .type = QFILE_MONITOR_TEST_OP_EVENT,
|
|
.filesrc = "two.txt", .watchid = &watch2,
|
|
.eventid = QFILE_MONITOR_EVENT_DELETED },
|
|
#endif
|
|
{ .type = QFILE_MONITOR_TEST_OP_EVENT,
|
|
.filesrc = "two.txt", .watchid = &watch0,
|
|
.eventid = QFILE_MONITOR_EVENT_CREATED },
|
|
{ .type = QFILE_MONITOR_TEST_OP_EVENT,
|
|
.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 = &watch4,
|
|
.eventid = QFILE_MONITOR_EVENT_IGNORED,
|
|
.swapnext = true },
|
|
{ .type = QFILE_MONITOR_TEST_OP_EVENT,
|
|
.filesrc = "fish", .watchid = &watch0,
|
|
.eventid = QFILE_MONITOR_EVENT_DELETED },
|
|
{ .type = QFILE_MONITOR_TEST_OP_DEL_WATCH,
|
|
.filesrc = "fish", .watchid = &watch4 },
|
|
|
|
|
|
{ .type = QFILE_MONITOR_TEST_OP_UNLINK,
|
|
.filesrc = "two.txt", },
|
|
{ .type = QFILE_MONITOR_TEST_OP_EVENT,
|
|
.filesrc = "two.txt", .watchid = &watch0,
|
|
.eventid = QFILE_MONITOR_EVENT_DELETED },
|
|
{ .type = QFILE_MONITOR_TEST_OP_EVENT,
|
|
.filesrc = "two.txt", .watchid = &watch2,
|
|
.eventid = QFILE_MONITOR_EVENT_DELETED },
|
|
|
|
|
|
{ .type = QFILE_MONITOR_TEST_OP_DEL_WATCH,
|
|
.filesrc = "two.txt", .watchid = &watch2 },
|
|
{ .type = QFILE_MONITOR_TEST_OP_DEL_WATCH,
|
|
.filesrc = NULL, .watchid = &watch0 },
|
|
};
|
|
Error *local_err = NULL;
|
|
GError *gerr = NULL;
|
|
QFileMonitor *mon = qemu_file_monitor_new(&local_err);
|
|
QemuThread th;
|
|
GTimer *timer;
|
|
gchar *dir = NULL;
|
|
int err = -1;
|
|
gsize i;
|
|
char *pathsrc = NULL;
|
|
char *pathdst = NULL;
|
|
QFileMonitorTestData data;
|
|
GHashTable *ids = g_hash_table_new(g_int64_hash, g_int64_equal);
|
|
char *travis_arch;
|
|
|
|
qemu_mutex_init(&data.lock);
|
|
data.records = NULL;
|
|
|
|
/*
|
|
* This test does not work on Travis LXD containers since some
|
|
* syscalls are blocked in that environment.
|
|
*/
|
|
travis_arch = getenv("TRAVIS_ARCH");
|
|
if (travis_arch && !g_str_equal(travis_arch, "x86_64")) {
|
|
g_test_skip("Test does not work on non-x86 Travis containers.");
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* The file monitor needs the main loop running in
|
|
* order to receive events from inotify. We must
|
|
* thus spawn a background thread to run an event
|
|
* loop impl, while this thread triggers the
|
|
* actual file operations we're testing
|
|
*/
|
|
evrunning = 1;
|
|
evstopping = 0;
|
|
qemu_thread_create(&th, "event-loop",
|
|
qemu_file_monitor_test_event_loop, NULL,
|
|
QEMU_THREAD_JOINABLE);
|
|
|
|
if (local_err) {
|
|
g_printerr("File monitoring not available: %s",
|
|
error_get_pretty(local_err));
|
|
error_free(local_err);
|
|
return;
|
|
}
|
|
|
|
dir = g_dir_make_tmp("test-util-filemonitor-XXXXXX",
|
|
&gerr);
|
|
if (!dir) {
|
|
g_printerr("Unable to create tmp dir %s",
|
|
gerr->message);
|
|
g_error_free(gerr);
|
|
abort();
|
|
}
|
|
|
|
/*
|
|
* Run through the operation sequence validating events
|
|
* as we go
|
|
*/
|
|
for (i = 0; i < G_N_ELEMENTS(ops); i++) {
|
|
const QFileMonitorTestOp *op = &(ops[i]);
|
|
int fd;
|
|
struct utimbuf ubuf;
|
|
char *watchdir;
|
|
const char *watchfile;
|
|
|
|
pathsrc = g_strdup_printf("%s/%s", dir, op->filesrc);
|
|
if (op->filedst) {
|
|
pathdst = g_strdup_printf("%s/%s", dir, op->filedst);
|
|
}
|
|
|
|
switch (op->type) {
|
|
case QFILE_MONITOR_TEST_OP_ADD_WATCH:
|
|
if (debug) {
|
|
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);
|
|
watchfile = strrchr(watchdir, '/');
|
|
*(char *)watchfile = '\0';
|
|
watchfile++;
|
|
if (*watchfile == '\0') {
|
|
watchfile = NULL;
|
|
}
|
|
} else {
|
|
watchdir = g_strdup(dir);
|
|
watchfile = op->filesrc;
|
|
}
|
|
*op->watchid =
|
|
qemu_file_monitor_add_watch(mon,
|
|
watchdir,
|
|
watchfile,
|
|
qemu_file_monitor_test_handler,
|
|
&data,
|
|
&local_err);
|
|
g_free(watchdir);
|
|
if (*op->watchid < 0) {
|
|
g_printerr("Unable to add watch %s",
|
|
error_get_pretty(local_err));
|
|
error_free(local_err);
|
|
goto cleanup;
|
|
}
|
|
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 %" PRIx64 "\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);
|
|
}
|
|
g_hash_table_remove(ids, op->watchid);
|
|
qemu_file_monitor_remove_watch(mon,
|
|
watchdir,
|
|
*op->watchid);
|
|
g_free(watchdir);
|
|
break;
|
|
case QFILE_MONITOR_TEST_OP_EVENT:
|
|
if (debug) {
|
|
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,
|
|
op->swapnext))
|
|
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",
|
|
pathsrc, strerror(errno));
|
|
goto cleanup;
|
|
}
|
|
close(fd);
|
|
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",
|
|
pathsrc, strerror(errno));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (write(fd, "Hello World", 10) != 10) {
|
|
g_printerr("Unable to write %s: %s",
|
|
pathsrc, strerror(errno));
|
|
close(fd);
|
|
goto cleanup;
|
|
}
|
|
close(fd);
|
|
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));
|
|
goto cleanup;
|
|
}
|
|
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));
|
|
goto cleanup;
|
|
}
|
|
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));
|
|
goto cleanup;
|
|
}
|
|
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) {
|
|
g_printerr("Unable to touch %s: %s",
|
|
pathsrc, strerror(errno));
|
|
goto cleanup;
|
|
}
|
|
break;
|
|
|
|
case QFILE_MONITOR_TEST_OP_MKDIR:
|
|
if (debug) {
|
|
g_printerr("Mkdir %s\n", pathsrc);
|
|
}
|
|
if (g_mkdir_with_parents(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();
|
|
}
|
|
|
|
g_free(pathsrc);
|
|
g_free(pathdst);
|
|
pathsrc = pathdst = NULL;
|
|
}
|
|
|
|
g_assert_cmpint(g_hash_table_size(ids), ==, 0);
|
|
|
|
err = 0;
|
|
|
|
cleanup:
|
|
g_free(pathsrc);
|
|
g_free(pathdst);
|
|
|
|
qemu_mutex_lock(&evlock);
|
|
evstopping = 1;
|
|
timer = g_timer_new();
|
|
while (evrunning && g_timer_elapsed(timer, NULL) < 5) {
|
|
qemu_mutex_unlock(&evlock);
|
|
usleep(10 * 1000);
|
|
qemu_mutex_lock(&evlock);
|
|
}
|
|
qemu_mutex_unlock(&evlock);
|
|
|
|
if (g_timer_elapsed(timer, NULL) >= 5) {
|
|
g_printerr("Event loop failed to quit after 5 seconds\n");
|
|
}
|
|
g_timer_destroy(timer);
|
|
|
|
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) {
|
|
for (i = 0; i < G_N_ELEMENTS(ops); i++) {
|
|
const QFileMonitorTestOp *op = &(ops[i]);
|
|
char *path = g_strdup_printf("%s/%s",
|
|
dir, op->filesrc);
|
|
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) {
|
|
g_printerr("Failed to remove %s: %s\n",
|
|
dir, strerror(errno));
|
|
abort();
|
|
}
|
|
}
|
|
g_hash_table_unref(ids);
|
|
g_free(dir);
|
|
g_assert(err == 0);
|
|
}
|
|
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
g_test_init(&argc, &argv, NULL);
|
|
|
|
qemu_init_main_loop(&error_abort);
|
|
|
|
qemu_mutex_init(&evlock);
|
|
|
|
debug = getenv("FILEMONITOR_DEBUG") != NULL;
|
|
g_test_add_func("/util/filemonitor", test_file_monitor_events);
|
|
|
|
return g_test_run();
|
|
}
|