28dfc89b15
- Replace undefined 'len' variable references with 'name_len' - Add explicit null terminators after memcpy operations - Move name_len declaration to Windows-specific scope to eliminate unused variable warning on BSD - Standardize string copy pattern across all platforms: strlen -> clamp -> memcpy -> null terminate - Fix increment operator placement for clarity (i++ moved outside array indexing) - Remove duplicate name_len declaration in platform_directory_watch_check This ensures proper bounds checking and null termination for filenames on both Windows and BSD systems, preventing potential buffer overflows when handling long filenames (approaching STK_PATH_MAX).
1003 lines
20 KiB
C
1003 lines
20 KiB
C
#include "stk.h"
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#ifndef _WIN32
|
|
#include <sys/file.h>
|
|
#endif
|
|
|
|
#if defined(__linux__)
|
|
#include <dirent.h>
|
|
#include <dlfcn.h>
|
|
#include <sys/inotify.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
#elif defined(_WIN32)
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#include <windows.h>
|
|
#else
|
|
#include <dirent.h>
|
|
#include <dlfcn.h>
|
|
#include <fcntl.h>
|
|
#include <sys/event.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/time.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
int is_mod_loaded(const char *module_name);
|
|
uint8_t is_valid_module_file(const char *filename);
|
|
void extract_module_id(const char *path, char *out_id);
|
|
|
|
#ifndef __linux__
|
|
static uint8_t is_file_ready(const char *dir_path, const char *filename)
|
|
{
|
|
char full_path[STK_PATH_MAX_OS];
|
|
#ifdef _WIN32
|
|
DWORD size;
|
|
HANDLE h;
|
|
|
|
sprintf(full_path, "%s\\%s", dir_path, filename);
|
|
h = CreateFileA(full_path, GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL, NULL);
|
|
if (h == INVALID_HANDLE_VALUE)
|
|
return 0;
|
|
|
|
size = GetFileSize(h, NULL);
|
|
CloseHandle(h);
|
|
|
|
if (size == INVALID_FILE_SIZE || size < 1024)
|
|
return 0;
|
|
|
|
return 1;
|
|
#else
|
|
int fd;
|
|
struct stat st;
|
|
|
|
sprintf(full_path, "%s/%s", dir_path, filename);
|
|
|
|
if (stat(full_path, &st) != 0)
|
|
return 0;
|
|
|
|
if (st.st_size < 1024)
|
|
return 0;
|
|
|
|
fd = open(full_path, O_RDONLY);
|
|
if (fd < 0)
|
|
return 0;
|
|
|
|
if (flock(fd, LOCK_EX | LOCK_NB) != 0) {
|
|
close(fd);
|
|
return 0;
|
|
}
|
|
|
|
flock(fd, LOCK_UN);
|
|
close(fd);
|
|
return 1;
|
|
#endif
|
|
}
|
|
|
|
typedef struct {
|
|
char filename[STK_PATH_MAX];
|
|
#ifdef _WIN32
|
|
FILETIME mtime;
|
|
#else
|
|
time_t mtime;
|
|
#endif
|
|
} platform_snapshot_t;
|
|
|
|
typedef struct {
|
|
char path[STK_PATH_MAX];
|
|
platform_snapshot_t *snaps;
|
|
size_t count;
|
|
union {
|
|
#ifdef _WIN32
|
|
HANDLE change_handle;
|
|
#else
|
|
struct {
|
|
int kq;
|
|
int dir_fd;
|
|
int *file_fds;
|
|
size_t file_fd_count;
|
|
} k;
|
|
#endif
|
|
} watch;
|
|
} platform_watch_context_t;
|
|
#endif
|
|
|
|
uint8_t platform_mkdir(const char *path)
|
|
{
|
|
#ifdef _WIN32
|
|
return CreateDirectoryA(path, NULL) ? STK_PLATFORM_OPERATION_SUCCESS
|
|
: STK_PLATFORM_MKDIR_ERROR;
|
|
#else
|
|
return mkdir(path, 0755) == 0 ? STK_PLATFORM_OPERATION_SUCCESS
|
|
: STK_PLATFORM_MKDIR_ERROR;
|
|
#endif
|
|
}
|
|
|
|
int platform_remove_file(const char *path)
|
|
{
|
|
#ifdef _WIN32
|
|
return DeleteFileA(path) ? STK_PLATFORM_OPERATION_SUCCESS
|
|
: STK_PLATFORM_REMOVE_FILE_ERROR;
|
|
#else
|
|
return unlink(path) == 0 ? STK_PLATFORM_OPERATION_SUCCESS
|
|
: STK_PLATFORM_REMOVE_FILE_ERROR;
|
|
#endif
|
|
}
|
|
|
|
uint8_t platform_copy_file(const char *from, const char *to)
|
|
{
|
|
char buf[STK_PATH_MAX_OS];
|
|
int ret = STK_PLATFORM_FILE_COPY_ERROR;
|
|
#ifdef _WIN32
|
|
sprintf(buf, "%s.tmp", to);
|
|
if (CopyFileA(from, buf, FALSE)) {
|
|
if (MoveFileExA(buf, to, MOVEFILE_REPLACE_EXISTING))
|
|
ret = 0;
|
|
else
|
|
DeleteFileA(buf);
|
|
}
|
|
#else
|
|
FILE *src = NULL, *dst = NULL;
|
|
char tmp_path[STK_PATH_MAX_OS];
|
|
size_t n;
|
|
#ifndef __linux__
|
|
char dir_path[STK_PATH_MAX_OS];
|
|
const char *filename;
|
|
|
|
filename = strrchr(from, '/');
|
|
if (filename) {
|
|
size_t dir_len = filename - from;
|
|
strncpy(dir_path, from, dir_len);
|
|
dir_path[dir_len] = '\0';
|
|
filename++;
|
|
} else {
|
|
strcpy(dir_path, ".");
|
|
filename = from;
|
|
}
|
|
|
|
if (!is_file_ready(dir_path, filename))
|
|
goto done;
|
|
#endif
|
|
|
|
sprintf(tmp_path, "%s.tmp", to);
|
|
|
|
src = fopen(from, "rb");
|
|
if (!src)
|
|
goto cleanup;
|
|
|
|
dst = fopen(tmp_path, "wb");
|
|
if (!dst)
|
|
goto cleanup;
|
|
|
|
while ((n = fread(buf, 1, sizeof(buf), src)) > 0)
|
|
fwrite(buf, 1, n, dst);
|
|
|
|
if (src)
|
|
fclose(src);
|
|
if (dst)
|
|
fclose(dst);
|
|
src = NULL;
|
|
dst = NULL;
|
|
|
|
if (rename(tmp_path, to) == 0)
|
|
ret = STK_PLATFORM_OPERATION_SUCCESS;
|
|
else
|
|
unlink(tmp_path);
|
|
|
|
goto done;
|
|
|
|
cleanup:
|
|
if (src)
|
|
fclose(src);
|
|
if (dst)
|
|
fclose(dst);
|
|
unlink(tmp_path);
|
|
|
|
done:
|
|
#endif
|
|
|
|
return ret;
|
|
}
|
|
|
|
uint8_t platform_remove_dir(const char *path)
|
|
{
|
|
#ifdef _WIN32
|
|
WIN32_FIND_DATAA fd;
|
|
HANDLE h;
|
|
char s[STK_PATH_MAX_OS], f[STK_PATH_MAX_OS];
|
|
sprintf(s, "%s\\*", path);
|
|
|
|
h = FindFirstFileA(s, &fd);
|
|
if (h == INVALID_HANDLE_VALUE)
|
|
goto remove_dir;
|
|
|
|
do {
|
|
if (strcmp(fd.cFileName, ".") == 0 ||
|
|
strcmp(fd.cFileName, "..") == 0)
|
|
continue;
|
|
|
|
sprintf(f, "%s\\%s", path, fd.cFileName);
|
|
DeleteFileA(f);
|
|
} while (FindNextFileA(h, &fd));
|
|
|
|
FindClose(h);
|
|
|
|
remove_dir:
|
|
return RemoveDirectoryA(path) ? STK_PLATFORM_OPERATION_SUCCESS
|
|
: STK_PLATFORM_REMOVE_DIR_ERROR;
|
|
#else
|
|
DIR *dir;
|
|
struct dirent *entry;
|
|
char filepath[STK_PATH_MAX_OS];
|
|
|
|
dir = opendir(path);
|
|
if (!dir)
|
|
return STK_PLATFORM_REMOVE_DIR_ERROR;
|
|
|
|
loop:
|
|
entry = readdir(dir);
|
|
if (!entry)
|
|
goto loop_end;
|
|
|
|
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
|
|
goto loop;
|
|
|
|
sprintf(filepath, "%s/%s", path, entry->d_name);
|
|
unlink(filepath);
|
|
goto loop;
|
|
|
|
loop_end:
|
|
closedir(dir);
|
|
return rmdir(path) == 0 ? STK_PLATFORM_OPERATION_SUCCESS
|
|
: STK_PLATFORM_REMOVE_DIR_ERROR;
|
|
#endif
|
|
}
|
|
|
|
void *platform_load_library(const char *path)
|
|
{
|
|
#ifdef _WIN32
|
|
return (void *)LoadLibraryA(path);
|
|
#else
|
|
return dlopen(path, RTLD_NOW);
|
|
#endif
|
|
}
|
|
|
|
void platform_unload_library(void *h)
|
|
{
|
|
#ifdef _WIN32
|
|
FreeLibrary((HMODULE)h);
|
|
#else
|
|
dlclose(h);
|
|
#endif
|
|
}
|
|
|
|
void *platform_get_symbol(void *h, const char *s)
|
|
{
|
|
#ifdef _WIN32
|
|
return (void *)(intptr_t)GetProcAddress((HMODULE)h, s);
|
|
#else
|
|
return dlsym(h, s);
|
|
#endif
|
|
}
|
|
|
|
char (*platform_directory_init_scan(const char *dir_path,
|
|
size_t *out_count))[STK_PATH_MAX]
|
|
{
|
|
size_t count = 0, i = 0, name_len;
|
|
char(*list)[STK_PATH_MAX] = NULL;
|
|
#ifdef _WIN32
|
|
WIN32_FIND_DATAA fd;
|
|
HANDLE h;
|
|
char s[STK_PATH_MAX_OS];
|
|
|
|
sprintf(s, "%s\\*", dir_path);
|
|
h = FindFirstFileA(s, &fd);
|
|
if (h == INVALID_HANDLE_VALUE)
|
|
goto create_and_exit;
|
|
|
|
do {
|
|
if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
|
|
continue;
|
|
if (is_valid_module_file(fd.cFileName))
|
|
count++;
|
|
} while (FindNextFileA(h, &fd));
|
|
|
|
FindClose(h);
|
|
|
|
if (count == 0)
|
|
goto exit;
|
|
|
|
list = malloc(count * sizeof(*list));
|
|
if (!list)
|
|
goto exit;
|
|
|
|
h = FindFirstFileA(s, &fd);
|
|
if (h == INVALID_HANDLE_VALUE)
|
|
goto exit;
|
|
|
|
do {
|
|
if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
|
|
continue;
|
|
if (is_valid_module_file(fd.cFileName) && i < count) {
|
|
name_len = strlen(fd.cFileName);
|
|
if (name_len >= STK_PATH_MAX)
|
|
name_len = STK_PATH_MAX - 1;
|
|
|
|
memcpy(list[i], fd.cFileName, name_len);
|
|
list[i][name_len] = '\0';
|
|
i++;
|
|
}
|
|
} while (FindNextFileA(h, &fd));
|
|
|
|
FindClose(h);
|
|
goto exit;
|
|
|
|
create_and_exit:
|
|
platform_mkdir(dir_path);
|
|
exit:
|
|
*out_count = i;
|
|
return list;
|
|
#else
|
|
DIR *d;
|
|
struct dirent *e;
|
|
struct stat st;
|
|
char f[STK_PATH_MAX_OS];
|
|
|
|
d = opendir(dir_path);
|
|
if (!d)
|
|
goto create_and_exit;
|
|
|
|
count_loop:
|
|
e = readdir(d);
|
|
if (!e)
|
|
goto count_done;
|
|
|
|
sprintf(f, "%s/%s", dir_path, e->d_name);
|
|
if (!is_valid_module_file(e->d_name))
|
|
goto count_loop;
|
|
|
|
if (stat(f, &st) != 0 || !S_ISREG(st.st_mode))
|
|
goto count_loop;
|
|
|
|
count++;
|
|
goto count_loop;
|
|
|
|
count_done:
|
|
if (count == 0)
|
|
goto close_and_exit;
|
|
|
|
rewinddir(d);
|
|
list = malloc(count * sizeof(*list));
|
|
if (!list)
|
|
goto close_and_exit;
|
|
|
|
fill_loop:
|
|
e = readdir(d);
|
|
if (!e || i >= count)
|
|
goto close_and_exit;
|
|
|
|
sprintf(f, "%s/%s", dir_path, e->d_name);
|
|
if (!is_valid_module_file(e->d_name))
|
|
goto fill_loop;
|
|
|
|
if (stat(f, &st) != 0 || !S_ISREG(st.st_mode))
|
|
goto fill_loop;
|
|
|
|
name_len = strlen(e->d_name);
|
|
if (name_len >= STK_PATH_MAX) {
|
|
name_len = STK_PATH_MAX - 1;
|
|
}
|
|
memcpy(list[i++], e->d_name, name_len);
|
|
list[i - 1][name_len] = '\0';
|
|
goto fill_loop;
|
|
|
|
create_and_exit:
|
|
platform_mkdir(dir_path);
|
|
*out_count = 0;
|
|
return NULL;
|
|
|
|
close_and_exit:
|
|
closedir(d);
|
|
*out_count = i;
|
|
return list;
|
|
#endif
|
|
}
|
|
|
|
#if !defined(__linux__) && !defined(_WIN32)
|
|
static void update_watches(platform_watch_context_t *ctx)
|
|
{
|
|
struct kevent ev;
|
|
DIR *d;
|
|
struct dirent *e;
|
|
char f[STK_PATH_MAX_OS];
|
|
size_t i, count = 0;
|
|
int fd;
|
|
int *new_fds = NULL;
|
|
|
|
for (i = 0; i < ctx->watch.k.file_fd_count; i++)
|
|
close(ctx->watch.k.file_fds[i]);
|
|
|
|
free(ctx->watch.k.file_fds);
|
|
ctx->watch.k.file_fds = NULL;
|
|
ctx->watch.k.file_fd_count = 0;
|
|
|
|
EV_SET(&ev, ctx->watch.k.dir_fd, EVFILT_VNODE,
|
|
EV_ADD | EV_ENABLE | EV_CLEAR,
|
|
NOTE_WRITE | NOTE_DELETE | NOTE_RENAME, 0, NULL);
|
|
kevent(ctx->watch.k.kq, &ev, 1, NULL, 0, NULL);
|
|
d = opendir(ctx->path);
|
|
if (!d)
|
|
return;
|
|
|
|
count_loop:
|
|
e = readdir(d);
|
|
if (!e)
|
|
goto count_done;
|
|
|
|
if (is_valid_module_file(e->d_name))
|
|
count++;
|
|
|
|
goto count_loop;
|
|
count_done:
|
|
if (count == 0)
|
|
goto cleanup;
|
|
|
|
new_fds = malloc(count * sizeof(int));
|
|
if (!new_fds)
|
|
goto cleanup;
|
|
|
|
rewinddir(d);
|
|
i = 0;
|
|
scan_loop:
|
|
e = readdir(d);
|
|
if (!e || i >= count)
|
|
goto scan_done;
|
|
|
|
if (!is_valid_module_file(e->d_name))
|
|
goto scan_loop;
|
|
|
|
sprintf(f, "%s/%s", ctx->path, e->d_name);
|
|
fd = open(f, O_RDONLY);
|
|
if (fd < 0)
|
|
goto scan_loop;
|
|
|
|
new_fds[i++] = fd;
|
|
EV_SET(&ev, fd, EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_CLEAR,
|
|
NOTE_WRITE | NOTE_ATTRIB, 0, NULL);
|
|
kevent(ctx->watch.k.kq, &ev, 1, NULL, 0, NULL);
|
|
goto scan_loop;
|
|
scan_done:
|
|
ctx->watch.k.file_fds = new_fds;
|
|
ctx->watch.k.file_fd_count = i;
|
|
cleanup:
|
|
closedir(d);
|
|
}
|
|
#endif
|
|
|
|
void *platform_directory_watch_start(const char *path)
|
|
{
|
|
#ifdef __linux__
|
|
int fd = inotify_init1(IN_NONBLOCK);
|
|
if (fd < 0)
|
|
return NULL;
|
|
|
|
inotify_add_watch(
|
|
fd, path, IN_CLOSE_WRITE | IN_DELETE | IN_MOVED_TO | IN_MOVED_FROM);
|
|
return (void *)(long)fd;
|
|
#else
|
|
#ifdef _WIN32
|
|
WIN32_FIND_DATAA fd;
|
|
HANDLE h;
|
|
char s[STK_PATH_MAX_OS];
|
|
size_t count = 0, i = 0, name_len;
|
|
#else
|
|
DIR *d;
|
|
struct dirent *e;
|
|
struct stat st;
|
|
char f[STK_PATH_MAX_OS];
|
|
size_t count = 0, i = 0;
|
|
#endif
|
|
platform_watch_context_t *ctx =
|
|
calloc(1, sizeof(platform_watch_context_t));
|
|
if (!ctx)
|
|
return NULL;
|
|
|
|
strncpy(ctx->path, path, STK_PATH_MAX - 1);
|
|
|
|
#ifdef _WIN32
|
|
ctx->watch.change_handle =
|
|
CreateFileA(path, FILE_LIST_DIRECTORY,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
|
NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
|
|
|
|
if (ctx->watch.change_handle == INVALID_HANDLE_VALUE)
|
|
goto error_cleanup;
|
|
|
|
sprintf(s, "%s\\*", path);
|
|
h = FindFirstFileA(s, &fd);
|
|
if (h == INVALID_HANDLE_VALUE)
|
|
goto error_cleanup;
|
|
|
|
do {
|
|
if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
|
|
is_valid_module_file(fd.cFileName))
|
|
count++;
|
|
} while (FindNextFileA(h, &fd));
|
|
|
|
FindClose(h);
|
|
if (count == 0)
|
|
goto done;
|
|
|
|
ctx->snaps = malloc(count * sizeof(platform_snapshot_t));
|
|
if (!ctx->snaps)
|
|
goto error_cleanup;
|
|
|
|
h = FindFirstFileA(s, &fd);
|
|
if (h == INVALID_HANDLE_VALUE)
|
|
goto error_cleanup;
|
|
|
|
do {
|
|
if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
|
|
is_valid_module_file(fd.cFileName) && i < count) {
|
|
name_len = strlen(fd.cFileName);
|
|
if (name_len >= STK_PATH_MAX)
|
|
name_len = STK_PATH_MAX - 1;
|
|
memcpy(ctx->snaps[i].filename, fd.cFileName, name_len);
|
|
ctx->snaps[i].filename[name_len] = '\0';
|
|
ctx->snaps[i].mtime = fd.ftLastWriteTime;
|
|
i++;
|
|
}
|
|
} while (FindNextFileA(h, &fd));
|
|
|
|
FindClose(h);
|
|
ctx->count = i;
|
|
|
|
#else
|
|
ctx->watch.k.kq = kqueue();
|
|
ctx->watch.k.dir_fd = open(path, O_RDONLY);
|
|
d = opendir(path);
|
|
if (!d)
|
|
goto bsd_setup;
|
|
|
|
bsd_count_loop:
|
|
e = readdir(d);
|
|
if (!e)
|
|
goto bsd_count_done;
|
|
|
|
sprintf(f, "%s/%s", path, e->d_name);
|
|
if (!is_valid_module_file(e->d_name))
|
|
goto bsd_count_loop;
|
|
|
|
if (stat(f, &st) != 0 || !S_ISREG(st.st_mode))
|
|
goto bsd_count_loop;
|
|
|
|
count++;
|
|
goto bsd_count_loop;
|
|
|
|
bsd_count_done:
|
|
if (count == 0)
|
|
goto bsd_setup;
|
|
|
|
ctx->snaps = malloc(count * sizeof(platform_snapshot_t));
|
|
if (!ctx->snaps)
|
|
goto bsd_setup;
|
|
|
|
rewinddir(d);
|
|
bsd_scan_loop:
|
|
e = readdir(d);
|
|
if (!e || i >= count)
|
|
goto bsd_scan_done;
|
|
|
|
sprintf(f, "%s/%s", path, e->d_name);
|
|
if (!is_valid_module_file(e->d_name))
|
|
goto bsd_scan_loop;
|
|
|
|
if (stat(f, &st) != 0 || !S_ISREG(st.st_mode))
|
|
goto bsd_scan_loop;
|
|
|
|
strncpy(ctx->snaps[i].filename, e->d_name, STK_PATH_MAX - 1);
|
|
ctx->snaps[i].mtime = st.st_mtime;
|
|
i++;
|
|
goto bsd_scan_loop;
|
|
|
|
bsd_scan_done:
|
|
closedir(d);
|
|
ctx->count = i;
|
|
bsd_setup:
|
|
update_watches(ctx);
|
|
#endif
|
|
|
|
#ifdef _WIN32
|
|
done:
|
|
#endif
|
|
return ctx;
|
|
|
|
#ifdef _WIN32
|
|
error_cleanup:
|
|
if (ctx) {
|
|
if (ctx->watch.change_handle != INVALID_HANDLE_VALUE)
|
|
CloseHandle(ctx->watch.change_handle);
|
|
free(ctx->snaps);
|
|
free(ctx);
|
|
}
|
|
return NULL;
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
void platform_directory_watch_stop(void *handle)
|
|
{
|
|
#if defined(__linux__)
|
|
if (handle)
|
|
close((int)(long)handle);
|
|
#else
|
|
#ifndef _WIN32
|
|
size_t i;
|
|
#endif
|
|
platform_watch_context_t *ctx = (platform_watch_context_t *)handle;
|
|
if (!ctx)
|
|
return;
|
|
#ifdef _WIN32
|
|
CloseHandle(ctx->watch.change_handle);
|
|
#else
|
|
for (i = 0; i < ctx->watch.k.file_fd_count; i++)
|
|
close(ctx->watch.k.file_fds[i]);
|
|
free(ctx->watch.k.file_fds);
|
|
close(ctx->watch.k.kq);
|
|
close(ctx->watch.k.dir_fd);
|
|
#endif
|
|
free(ctx->snaps);
|
|
free(ctx);
|
|
#endif
|
|
}
|
|
|
|
stk_module_event_t *platform_directory_watch_check(
|
|
void *handle, char (**file_list)[STK_PATH_MAX], size_t *out_count,
|
|
char (*loaded_ids)[STK_MOD_ID_BUFFER], const size_t loaded_count)
|
|
{
|
|
#if defined(__linux__)
|
|
int fd = (int)(long)handle;
|
|
char buf[STK_EVENT_BUFFER];
|
|
ssize_t len;
|
|
size_t index = 0, count = 0, i;
|
|
stk_module_event_t *evs;
|
|
char *ptr, *end;
|
|
struct inotify_event *e;
|
|
int event_type;
|
|
|
|
len = read(fd, buf, sizeof(buf));
|
|
if (len <= 0) {
|
|
*out_count = 0;
|
|
return NULL;
|
|
}
|
|
|
|
ptr = buf;
|
|
end = buf + len;
|
|
while (ptr < end) {
|
|
e = (struct inotify_event *)ptr;
|
|
if (e->len && is_valid_module_file(e->name))
|
|
count++;
|
|
ptr += sizeof(struct inotify_event) + e->len;
|
|
}
|
|
|
|
if (count == 0) {
|
|
*out_count = 0;
|
|
return NULL;
|
|
}
|
|
|
|
evs = malloc(count * sizeof(stk_module_event_t));
|
|
if (!evs) {
|
|
*out_count = 0;
|
|
return NULL;
|
|
}
|
|
|
|
*file_list = malloc(count * sizeof(**file_list));
|
|
if (!*file_list) {
|
|
free(evs);
|
|
*out_count = 0;
|
|
return NULL;
|
|
}
|
|
|
|
ptr = buf;
|
|
while (ptr < end && index < count) {
|
|
e = (struct inotify_event *)ptr;
|
|
if (e->len && is_valid_module_file(e->name)) {
|
|
if (e->mask & (IN_DELETE | IN_MOVED_FROM)) {
|
|
char *ptr2 =
|
|
ptr + sizeof(struct inotify_event) + e->len;
|
|
int has_create = 0;
|
|
|
|
while (ptr2 < end) {
|
|
struct inotify_event *e2 =
|
|
(struct inotify_event *)ptr2;
|
|
if (e2->len &&
|
|
strcmp(e->name, e2->name) == 0 &&
|
|
(e2->mask &
|
|
(IN_CLOSE_WRITE | IN_MOVED_TO))) {
|
|
has_create = 1;
|
|
break;
|
|
}
|
|
ptr2 += sizeof(struct inotify_event) +
|
|
e2->len;
|
|
}
|
|
|
|
if (has_create) {
|
|
ptr += sizeof(struct inotify_event) +
|
|
e->len;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
strncpy((*file_list)[index], e->name, STK_PATH_MAX - 1);
|
|
|
|
event_type = STK_MOD_UNLOAD;
|
|
if (e->mask & (IN_CLOSE_WRITE | IN_MOVED_TO)) {
|
|
char event_module_name[STK_MOD_ID_BUFFER];
|
|
extract_module_id(e->name, event_module_name);
|
|
|
|
if (is_mod_loaded(event_module_name) >= 0)
|
|
event_type = STK_MOD_RELOAD;
|
|
else
|
|
event_type = STK_MOD_LOAD;
|
|
}
|
|
|
|
evs[index++] = event_type;
|
|
}
|
|
|
|
ptr += sizeof(struct inotify_event) + e->len;
|
|
}
|
|
|
|
for (i = 0; i < index; ++i) {
|
|
size_t j;
|
|
for (j = i + 1; j < index; ++j) {
|
|
if (strcmp((*file_list)[i], (*file_list)[j]) == 0) {
|
|
evs[i] = -1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
*out_count = index;
|
|
return evs;
|
|
|
|
#else
|
|
platform_watch_context_t *ctx = (platform_watch_context_t *)handle;
|
|
platform_snapshot_t *new_snaps = NULL;
|
|
size_t new_count = 0, i, j, ev_index = 0, name_len;
|
|
stk_module_event_t *evs = NULL;
|
|
int found;
|
|
#ifdef _WIN32
|
|
WIN32_FIND_DATAA fd;
|
|
HANDLE h;
|
|
char s[STK_PATH_MAX_OS];
|
|
size_t count = 0;
|
|
|
|
sprintf(s, "%s\\*", ctx->path);
|
|
h = FindFirstFileA(s, &fd);
|
|
if (h == INVALID_HANDLE_VALUE)
|
|
goto build_diff;
|
|
|
|
do {
|
|
if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
|
|
is_valid_module_file(fd.cFileName))
|
|
count++;
|
|
} while (FindNextFileA(h, &fd));
|
|
|
|
FindClose(h);
|
|
if (count == 0)
|
|
goto build_diff;
|
|
|
|
new_snaps = malloc(count * sizeof(platform_snapshot_t));
|
|
if (!new_snaps)
|
|
goto build_diff;
|
|
|
|
h = FindFirstFileA(s, &fd);
|
|
if (h == INVALID_HANDLE_VALUE) {
|
|
free(new_snaps);
|
|
new_snaps = NULL;
|
|
goto build_diff;
|
|
}
|
|
|
|
do {
|
|
if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
|
|
is_valid_module_file(fd.cFileName) && new_count < count) {
|
|
name_len = strlen(fd.cFileName);
|
|
if (name_len >= STK_PATH_MAX)
|
|
name_len = STK_PATH_MAX - 1;
|
|
|
|
memcpy(new_snaps[new_count].filename, fd.cFileName,
|
|
name_len);
|
|
new_snaps[new_count].filename[name_len] = '\0';
|
|
new_snaps[new_count].mtime = fd.ftLastWriteTime;
|
|
new_count++;
|
|
}
|
|
} while (FindNextFileA(h, &fd));
|
|
|
|
FindClose(h);
|
|
#else
|
|
struct kevent kev;
|
|
struct timespec ts = {0, 0};
|
|
DIR *d;
|
|
struct dirent *e;
|
|
struct stat st;
|
|
char f[STK_PATH_MAX_OS];
|
|
size_t count = 0;
|
|
|
|
if (kevent(ctx->watch.k.kq, NULL, 0, &kev, 1, &ts) <= 0)
|
|
goto no_change;
|
|
|
|
d = opendir(ctx->path);
|
|
if (!d)
|
|
goto bsd_update;
|
|
|
|
bsd_count_loop:
|
|
e = readdir(d);
|
|
if (!e)
|
|
goto bsd_count_done;
|
|
|
|
if (!is_valid_module_file(e->d_name))
|
|
goto bsd_count_loop;
|
|
|
|
sprintf(f, "%s/%s", ctx->path, e->d_name);
|
|
if (stat(f, &st) != 0 || !S_ISREG(st.st_mode))
|
|
goto bsd_count_loop;
|
|
|
|
count++;
|
|
goto bsd_count_loop;
|
|
|
|
bsd_count_done:
|
|
if (count == 0) {
|
|
closedir(d);
|
|
goto bsd_update;
|
|
}
|
|
|
|
new_snaps = malloc(count * sizeof(platform_snapshot_t));
|
|
if (!new_snaps) {
|
|
closedir(d);
|
|
goto bsd_update;
|
|
}
|
|
|
|
rewinddir(d);
|
|
bsd_snap_loop:
|
|
e = readdir(d);
|
|
if (!e || new_count >= count)
|
|
goto bsd_snap_done;
|
|
|
|
if (!is_valid_module_file(e->d_name))
|
|
goto bsd_snap_loop;
|
|
|
|
sprintf(f, "%s/%s", ctx->path, e->d_name);
|
|
if (stat(f, &st) != 0 || !S_ISREG(st.st_mode))
|
|
goto bsd_snap_loop;
|
|
|
|
strncpy(new_snaps[new_count].filename, e->d_name, STK_PATH_MAX - 1);
|
|
new_snaps[new_count].mtime = st.st_mtime;
|
|
new_count++;
|
|
goto bsd_snap_loop;
|
|
bsd_snap_done:
|
|
closedir(d);
|
|
bsd_update:
|
|
update_watches(ctx);
|
|
#endif
|
|
|
|
#ifdef _WIN32
|
|
build_diff:
|
|
#endif
|
|
evs = malloc((ctx->count + new_count + 1) * sizeof(stk_module_event_t));
|
|
*file_list = malloc((ctx->count + new_count + 1) * sizeof(**file_list));
|
|
if (!evs || !*file_list)
|
|
goto cleanup_error;
|
|
|
|
for (i = 0; i < ctx->count; i++) {
|
|
found = 0;
|
|
for (j = 0; j < new_count; j++) {
|
|
if (strcmp(ctx->snaps[i].filename,
|
|
new_snaps[j].filename) != 0)
|
|
continue;
|
|
|
|
found = 1;
|
|
#ifdef _WIN32
|
|
if (CompareFileTime(&ctx->snaps[i].mtime,
|
|
&new_snaps[j].mtime) != 0) {
|
|
if (is_file_ready(ctx->path,
|
|
new_snaps[j].filename)) {
|
|
name_len =
|
|
strlen(new_snaps[j].filename);
|
|
if (name_len >= STK_PATH_MAX)
|
|
name_len = STK_PATH_MAX - 1;
|
|
memcpy((*file_list)[ev_index],
|
|
new_snaps[j].filename, name_len);
|
|
(*file_list)[ev_index][name_len] = '\0';
|
|
evs[ev_index++] = STK_MOD_RELOAD;
|
|
} else {
|
|
new_snaps[j].mtime =
|
|
ctx->snaps[i].mtime;
|
|
}
|
|
}
|
|
#else
|
|
if (ctx->snaps[i].mtime != new_snaps[j].mtime) {
|
|
if (is_file_ready(ctx->path,
|
|
new_snaps[j].filename)) {
|
|
name_len =
|
|
strlen(ctx->snaps[i].filename);
|
|
if (name_len >= STK_PATH_MAX)
|
|
name_len = STK_PATH_MAX - 1;
|
|
memcpy((*file_list)[ev_index],
|
|
ctx->snaps[i].filename,
|
|
name_len);
|
|
(*file_list)[ev_index][name_len] = '\0';
|
|
evs[ev_index++] = STK_MOD_RELOAD;
|
|
} else {
|
|
new_snaps[j].mtime =
|
|
ctx->snaps[i].mtime;
|
|
}
|
|
}
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
if (!found) {
|
|
name_len = strlen(ctx->snaps[i].filename);
|
|
if (name_len >= STK_PATH_MAX)
|
|
name_len = STK_PATH_MAX - 1;
|
|
memcpy((*file_list)[ev_index], ctx->snaps[i].filename,
|
|
name_len);
|
|
(*file_list)[ev_index][name_len] = '\0';
|
|
evs[ev_index++] = STK_MOD_UNLOAD;
|
|
}
|
|
}
|
|
|
|
for (j = 0; j < new_count; j++) {
|
|
found = 0;
|
|
for (i = 0; i < ctx->count; i++) {
|
|
if (strcmp(new_snaps[j].filename,
|
|
ctx->snaps[i].filename) == 0) {
|
|
found = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found) {
|
|
name_len = strlen(new_snaps[j].filename);
|
|
if (name_len >= STK_PATH_MAX)
|
|
name_len = STK_PATH_MAX - 1;
|
|
memcpy((*file_list)[ev_index], new_snaps[j].filename,
|
|
name_len);
|
|
(*file_list)[ev_index][name_len] = '\0';
|
|
evs[ev_index++] = STK_MOD_LOAD;
|
|
}
|
|
}
|
|
|
|
if (ev_index == 0)
|
|
goto cleanup_empty;
|
|
|
|
free(ctx->snaps);
|
|
ctx->snaps = new_snaps;
|
|
ctx->count = new_count;
|
|
*out_count = ev_index;
|
|
return evs;
|
|
|
|
cleanup_error:
|
|
if (evs)
|
|
free(evs);
|
|
|
|
if (*file_list)
|
|
free(*file_list);
|
|
|
|
cleanup_empty:
|
|
free(new_snaps);
|
|
|
|
#ifndef _WIN32
|
|
no_change:
|
|
#endif
|
|
*out_count = 0;
|
|
return NULL;
|
|
#endif
|
|
}
|