win_efile.c
来自「OTP是开放电信平台的简称」· C语言 代码 · 共 1,417 行 · 第 1/3 页
C
1,417 行
/* ``The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in * compliance with the License. You should have received a copy of the * Erlang Public License along with this software. If not, it can be * retrieved via the world wide web at http://www.erlang.org/. * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Initial Developer of the Original Code is Ericsson Utvecklings AB. * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings * AB. All Rights Reserved.'' * * $Id$ *//* * Purpose: Provides file and directory operations for Windows. */#include <windows.h>#include "sys.h"#include <ctype.h>#include "erl_efile.h"/* * Microsoft-specific function to map a WIN32 error code to a Posix errno. */#define ISSLASH(a) ((a) == '\\' || (a) == '/')#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] == '.' && (s[1] == '\0' || (s[1] == '.' && s[2] == '\0')))#ifndef INVALID_FILE_ATTRIBUTES#define INVALID_FILE_ATTRIBUTES ((DWORD) 0xFFFFFFFF)#endifstatic int check_error(int result, Efile_error* errInfo);static int set_error(Efile_error* errInfo);static int IsRootUNCName(const char* path);static int extract_root(char* name);static unsigned short dos_to_posix_mode(int attr, const char *name);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_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 intcheck_error(int result, Efile_error* errInfo){ if (result < 0) { errInfo->posix_errno = errno; errInfo->os_errno = GetLastError(); return 0; } return 1;}/* * Fills the provided error information structure with information * with the error code given by GetLastError() and its corresponding * Posix error number. * * Returns 0. */static intset_error(Efile_error* errInfo){ errInfo->posix_errno = errno_map(errInfo->os_errno = GetLastError()); return 0;}/* * 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;}intefile_mkdir(errInfo, name)Efile_error* errInfo; /* Where to return error codes. */char* name; /* Name of directory to create. */{ return check_error(mkdir(name), errInfo);}intefile_rmdir(errInfo, name)Efile_error* errInfo; /* Where to return error codes. */char* name; /* Name of directory to delete. */{ OSVERSIONINFO os; DWORD attr; if (RemoveDirectory(name) != FALSE) { return 1; } errno = errno_map(GetLastError()); if (errno == EACCES) { attr = GetFileAttributes(name); 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_DATA data; char buffer[2*MAX_PATH]; int len; len = strlen(name); strcpy(buffer, name); if (buffer[0] && buffer[len-1] != '\\' && buffer[len-1] != '/') { strcat(buffer, "\\"); } strcat(buffer, "*.*"); handle = FindFirstFile(buffer, &data); if (handle != INVALID_HANDLE_VALUE) { while (1) { if ((strcmp(data.cFileName, ".") != 0) && (strcmp(data.cFileName, "..") != 0)) { /* * Found something in this directory. */ errno = EEXIST; break; } if (FindNextFile(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: return check_error(-1, errInfo);}intefile_delete_file(errInfo, name)Efile_error* errInfo; /* Where to return error codes. */char* name; /* Name of file to delete. */{ DWORD attr; if (DeleteFile(name) != FALSE) { return 1; } errno = errno_map(GetLastError()); if (errno == EACCES) { attr = GetFileAttributes(name); 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 = GetFileAttributes(name); 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, 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. * *--------------------------------------------------------------------------- */intefile_rename(errInfo, src, dst)Efile_error* errInfo; /* Where to return error codes. */char* src; /* Original name. */char* dst; /* New name. */{ DWORD srcAttr, dstAttr; if (MoveFile(src, dst) != FALSE) { return 1; } errno = errno_map(GetLastError()); srcAttr = GetFileAttributes(src); dstAttr = GetFileAttributes(dst); if (srcAttr == (DWORD) -1) { srcAttr = 0; } if (dstAttr == (DWORD) -1) { dstAttr = 0; } if (errno == EBADF) { errno = EACCES; return check_error(-1, errInfo); } if (errno == EACCES) { decode: if (srcAttr & FILE_ATTRIBUTE_DIRECTORY) { char srcPath[MAX_PATH], dstPath[MAX_PATH]; char *srcRest, *dstRest; int size; size = GetFullPathName(src, sizeof(srcPath), srcPath, &srcRest); if ((size == 0) || (size > sizeof(srcPath))) { return check_error(-1, errInfo); } size = GetFullPathName(dst, sizeof(dstPath), dstPath, &dstRest); if ((size == 0) || (size > sizeof(dstPath))) { return check_error(-1, errInfo); } if (srcRest == NULL) { srcRest = srcPath + strlen(srcPath); } if (strnicmp(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] == '\0') { /* * The filename was invalid. (Don't know why, * but play it safe.) */ errno = EINVAL; } if (stricmp(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 (RemoveDirectory(dst)) { /* * 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 (MoveFile(src, dst) != FALSE) { return 1;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?