file tracking system revision 2

This commit is contained in:
Allen Webster 2016-08-25 21:42:46 -04:00
parent 6e9bcb4e36
commit 426b8d5576
5 changed files with 177 additions and 1213 deletions

View File

@ -39,18 +39,11 @@ uhash_equal(Unique_Hash a, Unique_Hash b){
#define Sys_Set_File_List_Sig(name) void name(File_List *file_list, String directory)
typedef Sys_Set_File_List_Sig(System_Set_File_List);
enum{
TrackFileFlag_ExistingOrFail = 0x0,
TrackFileFlag_NewOrFail = 0x1,
TrackFileFlag_NewAlways = 0x2,
TrackFileFlag_ExistingOrNew = 0x3,
};
#define Sys_Add_Listener_Sig(name) Unique_Hash name(char *filename)
typedef Sys_Add_Listener_Sig(System_Track_File);
#define Sys_Track_File_Sig(name) Unique_Hash name(char *filename, u32 flags)
typedef Sys_Track_File_Sig(System_Track_File);
#define Sys_Untrack_File_Sig(name) i32 name(Unique_Hash index)
typedef Sys_Untrack_File_Sig(System_Untrack_File);
#define Sys_Remove_Listener_Sig(name) i32 name(char *filename)
typedef Sys_Remove_Listener_Sig(System_Untrack_File);
#define Sys_Get_File_Index_Sig(name) i32 name(char *filename, Unique_Hash *index)
typedef Sys_Get_File_Index_Sig(System_Get_File_Index);

View File

@ -23,9 +23,6 @@ enum{
FileTrack_MemoryTooSmall,
FileTrack_OutOfTableMemory,
FileTrack_OutOfListenerMemory,
FileTrack_FileNotFound,
FileTrack_FileAlreadyTracked,
FileTrack_FileNotTracked,
FileTrack_NoMoreEvents,
FileTrack_FileSystemError
};
@ -34,30 +31,7 @@ typedef struct{
uint8_t opaque[128];
} File_Track_System;
typedef struct{
uint8_t opaque[16];
} File_Temp_Handle;
typedef struct{
uint32_t id[4];
} File_Index;
static File_Index
zero_file_index(){
File_Index a = {0};
return(a);
}
static int32_t
file_index_eq(File_Index a, File_Index b){
return ((a.id[0] == b.id[0]) &&
(a.id[1] == b.id[1]) &&
(a.id[2] == b.id[2]) &&
(a.id[3] == b.id[3]));
}
typedef int32_t File_Track_Result;
typedef uint64_t File_Time;
File_Track_Result
init_track_system(File_Track_System *system,
@ -65,34 +39,10 @@ init_track_system(File_Track_System *system,
void *listener_memory, int32_t listener_memory_size);
File_Track_Result
begin_tracking_file(File_Track_System *system, char *name, File_Index *index, File_Time *time);
add_listener(File_Track_System *system, char *filename);
File_Track_Result
begin_tracking_new_file(File_Track_System *system, char *name, File_Index *index, File_Time *time);
File_Track_Result
get_file_temp_handle(char *name, File_Temp_Handle *handle);
File_Track_Result
begin_tracking_from_handle(File_Track_System *system, char *name, File_Temp_Handle handle, File_Index *index, File_Time *time);
File_Track_Result
finish_with_temp_handle(File_Temp_Handle handle);
File_Track_Result
get_tracked_file_index(File_Track_System *system, char *name, File_Index *index);
File_Track_Result
stop_tracking_file(File_Track_System *system, File_Index index);
File_Track_Result
count_tracked_files(File_Track_System *system, int32_t *count);
File_Track_Result
get_tracked_file_time(File_Track_System *system, File_Index index, File_Time *time);
File_Track_Result
get_file_time_now(File_Time *time);
remove_listener(File_Track_System *system, char *filename);
File_Track_Result
move_track_system(File_Track_System *system, void *mem, int32_t size);
@ -101,21 +51,7 @@ File_Track_Result
expand_track_system_listeners(File_Track_System *system, void *mem, int32_t size);
File_Track_Result
get_change_event(File_Track_System *system, File_Index *index);
File_Track_Result
get_tracked_file_size(File_Track_System *system, File_Index index, uint32_t *size);
File_Track_Result
get_tracked_file_data(File_Track_System *system, File_Index index, void *mem, uint32_t size);
File_Track_Result
rewrite_tracked_file(File_Track_System *system, File_Index index,
void *data, int32_t size, File_Time *time);
File_Track_Result
rewrite_arbitrary_file(File_Track_System *system, char *filename,
void *data, int32_t size, File_Time *time);
get_change_event(File_Track_System *system, char *buffer, int32_t max);
File_Track_Result
shut_down_track_system(File_Track_System *system);

View File

@ -29,6 +29,24 @@ Created on: 20.07.2016
# define NotImplemented Assert(!"not implemented")
#endif
typedef struct{
uint32_t id[4];
} File_Index;
static File_Index
zero_file_index(){
File_Index a = {0};
return(a);
}
static int32_t
file_index_eq(File_Index a, File_Index b){
return ((a.id[0] == b.id[0]) &&
(a.id[1] == b.id[1]) &&
(a.id[2] == b.id[2]) &&
(a.id[3] == b.id[3]));
}
typedef uint32_t rptr32;
typedef struct DLL_Node {
@ -42,6 +60,7 @@ typedef struct {
int32_t user_count;
char dir_name[512];
int32_t dir_name_len;
// TODO(allen): I am only ever using one thread
// for reading results. So is it possible to
@ -58,7 +77,6 @@ typedef struct {
typedef struct {
HANDLE iocp;
HANDLE thread;
CRITICAL_SECTION table_lock;
void *tables;
@ -69,32 +87,17 @@ typedef struct {
typedef struct {
int32_t size;
uint32_t tracked_count;
uint32_t tracked_dir_count;
uint32_t max;
uint32_t change_write_pos;
uint32_t change_read_pos;
rptr32 change_queue;
rptr32 file_table;
} File_Track_Tables;
typedef struct {
HANDLE file, dir;
HANDLE dir;
File_Index hash;
union {
Directory_Listener_Node *listener_node;
struct {
int32_t change_pos;
int32_t skip_change;
};
};
Directory_Listener_Node *listener_node;
} File_Track_Entry;
typedef struct {
File_Index index;
int32_t still_active;
} File_Change_Record;
#define FILE_ENTRY_COST (sizeof(File_Change_Record) + sizeof(File_Track_Entry))
#define FILE_ENTRY_COST (sizeof(File_Track_Entry))
#define to_vars_(s) ((File_Track_Vars*)(s))
#define to_tables(v) ((File_Track_Tables*)(v->tables))
@ -220,12 +223,7 @@ get_file_entry(File_Track_Tables *tables, File_Index index){
return(entry);
}
File_Track_Result
internal_get_tracked_file_index(File_Track_Tables *tables,
char *name,
File_Index *index,
File_Track_Entry **entry);
#if 0
static DWORD
directory_watching(LPVOID ptr){
File_Track_Vars *vars = to_vars_(ptr);
@ -341,6 +339,7 @@ directory_watching(LPVOID ptr){
}
}
}
#endif
File_Track_Result
init_track_system(File_Track_System *system,
@ -359,14 +358,11 @@ init_track_system(File_Track_System *system,
{
tables->size = table_memory_size;
tables->tracked_count = 0;
tables->tracked_dir_count = 0;
int32_t likely_entry_size = FILE_ENTRY_COST;
int32_t max_number_of_entries = (table_memory_size - sizeof(*tables)) / likely_entry_size;
tables->change_queue = sizeof(*tables);
tables->file_table = tables->change_queue +
sizeof(File_Change_Record)*max_number_of_entries;
tables->file_table = sizeof(*tables);
tables->max = max_number_of_entries;
}
@ -381,16 +377,10 @@ init_track_system(File_Track_System *system,
}
}
// NOTE(allen): Launch the file watching thread
// NOTE(allen): Prepare the file tracking synchronization objects.
{
InitializeCriticalSection(&vars->table_lock);
vars->iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 1);
vars->thread = CreateThread(0, 0,
directory_watching,
system,
0, 0);
}
result = FileTrack_Good;
@ -432,12 +422,6 @@ internal_get_file_index(BY_HANDLE_FILE_INFORMATION info){
return(hash);
}
static void
internal_set_time(File_Time *time, BY_HANDLE_FILE_INFORMATION info){
*time = (((uint64_t)info.ftLastWriteTime.dwHighDateTime << 32) |
(info.ftLastWriteTime.dwLowDateTime));
}
static void
internal_free_slot(File_Track_Tables *tables, File_Track_Entry *entry){
Assert(!entry_is_available(entry));
@ -452,328 +436,138 @@ internal_free_slot(File_Track_Tables *tables, File_Track_Entry *entry){
}
File_Track_Result
internal_track_file(File_Track_Vars *vars, File_Track_Tables *tables,
char *name, HANDLE file, File_Index *index, File_Time *time){
add_listener(File_Track_System *system, char *filename){
File_Track_Result result = FileTrack_Good;
File_Track_Vars *vars = to_vars_(system);
if (file != INVALID_HANDLE_VALUE){
EnterCriticalSection(&vars->table_lock);
{
File_Track_Tables *tables = to_tables(vars);
// TODO(allen): make this real!
char dir_name[1024];
int32_t dir_name_len =
internal_get_parent_name(dir_name, sizeof(dir_name), name);
internal_get_parent_name(dir_name, sizeof(dir_name), filename);
HANDLE dir = CreateFile(
dir_name,
FILE_LIST_DIRECTORY,
FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
0,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
0);
if (dir != INVALID_HANDLE_VALUE){
BY_HANDLE_FILE_INFORMATION info = {0};
BY_HANDLE_FILE_INFORMATION dir_info = {0};
DWORD getinfo_result = GetFileInformationByHandle(dir, &dir_info);
if (GetFileInformationByHandle(dir, &dir_info)){
if (getinfo_result){
File_Index dir_hash = internal_get_file_index(dir_info);
File_Lookup_Result dir_lookup = tracking_system_lookup_entry(tables, dir_hash);
if (GetFileInformationByHandle(file, &info)){
File_Index hash = internal_get_file_index(info);
if (entry_is_available(dir_lookup.entry)){
if (tracking_system_has_space(tables, 2)){
Directory_Listener_Node *node = (Directory_Listener_Node*)
allocate_node(&vars->free_sentinel);
if (node){
if (CreateIoCompletionPort(dir, vars->iocp, (ULONG_PTR)node, 1)){
ZeroStruct(node->listener.overlapped);
if (ReadDirectoryChangesW(dir,
node->listener.result,
sizeof(node->listener.result),
0,
FILE_NOTIFY_CHANGE_LAST_WRITE,
0,
&node->listener.overlapped,
0)){
node->listener.dir = dir;
node->listener.user_count = 1;
// TODO(allen): make this real!
Assert(dir_name_len < sizeof(node->listener.dir_name));
for (int32_t i = 0; i < dir_name_len; ++i){
node->listener.dir_name[i] = dir_name[i];
}
node->listener.dir_name[dir_name_len] = 0;
dir_lookup.entry->hash = dir_hash;
dir_lookup.entry->file = dir;
dir_lookup.entry->dir = dir;
dir_lookup.entry->listener_node = node;
++tables->tracked_count;
++tables->tracked_dir_count;
File_Lookup_Result lookup =
tracking_system_lookup_entry(tables, hash);
Assert(entry_is_available(lookup.entry));
lookup.entry->hash = hash;
lookup.entry->file = file;
lookup.entry->dir = node->listener.dir;
lookup.entry->change_pos = -1;
++tables->tracked_count;
*index = hash;
internal_set_time(time, info);
}
else{
result = FileTrack_FileSystemError;
if (entry_is_available(dir_lookup.entry)){
if (tracking_system_has_space(tables, 1)){
Directory_Listener_Node *node = (Directory_Listener_Node*)
allocate_node(&vars->free_sentinel);
if (node){
if (CreateIoCompletionPort(dir, vars->iocp, (ULONG_PTR)node, 1)){
ZeroStruct(node->listener.overlapped);
if (ReadDirectoryChangesW(dir,
node->listener.result,
sizeof(node->listener.result),
0,
FILE_NOTIFY_CHANGE_LAST_WRITE,
0,
&node->listener.overlapped,
0)){
node->listener.dir = dir;
node->listener.user_count = 1;
// TODO(allen): make this real!
Assert(dir_name_len < sizeof(node->listener.dir_name));
for (int32_t i = 0; i < dir_name_len; ++i){
node->listener.dir_name[i] = dir_name[i];
}
node->listener.dir_name[dir_name_len] = 0;
node->listener.dir_name_len = dir_name_len;
dir_lookup.entry->hash = dir_hash;
dir_lookup.entry->dir = dir;
dir_lookup.entry->listener_node = node;
++tables->tracked_count;
}
else{
result = FileTrack_FileSystemError;
}
if (result != FileTrack_Good){
insert_node(&vars->free_sentinel, &node->node);
}
}
else{
result = FileTrack_OutOfListenerMemory;
result = FileTrack_FileSystemError;
}
if (result != FileTrack_Good){
insert_node(&vars->free_sentinel, &node->node);
}
}
else{
result = FileTrack_OutOfTableMemory;
result = FileTrack_OutOfListenerMemory;
}
}
else{
if (tracking_system_has_space(tables, 1)){
File_Lookup_Result lookup = tracking_system_lookup_entry(tables, hash);
if (entry_is_available(lookup.entry)){
Directory_Listener_Node *node = dir_lookup.entry->listener_node;
++node->listener.user_count;
lookup.entry->hash = hash;
lookup.entry->file = file;
lookup.entry->dir = node->listener.dir;
lookup.entry->change_pos = -1;
++tables->tracked_count;
*index = hash;
internal_set_time(time, info);
}
else{
result = FileTrack_FileAlreadyTracked;
}
}
else{
result = FileTrack_OutOfTableMemory;
}
result = FileTrack_OutOfTableMemory;
}
}
else{
result = FileTrack_FileSystemError;
Directory_Listener_Node *node = dir_lookup.entry->listener_node;
++node->listener.user_count;
}
}
else{
result = FileTrack_FileSystemError;
}
if (result != FileTrack_Good && dir != 0){
CloseHandle(dir);
}
}
else{
result = FileTrack_FileSystemError;
}
if (result != FileTrack_Good && dir != 0 && dir != INVALID_HANDLE_VALUE){
CloseHandle(dir);
}
}
else{
result = FileTrack_FileNotFound;
}
LeaveCriticalSection(&vars->table_lock);
return(result);
}
HANDLE
open_file_best_privledges(char *name, DWORD open_disposition){
// TODO(allen): Try multiple ways to open the
// file and get as many privledges as possible.
// Expand the API to be capable of reporting
// what privledges are available on a file.
HANDLE file = CreateFile(
name,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
0,
open_disposition,
FILE_ATTRIBUTE_NORMAL,
0);
return(file);
}
File_Track_Result
begin_tracking_file(File_Track_System *system,
char *name,
File_Index *index,
File_Time *time){
remove_listener(File_Track_System *system, char *filename){
File_Track_Result result = FileTrack_Good;
File_Track_Vars *vars = to_vars_(system);
EnterCriticalSection(&vars->table_lock);
{
File_Track_Tables *tables = to_tables(vars);
HANDLE file = open_file_best_privledges(name, OPEN_EXISTING);
result = internal_track_file(vars, tables, name, file, index, time);
if (file != INVALID_HANDLE_VALUE && result != FileTrack_Good){
CloseHandle(file);
}
}
LeaveCriticalSection(&vars->table_lock);
return(result);
}
File_Track_Result
begin_tracking_new_file(File_Track_System *system, char *name, File_Index *index, File_Time *time){
File_Track_Result result = FileTrack_Good;
File_Track_Vars *vars = to_vars_(system);
EnterCriticalSection(&vars->table_lock);
{
File_Track_Tables *tables = to_tables(vars);
HANDLE file = open_file_best_privledges(name, CREATE_ALWAYS);
result = internal_track_file(vars, tables, name, file, index, time);
if (file != INVALID_HANDLE_VALUE && result != FileTrack_Good){
CloseHandle(file);
}
}
LeaveCriticalSection(&vars->table_lock);
return(result);
}
File_Track_Result
get_file_temp_handle(char *name, File_Temp_Handle *handle){
File_Track_Result result = FileTrack_FileNotFound;
HANDLE h = open_file_best_privledges(name, OPEN_EXISTING);
if (h != INVALID_HANDLE_VALUE){
*(HANDLE*)handle = h;
result = FileTrack_Good;
}
return(result);
}
File_Track_Result
begin_tracking_from_handle(File_Track_System *system, char *name, File_Temp_Handle handle,
File_Index *index, File_Time *time){
File_Track_Result result = FileTrack_Good;
File_Track_Vars *vars = to_vars_(system);
EnterCriticalSection(&vars->table_lock);
{
File_Track_Tables *tables = to_tables(vars);
HANDLE file = *(HANDLE*)(&handle);
result = internal_track_file(vars, tables, name, file, index, time);
}
LeaveCriticalSection(&vars->table_lock);
return(result);
}
File_Track_Result
finish_with_temp_handle(File_Temp_Handle handle){
File_Track_Result result = FileTrack_Good;
HANDLE file = *(HANDLE*)(&handle);
CloseHandle(file);
return(result);
}
File_Track_Result
internal_get_tracked_file_index(File_Track_Tables *tables,
char *name,
File_Index *index,
File_Track_Entry **entry){
File_Track_Result result = FileTrack_Good;
// TODO(allen): Can I open this file with no permissions?
HANDLE file = CreateFile(
name,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
0,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
0);
if (file != INVALID_HANDLE_VALUE){
BY_HANDLE_FILE_INFORMATION info = {0};
if (GetFileInformationByHandle(file, &info)){
File_Index hash = internal_get_file_index(info);
File_Lookup_Result lookup = tracking_system_lookup_entry(tables, hash);
if (!entry_is_available(lookup.entry)){
*index = hash;
*entry = lookup.entry;
}
else{
result = FileTrack_FileNotTracked;
}
}
else{
result = FileTrack_FileSystemError;
}
CloseHandle(file);
}
else{
result = FileTrack_FileNotFound;
}
return(result);
}
File_Track_Result
get_tracked_file_index(File_Track_System *system,
char *name,
File_Index *index){
File_Track_Result result = FileTrack_Good;
File_Track_Vars *vars = to_vars_(system);
File_Track_Tables *tables = 0;
File_Track_Entry *entry = 0;
EnterCriticalSection(&vars->table_lock);
tables = to_tables(vars);
result = internal_get_tracked_file_index(tables, name, index, &entry);
LeaveCriticalSection(&vars->table_lock);
return(result);
}
File_Track_Result
stop_tracking_file(File_Track_System *system, File_Index index){
File_Track_Result result = FileTrack_Good;
File_Track_Vars *vars = to_vars_(system);
EnterCriticalSection(&vars->table_lock);
File_Track_Tables *tables = to_tables(vars);
File_Track_Entry *entry = get_file_entry(tables, index);
if (entry){
HANDLE dir = entry->dir;
if (dir != entry->file){
// TODO(allen): make this real!
char dir_name[1024];
internal_get_parent_name(dir_name, sizeof(dir_name), filename);
HANDLE dir = CreateFile(
dir_name,
FILE_LIST_DIRECTORY,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
0,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
0);
if (dir != INVALID_HANDLE_VALUE){
BY_HANDLE_FILE_INFORMATION dir_info = {0};
if (GetFileInformationByHandle(dir, &dir_info)){
DWORD getinfo_result = GetFileInformationByHandle(dir, &dir_info);
if (getinfo_result){
File_Index dir_hash = internal_get_file_index(dir_info);
File_Lookup_Result dir_lookup = tracking_system_lookup_entry(tables, dir_hash);
Assert(!entry_is_available(dir_lookup.entry));
@ -782,83 +576,26 @@ stop_tracking_file(File_Track_System *system, File_Index index){
if (node->listener.user_count == 0){
insert_node(&vars->free_sentinel, &node->node);
CloseHandle(entry->dir);
CloseHandle(dir_lookup.entry->dir);
internal_free_slot(tables, dir_lookup.entry);
--tables->tracked_dir_count;
}
CloseHandle(entry->file);
internal_free_slot(tables, entry);
}
else{
result = FileTrack_FileSystemError;
}
}
else{
result = FileTrack_FileNotTracked;
}
}
else{
result = FileTrack_FileNotTracked;
}
LeaveCriticalSection(&vars->table_lock);
return(result);
}
File_Track_Result
count_tracked_files(File_Track_System *system, int32_t *count){
File_Track_Result result = FileTrack_Good;
File_Track_Tables *tables = to_tables(to_vars_(system));
*count = tables->tracked_count - tables->tracked_dir_count;
return(result);
}
File_Track_Result
get_tracked_file_time(File_Track_System *system, File_Index index, File_Time *time){
File_Track_Result result = FileTrack_Good;
File_Track_Vars *vars = to_vars_(system);
EnterCriticalSection(&vars->table_lock);
File_Track_Tables *tables = to_tables(vars);
File_Track_Entry *entry = get_file_entry(tables, index);
if (entry){
BY_HANDLE_FILE_INFORMATION info = {0};
if (GetFileInformationByHandle(entry->file, &info)){
internal_set_time(time, info);
CloseHandle(dir);
}
else{
result = FileTrack_FileSystemError;
}
}
else{
result = FileTrack_FileNotTracked;
}
LeaveCriticalSection(&vars->table_lock);
return(result);
}
File_Track_Result
get_file_time_now(File_Time *time){
File_Track_Result result = FileTrack_Good;
FILETIME file_time;
SYSTEMTIME system_time;
GetSystemTime(&system_time);
SystemTimeToFileTime(&system_time, &file_time);
*time = (((uint64_t)file_time.dwHighDateTime << 32) |
(file_time.dwLowDateTime));
return(result);
}
File_Track_Result
move_track_system(File_Track_System *system, void *mem, int32_t size){
File_Track_Result result = FileTrack_Good;
@ -878,49 +615,13 @@ move_track_system(File_Track_System *system, void *mem, int32_t size){
int32_t likely_entry_size = FILE_ENTRY_COST;
int32_t max_number_of_entries = (size - sizeof(*tables)) / likely_entry_size;
tables->change_queue = sizeof(*tables);
tables->file_table = tables->change_queue +
sizeof(File_Change_Record)*max_number_of_entries;
tables->file_table = sizeof(*tables);
tables->max = max_number_of_entries;
}
if (tables->max > original_tables->max){
uint32_t original_max = original_tables->max;
uint32_t change_shift = 0;
uint32_t change_modulo = original_max;
// NOTE(allen): Copy the original change queue
{
uint32_t start_pos = original_tables->change_read_pos;
uint32_t end_pos = original_tables->change_write_pos;
uint32_t changes_count = 0;
if (original_tables->change_write_pos < original_tables->change_read_pos){
changes_count = original_max + end_pos - start_pos;
}
else{
changes_count = end_pos - start_pos;
}
change_shift = original_max - start_pos;
File_Change_Record *src = (File_Change_Record*)
to_ptr(original_tables, original_tables->change_queue);
File_Change_Record *dst = (File_Change_Record*)
to_ptr(tables, tables->change_queue);
for (uint32_t i_dst = 0, i_src = start_pos;
i_dst < changes_count;
++i_dst, i_src = (i_src+1)%original_max){
dst[i_dst] = src[i_src];
}
tables->change_read_pos = 0;
tables->change_write_pos = changes_count;
}
// NOTE(allen): Rehash the tracking table
{
File_Track_Entry *entries = (File_Track_Entry*)
@ -937,17 +638,10 @@ move_track_system(File_Track_System *system, void *mem, int32_t size){
Assert(entry_is_available(lookup.entry));
*lookup.entry = *entry;
if (lookup.entry->file != lookup.entry->dir &&
lookup.entry->change_pos != -1){
lookup.entry->change_pos =
(lookup.entry->change_pos+change_shift)%change_modulo;
}
}
}
tables->tracked_count = original_tables->tracked_count;
tables->tracked_dir_count = original_tables->tracked_dir_count;
}
// NOTE(allen): Update to the new table
@ -990,189 +684,84 @@ expand_track_system_listeners(File_Track_System *system, void *mem, int32_t size
}
File_Track_Result
get_change_event(File_Track_System *system, File_Index *index){
get_change_event(File_Track_System *system, char *buffer, int32_t max){
File_Track_Result result = FileTrack_NoMoreEvents;
File_Track_Vars *vars = to_vars_(system);
EnterCriticalSection(&vars->table_lock);
File_Track_Tables *tables = to_tables(vars);
uint32_t write_pos = tables->change_write_pos;
uint32_t read_pos = tables->change_read_pos;
uint32_t max = tables->max;
File_Change_Record *change_queue =
(File_Change_Record*)to_ptr(tables, tables->change_queue);
while (read_pos != write_pos){
File_Change_Record *record = change_queue + read_pos;
{
OVERLAPPED *overlapped = 0;
DWORD length = 0;
ULONG_PTR key = 0;
read_pos = (read_pos + 1) % max;
if (record->still_active){
File_Lookup_Result lookup = tracking_system_lookup_entry(tables, record->index);
if (!entry_is_available(lookup.entry)){
lookup.entry->change_pos = -1;
*index = record->index;
result = FileTrack_Good;
}
break;
}
}
tables->change_write_pos = write_pos;
tables->change_read_pos = read_pos;
LeaveCriticalSection(&vars->table_lock);
return(result);
}
File_Track_Result
get_tracked_file_size(File_Track_System *system, File_Index index, uint32_t *size){
File_Track_Result result = FileTrack_Good;
File_Track_Vars *vars = to_vars_(system);
EnterCriticalSection(&vars->table_lock);
File_Track_Tables *tables = to_tables(vars);
File_Lookup_Result lookup = tracking_system_lookup_entry(tables, index);
if (!entry_is_available(lookup.entry)){
DWORD lo, hi;
lo = GetFileSize(lookup.entry->file, &hi);
Assert(hi == 0);
*size = lo;
}
else{
result = FileTrack_FileNotTracked;
}
LeaveCriticalSection(&vars->table_lock);
return(result);
}
File_Track_Result
get_tracked_file_data(File_Track_System *system, File_Index index, void *mem, uint32_t size){
File_Track_Result result = FileTrack_Good;
File_Track_Vars *vars = to_vars_(system);
EnterCriticalSection(&vars->table_lock);
File_Track_Tables *tables = to_tables(vars);
File_Lookup_Result lookup = tracking_system_lookup_entry(tables, index);
if (!entry_is_available(lookup.entry)){
DWORD read_size = 0;
if (ReadFile(lookup.entry->file, mem, size, &read_size, 0)){
if (read_size != size){
result = FileTrack_FileSystemError;
}
}
else{
result = FileTrack_FileSystemError;
}
}
else{
result = FileTrack_FileNotTracked;
}
LeaveCriticalSection(&vars->table_lock);
return(result);
}
File_Track_Result
rewrite_tracked_file(File_Track_System *system, File_Index index,
void *data, int32_t size, File_Time *time){
File_Track_Result result = FileTrack_Good;
File_Track_Vars *vars = to_vars_(system);
EnterCriticalSection(&vars->table_lock);
File_Track_Tables *tables = to_tables(vars);
File_Lookup_Result lookup = tracking_system_lookup_entry(tables, index);
if (!entry_is_available(lookup.entry)){
DWORD written = 0;
lookup.entry->skip_change = 1;
SetFilePointer(lookup.entry->file, 0, 0, FILE_BEGIN);
if (WriteFile(lookup.entry->file, data, size, &written, 0)){
FILETIME file_time;
SYSTEMTIME system_time;
if (GetQueuedCompletionStatus(vars->iocp,
&length,
&key,
&overlapped,
0)){
GetSystemTime(&system_time);
SystemTimeToFileTime(&system_time, &file_time);
SetFileTime(lookup.entry->file, 0, 0, &file_time);
Directory_Listener *listener_ptr = (Directory_Listener*)overlapped;
Directory_Listener listener = *listener_ptr;
FlushFileBuffers(lookup.entry->file);
}
else{
result = FileTrack_FileSystemError;
}
}
else{
result = FileTrack_FileNotTracked;
}
LeaveCriticalSection(&vars->table_lock);
return(result);
}
File_Track_Result
rewrite_arbitrary_file(File_Track_System *system, char *filename,
void *data, int32_t size, File_Time *time){
File_Track_Result result = FileTrack_Good;
HANDLE file = CreateFile(
filename,
GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
0,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
0);
if (file != INVALID_HANDLE_VALUE){
File_Track_Vars *vars = to_vars_(system);
EnterCriticalSection(&vars->table_lock);
{
File_Track_Tables *tables = to_tables(vars);
BY_HANDLE_FILE_INFORMATION info = {0};
if (GetFileInformationByHandle(file, &info)){
DWORD written = 0;
ZeroStruct(listener_ptr->overlapped);
ReadDirectoryChangesW(listener_ptr->dir,
listener_ptr->result,
sizeof(listener_ptr->result),
0,
FILE_NOTIFY_CHANGE_LAST_WRITE,
0,
&listener_ptr->overlapped,
0);
char *listener_buffer = listener.result;
DWORD offset = 0;
FILE_NOTIFY_INFORMATION *info = 0;
for (;;){
info = (FILE_NOTIFY_INFORMATION*)(listener_buffer + offset);
File_Index index = internal_get_file_index(info);
File_Lookup_Result lookup = tracking_system_lookup_entry(tables, index);
if (!entry_is_available(lookup.entry)){
lookup.entry->skip_change = 1;
int32_t len = info->FileNameLength / 2;
if (listener.dir_name_len + 1 + len < max){
int32_t pos = 0;
char *src = listener.dir_name;
for (int32_t i = 0; src[i]; ++i, ++pos){
buffer[pos] = src[i];
}
buffer[pos++] = '/';
for (int32_t i = 0; i < len; ++i, ++pos){
buffer[pos] = (char)info->FileName[i];
}
buffer[pos] = 0;
result = FileTrack_Good;
}
else{
// TODO(allen): Need some way to stash this result so that if the
// user comes back with more memory we can give them the change
// notification they missed.
result = FileTrack_MemoryTooSmall;
}
WriteFile(file, data, size, &written, 0);
FILETIME file_time;
SYSTEMTIME system_time;
GetSystemTime(&system_time);
SystemTimeToFileTime(&system_time, &file_time);
SetFileTime(lookup.entry->file, 0, 0, &file_time);
}
else{
result = FileTrack_FileSystemError;
if (info->NextEntryOffset != 0){
// TODO(allen): We're not ready to handle this yet.
// For now I am breaking. In the future, if there
// are more results we should stash them and return
// them in future calls.
offset += info->NextEntryOffset;
break;
}
else{
break;
}
}
}
LeaveCriticalSection(&vars->table_lock);
CloseHandle(file);
}
else{
result = FileTrack_FileNotFound;
}
LeaveCriticalSection(&vars->table_lock);
return(result);
}
@ -1193,7 +782,7 @@ shut_down_track_system(File_Track_System *system){
for (; index < max; ++index){
File_Track_Entry *entry = entries + index;
if (!entry_is_available(entry)){
if (!CloseHandle(entry->file)){
if (!CloseHandle(entry->dir)){
win32_result = 1;
}
}
@ -1202,17 +791,13 @@ shut_down_track_system(File_Track_System *system){
if (!CloseHandle(vars->iocp)){
win32_result = 1;
}
TerminateThread(vars->thread, 0);
if (!CloseHandle(vars->thread)){
win32_result = 1;
}
DeleteCriticalSection(&vars->table_lock);
if (win32_result){
result = FileTrack_FileSystemError;
}
DeleteCriticalSection(&vars->table_lock);
return(result);
}

View File

@ -1,436 +0,0 @@
/*
Copy Right FourTech LLC, 2016
All Rights Are Reserved
A test bed for a cross platform file tracking reliability layer.
Developed for the use cases in 4coder, but I anticipate that this
will be a general problem for me. - Allen Webster
Created on: 20.07.2016
*/
// TOP
#define FILE_TRACK_MAIN
#include "4tech_file_track.h"
#include "4tech_file_track_win32.c"
#include "filetrack_test.c"
#include <stdint.h>
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#define FILE_TRACK_TEST_DIR1 "w:/filetrack/data/"
#define FILE_TRACK_TEST_DIR2 "w:/filetrack/data2/"
#define FAKE_TRACK_TEST_DIR "w:/filetrack/data1000/"
#define ALT_NAME_TEST_DIR1 "c:/work/filetrack/data/"
#define ALT_NAME_TEST_DIR2 "c:/work/filetrack/data2/"
static char * test_files[] = {
FILE_TRACK_TEST_DIR1"autotab.cpp",
FILE_TRACK_TEST_DIR1"basic.cpp",
FILE_TRACK_TEST_DIR1"basic.txt",
FILE_TRACK_TEST_DIR1"cleanme.cpp",
FILE_TRACK_TEST_DIR1"emptyfile.txt",
FILE_TRACK_TEST_DIR1"lexer_test.cpp",
FILE_TRACK_TEST_DIR1"lexer_test2.cpp",
FILE_TRACK_TEST_DIR1"lexer_test3.cpp",
FILE_TRACK_TEST_DIR1"saveas.txt",
FILE_TRACK_TEST_DIR1"test_large.cpp",
FILE_TRACK_TEST_DIR2"autotab.cpp",
FILE_TRACK_TEST_DIR2"basic.cpp",
FILE_TRACK_TEST_DIR2"basic.txt",
FILE_TRACK_TEST_DIR2"cleanme.cpp",
FILE_TRACK_TEST_DIR2"emptyfile.txt",
FILE_TRACK_TEST_DIR2"lexer_test.cpp",
FILE_TRACK_TEST_DIR2"lexer_test2.cpp",
FILE_TRACK_TEST_DIR2"lexer_test3.cpp",
FILE_TRACK_TEST_DIR2"saveas.txt",
FILE_TRACK_TEST_DIR2"test_large.cpp",
};
static char * test_alt_files[] = {
ALT_NAME_TEST_DIR1"autotab.cpp",
ALT_NAME_TEST_DIR1"basic.cpp",
ALT_NAME_TEST_DIR1"basic.txt",
ALT_NAME_TEST_DIR1"cleanme.cpp",
ALT_NAME_TEST_DIR1"emptyfile.txt",
ALT_NAME_TEST_DIR1"lexer_test.cpp",
ALT_NAME_TEST_DIR1"lexer_test2.cpp",
ALT_NAME_TEST_DIR1"lexer_test3.cpp",
ALT_NAME_TEST_DIR1"saveas.txt",
ALT_NAME_TEST_DIR1"test_large.cpp",
ALT_NAME_TEST_DIR2"autotab.cpp",
ALT_NAME_TEST_DIR2"basic.cpp",
ALT_NAME_TEST_DIR2"basic.txt",
ALT_NAME_TEST_DIR2"cleanme.cpp",
ALT_NAME_TEST_DIR2"emptyfile.txt",
ALT_NAME_TEST_DIR2"lexer_test.cpp",
ALT_NAME_TEST_DIR2"lexer_test2.cpp",
ALT_NAME_TEST_DIR2"lexer_test3.cpp",
ALT_NAME_TEST_DIR2"saveas.txt",
ALT_NAME_TEST_DIR2"test_large.cpp",
};
static char * fake_files[] = {
FAKE_TRACK_TEST_DIR"autotab.cpp",
FAKE_TRACK_TEST_DIR"basic.cpp",
FAKE_TRACK_TEST_DIR"basic.txt",
FAKE_TRACK_TEST_DIR"cleanme.cpp",
FAKE_TRACK_TEST_DIR"emptyfile.txt",
FAKE_TRACK_TEST_DIR"lexer_test.cpp",
FAKE_TRACK_TEST_DIR"lexer_test2.cpp",
FAKE_TRACK_TEST_DIR"lexer_test3.cpp",
FAKE_TRACK_TEST_DIR"saveas.txt",
FAKE_TRACK_TEST_DIR"test_large.cpp",
};
#define ArrayCount(a) ((sizeof(a))/(sizeof(*a)))
typedef struct{
File_Index unique_file_index;
File_Time time;
} MyFileThing;
void test_body_A(int32_t size1, int32_t size2){
void *mem1 = malloc(size1);
void *mem2 = malloc(size2);
memset(mem1, 0, size1);
File_Track_System track = {0};
int32_t result = init_track_system(&track,
mem1, size1,
mem2, size2);
assert(result == FileTrack_Good);
MyFileThing my_file_things[1000];
memset(my_file_things, 0, sizeof(my_file_things));
// NOTE(allen): track in all the test files
for (int32_t i = 0;
i < ArrayCount(test_files);
++i){
char *filename = test_files[i];
File_Index new_file = zero_file_index();
File_Time new_time = 0;
int32_t result = begin_tracking_file(&track, filename, &new_file, &new_time);
while (result != FileTrack_Good){
switch (result){
case FileTrack_OutOfTableMemory:
{
int32_t new_mem_size = size1*2;
void *new_mem = malloc(new_mem_size);
memset(new_mem, 0, new_mem_size);
move_track_system(&track, new_mem, new_mem_size);
free(mem1);
size1 = new_mem_size;
mem1 = new_mem;
}break;
case FileTrack_OutOfListenerMemory:
{
size2 *= 2;
void *new_mem = malloc(size2);
memset(new_mem, 0, size2);
expand_track_system_listeners(&track, new_mem, size2);
}break;
default:
{
Assert(result == FileTrack_Good);
}break;
}
result = begin_tracking_file(&track, filename, &new_file, &new_time);
}
my_file_things[i].unique_file_index = new_file;
my_file_things[i].time = new_time;
}
// NOTE(allen): track in fake directories
for (int32_t i = 0;
i < ArrayCount(fake_files);
++i){
File_Index new_file = zero_file_index();
File_Time new_time = 0;
char *filename = fake_files[i];
int32_t result = begin_tracking_file(&track, filename, &new_file, &new_time);
assert(result == FileTrack_FileNotFound);
}
// NOTE(allen): track in already tracked files
for (int32_t i = 0;
i < ArrayCount(test_files);
++i){
File_Index new_file = zero_file_index();
File_Time new_time = 0;
char *filename = test_files[i];
int32_t result = begin_tracking_file(&track, filename, &new_file, &new_time);
assert(result == FileTrack_FileAlreadyTracked);
}
// NOTE(allen): track in already tracked files via alt-names
for (int32_t i = 0;
i < ArrayCount(test_alt_files);
++i){
File_Index new_file = zero_file_index();
File_Time new_time = 0;
char *filename = test_alt_files[i];
int32_t result = begin_tracking_file(&track, filename, &new_file, &new_time);
assert(result == FileTrack_FileAlreadyTracked);
}
// NOTE(allen): each file is still up to date
for (int32_t i = 0;
i < ArrayCount(test_files);
++i){
File_Time time = 0;
File_Index index = my_file_things[i].unique_file_index;
get_tracked_file_time(&track, index, &time);
assert(time == my_file_things[i].time);
}
// NOTE(allen): can still get index from file name
for (int32_t i = 0;
i < ArrayCount(test_files);
++i){
File_Index index = my_file_things[i].unique_file_index;
File_Index result_index1 = zero_file_index();
char *filename1 = test_files[i];
get_tracked_file_index(&track, filename1, &result_index1);
File_Index result_index2 = zero_file_index();
char *filename2 = test_alt_files[i];
get_tracked_file_index(&track, filename2, &result_index2);
assert(file_index_eq(result_index1, index));
assert(file_index_eq(result_index2, index));
}
// NOTE(allen): rewrite all of the files
for (int32_t i = 0;
i < ArrayCount(test_files);
++i){
char *filename = test_files[i];
test_rewrite_file_in_child_proc(filename);
}
// NOTE(allen): each file is behind
for (int32_t i = 0;
i < ArrayCount(test_files);
++i){
File_Time time = 0;
File_Index index = my_file_things[i].unique_file_index;
get_tracked_file_time(&track, index, &time);
assert(my_file_things[i].time < time);
}
// NOTE(allen): poll the tracking system for changed files
for (;;){
File_Index index = zero_file_index();
File_Time time = 0;
int32_t result = get_change_event(&track, &index);
if (result == FileTrack_NoMoreEvents){
break;
}
result = get_tracked_file_time(&track, index, &time);
for (int32_t i = 0;
i < ArrayCount(test_files);
++i){
File_Index my_index = my_file_things[i].unique_file_index;
if (file_index_eq(my_index, index)){
my_file_things[i].time = time;
break;
}
}
}
// NOTE(allen): each file is still up to date (episode 2)
for (int32_t i = 0;
i < ArrayCount(test_files);
++i){
File_Time time = 0;
File_Index index = my_file_things[i].unique_file_index;
get_tracked_file_time(&track, index, &time);
assert(time == my_file_things[i].time);
}
// NOTE(allen): rewrite each file myself
for (int32_t i = 0;
i < ArrayCount(test_files);
++i){
File_Index index = my_file_things[i].unique_file_index;
File_Time time = test_rewrite_file(&track, index);
my_file_things[i].time = time;
}
// NOTE(allen): check there are no changed file events
{
File_Index index = zero_file_index();
int32_t result = get_change_event(&track, &index);
assert(result == FileTrack_NoMoreEvents);
}
// NOTE(allen): rewrite half of the files twice
int32_t mid_point = ArrayCount(test_files) / 2;
for (int32_t i = 0;
i < mid_point;
++i){
char *filename = test_files[i];
test_rewrite_file_in_child_proc(filename);
}
for (int32_t i = 0;
i < mid_point;
++i){
char *filename = test_files[i];
test_rewrite_file_in_child_proc(filename);
}
// NOTE(allen): check number of events equals mid_point
int32_t count = 0;
for (;;){
File_Index index = zero_file_index();
File_Time time = 0;
int32_t result = get_change_event(&track, &index);
if (result == FileTrack_NoMoreEvents){
break;
}
result = get_tracked_file_time(&track, index, &time);
++count;
for (int32_t i = 0;
i < ArrayCount(test_files);
++i){
File_Index my_index = my_file_things[i].unique_file_index;
if (file_index_eq(my_index, index)){
my_file_things[i].time = time;
break;
}
}
}
assert(count == mid_point);
// NOTE(allen): untrack half of the files
for (int32_t i = 0;
i < mid_point;
++i){
File_Index stop_file = my_file_things[i].unique_file_index;
stop_tracking_file(&track, stop_file);
}
// NOTE(allen): untrack the same files again
for (int32_t i = 0;
i < mid_point;
++i){
File_Index stop_file = my_file_things[i].unique_file_index;
int32_t result = stop_tracking_file(&track, stop_file);
assert(result == FileTrack_FileNotTracked);
}
// NOTE(allen): make sure the number of remaining files is correct
{
int32_t track_count = 0;
count_tracked_files(&track, &track_count);
assert(track_count == (ArrayCount(test_files) - mid_point));
}
// NOTE(allen): untrack the rest of the files
for (int32_t i = mid_point;
i < ArrayCount(test_files);
++i){
File_Index stop_file = my_file_things[i].unique_file_index;
stop_tracking_file(&track, stop_file);
}
// NOTE(allen): make sure the system is empty
{
int32_t track_count = 0;
count_tracked_files(&track, &track_count);
assert(track_count == 0);
}
// NOTE(allen): finish using the track system
{
int32_t result = shut_down_track_system(&track);
assert(result == FileTrack_Good);
}
}
// NOTE(allen): test basic tracking logic
void test_1(void){
int32_t size1 = (16 << 10);
int32_t size2 = (16 << 10);
test_body_A(size1, size2);
}
// NOTE(allen): test memory expansion system for tables
void test_2(void){
int32_t size1 = (1 << 10);
int32_t size2 = (16 << 10);
test_body_A(size1, size2);
}
// NOTE(allen): test memory expansion system for listening nodes
void test_3(void){
int32_t size1 = (16 << 10);
int32_t size2 = (5 << 10);
test_body_A(size1, size2);
}
// NOTE(allen): test both memory expansion systems
void test_4(void){
int32_t size1 = (1 << 10);
int32_t size2 = (5 << 10);
test_body_A(size1, size2);
}
int main(int argc, char **argv){
test_4();
return(0);
}
// BOTTOM

View File

@ -1,114 +0,0 @@
/*
Copy Right FourTech LLC, 2016
All Rights Are Reserved
Helpers for the filetrack_main.c test bed.
Created on: 20.07.2016
*/
// TOP
#define FILE_REWRITER "w:/filetrack/build/file_rewriter"
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
static void
rewrite(char *buffer, int32_t size){
for (int32_t i = 0;
i < size;
++i){
if (buffer[i] >= 'a' && buffer[i] < 'z'){
++buffer[i];
}
else if (buffer[i] == 'z'){
buffer[i] = 'a';
}
}
}
static void
append(char *buffer, int32_t *pos, char *src){
int32_t i = *pos;
src -= i;
for (; src[i]; ++i){
buffer[i] = src[i];
}
*pos = i;
}
static void
test_rewrite_file_in_child_proc(char *filename){
char space[2048];
int32_t pos = 0;
append(space, &pos, FILE_REWRITER" ");
append(space, &pos, filename);
space[pos] = 0;
int32_t result = system(space);
assert(result == 0);
}
#ifndef FILE_TRACK_MAIN
int
main(int argc, char **argv){
if (argc == 2){
char *filename = argv[1];
char *mem = 0;
int32_t size = 0;
FILE *file = fopen(filename, "rb");
assert(file);
fseek(file, 0, SEEK_END);
size = ftell(file);
fseek(file, 0, SEEK_SET);
mem = (char*)malloc(size+1);
fread(mem, 1, size, file);
fclose(file);
rewrite(mem, size);
file = fopen(filename, "wb");
assert(file);
fwrite(mem, 1, size, file);
fclose(file);
}
return(0);
}
#else
static File_Time
test_rewrite_file(File_Track_System *system, File_Index index){
char *mem = 0;
uint32_t size = 0;
int32_t result = 0;
result = get_tracked_file_size(system, index, &size);
assert(result == FileTrack_Good);
mem = (char*)malloc(size+1);
result = get_tracked_file_data(system, index, mem, size);
assert(result == FileTrack_Good);
rewrite(mem, size);
File_Time time = 0;
rewrite_tracked_file(system, index, mem, size, &time);
free(mem);
return(time);
}
#endif
// BOTTOM