blob: 244aacab24f39fc5b56bc135008eaffcd3fb21f2 [file] [log] [blame]
/* 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_FSFOLDER
#include "Lucy/Util/ToolSet.h"
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/stat.h>
#ifdef CHY_HAS_SYS_TYPES_H
#include <sys/types.h>
#endif
// For rmdir, (hard) link.
#ifdef CHY_HAS_UNISTD_H
#include <unistd.h>
#endif
// For mkdir, rmdir.
#ifdef CHY_HAS_DIRECT_H
#include <direct.h>
#endif
#include "Lucy/Store/FSFolder.h"
#include "Lucy/Store/CompoundFileReader.h"
#include "Lucy/Store/CompoundFileWriter.h"
#include "Lucy/Store/FSDirHandle.h"
#include "Lucy/Store/FSFileHandle.h"
#include "Lucy/Store/InStream.h"
#include "Lucy/Store/OutStream.h"
#include "Lucy/Util/IndexFileNames.h"
// Return a CharBuf containing a platform-specific absolute filepath.
static CharBuf*
S_fullpath(FSFolder *self, const CharBuf *path);
// Return true if the supplied path is a directory.
static bool_t
S_dir_ok(const CharBuf *path);
// Create a directory, or set Err_error and return false.
bool_t
S_create_dir(const CharBuf *path);
// Return true unless the supplied path contains a slash.
bool_t
S_is_local_entry(const CharBuf *path);
// Create a hard link.
bool_t
S_hard_link(CharBuf *from_path, CharBuf *to_path);
FSFolder*
FSFolder_new(const CharBuf *path) {
FSFolder *self = (FSFolder*)VTable_Make_Obj(FSFOLDER);
return FSFolder_init(self, path);
}
FSFolder*
FSFolder_init(FSFolder *self, const CharBuf *path) {
CharBuf *abs_path = FSFolder_absolutify(path);
Folder_init((Folder*)self, abs_path);
DECREF(abs_path);
return self;
}
void
FSFolder_initialize(FSFolder *self) {
if (!S_dir_ok(self->path)) {
if (!S_create_dir(self->path)) {
RETHROW(INCREF(Err_get_error()));
}
}
}
bool_t
FSFolder_check(FSFolder *self) {
return S_dir_ok(self->path);
}
FileHandle*
FSFolder_local_open_filehandle(FSFolder *self, const CharBuf *name,
uint32_t flags) {
CharBuf *fullpath = S_fullpath(self, name);
FSFileHandle *fh = FSFH_open(fullpath, flags);
if (!fh) { ERR_ADD_FRAME(Err_get_error()); }
DECREF(fullpath);
return (FileHandle*)fh;
}
bool_t
FSFolder_local_mkdir(FSFolder *self, const CharBuf *name) {
CharBuf *dir = S_fullpath(self, name);
bool_t result = S_create_dir(dir);
if (!result) { ERR_ADD_FRAME(Err_get_error()); }
DECREF(dir);
return result;
}
DirHandle*
FSFolder_local_open_dir(FSFolder *self) {
DirHandle *dh = (DirHandle*)FSDH_open(self->path);
if (!dh) { ERR_ADD_FRAME(Err_get_error()); }
return dh;
}
bool_t
FSFolder_local_exists(FSFolder *self, const CharBuf *name) {
if (Hash_Fetch(self->entries, (Obj*)name)) {
return true;
}
else if (!S_is_local_entry(name)) {
return false;
}
else {
struct stat stat_buf;
CharBuf *fullpath = S_fullpath(self, name);
bool_t retval = false;
if (stat((char*)CB_Get_Ptr8(fullpath), &stat_buf) != -1) {
retval = true;
}
DECREF(fullpath);
return retval;
}
}
bool_t
FSFolder_local_is_directory(FSFolder *self, const CharBuf *name) {
// Check for a cached object, then fall back to a system call.
Obj *elem = Hash_Fetch(self->entries, (Obj*)name);
if (elem && Obj_Is_A(elem, FOLDER)) {
return true;
}
else {
CharBuf *fullpath = S_fullpath(self, name);
bool_t result = S_dir_ok(fullpath);
DECREF(fullpath);
return result;
}
}
bool_t
FSFolder_rename(FSFolder *self, const CharBuf* from, const CharBuf *to) {
CharBuf *from_path = S_fullpath(self, from);
CharBuf *to_path = S_fullpath(self, to);
bool_t retval = !rename((char*)CB_Get_Ptr8(from_path),
(char*)CB_Get_Ptr8(to_path));
if (!retval) {
Err_set_error(Err_new(CB_newf("rename from '%o' to '%o' failed: %s",
from_path, to_path, strerror(errno))));
}
DECREF(from_path);
DECREF(to_path);
return retval;
}
bool_t
FSFolder_hard_link(FSFolder *self, const CharBuf *from,
const CharBuf *to) {
CharBuf *from_path = S_fullpath(self, from);
CharBuf *to_path = S_fullpath(self, to);
bool_t retval = S_hard_link(from_path, to_path);
DECREF(from_path);
DECREF(to_path);
return retval;
}
bool_t
FSFolder_local_delete(FSFolder *self, const CharBuf *name) {
CharBuf *fullpath = S_fullpath(self, name);
char *path_ptr = (char*)CB_Get_Ptr8(fullpath);
#ifdef CHY_REMOVE_ZAPS_DIRS
bool_t result = !remove(path_ptr);
#else
bool_t result = !rmdir(path_ptr) || !remove(path_ptr);
#endif
DECREF(Hash_Delete(self->entries, (Obj*)name));
DECREF(fullpath);
return result;
}
void
FSFolder_close(FSFolder *self) {
Hash_Clear(self->entries);
}
Folder*
FSFolder_local_find_folder(FSFolder *self, const CharBuf *name) {
Folder *subfolder = NULL;
if (!name || !CB_Get_Size(name)) {
// No entity can be identified by NULL or empty string.
return NULL;
}
else if (!S_is_local_entry(name)) {
return NULL;
}
else if (CB_Starts_With_Str(name, ".", 1)) {
// Don't allow access outside of the main dir.
return NULL;
}
else if (NULL != (subfolder = (Folder*)Hash_Fetch(self->entries, (Obj*)name))) {
if (Folder_Is_A(subfolder, FOLDER)) {
return subfolder;
}
else {
return NULL;
}
}
CharBuf *fullpath = S_fullpath(self, name);
if (S_dir_ok(fullpath)) {
subfolder = (Folder*)FSFolder_new(fullpath);
if (!subfolder) {
DECREF(fullpath);
THROW(ERR, "Failed to open FSFolder at '%o'", fullpath);
}
// Try to open a CompoundFileReader. On failure, just use the
// existing folder.
CharBuf *cfmeta_file = (CharBuf*)ZCB_WRAP_STR("cfmeta.json", 11);
if (Folder_Local_Exists(subfolder, cfmeta_file)) {
CompoundFileReader *cf_reader = CFReader_open(subfolder);
if (cf_reader) {
DECREF(subfolder);
subfolder = (Folder*)cf_reader;
}
}
Hash_Store(self->entries, (Obj*)name, (Obj*)subfolder);
}
DECREF(fullpath);
return subfolder;
}
static CharBuf*
S_fullpath(FSFolder *self, const CharBuf *path) {
CharBuf *fullpath = CB_newf("%o%s%o", self->path, DIR_SEP, path);
if (DIR_SEP[0] != '/') {
CB_Swap_Chars(fullpath, '/', DIR_SEP[0]);
}
return fullpath;
}
static bool_t
S_dir_ok(const CharBuf *path) {
struct stat stat_buf;
if (stat((char*)CB_Get_Ptr8(path), &stat_buf) != -1) {
if (stat_buf.st_mode & S_IFDIR) { return true; }
}
return false;
}
bool_t
S_create_dir(const CharBuf *path) {
if (-1 == chy_makedir((char*)CB_Get_Ptr8(path), 0777)) {
Err_set_error(Err_new(CB_newf("Couldn't create directory '%o': %s",
path, strerror(errno))));
return false;
}
return true;
}
bool_t
S_is_local_entry(const CharBuf *path) {
ZombieCharBuf *scratch = ZCB_WRAP(path);
uint32_t code_point;
while (0 != (code_point = ZCB_Nip_One(scratch))) {
if (code_point == '/') { return false; }
}
return true;
}
/***************************************************************************/
#if (defined(CHY_HAS_WINDOWS_H) && !defined(__CYGWIN__))
// Windows.h defines INCREF and DECREF, so we include it only at the end of
// this file and undef those symbols.
#undef INCREF
#undef DECREF
#include <windows.h>
bool_t
S_hard_link(CharBuf *from_path, CharBuf *to_path) {
char *from8 = (char*)CB_Get_Ptr8(from_path);
char *to8 = (char*)CB_Get_Ptr8(to_path);
if (CreateHardLink(to8, from8, NULL)) {
return true;
}
else {
char *win_error = Err_win_error();
Err_set_error(Err_new(CB_newf("CreateHardLink for new file '%o' from '%o' failed: %s",
to_path, from_path, win_error)));
FREEMEM(win_error);
return false;
}
}
#elif (defined(CHY_HAS_UNISTD_H))
bool_t
S_hard_link(CharBuf *from_path, CharBuf *to_path) {
char *from8 = (char*)CB_Get_Ptr8(from_path);
char *to8 = (char*)CB_Get_Ptr8(to_path);
if (-1 == link(from8, to8)) {
Err_set_error(Err_new(CB_newf("hard link for new file '%o' from '%o' failed: %s",
to_path, from_path, strerror(errno))));
return false;
}
else {
return true;
}
}
#else
#error "Need either windows.h or unistd.h"
#endif /* CHY_HAS_UNISTD_H vs. CHY_HAS_WINDOWS_H */