int fs_mkdir(FileSystem *fs, const char *d) { int p = 0; Inode *inode = NULL; Folder *fd = NULL; char ppath[4096] = ""; char cname[4096] = ""; Folder *pfd = NULL; int parent_page_num = 0; p = freelist_allocate(fs->freelist); if (p <= 1) return ERROR; inode = inode_new(p); if (!inode) return ERROR; inode->type = INODE_FOLDER; inode->filesize = 0; inode->lastmod = time(NULL); inode->firstpage = 0; fs_save_inode(fs, inode); inode_free(&inode); fs_split_path(d, ppath, cname); #ifdef DEBUG fprintf(stderr, "fs_mkdir, d=`%s`, ppath=`%s`, cname=`%s`\n", d, ppath, cname); #endif pfd = folder_open(fs, folder_lookup(fs, fs->cur, ppath)); parent_page_num = AS_FILE(pfd)->inode->page_num; folder_add_child(pfd, cname, p); folder_close(&pfd); fd = folder_open(fs, fs_load_inode(fs, p)); fs_split_path(d, ppath, cname); folder_add_child(fd, "", ROOT_PAGE_NUM()); folder_add_child(fd, ".", p); folder_add_child(fd, "..", parent_page_num); folder_close(&fd); return OK; }
int fs_create(FileSystem *fs, const char *f) { int p = 0; Inode *inode = NULL; char ppath[4096] = ""; char cname[4096] = ""; Folder *pfd = NULL; p = freelist_allocate(fs->freelist); if (p <= 1) return ERROR; inode = inode_new(p); if (!inode) return ERROR; inode->type = INODE_FILE; inode->filesize = 0; inode->firstpage = 0; fs_save_inode(fs, inode); inode_free(&inode); fs_split_path(f, ppath, cname); pfd = folder_open(fs, folder_lookup(fs, fs->cur, ppath)); folder_add_child(pfd, cname, p); folder_close(&pfd); return OK; }
/** * Process a null-terminated path, closing any directories and building the * nodes as needed, and opening the new directories to support the current path. * * Once the proper set of folders are open, create a node and write it into * the folder. */ static process_path_result_t process_path( from_flat_state_t *state, const char *path, size_t max_len) { size_t path_scan_index; size_t current_path_start; size_t open_folder_index; // match as many path components as we can for (path_scan_index = 0, current_path_start = 0, open_folder_index = 0; path[path_scan_index] != 0; path_scan_index++) { if (path_scan_index == max_len) { return COMPOUND_LITERAL(process_path_result_t) { PROCESS_PATH_CORRUPT, NULL, 0}; } // check for a path separator. if (path[path_scan_index] != '/') { continue; } size_t path_len = path_scan_index + 1 /* to include the / */ - current_path_start; bool open_new_folder = true; // check if the *next* open folder is valid, and if it matches the path // component we just found. if (open_folder_index + 1 < state->open_folder_count) { if (folder_name_compare( &path[current_path_start], path_len, &state->folders[open_folder_index + 1]) == 0) { // we found the folder we needed, so we can just reuse it. open_new_folder = false; open_folder_index++; } else { close_folder_result_t close_folder_result = close_folder(state, open_folder_index + 1); if (close_folder_result.code == CLOSE_FOLDER_OOM) { return COMPOUND_LITERAL(process_path_result_t) {PROCESS_PATH_OOM, NULL, 0}; } } } if (open_new_folder == true) { // if we're opening a new folder, that means there should be no child // folders open. assert(state->open_folder_count == open_folder_index + 1); open_folder_index++; state->open_folder_count++; open_folder_t *folder = &state->folders[open_folder_index]; assert(folder->in_use == false); assert(folder->closed_children == folder->closed_children_prealloc); assert(folder->closed_children_count == 0); // link the name in. remember, we don't own the memory!! folder->in_use = true; folder->subfolder_name = &path[current_path_start]; folder->subfolder_name_sz = path_len; } // path starts after the / current_path_start = path_scan_index + 1; } // close path components that are not matched, building their nodes. if (open_folder_index + 1 < state->open_folder_count) { close_folder_result_t close_folder_result = close_folder(state, open_folder_index + 1); if (close_folder_result.code == CLOSE_FOLDER_OOM) { return COMPOUND_LITERAL(process_path_result_t) {PROCESS_PATH_OOM, NULL, 0}; } } // build a node for the remaining path (which should just be the // filename). add it to the currently open folder. arena_alloc_node_result_t arena_alloc_node_result = arena_alloc_node( state->tree, &path[current_path_start], path_scan_index - current_path_start, 0); if (arena_alloc_node_result.code == ARENA_ALLOC_OOM) { return COMPOUND_LITERAL(process_path_result_t) {PROCESS_PATH_OOM, NULL, 0}; } arena_alloc_node_result.node->type = TYPE_LEAF; // jam the new node into the currently open folder. open_folder_t *folder = &state->folders[open_folder_index]; folder_add_child(state, folder, arena_alloc_node_result.node); return COMPOUND_LITERAL(process_path_result_t) { PROCESS_PATH_OK, arena_alloc_node_result.node, path_scan_index + 1}; }
/** * Close the folder at index `folder_index`. This may require closing nested * folders. If folder_index is > 0, then add the closed folder to its parent. * If the folder_index is 0, it is responsibility of the caller to attach the * returned node to the shadow root. */ static close_folder_result_t close_folder( from_flat_state_t *state, size_t folder_index) { open_folder_t *folder = &state->folders[folder_index]; assert(folder->in_use == true); if (folder_index < MAX_FOLDER_DEPTH - 1) { // maybe a nested folder needs to be closed? if (state->folders[folder_index + 1].in_use) { // yup, it needs to be closed. close_folder_result_t close_folder_result = close_folder(state, folder_index + 1); if (close_folder_result.code != CLOSE_FOLDER_OK) { return COMPOUND_LITERAL(close_folder_result_t) { close_folder_result.code, NULL}; } } } // allocate a node and set it up. arena_alloc_node_result_t arena_alloc_node_result = arena_alloc_node( state->tree, folder->subfolder_name, folder->subfolder_name_sz, folder->closed_children_count); if (arena_alloc_node_result.code == ARENA_ALLOC_OOM) { return COMPOUND_LITERAL(close_folder_result_t) { CLOSE_FOLDER_OOM, NULL}; } node_t *node = arena_alloc_node_result.node; node->type = TYPE_IMPLICIT; // we must initialize flags to a known value, even if it's not used // because it participates in checksum calculation. node->flags = 0; if (!VERIFY_CHILD_NUM(folder->closed_children_count)) { abort(); } // this is a huge abstraction violation, but it allows us to use // `set_child_by_index`, which is significantly more efficient. node->num_children = (child_num_t) folder->closed_children_count; // node is set up. now add all the children! intptr_t arena_start = (intptr_t) state->tree->arena; for (size_t ix = 0; ix < folder->closed_children_count; ix++) { ptrdiff_t child_offset = (intptr_t) folder->closed_children[ix]; intptr_t address = arena_start + child_offset; set_child_by_index(node, ix, (node_t *) address); } init_open_folder(folder); // zap the folder so it can be reused. state->open_folder_count--; // attach to parent folder if it's not the root folder. assert(folder_index == state->open_folder_count); if (folder_index > 0) { open_folder_t *parent_folder = &state->folders[folder_index - 1]; if (folder_add_child(state, parent_folder, node) == false) { return COMPOUND_LITERAL(close_folder_result_t) { CLOSE_FOLDER_OOM, NULL}; } } return COMPOUND_LITERAL(close_folder_result_t) { CLOSE_FOLDER_OK, node}; }