/* Copyright (c) 2005 PrimeBase Technologies GmbH * * PrimeBase XT * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * 2005-01-12 Paul McCullagh * * H&G2JCtL */ #include "xt_config.h" #ifdef DRIZZLED #include <bitset> #endif #ifndef XT_WIN #include <unistd.h> #include <dirent.h> #include <sys/mman.h> #endif #include <stdio.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/types.h> #include <ctype.h> #include <string.h> #include <errno.h> #include "strutil_xt.h" #include "pthread_xt.h" #include "thread_xt.h" #include "filesys_xt.h" #include "memory_xt.h" #include "cache_xt.h" #include "sortedlist_xt.h" #include "trace_xt.h" #ifdef DEBUG //#define DEBUG_PRINT_IO //#define DEBUG_TRACE_IO //#define DEBUG_TRACE_MAP_IO //#define DEBUG_TRACE_FILES //#define INJECT_WRITE_REMAP_ERROR /* This is required to make testing on the Mac faster: */ /* It turns of full file sync. */ #define DEBUG_FAST_MAC #endif #ifdef DEBUG_TRACE_FILES //#define PRINTF xt_ftracef #define PRINTF xt_trace #endif #ifdef INJECT_WRITE_REMAP_ERROR #define INJECT_REMAP_FILE_SIZE 1000000 #define INJECT_REMAP_FILE_TYPE "xtd" #endif /* ---------------------------------------------------------------------- * Globals */ typedef struct FsGlobals { xt_mutex_type *fsg_lock; /* The xtPublic cache lock. */ u_int fsg_current_id; XTSortedListPtr fsg_open_files; } FsGlobalsRec; static FsGlobalsRec fs_globals; #ifdef XT_WIN static int fs_get_win_error() { return (int) GetLastError(); } xtPublic void xt_get_win_message(char *buffer, size_t size, int err) { FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buffer, size, NULL); } #endif /* ---------------------------------------------------------------------- * Open file list */ static XTFilePtr fs_new_file(XTThreadPtr self, char *file) { XTFilePtr file_ptr; pushsr_(file_ptr, xt_free, (XTFilePtr) xt_calloc(self, sizeof(XTFileRec))); file_ptr->fil_path = xt_dup_string(self, file); file_ptr->fil_id = fs_globals.fsg_current_id++; #ifdef DEBUG_TRACE_FILES PRINTF("%s: allocated file: (%d) %s\n", self->t_name, (int) file_ptr->fil_id, xt_last_2_names_of_path(file_ptr->fil_path)); #endif if (!fs_globals.fsg_current_id) fs_globals.fsg_current_id++; file_ptr->fil_filedes = XT_NULL_FD; file_ptr->fil_handle_count = 0; popr_(); // Discard xt_free(file_ptr) return file_ptr; } static void fs_close_fmap(XTThreadPtr self, XTFileMemMapPtr mm) { #ifdef XT_WIN if (mm->mm_start) { FlushViewOfFile(mm->mm_start, 0); UnmapViewOfFile(mm->mm_start); mm->mm_start = NULL; } if (mm->mm_mapdes != NULL) { CloseHandle(mm->mm_mapdes); mm->mm_mapdes = NULL; } #else if (mm->mm_start) { msync( (char *)mm->mm_start, (size_t) mm->mm_length, MS_SYNC); munmap((caddr_t) mm->mm_start, (size_t) mm->mm_length); mm->mm_start = NULL; } #endif FILE_MAP_FREE_LOCK(self, &mm->mm_lock); xt_free(self, mm); } static void fs_free_file(XTThreadPtr self, void *XT_UNUSED(thunk), void *item) { XTFilePtr file_ptr = *((XTFilePtr *) item); if (file_ptr->fil_filedes != XT_NULL_FD) { #ifdef DEBUG_TRACE_FILES PRINTF("%s: close file: (%d) %s\n", self->t_name, (int) file_ptr->fil_id, xt_last_2_names_of_path(file_ptr->fil_path)); #endif #ifdef XT_WIN CloseHandle(file_ptr->fil_filedes); #else close(file_ptr->fil_filedes); #endif //PRINTF("close (FILE) %d %s\n", file_ptr->fil_filedes, file_ptr->fil_path); file_ptr->fil_filedes = XT_NULL_FD; } #ifdef DEBUG_TRACE_FILES PRINTF("%s: free file: (%d) %s\n", self->t_name, (int) file_ptr->fil_id, file_ptr->fil_path ? xt_last_2_names_of_path(file_ptr->fil_path) : "?"); #endif if (!file_ptr->fil_ref_count) { ASSERT_NS(!file_ptr->fil_handle_count); /* Flush any cache before this file is invalid: */ if (file_ptr->fil_path) { xt_free(self, file_ptr->fil_path); file_ptr->fil_path = NULL; } xt_free(self, file_ptr); } } static int fs_comp_file(XTThreadPtr XT_UNUSED(self), register const void *XT_UNUSED(thunk), register const void *a, register const void *b) { char *file_name = (char *) a; XTFilePtr file_ptr = *((XTFilePtr *) b); return strcmp(file_name, file_ptr->fil_path); } static int fs_comp_file_ci(XTThreadPtr XT_UNUSED(self), register const void *XT_UNUSED(thunk), register const void *a, register const void *b) { char *file_name = (char *) a; XTFilePtr file_ptr = *((XTFilePtr *) b); return strcasecmp(file_name, file_ptr->fil_path); } /* ---------------------------------------------------------------------- * init & exit */ xtPublic void xt_fs_init(XTThreadPtr self) { fs_globals.fsg_open_files = xt_new_sortedlist(self, sizeof(XTFilePtr), 20, 20, pbxt_ignore_case ? fs_comp_file_ci : fs_comp_file, NULL, fs_free_file, TRUE, FALSE); fs_globals.fsg_lock = fs_globals.fsg_open_files->sl_lock; fs_globals.fsg_current_id = 1; } xtPublic void xt_fs_exit(XTThreadPtr self) { if (fs_globals.fsg_open_files) { xt_free_sortedlist(self, fs_globals.fsg_open_files); fs_globals.fsg_open_files = NULL; } fs_globals.fsg_lock = NULL; fs_globals.fsg_current_id = 0; } /* ---------------------------------------------------------------------- * File operations */ static void fs_set_stats(XTThreadPtr self, char *path) { char super_path[PATH_MAX]; struct stat stats; char *ptr; ptr = xt_last_name_of_path(path); if (ptr == path) strcpy(super_path, "."); else { xt_strcpy(PATH_MAX, super_path, path); if ((ptr = xt_last_name_of_path(super_path))) *ptr = 0; } if (stat(super_path, &stats) == -1) xt_throw_ferrno(XT_CONTEXT, errno, super_path); if (chmod(path, stats.st_mode) == -1) xt_throw_ferrno(XT_CONTEXT, errno, path); /*chown(path, stats.st_uid, stats.st_gid);*/ } xtPublic char *xt_file_path(struct XTFileRef *of) { return of->fr_file->fil_path; } xtBool xt_fs_exists(char *path) { int err; err = access(path, F_OK); if (err == -1) return FALSE; return TRUE; } /* * No error is generated if the file dose not exist. */ xtPublic xtBool xt_fs_delete(XTThreadPtr self, char *name) { #ifdef DEBUG_TRACE_FILES PRINTF("%s: DELETE FILE: %s\n", xt_get_self()->t_name, xt_last_2_names_of_path(name)); #endif #ifdef XT_WIN //PRINTF("delete %s\n", name); if (!DeleteFile(name)) { int err = fs_get_win_error(); if (!XT_FILE_NOT_FOUND(err)) { xt_throw_ferrno(XT_CONTEXT, err, name); return FAILED; } } #else if (unlink(name) == -1) { int err = errno; if (err != ENOENT) { xt_throw_ferrno(XT_CONTEXT, err, name); return FAILED; } } #endif return OK; } xtPublic xtBool xt_fs_file_not_found(int err) { #ifdef XT_WIN return XT_FILE_NOT_FOUND(err); #else return err == ENOENT; #endif } xtPublic void xt_fs_move(struct XTThread *self, char *from_path, char *to_path) { #ifdef DEBUG_TRACE_FILES PRINTF("%s: MOVE FILE: %s --> %s\n", xt_get_self()->t_name, xt_last_2_names_of_path(from_path), xt_last_2_names_of_path(to_path)); #endif #ifdef XT_WIN if (!MoveFile(from_path, to_path)) xt_throw_ferrno(XT_CONTEXT, fs_get_win_error(), from_path); #else int err; if (link(from_path, to_path) == -1) { err = errno; xt_throw_ferrno(XT_CONTEXT, err, from_path); } if (unlink(from_path) == -1) { err = errno; unlink(to_path); xt_throw_ferrno(XT_CONTEXT, err, from_path); } #endif } xtPublic xtBool xt_fs_rename(struct XTThread *self, char *from_path, char *to_path) { int err; #ifdef DEBUG_TRACE_FILES PRINTF("%s: RENAME FILE: %s --> %s\n", xt_get_self()->t_name, xt_last_2_names_of_path(from_path), xt_last_2_names_of_path(to_path)); #endif if (rename(from_path, to_path) == -1) { err = errno; xt_throw_ferrno(XT_CONTEXT, err, from_path); return FAILED; } return OK; } xtPublic xtBool xt_fs_stat(XTThreadPtr self, char *path, off_t *size, struct timespec *mod_time) { #ifdef XT_WIN HANDLE fh; BY_HANDLE_FILE_INFORMATION info; SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES), 0, 0 }; fh = CreateFile( path, GENERIC_READ, FILE_SHARE_READ, &sa, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (fh == INVALID_HANDLE_VALUE) { xt_throw_ferrno(XT_CONTEXT, fs_get_win_error(), path); return FAILED; } if (!GetFileInformationByHandle(fh, &info)) { CloseHandle(fh); xt_throw_ferrno(XT_CONTEXT, fs_get_win_error(), path); return FAILED; } CloseHandle(fh); if (size) *size = (off_t) info.nFileSizeLow | (((off_t) info.nFileSizeHigh) << 32); memset(mod_time, 0, sizeof(*mod_time)); #else struct stat sb; if (stat(path, &sb) == -1) { xt_throw_ferrno(XT_CONTEXT, errno, path); return FAILED; } if (size) *size = sb.st_size; if (mod_time) { mod_time->tv_sec = sb.st_mtime; #ifdef XT_MAC /* This is the Mac OS X version: */ mod_time->tv_nsec = sb.st_mtimespec.tv_nsec; #else #ifdef __USE_MISC /* This is the Linux version: */ mod_time->tv_nsec = sb.st_mtim.tv_nsec; #else /* Not supported? */ mod_time->tv_nsec = 0; #endif #endif } #endif return OK; } void xt_fs_mkdir(XTThreadPtr self, char *name) { char path[PATH_MAX]; xt_strcpy(PATH_MAX, path, name); xt_remove_dir_char(path); #ifdef XT_WIN { SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES), 0, 0 }; if (!CreateDirectory(path, &sa)) xt_throw_ferrno(XT_CONTEXT, fs_get_win_error(), path); } #else if (mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO) == -1) xt_throw_ferrno(XT_CONTEXT, errno, path); try_(a) { fs_set_stats(self, path); } catch_(a) { xt_fs_rmdir(NULL, name); throw_(); } cont_(a); #endif } void xt_fs_mkpath(XTThreadPtr self, char *path) { char *ptr; if (xt_fs_exists(path)) return; if (!(ptr = (char *) xt_last_directory_of_path((c_char *) path))) return; if (ptr == path) return; ptr--; if (XT_IS_DIR_CHAR(*ptr)) { *ptr = 0; xt_fs_mkpath(self, path); *ptr = XT_DIR_CHAR; xt_fs_mkdir(self, path); } } xtBool xt_fs_rmdir(XTThreadPtr self, char *name) { char path[PATH_MAX]; xt_strcpy(PATH_MAX, path, name); xt_remove_dir_char(path); #ifdef XT_WIN if (!RemoveDirectory(path)) { int err = fs_get_win_error(); if (!XT_FILE_NOT_FOUND(err)) { xt_throw_ferrno(XT_CONTEXT, err, path); return FAILED; } } #else if (rmdir(path) == -1) { int err = errno; if (err != ENOENT) { xt_throw_ferrno(XT_CONTEXT, err, path); return FAILED; } } #endif return OK; } /* ---------------------------------------------------------------------- * Open & Close operations */ xtPublic XTFilePtr xt_fs_get_file(XTThreadPtr self, char *file_name) { XTFilePtr file_ptr, *file_pptr; xt_sl_lock(self, fs_globals.fsg_open_files); pushr_(xt_sl_unlock, fs_globals.fsg_open_files); if ((file_pptr = (XTFilePtr *) xt_sl_find(self, fs_globals.fsg_open_files, file_name))) file_ptr = *file_pptr; else { file_ptr = fs_new_file(self, file_name); xt_sl_insert(self, fs_globals.fsg_open_files, file_name, &file_ptr); } file_ptr->fil_ref_count++; freer_(); // xt_sl_unlock(fs_globals.fsg_open_files) return file_ptr; } xtPublic void xt_fs_release_file(XTThreadPtr self, XTFilePtr file_ptr) { xt_sl_lock(self, fs_globals.fsg_open_files); pushr_(xt_sl_unlock, fs_globals.fsg_open_files); file_ptr->fil_ref_count--; if (!file_ptr->fil_ref_count) { xt_sl_delete(self, fs_globals.fsg_open_files, file_ptr->fil_path); } freer_(); // xt_ht_unlock(fs_globals.fsg_open_files) } static xtBool fs_open_file(XTThreadPtr self, XT_FD *fd, XTFilePtr file, int mode) { int retried = FALSE; #ifdef DEBUG_TRACE_FILES PRINTF("%s: OPEN FILE: (%d) %s\n", self->t_name, (int) file->fil_id, xt_last_2_names_of_path(file->fil_path)); #endif retry: #ifdef XT_WIN SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES), 0, 0 }; DWORD flags; if (mode & XT_FS_EXCLUSIVE) flags = CREATE_NEW; else if (mode & XT_FS_CREATE) flags = OPEN_ALWAYS; else flags = OPEN_EXISTING; *fd = CreateFile( file->fil_path, mode & XT_FS_READONLY ? GENERIC_READ : (GENERIC_READ | GENERIC_WRITE), FILE_SHARE_READ | FILE_SHARE_WRITE, &sa, flags, FILE_FLAG_RANDOM_ACCESS, NULL); if (*fd == INVALID_HANDLE_VALUE) { int err = fs_get_win_error(); if (!(mode & XT_FS_MISSING_OK) || !XT_FILE_NOT_FOUND(err)) { if (!retried && (mode & XT_FS_MAKE_PATH) && XT_FILE_NOT_FOUND(err)) { char path[PATH_MAX]; xt_strcpy(PATH_MAX, path, file->fil_path); xt_remove_last_name_of_path(path); xt_fs_mkpath(self, path); retried = TRUE; goto retry; } xt_throw_ferrno(XT_CONTEXT, err, file->fil_path); } /* File is missing, but don't throw an error. */ return FAILED; } //PRINTF("open %d %s\n", *fd, file->fil_path); return OK; #else int flags = 0; if (mode & XT_FS_READONLY) flags = O_RDONLY; else flags = O_RDWR; if (mode & XT_FS_CREATE) flags |= O_CREAT; if (mode & XT_FS_EXCLUSIVE) flags |= O_EXCL; #ifdef O_DIRECT if (mode & XT_FS_DIRECT_IO) flags |= O_DIRECT; #endif *fd = open(file->fil_path, flags, XT_MASK); if (*fd == -1) { int err = errno; if (!(mode & XT_FS_MISSING_OK) || err != ENOENT) { if (!retried && (mode & XT_FS_MAKE_PATH) && err == ENOENT) { char path[PATH_MAX]; xt_strcpy(PATH_MAX, path, file->fil_path); xt_remove_last_name_of_path(path); xt_fs_mkpath(self, path); retried = TRUE; goto retry; } xt_throw_ferrno(XT_CONTEXT, err, file->fil_path); } /* File is missing, but don't throw an error. */ return FAILED; } ///PRINTF("open %d %s\n", *fd, file->fil_path); return OK; #endif } xtPublic XTOpenFilePtr xt_open_file(XTThreadPtr self, char *file, int mode) { XTOpenFilePtr of; pushsr_(of, xt_close_file, (XTOpenFilePtr) xt_calloc(self, sizeof(XTOpenFileRec))); of->fr_file = xt_fs_get_file(self, file); of->fr_id = of->fr_file->fil_id; of->of_filedes = XT_NULL_FD; #ifdef XT_WIN if (!fs_open_file(self, &of->of_filedes, of->fr_file, mode)) { xt_close_file(self, of); of = NULL; } #else xtBool failed = FALSE; if (of->fr_file->fil_filedes == -1) { xt_sl_lock(self, fs_globals.fsg_open_files); pushr_(xt_sl_unlock, fs_globals.fsg_open_files); if (of->fr_file->fil_filedes == -1) { if (!fs_open_file(self, &of->fr_file->fil_filedes, of->fr_file, mode)) failed = TRUE; } freer_(); // xt_ht_unlock(fs_globals.fsg_open_files) } if (failed) { /* Close, but after we have release the fsg_open_files lock! */ xt_close_file(self, of); of = NULL; } else of->of_filedes = of->fr_file->fil_filedes; #endif popr_(); // Discard xt_close_file(of) return of; } xtPublic XTOpenFilePtr xt_open_file_ns(char *file, int mode) { XTThreadPtr self = xt_get_self(); XTOpenFilePtr of; try_(a) { of = xt_open_file(self, file, mode); } catch_(a) { of = NULL; } cont_(a); return of; } xtPublic xtBool xt_open_file_ns(XTOpenFilePtr *fh, char *file, int mode) { XTThreadPtr self = xt_get_self(); xtBool ok = TRUE; try_(a) { *fh = xt_open_file(self, file, mode); } catch_(a) { ok = FALSE; } cont_(a); return ok; } xtPublic void xt_close_file(XTThreadPtr self, XTOpenFilePtr of) { if (of->of_filedes != XT_NULL_FD) { #ifdef XT_WIN CloseHandle(of->of_filedes); #ifdef DEBUG_TRACE_FILES PRINTF("%s: close file: (%d) %s\n", self->t_name, (int) of->fr_file->fil_id, xt_last_2_names_of_path(of->fr_file->fil_path)); #endif #else if (!of->fr_file || of->of_filedes != of->fr_file->fil_filedes) { close(of->of_filedes); #ifdef DEBUG_TRACE_FILES PRINTF("%s: close file: (%d) %s\n", self->t_name, (int) of->fr_file->fil_id, xt_last_2_names_of_path(of->fr_file->fil_path)); #endif } #endif of->of_filedes = XT_NULL_FD; } if (of->fr_file) { xt_fs_release_file(self, of->fr_file); of->fr_file = NULL; } xt_free(self, of); } xtPublic xtBool xt_close_file_ns(XTOpenFilePtr of) { XTThreadPtr self = xt_get_self(); xtBool failed = FALSE; try_(a) { xt_close_file(self, of); } catch_(a) { failed = TRUE; } cont_(a); return failed; } /* ---------------------------------------------------------------------- * I/O operations */ xtPublic xtBool xt_lock_file(struct XTThread *self, XTOpenFilePtr of) { #ifdef XT_WIN if (!LockFile(of->of_filedes, 0, 0, 512, 0)) { int err = fs_get_win_error(); if (err == ERROR_LOCK_VIOLATION || err == ERROR_LOCK_FAILED) return FAILED; xt_throw_ferrno(XT_CONTEXT, err, xt_file_path(of)); return FAILED; } return OK; #else if (lockf(of->of_filedes, F_TLOCK, 0) == 0) return OK; if (errno == EAGAIN) return FAILED; xt_throw_ferrno(XT_CONTEXT, errno, xt_file_path(of)); return FAILED; #endif } xtPublic void xt_unlock_file(struct XTThread *self, XTOpenFilePtr of) { #ifdef XT_WIN if (!UnlockFile(of->of_filedes, 0, 0, 512, 0)) { int err = fs_get_win_error(); if (err != ERROR_NOT_LOCKED) xt_throw_ferrno(XT_CONTEXT, err, xt_file_path(of)); } #else if (lockf(of->of_filedes, F_ULOCK, 0) == -1) xt_throw_ferrno(XT_CONTEXT, errno, xt_file_path(of)); #endif } static off_t fs_seek_eof(XTThreadPtr self, XT_FD fd, XTFilePtr file) { #ifdef XT_WIN DWORD result; LARGE_INTEGER lpFileSize; result = SetFilePointer(fd, 0, NULL, FILE_END); if (result == 0xFFFFFFFF) { xt_throw_ferrno(XT_CONTEXT, fs_get_win_error(), file->fil_path); return (off_t) -1; } if (!GetFileSizeEx(fd, &lpFileSize)) { xt_throw_ferrno(XT_CONTEXT, fs_get_win_error(), file->fil_path); return (off_t) -1; } return lpFileSize.QuadPart; #else off_t off; off = lseek(fd, 0, SEEK_END); if (off == -1) { xt_throw_ferrno(XT_CONTEXT, errno, file->fil_path); return -1; } return off; #endif } xtPublic off_t xt_seek_eof_file(XTThreadPtr self, XTOpenFilePtr of) { return fs_seek_eof(self, of->of_filedes, of->fr_file); } xtPublic xtBool xt_set_eof_file(XTThreadPtr self, XTOpenFilePtr of, off_t offset) { #ifdef XT_WIN LARGE_INTEGER liDistanceToMove; liDistanceToMove.QuadPart = offset; if (!SetFilePointerEx(of->of_filedes, liDistanceToMove, NULL, FILE_BEGIN)) { xt_throw_ferrno(XT_CONTEXT, fs_get_win_error(), xt_file_path(of)); return FAILED; } if (!SetEndOfFile(of->of_filedes)) { xt_throw_ferrno(XT_CONTEXT, fs_get_win_error(), xt_file_path(of)); return FAILED; } #else if (ftruncate(of->of_filedes, offset) == -1) { xt_throw_ferrno(XT_CONTEXT, errno, xt_file_path(of)); return FAILED; } #endif return OK; } xtPublic xtBool xt_pwrite_file(XTOpenFilePtr of, off_t offset, size_t size, void *data, XTIOStatsPtr stat, XTThreadPtr XT_UNUSED(thread)) { #ifdef DEBUG_PRINT_IO PRINTF("PBXT WRITE %s offs=%d size=%d\n", of->fr_file->fil_path, (int) offset, (int) size); #endif #ifdef DEBUG_TRACE_IO char timef[50]; xtWord8 start = xt_trace_clock(); #endif #ifdef XT_WIN LARGE_INTEGER liDistanceToMove; DWORD result; liDistanceToMove.QuadPart = offset; if (!SetFilePointerEx(of->of_filedes, liDistanceToMove, NULL, FILE_BEGIN)) return xt_register_ferrno(XT_REG_CONTEXT, fs_get_win_error(), xt_file_path(of)); if (!WriteFile(of->of_filedes, data, size, &result, NULL)) return xt_register_ferrno(XT_REG_CONTEXT, fs_get_win_error(), xt_file_path(of)); if (result != size) return xt_register_ferrno(XT_REG_CONTEXT, ERROR_HANDLE_EOF, xt_file_path(of)); #else ssize_t write_size; write_size = pwrite(of->of_filedes, data, size, offset); if (write_size == -1) return xt_register_ferrno(XT_REG_CONTEXT, errno, xt_file_path(of)); if ((size_t) write_size != size) return xt_register_ferrno(XT_REG_CONTEXT, ESPIPE, xt_file_path(of)); #endif stat->ts_write += (u_int) size; #ifdef DEBUG_TRACE_IO xt_trace("/* %s */ pbxt_file_writ(\"%s\", %lu, %lu);\n", xt_trace_clock_diff(timef, start), of->fr_file->fil_path, (u_long) offset, (u_long) size); #endif return OK; } xtPublic xtBool xt_flush_file(XTOpenFilePtr of, XTIOStatsPtr stat, XTThreadPtr XT_UNUSED(thread)) { xtWord8 s; #ifdef DEBUG_PRINT_IO PRINTF("PBXT FLUSH %s\n", of->fr_file->fil_path); #endif #ifdef DEBUG_TRACE_IO char timef[50]; xtWord8 start = xt_trace_clock(); #endif stat->ts_flush_start = xt_trace_clock(); #ifdef XT_WIN if (!FlushFileBuffers(of->of_filedes)) { xt_register_ferrno(XT_REG_CONTEXT, fs_get_win_error(), xt_file_path(of)); goto failed; } #else /* Mac OS X has problems with fsync. We had several cases of index corruption presumably because * fsync didn't really flush index pages to disk. fcntl(F_FULLFSYNC) is considered more effective * in such case. */ #if defined(F_FULLFSYNC) && !defined(DEBUG_FAST_MAC) if (fcntl(of->of_filedes, F_FULLFSYNC, 0) == -1) { xt_register_ferrno(XT_REG_CONTEXT, errno, xt_file_path(of)); goto failed; } #else if (fsync(of->of_filedes) == -1) { xt_register_ferrno(XT_REG_CONTEXT, errno, xt_file_path(of)); goto failed; } #endif #endif #ifdef DEBUG_TRACE_IO xt_trace("/* %s */ pbxt_file_sync(\"%s\");\n", xt_trace_clock_diff(timef, start), of->fr_file->fil_path); #endif s = stat->ts_flush_start; stat->ts_flush_start = 0; stat->ts_flush_time += xt_trace_clock() - s; stat->ts_flush++; return OK; failed: s = stat->ts_flush_start; stat->ts_flush_start = 0; stat->ts_flush_time += xt_trace_clock() - s; return FAILED; } xtBool xt_pread_file(XTOpenFilePtr of, off_t offset, size_t size, size_t min_size, void *data, size_t *red_size, XTIOStatsPtr stat, XTThreadPtr XT_UNUSED(thread)) { #ifdef DEBUG_PRINT_IO PRINTF("PBXT READ %s offset=%d size=%d\n", of->fr_file->fil_path, (int) offset, (int) size); #endif #ifdef DEBUG_TRACE_IO char timef[50]; xtWord8 start = xt_trace_clock(); #endif #ifdef XT_WIN LARGE_INTEGER liDistanceToMove; DWORD result; liDistanceToMove.QuadPart = offset; if (!SetFilePointerEx(of->of_filedes, liDistanceToMove, NULL, FILE_BEGIN)) return xt_register_ferrno(XT_REG_CONTEXT, fs_get_win_error(), xt_file_path(of)); if (!ReadFile(of->of_filedes, data, size, &result, NULL)) return xt_register_ferrno(XT_REG_CONTEXT, fs_get_win_error(), xt_file_path(of)); if ((size_t) result < min_size) return xt_register_ferrno(XT_REG_CONTEXT, ERROR_HANDLE_EOF, xt_file_path(of)); if (red_size) *red_size = (size_t) result; stat->ts_read += (u_int) result; #else ssize_t read_size; read_size = pread(of->of_filedes, data, size, offset); if (read_size == -1) return xt_register_ferrno(XT_REG_CONTEXT, errno, xt_file_path(of)); /* Throw an error if read less than the minimum: */ if ((size_t) read_size < min_size) { //PRINTF("PMC PBXT <-- offset:%llu, count:%lu \n", (u_llong) offset, (u_long) size); return xt_register_ferrno(XT_REG_CONTEXT, ESPIPE, xt_file_path(of)); } if (red_size) *red_size = (size_t) read_size; stat->ts_read += (u_int) read_size; #endif #ifdef DEBUG_TRACE_IO xt_trace("/* %s */ pbxt_file_read(\"%s\", %lu, %lu);\n", xt_trace_clock_diff(timef, start), of->fr_file->fil_path, (u_long) offset, (u_long) size); #endif return OK; } xtPublic xtBool xt_lock_file_ptr(XTOpenFilePtr of, xtWord1 **data, off_t offset, size_t size, XTIOStatsPtr stat, XTThreadPtr thread) { size_t red_size; if (!*data) { if (!(*data = (xtWord1 *) xt_malloc_ns(size))) return FAILED; } if (!xt_pread_file(of, offset, size, 0, *data, &red_size, stat, thread)) return FAILED; //if (red_size < size) // memset(); return OK; } xtPublic void xt_unlock_file_ptr(XTOpenFilePtr XT_UNUSED(of), xtWord1 *data, XTThreadPtr XT_UNUSED(thread)) { if (data) xt_free_ns(data); } /* ---------------------------------------------------------------------- * Directory operations */ /* * The filter may contain one '*' as wildcard. */ XTOpenDirPtr xt_dir_open(XTThreadPtr self, c_char *path, c_char *filter) { XTOpenDirPtr od; #ifdef XT_SOLARIS /* see the comment in filesys_xt.h */ size_t sz = pathconf(path, _PC_NAME_MAX) + sizeof(XTOpenDirRec) + 1; #else size_t sz = sizeof(XTOpenDirRec); #endif pushsr_(od, xt_dir_close, (XTOpenDirPtr) xt_calloc(self, sz)); #ifdef XT_WIN size_t len; od->od_handle = XT_NULL_FD; // path = path\(filter | *) len = strlen(path) + 1 + (filter ? strlen(filter) : 1) + 1; od->od_path = (char *) xt_malloc(self, len); strcpy(od->od_path, path); xt_add_dir_char(len, od->od_path); if (filter) strcat(od->od_path, filter); else strcat(od->od_path, "*"); #else od->od_path = xt_dup_string(self, path); if (filter) od->od_filter = xt_dup_string(self, filter); od->od_dir = opendir(path); if (!od->od_dir) xt_throw_ferrno(XT_CONTEXT, errno, path); #endif popr_(); // Discard xt_dir_close(od) return od; } void xt_dir_close(XTThreadPtr self, XTOpenDirPtr od) { if (od) { #ifdef XT_WIN if (od->od_handle != XT_NULL_FD) { FindClose(od->od_handle); od->od_handle = XT_NULL_FD; } #else if (od->od_dir) { closedir(od->od_dir); od->od_dir = NULL; } if (od->od_filter) { xt_free(self, od->od_filter); od->od_filter = NULL; } #endif if (od->od_path) { xt_free(self, od->od_path); od->od_path = NULL; } xt_free(self, od); } } #ifdef XT_WIN xtBool xt_dir_next(XTThreadPtr self, XTOpenDirPtr od) { int err = 0; if (od->od_handle == INVALID_HANDLE_VALUE) { od->od_handle = FindFirstFile(od->od_path, &od->od_data); if (od->od_handle == INVALID_HANDLE_VALUE) err = fs_get_win_error(); } else { if (!FindNextFile(od->od_handle, &od->od_data)) err = fs_get_win_error(); } if (err) { if (err != ERROR_NO_MORE_FILES) { if (err == ERROR_FILE_NOT_FOUND) { char path[PATH_MAX]; xt_strcpy(PATH_MAX, path, od->od_path); xt_remove_last_name_of_path(path); if (!xt_fs_exists(path)) xt_throw_ferrno(XT_CONTEXT, err, path); } else xt_throw_ferrno(XT_CONTEXT, err, od->od_path); } return FAILED; } return OK; } #else static xtBool fs_match_filter(c_char *name, c_char *filter) { while (*name && *filter) { if (*filter == '*') { if (filter[1] == *name) filter++; else name++; } else { if (*name != *filter) return FALSE; name++; filter++; } } if (!*name) { if (!*filter || (*filter == '*' && !filter[1])) return TRUE; } return FALSE; } xtBool xt_dir_next(XTThreadPtr self, XTOpenDirPtr od) { int err; struct dirent *result; for (;;) { err = readdir_r(od->od_dir, &od->od_entry, &result); if (err) { xt_throw_ferrno(XT_CONTEXT, err, od->od_path); return FAILED; } if (!result) break; /* Filter out '.' and '..': */ if (od->od_entry.d_name[0] == '.') { if (od->od_entry.d_name[1] == '.') { if (od->od_entry.d_name[2] == '\0') continue; } else { if (od->od_entry.d_name[1] == '\0') continue; } } if (!od->od_filter) break; if (fs_match_filter(od->od_entry.d_name, od->od_filter)) break; } return result ? TRUE : FALSE; } #endif char *xt_dir_name(XTThreadPtr XT_UNUSED(self), XTOpenDirPtr od) { #ifdef XT_WIN return od->od_data.cFileName; #else return od->od_entry.d_name; #endif } xtBool xt_dir_is_file(XTThreadPtr self, XTOpenDirPtr od) { (void) self; #ifdef XT_WIN if (od->od_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) return FALSE; #elif defined(XT_SOLARIS) char path[PATH_MAX]; struct stat sb; xt_strcpy(PATH_MAX, path, od->od_path); xt_add_dir_char(PATH_MAX, path); xt_strcat(PATH_MAX, path, od->od_entry.d_name); if (stat(path, &sb) == -1) { xt_throw_ferrno(XT_CONTEXT, errno, path); return FAILED; } if ( sb.st_mode & S_IFDIR ) return FALSE; #else if (od->od_entry.d_type & DT_DIR) return FALSE; #endif return TRUE; } off_t xt_dir_file_size(XTThreadPtr self, XTOpenDirPtr od) { #ifdef XT_WIN return (off_t) od->od_data.nFileSizeLow | (((off_t) od->od_data.nFileSizeHigh) << 32); #else char path[PATH_MAX]; off_t size; xt_strcpy(PATH_MAX, path, od->od_path); xt_add_dir_char(PATH_MAX, path); xt_strcat(PATH_MAX, path, od->od_entry.d_name); if (!xt_fs_stat(self, path, &size, NULL)) return -1; return size; #endif } /* ---------------------------------------------------------------------- * File mapping operations */ static xtBool fs_map_file(XTFileMemMapPtr mm, XTFilePtr file, xtBool grow) { #ifdef INJECT_WRITE_REMAP_ERROR if (xt_is_extension(file->fil_path, INJECT_REMAP_FILE_TYPE)) { if (mm->mm_length > INJECT_REMAP_FILE_SIZE) { xt_register_ferrno(XT_REG_CONTEXT, 30, file->fil_path); return FAILED; } } #endif ASSERT_NS(!mm->mm_start); #ifdef XT_WIN /* This will grow the file to the given size: */ mm->mm_mapdes = CreateFileMapping(file->fil_filedes, NULL, PAGE_READWRITE, (DWORD) (mm->mm_length >> 32), (DWORD) mm->mm_length, NULL); if (mm->mm_mapdes == NULL) { xt_register_ferrno(XT_REG_CONTEXT, fs_get_win_error(), file->fil_path); return FAILED; } mm->mm_start = (xtWord1 *) MapViewOfFile(mm->mm_mapdes, FILE_MAP_WRITE, 0, 0, 0); if (!mm->mm_start) { CloseHandle(mm->mm_mapdes); mm->mm_mapdes = NULL; xt_register_ferrno(XT_REG_CONTEXT, fs_get_win_error(), file->fil_path); return FAILED; } #else if (grow) { char data[2]; if (pwrite(file->fil_filedes, data, 1, mm->mm_length - 1) == -1) { xt_register_ferrno(XT_REG_CONTEXT, errno, file->fil_path); return FAILED; } } /* Remap: */ mm->mm_start = (xtWord1 *) mmap(0, (size_t) mm->mm_length, PROT_READ | PROT_WRITE, MAP_SHARED, file->fil_filedes, 0); if (mm->mm_start == MAP_FAILED) { mm->mm_start = NULL; xt_register_ferrno(XT_REG_CONTEXT, errno, file->fil_path); return FAILED; } #endif return OK; } xtPublic XTMapFilePtr xt_open_fmap(XTThreadPtr self, char *file, size_t grow_size) { XTMapFilePtr map; pushsr_(map, xt_close_fmap, (XTMapFilePtr) xt_calloc(self, sizeof(XTMapFileRec))); map->fr_file = xt_fs_get_file(self, file); map->fr_id = map->fr_file->fil_id; xt_sl_lock(self, fs_globals.fsg_open_files); pushr_(xt_sl_unlock, fs_globals.fsg_open_files); if (map->fr_file->fil_filedes == XT_NULL_FD) { if (!fs_open_file(self, &map->fr_file->fil_filedes, map->fr_file, XT_FS_DEFAULT)) { xt_close_fmap(self, map); map = NULL; } } map->fr_file->fil_handle_count++; freer_(); // xt_ht_unlock(fs_globals.fsg_open_files) if (!map->fr_file->fil_memmap) { xt_sl_lock(self, fs_globals.fsg_open_files); pushr_(xt_sl_unlock, fs_globals.fsg_open_files); if (!map->fr_file->fil_memmap) { XTFileMemMapPtr mm; mm = (XTFileMemMapPtr) xt_calloc(self, sizeof(XTFileMemMapRec)); pushr_(fs_close_fmap, mm); #ifdef XT_WIN /* NULL is the value returned on error! */ mm->mm_mapdes = NULL; #endif FILE_MAP_INIT_LOCK(self, &mm->mm_lock); mm->mm_length = fs_seek_eof(self, map->fr_file->fil_filedes, map->fr_file); if (sizeof(size_t) == 4 && mm->mm_length >= (off_t) 0xFFFFFFFF) xt_throw_ixterr(XT_CONTEXT, XT_ERR_FILE_TOO_LONG, map->fr_file->fil_path); mm->mm_grow_size = grow_size; if (mm->mm_length < (off_t) grow_size) { mm->mm_length = (off_t) grow_size; if (!fs_map_file(mm, map->fr_file, TRUE)) xt_throw(self); } else { if (!fs_map_file(mm, map->fr_file, FALSE)) xt_throw(self); } popr_(); // Discard fs_close_fmap(mm) map->fr_file->fil_memmap = mm; } freer_(); // xt_ht_unlock(fs_globals.fsg_open_files) } map->mf_memmap = map->fr_file->fil_memmap; popr_(); // Discard xt_close_fmap(map) return map; } xtPublic void xt_close_fmap(XTThreadPtr self, XTMapFilePtr map) { ASSERT_NS(!map->mf_slock_count); if (map->fr_file) { xt_sl_lock(self, fs_globals.fsg_open_files); pushr_(xt_sl_unlock, fs_globals.fsg_open_files); map->fr_file->fil_handle_count--; if (!map->fr_file->fil_handle_count) { fs_close_fmap(self, map->fr_file->fil_memmap); map->fr_file->fil_memmap = NULL; } freer_(); xt_fs_release_file(self, map->fr_file); map->fr_file = NULL; } map->mf_memmap = NULL; xt_free(self, map); } xtPublic xtBool xt_close_fmap_ns(XTMapFilePtr map) { XTThreadPtr self = xt_get_self(); xtBool failed = FALSE; try_(a) { xt_close_fmap(self, map); } catch_(a) { failed = TRUE; } cont_(a); return failed; } static xtBool fs_remap_file(XTMapFilePtr map, off_t offset, size_t size, XTIOStatsPtr stat) { off_t new_size = 0; XTFileMemMapPtr mm = map->mf_memmap; xtWord8 s; if (offset + (off_t) size > mm->mm_length) { /* Expand the file: */ new_size = (mm->mm_length + (off_t) mm->mm_grow_size) / (off_t) mm->mm_grow_size; new_size *= mm->mm_grow_size; while (new_size < offset + (off_t) size) new_size += mm->mm_grow_size; if (sizeof(size_t) == 4 && new_size >= (off_t) 0xFFFFFFFF) { xt_register_ixterr(XT_REG_CONTEXT, XT_ERR_FILE_TOO_LONG, xt_file_path(map)); return FAILED; } } else if (!mm->mm_start) new_size = mm->mm_length; if (new_size) { if (mm->mm_start) { /* Flush & unmap: */ stat->ts_flush_start = xt_trace_clock(); #ifdef XT_WIN if (!FlushViewOfFile(mm->mm_start, 0)) { xt_register_ferrno(XT_REG_CONTEXT, fs_get_win_error(), xt_file_path(map)); goto failed; } if (!UnmapViewOfFile(mm->mm_start)) { xt_register_ferrno(XT_REG_CONTEXT, fs_get_win_error(), xt_file_path(map)); goto failed; } #else if (msync( (char *)mm->mm_start, (size_t) mm->mm_length, MS_SYNC) == -1) { xt_register_ferrno(XT_REG_CONTEXT, errno, xt_file_path(map)); goto failed; } /* Unmap: */ if (munmap((caddr_t) mm->mm_start, (size_t) mm->mm_length) == -1) { xt_register_ferrno(XT_REG_CONTEXT, errno, xt_file_path(map)); goto failed; } #endif s = stat->ts_flush_start; stat->ts_flush_start = 0; stat->ts_flush_time += xt_trace_clock() - s; stat->ts_flush++; } mm->mm_start = NULL; #ifdef XT_WIN /* It is possible that a previous remap attempt has failed: the map was closed * but the new map was not allocated (e.g. because of insufficient disk space). * In this case mm->mm_mapdes will be NULL. */ if (mm->mm_mapdes && !CloseHandle(mm->mm_mapdes)) return xt_register_ferrno(XT_REG_CONTEXT, fs_get_win_error(), xt_file_path(map)); mm->mm_mapdes = NULL; #endif off_t old_size = mm->mm_length; mm->mm_length = new_size; if (!fs_map_file(mm, map->fr_file, TRUE)) { /* Try to restore old mapping */ mm->mm_length = old_size; fs_map_file(mm, map->fr_file, FALSE); return FAILED; } } return OK; failed: s = stat->ts_flush_start; stat->ts_flush_start = 0; stat->ts_flush_time += xt_trace_clock() - s; return FAILED; } xtPublic xtBool xt_pwrite_fmap(XTMapFilePtr map, off_t offset, size_t size, void *data, XTIOStatsPtr stat, XTThreadPtr thread) { XTFileMemMapPtr mm = map->mf_memmap; #ifndef FILE_MAP_USE_PTHREAD_RW xtThreadID thd_id = thread->t_id; #endif #ifdef DEBUG_TRACE_MAP_IO xt_trace("/* %s */ pbxt_fmap_writ(\"%s\", %lu, %lu);\n", xt_trace_clock_diff(NULL), map->fr_file->fil_path, (u_long) offset, (u_long) size); #endif ASSERT_NS(!map->mf_slock_count); FILE_MAP_READ_LOCK(&mm->mm_lock, thd_id); if (!mm->mm_start || offset + (off_t) size > mm->mm_length) { FILE_MAP_UNLOCK(&mm->mm_lock, thd_id); FILE_MAP_WRITE_LOCK(&mm->mm_lock, thd_id); if (!fs_remap_file(map, offset, size, stat)) goto failed; } #ifdef XT_WIN __try { memcpy(mm->mm_start + offset, data, size); } // GetExceptionCode()== EXCEPTION_IN_PAGE_ERROR ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH __except(EXCEPTION_EXECUTE_HANDLER) { xt_register_ferrno(XT_REG_CONTEXT, GetExceptionCode(), xt_file_path(map)); goto failed; } #else memcpy(mm->mm_start + offset, data, size); #endif FILE_MAP_UNLOCK(&mm->mm_lock, thd_id); stat->ts_write += size; return OK; failed: FILE_MAP_UNLOCK(&mm->mm_lock, thd_id); return FAILED; } xtPublic xtBool xt_pread_fmap_4(XTMapFilePtr map, off_t offset, xtWord4 *value, XTIOStatsPtr stat, XTThreadPtr thread) { XTFileMemMapPtr mm = map->mf_memmap; #ifndef FILE_MAP_USE_PTHREAD_RW xtThreadID thd_id = thread->t_id; #endif #ifdef DEBUG_TRACE_MAP_IO xt_trace("/* %s */ pbxt_fmap_read_4(\"%s\", %lu, 4);\n", xt_trace_clock_diff(NULL), map->fr_file->fil_path, (u_long) offset); #endif if (!map->mf_slock_count) FILE_MAP_READ_LOCK(&mm->mm_lock, thd_id); if (!mm->mm_start) { FILE_MAP_UNLOCK(&mm->mm_lock, thd_id); FILE_MAP_WRITE_LOCK(&mm->mm_lock, thd_id); if (!fs_remap_file(map, 0, 0, stat)) { FILE_MAP_UNLOCK(&mm->mm_lock, thd_id); return FAILED; } } if (offset >= mm->mm_length) *value = 0; else { xtWord1 *data; data = mm->mm_start + offset; #ifdef XT_WIN __try { *value = XT_GET_DISK_4(data); // GetExceptionCode()== EXCEPTION_IN_PAGE_ERROR ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH } __except(EXCEPTION_EXECUTE_HANDLER) { FILE_MAP_UNLOCK(&mm->mm_lock, thd_id); return xt_register_ferrno(XT_REG_CONTEXT, GetExceptionCode(), xt_file_path(map)); } #else *value = XT_GET_DISK_4(data); #endif } if (!map->mf_slock_count) FILE_MAP_UNLOCK(&mm->mm_lock, thd_id); stat->ts_read += 4; return OK; } xtPublic xtBool xt_pread_fmap(XTMapFilePtr map, off_t offset, size_t size, size_t min_size, void *data, size_t *red_size, XTIOStatsPtr stat, XTThreadPtr thread) { XTFileMemMapPtr mm = map->mf_memmap; #ifndef FILE_MAP_USE_PTHREAD_RW xtThreadID thd_id = thread->t_id; #endif size_t tfer; #ifdef DEBUG_TRACE_MAP_IO xt_trace("/* %s */ pbxt_fmap_read(\"%s\", %lu, %lu);\n", xt_trace_clock_diff(NULL), map->fr_file->fil_path, (u_long) offset, (u_long) size); #endif /* NOTE!! The file map may already be locked, * by a call to xt_lock_fmap_ptr()! * * 20.05.2009: This problem should be fixed now with mf_slock_count! * * This can occur during a sequential scan: * xt_pread_fmap() Line 1330 * XTTabCache::tc_read_direct() Line 361 * XTTabCache::xt_tc_read() Line 220 * xt_tab_get_rec_data() * tab_visible() Line 2412 * xt_tab_seq_next() Line 4068 * * And occurs during the following test: * create table t1 ( a int not null, b int not null) ; * --disable_query_log * insert into t1 values (1,1),(2,2),(3,3),(4,4); * let $1=19; * set @d=4; * while ($1) * { * eval insert into t1 select a+@d,b+@d from t1; * eval set @d=@d*2; * dec $1; * } * * --enable_query_log * alter table t1 add index i1(a); * delete from t1 where a > 2000000; * create table t2 like t1; * insert into t2 select * from t1; * * As a result, the slock must be able to handle * nested calls to lock/unlock. */ if (!map->mf_slock_count) FILE_MAP_READ_LOCK(&mm->mm_lock, thd_id); tfer = size; if (!mm->mm_start) { FILE_MAP_UNLOCK(&mm->mm_lock, thd_id); ASSERT_NS(!map->mf_slock_count); FILE_MAP_WRITE_LOCK(&mm->mm_lock, thd_id); if (!fs_remap_file(map, 0, 0, stat)) { if (!map->mf_slock_count) FILE_MAP_UNLOCK(&mm->mm_lock, thd_id); return FAILED; } } if (offset >= mm->mm_length) tfer = 0; else { if (mm->mm_length - offset < (off_t) tfer) tfer = (size_t) (mm->mm_length - offset); #ifdef XT_WIN __try { memcpy(data, mm->mm_start + offset, tfer); // GetExceptionCode()== EXCEPTION_IN_PAGE_ERROR ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH } __except(EXCEPTION_EXECUTE_HANDLER) { if (!map->mf_slock_count) FILE_MAP_UNLOCK(&mm->mm_lock, thd_id); return xt_register_ferrno(XT_REG_CONTEXT, GetExceptionCode(), xt_file_path(map)); } #else memcpy(data, mm->mm_start + offset, tfer); #endif } if (!map->mf_slock_count) FILE_MAP_UNLOCK(&mm->mm_lock, thd_id); if (tfer < min_size) return xt_register_ferrno(XT_REG_CONTEXT, ESPIPE, xt_file_path(map)); if (red_size) *red_size = tfer; stat->ts_read += tfer; return OK; } xtPublic xtBool xt_flush_fmap(XTMapFilePtr map, XTIOStatsPtr stat, XTThreadPtr thread) { XTFileMemMapPtr mm = map->mf_memmap; #ifndef FILE_MAP_USE_PTHREAD_RW xtThreadID thd_id = thread->t_id; #endif xtWord8 s; #ifdef DEBUG_TRACE_MAP_IO xt_trace("/* %s */ pbxt_fmap_sync(\"%s\");\n", xt_trace_clock_diff(NULL), map->fr_file->fil_path); #endif if (!map->mf_slock_count) FILE_MAP_READ_LOCK(&mm->mm_lock, thd_id); if (!mm->mm_start) { FILE_MAP_UNLOCK(&mm->mm_lock, thd_id); ASSERT_NS(!map->mf_slock_count); FILE_MAP_WRITE_LOCK(&mm->mm_lock, thd_id); if (!fs_remap_file(map, 0, 0, stat)) { if (!map->mf_slock_count) FILE_MAP_UNLOCK(&mm->mm_lock, thd_id); return FAILED; } } stat->ts_flush_start = xt_trace_clock(); #ifdef XT_WIN if (!FlushViewOfFile(mm->mm_start, 0)) { xt_register_ferrno(XT_REG_CONTEXT, fs_get_win_error(), xt_file_path(map)); goto failed; } #else if (msync( (char *)mm->mm_start, (size_t) mm->mm_length, MS_SYNC) == -1) { xt_register_ferrno(XT_REG_CONTEXT, errno, xt_file_path(map)); goto failed; } #endif if (!map->mf_slock_count) FILE_MAP_UNLOCK(&mm->mm_lock, thd_id); s = stat->ts_flush_start; stat->ts_flush_start = 0; stat->ts_flush_time += xt_trace_clock() - s; stat->ts_flush++; return OK; failed: if (!map->mf_slock_count) FILE_MAP_UNLOCK(&mm->mm_lock, thd_id); s = stat->ts_flush_start; stat->ts_flush_start = 0; stat->ts_flush_time += xt_trace_clock() - s; return FAILED; } xtPublic xtWord1 *xt_lock_fmap_ptr(XTMapFilePtr map, off_t offset, size_t size, XTIOStatsPtr stat, XTThreadPtr thread) { XTFileMemMapPtr mm = map->mf_memmap; #ifndef FILE_MAP_USE_PTHREAD_RW xtThreadID thd_id = thread->t_id; #endif if (!map->mf_slock_count) FILE_MAP_READ_LOCK(&mm->mm_lock, thd_id); map->mf_slock_count++; if (!mm->mm_start) { FILE_MAP_UNLOCK(&mm->mm_lock, thd_id); FILE_MAP_WRITE_LOCK(&mm->mm_lock, thd_id); if (!fs_remap_file(map, 0, 0, stat)) goto failed; } if (offset >= mm->mm_length) goto failed; if (offset + (off_t) size > mm->mm_length) stat->ts_read += (u_int) (offset + (off_t) size - mm->mm_length); else stat->ts_read += size; return mm->mm_start + offset; failed: map->mf_slock_count--; if (!map->mf_slock_count) FILE_MAP_UNLOCK(&mm->mm_lock, thd_id); return NULL; } xtPublic void xt_unlock_fmap_ptr(XTMapFilePtr map, XTThreadPtr thread) { map->mf_slock_count--; if (!map->mf_slock_count) FILE_MAP_UNLOCK(&map->mf_memmap->mm_lock, thread->t_id); } /* ---------------------------------------------------------------------- * Copy files/directories */ static void fs_copy_file(XTThreadPtr self, char *from_path, char *to_path, void *copy_buf) { XTOpenFilePtr from; XTOpenFilePtr to; off_t offset = 0; size_t read_size= 0; from = xt_open_file(self, from_path, XT_FS_READONLY); pushr_(xt_close_file, from); to = xt_open_file(self, to_path, XT_FS_CREATE | XT_FS_MAKE_PATH); pushr_(xt_close_file, to); for (;;) { if (!xt_pread_file(from, offset, 16*1024, 0, copy_buf, &read_size, &self->st_statistics.st_x, self)) xt_throw(self); if (!read_size) break; if (!xt_pwrite_file(to, offset, read_size, copy_buf, &self->st_statistics.st_x, self)) xt_throw(self); offset += (off_t) read_size; } freer_(); freer_(); } xtPublic void xt_fs_copy_file(XTThreadPtr self, char *from_path, char *to_path) { void *buffer; buffer = xt_malloc(self, 16*1024); pushr_(xt_free, buffer); fs_copy_file(self, from_path, to_path, buffer); freer_(); } static void fs_copy_dir(XTThreadPtr self, char *from_path, char *to_path, void *copy_buf) { XTOpenDirPtr od; char *file; xt_add_dir_char(PATH_MAX, from_path); xt_add_dir_char(PATH_MAX, to_path); pushsr_(od, xt_dir_close, xt_dir_open(self, from_path, NULL)); while (xt_dir_next(self, od)) { file = xt_dir_name(self, od); if (*file == '.') continue; #ifdef XT_WIN if (strcmp(file, "pbxt-lock") == 0) continue; #endif xt_strcat(PATH_MAX, from_path, file); xt_strcat(PATH_MAX, to_path, file); if (xt_dir_is_file(self, od)) fs_copy_file(self, from_path, to_path, copy_buf); else fs_copy_dir(self, from_path, to_path, copy_buf); xt_remove_last_name_of_path(from_path); xt_remove_last_name_of_path(to_path); } freer_(); xt_remove_dir_char(from_path); xt_remove_dir_char(to_path); } xtPublic void xt_fs_copy_dir(XTThreadPtr self, const char *from, const char *to) { void *buffer; char from_path[PATH_MAX]; char to_path[PATH_MAX]; xt_strcpy(PATH_MAX, from_path, from); xt_strcpy(PATH_MAX, to_path, to); buffer = xt_malloc(self, 16*1024); pushr_(xt_free, buffer); fs_copy_dir(self, from_path, to_path, buffer); freer_(); }