| /* 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 CharBuf* |
| S_fullpath(RAMFolder *self, const CharBuf *path); |
| |
| RAMFolder* |
| RAMFolder_new(const CharBuf *path) { |
| RAMFolder *self = (RAMFolder*)VTable_Make_Obj(RAMFOLDER); |
| return RAMFolder_init(self, path); |
| } |
| |
| RAMFolder* |
| RAMFolder_init(RAMFolder *self, const CharBuf *path) { |
| Folder_init((Folder*)self, path); |
| return self; |
| } |
| |
| void |
| RAMFolder_initialize(RAMFolder *self) { |
| UNUSED_VAR(self); |
| } |
| |
| bool_t |
| RAMFolder_check(RAMFolder *self) { |
| UNUSED_VAR(self); |
| return true; |
| } |
| |
| bool_t |
| RAMFolder_local_mkdir(RAMFolder *self, const CharBuf *name) { |
| if (Hash_Fetch(self->entries, (Obj*)name)) { |
| Err_set_error(Err_new(CB_newf("Can't MkDir, '%o' already exists", |
| name))); |
| return false; |
| } |
| else { |
| CharBuf *fullpath = S_fullpath(self, name); |
| Hash_Store(self->entries, (Obj*)name, |
| (Obj*)RAMFolder_new(fullpath)); |
| DECREF(fullpath); |
| return true; |
| } |
| } |
| |
| FileHandle* |
| RAMFolder_local_open_filehandle(RAMFolder *self, const CharBuf *name, |
| uint32_t flags) { |
| RAMFileHandle *fh; |
| CharBuf *fullpath = S_fullpath(self, name); |
| RAMFile *file = (RAMFile*)Hash_Fetch(self->entries, (Obj*)name); |
| bool_t 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(CB_newf("Not a file: '%o'", fullpath))); |
| DECREF(fullpath); |
| return NULL; |
| } |
| } |
| else if (!can_create) { |
| Err_set_error(Err_new(CB_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(self->entries, (Obj*)name, INCREF(file)); |
| } |
| } |
| else { |
| Err *error = Err_get_error(); |
| ERR_ADD_FRAME(error); |
| } |
| |
| DECREF(fullpath); |
| |
| return (FileHandle*)fh; |
| } |
| |
| DirHandle* |
| RAMFolder_local_open_dir(RAMFolder *self) { |
| RAMDirHandle *dh = RAMDH_new(self); |
| if (!dh) { ERR_ADD_FRAME(Err_get_error()); } |
| return (DirHandle*)dh; |
| } |
| |
| bool_t |
| RAMFolder_local_exists(RAMFolder *self, const CharBuf *name) { |
| return !!Hash_Fetch(self->entries, (Obj*)name); |
| } |
| |
| bool_t |
| RAMFolder_local_is_directory(RAMFolder *self, const CharBuf *name) { |
| Obj *entry = Hash_Fetch(self->entries, (Obj*)name); |
| if (entry && Obj_Is_A(entry, FOLDER)) { return true; } |
| return false; |
| } |
| |
| #define OP_RENAME 1 |
| #define OP_HARD_LINK 2 |
| |
| static bool_t |
| S_rename_or_hard_link(RAMFolder *self, const CharBuf* from, const CharBuf *to, |
| Folder *from_folder, Folder *to_folder, |
| ZombieCharBuf *from_name, ZombieCharBuf *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(CB_newf("File not found: '%o'", from))); |
| return false; |
| } |
| if (!to_folder) { |
| Err_set_error(Err_new(CB_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(CB_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(CB_newf("Not a RAMFolder, but a '%o'", |
| Obj_Get_Class_Name((Obj*)inner_to_folder)))); |
| return false; |
| } |
| |
| // Find the original element. |
| elem = Hash_Fetch(inner_from_folder->entries, (Obj*)from_name); |
| if (!elem) { |
| if (Folder_Is_A(from_folder, COMPOUNDFILEREADER) |
| && Folder_Local_Exists(from_folder, (CharBuf*)from_name) |
| ) { |
| Err_set_error(Err_new(CB_newf("Source file '%o' is virtual", |
| from))); |
| } |
| else { |
| Err_set_error(Err_new(CB_newf("File not found: '%o'", from))); |
| } |
| return false; |
| } |
| |
| // Execute the rename/hard-link. |
| if (op == OP_RENAME) { |
| Obj *existing = Hash_Fetch(inner_to_folder->entries, (Obj*)to_name); |
| if (existing) { |
| bool_t conflict = false; |
| |
| // Return success fast if file is copied on top of itself. |
| if (inner_from_folder == inner_to_folder |
| && ZCB_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(CB_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(inner_to_folder->entries, (Obj*)to_name, INCREF(elem)); |
| DECREF(Hash_Delete(inner_from_folder->entries, (Obj*)from_name)); |
| if (Obj_Is_A(elem, FOLDER)) { |
| CharBuf *newpath = S_fullpath(inner_to_folder, (CharBuf*)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(CB_newf("'%o' isn't a file, it's a %o", |
| from, Obj_Get_Class_Name(elem)))); |
| return false; |
| } |
| else { |
| Obj *existing |
| = Hash_Fetch(inner_to_folder->entries, (Obj*)to_name); |
| if (existing) { |
| Err_set_error(Err_new(CB_newf("'%o' already exists", to))); |
| return false; |
| } |
| else { |
| Hash_Store(inner_to_folder->entries, (Obj*)to_name, |
| INCREF(elem)); |
| } |
| } |
| } |
| else { |
| THROW(ERR, "Unexpected op: %i32", (int32_t)op); |
| } |
| |
| return true; |
| } |
| |
| bool_t |
| RAMFolder_rename(RAMFolder *self, const CharBuf* from, const CharBuf *to) { |
| Folder *from_folder = RAMFolder_Enclosing_Folder(self, from); |
| Folder *to_folder = RAMFolder_Enclosing_Folder(self, to); |
| ZombieCharBuf *from_name = IxFileNames_local_part(from, ZCB_BLANK()); |
| ZombieCharBuf *to_name = IxFileNames_local_part(to, ZCB_BLANK()); |
| bool_t 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()); } |
| return result; |
| } |
| |
| bool_t |
| RAMFolder_hard_link(RAMFolder *self, const CharBuf *from, const CharBuf *to) { |
| Folder *from_folder = RAMFolder_Enclosing_Folder(self, from); |
| Folder *to_folder = RAMFolder_Enclosing_Folder(self, to); |
| ZombieCharBuf *from_name = IxFileNames_local_part(from, ZCB_BLANK()); |
| ZombieCharBuf *to_name = IxFileNames_local_part(to, ZCB_BLANK()); |
| bool_t 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()); } |
| return result; |
| } |
| |
| bool_t |
| RAMFolder_local_delete(RAMFolder *self, const CharBuf *name) { |
| Obj *entry = Hash_Fetch(self->entries, (Obj*)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(inner_folder->entries)) { |
| // Can't delete non-empty dir. |
| return false; |
| } |
| } |
| else { |
| return false; |
| } |
| DECREF(Hash_Delete(self->entries, (Obj*)name)); |
| return true; |
| } |
| else { |
| return false; |
| } |
| } |
| |
| Folder* |
| RAMFolder_local_find_folder(RAMFolder *self, const CharBuf *path) { |
| Folder *local_folder = (Folder*)Hash_Fetch(self->entries, (Obj*)path); |
| if (local_folder && Folder_Is_A(local_folder, FOLDER)) { |
| return local_folder; |
| } |
| return NULL; |
| } |
| |
| void |
| RAMFolder_close(RAMFolder *self) { |
| UNUSED_VAR(self); |
| } |
| |
| static CharBuf* |
| S_fullpath(RAMFolder *self, const CharBuf *path) { |
| if (CB_Get_Size(self->path)) { |
| return CB_newf("%o/%o", self->path, path); |
| } |
| else { |
| return CB_Clone(path); |
| } |
| } |
| |
| |