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 + -
显示快捷键?