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
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.*/
my_open_osfhandle(HANDLE handle,int oflag)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
invalidate_fd(File fd)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 */
my_get_osfhandle(File fd)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
my_get_open_flags(File fd)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
my_win_sopen(const char * path,int oflag,int shflag,int pmode)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
my_win_open(const char * path,int flags)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
my_win_close(File fd)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
my_win_pread(File Filedes,uchar * Buffer,size_t Count,my_off_t offset)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
my_win_read(File Filedes,uchar * Buffer,size_t Count)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
my_win_pwrite(File Filedes,const uchar * Buffer,size_t Count,my_off_t offset)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
my_win_lseek(File fd,my_off_t pos,int whence)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
my_win_write(File fd,const uchar * Buffer,size_t Count)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
my_win_chsize(File fd,my_off_t newlength)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 */
my_get_stdfile_descriptor(FILE * stream)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
my_win_fileno(FILE * file)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
my_win_fopen(const char * filename,const char * type)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
my_win_fdopen(File fd,const char * type)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
my_win_fclose(FILE * file)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 */
my_win_fstat(File fd,struct _stati64 * buf)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
my_win_stat(const char * path,struct _stati64 * buf)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
my_win_fsync(File fd)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
my_win_dup(File fd)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