aboutsummaryrefslogtreecommitdiffstats
path: root/erts/emulator/drivers/win32/win_efile.c
diff options
context:
space:
mode:
Diffstat (limited to 'erts/emulator/drivers/win32/win_efile.c')
-rw-r--r--erts/emulator/drivers/win32/win_efile.c2058
1 files changed, 0 insertions, 2058 deletions
diff --git a/erts/emulator/drivers/win32/win_efile.c b/erts/emulator/drivers/win32/win_efile.c
deleted file mode 100644
index 2d366b5833..0000000000
--- a/erts/emulator/drivers/win32/win_efile.c
+++ /dev/null
@@ -1,2058 +0,0 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 1997-2016. All Rights Reserved.
- *
- * Licensed 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.
- *
- * %CopyrightEnd%
- */
-/*
- * Purpose: Provides file and directory operations for Windows.
- */
-
-#include <windows.h>
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-#include "sys.h"
-#include <ctype.h>
-#include <wchar.h>
-#include "erl_efile.h"
-
-#define DBG_TRACE_MASK 0
-/* 1 = file name ops
- * 2 = file descr ops
- * 4 = errors
- * 8 = path name conversion
- */
-#if !DBG_TRACE_MASK
-# define DBG_TRACE(M,S)
-# define DBG_TRACE1(M,FMT,A)
-# define DBG_TRACE2(M,FMT,A,B)
-#else
-# define DBG_TRACE(M,S) do { if ((M)&DBG_TRACE_MASK) fwprintf(stderr, L"DBG_TRACE %d: %s\r\n", __LINE__, (WCHAR*)(S)); }while(0)
-# define DBG_TRACE1(M,FMT,A) do { if ((M)&DBG_TRACE_MASK) fwprintf(stderr, L"DBG_TRACE %d: " L##FMT L"\r\n", __LINE__, (A)); }while(0)
-# define DBG_TRACE2(M,FMT,A,B) do { if ((M)&DBG_TRACE_MASK) fwprintf(stderr, L"DBG_TRACE %d: " L##FMT L"\r\n", __LINE__, (A), (B)); }while(0)
-#endif
-
-/*
- * Microsoft-specific function to map a WIN32 error code to a Posix errno.
- */
-
-#define ISSLASH(a) ((a) == L'\\' || (a) == L'/')
-#define ISDIR(st) (((st).st_mode&S_IFMT) == S_IFDIR)
-#define ISREG(st) (((st).st_mode&S_IFMT) == S_IFREG)
-
-#define IS_DOT_OR_DOTDOT(s) \
- ((s)[0] == L'.' && ((s)[1] == L'\0' || ((s)[1] == L'.' && (s)[2] == L'\0')))
-
-#define FILE_SHARE_FLAGS (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE)
-
-#ifndef INVALID_FILE_ATTRIBUTES
-#define INVALID_FILE_ATTRIBUTES ((DWORD) 0xFFFFFFFF)
-#endif
-
-#define TICKS_PER_SECOND (10000000ULL)
-#define EPOCH_DIFFERENCE (11644473600LL)
-
-#define FILETIME_TO_EPOCH(epoch, ft) \
- do { \
- ULARGE_INTEGER ull; \
- ull.LowPart = (ft).dwLowDateTime; \
- ull.HighPart = (ft).dwHighDateTime; \
- (epoch) = ((ull.QuadPart / TICKS_PER_SECOND) - EPOCH_DIFFERENCE); \
- } while(0)
-
-#define EPOCH_TO_FILETIME(ft, epoch) \
- do { \
- ULARGE_INTEGER ull; \
- ull.QuadPart = (((epoch) + EPOCH_DIFFERENCE) * TICKS_PER_SECOND); \
- (ft).dwLowDateTime = ull.LowPart; \
- (ft).dwHighDateTime = ull.HighPart; \
- } while(0)
-
-
-static int check_error(int result, Efile_error* errInfo);
-static int set_error(Efile_error* errInfo);
-static int set_os_errno(Efile_error* errInfo, DWORD os_errno);
-static int is_root_unc_name(const WCHAR *path);
-static int extract_root(WCHAR *name);
-static unsigned short dos_to_posix_mode(int attr, const WCHAR *name);
-
-
-struct wpath_tmp_buffer {
- struct wpath_tmp_buffer* next;
- WCHAR buffer[1];
-};
-
-typedef struct {
- Efile_error* errInfo;
- struct wpath_tmp_buffer* buf_list;
-}Efile_call_state;
-
-static void call_state_init(Efile_call_state* state, Efile_error* errInfo)
-{
- state->errInfo = errInfo;
- state->buf_list = NULL;
-}
-static WCHAR* wpath_tmp_alloc(Efile_call_state* state, size_t len)
-{
- size_t sz = offsetof(struct wpath_tmp_buffer, buffer)
- + (len+1)*sizeof(WCHAR);
- struct wpath_tmp_buffer* p = driver_alloc(sz);
- p->next = state->buf_list;
- state->buf_list = p;
- return p->buffer;
-}
-static void call_state_free(Efile_call_state* state)
-{
- while(state->buf_list) {
- struct wpath_tmp_buffer* next = state->buf_list->next;
- driver_free(state->buf_list);
- state->buf_list = next;
- }
-}
-static WCHAR* get_cwd_wpath_tmp(Efile_call_state* state)
-{
- WCHAR dummy;
- DWORD size = GetCurrentDirectoryW(0, &dummy);
- WCHAR* ret = NULL;
-
- if (size) {
- ret = wpath_tmp_alloc(state, size);
- if (!GetCurrentDirectoryW(size, ret)) {
- ret = NULL;
- }
- }
- return ret;
-}
-static WCHAR* get_full_wpath_tmp(Efile_call_state* state,
- const WCHAR* file,
- WCHAR** file_part,
- DWORD extra)
-{
- WCHAR dummy;
- DWORD size = GetFullPathNameW(file, 0, &dummy, NULL);
- WCHAR* ret = NULL;
-
- if (size) {
- int ok;
- ret = wpath_tmp_alloc(state, size + extra);
- if (file_part) {
- ok = (GetFullPathNameW(file, size, ret, file_part) != 0);
- }
- else {
- ok = (_wfullpath(ret, file, size) != NULL);
- }
- if (!ok) {
- ret = NULL;
- }
- }
- return ret;
-}
-
-static void ensure_wpath_max(Efile_call_state* state, WCHAR** pathp, size_t max);
-static int do_rmdir(Efile_call_state*, char* name);
-static int do_rename(Efile_call_state*, char* src, char* dst);
-static int do_readdir(Efile_call_state*, char* name, EFILE_DIR_HANDLE*, char* buffer, size_t *size);
-static int do_fileinfo(Efile_call_state*, Efile_info*, char* orig_name, int info_for_link);
-static char* do_readlink(Efile_call_state*, char* name, char* buffer, size_t size);
-static int do_altname(Efile_call_state*, char* orig_name, char* buffer, size_t size);
-
-
-static int errno_map(DWORD last_error) {
-
- switch (last_error) {
- case ERROR_SUCCESS:
- return 0;
- case ERROR_INVALID_FUNCTION:
- case ERROR_INVALID_DATA:
- case ERROR_INVALID_PARAMETER:
- case ERROR_INVALID_TARGET_HANDLE:
- case ERROR_INVALID_CATEGORY:
- case ERROR_NEGATIVE_SEEK:
- return EINVAL;
- case ERROR_DIR_NOT_EMPTY:
- return EEXIST;
- case ERROR_BAD_FORMAT:
- return ENOEXEC;
- case ERROR_PATH_NOT_FOUND:
- case ERROR_FILE_NOT_FOUND:
- case ERROR_NO_MORE_FILES:
- return ENOENT;
- case ERROR_TOO_MANY_OPEN_FILES:
- return EMFILE;
- case ERROR_ACCESS_DENIED:
- case ERROR_INVALID_ACCESS:
- case ERROR_CURRENT_DIRECTORY:
- case ERROR_SHARING_VIOLATION:
- case ERROR_LOCK_VIOLATION:
- case ERROR_INVALID_PASSWORD:
- case ERROR_DRIVE_LOCKED:
- return EACCES;
- case ERROR_INVALID_HANDLE:
- return EBADF;
- case ERROR_NOT_ENOUGH_MEMORY:
- case ERROR_OUTOFMEMORY:
- case ERROR_OUT_OF_STRUCTURES:
- return ENOMEM;
- case ERROR_INVALID_DRIVE:
- case ERROR_BAD_UNIT:
- case ERROR_NOT_READY:
- case ERROR_REM_NOT_LIST:
- case ERROR_DUP_NAME:
- case ERROR_BAD_NETPATH:
- case ERROR_NETWORK_BUSY:
- case ERROR_DEV_NOT_EXIST:
- case ERROR_BAD_NET_NAME:
- return ENXIO;
- case ERROR_NOT_SAME_DEVICE:
- return EXDEV;
- case ERROR_WRITE_PROTECT:
- return EROFS;
- case ERROR_BAD_LENGTH:
- case ERROR_BUFFER_OVERFLOW:
- return E2BIG;
- case ERROR_SEEK:
- case ERROR_SECTOR_NOT_FOUND:
- return ESPIPE;
- case ERROR_NOT_DOS_DISK:
- return ENODEV;
- case ERROR_GEN_FAILURE:
- return ENODEV;
- case ERROR_SHARING_BUFFER_EXCEEDED:
- case ERROR_NO_MORE_SEARCH_HANDLES:
- return EMFILE;
- case ERROR_HANDLE_EOF:
- case ERROR_BROKEN_PIPE:
- return EPIPE;
- case ERROR_HANDLE_DISK_FULL:
- case ERROR_DISK_FULL:
- return ENOSPC;
- case ERROR_NOT_SUPPORTED:
- return ENOTSUP;
- case ERROR_FILE_EXISTS:
- case ERROR_ALREADY_EXISTS:
- case ERROR_CANNOT_MAKE:
- return EEXIST;
- case ERROR_ALREADY_ASSIGNED:
- return EBUSY;
- case ERROR_NO_PROC_SLOTS:
- return EAGAIN;
- case ERROR_CANT_RESOLVE_FILENAME:
- return EMLINK;
- case ERROR_PRIVILEGE_NOT_HELD:
- return EPERM;
- case ERROR_ARENA_TRASHED:
- case ERROR_INVALID_BLOCK:
- case ERROR_BAD_ENVIRONMENT:
- case ERROR_BAD_COMMAND:
- case ERROR_CRC:
- case ERROR_OUT_OF_PAPER:
- case ERROR_READ_FAULT:
- case ERROR_WRITE_FAULT:
- case ERROR_WRONG_DISK:
- case ERROR_NET_WRITE_FAULT:
- return EIO;
- default: /* not to do with files I expect. */
- return EIO;
- }
-}
-
-static int
-check_error(int result, Efile_error* errInfo)
-{
- if (result < 0) {
- errInfo->posix_errno = errno;
- errInfo->os_errno = GetLastError();
- DBG_TRACE2(4, "ERROR os_error=%d errno=%d @@@@@@@@@@@@@@@@@@@@@@@@@@@@",
- errInfo->os_errno, errInfo->posix_errno);
- return 0;
- }
- return 1;
-}
-
-static void
-save_last_error(Efile_error* errInfo)
-{
- errInfo->posix_errno = errno;
- errInfo->os_errno = GetLastError();
- DBG_TRACE2(4, "ERROR os_error=%d errno=%d $$$$$$$$$$$$$$$$$$$$$$$$$$$$$",
- errInfo->os_errno, errInfo->posix_errno);
-}
-
-
-/*
- * Fills the provided error information structure with information
- * with the error code given by GetLastError() and its corresponding
- * Posix error number.
- *
- * Returns 0.
- */
-
-static int
-set_error(Efile_error* errInfo)
-{
- set_os_errno(errInfo, GetLastError());
- return 0;
-}
-
-
-static int
-set_os_errno(Efile_error* errInfo, DWORD os_errno)
-{
- errInfo->os_errno = os_errno;
- errInfo->posix_errno = errno_map(os_errno);
- DBG_TRACE2(4, "ERROR os_error=%d errno=%d ############################",
- errInfo->os_errno, errInfo->posix_errno);
- return 0;
-}
-
-int
-efile_init() {
- return 1;
-}
-
-/*
- * A writev with Unix semantics, but with Windows arguments
- */
-static int
-win_writev(Efile_error* errInfo,
- HANDLE fd, /* handle to file */
- FILE_SEGMENT_ELEMENT iov[], /* array of buffer pointers */
- DWORD *size) /* number of bytes to write */
-{
- OVERLAPPED ov;
- ov.Offset = 0L;
- ov.OffsetHigh = 0L;
- ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
- if (ov.hEvent == NULL)
- return set_error(errInfo);
- if (! write_file_gather(fd, iov, *size, NULL, &ov))
- return set_error(errInfo);
- if (WaitForSingleObject(ov.hEvent, INFINITE) != WAIT_OBJECT_0)
- return set_error(errInfo);
- if (! GetOverlappedResult(fd, &ov, size, FALSE))
- return set_error(errInfo);
- return 1;
-}
-
-
-/* Check '*pathp' and convert it if needed to something that windows will accept.
- * Typically use UNC path with \\?\ prefix if absolute path is longer than 260.
- */
-static void ensure_wpath(Efile_call_state* state, WCHAR** pathp)
-{
- ensure_wpath_max(state, pathp, MAX_PATH);
-}
-
-static void ensure_wpath_max(Efile_call_state* state, WCHAR** pathp, size_t max)
-{
- WCHAR* path = *pathp;
- WCHAR* p;
- size_t len = wcslen(path);
- int unc_fixup = 0;
-
- if (path[0] == 0) {
- DBG_TRACE(8, L"Let empty path pass through");
- return;
- }
-
- DBG_TRACE1(8,"IN: %s", path);
-
- if (path[1] == L':' && ISSLASH(path[2])) { /* absolute path */
- if (len >= max) {
- WCHAR *src, *dst;
-
- *pathp = wpath_tmp_alloc(state, 4+len+1);
- dst = *pathp;
- wcscpy(dst, L"\\\\?\\");
- for (src=path,dst+=4; *src; src++) {
- if (*src == L'/') {
- if (dst[-1] != L'\\') {
- *dst++ = L'\\';
- }
- /*else ignore redundant slashes */
- }
- else
- *dst++ = *src;
- }
- *dst = 0;
- unc_fixup = 1;
- }
- }
- else if (!(ISSLASH(path[0]) && ISSLASH(path[1]))) { /* relative path */
- DWORD cwdLen = GetCurrentDirectoryW(0, NULL);
- DWORD absLen = cwdLen + 1 + len;
- if (absLen >= max) {
- WCHAR *fullPath = wpath_tmp_alloc(state, 4+4+absLen);
- DWORD fullLen;
-
- fullLen = GetFullPathNameW(path, 4 + absLen, fullPath+4, NULL);
- if (fullLen >= 4+absLen) {
- *pathp = path;
- DBG_TRACE2(8,"ensure_wpath FAILED absLen=%u %s", (int)absLen, path);
- return;
- }
- /* GetFullPathNameW can return paths longer than MAX_PATH without the \\?\ prefix.
- * At least seen on Windows 7. Go figure...
- */
- if (fullLen >= max && wcsncmp(fullPath+4, L"\\\\?\\", 4) != 0) {
- wcsncpy(fullPath, L"\\\\?\\", 4);
- *pathp = fullPath;
- }
- else {
- *pathp = fullPath + 4;
- }
- }
- }
-
- if (unc_fixup) {
- WCHAR* endp;
-
- p = *pathp;
- len = wcslen(p);
- endp = p + len;
- if (len > 4) {
- p += 4;
- while (*p) {
- if (p[0] == L'\\' && p[1] == L'.') {
- if (p[2] == L'\\' || !p[2]) { /* single dot */
- wmemmove(p, p+2, (&endp[1] - &p[2]));
- endp -= 2;
- }
- else if (p[2] == L'.' && (p[3] == L'\\' || !p[3])) { /* double dot */
- WCHAR* r;
- for (r=p-1; *r == L'\\'; --r)
- /*skip redundant slashes*/;
- for (; *r != L'\\'; --r)
- /*find start of prev directory*/;
- if (r < *pathp + 6)
- break;
- wmemmove(r, p+3, (&endp[1] - &p[3]));
- p = r;
- }
- else p += 3;
- }
- else ++p;
- }
- }
- }
- DBG_TRACE1(8,"OUT: %s", *pathp);
-}
-
-int
-efile_mkdir(Efile_error* errInfo, /* Where to return error codes. */
- char* name) /* Name of directory to create. */
-{
- Efile_call_state state;
- WCHAR* wname = (WCHAR*)name;
- int ret;
-
- DBG_TRACE(1, name);
- call_state_init(&state, errInfo);
- ensure_wpath_max(&state, &wname, 248); /* Yes, 248 limit for normal paths */
-
- ret = (int) CreateDirectoryW(wname, NULL);
- if (!ret)
- set_error(errInfo);
-
- call_state_free(&state);
- return ret;
-}
-
-int
-efile_rmdir(Efile_error* errInfo, /* Where to return error codes. */
- char* name) /* Name of directory to delete. */
-{
- Efile_call_state state;
- int ret;
-
- DBG_TRACE(1, name);
- call_state_init(&state, errInfo);
- ret = do_rmdir(&state, name);
- call_state_free(&state);
- return ret;
-}
-
-static int do_rmdir(Efile_call_state* state, char* name)
-{
- OSVERSIONINFO os;
- DWORD attr;
- WCHAR *wname = (WCHAR *) name;
- WCHAR *buffer = NULL;
-
- ensure_wpath(state, &wname);
-
- if (RemoveDirectoryW(wname) != FALSE) {
- return 1;
- }
- errno = errno_map(GetLastError());
- if (errno == EACCES) {
- attr = GetFileAttributesW(wname);
- if (attr != (DWORD) -1) {
- if ((attr & FILE_ATTRIBUTE_DIRECTORY) == 0) {
- /*
- * Windows 95 reports calling RemoveDirectory on a file as an
- * EACCES, not an ENOTDIR.
- */
-
- errno = ENOTDIR;
- goto end;
- }
-
- /*
- * Windows 95 reports removing a non-empty directory as
- * an EACCES, not an EEXIST. If the directory is not empty,
- * change errno so caller knows what's going on.
- */
-
- os.dwOSVersionInfoSize = sizeof(os);
- GetVersionEx(&os);
- if (os.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
- HANDLE handle;
- WIN32_FIND_DATAW data;
- int len = wcslen(wname);
-
- buffer = wpath_tmp_alloc(state, len + 4);
- wcscpy(buffer, wname);
- if (buffer[0] && buffer[len-1] != L'\\' && buffer[len-1] != L'/') {
- wcscat(buffer, L"\\");
- }
- wcscat(buffer, L"*.*");
- handle = FindFirstFileW(buffer, &data);
- if (handle != INVALID_HANDLE_VALUE) {
- while (1) {
- if ((wcscmp(data.cFileName, L".") != 0)
- && (wcscmp(data.cFileName, L"..") != 0)) {
- /*
- * Found something in this directory.
- */
-
- errno = EEXIST;
- break;
- }
- if (FindNextFileW(handle, &data) == FALSE) {
- break;
- }
- }
- FindClose(handle);
- }
- }
- }
- }
-
- if (errno == ENOTEMPTY) {
- /*
- * Posix allows both EEXIST or ENOTEMPTY, but we'll always
- * return EEXIST to allow easy matching in Erlang code.
- */
-
- errno = EEXIST;
- }
-
- end:
- save_last_error(state->errInfo);
- return 0;
-}
-
-int
-efile_delete_file(Efile_error* errInfo, /* Where to return error codes. */
- char* name) /* Name of file to delete. */
-{
- Efile_call_state state;
- int ret;
- DBG_TRACE(1, name);
- call_state_init(&state, errInfo);
- ret = do_delete_file(&state, name);
- call_state_free(&state);
- return ret;
-}
-
-static int do_delete_file(Efile_call_state* state, char* name)
-{
- DWORD attr;
- WCHAR *wname = (WCHAR *) name;
-
- ensure_wpath(state, &wname);
-
- if (DeleteFileW(wname) != FALSE) {
- return 1;
- }
-
- errno = errno_map(GetLastError());
- if (errno == EACCES) {
- attr = GetFileAttributesW(wname);
- if (attr != (DWORD) -1) {
- if (attr & FILE_ATTRIBUTE_DIRECTORY) {
- /*
- * Windows NT reports removing a directory as EACCES instead
- * of EPERM.
- */
-
- errno = EPERM;
- }
- }
- } else if (errno == ENOENT) {
- attr = GetFileAttributesW(wname);
- if (attr != (DWORD) -1) {
- if (attr & FILE_ATTRIBUTE_DIRECTORY) {
- /*
- * Windows 95 reports removing a directory as ENOENT instead
- * of EPERM.
- */
-
- errno = EPERM;
- }
- }
- } else if (errno == EINVAL) {
- /*
- * Windows NT reports removing a char device as EINVAL instead of
- * EACCES.
- */
-
- errno = EACCES;
- }
-
- return check_error(-1, state->errInfo);
-}
-
-/*
- *---------------------------------------------------------------------------
- *
- * Changes the name of an existing file or directory, from src to dst.
- * If src and dst refer to the same file or directory, does nothing
- * and returns success. Otherwise if dst already exists, it will be
- * deleted and replaced by src subject to the following conditions:
- * If src is a directory, dst may be an empty directory.
- * If src is a file, dst may be a file.
- * In any other situation where dst already exists, the rename will
- * fail.
- *
- * Some possible error codes:
- *
- * EACCES: src or dst parent directory can't be read and/or written.
- * EEXIST: dst is a non-empty directory.
- * EINVAL: src is a root directory or dst is a subdirectory of src.
- * EISDIR: dst is a directory, but src is not.
- * ENOENT: src doesn't exist, or src or dst is "".
- * ENOTDIR: src is a directory, but dst is not.
- * EXDEV: src and dst are on different filesystems.
- *
- * Side effects:
- * The implementation of rename may allow cross-filesystem renames,
- * but the caller should be prepared to emulate it with copy and
- * delete if errno is EXDEV.
- *
- *---------------------------------------------------------------------------
- */
-
-int
-efile_rename(Efile_error* errInfo, char* src, char* dst)
-{
- Efile_call_state state;
- int ret;
- DBG_TRACE(1, src);
- call_state_init(&state, errInfo);
- ret = do_rename(&state, src, dst);
- call_state_free(&state);
- return ret;
-}
-
-static int
-do_rename(Efile_call_state* state,
- char* src, /* Original name. */
- char* dst) /* New name. */
-{
- DWORD srcAttr, dstAttr;
- WCHAR *wsrc = (WCHAR *) src;
- WCHAR *wdst = (WCHAR *) dst;
-
- ensure_wpath(state, &wsrc);
- ensure_wpath(state, &wdst);
-
- if (MoveFileW(wsrc, wdst) != FALSE) {
- return 1;
- }
-
- errno = errno_map(GetLastError());
- srcAttr = GetFileAttributesW(wsrc);
- dstAttr = GetFileAttributesW(wdst);
- if (srcAttr == (DWORD) -1) {
- srcAttr = 0;
- }
- if (dstAttr == (DWORD) -1) {
- dstAttr = 0;
- }
-
- if (errno == EBADF) {
- errno = EACCES;
- return check_error(-1, state->errInfo);
- }
- if (errno == EACCES) {
- decode:
- if (srcAttr & FILE_ATTRIBUTE_DIRECTORY) {
- WCHAR *srcPath, *dstPath;
- WCHAR *srcRest, *dstRest;
- int size;
-
- srcPath = get_full_wpath_tmp(state, wsrc, &srcRest, 0);
- if (!srcPath) {
- save_last_error(state->errInfo);
- return 0;
- }
-
- dstPath = get_full_wpath_tmp(state, wdst, &dstRest, 0);
- if (!dstPath) {
- save_last_error(state->errInfo);
- return 0;
- }
-
- if (srcRest == NULL) {
- srcRest = srcPath + wcslen(srcPath);
- }
- if (_wcsnicmp(srcPath, dstPath, srcRest - srcPath) == 0) {
- /*
- * Trying to move a directory into itself.
- */
-
- errno = EINVAL;
- }
- if (extract_root(srcPath)) {
- /*
- * Attempt to move a root directory. Never allowed.
- */
- errno = EINVAL;
- }
-
- (void) extract_root(dstPath);
- if (dstPath[0] == L'\0') {
- /*
- * The filename was invalid. (Don't know why,
- * but play it safe.)
- */
- errno = EINVAL;
- }
- if (_wcsicmp(srcPath, dstPath) != 0) {
- /*
- * If src is a directory and dst filesystem != src
- * filesystem, errno should be EXDEV. It is very
- * important to get this behavior, so that the caller
- * can respond to a cross filesystem rename by
- * simulating it with copy and delete. The MoveFile
- * system call already handles the case of moving a
- * *file* between filesystems.
- */
-
- errno = EXDEV;
- }
- }
-
- /*
- * Other types of access failure is that dst is a read-only
- * filesystem, that an open file referred to src or dest, or that
- * src or dest specified the current working directory on the
- * current filesystem. EACCES is returned for those cases.
- */
-
- } else if (errno == EEXIST) {
- /*
- * Reports EEXIST any time the target already exists. If it makes
- * sense, remove the old file and try renaming again.
- */
-
- if (srcAttr & FILE_ATTRIBUTE_DIRECTORY) {
- if (dstAttr & FILE_ATTRIBUTE_DIRECTORY) {
- /*
- * Overwrite empty dst directory with src directory. The
- * following call will remove an empty directory. If it
- * fails, it's because it wasn't empty.
- */
-
- if (RemoveDirectoryW(wdst)) {
- /*
- * Now that that empty directory is gone, we can try
- * renaming again. If that fails, we'll put this empty
- * directory back, for completeness.
- */
-
- if (MoveFileW(wsrc, wdst) != FALSE) {
- return 1;
- }
-
- /*
- * Some new error has occurred. Don't know what it
- * could be, but report this one.
- */
-
- errno = errno_map(GetLastError());
- CreateDirectoryW(wdst, NULL);
- SetFileAttributesW(wdst, dstAttr);
- if (errno == EACCES) {
- /*
- * Decode the EACCES to a more meaningful error.
- */
-
- goto decode;
- }
- }
- } else { /* (dstAttr & FILE_ATTRIBUTE_DIRECTORY) == 0 */
- errno = ENOTDIR;
- }
- } else { /* (srcAttr & FILE_ATTRIBUTE_DIRECTORY) == 0 */
- if (dstAttr & FILE_ATTRIBUTE_DIRECTORY) {
- errno = EISDIR;
- } else {
- /*
- * Overwrite existing file by:
- *
- * 1. Rename existing file to temp name.
- * 2. Rename old file to new name.
- * 3. If success, delete temp file. If failure,
- * put temp file back to old name.
- */
-
- WCHAR *tempName;
- int result;
- WCHAR *rest;
-
- tempName = get_full_wpath_tmp(state, wdst, &rest, 14);
- if (!tempName || !rest) {
- save_last_error(state->errInfo);
- return 0;
- }
-
- *rest = L'\0';
- result = -1;
- if (GetTempFileNameW(tempName, L"erlr", 0, tempName) != 0) {
- /*
- * Strictly speaking, need the following DeleteFile and
- * MoveFile to be joined as an atomic operation so no
- * other app comes along in the meantime and creates the
- * same temp file.
- */
-
- DeleteFileW(tempName);
- if (MoveFileW(wdst, tempName) != FALSE) {
- if (MoveFileW(wsrc, wdst) != FALSE) {
- SetFileAttributesW(tempName, FILE_ATTRIBUTE_NORMAL);
- DeleteFileW(tempName);
- return 1;
- } else {
- DeleteFileW(wdst);
- MoveFileW(tempName, wdst);
- }
- }
-
- /*
- * Can't backup dst file or move src file. Return that
- * error. Could happen if an open file refers to dst.
- */
-
- errno = errno_map(GetLastError());
- if (errno == EACCES) {
- /*
- * Decode the EACCES to a more meaningful error.
- */
- goto decode;
- }
- }
- return result;
- }
- }
- }
- return check_error(-1, state->errInfo);
-}
-
-int
-efile_chdir(Efile_error* errInfo, /* Where to return error codes. */
- char* name) /* Name of directory to make current. */
-{
- /* We don't even try to handle long paths here
- * as current working directory is always limited to MAX_PATH
- * even if we use UNC paths and SetCurrentDirectoryW()
- */
- int success = check_error(_wchdir((WCHAR *) name), errInfo);
- if (!success && errInfo->posix_errno == EINVAL)
- /* POSIXification of errno */
- errInfo->posix_errno = ENOENT;
- return success;
-}
-
-int
-efile_getdcwd(Efile_error* errInfo, /* Where to return error codes. */
- int drive, /* 0 - current, 1 - A, 2 - B etc. */
- char* buffer, /* Where to return the current directory. */
- size_t size) /* Size of buffer. */
-{
- WCHAR *wbuffer = (WCHAR *) buffer;
- size_t wbuffer_size = size / 2;
- DBG_TRACE(1, L"#getdcwd#");
- if (_wgetdcwd(drive, wbuffer, wbuffer_size) == NULL) {
- return check_error(-1, errInfo);
- }
- DBG_TRACE1(8, "getdcwd OS=%s", wbuffer);
- if (wcsncmp(wbuffer, L"\\\\?\\", 4) == 0) {
- wmemmove(wbuffer, wbuffer+4, wcslen(wbuffer+4)+1);
- }
- for ( ; *wbuffer; wbuffer++)
- if (*wbuffer == L'\\')
- *wbuffer = L'/';
- DBG_TRACE1(8, "getdcwd ERLANG=%s", (WCHAR*)buffer);
- return 1;
-}
-
-int
-efile_readdir(Efile_error* errInfo, char* name, EFILE_DIR_HANDLE* dir_handle,
- char* buffer, size_t *size)
-{
- Efile_call_state state;
- int ret;
- DBG_TRACE(dir_handle?2:1, name);
- call_state_init(&state, errInfo);
- ret = do_readdir(&state, name, dir_handle, buffer, size);
- call_state_free(&state);
- return ret;
-}
-
-static int do_readdir(Efile_call_state* state,
- char* name, /* Name of directory to list */
- EFILE_DIR_HANDLE* dir_handle, /* Handle of opened directory or NULL */
- char* buffer, /* Buffer to put one filename in */
- size_t *size) /* in-out size of buffer/size of filename excluding zero
- termination in bytes*/
-{
- HANDLE dir; /* Handle to directory. */
- WIN32_FIND_DATAW findData; /* Data found by FindFirstFile() or FindNext(). */
- /* Alignment is not honored, this works on x86 because of alignment fixup by processor.
- Not perfect, but faster than alinging by hand (really) */
- WCHAR *wbuffer = (WCHAR *) buffer;
-
- /*
- * First time we must setup everything.
- */
-
- if (*dir_handle == NULL) {
- WCHAR *wname = (WCHAR *) name;
- WCHAR* wildcard;
- int length;
- WCHAR* s;
-
- ensure_wpath_max(state, &wname, MAX_PATH-2);
- length = wcslen(wname);
-
- wildcard = wpath_tmp_alloc(state, length+3);
-
- wcscpy(wildcard, wname);
- s = wildcard+length-1;
- if (*s != L'/' && *s != L'\\')
- *++s = L'\\';
- *++s = L'*';
- *++s = L'\0';
- DEBUGF(("Reading %ws\n", wildcard));
- dir = FindFirstFileW(wildcard, &findData);
- if (dir == INVALID_HANDLE_VALUE) {
- set_error(state->errInfo);
- return 0;
- }
- *dir_handle = (EFILE_DIR_HANDLE) dir;
-
- if (!IS_DOT_OR_DOTDOT(findData.cFileName)) {
- wcscpy(wbuffer, findData.cFileName);
- *size = wcslen(wbuffer)*2;
- return 1;
- }
- }
-
- /*
- * Retrieve the name of the next file using the directory handle.
- */
-
- dir = (HANDLE) *dir_handle;
-
- for (;;) {
- if (FindNextFileW(dir, &findData)) {
- if (IS_DOT_OR_DOTDOT(findData.cFileName))
- continue;
- wcscpy(wbuffer, findData.cFileName);
- *size = wcslen(wbuffer)*2;
- return 1;
- }
-
- if (GetLastError() == ERROR_NO_MORE_FILES) {
- state->errInfo->posix_errno = state->errInfo->os_errno = 0;
- }
- else {
- set_error(state->errInfo);
- }
- FindClose(dir);
- return 0;
- }
-}
-
-int
-efile_openfile(Efile_error* errInfo, char* name, int flags, int* pfd, Sint64* pSize)
-{
- Efile_call_state state;
- int ret;
- DBG_TRACE1(1, "openfile(%s)", name);
- call_state_init(&state, errInfo);
- ret = do_openfile(&state, name, flags, pfd, pSize);
- call_state_free(&state);
- return ret;
-}
-
-static
-int do_openfile(Efile_call_state* state, /* Where to return error codes. */
- char* name, /* Name of directory to open. */
- int flags, /* Flags to use for opening. */
- int* pfd, /* Where to store the file descriptor. */
- Sint64* pSize) /* Where to store the size of the file. */
-{
- Efile_error* errInfo = state->errInfo;
- BY_HANDLE_FILE_INFORMATION fileInfo; /* File information from a handle. */
- HANDLE fd; /* Handle to open file. */
- DWORD access; /* Access mode: GENERIC_READ, GENERIC_WRITE. */
- DWORD crFlags;
- DWORD flagsAndAttrs = FILE_ATTRIBUTE_NORMAL;
- WCHAR *wname = (WCHAR *) name;
-
- switch (flags & (EFILE_MODE_READ|EFILE_MODE_WRITE)) {
- case EFILE_MODE_READ:
- access = GENERIC_READ;
- crFlags = OPEN_EXISTING;
- break;
- case EFILE_MODE_WRITE:
- access = GENERIC_WRITE;
- crFlags = CREATE_ALWAYS;
- break;
- case EFILE_MODE_READ_WRITE:
- access = GENERIC_READ|GENERIC_WRITE;
- crFlags = OPEN_ALWAYS;
- break;
- default:
- errno = EINVAL;
- check_error(-1, errInfo);
- return 0;
- }
-
- if (flags & EFILE_MODE_SYNC) {
- flagsAndAttrs = FILE_FLAG_WRITE_THROUGH;
- }
-
- if (flags & EFILE_MODE_APPEND) {
- crFlags = OPEN_ALWAYS;
- }
- if (flags & EFILE_MODE_EXCL) {
- crFlags = CREATE_NEW;
- }
- ensure_wpath(state, &wname);
- fd = CreateFileW(wname, access,
- FILE_SHARE_FLAGS,
- NULL, crFlags, flagsAndAttrs, NULL);
-
- /*
- * Check for errors.
- */
-
- if (fd == INVALID_HANDLE_VALUE) {
- DWORD attr;
-
- set_error(errInfo);
-
- /*
- * If the error is EACESS, the reason could be that we tried to
- * open a directory. In that case, we'll change the error code
- * to EISDIR.
- */
- if (errInfo->posix_errno &&
- (attr = GetFileAttributesW(wname)) != INVALID_FILE_ATTRIBUTES &&
- (attr & FILE_ATTRIBUTE_DIRECTORY)) {
- errInfo->posix_errno = EISDIR;
- }
- return 0;
- }
-
- /*
- * Get and return the length of the open file.
- */
-
- if (!GetFileInformationByHandle(fd, &fileInfo))
- return set_error(errInfo);
- *pfd = (int) fd;
- if (pSize) {
- *pSize = (Sint64)
- (((Uint64)fileInfo.nFileSizeHigh << 32) |
- (Uint64)fileInfo.nFileSizeLow);
- }
- return 1;
-}
-
-int
-efile_may_openfile(Efile_error* errInfo, char *name)
-{
- Efile_call_state state;
- WCHAR *wname = (WCHAR *) name;
- DWORD attr;
- int ret;
-
- DBG_TRACE(1, name);
- call_state_init(&state, errInfo);
- ensure_wpath(&state, &wname);
- if ((attr = GetFileAttributesW(wname)) == INVALID_FILE_ATTRIBUTES) {
- errno = ENOENT;
- ret = check_error(-1, errInfo);
- }
- else if (attr & FILE_ATTRIBUTE_DIRECTORY) {
- errno = EISDIR;
- ret = check_error(-1, errInfo);
- }
- else ret = 1;
-
- call_state_free(&state);
- return ret;
-}
-
-void
-efile_closefile(fd)
-int fd; /* File descriptor for file to close. */
-{
- DBG_TRACE(2, L"");
- CloseHandle((HANDLE) fd);
-}
-
-FILE* efile_wfopen(const WCHAR* name, const WCHAR* mode)
-{
- Efile_call_state state;
- Efile_error dummy;
- FILE* f;
- call_state_init(&state, &dummy);
- ensure_wpath(&state, (WCHAR**)&name);
- f = _wfopen(name, mode);
- call_state_free(&state);
- return f;
-}
-
-int
-efile_fdatasync(errInfo, fd)
-Efile_error* errInfo; /* Where to return error codes. */
-int fd; /* File descriptor for file to sync. */
-{
- DBG_TRACE(2, L"");
- /* Not available in Windows, just call regular fsync */
- return efile_fsync(errInfo, fd);
-}
-
-int
-efile_fsync(errInfo, fd)
-Efile_error* errInfo; /* Where to return error codes. */
-int fd; /* File descriptor for file to sync. */
-{
- DBG_TRACE(2, L"");
- if (!FlushFileBuffers((HANDLE) fd)) {
- return check_error(-1, errInfo);
- }
- return 1;
-}
-
-int
-efile_fileinfo(Efile_error* errInfo, Efile_info* pInfo,
- char* orig_name, int info_for_link)
-{
- Efile_call_state state;
- int ret;
- DBG_TRACE(1, L"");
- call_state_init(&state, errInfo);
- ret = do_fileinfo(&state, pInfo, orig_name, info_for_link);
- call_state_free(&state);
- return ret;
-}
-
-static int
-do_fileinfo(Efile_call_state* state, Efile_info* pInfo,
- char* orig_name, int info_for_link)
-{
- Efile_error* errInfo = state->errInfo;
- HANDLE findhandle; /* Handle returned by FindFirstFile(). */
- WIN32_FIND_DATAW findbuf; /* Data return by FindFirstFile(). */
- WCHAR* name = NULL;
- WCHAR* win_path;
- int name_len;
- int drive; /* Drive for filename (1 = A:, 2 = B: etc). */
- WCHAR *worig_name = (WCHAR *) orig_name;
-
- ensure_wpath(state, &worig_name);
- /* Don't allow wildcards to be interpreted by system */
-
-
- /*
- * Move the name to a buffer and make sure to remove a trailing
- * slash, because it causes FindFirstFile() to fail on Win95.
- */
-
- name_len = wcslen(worig_name);
-
- name = wpath_tmp_alloc(state, name_len+1);
- wcscpy(name, worig_name);
- if (name_len > 2 && ISSLASH(name[name_len-1]) &&
- name[name_len-2] != L':') {
- name[name_len-1] = L'\0';
- }
-
- win_path = name;
- if (wcsncmp(name, L"\\\\?\\", 4) == 0) {
- win_path += 4;
- }
-
- if (wcspbrk(win_path, L"?*")) {
- enoent:
- errInfo->posix_errno = ENOENT;
- errInfo->os_errno = ERROR_FILE_NOT_FOUND;
- return 0;
- }
-
- /* Try to get disk from name. If none, get current disk. */
-
- if (win_path[1] != L':') {
- WCHAR* cwd_path = get_cwd_wpath_tmp(state);
- drive = 0;
- if (cwd_path[1] == L':') {
- drive = towlower(cwd_path[0]) - L'a' + 1;
- }
- } else if (*win_path && win_path[2] == L'\0') {
- /*
- * X: and nothing more is an error.
- */
- errInfo->posix_errno = ENOENT;
- errInfo->os_errno = ERROR_FILE_NOT_FOUND;
- return 0;
- } else {
- drive = towlower(*win_path) - L'a' + 1;
- }
-
- findhandle = FindFirstFileW(name, &findbuf);
- if (findhandle == INVALID_HANDLE_VALUE) {
- WCHAR* path = NULL;
-
- if (!(wcspbrk(name, L"./\\") &&
- (path = get_full_wpath_tmp(state, name, NULL, 0)) &&
- /* root dir. ('C:\') or UNC root dir. ('\\server\share\') */
- ((wcslen(path) == 3) || is_root_unc_name(path)) &&
- (GetDriveTypeW(path) > 1) ) ) {
-
- errInfo->posix_errno = ENOENT;
- errInfo->os_errno = ERROR_FILE_NOT_FOUND;
- return 0;
- }
-
- /*
- * Root directories (such as C:\ or \\server\share\ are fabricated.
- */
-
- findbuf.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
- findbuf.nFileSizeHigh = 0;
- findbuf.nFileSizeLow = 0;
- findbuf.cFileName[0] = L'\0';
-
- pInfo->links = 1;
- pInfo->cTime = pInfo->accessTime = pInfo->modifyTime = 0;
- } else {
- SYSTEMTIME SystemTime;
- FILETIME LocalFTime;
-
- /*first check if we are a symlink */
- if (!info_for_link && (findbuf.dwFileAttributes &
- FILE_ATTRIBUTE_REPARSE_POINT)){
- /*
- * given that we know this is a symlink,
- we should be able to find its target */
- WCHAR* target_name = (WCHAR*) do_readlink(state, (char *) name, NULL, 0);
- if (target_name) {
- FindClose(findhandle);
- return do_fileinfo(state, pInfo,
- (char *) target_name, info_for_link);
- }
- }
-
- /* number of links: */
- {
- HANDLE handle; /* Handle returned by CreateFile() */
- BY_HANDLE_FILE_INFORMATION fileInfo; /* from CreateFile() */
-
- /* We initialise nNumberOfLinks as GetFileInformationByHandle
- does not always initialise this field */
- fileInfo.nNumberOfLinks = 1;
- if (handle = CreateFileW(name, GENERIC_READ, FILE_SHARE_FLAGS, NULL,
- OPEN_EXISTING, 0, NULL)) {
- GetFileInformationByHandle(handle, &fileInfo);
- pInfo->links = fileInfo.nNumberOfLinks;
- CloseHandle(handle);
- } else {
- pInfo->links = 1;
- }
- }
-
- FILETIME_TO_EPOCH(pInfo->modifyTime, findbuf.ftLastWriteTime);
-
- if (findbuf.ftLastAccessTime.dwLowDateTime == 0 &&
- findbuf.ftLastAccessTime.dwHighDateTime == 0) {
- pInfo->accessTime = pInfo->modifyTime;
- } else {
- FILETIME_TO_EPOCH(pInfo->accessTime, findbuf.ftLastAccessTime);
- }
-
- if (findbuf.ftCreationTime.dwLowDateTime == 0 &&
- findbuf.ftCreationTime.dwHighDateTime == 0) {
- pInfo->cTime = pInfo->modifyTime;
- } else {
- FILETIME_TO_EPOCH(pInfo->cTime ,findbuf.ftCreationTime);
- }
- FindClose(findhandle);
- }
-
- pInfo->size_low = findbuf.nFileSizeLow;
- pInfo->size_high = findbuf.nFileSizeHigh;
-
- if (info_for_link && (findbuf.dwFileAttributes &
- FILE_ATTRIBUTE_REPARSE_POINT))
- pInfo->type = FT_SYMLINK;
- else if (findbuf.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
- pInfo->type = FT_DIRECTORY;
- else
- pInfo->type = FT_REGULAR;
-
- if (findbuf.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
- pInfo->access = FA_READ;
- else
- pInfo->access = FA_READ|FA_WRITE;
-
- pInfo->mode = dos_to_posix_mode(findbuf.dwFileAttributes, name);
- pInfo->major_device = drive;
- pInfo->minor_device = 0;
- pInfo->inode = 0;
- pInfo->uid = 0;
- pInfo->gid = 0;
-
- return 1;
-}
-
-int
-efile_write_info(Efile_error* errInfo,
- Efile_info* pInfo,
- char* name)
-{
- Efile_call_state state;
- int ret;
- call_state_init(&state, errInfo);
- ret = do_write_info(&state, pInfo, name);
- call_state_free(&state);
- return ret;
-}
-
-static int
-do_write_info(Efile_call_state* state,
- Efile_info* pInfo,
- char* name)
-{
- Efile_error* errInfo = state->errInfo;
- SYSTEMTIME timebuf;
- FILETIME ModifyFileTime;
- FILETIME AccessFileTime;
- FILETIME CreationFileTime;
- HANDLE fd;
- DWORD attr;
- DWORD tempAttr;
- WCHAR *wname = (WCHAR *) name;
-
- DBG_TRACE(1, name);
-
- ensure_wpath(state, &wname);
-
- /*
- * Get the attributes for the file.
- */
-
- tempAttr = attr = GetFileAttributesW(wname);
- if (attr == 0xffffffff) {
- return set_error(errInfo);
- }
- if (pInfo->mode != -1) {
- if (pInfo->mode & _S_IWRITE) {
- /* clear read only bit */
- attr &= ~FILE_ATTRIBUTE_READONLY;
- } else {
- /* set read only bit */
- attr |= FILE_ATTRIBUTE_READONLY;
- }
- }
-
- /*
- * Construct all file times.
- */
-
- EPOCH_TO_FILETIME(ModifyFileTime, pInfo->modifyTime);
- EPOCH_TO_FILETIME(AccessFileTime, pInfo->accessTime);
- EPOCH_TO_FILETIME(CreationFileTime, pInfo->cTime);
-
- /*
- * If necessary, set the file times.
- */
-
- /*
- * If the has read only access, we must temporarily turn on
- * write access (this is necessary for native filesystems,
- * but not for NFS filesystems).
- */
-
- if (tempAttr & FILE_ATTRIBUTE_READONLY) {
- tempAttr &= ~FILE_ATTRIBUTE_READONLY;
- if (!SetFileAttributesW(wname, tempAttr)) {
- return set_error(errInfo);
- }
- }
-
- fd = CreateFileW(wname, GENERIC_READ|GENERIC_WRITE,
- FILE_SHARE_FLAGS,
- NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
- if (fd != INVALID_HANDLE_VALUE) {
- BOOL result = SetFileTime(fd, &CreationFileTime, &AccessFileTime, &ModifyFileTime);
- if (!result) {
- return set_error(errInfo);
- }
- CloseHandle(fd);
- }
-
- /*
- * If the file doesn't have the correct attributes, set them now.
- * (It could have been done before setting the file times, above).
- */
-
- if (tempAttr != attr) {
- if (!SetFileAttributesW(wname, attr)) {
- return set_error(errInfo);
- }
- }
- return 1;
-}
-
-
-int
-efile_pwrite(errInfo, fd, buf, count, offset)
-Efile_error* errInfo; /* Where to return error codes. */
-int fd; /* File descriptor to write to. */
-char* buf; /* Buffer to write. */
-size_t count; /* Number of bytes to write. */
-Sint64 offset; /* where to write it */
-{
- int res;
- DBG_TRACE(2, L"");
- res = efile_seek(errInfo, fd, offset, EFILE_SEEK_SET, NULL);
- if (res) {
- return efile_write(errInfo, EFILE_MODE_WRITE, fd, buf, count);
- } else {
- return res;
- }
-}
-
-/* position and read/write as a single atomic op */
-int
-efile_pread(errInfo, fd, offset, buf, count, pBytesRead)
-Efile_error* errInfo; /* Where to return error codes. */
-int fd; /* File descriptor to read from. */
-Sint64 offset; /* Offset in bytes from BOF. */
-char* buf; /* Buffer to read into. */
-size_t count; /* Number of bytes to read. */
-size_t* pBytesRead; /* Where to return number of bytes read. */
-{
- int res;
- DBG_TRACE(2, L"");
- res = efile_seek(errInfo, fd, offset, EFILE_SEEK_SET, NULL);
- if (res) {
- return efile_read(errInfo, EFILE_MODE_READ, fd, buf, count, pBytesRead);
- } else {
- return res;
- }
-}
-
-
-
-int
-efile_write(errInfo, flags, fd, buf, count)
-Efile_error* errInfo; /* Where to return error codes. */
-int flags; /* Flags given when file was opened. */
-int fd; /* File descriptor to write to. */
-char* buf; /* Buffer to write. */
-size_t count; /* Number of bytes to write. */
-{
- DWORD written; /* Bytes written in last operation. */
- OVERLAPPED overlapped;
- OVERLAPPED* pOverlapped = NULL;
-
- DBG_TRACE(2, L"");
- if (flags & EFILE_MODE_APPEND) {
- memset(&overlapped, 0, sizeof(overlapped));
- overlapped.Offset = 0xffffffff;
- overlapped.OffsetHigh = 0xffffffff;
- pOverlapped = &overlapped;
- }
- while (count > 0) {
- if (!WriteFile((HANDLE) fd, buf, count, &written, pOverlapped))
- return set_error(errInfo);
- buf += written;
- count -= written;
- }
- return 1;
-}
-
-int
-efile_writev(Efile_error* errInfo, /* Where to return error codes */
- int flags, /* Flags given when file was
- * opened */
- int fd, /* File descriptor to write to */
- SysIOVec* iov, /* Vector of buffer structs.
- * The structs are unchanged
- * after the call */
- int iovcnt) /* Number of structs in vector */
-{
- int cnt; /* Buffers so far written */
- OVERLAPPED overlapped;
- OVERLAPPED* pOverlapped = NULL;
-
- DBG_TRACE(2, L"");
- ASSERT(iovcnt >= 0);
-
- if (flags & EFILE_MODE_APPEND) {
- memset(&overlapped, 0, sizeof(overlapped));
- overlapped.Offset = 0xffffffff;
- overlapped.OffsetHigh = 0xffffffff;
- pOverlapped = &overlapped;
- }
- for (cnt = 0; cnt < iovcnt; cnt++) {
- if (iov[cnt].iov_base && iov[cnt].iov_len > 0) {
- /* Non-empty buffer */
- int p; /* Position in buffer */
- int w = iov[cnt].iov_len;/* Bytes written in this call */
- for (p = 0; p < iov[cnt].iov_len; p += w) {
- if (!WriteFile((HANDLE) fd,
- iov[cnt].iov_base + p,
- iov[cnt].iov_len - p,
- &w,
- pOverlapped))
- return set_error(errInfo);
- }
- }
- }
- return 1;
-}
-
-int
-efile_read(errInfo, flags, fd, buf, count, pBytesRead)
-Efile_error* errInfo; /* Where to return error codes. */
-int flags; /* Flags given when file was opened. */
-int fd; /* File descriptor to read from. */
-char* buf; /* Buffer to read into. */
-size_t count; /* Number of bytes to read. */
-size_t* pBytesRead; /* Where to return number of bytes read. */
-{
- DWORD nbytes = 0;
-
- DBG_TRACE(2, L"");
- if (!ReadFile((HANDLE) fd, buf, count, &nbytes, NULL))
- return set_error(errInfo);
-
- *pBytesRead = nbytes;
- return 1;
-}
-
-int
-efile_seek(errInfo, fd, offset, origin, new_location)
-Efile_error* errInfo; /* Where to return error codes. */
-int fd; /* File descriptor to do the seek on. */
-Sint64 offset; /* Offset in bytes from the given origin. */
-int origin; /* Origin of seek (SEEK_SET, SEEK_CUR,
- * SEEK_END).
- */
-Sint64* new_location; /* Resulting new location in file. */
-{
- LARGE_INTEGER off, new_loc;
-
- DBG_TRACE(2, L"");
- switch (origin) {
- case EFILE_SEEK_SET: origin = FILE_BEGIN; break;
- case EFILE_SEEK_CUR: origin = FILE_CURRENT; break;
- case EFILE_SEEK_END: origin = FILE_END; break;
- default:
- errno = EINVAL;
- check_error(-1, errInfo);
- break;
- }
-
- off.QuadPart = offset;
- if (! SetFilePointerEx((HANDLE) fd, off,
- new_location ? &new_loc : NULL, origin)) {
- return set_error(errInfo);
- }
- if (new_location) {
- *new_location = new_loc.QuadPart;
- DEBUGF(("efile_seek(offset=%ld, origin=%d) -> %ld\n",
- (long) offset, origin, (long) *new_location));
- } else {
- DEBUGF(("efile_seek(offset=%ld, origin=%d)\n", (long) offset, origin));
- }
- return 1;
-}
-
-int
-efile_truncate_file(errInfo, fd, flags)
-Efile_error* errInfo; /* Where to return error codes. */
-int *fd; /* File descriptor for file to truncate. */
-int flags;
-{
- DBG_TRACE(2, L"");
- if (!SetEndOfFile((HANDLE) (*fd)))
- return set_error(errInfo);
- return 1;
-}
-
-
-/*
- * is_root_unc_name - returns TRUE if the argument is a UNC name specifying
- * a root share. That is, if it is of the form \\server\share\.
- * This routine will also return true if the argument is of the
- * form \\server\share (no trailing slash) but Win32 currently
- * does not like that form.
- *
- * Forward slashes ('/') may be used instead of backslashes ('\').
- */
-
-static int
-is_root_unc_name(const WCHAR *path)
-{
- /*
- * If a root UNC name, path will start with 2 (but not 3) slashes
- */
-
- if ((wcslen(path) >= 5) /* minimum string is "//x/y" */
- && ISSLASH(path[0]) && ISSLASH(path[1]))
- {
- const WCHAR *p = path + 2;
-
- /*
- * find the slash between the server name and share name
- */
- while ( * ++ p )
- if ( ISSLASH(*p) )
- break ;
-
- if ( *p && p[1] )
- {
- /*
- * is there a further slash?
- */
- while ( * ++ p )
- if ( ISSLASH(*p) )
- break ;
-
- /*
- * just final slash (or no final slash)
- */
- if ( !*p || !p[1])
- return 1;
- }
- }
-
- return 0 ;
-}
-
-/*
- * Extracts the root part of an absolute filename (by modifying the string
- * pointed to by the name argument). The name can start
- * with either a driver letter (for example, C:\), or a UNC name
- * (for example, \\guinness\bjorn).
- *
- * If the name is invalid, the buffer will be modified to point to
- * an empty string.
- *
- * Returns: 1 if the name consists of just the root part, 0 if
- * the name was longer.
- */
-
-static int
-extract_root(WCHAR* name)
-{
- int len = wcslen(name);
-
- if (iswalpha(name[0]) && name[1] == L':' && ISSLASH(name[2])) {
- WCHAR c = name[3];
- name[3] = L'\0';
- return c == L'\0';
- } else if (len < 5 || !ISSLASH(name[0]) || !ISSLASH(name[1])) {
- goto error;
- } else { /* Try to find the end of the UNC name. */
- WCHAR* p;
- WCHAR c;
-
- /*
- * Find the slash between the server name and share name.
- */
-
- for (p = name + 2; *p; p++)
- if (ISSLASH(*p))
- break;
- if (*p == L'\0')
- goto error;
-
- /*
- * Find the slash after the share name.
- */
-
- for (p++; *p; p++)
- if (ISSLASH(*p))
- break;
- c = *p;
- *p = L'\0';
- return c == L'\0' || p[1] == L'\0';
- }
-
- error:
- *name = L'\0';
- return 1;
-}
-
-static unsigned short
-dos_to_posix_mode(int attr, const WCHAR *name)
-{
- register unsigned short uxmode;
- unsigned dosmode;
- register const WCHAR *p;
-
- dosmode = attr & 0xff;
- if ((p = name)[1] == L':')
- p += 2;
-
- /* check to see if this is a directory - note we must make a special
- * check for the root, which DOS thinks is not a directory
- */
-
- uxmode = (unsigned short)
- (((ISSLASH(*p) && !p[1]) || (dosmode & FILE_ATTRIBUTE_DIRECTORY) ||
- *p == L'\0') ? _S_IFDIR|_S_IEXEC : _S_IFREG);
-
- /* If attribute byte does not have read-only bit, it is read-write */
-
- uxmode |= (dosmode & FILE_ATTRIBUTE_READONLY) ?
- _S_IREAD : (_S_IREAD|_S_IWRITE);
-
- /* see if file appears to be executable - check extension of name */
-
- if (p = wcsrchr(name, L'.')) {
- if (!_wcsicmp(p, L".exe") ||
- !_wcsicmp(p, L".cmd") ||
- !_wcsicmp(p, L".bat") ||
- !_wcsicmp(p, L".com"))
- uxmode |= _S_IEXEC;
- }
-
- /* propagate user read/write/execute bits to group/other fields */
-
- uxmode |= (uxmode & 0700) >> 3;
- uxmode |= (uxmode & 0700) >> 6;
-
- return uxmode;
-}
-
-
-int
-efile_readlink(Efile_error* errInfo, char* name, char* buffer, size_t size)
-{
- Efile_call_state state;
- int ret;
- DBG_TRACE(1, name);
- call_state_init(&state, errInfo);
- ret = !!do_readlink(&state, name, buffer, size);
- call_state_free(&state);
- return ret;
-}
-
-/* If buffer==0, return buffer allocated by wpath_tmp_allocate
-*/
-static char*
-do_readlink(Efile_call_state* state, char* name, char* buffer, size_t size)
-{
- /*
- * load dll and see if we have CreateSymbolicLink at runtime:
- * (Vista only)
- */
- HINSTANCE hModule = NULL;
- WCHAR *wname = (WCHAR *) name;
- WCHAR *wbuffer = (WCHAR *) buffer;
- DWORD wsize = size / sizeof(WCHAR);
- char* ret = NULL;
-
- if ((hModule = LoadLibrary("kernel32.dll")) != NULL) {
- typedef DWORD (WINAPI * GETFINALPATHNAMEBYHANDLEPTR)(
- HANDLE hFile,
- LPCWSTR lpFilePath,
- DWORD cchFilePath,
- DWORD dwFlags);
-
- GETFINALPATHNAMEBYHANDLEPTR pGetFinalPathNameByHandle =
- (GETFINALPATHNAMEBYHANDLEPTR)GetProcAddress(hModule, "GetFinalPathNameByHandleW");
-
- if (pGetFinalPathNameByHandle != NULL) {
- DWORD fileAttributes;
- ensure_wpath(state, &wname);
- /* first check if file is a symlink; {error, einval} otherwise */
- fileAttributes = GetFileAttributesW(wname);
- if ((fileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
- DWORD success = 0;
- HANDLE h = CreateFileW(wname, GENERIC_READ, FILE_SHARE_FLAGS, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
- int len;
- if(h != INVALID_HANDLE_VALUE) {
- if (!wbuffer) { /* dynamic allocation */
- WCHAR dummy;
- wsize = pGetFinalPathNameByHandle(h, &dummy, 0, 0);
- if (wsize) {
- wbuffer = wpath_tmp_alloc(state, wsize);
- }
- }
- if (wbuffer
- && (success = pGetFinalPathNameByHandle(h, wbuffer, wsize, 0))
- && success < wsize) {
- WCHAR* wp;
-
- /* GetFinalPathNameByHandle prepends path with "\\?\": */
- len = wcslen(wbuffer);
- wmemmove(wbuffer,wbuffer+4,len-3);
- if (len - 4 >= 2 && wbuffer[1] == L':' && wbuffer[0] >= L'A' &&
- wbuffer[0] <= L'Z') {
- wbuffer[0] = wbuffer[0] + L'a' - L'A';
- }
-
- for (wp=wbuffer ; *wp; wp++)
- if (*wp == L'\\')
- *wp = L'/';
- }
- CloseHandle(h);
- }
- if (success) {
- ret = (char*) wbuffer;
- } else {
- set_error(state->errInfo);
- }
- } else {
- errno = EINVAL;
- save_last_error(state->errInfo);
- }
- goto done;
- }
- }
- errno = ENOTSUP;
- save_last_error(state->errInfo);
-
-done:
- if (hModule)
- FreeLibrary(hModule);
- return ret;
-}
-
-
-int
-efile_altname(Efile_error* errInfo, char* orig_name, char* buffer, size_t size)
-{
- Efile_call_state state;
- int ret;
- DBG_TRACE(1, orig_name);
- call_state_init(&state, errInfo);
- ret = do_altname(&state, orig_name, buffer, size);
- call_state_free(&state);
- return ret;
-}
-
-static int
-do_altname(Efile_call_state* state, char* orig_name, char* buffer, size_t size)
-{
- WIN32_FIND_DATAW wfd;
- HANDLE fh;
- WCHAR* name;
- int name_len;
- WCHAR* full_path = NULL;
- WCHAR *worig_name = (WCHAR *) orig_name;
- WCHAR *wbuffer = (WCHAR *) buffer;
- int drive; /* Drive for filename (1 = A:, 2 = B: etc). */
-
- /* Don't allow wildcards to be interpreted by system */
-
- if (wcspbrk(worig_name, L"?*")) {
- enoent:
- state->errInfo->posix_errno = ENOENT;
- state->errInfo->os_errno = ERROR_FILE_NOT_FOUND;
- return 0;
- }
-
- /*
- * Move the name to a buffer and make sure to remove a trailing
- * slash, because it causes FindFirstFile() to fail on Win95.
- */
- ensure_wpath(state, &worig_name);
- name_len = wcslen(worig_name);
-
- name = wpath_tmp_alloc(state, name_len + 1);
- wcscpy(name, worig_name);
- if (name_len > 2 && ISSLASH(name[name_len-1]) &&
- name[name_len-2] != L':') {
- name[name_len-1] = L'\0';
- }
-
- /* Try to get disk from name. If none, get current disk. */
-
- if (name[1] != L':') {
- WCHAR* cwd_path = get_cwd_wpath_tmp(state);
- drive = 0;
- if (cwd_path[1] == L':') {
- drive = towlower(cwd_path[0]) - L'a' + 1;
- }
- } else if (*name && name[2] == L'\0') {
- /*
- * X: and nothing more is an error.
- */
- goto enoent;
- } else {
- drive = towlower(*name) - L'a' + 1;
- }
- fh = FindFirstFileW(name,&wfd);
- if (fh == INVALID_HANDLE_VALUE) {
- DWORD fff_error = GetLastError();
- if (!(wcspbrk(name, L"./\\") &&
- (full_path = get_full_wpath_tmp(state, name, NULL, 0)) &&
- /* root dir. ('C:\') or UNC root dir. ('\\server\share\') */
- ((wcslen(full_path) == 3) || is_root_unc_name(full_path)) &&
- (GetDriveTypeW(full_path) > 1) ) ) {
-
- set_os_errno(state->errInfo, fff_error);
- return 0;
- }
- /*
- * Root directories (such as C:\ or \\server\share\ are fabricated.
- */
- wcscpy(wbuffer,name);
- return 1;
- }
-
- wcscpy(wbuffer,wfd.cAlternateFileName);
- if (!*wbuffer) {
- wcscpy(wbuffer,wfd.cFileName);
- }
- FindClose(fh);
- return 1;
-}
-
-
-int
-efile_link(Efile_error* errInfo, char* old, char* new)
-{
- Efile_call_state state;
- WCHAR *wold = (WCHAR *) old;
- WCHAR *wnew = (WCHAR *) new;
- int ret;
- DBG_TRACE(1, old);
- call_state_init(&state, errInfo);
- ensure_wpath(&state, &wold);
- ensure_wpath(&state, &wnew);
- if(!CreateHardLinkW(wnew, wold, NULL)) {
- ret = set_error(errInfo);
- }
- else ret =1;
- call_state_free(&state);
- return ret;
-}
-
-int
-efile_symlink(Efile_error* errInfo, char* old, char* new)
-{
- Efile_call_state state;
- int ret;
- DBG_TRACE2(1, "symlink(%s <- %s)", old, new);
- call_state_init(&state, errInfo);
- ret = do_symlink(&state, old, new);
- call_state_free(&state);
- return ret;
-}
-
-static int
-do_symlink(Efile_call_state* state, char* old, char* new)
-{
- /*
- * Load dll and see if we have CreateSymbolicLink at runtime:
- * (Vista only)
- */
- HINSTANCE hModule = NULL;
- WCHAR *wold = (WCHAR *) old;
- WCHAR *wnew = (WCHAR *) new;
-
- DBG_TRACE(1, old);
- if ((hModule = LoadLibrary("kernel32.dll")) != NULL) {
- typedef BOOLEAN (WINAPI * CREATESYMBOLICLINKFUNCPTR) (
- LPCWSTR lpSymlinkFileName,
- LPCWSTR lpTargetFileName,
- DWORD dwFlags);
-
- CREATESYMBOLICLINKFUNCPTR pCreateSymbolicLink =
- (CREATESYMBOLICLINKFUNCPTR) GetProcAddress(hModule,
- "CreateSymbolicLinkW");
- /* A for MBCS, W for UNICODE... char* above implies 'W'! */
- if (pCreateSymbolicLink != NULL) {
- ensure_wpath(state, &wold);
- ensure_wpath(state, &wnew);
- {
- DWORD attr = GetFileAttributesW(wold);
- int flag = (attr != INVALID_FILE_ATTRIBUTES &&
- attr & FILE_ATTRIBUTE_DIRECTORY) ? 1 : 0;
- /* SYMBOLIC_LINK_FLAG_DIRECTORY = 1 */
- BOOLEAN success = pCreateSymbolicLink(wnew, wold, flag);
- FreeLibrary(hModule);
-
- if (success) {
- return 1;
- } else {
- return set_error(state->errInfo);
- }
- }
- } else
- FreeLibrary(hModule);
- }
- errno = ENOTSUP;
- return check_error(-1, state->errInfo);
-}
-
-int
-efile_fadvise(Efile_error* errInfo, int fd, Sint64 offset,
- Sint64 length, int advise)
-{
- DBG_TRACE(2, L"");
- /* posix_fadvise is not available on Windows, do nothing */
- errno = ERROR_SUCCESS;
- return check_error(0, errInfo);
-}
-
-int
-efile_fallocate(Efile_error* errInfo, int fd, Sint64 offset, Sint64 length)
-{
- DBG_TRACE(2, L"");
- /* No file preallocation method available in Windows. */
- errno = errno_map(ERROR_NOT_SUPPORTED);
- SetLastError(ERROR_NOT_SUPPORTED);
-
- return check_error(-1, errInfo);
-}