diff options
Diffstat (limited to '')
-rw-r--r-- | mysys/my_winfile.c | 738 |
1 files changed, 738 insertions, 0 deletions
diff --git a/mysys/my_winfile.c b/mysys/my_winfile.c new file mode 100644 index 00000000..35bc6b35 --- /dev/null +++ b/mysys/my_winfile.c @@ -0,0 +1,738 @@ +/* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. + +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; version 2 of the License. + +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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */ + +/* + The purpose of this file is to provide implementation of file IO routines on + Windows that can be thought as drop-in replacement for corresponding C runtime + functionality. + + Compared to Windows CRT, this one + - does not have the same file descriptor + limitation (default is 16384 and can be increased further, whereas CRT poses + a hard limit of 2048 file descriptors) + - the file operations are not serialized + - positional IO pread/pwrite is ported here. + - no text mode for files, all IO is "binary" + + Naming convention: + All routines are prefixed with my_win_, e.g Posix open() is implemented with + my_win_open() + + Implemented are + - POSIX routines(e.g open, read, lseek ...) + - Some ANSI C stream routines (fopen, fdopen, fileno, fclose) + - Windows CRT equvalients (my_get_osfhandle, open_osfhandle) + + Worth to note: + - File descriptors used here are located in a range that is not compatible + with CRT on purpose. Attempt to use a file descriptor from Windows CRT library + range in my_win_* function will be punished with DBUG_ASSERT() + + - File streams (FILE *) are actually from the C runtime. The routines provided + here are useful only in scernarios that use low-level IO with my_win_fileno() +*/ + +#ifdef _WIN32 + +#include "mysys_priv.h" +#include <share.h> +#include <sys/stat.h> + +/* Associates a file descriptor with an existing operating-system file handle.*/ +File my_open_osfhandle(HANDLE handle, int oflag) +{ + int offset= -1; + uint i; + DBUG_ENTER("my_open_osfhandle"); + + mysql_mutex_lock(&THR_LOCK_open); + for(i= MY_FILE_MIN; i < my_file_limit;i++) + { + if(my_file_info[i].fhandle == 0) + { + struct st_my_file_info *finfo= &(my_file_info[i]); + finfo->type= FILE_BY_OPEN; + finfo->fhandle= handle; + finfo->oflag= oflag; + offset= i; + break; + } + } + mysql_mutex_unlock(&THR_LOCK_open); + if(offset == -1) + errno= EMFILE; /* to many file handles open */ + DBUG_RETURN(offset); +} + + +static void invalidate_fd(File fd) +{ + DBUG_ENTER("invalidate_fd"); + DBUG_ASSERT(fd >= MY_FILE_MIN && fd < (int)my_file_limit); + my_file_info[fd].fhandle= 0; + DBUG_VOID_RETURN; +} + + +/* Get Windows handle for a file descriptor */ +HANDLE my_get_osfhandle(File fd) +{ + DBUG_ENTER("my_get_osfhandle"); + DBUG_ASSERT(fd >= MY_FILE_MIN && fd < (int)my_file_limit); + DBUG_RETURN(my_file_info[fd].fhandle); +} + + +static int my_get_open_flags(File fd) +{ + DBUG_ENTER("my_get_open_flags"); + DBUG_ASSERT(fd >= MY_FILE_MIN && fd < (int)my_file_limit); + DBUG_RETURN(my_file_info[fd].oflag); +} + +/* + CreateFile with retry logic. + + Uses retries, to avoid or reduce CreateFile errors + with ERROR_SHARING_VIOLATION, in case the file is opened + by another process, which used incompatible sharing + flags when opening. + + See Windows' CreateFile() documentation for details. +*/ +static HANDLE my_create_file_with_retries( + LPCSTR lpFileName, DWORD dwDesiredAccess, + DWORD dwShareMode, + LPSECURITY_ATTRIBUTES lpSecurityAttributes, + DWORD dwCreationDisposition, + DWORD dwFlagsAndAttributes, + HANDLE hTemplateFile) +{ + int retries; + DBUG_INJECT_FILE_SHARING_VIOLATION(lpFileName); + + for (retries = FILE_SHARING_VIOLATION_RETRIES;;) + { + HANDLE h= CreateFile(lpFileName, dwDesiredAccess, dwShareMode, + lpSecurityAttributes, dwCreationDisposition, + dwFlagsAndAttributes, hTemplateFile); + DBUG_CLEAR_FILE_SHARING_VIOLATION(); + + if (h != INVALID_HANDLE_VALUE || + GetLastError() != ERROR_SHARING_VIOLATION || --retries == 0) + return h; + + Sleep(FILE_SHARING_VIOLATION_DELAY_MS); + } + return INVALID_HANDLE_VALUE; +} + +/* + Default security attributes for files and directories + Usually NULL, but can be set + - by either mysqld --bootstrap when started from + mysql_install_db.exe, and creating windows service + - or by mariabackup --copy-back. + + The objective in both cases is to fix file or directory + privileges for those files that are outside of the usual + datadir, so that unprivileged service account has full + access to the files. +*/ +LPSECURITY_ATTRIBUTES my_win_file_secattr() +{ + return my_dir_security_attributes.lpSecurityDescriptor? + &my_dir_security_attributes : NULL; +} + + +/* + Open a file with sharing. Similar to _sopen() from libc, but allows managing + share delete on win32 + + SYNOPSIS + my_win_sopen() + path file name + oflag operation flags + shflag share flag + pmode permission flags + + RETURN VALUE + File descriptor of opened file if success + -1 and sets errno if fails. +*/ + +File my_win_sopen(const char *path, int oflag, int shflag, int pmode) +{ + int fh; /* handle of opened file */ + int mask; + HANDLE osfh; /* OS handle of opened file */ + DWORD fileaccess; /* OS file access (requested) */ + DWORD fileshare; /* OS file sharing mode */ + DWORD filecreate; /* OS method of opening/creating */ + DWORD fileattrib; /* OS file attribute flags */ + + DBUG_ENTER("my_win_sopen"); + + if (check_if_legal_filename(path)) + { + errno= EACCES; + DBUG_RETURN(-1); + } + + /* decode the access flags */ + switch (oflag & (_O_RDONLY | _O_WRONLY | _O_RDWR)) { + case _O_RDONLY: /* read access */ + fileaccess= GENERIC_READ; + break; + case _O_WRONLY: /* write access */ + fileaccess= GENERIC_WRITE; + break; + case _O_RDWR: /* read and write access */ + fileaccess= GENERIC_READ | GENERIC_WRITE; + break; + default: /* error, bad oflag */ + errno= EINVAL; + DBUG_RETURN(-1); + } + + /* decode sharing flags */ + switch (shflag) { + case _SH_DENYRW: /* exclusive access except delete */ + fileshare= FILE_SHARE_DELETE; + break; + case _SH_DENYWR: /* share read and delete access */ + fileshare= FILE_SHARE_READ | FILE_SHARE_DELETE; + break; + case _SH_DENYRD: /* share write and delete access */ + fileshare= FILE_SHARE_WRITE | FILE_SHARE_DELETE; + break; + case _SH_DENYNO: /* share read, write and delete access */ + fileshare= FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; + break; + case _SH_DENYRWD: /* exclusive access */ + fileshare= 0L; + break; + case _SH_DENYWRD: /* share read access */ + fileshare= FILE_SHARE_READ; + break; + case _SH_DENYRDD: /* share write access */ + fileshare= FILE_SHARE_WRITE; + break; + case _SH_DENYDEL: /* share read and write access */ + fileshare= FILE_SHARE_READ | FILE_SHARE_WRITE; + break; + default: /* error, bad shflag */ + errno= EINVAL; + DBUG_RETURN(-1); + } + + /* decode open/create method flags */ + switch (oflag & (_O_CREAT | _O_EXCL | _O_TRUNC)) { + case 0: + case _O_EXCL: /* ignore EXCL w/o CREAT */ + filecreate= OPEN_EXISTING; + break; + + case _O_CREAT: + filecreate= OPEN_ALWAYS; + break; + + case _O_CREAT | _O_EXCL: + case _O_CREAT | _O_TRUNC | _O_EXCL: + filecreate= CREATE_NEW; + break; + + case _O_TRUNC: + case _O_TRUNC | _O_EXCL: /* ignore EXCL w/o CREAT */ + filecreate= TRUNCATE_EXISTING; + break; + + case _O_CREAT | _O_TRUNC: + filecreate= CREATE_ALWAYS; + break; + + default: + /* this can't happen ... all cases are covered */ + errno= EINVAL; + DBUG_RETURN(-1); + } + + /* decode file attribute flags if _O_CREAT was specified */ + fileattrib= FILE_ATTRIBUTE_NORMAL; /* default */ + if (oflag & _O_CREAT) + { + _umask((mask= _umask(0))); + + if (!((pmode & ~mask) & _S_IWRITE)) + fileattrib= FILE_ATTRIBUTE_READONLY; + } + + /* Set temporary file (delete-on-close) attribute if requested. */ + if (oflag & _O_TEMPORARY) + { + fileattrib|= FILE_FLAG_DELETE_ON_CLOSE; + fileaccess|= DELETE; + } + + /* Set temporary file (delay-flush-to-disk) attribute if requested.*/ + if (oflag & _O_SHORT_LIVED) + fileattrib|= FILE_ATTRIBUTE_TEMPORARY; + + /* Set sequential or random access attribute if requested. */ + if (oflag & _O_SEQUENTIAL) + fileattrib|= FILE_FLAG_SEQUENTIAL_SCAN; + else if (oflag & _O_RANDOM) + fileattrib|= FILE_FLAG_RANDOM_ACCESS; + + /* try to open/create the file */ + if ((osfh= my_create_file_with_retries(path, fileaccess, fileshare,my_win_file_secattr(), + filecreate, fileattrib, NULL)) == INVALID_HANDLE_VALUE) + { + DWORD last_error= GetLastError(); + if (last_error == ERROR_PATH_NOT_FOUND && strlen(path) >= MAX_PATH) + errno= ENAMETOOLONG; + else + my_osmaperr(last_error); /* map error */ + DBUG_RETURN(-1); + } + + if ((fh= my_open_osfhandle(osfh, + oflag & (_O_APPEND | _O_RDONLY | _O_TEXT))) == -1) + { + CloseHandle(osfh); + } + + DBUG_RETURN(fh); /* return handle */ +} + + +File my_win_open(const char *path, int flags) +{ + DBUG_ENTER("my_win_open"); + DBUG_RETURN(my_win_sopen((char *) path, flags | _O_BINARY, _SH_DENYNO, + _S_IREAD | S_IWRITE)); +} + + +int my_win_close(File fd) +{ + DBUG_ENTER("my_win_close"); + if(CloseHandle(my_get_osfhandle(fd))) + { + invalidate_fd(fd); + DBUG_RETURN(0); + } + my_osmaperr(GetLastError()); + DBUG_RETURN(-1); +} + + +size_t my_win_pread(File Filedes, uchar *Buffer, size_t Count, my_off_t offset) +{ + DWORD nBytesRead; + HANDLE hFile; + OVERLAPPED ov= {0}; + LARGE_INTEGER li; + + DBUG_ENTER("my_win_pread"); + + if(!Count) + DBUG_RETURN(0); +#ifdef _WIN64 + if(Count > UINT_MAX) + Count= UINT_MAX; +#endif + + hFile= (HANDLE)my_get_osfhandle(Filedes); + li.QuadPart= offset; + ov.Offset= li.LowPart; + ov.OffsetHigh= li.HighPart; + + if(!ReadFile(hFile, Buffer, (DWORD)Count, &nBytesRead, &ov)) + { + DWORD lastError= GetLastError(); + /* + ERROR_BROKEN_PIPE is returned when no more data coming + through e.g. a command pipe in windows : see MSDN on ReadFile. + */ + if(lastError == ERROR_HANDLE_EOF || lastError == ERROR_BROKEN_PIPE) + DBUG_RETURN(0); /*return 0 at EOF*/ + my_osmaperr(lastError); + DBUG_RETURN((size_t)-1); + } + DBUG_RETURN(nBytesRead); +} + + +size_t my_win_read(File Filedes, uchar *Buffer, size_t Count) +{ + DWORD nBytesRead; + HANDLE hFile; + + DBUG_ENTER("my_win_read"); + if(!Count) + DBUG_RETURN(0); +#ifdef _WIN64 + if(Count > UINT_MAX) + Count= UINT_MAX; +#endif + + hFile= (HANDLE)my_get_osfhandle(Filedes); + + if(!ReadFile(hFile, Buffer, (DWORD)Count, &nBytesRead, NULL)) + { + DWORD lastError= GetLastError(); + /* + ERROR_BROKEN_PIPE is returned when no more data coming + through e.g. a command pipe in windows : see MSDN on ReadFile. + */ + if(lastError == ERROR_HANDLE_EOF || lastError == ERROR_BROKEN_PIPE) + DBUG_RETURN(0); /*return 0 at EOF*/ + my_osmaperr(lastError); + DBUG_RETURN((size_t)-1); + } + DBUG_RETURN(nBytesRead); +} + + +size_t my_win_pwrite(File Filedes, const uchar *Buffer, size_t Count, + my_off_t offset) +{ + DWORD nBytesWritten; + HANDLE hFile; + OVERLAPPED ov= {0}; + LARGE_INTEGER li; + + DBUG_ENTER("my_win_pwrite"); + DBUG_PRINT("my",("Filedes: %d, Buffer: %p, Count: %llu, offset: %llu", + Filedes, Buffer, (ulonglong)Count, (ulonglong)offset)); + + if(!Count) + DBUG_RETURN(0); + +#ifdef _WIN64 + if(Count > UINT_MAX) + Count= UINT_MAX; +#endif + + hFile= (HANDLE)my_get_osfhandle(Filedes); + li.QuadPart= offset; + ov.Offset= li.LowPart; + ov.OffsetHigh= li.HighPart; + + if(!WriteFile(hFile, Buffer, (DWORD)Count, &nBytesWritten, &ov)) + { + my_osmaperr(GetLastError()); + DBUG_RETURN((size_t)-1); + } + else + DBUG_RETURN(nBytesWritten); +} + + +my_off_t my_win_lseek(File fd, my_off_t pos, int whence) +{ + LARGE_INTEGER offset; + LARGE_INTEGER newpos; + + DBUG_ENTER("my_win_lseek"); + + /* Check compatibility of Windows and Posix seek constants */ + compile_time_assert(FILE_BEGIN == SEEK_SET && FILE_CURRENT == SEEK_CUR + && FILE_END == SEEK_END); + + offset.QuadPart= pos; + if(!SetFilePointerEx(my_get_osfhandle(fd), offset, &newpos, whence)) + { + my_osmaperr(GetLastError()); + newpos.QuadPart= -1; + } + DBUG_RETURN(newpos.QuadPart); +} + + +#ifndef FILE_WRITE_TO_END_OF_FILE +#define FILE_WRITE_TO_END_OF_FILE 0xffffffff +#endif +size_t my_win_write(File fd, const uchar *Buffer, size_t Count) +{ + DWORD nWritten; + OVERLAPPED ov; + OVERLAPPED *pov= NULL; + HANDLE hFile; + + DBUG_ENTER("my_win_write"); + DBUG_PRINT("my",("Filedes: %d, Buffer: %p, Count %llu", fd, Buffer, + (ulonglong)Count)); + + if(!Count) + DBUG_RETURN(0); + +#ifdef _WIN64 + if(Count > UINT_MAX) + Count= UINT_MAX; +#endif + + if(my_get_open_flags(fd) & _O_APPEND) + { + /* + Atomic append to the end of file is is done by special initialization of + the OVERLAPPED structure. See MSDN WriteFile documentation for more info. + */ + memset(&ov, 0, sizeof(ov)); + ov.Offset= FILE_WRITE_TO_END_OF_FILE; + ov.OffsetHigh= -1; + pov= &ov; + } + + hFile= my_get_osfhandle(fd); + if(!WriteFile(hFile, Buffer, (DWORD)Count, &nWritten, pov)) + { + my_osmaperr(GetLastError()); + DBUG_RETURN((size_t)-1); + } + DBUG_RETURN(nWritten); +} + + +int my_win_chsize(File fd, my_off_t newlength) +{ + HANDLE hFile; + LARGE_INTEGER length; + DBUG_ENTER("my_win_chsize"); + + hFile= (HANDLE) my_get_osfhandle(fd); + length.QuadPart= newlength; + if (!SetFilePointerEx(hFile, length , NULL , FILE_BEGIN)) + goto err; + if (!SetEndOfFile(hFile)) + goto err; + DBUG_RETURN(0); +err: + my_osmaperr(GetLastError()); + my_errno= errno; + DBUG_RETURN(-1); +} + + +/* Get the file descriptor for stdin,stdout or stderr */ +static File my_get_stdfile_descriptor(FILE *stream) +{ + HANDLE hFile; + DWORD nStdHandle; + DBUG_ENTER("my_get_stdfile_descriptor"); + + if(stream == stdin) + nStdHandle= STD_INPUT_HANDLE; + else if(stream == stdout) + nStdHandle= STD_OUTPUT_HANDLE; + else if(stream == stderr) + nStdHandle= STD_ERROR_HANDLE; + else + DBUG_RETURN(-1); + + hFile= GetStdHandle(nStdHandle); + if(hFile != INVALID_HANDLE_VALUE) + DBUG_RETURN(my_open_osfhandle(hFile, 0)); + DBUG_RETURN(-1); +} + + +File my_win_handle2File(HANDLE hFile) +{ + int retval= -1; + uint i; + + DBUG_ENTER("my_win_handle2File"); + + for(i= MY_FILE_MIN; i < my_file_limit; i++) + { + if(my_file_info[i].fhandle == hFile) + { + retval= i; + break; + } + } + DBUG_RETURN(retval); +} + + +File my_win_fileno(FILE *file) +{ + DBUG_ENTER("my_win_fileno"); + int retval= my_win_handle2File((HANDLE) _get_osfhandle(fileno(file))); + if(retval == -1) + /* try std stream */ + DBUG_RETURN(my_get_stdfile_descriptor(file)); + DBUG_RETURN(retval); +} + + +FILE *my_win_fopen(const char *filename, const char *type) +{ + FILE *file; + int flags= 0; + DBUG_ENTER("my_win_fopen"); + + /* + If we are not creating, then we need to use my_access to make sure + the file exists since Windows doesn't handle files like "com1.sym" + very well + */ + if (check_if_legal_filename(filename)) + { + errno= EACCES; + DBUG_RETURN(NULL); + } + + file= fopen(filename, type); + if(!file) + DBUG_RETURN(NULL); + + if(strchr(type,'a') != NULL) + flags= O_APPEND; + + /* + Register file handle in my_table_info. + Necessary for my_fileno() + */ + if(my_open_osfhandle((HANDLE)_get_osfhandle(fileno(file)), flags) < 0) + { + fclose(file); + DBUG_RETURN(NULL); + } + DBUG_RETURN(file); +} + + +FILE * my_win_fdopen(File fd, const char *type) +{ + FILE *file; + int crt_fd; + int flags= 0; + + DBUG_ENTER("my_win_fdopen"); + + if(strchr(type,'a') != NULL) + flags= O_APPEND; + /* Convert OS file handle to CRT file descriptor and then call fdopen*/ + crt_fd= _open_osfhandle((intptr_t)my_get_osfhandle(fd), flags); + if(crt_fd < 0) + file= NULL; + else + file= fdopen(crt_fd, type); + DBUG_RETURN(file); +} + + +int my_win_fclose(FILE *file) +{ + File fd; + + DBUG_ENTER("my_win_fclose"); + fd= my_fileno(file); + if(fd < 0) + DBUG_RETURN(-1); + if(fclose(file) < 0) + DBUG_RETURN(-1); + invalidate_fd(fd); + DBUG_RETURN(0); +} + + + +/* + Quick and dirty my_fstat() implementation for Windows. + Use CRT fstat on temporarily allocated file descriptor. + Patch file size, because size that fstat returns is not + reliable (may be outdated) +*/ +int my_win_fstat(File fd, struct _stati64 *buf) +{ + int crt_fd; + int retval; + HANDLE hFile, hDup; + + DBUG_ENTER("my_win_fstat"); + + hFile= my_get_osfhandle(fd); + if(!DuplicateHandle( GetCurrentProcess(), hFile, GetCurrentProcess(), + &hDup ,0,FALSE,DUPLICATE_SAME_ACCESS)) + { + my_osmaperr(GetLastError()); + DBUG_RETURN(-1); + } + if ((crt_fd= _open_osfhandle((intptr_t)hDup,0)) < 0) + DBUG_RETURN(-1); + + retval= _fstati64(crt_fd, buf); + if(retval == 0) + { + /* File size returned by stat is not accurate (may be outdated), fix it*/ + GetFileSizeEx(hDup, (PLARGE_INTEGER) (&(buf->st_size))); + } + _close(crt_fd); + DBUG_RETURN(retval); +} + + + +int my_win_stat( const char *path, struct _stati64 *buf) +{ + DBUG_ENTER("my_win_stat"); + if(_stati64( path, buf) == 0) + { + /* File size returned by stat is not accurate (may be outdated), fix it*/ + WIN32_FILE_ATTRIBUTE_DATA data; + if (GetFileAttributesEx(path, GetFileExInfoStandard, &data)) + { + LARGE_INTEGER li; + li.LowPart= data.nFileSizeLow; + li.HighPart= data.nFileSizeHigh; + buf->st_size= li.QuadPart; + } + DBUG_RETURN(0); + } + DBUG_RETURN(-1); +} + + + +int my_win_fsync(File fd) +{ + DBUG_ENTER("my_win_fsync"); + if(FlushFileBuffers(my_get_osfhandle(fd))) + DBUG_RETURN(0); + my_osmaperr(GetLastError()); + DBUG_RETURN(-1); +} + + + +int my_win_dup(File fd) +{ + HANDLE hDup; + DBUG_ENTER("my_win_dup"); + if (DuplicateHandle(GetCurrentProcess(), my_get_osfhandle(fd), + GetCurrentProcess(), &hDup, 0, FALSE, DUPLICATE_SAME_ACCESS)) + { + DBUG_RETURN(my_open_osfhandle(hDup, my_get_open_flags(fd))); + } + my_osmaperr(GetLastError()); + DBUG_RETURN(-1); +} + +#endif /*_WIN32*/ |