| /* Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #define C_LUCY_RAMFOLDER |
| #include "Lucy/Util/ToolSet.h" |
| |
| #include "Lucy/Store/RAMFolder.h" |
| #include "Lucy/Store/CompoundFileReader.h" |
| #include "Lucy/Store/InStream.h" |
| #include "Lucy/Store/OutStream.h" |
| #include "Lucy/Store/RAMDirHandle.h" |
| #include "Lucy/Store/RAMFile.h" |
| #include "Lucy/Store/RAMFileHandle.h" |
| #include "Lucy/Util/IndexFileNames.h" |
| |
| // Return the concatenation of the Folder's path and the supplied path. |
| static String* |
| S_fullpath(RAMFolder *self, String *path); |
| |
| RAMFolder* |
| RAMFolder_new(String *path) { |
| RAMFolder *self = (RAMFolder*)Class_Make_Obj(RAMFOLDER); |
| return RAMFolder_init(self, path); |
| } |
| |
| RAMFolder* |
| RAMFolder_init(RAMFolder *self, String *path) { |
| Folder_init((Folder*)self, path); |
| return self; |
| } |
| |
| void |
| RAMFolder_Initialize_IMP(RAMFolder *self) { |
| UNUSED_VAR(self); |
| } |
| |
| bool |
| RAMFolder_Check_IMP(RAMFolder *self) { |
| UNUSED_VAR(self); |
| return true; |
| } |
| |
| bool |
| RAMFolder_Local_MkDir_IMP(RAMFolder *self, String *name) { |
| RAMFolderIVARS *const ivars = RAMFolder_IVARS(self); |
| if (Hash_Fetch(ivars->entries, name)) { |
| Err_set_error(Err_new(Str_newf("Can't MkDir, '%o' already exists", |
| name))); |
| return false; |
| } |
| else { |
| String *fullpath = S_fullpath(self, name); |
| Hash_Store(ivars->entries, name, (Obj*)RAMFolder_new(fullpath)); |
| DECREF(fullpath); |
| return true; |
| } |
| } |
| |
| FileHandle* |
| RAMFolder_Local_Open_FileHandle_IMP(RAMFolder *self, String *name, |
| uint32_t flags) { |
| RAMFolderIVARS *const ivars = RAMFolder_IVARS(self); |
| RAMFileHandle *fh; |
| String *fullpath = S_fullpath(self, name); |
| RAMFile *file = (RAMFile*)Hash_Fetch(ivars->entries, name); |
| bool can_create |
| = (flags & (FH_WRITE_ONLY | FH_CREATE)) == (FH_WRITE_ONLY | FH_CREATE) |
| ? true : false; |
| |
| // Make sure the filepath isn't a directory, and that it either exists |
| // or we have permission to create it. |
| if (file) { |
| if (!RAMFile_is_a(file, RAMFILE)) { |
| Err_set_error(Err_new(Str_newf("Not a file: '%o'", fullpath))); |
| DECREF(fullpath); |
| return NULL; |
| } |
| } |
| else if (!can_create) { |
| Err_set_error(Err_new(Str_newf("File not found: '%o'", fullpath))); |
| DECREF(fullpath); |
| return NULL; |
| } |
| |
| // Open the file and store it if it was just created. |
| fh = RAMFH_open(fullpath, flags, file); |
| if (fh) { |
| if (!file) { |
| file = RAMFH_Get_File(fh); |
| Hash_Store(ivars->entries, name, INCREF(file)); |
| } |
| } |
| else { |
| Err *error = Err_get_error(); |
| ERR_ADD_FRAME(error); |
| } |
| |
| DECREF(fullpath); |
| |
| return (FileHandle*)fh; |
| } |
| |
| DirHandle* |
| RAMFolder_Local_Open_Dir_IMP(RAMFolder *self) { |
| RAMDirHandle *dh = RAMDH_new(self); |
| if (!dh) { ERR_ADD_FRAME(Err_get_error()); } |
| return (DirHandle*)dh; |
| } |
| |
| bool |
| RAMFolder_Local_Exists_IMP(RAMFolder *self, String *name) { |
| RAMFolderIVARS *const ivars = RAMFolder_IVARS(self); |
| return !!Hash_Fetch(ivars->entries, name); |
| } |
| |
| bool |
| RAMFolder_Local_Is_Directory_IMP(RAMFolder *self, String *name) { |
| RAMFolderIVARS *const ivars = RAMFolder_IVARS(self); |
| Obj *entry = Hash_Fetch(ivars->entries, name); |
| if (entry && Obj_is_a(entry, FOLDER)) { return true; } |
| return false; |
| } |
| |
| #define OP_RENAME 1 |
| #define OP_HARD_LINK 2 |
| |
| static bool |
| S_rename_or_hard_link(RAMFolder *self, String* from, String *to, |
| Folder *from_folder, Folder *to_folder, |
| String *from_name, String *to_name, |
| int op) { |
| Obj *elem = NULL; |
| RAMFolder *inner_from_folder = NULL; |
| RAMFolder *inner_to_folder = NULL; |
| UNUSED_VAR(self); |
| |
| // Make sure the source and destination folders exist. |
| if (!from_folder) { |
| Err_set_error(Err_new(Str_newf("File not found: '%o'", from))); |
| return false; |
| } |
| if (!to_folder) { |
| Err_set_error(Err_new(Str_newf("Invalid file path (can't find dir): '%o'", |
| to))); |
| return false; |
| } |
| |
| // Extract RAMFolders from compound reader wrappers, if necessary. |
| if (Folder_is_a(from_folder, COMPOUNDFILEREADER)) { |
| inner_from_folder = (RAMFolder*)CFReader_Get_Real_Folder( |
| (CompoundFileReader*)from_folder); |
| } |
| else { |
| inner_from_folder = (RAMFolder*)from_folder; |
| } |
| if (Folder_is_a(to_folder, COMPOUNDFILEREADER)) { |
| inner_to_folder = (RAMFolder*)CFReader_Get_Real_Folder( |
| (CompoundFileReader*)to_folder); |
| } |
| else { |
| inner_to_folder = (RAMFolder*)to_folder; |
| } |
| if (!RAMFolder_is_a(inner_from_folder, RAMFOLDER)) { |
| Err_set_error(Err_new(Str_newf("Not a RAMFolder, but a '%o'", |
| Obj_get_class_name((Obj*)inner_from_folder)))); |
| return false; |
| } |
| if (!RAMFolder_is_a(inner_to_folder, RAMFOLDER)) { |
| Err_set_error(Err_new(Str_newf("Not a RAMFolder, but a '%o'", |
| Obj_get_class_name((Obj*)inner_to_folder)))); |
| return false; |
| } |
| |
| // Find the original element. |
| elem = Hash_Fetch(RAMFolder_IVARS(inner_from_folder)->entries, from_name); |
| if (!elem) { |
| if (Folder_is_a(from_folder, COMPOUNDFILEREADER) |
| && Folder_Local_Exists(from_folder, from_name) |
| ) { |
| Err_set_error(Err_new(Str_newf("Source file '%o' is virtual", |
| from))); |
| } |
| else { |
| Err_set_error(Err_new(Str_newf("File not found: '%o'", from))); |
| } |
| return false; |
| } |
| |
| // Execute the rename/hard-link. |
| if (op == OP_RENAME) { |
| Obj *existing = Hash_Fetch(RAMFolder_IVARS(inner_to_folder)->entries, |
| to_name); |
| if (existing) { |
| bool conflict = false; |
| |
| // Return success fast if file is copied on top of itself. |
| if (inner_from_folder == inner_to_folder |
| && Str_Equals(from_name, (Obj*)to_name) |
| ) { |
| return true; |
| } |
| |
| // Don't allow clobbering of different entry type. |
| if (Obj_is_a(elem, RAMFILE)) { |
| if (!Obj_is_a(existing, RAMFILE)) { |
| conflict = true; |
| } |
| } |
| else if (Obj_is_a(elem, FOLDER)) { |
| if (!Obj_is_a(existing, FOLDER)) { |
| conflict = true; |
| } |
| } |
| if (conflict) { |
| Err_set_error(Err_new(Str_newf("Can't clobber a %o with a %o", |
| Obj_get_class_name(existing), |
| Obj_get_class_name(elem)))); |
| return false; |
| } |
| } |
| |
| // Perform the store first, then the delete. Inform Folder objects |
| // about the relocation. |
| Hash_Store(RAMFolder_IVARS(inner_to_folder)->entries, |
| to_name, INCREF(elem)); |
| DECREF(Hash_Delete(RAMFolder_IVARS(inner_from_folder)->entries, |
| from_name)); |
| if (Obj_is_a(elem, FOLDER)) { |
| String *newpath = S_fullpath(inner_to_folder, to_name); |
| Folder_Set_Path((Folder*)elem, newpath); |
| DECREF(newpath); |
| } |
| } |
| else if (op == OP_HARD_LINK) { |
| if (!Obj_is_a(elem, RAMFILE)) { |
| Err_set_error(Err_new(Str_newf("'%o' isn't a file, it's a %o", |
| from, Obj_get_class_name(elem)))); |
| return false; |
| } |
| else { |
| Obj *existing |
| = Hash_Fetch(RAMFolder_IVARS(inner_to_folder)->entries, |
| to_name); |
| if (existing) { |
| Err_set_error(Err_new(Str_newf("'%o' already exists", to))); |
| return false; |
| } |
| else { |
| Hash_Store(RAMFolder_IVARS(inner_to_folder)->entries, |
| to_name, INCREF(elem)); |
| } |
| } |
| } |
| else { |
| THROW(ERR, "Unexpected op: %i32", (int32_t)op); |
| } |
| |
| return true; |
| } |
| |
| bool |
| RAMFolder_Rename_IMP(RAMFolder *self, String* from, |
| String *to) { |
| Folder *from_folder = RAMFolder_Enclosing_Folder(self, from); |
| Folder *to_folder = RAMFolder_Enclosing_Folder(self, to); |
| String *from_name = IxFileNames_local_part(from); |
| String *to_name = IxFileNames_local_part(to); |
| bool result = S_rename_or_hard_link(self, from, to, from_folder, to_folder, |
| from_name, to_name, OP_RENAME); |
| if (!result) { ERR_ADD_FRAME(Err_get_error()); } |
| DECREF(to_name); |
| DECREF(from_name); |
| return result; |
| } |
| |
| bool |
| RAMFolder_Hard_Link_IMP(RAMFolder *self, String *from, |
| String *to) { |
| Folder *from_folder = RAMFolder_Enclosing_Folder(self, from); |
| Folder *to_folder = RAMFolder_Enclosing_Folder(self, to); |
| String *from_name = IxFileNames_local_part(from); |
| String *to_name = IxFileNames_local_part(to); |
| bool result = S_rename_or_hard_link(self, from, to, from_folder, to_folder, |
| from_name, to_name, OP_HARD_LINK); |
| if (!result) { ERR_ADD_FRAME(Err_get_error()); } |
| DECREF(to_name); |
| DECREF(from_name); |
| return result; |
| } |
| |
| bool |
| RAMFolder_Local_Delete_IMP(RAMFolder *self, String *name) { |
| RAMFolderIVARS *const ivars = RAMFolder_IVARS(self); |
| Obj *entry = Hash_Fetch(ivars->entries, name); |
| if (entry) { |
| if (Obj_is_a(entry, RAMFILE)) { |
| ; |
| } |
| else if (Obj_is_a(entry, FOLDER)) { |
| RAMFolder *inner_folder; |
| if (Obj_is_a(entry, COMPOUNDFILEREADER)) { |
| inner_folder = (RAMFolder*)CERTIFY( |
| CFReader_Get_Real_Folder((CompoundFileReader*)entry), |
| RAMFOLDER); |
| } |
| else { |
| inner_folder = (RAMFolder*)CERTIFY(entry, RAMFOLDER); |
| } |
| if (Hash_Get_Size(RAMFolder_IVARS(inner_folder)->entries)) { |
| // Can't delete non-empty dir. |
| return false; |
| } |
| } |
| else { |
| return false; |
| } |
| DECREF(Hash_Delete(ivars->entries, name)); |
| return true; |
| } |
| else { |
| return false; |
| } |
| } |
| |
| Folder* |
| RAMFolder_Local_Find_Folder_IMP(RAMFolder *self, String *path) { |
| RAMFolderIVARS *const ivars = RAMFolder_IVARS(self); |
| Folder *local_folder = (Folder*)Hash_Fetch(ivars->entries, path); |
| if (local_folder && Folder_is_a(local_folder, FOLDER)) { |
| return local_folder; |
| } |
| return NULL; |
| } |
| |
| void |
| RAMFolder_Close_IMP(RAMFolder *self) { |
| UNUSED_VAR(self); |
| } |
| |
| static String* |
| S_fullpath(RAMFolder *self, String *path) { |
| RAMFolderIVARS *const ivars = RAMFolder_IVARS(self); |
| if (Str_Get_Size(ivars->path)) { |
| return Str_newf("%o/%o", ivars->path, path); |
| } |
| else { |
| return Str_Clone(path); |
| } |
| } |
| |
| |