Implement wait_directory_event, and the appropriate cleanup. No actual directory...
authorcoderain <coderain@sdf.org>
Fri, 21 Jul 2017 04:37:07 +0000 (06:37 +0200)
committercoderain <coderain@sdf.org>
Fri, 21 Jul 2017 04:37:07 +0000 (06:37 +0200)
kernel/include/filesystem.h
kernel/src/filesystem.c

index 88f477386d1d9ea59681313f161a56ab7f9c2657..786a53289ff4a9bed9c10b4c6006271f872364a6 100644 (file)
@@ -24,6 +24,7 @@
 #include <object.h>
 #include <list.h>
 #include <clock.h>
+#include <sync.h>
 
 #define PATH_DELIMITER_CHAR   '/'
 #define PATH_DELIMITER_STRING "/"
@@ -46,6 +47,8 @@
 
 #define MOUNT_FLAG_READONLY (1 << 0)
 
+#define EVENT_QUEUE_CAPACITY 256
+
 typedef struct mounted_volume mounted_volume_t;
 typedef struct file file_t;
 typedef struct file_instance file_instance_t;
@@ -95,6 +98,28 @@ typedef struct
     clock_time_t last_access_time;
 } file_time_info_t;
 
+typedef struct
+{
+    dword_t type;
+    char filename[VARIABLE_SIZE];
+} file_event_t;
+
+typedef struct
+{
+    list_entry_t list;
+    file_event_t event;
+} event_queue_entry_t;
+
+typedef struct
+{
+    list_entry_t list;
+    lock_t lock;
+    file_instance_t *directory;
+    dword_t event_mask;
+    semaphore_t event_semaphore;
+    list_entry_t event_queue;
+} event_watch_entry_t;
+
 struct file
 {
     object_t header;
@@ -111,6 +136,7 @@ struct file_instance
     object_t header;
     file_t *global;
     dword_t mode;
+    event_watch_entry_t *watch;
 };
 
 #include <device.h>
index 1925b0bcb1d1e7830c368e97cc0162fd8ed46925..47dd3dec9df511fbe0b7352fac8083d83ece117e 100644 (file)
@@ -26,7 +26,9 @@
 
 static DECLARE_LIST(fs_driver_list);
 static DECLARE_LIST(volumes);
+static DECLARE_LIST(event_watch_list);
 lock_t fs_driver_list_lock = 0;
+lock_t event_watch_list_lock = 0;
 resource_t volume_list_res = 0;
 
 static inline int count_delimiters(const char *string)
@@ -48,6 +50,29 @@ void file_cleanup(file_t *file)
 void file_instance_cleanup(file_instance_t *instance)
 {
     file_t *file = instance->global;
+
+    if (instance->watch)
+    {
+        ASSERT(file->attributes & FILE_ATTR_DIRECTORY);
+        ASSERT(instance->watch->directory == instance);
+
+        acquire_lock(&event_watch_list_lock);
+        list_remove(&instance->watch->list);
+        release_lock(&event_watch_list_lock);
+
+        ASSERT(!instance->watch->lock);
+
+        while (instance->watch->event_queue.next != &instance->watch->event_queue)
+        {
+            event_queue_entry_t *entry = CONTAINER_OF(instance->watch->event_queue.next, event_queue_entry_t, list);
+            list_remove(&entry->list);
+            free(entry);
+        }
+
+        free(instance->watch);
+        instance->watch = NULL;
+    }
+
     file->volume->driver->close_file(instance);
 
     if (instance->mode & FILE_MODE_DELETE_ON_CLOSE)
@@ -644,3 +669,72 @@ cleanup:
     dereference(&file->header);
     return ret;
 }
+
+dword_t wait_directory_event(handle_t handle, dword_t event_mask, file_event_t *buffer, size_t size, dword_t timeout)
+{
+    file_instance_t *directory;
+
+    if (get_previous_mode() == USER_MODE && !check_usermode(buffer, size)) return ERR_BADPTR;
+    if (!reference_by_handle(handle, OBJECT_FILE_INSTANCE, (object_t**)&directory)) return ERR_INVALID;
+
+    if (!(directory->global->attributes & FILE_ATTR_DIRECTORY))
+    {
+        dereference(&directory->header);
+        return ERR_ISNOTDIR;
+    }
+
+    if (directory->watch == NULL)
+    {
+        event_watch_entry_t *watch = (event_watch_entry_t*)malloc(sizeof(event_watch_entry_t));
+        watch->directory = directory;
+        init_semaphore(&watch->event_semaphore, 0, EVENT_QUEUE_CAPACITY);
+        list_init(&watch->event_queue);
+
+        acquire_lock(&event_watch_list_lock);
+        list_append(&event_watch_list, &watch->list);
+
+        if (!__sync_bool_compare_and_swap(&directory->watch, NULL, watch))
+        {
+            list_remove(&watch->list);
+            free(watch);
+        }
+
+        release_lock(&event_watch_list_lock);
+    }
+
+    directory->watch->event_mask = event_mask;
+
+    dword_t ret = wait_semaphore(&directory->watch->event_semaphore, 1, timeout);
+    if (ret != ERR_SUCCESS)
+    {
+        dereference(&directory->header);
+        return ret;
+    }
+
+    acquire_lock(&directory->watch->lock);
+
+    event_queue_entry_t *entry = CONTAINER_OF(directory->watch->event_queue.next, event_queue_entry_t, list);
+    dword_t event_size = sizeof(entry->event) + strlen(entry->event.filename) + 1;
+
+    if (size >= event_size)
+    {
+        EH_TRY memcpy(buffer, &entry->event, event_size);
+        EH_CATCH ret = ERR_BADPTR;
+        EH_DONE;
+
+        if (ret == ERR_SUCCESS)
+        {
+            list_remove(&entry->list);
+            free(entry);
+        }
+    }
+    else
+    {
+        ret = ERR_SMALLBUF;
+    }
+
+    if (ret != ERR_SUCCESS) release_semaphore(&directory->watch->event_semaphore, 1);
+    release_lock(&directory->watch->lock);
+    dereference(&directory->header);
+    return ret;
+}