#include "precompile.h" #include "sp_pst.h" #include "sp_def.h" #include "memutil.h" #include "fileutil.h" #include "strutil.h" #include "list.h" #include #include struct sp_pst_tree_t { sp_pst_elem_t *root; }; struct sp_pst_elem_t { struct list_head entry; int type; char *key; void *value; int value_len; sp_pst_elem_t *parent; int file_offset; struct list_head child_list; }; // file serialize order are first-order static const char *get_full_path(const char *base_dir, const char *ent, const char *cls, const char *obj, char *buf) { #ifdef _WIN32 if (cls) { if (obj) { sprintf(buf, "%s\\objects\\%s\\%s\\%s.dat", base_dir, ent, cls, obj); } else { sprintf(buf, "%s\\objects\\%s\\%s", base_dir, ent, cls); } } else { sprintf(buf, "%s\\objects\\%s", base_dir, ent); } #else if (cls) { if (obj) { sprintf(buf, "%s/objects/%s/%s/%s.dat", base_dir, ent, cls, obj); } else { sprintf(buf, "%s/objects/%s/%s", base_dir, ent, cls); } } else { sprintf(buf, "%s/objects/%s", base_dir, ent); } #endif return buf; } static int file_write_elem_single(FILE *fp, sp_pst_elem_t *elem) { int type = elem->type; int child_offset; int next_sibling_offset; int key_len = strlen(elem->key); void *key = elem->key; int value_len = elem->value_len; void *value = elem->value; size_t t, cnt; if (list_empty(&elem->child_list)) { child_offset = 0; } else { sp_pst_elem_t *child = list_first_entry(&elem->child_list, sp_pst_elem_t, entry); child_offset = child->file_offset; } if (elem->entry.next) { sp_pst_elem_t *next_sibling = sp_pst_elem_next_sibling(elem); next_sibling_offset = next_sibling ? next_sibling->file_offset : 0; } else { next_sibling_offset = 0; } // type | child_offset | next_sibling_offset | key | value cnt = sizeof(int); t = fwrite(&type, 1, cnt, fp); if (t != cnt) goto on_error; t = fwrite(&child_offset, 1, cnt, fp); if (t != cnt) goto on_error; t = fwrite(&next_sibling_offset, 1, cnt, fp); if (t != cnt) goto on_error; t = fwrite(&key_len, 1, cnt, fp); if (t != cnt) goto on_error; cnt = key_len; t = fwrite(key, 1, key_len, fp); if (t != cnt) goto on_error; cnt = sizeof(int); t = fwrite(&value_len, 1, cnt, fp); if (t != cnt) goto on_error; cnt = value_len; t = fwrite(value, 1, value_len, fp); if (t != cnt) goto on_error; return 0; on_error: return Error_IO; } // first-order write static int file_write_elem(FILE *fp, sp_pst_elem_t *elem) { int rc; rc = file_write_elem_single(fp, elem); if (rc == 0) { sp_pst_elem_t *pos; list_for_each_entry(pos, &elem->child_list, sp_pst_elem_t, entry) { rc = file_write_elem(fp, pos); if (rc != 0) break; } } return rc; } static int file_write(FILE *fp, sp_pst_tree_t *tree) { return file_write_elem(fp, tree->root); } // first-order fill static void fill_offset_elem(int *curr_offset, sp_pst_elem_t *elem) { sp_pst_elem_t *pos; int size = 5 * sizeof(int) + strlen(elem->key) + elem->value_len; elem->file_offset = *curr_offset; *curr_offset += size; list_for_each_entry(pos, &elem->child_list, sp_pst_elem_t, entry) { fill_offset_elem(curr_offset, pos); } } static int fill_offset(sp_pst_tree_t *tree) { int curr_offset = 0; fill_offset_elem(&curr_offset, tree->root); return curr_offset; } static int file_read_elem_single(FILE *fp, int offset, sp_pst_elem_t **p_elem, int *next_sibling_offset, int *first_child_offset) { int rc = 0; sp_pst_elem_t *elem = NULL; size_t t, cnt; int key_len; rc = fseek(fp, offset, SEEK_SET); if (rc != 0) goto on_error; elem = ZALLOC_T(sp_pst_elem_t); cnt = sizeof(int); t = fread(&elem->type, 1, cnt, fp); if (t != cnt) goto on_error; t = fread(first_child_offset, 1, cnt, fp); if (t != cnt) goto on_error; t = fread(next_sibling_offset, 1, cnt, fp); if (t != cnt) goto on_error; cnt = sizeof(int); t = fread(&key_len, 1, cnt, fp); if (t != cnt) goto on_error; if (key_len) { elem->key = malloc(key_len+1); cnt = key_len; t = fread(elem->key, 1, cnt, fp); if (t != cnt) goto on_error; } cnt = sizeof(int); t = fread(&elem->value_len, 1, cnt, fp); if (t != cnt) goto on_error; if (elem->value_len) { elem->value = malloc(elem->value_len); cnt = elem->value_len; t = fread(elem->value, 1, cnt, fp); if (t != cnt) goto on_error; } elem->file_offset = offset; INIT_LIST_HEAD(&elem->child_list); *p_elem = elem; return 0; on_error: if (elem) { if (elem->key) free(elem->key); if (elem->value) free(elem->value); free(elem); } return Error_IO; } static int file_read_elem(FILE *fp, sp_pst_elem_t *parent, int offset, sp_pst_elem_t **p_elem, int *next_sibling_offset) { int rc, first_child_offset; sp_pst_elem_t *elem; rc = file_read_elem_single(fp, offset, &elem, next_sibling_offset, &first_child_offset); if (rc != 0) return rc; list_add_tail(&elem->entry, &parent->child_list); elem->parent = parent; *p_elem = elem; if (first_child_offset != 0) { sp_pst_elem_t *child_elem = NULL; int child_next_sibling_offset; rc = file_read_elem(fp, elem, first_child_offset, &child_elem, &child_next_sibling_offset); if (rc == 0) { while (child_next_sibling_offset != 0) { sp_pst_elem_t *tmp; rc = file_read_elem(fp, elem, child_next_sibling_offset, &tmp, &child_next_sibling_offset); if (rc != 0) { sp_pst_elem_destroy(tmp); break; } else { child_elem = tmp; } } } else { sp_pst_elem_destroy(child_elem); } } return rc; } int sp_pst_tree_create(sp_pst_tree_t **p_tree) { sp_pst_tree_t *tree = MALLOC_T(sp_pst_tree_t); tree->root = NULL; *p_tree = tree; return 0; } void sp_pst_tree_destroy(sp_pst_tree_t *tree) { sp_pst_elem_t *root = tree->root; if (root) { sp_pst_elem_destroy(root); free(tree); } } sp_pst_elem_t *sp_pst_tree_get_root(sp_pst_tree_t *tree) { return tree->root; } int sp_pst_tree_set_root(sp_pst_tree_t *tree, sp_pst_elem_t *elem) { if (tree->root) return Error_AlreadyExist; tree->root = elem; return 0; } sp_pst_elem_t *sp_pst_elem_create(sp_pst_elem_t *parent, const char *key) { sp_pst_elem_t *elem = ZALLOC_T(sp_pst_elem_t); INIT_LIST_HEAD(&elem->child_list); elem->key = _strdup(key); elem->parent = parent; return elem; } void sp_pst_elem_destroy(sp_pst_elem_t *elem) { if (elem) { while (!list_empty(&elem->child_list)) { sp_pst_elem_t *child = list_first_entry(&elem->child_list, sp_pst_elem_t, entry); list_del(&child->entry); sp_pst_elem_destroy(child); } free(elem->key); free(elem->value); free(elem); } } int sp_pst_elem_set_value(sp_pst_elem_t *elem, int type, const void *value, int value_len) { if (elem->value) { free(elem->value); elem->value = NULL; elem->type = SP_PST_T_UNKNOWN; } elem->type = type; if (value_len == -1) { elem->value_len = value ? strlen(value) : 0; elem->value = _strdup(value); } else if (value_len == 0) { elem->value = NULL; elem->value_len = 0; } else { elem->value = malloc(value_len); elem->value_len = value_len; memcpy(elem->value, value, value_len); // {bug} added } return 0; } sp_pst_elem_t *sp_pst_elem_get_parent(sp_pst_elem_t *elem) { return elem->parent; } const char *sp_pst_elem_get_key(sp_pst_elem_t *elem) { return elem->key; } int sp_pst_elem_get_type(sp_pst_elem_t *elem) { return elem->type; } const void *sp_pst_elem_get_value(sp_pst_elem_t *elem) { return elem->value; } int sp_pst_elem_get_value_len(sp_pst_elem_t *elem) { return elem->value_len; } int sp_pst_elem_append_child(sp_pst_elem_t *elem, sp_pst_elem_t *new_elem) { if (elem && new_elem) { list_add_tail(&new_elem->entry, &elem->child_list); return 0; } else { return Error_Param; } } int sp_pst_elem_remove_child_by_key(sp_pst_elem_t *elem, const char *key) { sp_pst_elem_t *child = sp_pst_elem_find_child(elem, key); if (child) { list_del(&child->entry); sp_pst_elem_destroy(child); } else { return Error_NotExist; } return 0; } int sp_pst_elem_remove(sp_pst_elem_t *elem) { if (elem) { if (elem->entry.next && elem->entry.prev) list_del(&elem->entry); } else { return Error_Param; } return 0; } sp_pst_elem_t *sp_pst_elem_find_child(sp_pst_elem_t *elem, const char *key) { if (elem && key) { sp_pst_elem_t *pos; list_for_each_entry(pos, &elem->child_list, sp_pst_elem_t, entry) { if (_stricmp(pos->key, key) == 0) return pos; } } return NULL; } int sp_pst_elem_insert_before( sp_pst_elem_t *pos, sp_pst_elem_t *new_elem ) { if (pos && new_elem) { __list_add(&new_elem->entry, pos->entry.prev, &pos->entry); } else { return Error_Param; } return 0; } int sp_pst_elem_insert_after(sp_pst_elem_t *pos, sp_pst_elem_t *new_elem) { if (pos && new_elem) { __list_add(&new_elem->entry, &pos->entry, pos->entry.next); } else { return Error_Param; } return 0; } sp_pst_elem_t *sp_pst_elem_first_child(sp_pst_elem_t *parent_elem) { if (!list_empty(&parent_elem->child_list)) return list_first_entry(&parent_elem->child_list, sp_pst_elem_t, entry); return NULL; } sp_pst_elem_t *sp_pst_elem_last_child(sp_pst_elem_t *parent_elem) { if (!list_empty(&parent_elem->child_list)) return list_last_entry(&parent_elem->child_list, sp_pst_elem_t, entry); return NULL; } sp_pst_elem_t *sp_pst_elem_next_sibling(sp_pst_elem_t *iter_elem) { sp_pst_elem_t *parent_elem = iter_elem->parent; struct list_head *next = iter_elem->entry.next; if (parent_elem->child_list.next != next) { return list_entry(next, sp_pst_elem_t, entry); } return NULL; } sp_pst_elem_t *sp_pst_elem_last_sibling(sp_pst_elem_t *iter_elem) { sp_pst_elem_t *parent_elem = iter_elem->parent; struct list_head *last = iter_elem->entry.prev; if (parent_elem->child_list.prev != last) { return list_entry(last, sp_pst_elem_t, entry); } return NULL; } int sp_pst_tree_load(const char *base_dir, const char *ent, const char *cls, const char *obj, sp_pst_tree_t **p_tree) { int rc = 0; char tmp[MAX_PATH]; FILE *fp; get_full_path(base_dir, ent, cls, obj, tmp); fp = fileutil_transaction_fopen(tmp, "rb"); if (fp) { int next_sibling_offset; sp_pst_elem_t *root = NULL; rc = file_read_elem(fp, NULL, 0, &root, &next_sibling_offset); if (rc != 0) { sp_pst_elem_destroy(root); } else { sp_pst_tree_create(p_tree); sp_pst_tree_set_root(*p_tree, root); } fileutil_transaction_fclose(tmp, fp); } else { if (!ExistsFileA(tmp)) { rc = Error_NotExist; } else { rc = Error_IO; } } return rc; } int sp_pst_tree_save(const char *base_dir, const char *ent, const char *cls, const char *obj, sp_pst_tree_t *tree) { int rc = 0; FILE *fp; char tmp[MAX_PATH]; get_full_path(base_dir, ent, cls, obj, tmp); CreateParentDirA(tmp, TRUE); fp = fileutil_transaction_fopen(tmp, "wb"); if (fp) { fill_offset(tree); rc = file_write(fp, tree); fileutil_transaction_fclose(tmp, fp); } else { rc = Error_IO; } return rc; } static void recover_persist_dir_files(const char *dir) { HANDLE hFind; char szFile[MAX_PATH]; WIN32_FIND_DATAA fd; strcpy(szFile, dir); #ifdef _WIN32 strcat(szFile, "\\*"); #else strcat(szFile, "/*"); #endif hFind = FindFirstFileA(szFile, &fd); if (hFind != INVALID_HANDLE_VALUE) { do { if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_READONLY)) { char t[MAX_PATH]; strcpy(t, szFile); t[strlen(t)-1] = 0; strcat(t, fd.cFileName); DeleteFileA(t); } } } while (FindNextFileA(hFind, &fd)); FindClose(hFind); } hFind = FindFirstFileA(szFile, &fd); if (hFind != INVALID_HANDLE_VALUE) { do { if (fd.dwFileAttributes & FILE_ATTRIBUTE_NORMAL & FILE_ATTRIBUTE_READONLY) { if (str_has_suffix(fd.cFileName, ".dat") == 0) { char t[MAX_PATH]; strcpy(t, szFile); szFile[strlen(szFile)-1] = 0; strcat(t, fd.cFileName); strcat(t, ".bak"); if (ExistsFileA(t)) DeleteFileA(t); } else if (str_has_suffix(fd.cFileName, ".bak") == 0) { char t[MAX_PATH]; DWORD dwType; strcpy(t, szFile); szFile[strlen(szFile)-1] = 0; strcat(t, fd.cFileName); t[strlen(t)-4] = 0; dwType = GetFileAttributesA(t); if (dwType & FILE_ATTRIBUTE_READONLY) { t[strlen(t)] = '.'; SetFileAttributesA(t, dwType & ~FILE_ATTRIBUTE_READONLY); DeleteFileA(t); } else { char tt[MAX_PATH]; strcpy(tt, t); strcat(tt, ".bak"); CopyFileA(t, tt, FALSE); SetFileAttributesA(tt, fd.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY); DeleteFileA(tt); } } } } while (FindNextFileA(hFind, &fd)); FindClose(hFind); } } void sp_pst_recover(const char *base_dir) { HANDLE hFind; char szObject[MAX_PATH]; WIN32_FIND_DATAA fd; int rc = 0; sprintf(szObject, "%s" SPLIT_SLASH_STR "*", base_dir); hFind = FindFirstFileA(szObject, &fd); if (hFind != INVALID_HANDLE_VALUE) { do { if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY && strcmp(fd.cFileName, ".") && strcmp(fd.cFileName, "..")) { HANDLE hChildFind; char szEntity[MAX_PATH]; WIN32_FIND_DATAA fdChild; strcpy(szEntity, szObject); szEntity[strlen(szEntity)-1] = 0; strcat(szEntity, fd.cFileName); strcat(szEntity, SPLIT_SLASH_STR "*"); hChildFind = FindFirstFileA(szEntity, &fdChild); if (hChildFind != INVALID_HANDLE_VALUE) { do { if (fdChild.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY && strcmp(fdChild.cFileName, ".") && strcmp(fdChild.cFileName, "..")) { char szClass[MAX_PATH]; strcpy(szClass, szEntity); szClass[strlen(szClass)-1] = 0; strcat(szClass, fdChild.cFileName); recover_persist_dir_files(szClass); } } while (FindNextFileA(hChildFind, &fdChild)); FindClose(hChildFind); } } } while (FindNextFileA(hFind, &fd)); FindClose(hFind); } } int sp_pst_get_object_count(const char *base_dir, const char *ent, const char *cls, int *p_cnt) { char tmp[MAX_PATH]; HANDLE hFind; WIN32_FIND_DATAA fd; int rc = 0; int cnt = 0; get_full_path(base_dir, ent, cls, "*", tmp); hFind = FindFirstFileA(tmp, &fd); if (hFind != INVALID_HANDLE_VALUE) { do { if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && fd.dwFileAttributes & FILE_ATTRIBUTE_READONLY) { if (str_has_suffix(fd.cFileName, ".dat") == 0) cnt++; } } while (FindNextFileA(hFind, &fd)); FindClose(hFind); } else { if (GetLastError() != ERROR_FILE_NOT_FOUND) rc = Error_IO; } *p_cnt = cnt; return rc; } array_header_t* sp_pst_get_object_keys(const char *base_dir, const char *ent, const char *cls) { array_header_t *arr = NULL; char tmp[MAX_PATH]; HANDLE hFind; WIN32_FIND_DATAA fd; get_full_path(base_dir, ent, cls, "*", tmp); hFind = FindFirstFileA(tmp, &fd); if (hFind != INVALID_HANDLE_VALUE) { arr = array_make(-1, sizeof(char*)); do { if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && fd.dwFileAttributes & FILE_ATTRIBUTE_READONLY) { if (str_has_suffix(fd.cFileName, ".dat") == 0) { size_t idx = strlen(fd.cFileName) - 4; int t = fd.cFileName[idx]; fd.cFileName[idx] = '\0'; ARRAY_PUSH(arr, char*) = _strdup(fd.cFileName); fd.cFileName[idx] = t; } } } while (FindNextFileA(hFind, &fd)); FindClose(hFind); } else { if (GetLastError() != ERROR_FILE_NOT_FOUND) arr = array_make(-1, sizeof(char*)); } return arr; } int sp_pst_delete_object(const char *base_dir, const char *ent, const char *cls, const char *obj) { char tmp[MAX_PATH]; int rc = 0; get_full_path(base_dir, ent, cls, obj, tmp); if (ExistsFileA(tmp)) { BOOL bRet = DeleteFileA(tmp); if (!bRet) rc = Error_IO; } else { rc = Error_NotExist; } strcat(tmp, ".bak"); DeleteFileA(tmp); return rc; } int sp_pst_delete_class_objects(const char *base_dir, const char *ent, const char *cls) { char tmp[MAX_PATH]; get_full_path(base_dir, ent, cls, NULL, tmp); RemoveDirRecursiveA(tmp); return 0; }