1 // natFileChannelImplWin32.cc - Native part of FileChannelImpl class.
2 
3 /* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004  Free Software
4    Foundation, Inc.
5 
6    This file is part of libgcj.
7 
8 This software is copyrighted work licensed under the terms of the
9 Libgcj License.  Please consult the file "LIBGCJ_LICENSE" for
10 details.  */
11 
12 // FIXME: In order to support interrupting of IO operations, we
13 // need to change to use the windows asynchronous IO functions
14 
15 #include <config.h>
16 #include <platform.h>
17 
18 #include <gcj/cni.h>
19 #include <gcj/javaprims.h>
20 #include <jvm.h>
21 
22 #include <stdio.h>
23 
24 #include <gnu/gcj/RawData.h>
25 #include <gnu/java/nio/FileLockImpl.h>
26 #include <gnu/java/nio/channels/FileChannelImpl.h>
27 #include <java/io/FileNotFoundException.h>
28 #include <java/io/IOException.h>
29 #include <java/io/SyncFailedException.h>
30 #include <java/io/InterruptedIOException.h>
31 #include <java/io/EOFException.h>
32 #include <java/lang/ArrayIndexOutOfBoundsException.h>
33 #include <java/lang/NullPointerException.h>
34 #include <java/lang/System.h>
35 #include <java/lang/String.h>
36 #include <java/lang/Thread.h>
37 #include <java/nio/ByteBuffer.h>
38 #include <java/nio/MappedByteBufferImpl.h>
39 #include <java/nio/channels/FileChannel.h>
40 #include <java/nio/channels/FileLock.h>
41 #include <gnu/java/nio/channels/FileChannelImpl.h>
42 
43 using gnu::gcj::RawData;
44 using java::io::IOException;
45 using java::nio::MappedByteBufferImpl;
46 using java::io::InterruptedIOException;
47 using java::io::FileNotFoundException;
48 using java::lang::ArrayIndexOutOfBoundsException;
49 using gnu::java::nio::channels::FileChannelImpl;
50 
51 #undef STRICT
52 
testCanUseGetHandleInfo()53 static bool testCanUseGetHandleInfo()
54 {
55   /* Test to see whether GetHandleInformation can be used
56      for console input or screen buffers. This is better
57      a kludgy OS version check. */
58   DWORD dwFlags;
59   return GetHandleInformation (GetStdHandle (STD_INPUT_HANDLE),
60     &dwFlags) != 0;
61 }
62 
63 // FIXME: casting a FILE (pointer) to a jint will not work on Win64 --
64 //        we should be using gnu.gcj.RawData's.
65 
66 void
init(void)67 FileChannelImpl::init(void)
68 {
69   in = new FileChannelImpl((jint)(GetStdHandle (STD_INPUT_HANDLE)),
70 			   FileChannelImpl::READ);
71   out = new FileChannelImpl((jint)(GetStdHandle (STD_OUTPUT_HANDLE)),
72 			    FileChannelImpl::WRITE);
73   err = new FileChannelImpl((jint)(GetStdHandle (STD_ERROR_HANDLE)),
74 			    FileChannelImpl::WRITE);
75 }
76 
77 #if 0
78 FileChannelImpl::sync (void) {
79   if (! FlushFileBuffers ((HANDLE)fd))
80   {
81     DWORD dwErrorCode = GetLastError ();
82     throw new SyncFailedException (_Jv_WinStrError (dwErrorCode));
83   }
84 }
85 #endif
86 
87 jint
open(jstring path,jint jflags)88 FileChannelImpl::open (jstring path, jint jflags) {
89 
90   HANDLE handle = NULL;
91   DWORD access = 0;
92   DWORD create = OPEN_EXISTING;
93 
94   JV_TEMP_STRING_WIN32(cpath, path)
95 
96   JvAssert((jflags & READ) || (jflags & WRITE));
97 
98   if ((jflags & READ) && (jflags & WRITE))
99     {
100       access = GENERIC_READ | GENERIC_WRITE;
101       if (jflags & EXCL)
102         create = CREATE_NEW; // this will raise error if file exists.
103       else
104         create = OPEN_ALWAYS; // equivalent to O_CREAT
105     }
106   else if (jflags & READ)
107     {
108       access = GENERIC_READ;
109       create = OPEN_EXISTING; // ignore EXCL
110     }
111   else
112     {
113       access = GENERIC_WRITE;
114       if (jflags & EXCL)
115         create = CREATE_NEW;
116       else if (jflags & APPEND)
117         create = OPEN_ALWAYS;
118       else
119         create = CREATE_ALWAYS;
120     }
121 
122   handle = CreateFile(cpath, access, FILE_SHARE_READ | FILE_SHARE_WRITE,
123     NULL, create, 0, NULL);
124 
125   if (handle == INVALID_HANDLE_VALUE)
126     {
127        DWORD dwErrorCode = GetLastError ();
128        throw new FileNotFoundException (_Jv_WinStrError (cpath, dwErrorCode));
129     }
130 
131   // For APPEND mode, move the file pointer to the end of the file.
132   if (jflags & APPEND)
133     {
134       DWORD low = SetFilePointer (handle, 0, NULL, FILE_END);
135       if ((low == (DWORD) 0xffffffff) && (GetLastError () != NO_ERROR))
136       {
137         DWORD dwErrorCode = GetLastError ();
138         throw new FileNotFoundException (_Jv_WinStrError (cpath, dwErrorCode));
139       }
140     }
141 
142   // Make this handle non-inheritable so that child
143   // processes don't inadvertently prevent us from
144   // closing this file.
145   _Jv_platform_close_on_exec (handle);
146 
147   return (jint) handle;
148 }
149 
150 void
write(jint b)151 FileChannelImpl::write (jint b)
152 {
153   DWORD bytesWritten;
154   jbyte buf = (jbyte)b;
155 
156   if (WriteFile ((HANDLE)fd, &buf, 1, &bytesWritten, NULL))
157     {
158       if (::java::lang::Thread::interrupted())
159         {
160           InterruptedIOException *iioe = new InterruptedIOException (JvNewStringLatin1 ("write interrupted"));
161           iioe->bytesTransferred = bytesWritten;
162     throw iioe;
163         }
164       if (bytesWritten != 1)
165         _Jv_ThrowIOException ();
166     }
167   else
168     _Jv_ThrowIOException ();
169   // FIXME: loop until bytesWritten == 1
170 }
171 
172 void
write(jbyteArray b,jint offset,jint len)173 FileChannelImpl::write(jbyteArray b, jint offset, jint len)
174 {
175   if (! b)
176     throw new ::java::lang::NullPointerException;
177   if(offset < 0 || len < 0 || offset + len > JvGetArrayLength (b))
178     throw new ArrayIndexOutOfBoundsException;
179 
180   jbyte *buf = elements (b) + offset;
181   DWORD bytesWritten;
182 
183   if (WriteFile ((HANDLE)fd, buf, len, &bytesWritten, NULL))
184     {
185       if (::java::lang::Thread::interrupted())
186         {
187           InterruptedIOException *iioe = new InterruptedIOException (JvNewStringLatin1 ("write interrupted"));
188           iioe->bytesTransferred = bytesWritten;
189           throw iioe;
190         }
191     }
192   else
193     _Jv_ThrowIOException ();
194   // FIXME: loop until bytesWritten == len
195 }
196 
197 void
implCloseChannel(void)198 FileChannelImpl::implCloseChannel (void)
199 {
200   HANDLE save = (HANDLE)fd;
201   fd = (jint)INVALID_HANDLE_VALUE;
202   if (! CloseHandle (save))
203     _Jv_ThrowIOException ();
204 }
205 
206 void
implTruncate(jlong size)207 FileChannelImpl::implTruncate (jlong size)
208 {
209   LONG liOrigFilePointer;
210   LONG liNewFilePointer;
211   LONG liEndFilePointer;
212 
213   // Get the original file pointer.
214   if (SetFilePointer((HANDLE) fd, (LONG) 0, &liOrigFilePointer,
215          FILE_CURRENT) != (BOOL) 0
216       && (GetLastError() != NO_ERROR))
217     _Jv_ThrowIOException ();
218 
219   // Get the length of the file.
220   if (SetFilePointer((HANDLE) fd, (LONG) 0, &liEndFilePointer,
221          FILE_END) != (BOOL) 0
222       && (GetLastError() != NO_ERROR))
223     _Jv_ThrowIOException ();
224 
225   if ((jlong)liEndFilePointer == size)
226     {
227       // Restore the file pointer.
228       if (liOrigFilePointer != liEndFilePointer)
229   {
230     if (SetFilePointer((HANDLE) fd, liOrigFilePointer, &liNewFilePointer,
231            FILE_BEGIN) != (BOOL) 0
232         && (GetLastError() != NO_ERROR))
233       _Jv_ThrowIOException ();
234   }
235       return;
236     }
237 
238   // Seek to the new end of file.
239   if (SetFilePointer((HANDLE) fd, (LONG) size, &liNewFilePointer,
240          FILE_BEGIN) != (BOOL) 0
241       && (GetLastError() != NO_ERROR))
242     _Jv_ThrowIOException ();
243 
244   // Truncate the file at this point.
245   if (SetEndOfFile((HANDLE) fd) != (BOOL) 0 && (GetLastError() != NO_ERROR))
246     _Jv_ThrowIOException ();
247 
248   if (liOrigFilePointer < liNewFilePointer)
249     {
250       // Restore the file pointer.
251       if (SetFilePointer((HANDLE) fd, liOrigFilePointer, &liNewFilePointer,
252         FILE_BEGIN) != (BOOL) 0
253         && (GetLastError() != NO_ERROR))
254         _Jv_ThrowIOException ();
255     }
256 }
257 
258 void
seek(jlong newPos)259 FileChannelImpl::seek (jlong newPos)
260 {
261   LONG high = pos >> 32;
262   DWORD low = SetFilePointer ((HANDLE)fd, (DWORD)(0xffffffff & newPos), &high, FILE_BEGIN);
263   if ((low == 0xffffffff) && (GetLastError () != NO_ERROR))
264     _Jv_ThrowIOException ();
265 }
266 
267 jlong
implPosition(void)268 FileChannelImpl::implPosition (void)
269 {
270   LONG high = 0;
271   DWORD low = SetFilePointer ((HANDLE)fd, 0, &high, FILE_CURRENT);
272   if ((low == 0xffffffff) && (GetLastError() != NO_ERROR))
273     _Jv_ThrowIOException ();
274   return (((jlong)high) << 32L) | (jlong)low;
275 }
276 
277 jlong
size(void)278 FileChannelImpl::size (void)
279 {
280   DWORD high;
281   DWORD low;
282 
283   low = GetFileSize ((HANDLE)fd, &high);
284   // FIXME: Error checking
285   return (((jlong)high) << 32L) | (jlong)low;
286 }
287 
288 jint
read(void)289 FileChannelImpl::read (void)
290 {
291   CHAR buf;
292   DWORD read;
293 
294   if (! ReadFile ((HANDLE)fd, &buf, 1, &read, NULL))
295     {
296       if (GetLastError () == ERROR_BROKEN_PIPE)
297         return -1;
298       else
299         _Jv_ThrowIOException ();
300     }
301 
302   if (! read)
303     return -1;
304   else
305     return (jint)(buf & 0xff);
306 }
307 
308 jint
read(jbyteArray buffer,jint offset,jint count)309 FileChannelImpl::read (jbyteArray buffer, jint offset, jint count)
310 {
311   if (! buffer)
312     throw new ::java::lang::NullPointerException;
313 
314   jsize bsize = JvGetArrayLength (buffer);
315   if (offset < 0 || count < 0 || offset + count > bsize)
316     throw new ArrayIndexOutOfBoundsException;
317 
318   // Must return 0 if an attempt is made to read 0 bytes.
319   if (count == 0)
320     return 0;
321 
322   jbyte *bytes = elements (buffer) + offset;
323 
324   DWORD read;
325   if (! ReadFile((HANDLE)fd, bytes, count, &read, NULL))
326     {
327       if (GetLastError () == ERROR_BROKEN_PIPE)
328         return -1;
329       else
330         _Jv_ThrowIOException ();
331     }
332 
333   if (read == 0) return -1;
334 
335   return (jint)read;
336 }
337 
338 jint
available(void)339 FileChannelImpl::available (void)
340 {
341   // FIXME:
342   return size() - position();
343 }
344 
345 jboolean
lock(jlong pos,jlong len,jboolean shared,jboolean wait)346 FileChannelImpl::lock (jlong pos, jlong len, jboolean shared, jboolean wait)
347 {
348   DWORD flags = 0;
349   OVERLAPPED ovlpd;
350 
351   ZeroMemory(&ovlpd,sizeof(OVERLAPPED));
352 
353   if(!shared)
354     flags |= LOCKFILE_EXCLUSIVE_LOCK;
355   if(!wait)
356     flags |= LOCKFILE_FAIL_IMMEDIATELY;
357 
358   ovlpd.Offset = (DWORD)pos;
359   ovlpd.OffsetHigh = pos>>32;
360 
361   DWORD lenlow = (DWORD)len;
362   DWORD lenhigh = len>>32;
363 
364   BOOL ret = LockFileEx((HANDLE)fd,flags,0,lenlow,lenhigh,&ovlpd);
365 
366   if(ret==ERROR_IO_PENDING && !shared && wait)
367     ret = GetOverlappedResult((HANDLE)fd,&ovlpd,NULL,wait);
368 
369   if(!ret)
370     _Jv_ThrowIOException(GetLastError());
371 
372   return true;
373 }
374 
375 void
unlock(jlong pos,jlong len)376 FileChannelImpl::unlock (jlong pos, jlong len)
377 {
378   OVERLAPPED ovlpd;
379 
380   ZeroMemory(&ovlpd,sizeof(OVERLAPPED));
381 
382   ovlpd.Offset = (DWORD)pos;
383   ovlpd.OffsetHigh = pos>>32;
384 
385   DWORD lenlow = (DWORD)len;
386   DWORD lenhigh = len>>32;
387 
388   BOOL ret = UnlockFileEx((HANDLE)fd,0,lenlow,lenhigh,&ovlpd);
389 
390   if(!ret)
391     _Jv_ThrowIOException(GetLastError());
392 }
393 
394 java::nio::MappedByteBuffer *
mapImpl(jchar mmode,jlong position,jint size)395 FileChannelImpl::mapImpl (jchar mmode, jlong position, jint size)
396 {
397   SYSTEM_INFO siSysInfo;
398   GetSystemInfo(&siSysInfo);
399   DWORD page_size = siSysInfo.dwPageSize;
400   jlong offset = position & ~(page_size-1);
401   jint align = position - offset;
402   jlong high = position + size;
403   jlong max_size;
404   if (mmode == '+')
405     max_size = high - offset;
406   else
407     max_size = 0;
408   DWORD access, protect;
409   if (mmode == 'r')
410     {
411       access = FILE_MAP_READ;
412       protect = PAGE_READONLY;
413     }
414   else if (mmode == '+')
415     {
416       access = FILE_MAP_WRITE;
417       protect = PAGE_READWRITE;
418     }
419   else
420     {
421       access = FILE_MAP_COPY;
422       protect = PAGE_WRITECOPY;
423     }
424   HANDLE hFileMapping = CreateFileMapping((HANDLE) fd,
425 					  (LPSECURITY_ATTRIBUTES) NULL,
426 					  protect,
427 					  (DWORD) (max_size >> 32),
428 					  (DWORD) max_size,
429 					  (LPCTSTR) NULL);
430   if (hFileMapping == NULL)
431     throw new IOException();
432   void *ptr = MapViewOfFile(hFileMapping, access,
433 			    (DWORD) (offset >> 32), (DWORD) offset,
434 			    (SIZE_T) (high - offset));
435   if (ptr == NULL)
436     throw new IOException();
437   MappedByteBufferImpl *buf
438     = new MappedByteBufferImpl((RawData *) ((char *) ptr + align),
439 			       size, mode == 'r');
440   buf->implPtr = reinterpret_cast<RawData*> (ptr);
441   buf->implLen = (jlong) (size_t) hFileMapping;
442   return buf;
443 }
444 
445 void
unmapImpl()446 MappedByteBufferImpl::unmapImpl ()
447 {
448   UnmapViewOfFile((void*)implPtr);
449   CloseHandle((HANDLE) (size_t) implLen);
450 }
451 
452 void
loadImpl()453 MappedByteBufferImpl::loadImpl ()
454 {
455 }
456 
457 jboolean
isLoadedImpl()458 MappedByteBufferImpl::isLoadedImpl ()
459 {
460   return true;
461 }
462 
463 void
forceImpl()464 MappedByteBufferImpl::forceImpl ()
465 {
466 }
467