void Folder_consolidate(Folder *self, const CharBuf *path) { Folder *folder = Folder_Find_Folder(self, path); Folder *enclosing_folder = Folder_Enclosing_Folder(self, path); if (!folder) { THROW(ERR, "Can't consolidate %o", path); } else if (Folder_Is_A(folder, COMPOUNDFILEREADER)) { THROW(ERR, "Can't consolidate %o twice", path); } else { CompoundFileWriter *cf_writer = CFWriter_new(folder); CFWriter_Consolidate(cf_writer); DECREF(cf_writer); if (CB_Get_Size(path)) { ZombieCharBuf *name = IxFileNames_local_part(path, ZCB_BLANK()); CompoundFileReader *cf_reader = CFReader_open(folder); if (!cf_reader) { RETHROW(INCREF(Err_get_error())); } Hash_Store(enclosing_folder->entries, (Obj*)name, (Obj*)cf_reader); } } }
static void test_Local_MkDir_and_Find_Folder(TestBatchRunner *runner) { Folder *real_folder = S_folder_with_contents(); CompoundFileReader *cf_reader = CFReader_open(real_folder); TEST_FALSE(runner, CFReader_Local_Is_Directory(cf_reader, stuff), "Local_Is_Directory returns false for non-existent entry"); TEST_TRUE(runner, CFReader_MkDir(cf_reader, stuff), "MkDir returns true"); TEST_TRUE(runner, Folder_Find_Folder(real_folder, stuff) != NULL, "Local_MkDir pass-through"); TEST_TRUE(runner, Folder_Find_Folder(real_folder, stuff) == CFReader_Find_Folder(cf_reader, stuff), "Local_Find_Folder pass-through"); TEST_TRUE(runner, CFReader_Local_Is_Directory(cf_reader, stuff), "Local_Is_Directory pass through"); Err_set_error(NULL); TEST_FALSE(runner, CFReader_MkDir(cf_reader, stuff), "MkDir returns false when dir already exists"); TEST_TRUE(runner, Err_get_error() != NULL, "MkDir sets global error when dir already exists"); Err_set_error(NULL); TEST_FALSE(runner, CFReader_MkDir(cf_reader, foo), "MkDir returns false when virtual file exists"); TEST_TRUE(runner, Err_get_error() != NULL, "MkDir sets global error when virtual file exists"); TEST_TRUE(runner, CFReader_Find_Folder(cf_reader, foo) == NULL, "Virtual file not reported as directory"); TEST_FALSE(runner, CFReader_Local_Is_Directory(cf_reader, foo), "Local_Is_Directory returns false for virtual file"); DECREF(real_folder); DECREF(cf_reader); }
DirHandle* Folder_open_dir(Folder *self, const CharBuf *path) { DirHandle *dh = NULL; Folder *folder; if (path) { folder = Folder_Find_Folder(self, path); } else { ZombieCharBuf *empty = ZCB_BLANK(); folder = Folder_Find_Folder(self, (CharBuf*)empty); } if (!folder) { Err_set_error(Err_new(CB_newf("Invalid path: '%o'", path))); } else { dh = Folder_Local_Open_Dir(folder); if (!dh) { ERR_ADD_FRAME(Err_get_error()); } } return dh; }
VArray* Folder_list_r(Folder *self, const CharBuf *path) { Folder *local_folder = Folder_Find_Folder(self, path); VArray *list = VA_new(0); if (local_folder) { CharBuf *dir = CB_new(20); CharBuf *prefix = CB_new(20); if (path && CB_Get_Size(path)) { CB_setf(prefix, "%o/", path); } S_add_to_file_list(local_folder, list, dir, prefix); DECREF(prefix); DECREF(dir); } return list; }
VArray* Folder_list(Folder *self, const CharBuf *path) { Folder *local_folder = Folder_Find_Folder(self, path); VArray *list = NULL; DirHandle *dh = Folder_Local_Open_Dir(local_folder); if (dh) { CharBuf *entry = DH_Get_Entry(dh); list = VA_new(32); while (DH_Next(dh)) { VA_Push(list, (Obj*)CB_Clone(entry)); } DECREF(dh); } else { ERR_ADD_FRAME(Err_get_error()); } return list; }
bool Lock_make_lock_dir(Folder *folder) { String *lock_dir_name = SSTR_WRAP_C("locks"); if (!Folder_MkDir(folder, lock_dir_name)) { Err *err = (Err*)INCREF(Err_get_error()); // Maybe our attempt failed because another process succeeded. if (Folder_Find_Folder(folder, lock_dir_name)) { DECREF(err); } else { // Nope, everything failed, so bail out. Err_set_error(err); return false; } } return true; }
bool_t LFLock_request(LockFileLock *self) { Hash *file_data; bool_t wrote_json; bool_t success = false; bool_t deletion_failed = false; if (Folder_Exists(self->folder, self->lock_path)) { Err_set_error((Err*)LockErr_new(CB_newf("Can't obtain lock: '%o' exists", self->lock_path))); return false; } // Create the "locks" subdirectory if necessary. CharBuf *lock_dir_name = (CharBuf*)ZCB_WRAP_STR("locks", 5); if (!Folder_Exists(self->folder, lock_dir_name)) { if (!Folder_MkDir(self->folder, lock_dir_name)) { Err *mkdir_err = (Err*)CERTIFY(Err_get_error(), ERR); LockErr *err = LockErr_new(CB_newf("Can't create 'locks' directory: %o", Err_Get_Mess(mkdir_err))); // Maybe our attempt failed because another process succeeded. if (Folder_Find_Folder(self->folder, lock_dir_name)) { DECREF(err); } else { // Nope, everything failed, so bail out. Err_set_error((Err*)err); return false; } } } // Prepare to write pid, lock name, and host to the lock file as JSON. file_data = Hash_new(3); Hash_Store_Str(file_data, "pid", 3, (Obj*)CB_newf("%i32", (int32_t)PID_getpid())); Hash_Store_Str(file_data, "host", 4, INCREF(self->host)); Hash_Store_Str(file_data, "name", 4, INCREF(self->name)); // Write to a temporary file, then use the creation of a hard link to // ensure atomic but non-destructive creation of the lockfile with its // complete contents. wrote_json = Json_spew_json((Obj*)file_data, self->folder, self->link_path); if (wrote_json) { success = Folder_Hard_Link(self->folder, self->link_path, self->lock_path); if (!success) { Err *hard_link_err = (Err*)CERTIFY(Err_get_error(), ERR); Err_set_error((Err*)LockErr_new(CB_newf("Failed to obtain lock at '%o': %o", self->lock_path, Err_Get_Mess(hard_link_err)))); } deletion_failed = !Folder_Delete(self->folder, self->link_path); } else { Err *spew_json_err = (Err*)CERTIFY(Err_get_error(), ERR); Err_set_error((Err*)LockErr_new(CB_newf("Failed to obtain lock at '%o': %o", self->lock_path, Err_Get_Mess(spew_json_err)))); } DECREF(file_data); // Verify that our temporary file got zapped. if (wrote_json && deletion_failed) { CharBuf *mess = MAKE_MESS("Failed to delete '%o'", self->link_path); Err_throw_mess(ERR, mess); } return success; }
bool LFLock_Request_IMP(LockFileLock *self) { LockFileLockIVARS *const ivars = LFLock_IVARS(self); bool success = false; if (Folder_Exists(ivars->folder, ivars->lock_path)) { Err_set_error((Err*)LockErr_new(Str_newf("Can't obtain lock: '%o' exists", ivars->lock_path))); return false; } // Create the "locks" subdirectory if necessary. String *lock_dir_name = (String*)SSTR_WRAP_UTF8("locks", 5); if (!Folder_Exists(ivars->folder, lock_dir_name)) { if (!Folder_MkDir(ivars->folder, lock_dir_name)) { Err *mkdir_err = (Err*)CERTIFY(Err_get_error(), ERR); LockErr *err = LockErr_new(Str_newf("Can't create 'locks' directory: %o", Err_Get_Mess(mkdir_err))); // Maybe our attempt failed because another process succeeded. if (Folder_Find_Folder(ivars->folder, lock_dir_name)) { DECREF(err); } else { // Nope, everything failed, so bail out. Err_set_error((Err*)err); return false; } } } // Prepare to write pid, lock name, and host to the lock file as JSON. Hash *file_data = Hash_new(3); Hash_Store_Utf8(file_data, "pid", 3, (Obj*)Str_newf("%i32", (int32_t)PID_getpid())); Hash_Store_Utf8(file_data, "host", 4, INCREF(ivars->host)); Hash_Store_Utf8(file_data, "name", 4, INCREF(ivars->name)); String *json = Json_to_json((Obj*)file_data); DECREF(file_data); // Write to a temporary file, then use the creation of a hard link to // ensure atomic but non-destructive creation of the lockfile with its // complete contents. OutStream *outstream = Folder_Open_Out(ivars->folder, ivars->link_path); if (!outstream) { ERR_ADD_FRAME(Err_get_error()); DECREF(json); return false; } struct lockfile_context context; context.outstream = outstream; context.json = json; Err *json_error = Err_trap(S_write_lockfile_json, &context); bool wrote_json = !json_error; DECREF(outstream); DECREF(json); if (wrote_json) { success = Folder_Hard_Link(ivars->folder, ivars->link_path, ivars->lock_path); if (!success) { Err *hard_link_err = (Err*)CERTIFY(Err_get_error(), ERR); Err_set_error((Err*)LockErr_new(Str_newf("Failed to obtain lock at '%o': %o", ivars->lock_path, Err_Get_Mess(hard_link_err)))); } } else { Err_set_error((Err*)LockErr_new(Str_newf("Failed to obtain lock at '%o': %o", ivars->lock_path, Err_Get_Mess(json_error)))); DECREF(json_error); } // Verify that our temporary file got zapped. bool deletion_failed = !Folder_Delete(ivars->folder, ivars->link_path); if (deletion_failed) { String *mess = MAKE_MESS("Failed to delete '%o'", ivars->link_path); Err_throw_mess(ERR, mess); } return success; }
static void test_Enclosing_Folder_and_Find_Folder(TestBatch *batch) { Folder *folder = (Folder*)RAMFolder_new(NULL); FileHandle *fh; Folder_MkDir(folder, &foo); Folder_MkDir(folder, &foo_bar); Folder_MkDir(folder, &foo_bar_baz); fh = Folder_Open_FileHandle(folder, &foo_bar_baz_boffo, FH_CREATE | FH_WRITE_ONLY); { Folder *encloser = Folder_Enclosing_Folder(folder, (CharBuf*)&nope); Folder *found = Folder_Find_Folder(folder, (CharBuf*)&nope); TEST_TRUE(batch, encloser == folder, "Enclosing_Folder() - non-existent entry yields parent"); TEST_TRUE(batch, found == NULL, "Find_Folder() - non-existent entry yields NULL"); } { Folder *encloser = Folder_Enclosing_Folder(folder, &foo_bar); Folder *found = Folder_Find_Folder(folder, &foo_bar); TEST_TRUE(batch, encloser && Folder_Is_A(encloser, FOLDER) && CB_Ends_With(Folder_Get_Path(encloser), &foo), "Enclosing_Folder() - find one directory down"); TEST_TRUE(batch, found && Folder_Is_A(found, FOLDER) && CB_Ends_With(Folder_Get_Path(found), &bar), "Find_Folder() - 'foo/bar'"); } { Folder *encloser = Folder_Enclosing_Folder(folder, &foo_bar_baz); Folder *found = Folder_Find_Folder(folder, &foo_bar_baz); TEST_TRUE(batch, encloser && Folder_Is_A(encloser, FOLDER) && CB_Ends_With(Folder_Get_Path(encloser), &bar), "Find two directories down"); TEST_TRUE(batch, found && Folder_Is_A(found, FOLDER) && CB_Ends_With(Folder_Get_Path(found), &baz), "Find_Folder() - 'foo/bar/baz'"); } { Folder *encloser = Folder_Enclosing_Folder(folder, &foo_bar_baz_boffo); Folder *found = Folder_Find_Folder(folder, &foo_bar_baz_boffo); TEST_TRUE(batch, encloser && Folder_Is_A(encloser, FOLDER) && CB_Ends_With(Folder_Get_Path(encloser), &baz), "Recurse to find a directory containing a real file"); TEST_TRUE(batch, found == NULL, "Find_Folder() - file instead of folder yields NULL"); } DECREF(fh); DECREF(folder); }