1 /*
2    Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved.
3 
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License, version 2.0,
6    as published by the Free Software Foundation.
7 
8    This program is also distributed with certain software (including
9    but not limited to OpenSSL) that is licensed under separate terms,
10    as designated in a particular file or component or in included license
11    documentation.  The authors of MySQL hereby grant you an additional
12    permission to link the program and your derivative works with the
13    separately licensed software that they have included with MySQL.
14 
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License, version 2.0, for more details.
19 
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software
22    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
23 */
24 
25 #include <ndb_global.h>
26 
27 #include "Win32AsyncFile.hpp"
28 
29 #include <signaldata/FsRef.hpp>
30 #include <signaldata/FsOpenReq.hpp>
31 #include <signaldata/FsReadWriteReq.hpp>
32 
33 #define JAM_FILE_ID 399
34 
35 
Win32AsyncFile(SimulatedBlock & fs)36 Win32AsyncFile::Win32AsyncFile(SimulatedBlock& fs) :
37   AsyncFile(fs),hFile(INVALID_HANDLE_VALUE)
38 {
39 }
40 
~Win32AsyncFile()41 Win32AsyncFile::~Win32AsyncFile()
42 {
43 
44 }
45 
46 int
init()47 Win32AsyncFile::init()
48 {
49   return 0;
50 }
51 
openReq(Request * request)52 void Win32AsyncFile::openReq(Request* request)
53 {
54   m_auto_sync_freq = 0;
55   m_write_wo_sync = 0;
56   m_open_flags = request->par.open.flags;
57 
58   // for open.flags, see signal FSOPENREQ
59   DWORD dwCreationDisposition;
60   DWORD dwDesiredAccess = 0;
61   DWORD dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
62   /**
63    * FIXME:
64    * Previously we had FILE_FLAG_NO_BUFFERING also set here.
65    * This has similar alignment rules to O_DIRECT on 2.4 kernels.
66    * which means we should obey the directio req as we can't do it
67    * everywhere (this seemingly "worked" in 5.0 though), e.g. by default
68    * LCP isn't aligned IO.
69    */
70   DWORD dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
71   Uint32 flags = request->par.open.flags;
72 
73     // Convert file open flags from Solaris to Windows
74   if ((flags & FsOpenReq::OM_CREATE) && (flags & FsOpenReq::OM_TRUNCATE)){
75     dwCreationDisposition = CREATE_ALWAYS;
76   } else if (flags & FsOpenReq::OM_TRUNCATE){
77     dwCreationDisposition = TRUNCATE_EXISTING;
78   } else if (flags & (FsOpenReq::OM_CREATE|FsOpenReq::OM_CREATE_IF_NONE)){
79     dwCreationDisposition = CREATE_NEW;
80   } else {
81     dwCreationDisposition = OPEN_EXISTING;
82   }
83 
84   m_always_sync = false;
85   if (flags & FsOpenReq::OM_SYNC)
86   {
87     m_always_sync = true;
88   }
89 
90   switch(flags & 3){
91   case FsOpenReq::OM_READONLY:
92     dwDesiredAccess = GENERIC_READ;
93     break;
94   case FsOpenReq::OM_WRITEONLY:
95     dwDesiredAccess = GENERIC_WRITE;
96     break;
97   case FsOpenReq::OM_READWRITE:
98     dwDesiredAccess = GENERIC_READ | GENERIC_WRITE;
99     break;
100   default:
101     request->error = 1000;
102     break;
103     return;
104   }
105 
106   hFile = CreateFile(theFileName.c_str(), dwDesiredAccess, dwShareMode,
107                      0, dwCreationDisposition, dwFlagsAndAttributes, 0);
108 
109   if(INVALID_HANDLE_VALUE == hFile) {
110     request->error = GetLastError();
111 
112     if((ERROR_FILE_EXISTS == request->error) && (flags & (FsOpenReq::OM_CREATE|FsOpenReq::OM_CREATE_IF_NONE))) {
113       request->error = FsRef::fsErrFileExists;
114       return;
115     }
116 
117     if(((ERROR_PATH_NOT_FOUND == request->error) || (ERROR_INVALID_NAME == request->error))
118 		&& (flags & (FsOpenReq::OM_CREATE|FsOpenReq::OM_CREATE_IF_NONE))) {
119       createDirectories();
120       hFile = CreateFile(theFileName.c_str(), dwDesiredAccess, dwShareMode,
121                          0, dwCreationDisposition, dwFlagsAndAttributes, 0);
122 
123       if(INVALID_HANDLE_VALUE == hFile)
124         request->error = GetLastError();
125       else
126         request->error = 0;
127     }
128   }
129   else {
130     request->error = 0;
131   }
132 
133   if (flags & FsOpenReq::OM_INIT)
134   {
135     LARGE_INTEGER off;
136     off.QuadPart= 0;
137     LARGE_INTEGER sz;
138     sz.QuadPart= request->par.open.file_size;
139     char buf[4096];
140     bzero(buf,sizeof(buf));
141     while(off.QuadPart < sz.QuadPart)
142     {
143       BOOL r= SetFilePointerEx(hFile, off, NULL, FILE_BEGIN);
144       if(r==0)
145       {
146         request->error= GetLastError();
147         return;
148       }
149       DWORD dwWritten;
150       BOOL bWrite= WriteFile(hFile, buf, sizeof(buf), &dwWritten, 0);
151       if(!bWrite || dwWritten!=sizeof(buf))
152       {
153         request->error= GetLastError();
154       }
155       off.QuadPart+=sizeof(buf);
156     }
157     off.QuadPart= 0;
158     BOOL r= SetFilePointerEx(hFile, off, NULL, FILE_BEGIN);
159     if(r==0)
160     {
161       request->error= GetLastError();
162       return;
163     }
164 
165     /* Write initial data */
166     SignalT<25> tmp;
167     Signal * signal = (Signal*)(&tmp);
168     bzero(signal, sizeof(tmp));
169     FsReadWriteReq* req = (FsReadWriteReq*)signal->getDataPtrSend();
170     Uint32 index = 0;
171     Uint32 block = refToMain(request->theUserReference);
172     Uint32 instance = refToInstance(request->theUserReference);
173 
174     off.QuadPart= 0;
175     sz.QuadPart= request->par.open.file_size;
176     while(off.QuadPart < sz.QuadPart)
177     {
178       req->filePointer = 0;          // DATA 0
179       req->userPointer = request->theUserPointer;          // DATA 2
180       req->numberOfPages = 1;        // DATA 5
181       req->varIndex = index++;
182       req->data.pageData[0] = m_page_ptr.i;
183 
184       m_fs.EXECUTE_DIRECT(block, GSN_FSWRITEREQ, signal,
185 			  FsReadWriteReq::FixedLength + 1,
186                           instance // wl4391_todo This EXECUTE_DIRECT is thread safe
187                           );
188       Uint32 size = request->par.open.page_size;
189       char* buf = (char*)m_page_ptr.p;
190       DWORD dwWritten;
191       while(size > 0){
192 	BOOL bWrite= WriteFile(hFile, buf, size, &dwWritten, 0);
193 	if(!bWrite || dwWritten!=size)
194 	{
195 	  request->error= GetLastError();
196 	}
197 	size -= dwWritten;
198 	buf += dwWritten;
199       }
200       if(size != 0)
201       {
202 	int err = errno;
203 	/*	close(theFd);
204 		unlink(theFileName.c_str());*/
205 	request->error = err;
206 	return;
207       }
208       off.QuadPart += request->par.open.page_size;
209     }
210 
211     off.QuadPart= 0;
212     r= SetFilePointerEx(hFile, off, NULL, FILE_BEGIN);
213     if(r==0)
214     {
215       request->error= GetLastError();
216       return;
217     }
218   }
219 
220   return;
221 }
222 
223 int
readBuffer(Request * req,char * buf,size_t size,off_t offset)224 Win32AsyncFile::readBuffer(Request* req, char * buf, size_t size, off_t offset)
225 {
226   req->par.readWrite.pages[0].size = 0;
227 
228   while (size > 0) {
229     size_t bytes_read = 0;
230 
231     OVERLAPPED ov;
232     bzero(&ov, sizeof(ov));
233 
234     LARGE_INTEGER li;
235     li.QuadPart = offset;
236     ov.Offset = li.LowPart;
237     ov.OffsetHigh = li.HighPart;
238 
239     DWORD dwBytesRead;
240     BOOL bRead = ReadFile(hFile,
241                           buf,
242                           (DWORD)size,
243                           &dwBytesRead,
244                           &ov);
245     if(!bRead){
246       int err = GetLastError();
247       if (err == ERROR_HANDLE_EOF && req->action == Request::readPartial)
248       {
249         return 0;
250       }
251       return err;
252     }
253     bytes_read = dwBytesRead;
254 
255     req->par.readWrite.pages[0].size += bytes_read;
256     if(bytes_read == 0){
257       if(req->action == Request::readPartial)
258       {
259 	return 0;
260       }
261       DEBUG(ndbout_c("Read underflow %d %d\n %x\n%d %d",
262 		     size, offset, buf, bytes_read, return_value));
263       return ERR_ReadUnderflow;
264     }
265 
266     if(bytes_read != size){
267       DEBUG(ndbout_c("Warning partial read %d != %d",
268 		     bytes_read, size));
269     }
270 
271     buf += bytes_read;
272     size -= bytes_read;
273     offset += (off_t)bytes_read;
274   }
275   return 0;
276 }
277 
278 int
writeBuffer(const char * buf,size_t size,off_t offset)279 Win32AsyncFile::writeBuffer(const char * buf, size_t size, off_t offset)
280 {
281   size_t chunk_size = 256 * 1024;
282   size_t bytes_to_write = chunk_size;
283 
284   m_write_wo_sync += size;
285 
286   while (size > 0) {
287     OVERLAPPED ov;
288     bzero(&ov, sizeof(ov));
289 
290     LARGE_INTEGER li;
291     li.QuadPart = offset;
292     ov.Offset = li.LowPart;
293     ov.OffsetHigh = li.HighPart;
294 
295     if (size < bytes_to_write){
296       // We are at the last chunk
297       bytes_to_write = size;
298     }
299     size_t bytes_written = 0;
300 
301     DWORD dwWritten;
302     BOOL bWrite = WriteFile(hFile, buf, (DWORD)bytes_to_write, &dwWritten, &ov);
303     if(!bWrite) {
304       return GetLastError();
305     }
306     bytes_written = dwWritten;
307     if (bytes_written != bytes_to_write) {
308       DEBUG(ndbout_c("Warning partial write %d != %d", bytes_written, bytes_to_write));
309     }
310 
311     buf += bytes_written;
312     size -= bytes_written;
313     offset += (off_t)bytes_written;
314   }
315   return 0;
316 }
317 
318 void
closeReq(Request * request)319 Win32AsyncFile::closeReq(Request * request)
320 {
321   if (m_open_flags & (
322       FsOpenReq::OM_WRITEONLY |
323       FsOpenReq::OM_READWRITE |
324       FsOpenReq::OM_APPEND )) {
325     syncReq(request);
326   }
327 
328   if(!CloseHandle(hFile)) {
329     request->error = GetLastError();
330   }
331   hFile = INVALID_HANDLE_VALUE;
332 }
333 
isOpen()334 bool Win32AsyncFile::isOpen(){
335   return (hFile != INVALID_HANDLE_VALUE);
336 }
337 
338 
339 void
syncReq(Request * request)340 Win32AsyncFile::syncReq(Request * request)
341 {
342   if ((m_auto_sync_freq && m_write_wo_sync == 0) ||
343       m_always_sync)
344   {
345     return;
346   }
347   if(!FlushFileBuffers(hFile)) {
348     request->error = GetLastError();
349     return;
350   }
351   m_write_wo_sync = 0;
352 }
353 
354 void
appendReq(Request * request)355 Win32AsyncFile::appendReq(Request * request){
356 
357   const char * buf = request->par.append.buf;
358   Uint32 size = Uint32(request->par.append.size);
359 
360   m_write_wo_sync += size;
361 
362   DWORD dwWritten = 0;
363   while(size > 0){
364     if(!WriteFile(hFile, buf, size, &dwWritten, 0)){
365       request->error = GetLastError();
366       return ;
367     }
368 
369     buf += dwWritten;
370     size -= dwWritten;
371   }
372 
373   if((m_auto_sync_freq && m_write_wo_sync > m_auto_sync_freq) ||
374       m_always_sync)
375   {
376     syncReq(request);
377   }
378 }
379 
380 void
removeReq(Request * request)381 Win32AsyncFile::removeReq(Request * request)
382 {
383   if(!DeleteFile(theFileName.c_str())) {
384     request->error = GetLastError();
385   }
386 }
387 
388 void
rmrfReq(Request * request,const char * src,bool removePath)389 Win32AsyncFile::rmrfReq(Request * request, const char * src, bool removePath){
390   if (!request->par.rmrf.directory)
391   {
392     // Remove file
393     if (!DeleteFile(src))
394     {
395       DWORD dwError = GetLastError();
396       if (dwError != ERROR_FILE_NOT_FOUND)
397 	request->error = dwError;
398     }
399     return;
400   }
401 
402   char path[PATH_MAX];
403   strcpy(path, src);
404   strcat(path, "\\*");
405 
406   WIN32_FIND_DATA ffd;
407   HANDLE hFindFile;
408 loop:
409   hFindFile = FindFirstFile(path, &ffd);
410   if (INVALID_HANDLE_VALUE == hFindFile)
411   {
412     DWORD dwError = GetLastError();
413     if (dwError != ERROR_PATH_NOT_FOUND)
414       request->error = dwError;
415     return;
416   }
417   path[strlen(path) - 1] = 0; // remove '*'
418 
419   do {
420     if (0 != strcmp(".", ffd.cFileName) && 0 != strcmp("..", ffd.cFileName))
421     {
422       int len = (int)strlen(path);
423       strcat(path, ffd.cFileName);
424       if(DeleteFile(path) || RemoveDirectory(path))
425       {
426         path[len] = 0;
427 	continue;
428       }//if
429 
430       FindClose(hFindFile);
431       strcat(path, "\\*");
432       goto loop;
433     }
434   } while(FindNextFile(hFindFile, &ffd));
435 
436   FindClose(hFindFile);
437   path[strlen(path)-1] = 0; // remove '\'
438   if (strcmp(src, path) != 0)
439   {
440     char * t = strrchr(path, '\\');
441     t[1] = '*';
442     t[2] = 0;
443     goto loop;
444   }
445 
446   if(removePath && !RemoveDirectory(src))
447     request->error = GetLastError();
448 }
449 
createDirectories()450 void Win32AsyncFile::createDirectories()
451 {
452   char* tmp;
453   const char * name = theFileName.c_str();
454   const char * base = theFileName.get_base_name();
455   while((tmp = (char *)strstr(base, DIR_SEPARATOR)))
456   {
457     char t = tmp[0];
458     tmp[0] = 0;
459     CreateDirectory(name, 0);
460     tmp[0] = t;
461     base = tmp + sizeof(DIR_SEPARATOR);
462   }
463 }
464