1 /* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. 2 3 This program is free software; you can redistribute it and/or modify 4 it under the terms of the GNU General Public License as published by 5 the Free Software Foundation; version 2 of the License. 6 7 This program is distributed in the hope that it will be useful, 8 but WITHOUT ANY WARRANTY; without even the implied warranty of 9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 GNU General Public License for more details. 11 12 You should have received a copy of the GNU General Public License 13 along with this program; if not, write to the Free Software 14 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */ 15 16 /* 17 The purpose of this file is to provide implementation of file IO routines on 18 Windows that can be thought as drop-in replacement for corresponding C runtime 19 functionality. 20 21 Compared to Windows CRT, this one 22 - does not have the same file descriptor 23 limitation (default is 16384 and can be increased further, whereas CRT poses 24 a hard limit of 2048 file descriptors) 25 - the file operations are not serialized 26 - positional IO pread/pwrite is ported here. 27 - no text mode for files, all IO is "binary" 28 29 Naming convention: 30 All routines are prefixed with my_win_, e.g Posix open() is implemented with 31 my_win_open() 32 33 Implemented are 34 - POSIX routines(e.g open, read, lseek ...) 35 - Some ANSI C stream routines (fopen, fdopen, fileno, fclose) 36 - Windows CRT equvalients (my_get_osfhandle, open_osfhandle) 37 get_date(register char * to,int flag,time_t date)38 Worth to note: 39 - File descriptors used here are located in a range that is not compatible 40 with CRT on purpose. Attempt to use a file descriptor from Windows CRT library 41 range in my_win_* function will be punished with DBUG_ASSERT() 42 43 - File streams (FILE *) are actually from the C runtime. The routines provided 44 here are useful only in scernarios that use low-level IO with my_win_fileno() 45 */ 46 47 #ifdef _WIN32 48 49 #include "mysys_priv.h" 50 #include <share.h> 51 #include <sys/stat.h> 52 53 /* Associates a file descriptor with an existing operating-system file handle.*/ 54 File my_open_osfhandle(HANDLE handle, int oflag) 55 { 56 int offset= -1; 57 uint i; 58 DBUG_ENTER("my_open_osfhandle"); 59 60 mysql_mutex_lock(&THR_LOCK_open); 61 for(i= MY_FILE_MIN; i < my_file_limit;i++) 62 { 63 if(my_file_info[i].fhandle == 0) 64 { 65 struct st_my_file_info *finfo= &(my_file_info[i]); 66 finfo->type= FILE_BY_OPEN; 67 finfo->fhandle= handle; 68 finfo->oflag= oflag; 69 offset= i; 70 break; 71 } 72 } 73 mysql_mutex_unlock(&THR_LOCK_open); 74 if(offset == -1) 75 errno= EMFILE; /* to many file handles open */ 76 DBUG_RETURN(offset); 77 } 78 79 80 static void invalidate_fd(File fd) 81 { 82 DBUG_ENTER("invalidate_fd"); 83 DBUG_ASSERT(fd >= MY_FILE_MIN && fd < (int)my_file_limit); 84 my_file_info[fd].fhandle= 0; 85 DBUG_VOID_RETURN; 86 } 87 88 89 /* Get Windows handle for a file descriptor */ 90 HANDLE my_get_osfhandle(File fd) 91 { 92 DBUG_ENTER("my_get_osfhandle"); 93 DBUG_ASSERT(fd >= MY_FILE_MIN && fd < (int)my_file_limit); 94 DBUG_RETURN(my_file_info[fd].fhandle); 95 } 96 97 98 static int my_get_open_flags(File fd) 99 { 100 DBUG_ENTER("my_get_open_flags"); 101 DBUG_ASSERT(fd >= MY_FILE_MIN && fd < (int)my_file_limit); 102 DBUG_RETURN(my_file_info[fd].oflag); 103 } 104 105 106 /* 107 Open a file with sharing. Similar to _sopen() from libc, but allows managing 108 share delete on win32 109 110 SYNOPSIS 111 my_win_sopen() 112 path file name 113 oflag operation flags 114 shflag share flag 115 pmode permission flags 116 117 RETURN VALUE 118 File descriptor of opened file if success 119 -1 and sets errno if fails. 120 */ 121 122 File my_win_sopen(const char *path, int oflag, int shflag, int pmode) 123 { 124 int fh; /* handle of opened file */ 125 int mask; 126 HANDLE osfh; /* OS handle of opened file */ 127 DWORD fileaccess; /* OS file access (requested) */ 128 DWORD fileshare; /* OS file sharing mode */ 129 DWORD filecreate; /* OS method of opening/creating */ 130 DWORD fileattrib; /* OS file attribute flags */ 131 SECURITY_ATTRIBUTES SecurityAttributes; 132 133 DBUG_ENTER("my_win_sopen"); 134 135 if (check_if_legal_filename(path)) 136 { 137 errno= EACCES; 138 DBUG_RETURN(-1); 139 } 140 SecurityAttributes.nLength= sizeof(SecurityAttributes); 141 SecurityAttributes.lpSecurityDescriptor= NULL; 142 SecurityAttributes.bInheritHandle= !(oflag & _O_NOINHERIT); 143 144 /* decode the access flags */ 145 switch (oflag & (_O_RDONLY | _O_WRONLY | _O_RDWR)) { 146 case _O_RDONLY: /* read access */ 147 fileaccess= GENERIC_READ; 148 break; 149 case _O_WRONLY: /* write access */ 150 fileaccess= GENERIC_WRITE; 151 break; 152 case _O_RDWR: /* read and write access */ 153 fileaccess= GENERIC_READ | GENERIC_WRITE; 154 break; 155 default: /* error, bad oflag */ 156 errno= EINVAL; 157 DBUG_RETURN(-1); 158 } 159 160 /* decode sharing flags */ 161 switch (shflag) { 162 case _SH_DENYRW: /* exclusive access except delete */ 163 fileshare= FILE_SHARE_DELETE; 164 break; 165 case _SH_DENYWR: /* share read and delete access */ 166 fileshare= FILE_SHARE_READ | FILE_SHARE_DELETE; 167 break; 168 case _SH_DENYRD: /* share write and delete access */ 169 fileshare= FILE_SHARE_WRITE | FILE_SHARE_DELETE; 170 break; 171 case _SH_DENYNO: /* share read, write and delete access */ 172 fileshare= FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; 173 break; 174 case _SH_DENYRWD: /* exclusive access */ 175 fileshare= 0L; 176 break; 177 case _SH_DENYWRD: /* share read access */ 178 fileshare= FILE_SHARE_READ; 179 break; 180 case _SH_DENYRDD: /* share write access */ 181 fileshare= FILE_SHARE_WRITE; 182 break; 183 case _SH_DENYDEL: /* share read and write access */ 184 fileshare= FILE_SHARE_READ | FILE_SHARE_WRITE; 185 break; 186 default: /* error, bad shflag */ 187 errno= EINVAL; 188 DBUG_RETURN(-1); 189 } 190 191 /* decode open/create method flags */ 192 switch (oflag & (_O_CREAT | _O_EXCL | _O_TRUNC)) { 193 case 0: 194 case _O_EXCL: /* ignore EXCL w/o CREAT */ 195 filecreate= OPEN_EXISTING; 196 break; 197 198 case _O_CREAT: 199 filecreate= OPEN_ALWAYS; 200 break; 201 202 case _O_CREAT | _O_EXCL: 203 case _O_CREAT | _O_TRUNC | _O_EXCL: 204 filecreate= CREATE_NEW; 205 break; 206 207 case _O_TRUNC: 208 case _O_TRUNC | _O_EXCL: /* ignore EXCL w/o CREAT */ 209 filecreate= TRUNCATE_EXISTING; 210 break; 211 212 case _O_CREAT | _O_TRUNC: 213 filecreate= CREATE_ALWAYS; 214 break; 215 216 default: 217 /* this can't happen ... all cases are covered */ 218 errno= EINVAL; 219 DBUG_RETURN(-1); 220 } 221 222 /* decode file attribute flags if _O_CREAT was specified */ 223 fileattrib= FILE_ATTRIBUTE_NORMAL; /* default */ 224 if (oflag & _O_CREAT) 225 { 226 _umask((mask= _umask(0))); 227 228 if (!((pmode & ~mask) & _S_IWRITE)) 229 fileattrib= FILE_ATTRIBUTE_READONLY; 230 } 231 232 /* Set temporary file (delete-on-close) attribute if requested. */ 233 if (oflag & _O_TEMPORARY) 234 { 235 fileattrib|= FILE_FLAG_DELETE_ON_CLOSE; 236 fileaccess|= DELETE; 237 } 238 239 /* Set temporary file (delay-flush-to-disk) attribute if requested.*/ 240 if (oflag & _O_SHORT_LIVED) 241 fileattrib|= FILE_ATTRIBUTE_TEMPORARY; 242 243 /* Set sequential or random access attribute if requested. */ 244 if (oflag & _O_SEQUENTIAL) 245 fileattrib|= FILE_FLAG_SEQUENTIAL_SCAN; 246 else if (oflag & _O_RANDOM) 247 fileattrib|= FILE_FLAG_RANDOM_ACCESS; 248 249 /* try to open/create the file */ 250 if ((osfh= CreateFile(path, fileaccess, fileshare, &SecurityAttributes, 251 filecreate, fileattrib, NULL)) == INVALID_HANDLE_VALUE) 252 { 253 /* 254 OS call to open/create file failed! map the error, release 255 the lock, and return -1. note that it's not necessary to 256 call _free_osfhnd (it hasn't been used yet). 257 */ 258 my_osmaperr(GetLastError()); /* map error */ 259 DBUG_RETURN(-1); /* return error to caller */ 260 } 261 262 if ((fh= my_open_osfhandle(osfh, 263 oflag & (_O_APPEND | _O_RDONLY | _O_TEXT))) == -1) 264 { 265 CloseHandle(osfh); 266 } 267 268 DBUG_RETURN(fh); /* return handle */ 269 } 270 271 272 File my_win_open(const char *path, int flags) 273 { 274 DBUG_ENTER("my_win_open"); 275 DBUG_RETURN(my_win_sopen((char *) path, flags | _O_BINARY, _SH_DENYNO, 276 _S_IREAD | S_IWRITE)); 277 } 278 279 280 int my_win_close(File fd) 281 { 282 DBUG_ENTER("my_win_close"); 283 if(CloseHandle(my_get_osfhandle(fd))) 284 { 285 invalidate_fd(fd); 286 DBUG_RETURN(0); 287 } 288 my_osmaperr(GetLastError()); 289 DBUG_RETURN(-1); 290 } 291 292 293 size_t my_win_pread(File Filedes, uchar *Buffer, size_t Count, my_off_t offset) 294 { 295 DWORD nBytesRead; 296 HANDLE hFile; 297 OVERLAPPED ov= {0}; 298 LARGE_INTEGER li; 299 300 DBUG_ENTER("my_win_pread"); 301 302 if(!Count) 303 DBUG_RETURN(0); 304 #ifdef _WIN64 305 if(Count > UINT_MAX) 306 Count= UINT_MAX; 307 #endif 308 309 hFile= (HANDLE)my_get_osfhandle(Filedes); 310 li.QuadPart= offset; 311 ov.Offset= li.LowPart; 312 ov.OffsetHigh= li.HighPart; 313 314 if(!ReadFile(hFile, Buffer, (DWORD)Count, &nBytesRead, &ov)) 315 { 316 DWORD lastError= GetLastError(); 317 /* 318 ERROR_BROKEN_PIPE is returned when no more data coming 319 through e.g. a command pipe in windows : see MSDN on ReadFile. 320 */ 321 if(lastError == ERROR_HANDLE_EOF || lastError == ERROR_BROKEN_PIPE) 322 DBUG_RETURN(0); /*return 0 at EOF*/ 323 my_osmaperr(lastError); 324 DBUG_RETURN((size_t)-1); 325 } 326 DBUG_RETURN(nBytesRead); 327 } 328 329 330 size_t my_win_read(File Filedes, uchar *Buffer, size_t Count) 331 { 332 DWORD nBytesRead; 333 HANDLE hFile; 334 335 DBUG_ENTER("my_win_read"); 336 if(!Count) 337 DBUG_RETURN(0); 338 #ifdef _WIN64 339 if(Count > UINT_MAX) 340 Count= UINT_MAX; 341 #endif 342 343 hFile= (HANDLE)my_get_osfhandle(Filedes); 344 345 if(!ReadFile(hFile, Buffer, (DWORD)Count, &nBytesRead, NULL)) 346 { 347 DWORD lastError= GetLastError(); 348 /* 349 ERROR_BROKEN_PIPE is returned when no more data coming 350 through e.g. a command pipe in windows : see MSDN on ReadFile. 351 */ 352 if(lastError == ERROR_HANDLE_EOF || lastError == ERROR_BROKEN_PIPE) 353 DBUG_RETURN(0); /*return 0 at EOF*/ 354 my_osmaperr(lastError); 355 DBUG_RETURN((size_t)-1); 356 } 357 DBUG_RETURN(nBytesRead); 358 } 359 360 361 size_t my_win_pwrite(File Filedes, const uchar *Buffer, size_t Count, 362 my_off_t offset) 363 { 364 DWORD nBytesWritten; 365 HANDLE hFile; 366 OVERLAPPED ov= {0}; 367 LARGE_INTEGER li; 368 369 DBUG_ENTER("my_win_pwrite"); 370 DBUG_PRINT("my",("Filedes: %d, Buffer: %p, Count: %llu, offset: %llu", 371 Filedes, Buffer, (ulonglong)Count, (ulonglong)offset)); 372 373 if(!Count) 374 DBUG_RETURN(0); 375 376 #ifdef _WIN64 377 if(Count > UINT_MAX) 378 Count= UINT_MAX; 379 #endif 380 381 hFile= (HANDLE)my_get_osfhandle(Filedes); 382 li.QuadPart= offset; 383 ov.Offset= li.LowPart; 384 ov.OffsetHigh= li.HighPart; 385 386 if(!WriteFile(hFile, Buffer, (DWORD)Count, &nBytesWritten, &ov)) 387 { 388 my_osmaperr(GetLastError()); 389 DBUG_RETURN((size_t)-1); 390 } 391 else 392 DBUG_RETURN(nBytesWritten); 393 } 394 395 396 my_off_t my_win_lseek(File fd, my_off_t pos, int whence) 397 { 398 LARGE_INTEGER offset; 399 LARGE_INTEGER newpos; 400 401 DBUG_ENTER("my_win_lseek"); 402 403 /* Check compatibility of Windows and Posix seek constants */ 404 compile_time_assert(FILE_BEGIN == SEEK_SET && FILE_CURRENT == SEEK_CUR 405 && FILE_END == SEEK_END); 406 407 offset.QuadPart= pos; 408 if(!SetFilePointerEx(my_get_osfhandle(fd), offset, &newpos, whence)) 409 { 410 my_osmaperr(GetLastError()); 411 newpos.QuadPart= -1; 412 } 413 DBUG_RETURN(newpos.QuadPart); 414 } 415 416 417 #ifndef FILE_WRITE_TO_END_OF_FILE 418 #define FILE_WRITE_TO_END_OF_FILE 0xffffffff 419 #endif 420 size_t my_win_write(File fd, const uchar *Buffer, size_t Count) 421 { 422 DWORD nWritten; 423 OVERLAPPED ov; 424 OVERLAPPED *pov= NULL; 425 HANDLE hFile; 426 427 DBUG_ENTER("my_win_write"); 428 DBUG_PRINT("my",("Filedes: %d, Buffer: %p, Count %llu", fd, Buffer, 429 (ulonglong)Count)); 430 431 if(!Count) 432 DBUG_RETURN(0); 433 434 #ifdef _WIN64 435 if(Count > UINT_MAX) 436 Count= UINT_MAX; 437 #endif 438 439 if(my_get_open_flags(fd) & _O_APPEND) 440 { 441 /* 442 Atomic append to the end of file is is done by special initialization of 443 the OVERLAPPED structure. See MSDN WriteFile documentation for more info. 444 */ 445 memset(&ov, 0, sizeof(ov)); 446 ov.Offset= FILE_WRITE_TO_END_OF_FILE; 447 ov.OffsetHigh= -1; 448 pov= &ov; 449 } 450 451 hFile= my_get_osfhandle(fd); 452 if(!WriteFile(hFile, Buffer, (DWORD)Count, &nWritten, pov)) 453 { 454 my_osmaperr(GetLastError()); 455 DBUG_RETURN((size_t)-1); 456 } 457 DBUG_RETURN(nWritten); 458 } 459 460 461 int my_win_chsize(File fd, my_off_t newlength) 462 { 463 HANDLE hFile; 464 LARGE_INTEGER length; 465 DBUG_ENTER("my_win_chsize"); 466 467 hFile= (HANDLE) my_get_osfhandle(fd); 468 length.QuadPart= newlength; 469 if (!SetFilePointerEx(hFile, length , NULL , FILE_BEGIN)) 470 goto err; 471 if (!SetEndOfFile(hFile)) 472 goto err; 473 DBUG_RETURN(0); 474 err: 475 my_osmaperr(GetLastError()); 476 my_errno= errno; 477 DBUG_RETURN(-1); 478 } 479 480 481 /* Get the file descriptor for stdin,stdout or stderr */ 482 static File my_get_stdfile_descriptor(FILE *stream) 483 { 484 HANDLE hFile; 485 DWORD nStdHandle; 486 DBUG_ENTER("my_get_stdfile_descriptor"); 487 488 if(stream == stdin) 489 nStdHandle= STD_INPUT_HANDLE; 490 else if(stream == stdout) 491 nStdHandle= STD_OUTPUT_HANDLE; 492 else if(stream == stderr) 493 nStdHandle= STD_ERROR_HANDLE; 494 else 495 DBUG_RETURN(-1); 496 497 hFile= GetStdHandle(nStdHandle); 498 if(hFile != INVALID_HANDLE_VALUE) 499 DBUG_RETURN(my_open_osfhandle(hFile, 0)); 500 DBUG_RETURN(-1); 501 } 502 503 504 File my_win_fileno(FILE *file) 505 { 506 HANDLE hFile= (HANDLE)_get_osfhandle(fileno(file)); 507 int retval= -1; 508 uint i; 509 510 DBUG_ENTER("my_win_fileno"); 511 512 for(i= MY_FILE_MIN; i < my_file_limit; i++) 513 { 514 if(my_file_info[i].fhandle == hFile) 515 { 516 retval= i; 517 break; 518 } 519 } 520 if(retval == -1) 521 /* try std stream */ 522 DBUG_RETURN(my_get_stdfile_descriptor(file)); 523 DBUG_RETURN(retval); 524 } 525 526 527 FILE *my_win_fopen(const char *filename, const char *type) 528 { 529 FILE *file; 530 int flags= 0; 531 DBUG_ENTER("my_win_fopen"); 532 533 /* 534 If we are not creating, then we need to use my_access to make sure 535 the file exists since Windows doesn't handle files like "com1.sym" 536 very well 537 */ 538 if (check_if_legal_filename(filename)) 539 { 540 errno= EACCES; 541 DBUG_RETURN(NULL); 542 } 543 544 file= fopen(filename, type); 545 if(!file) 546 DBUG_RETURN(NULL); 547 548 if(strchr(type,'a') != NULL) 549 flags= O_APPEND; 550 551 /* 552 Register file handle in my_table_info. 553 Necessary for my_fileno() 554 */ 555 if(my_open_osfhandle((HANDLE)_get_osfhandle(fileno(file)), flags) < 0) 556 { 557 fclose(file); 558 DBUG_RETURN(NULL); 559 } 560 DBUG_RETURN(file); 561 } 562 563 564 FILE * my_win_fdopen(File fd, const char *type) 565 { 566 FILE *file; 567 int crt_fd; 568 int flags= 0; 569 570 DBUG_ENTER("my_win_fdopen"); 571 572 if(strchr(type,'a') != NULL) 573 flags= O_APPEND; 574 /* Convert OS file handle to CRT file descriptor and then call fdopen*/ 575 crt_fd= _open_osfhandle((intptr_t)my_get_osfhandle(fd), flags); 576 if(crt_fd < 0) 577 file= NULL; 578 else 579 file= fdopen(crt_fd, type); 580 DBUG_RETURN(file); 581 } 582 583 584 int my_win_fclose(FILE *file) 585 { 586 File fd; 587 588 DBUG_ENTER("my_win_fclose"); 589 fd= my_fileno(file); 590 if(fd < 0) 591 DBUG_RETURN(-1); 592 if(fclose(file) < 0) 593 DBUG_RETURN(-1); 594 invalidate_fd(fd); 595 DBUG_RETURN(0); 596 } 597 598 599 600 /* 601 Quick and dirty my_fstat() implementation for Windows. 602 Use CRT fstat on temporarily allocated file descriptor. 603 Patch file size, because size that fstat returns is not 604 reliable (may be outdated) 605 */ 606 int my_win_fstat(File fd, struct _stati64 *buf) 607 { 608 int crt_fd; 609 int retval; 610 HANDLE hFile, hDup; 611 612 DBUG_ENTER("my_win_fstat"); 613 614 hFile= my_get_osfhandle(fd); 615 if(!DuplicateHandle( GetCurrentProcess(), hFile, GetCurrentProcess(), 616 &hDup ,0,FALSE,DUPLICATE_SAME_ACCESS)) 617 { 618 my_osmaperr(GetLastError()); 619 DBUG_RETURN(-1); 620 } 621 if ((crt_fd= _open_osfhandle((intptr_t)hDup,0)) < 0) 622 DBUG_RETURN(-1); 623 624 retval= _fstati64(crt_fd, buf); 625 if(retval == 0) 626 { 627 /* File size returned by stat is not accurate (may be outdated), fix it*/ 628 GetFileSizeEx(hDup, (PLARGE_INTEGER) (&(buf->st_size))); 629 } 630 _close(crt_fd); 631 DBUG_RETURN(retval); 632 } 633 634 635 636 int my_win_stat( const char *path, struct _stati64 *buf) 637 { 638 DBUG_ENTER("my_win_stat"); 639 if(_stati64( path, buf) == 0) 640 { 641 /* File size returned by stat is not accurate (may be outdated), fix it*/ 642 WIN32_FILE_ATTRIBUTE_DATA data; 643 if (GetFileAttributesEx(path, GetFileExInfoStandard, &data)) 644 { 645 LARGE_INTEGER li; 646 li.LowPart= data.nFileSizeLow; 647 li.HighPart= data.nFileSizeHigh; 648 buf->st_size= li.QuadPart; 649 } 650 DBUG_RETURN(0); 651 } 652 DBUG_RETURN(-1); 653 } 654 655 656 657 int my_win_fsync(File fd) 658 { 659 DBUG_ENTER("my_win_fsync"); 660 if(FlushFileBuffers(my_get_osfhandle(fd))) 661 DBUG_RETURN(0); 662 my_osmaperr(GetLastError()); 663 DBUG_RETURN(-1); 664 } 665 666 667 668 int my_win_dup(File fd) 669 { 670 HANDLE hDup; 671 DBUG_ENTER("my_win_dup"); 672 if (DuplicateHandle(GetCurrentProcess(), my_get_osfhandle(fd), 673 GetCurrentProcess(), &hDup, 0, FALSE, DUPLICATE_SAME_ACCESS)) 674 { 675 DBUG_RETURN(my_open_osfhandle(hDup, my_get_open_flags(fd))); 676 } 677 my_osmaperr(GetLastError()); 678 DBUG_RETURN(-1); 679 } 680 681 #endif /*_WIN32*/ 682