MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
my_winfile.c
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-1301 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.*/
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_open");
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_close");
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*/