- Move "questions and comments" at the top of the document.
[wine/multimedia.git] / files / file.c
blobb1a596127b8a08510e2c46f1a673ba8c44f42a59
1 /*
2 * File handling functions
4 * Copyright 1993 John Burton
5 * Copyright 1996 Alexandre Julliard
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 * TODO:
22 * Fix the CopyFileEx methods to implement the "extended" functionality.
23 * Right now, they simply call the CopyFile method.
26 #include "config.h"
27 #include "wine/port.h"
29 #include <assert.h>
30 #include <ctype.h>
31 #include <errno.h>
32 #include <fcntl.h>
33 #include <stdlib.h>
34 #include <stdio.h>
35 #include <string.h>
36 #ifdef HAVE_SYS_ERRNO_H
37 #include <sys/errno.h>
38 #endif
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #ifdef HAVE_SYS_MMAN_H
42 #include <sys/mman.h>
43 #endif
44 #include <sys/time.h>
45 #include <sys/poll.h>
46 #include <time.h>
47 #include <unistd.h>
48 #include <utime.h>
50 #include "winerror.h"
51 #include "windef.h"
52 #include "winbase.h"
53 #include "wine/winbase16.h"
54 #include "wine/server.h"
56 #include "drive.h"
57 #include "file.h"
58 #include "async.h"
59 #include "heap.h"
60 #include "msdos.h"
61 #include "wincon.h"
63 #include "smb.h"
64 #include "wine/debug.h"
66 WINE_DEFAULT_DEBUG_CHANNEL(file);
68 #if defined(MAP_ANONYMOUS) && !defined(MAP_ANON)
69 #define MAP_ANON MAP_ANONYMOUS
70 #endif
72 /* Size of per-process table of DOS handles */
73 #define DOS_TABLE_SIZE 256
75 /* Macro to derive file offset from OVERLAPPED struct */
76 #define OVERLAPPED_OFFSET(overlapped) ((off_t) (overlapped)->Offset + ((off_t) (overlapped)->OffsetHigh << 32))
78 static HANDLE dos_handles[DOS_TABLE_SIZE];
80 mode_t FILE_umask;
82 extern HANDLE WINAPI FILE_SmbOpen(LPCSTR name);
84 /***********************************************************************
85 * Asynchronous file I/O *
87 static DWORD fileio_get_async_status (const async_private *ovp);
88 static DWORD fileio_get_async_count (const async_private *ovp);
89 static void fileio_set_async_status (async_private *ovp, const DWORD status);
90 static void CALLBACK fileio_call_completion_func (ULONG_PTR data);
91 static void fileio_async_cleanup (async_private *ovp);
93 static async_ops fileio_async_ops =
95 fileio_get_async_status, /* get_status */
96 fileio_set_async_status, /* set_status */
97 fileio_get_async_count, /* get_count */
98 fileio_call_completion_func, /* call_completion */
99 fileio_async_cleanup /* cleanup */
102 static async_ops fileio_nocomp_async_ops =
104 fileio_get_async_status, /* get_status */
105 fileio_set_async_status, /* set_status */
106 fileio_get_async_count, /* get_count */
107 NULL, /* call_completion */
108 fileio_async_cleanup /* cleanup */
111 typedef struct async_fileio
113 struct async_private async;
114 LPOVERLAPPED lpOverlapped;
115 LPOVERLAPPED_COMPLETION_ROUTINE completion_func;
116 char *buffer;
117 int count;
118 enum fd_type fd_type;
119 } async_fileio;
121 static DWORD fileio_get_async_status (const struct async_private *ovp)
123 return ((async_fileio*) ovp)->lpOverlapped->Internal;
126 static void fileio_set_async_status (async_private *ovp, const DWORD status)
128 ((async_fileio*) ovp)->lpOverlapped->Internal = status;
131 static DWORD fileio_get_async_count (const struct async_private *ovp)
133 async_fileio *fileio = (async_fileio*) ovp;
134 DWORD ret = fileio->count - fileio->lpOverlapped->InternalHigh;
135 return (ret < 0 ? 0 : ret);
138 static void CALLBACK fileio_call_completion_func (ULONG_PTR data)
140 async_fileio *ovp = (async_fileio*) data;
141 TRACE ("data: %p\n", ovp);
143 ovp->completion_func( ovp->lpOverlapped->Internal,
144 ovp->lpOverlapped->InternalHigh,
145 ovp->lpOverlapped );
147 fileio_async_cleanup ( &ovp->async );
150 static void fileio_async_cleanup ( struct async_private *ovp )
152 HeapFree ( GetProcessHeap(), 0, ovp );
155 /***********************************************************************
156 * FILE_ConvertOFMode
158 * Convert OF_* mode into flags for CreateFile.
160 static void FILE_ConvertOFMode( INT mode, DWORD *access, DWORD *sharing )
162 switch(mode & 0x03)
164 case OF_READ: *access = GENERIC_READ; break;
165 case OF_WRITE: *access = GENERIC_WRITE; break;
166 case OF_READWRITE: *access = GENERIC_READ | GENERIC_WRITE; break;
167 default: *access = 0; break;
169 switch(mode & 0x70)
171 case OF_SHARE_EXCLUSIVE: *sharing = 0; break;
172 case OF_SHARE_DENY_WRITE: *sharing = FILE_SHARE_READ; break;
173 case OF_SHARE_DENY_READ: *sharing = FILE_SHARE_WRITE; break;
174 case OF_SHARE_DENY_NONE:
175 case OF_SHARE_COMPAT:
176 default: *sharing = FILE_SHARE_READ | FILE_SHARE_WRITE; break;
181 /***********************************************************************
182 * FILE_strcasecmp
184 * locale-independent case conversion for file I/O
186 int FILE_strcasecmp( const char *str1, const char *str2 )
188 for (;;)
190 int ret = FILE_toupper(*str1) - FILE_toupper(*str2);
191 if (ret || !*str1) return ret;
192 str1++;
193 str2++;
198 /***********************************************************************
199 * FILE_strncasecmp
201 * locale-independent case conversion for file I/O
203 int FILE_strncasecmp( const char *str1, const char *str2, int len )
205 int ret = 0;
206 for ( ; len > 0; len--, str1++, str2++)
207 if ((ret = FILE_toupper(*str1) - FILE_toupper(*str2)) || !*str1) break;
208 return ret;
212 /***********************************************************************
213 * FILE_GetNtStatus(void)
215 * Retrieve the Nt Status code from errno.
216 * Try to be consistent with FILE_SetDosError().
218 DWORD FILE_GetNtStatus(void)
220 int err = errno;
221 DWORD nt;
222 TRACE ( "errno = %d\n", errno );
223 switch ( err )
225 case EAGAIN: nt = STATUS_SHARING_VIOLATION; break;
226 case EBADF: nt = STATUS_INVALID_HANDLE; break;
227 case ENOSPC: nt = STATUS_DISK_FULL; break;
228 case EPERM:
229 case EROFS:
230 case EACCES: nt = STATUS_ACCESS_DENIED; break;
231 case ENOENT: nt = STATUS_SHARING_VIOLATION; break;
232 case EISDIR: nt = STATUS_FILE_IS_A_DIRECTORY; break;
233 case EMFILE:
234 case ENFILE: nt = STATUS_NO_MORE_FILES; break;
235 case EINVAL:
236 case ENOTEMPTY: nt = STATUS_DIRECTORY_NOT_EMPTY; break;
237 case EPIPE: nt = STATUS_PIPE_BROKEN; break;
238 case ENOEXEC: /* ?? */
239 case ESPIPE: /* ?? */
240 case EEXIST: /* ?? */
241 default:
242 FIXME ( "Converting errno %d to STATUS_UNSUCCESSFUL\n", err );
243 nt = STATUS_UNSUCCESSFUL;
245 return nt;
248 /***********************************************************************
249 * FILE_SetDosError
251 * Set the DOS error code from errno.
253 void FILE_SetDosError(void)
255 int save_errno = errno; /* errno gets overwritten by printf */
257 TRACE("errno = %d %s\n", errno, strerror(errno));
258 switch (save_errno)
260 case EAGAIN:
261 SetLastError( ERROR_SHARING_VIOLATION );
262 break;
263 case EBADF:
264 SetLastError( ERROR_INVALID_HANDLE );
265 break;
266 case ENOSPC:
267 SetLastError( ERROR_HANDLE_DISK_FULL );
268 break;
269 case EACCES:
270 case EPERM:
271 case EROFS:
272 SetLastError( ERROR_ACCESS_DENIED );
273 break;
274 case EBUSY:
275 SetLastError( ERROR_LOCK_VIOLATION );
276 break;
277 case ENOENT:
278 SetLastError( ERROR_FILE_NOT_FOUND );
279 break;
280 case EISDIR:
281 SetLastError( ERROR_CANNOT_MAKE );
282 break;
283 case ENFILE:
284 case EMFILE:
285 SetLastError( ERROR_NO_MORE_FILES );
286 break;
287 case EEXIST:
288 SetLastError( ERROR_FILE_EXISTS );
289 break;
290 case EINVAL:
291 case ESPIPE:
292 SetLastError( ERROR_SEEK );
293 break;
294 case ENOTEMPTY:
295 SetLastError( ERROR_DIR_NOT_EMPTY );
296 break;
297 case ENOEXEC:
298 SetLastError( ERROR_BAD_FORMAT );
299 break;
300 default:
301 WARN("unknown file error: %s\n", strerror(save_errno) );
302 SetLastError( ERROR_GEN_FAILURE );
303 break;
305 errno = save_errno;
309 /***********************************************************************
310 * FILE_GetUnixHandleType
312 * Retrieve the Unix handle corresponding to a file handle.
313 * Returns -1 on failure.
315 static int FILE_GetUnixHandleType( HANDLE handle, DWORD access, enum fd_type *type, int *flags_ptr )
317 int ret, flags, fd = -1;
319 ret = wine_server_handle_to_fd( handle, access, &fd, type, &flags );
320 if (flags_ptr) *flags_ptr = flags;
321 if (ret) SetLastError( RtlNtStatusToDosError(ret) );
322 else if (((access & GENERIC_READ) && (flags & FD_FLAG_RECV_SHUTDOWN)) ||
323 ((access & GENERIC_WRITE) && (flags & FD_FLAG_SEND_SHUTDOWN)))
325 close (fd);
326 SetLastError ( ERROR_PIPE_NOT_CONNECTED );
327 return -1;
329 return fd;
332 /***********************************************************************
333 * FILE_GetUnixHandle
335 * Retrieve the Unix handle corresponding to a file handle.
336 * Returns -1 on failure.
338 int FILE_GetUnixHandle( HANDLE handle, DWORD access )
340 return FILE_GetUnixHandleType( handle, access, NULL, NULL );
343 /*************************************************************************
344 * FILE_OpenConsole
346 * Open a handle to the current process console.
347 * Returns 0 on failure.
349 static HANDLE FILE_OpenConsole( BOOL output, DWORD access, DWORD sharing, LPSECURITY_ATTRIBUTES sa )
351 HANDLE ret;
353 SERVER_START_REQ( open_console )
355 req->from = output;
356 req->access = access;
357 req->share = sharing;
358 req->inherit = (sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle);
359 SetLastError(0);
360 wine_server_call_err( req );
361 ret = reply->handle;
363 SERVER_END_REQ;
364 return ret;
368 /***********************************************************************
369 * FILE_CreateFile
371 * Implementation of CreateFile. Takes a Unix path name.
372 * Returns 0 on failure.
374 HANDLE FILE_CreateFile( LPCSTR filename, DWORD access, DWORD sharing,
375 LPSECURITY_ATTRIBUTES sa, DWORD creation,
376 DWORD attributes, HANDLE template, BOOL fail_read_only,
377 UINT drive_type )
379 unsigned int err;
380 HANDLE ret;
382 for (;;)
384 SERVER_START_REQ( create_file )
386 req->access = access;
387 req->inherit = (sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle);
388 req->sharing = sharing;
389 req->create = creation;
390 req->attrs = attributes;
391 req->drive_type = drive_type;
392 wine_server_add_data( req, filename, strlen(filename) );
393 SetLastError(0);
394 err = wine_server_call( req );
395 ret = reply->handle;
397 SERVER_END_REQ;
399 /* If write access failed, retry without GENERIC_WRITE */
401 if (!ret && !fail_read_only && (access & GENERIC_WRITE))
403 if ((err == STATUS_MEDIA_WRITE_PROTECTED) || (err == STATUS_ACCESS_DENIED))
405 TRACE("Write access failed for file '%s', trying without "
406 "write access\n", filename);
407 access &= ~GENERIC_WRITE;
408 continue;
412 if (err) SetLastError( RtlNtStatusToDosError(err) );
414 if (!ret) WARN("Unable to create file '%s' (GLE %ld)\n", filename, GetLastError());
415 return ret;
420 /***********************************************************************
421 * FILE_CreateDevice
423 * Same as FILE_CreateFile but for a device
424 * Returns 0 on failure.
426 HANDLE FILE_CreateDevice( int client_id, DWORD access, LPSECURITY_ATTRIBUTES sa )
428 HANDLE ret;
429 SERVER_START_REQ( create_device )
431 req->access = access;
432 req->inherit = (sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle);
433 req->id = client_id;
434 SetLastError(0);
435 wine_server_call_err( req );
436 ret = reply->handle;
438 SERVER_END_REQ;
439 return ret;
442 static HANDLE FILE_OpenPipe(LPCSTR name, DWORD access)
444 WCHAR buffer[MAX_PATH];
445 HANDLE ret;
446 DWORD len = 0;
448 if (name && !(len = MultiByteToWideChar( CP_ACP, 0, name, strlen(name), buffer, MAX_PATH )))
450 SetLastError( ERROR_FILENAME_EXCED_RANGE );
451 return 0;
453 SERVER_START_REQ( open_named_pipe )
455 req->access = access;
456 SetLastError(0);
457 wine_server_add_data( req, buffer, len * sizeof(WCHAR) );
458 wine_server_call_err( req );
459 ret = reply->handle;
461 SERVER_END_REQ;
462 TRACE("Returned %d\n",ret);
463 return ret;
466 /*************************************************************************
467 * CreateFileA [KERNEL32.@] Creates or opens a file or other object
469 * Creates or opens an object, and returns a handle that can be used to
470 * access that object.
472 * PARAMS
474 * filename [in] pointer to filename to be accessed
475 * access [in] access mode requested
476 * sharing [in] share mode
477 * sa [in] pointer to security attributes
478 * creation [in] how to create the file
479 * attributes [in] attributes for newly created file
480 * template [in] handle to file with extended attributes to copy
482 * RETURNS
483 * Success: Open handle to specified file
484 * Failure: INVALID_HANDLE_VALUE
486 * NOTES
487 * Should call SetLastError() on failure.
489 * BUGS
491 * Doesn't support character devices, template files, or a
492 * lot of the 'attributes' flags yet.
494 HANDLE WINAPI CreateFileA( LPCSTR filename, DWORD access, DWORD sharing,
495 LPSECURITY_ATTRIBUTES sa, DWORD creation,
496 DWORD attributes, HANDLE template )
498 DOS_FULL_NAME full_name;
499 HANDLE ret;
501 if (!filename)
503 SetLastError( ERROR_INVALID_PARAMETER );
504 return INVALID_HANDLE_VALUE;
506 TRACE("%s %s%s%s%s%s%s%s\n",filename,
507 ((access & GENERIC_READ)==GENERIC_READ)?"GENERIC_READ ":"",
508 ((access & GENERIC_WRITE)==GENERIC_WRITE)?"GENERIC_WRITE ":"",
509 (!access)?"QUERY_ACCESS ":"",
510 ((sharing & FILE_SHARE_READ)==FILE_SHARE_READ)?"FILE_SHARE_READ ":"",
511 ((sharing & FILE_SHARE_WRITE)==FILE_SHARE_WRITE)?"FILE_SHARE_WRITE ":"",
512 ((sharing & FILE_SHARE_DELETE)==FILE_SHARE_DELETE)?"FILE_SHARE_DELETE ":"",
513 (creation ==CREATE_NEW)?"CREATE_NEW":
514 (creation ==CREATE_ALWAYS)?"CREATE_ALWAYS ":
515 (creation ==OPEN_EXISTING)?"OPEN_EXISTING ":
516 (creation ==OPEN_ALWAYS)?"OPEN_ALWAYS ":
517 (creation ==TRUNCATE_EXISTING)?"TRUNCATE_EXISTING ":"");
519 /* If the name starts with '\\?\', ignore the first 4 chars. */
520 if (!strncmp(filename, "\\\\?\\", 4))
522 filename += 4;
523 if (!strncmp(filename, "UNC\\", 4))
525 FIXME("UNC name (%s) not supported.\n", filename );
526 SetLastError( ERROR_PATH_NOT_FOUND );
527 return INVALID_HANDLE_VALUE;
531 if (!strncmp(filename, "\\\\.\\", 4)) {
532 if(!strncasecmp(&filename[4],"pipe\\",5))
534 TRACE("Opening a pipe: %s\n",filename);
535 ret = FILE_OpenPipe(filename,access);
536 goto done;
538 else if (isalpha(filename[4]) && filename[5] == ':' && filename[6] == '\0')
540 ret = FILE_CreateDevice( (toupper(filename[4]) - 'A') | 0x20000, access, sa );
541 goto done;
543 else if (!DOSFS_GetDevice( filename ))
545 ret = DEVICE_Open( filename+4, access, sa );
546 goto done;
548 else
549 filename+=4; /* fall into DOSFS_Device case below */
552 /* If the name still starts with '\\', it's a UNC name. */
553 if (!strncmp(filename, "\\\\", 2))
555 ret = SMB_CreateFileA(filename, access, sharing, sa, creation, attributes, template );
556 goto done;
559 /* If the name contains a DOS wild card (* or ?), do no create a file */
560 if(strchr(filename,'*') || strchr(filename,'?'))
561 return INVALID_HANDLE_VALUE;
563 /* Open a console for CONIN$ or CONOUT$ */
564 if (!strcasecmp(filename, "CONIN$"))
566 ret = FILE_OpenConsole( FALSE, access, sharing, sa );
567 goto done;
569 if (!strcasecmp(filename, "CONOUT$"))
571 ret = FILE_OpenConsole( TRUE, access, sharing, sa );
572 goto done;
575 if (DOSFS_GetDevice( filename ))
577 TRACE("opening device '%s'\n", filename );
579 if (!(ret = DOSFS_OpenDevice( filename, access, attributes, sa )))
581 /* Do not silence this please. It is a critical error. -MM */
582 ERR("Couldn't open device '%s'!\n",filename);
583 SetLastError( ERROR_FILE_NOT_FOUND );
585 goto done;
588 /* check for filename, don't check for last entry if creating */
589 if (!DOSFS_GetFullName( filename,
590 (creation == OPEN_EXISTING) ||
591 (creation == TRUNCATE_EXISTING),
592 &full_name )) {
593 WARN("Unable to get full filename from '%s' (GLE %ld)\n",
594 filename, GetLastError());
595 return INVALID_HANDLE_VALUE;
598 ret = FILE_CreateFile( full_name.long_name, access, sharing,
599 sa, creation, attributes, template,
600 DRIVE_GetFlags(full_name.drive) & DRIVE_FAIL_READ_ONLY,
601 GetDriveTypeA( full_name.short_name ) );
602 done:
603 if (!ret) ret = INVALID_HANDLE_VALUE;
604 return ret;
609 /*************************************************************************
610 * CreateFileW (KERNEL32.@)
612 HANDLE WINAPI CreateFileW( LPCWSTR filename, DWORD access, DWORD sharing,
613 LPSECURITY_ATTRIBUTES sa, DWORD creation,
614 DWORD attributes, HANDLE template)
616 LPSTR afn = HEAP_strdupWtoA( GetProcessHeap(), 0, filename );
617 HANDLE res = CreateFileA( afn, access, sharing, sa, creation, attributes, template );
618 HeapFree( GetProcessHeap(), 0, afn );
619 return res;
623 /***********************************************************************
624 * FILE_FillInfo
626 * Fill a file information from a struct stat.
628 static void FILE_FillInfo( struct stat *st, BY_HANDLE_FILE_INFORMATION *info )
630 if (S_ISDIR(st->st_mode))
631 info->dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
632 else
633 info->dwFileAttributes = FILE_ATTRIBUTE_ARCHIVE;
634 if (!(st->st_mode & S_IWUSR))
635 info->dwFileAttributes |= FILE_ATTRIBUTE_READONLY;
637 RtlSecondsSince1970ToTime( st->st_mtime, &info->ftCreationTime );
638 RtlSecondsSince1970ToTime( st->st_mtime, &info->ftLastWriteTime );
639 RtlSecondsSince1970ToTime( st->st_atime, &info->ftLastAccessTime );
641 info->dwVolumeSerialNumber = 0; /* FIXME */
642 info->nFileSizeHigh = 0;
643 info->nFileSizeLow = 0;
644 if (!S_ISDIR(st->st_mode)) {
645 info->nFileSizeHigh = st->st_size >> 32;
646 info->nFileSizeLow = st->st_size & 0xffffffff;
648 info->nNumberOfLinks = st->st_nlink;
649 info->nFileIndexHigh = 0;
650 info->nFileIndexLow = st->st_ino;
654 /***********************************************************************
655 * FILE_Stat
657 * Stat a Unix path name. Return TRUE if OK.
659 BOOL FILE_Stat( LPCSTR unixName, BY_HANDLE_FILE_INFORMATION *info )
661 struct stat st;
663 if (lstat( unixName, &st ) == -1)
665 FILE_SetDosError();
666 return FALSE;
668 if (!S_ISLNK(st.st_mode)) FILE_FillInfo( &st, info );
669 else
671 /* do a "real" stat to find out
672 about the type of the symlink destination */
673 if (stat( unixName, &st ) == -1)
675 FILE_SetDosError();
676 return FALSE;
678 FILE_FillInfo( &st, info );
679 info->dwFileAttributes |= FILE_ATTRIBUTE_SYMLINK;
681 return TRUE;
685 /***********************************************************************
686 * GetFileInformationByHandle (KERNEL32.@)
688 DWORD WINAPI GetFileInformationByHandle( HANDLE hFile,
689 BY_HANDLE_FILE_INFORMATION *info )
691 DWORD ret;
692 if (!info) return 0;
694 SERVER_START_REQ( get_file_info )
696 req->handle = hFile;
697 if ((ret = !wine_server_call_err( req )))
699 /* FIXME: which file types are supported ?
700 * Serial ports (FILE_TYPE_CHAR) are not,
701 * and MSDN also says that pipes are not supported.
702 * FILE_TYPE_REMOTE seems to be supported according to
703 * MSDN q234741.txt */
704 if ((reply->type == FILE_TYPE_DISK) || (reply->type == FILE_TYPE_REMOTE))
706 RtlSecondsSince1970ToTime( reply->write_time, &info->ftCreationTime );
707 RtlSecondsSince1970ToTime( reply->write_time, &info->ftLastWriteTime );
708 RtlSecondsSince1970ToTime( reply->access_time, &info->ftLastAccessTime );
709 info->dwFileAttributes = reply->attr;
710 info->dwVolumeSerialNumber = reply->serial;
711 info->nFileSizeHigh = reply->size_high;
712 info->nFileSizeLow = reply->size_low;
713 info->nNumberOfLinks = reply->links;
714 info->nFileIndexHigh = reply->index_high;
715 info->nFileIndexLow = reply->index_low;
717 else
719 SetLastError(ERROR_NOT_SUPPORTED);
720 ret = 0;
724 SERVER_END_REQ;
725 return ret;
729 /**************************************************************************
730 * GetFileAttributes (KERNEL.420)
732 DWORD WINAPI GetFileAttributes16( LPCSTR name )
734 return GetFileAttributesA( name );
738 /**************************************************************************
739 * GetFileAttributesA (KERNEL32.@)
741 DWORD WINAPI GetFileAttributesA( LPCSTR name )
743 DOS_FULL_NAME full_name;
744 BY_HANDLE_FILE_INFORMATION info;
746 if (name == NULL)
748 SetLastError( ERROR_INVALID_PARAMETER );
749 return -1;
751 if (!DOSFS_GetFullName( name, TRUE, &full_name) )
752 return -1;
753 if (!FILE_Stat( full_name.long_name, &info )) return -1;
754 return info.dwFileAttributes;
758 /**************************************************************************
759 * GetFileAttributesW (KERNEL32.@)
761 DWORD WINAPI GetFileAttributesW( LPCWSTR name )
763 LPSTR nameA = HEAP_strdupWtoA( GetProcessHeap(), 0, name );
764 DWORD res = GetFileAttributesA( nameA );
765 HeapFree( GetProcessHeap(), 0, nameA );
766 return res;
770 /**************************************************************************
771 * SetFileAttributes (KERNEL.421)
773 BOOL16 WINAPI SetFileAttributes16( LPCSTR lpFileName, DWORD attributes )
775 return SetFileAttributesA( lpFileName, attributes );
779 /**************************************************************************
780 * SetFileAttributesA (KERNEL32.@)
782 BOOL WINAPI SetFileAttributesA(LPCSTR lpFileName, DWORD attributes)
784 struct stat buf;
785 DOS_FULL_NAME full_name;
787 if (!DOSFS_GetFullName( lpFileName, TRUE, &full_name ))
788 return FALSE;
790 TRACE("(%s,%lx)\n",lpFileName,attributes);
791 if(stat(full_name.long_name,&buf)==-1)
793 FILE_SetDosError();
794 return FALSE;
796 if (attributes & FILE_ATTRIBUTE_READONLY)
798 if(S_ISDIR(buf.st_mode))
799 /* FIXME */
800 WARN("FILE_ATTRIBUTE_READONLY ignored for directory.\n");
801 else
802 buf.st_mode &= ~0222; /* octal!, clear write permission bits */
803 attributes &= ~FILE_ATTRIBUTE_READONLY;
805 else
807 /* add write permission */
808 buf.st_mode |= (0600 | ((buf.st_mode & 044) >> 1)) & (~FILE_umask);
810 if (attributes & FILE_ATTRIBUTE_DIRECTORY)
812 if (!S_ISDIR(buf.st_mode))
813 FIXME("SetFileAttributes expected the file '%s' to be a directory\n",
814 lpFileName);
815 attributes &= ~FILE_ATTRIBUTE_DIRECTORY;
817 attributes &= ~(FILE_ATTRIBUTE_NORMAL|FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM);
818 if (attributes)
819 FIXME("(%s):%lx attribute(s) not implemented.\n", lpFileName,attributes);
820 if (-1==chmod(full_name.long_name,buf.st_mode))
822 if(GetDriveTypeA(lpFileName) == DRIVE_CDROM) {
823 SetLastError( ERROR_ACCESS_DENIED );
824 return FALSE;
828 * FIXME: We don't return FALSE here because of differences between
829 * Linux and Windows privileges. Under Linux only the owner of
830 * the file is allowed to change file attributes. Under Windows,
831 * applications expect that if you can write to a file, you can also
832 * change its attributes (see GENERIC_WRITE). We could try to be
833 * clever here but that would break multi-user installations where
834 * users share read-only DLLs. This is because some installers like
835 * to change attributes of already installed DLLs.
837 FIXME("Couldn't set file attributes for existing file \"%s\".\n"
838 "Check permissions or set VFAT \"quiet\" mount flag\n", full_name.long_name);
840 return TRUE;
844 /**************************************************************************
845 * SetFileAttributesW (KERNEL32.@)
847 BOOL WINAPI SetFileAttributesW(LPCWSTR lpFileName, DWORD attributes)
849 BOOL res;
850 DWORD len = WideCharToMultiByte( CP_ACP, 0, lpFileName, -1, NULL, 0, NULL, NULL );
851 LPSTR afn = HeapAlloc( GetProcessHeap(), 0, len );
853 WideCharToMultiByte( CP_ACP, 0, lpFileName, -1, afn, len, NULL, NULL );
854 res = SetFileAttributesA( afn, attributes );
855 HeapFree( GetProcessHeap(), 0, afn );
856 return res;
860 /***********************************************************************
861 * GetFileSize (KERNEL32.@)
863 DWORD WINAPI GetFileSize( HANDLE hFile, LPDWORD filesizehigh )
865 BY_HANDLE_FILE_INFORMATION info;
866 if (!GetFileInformationByHandle( hFile, &info )) return -1;
867 if (filesizehigh) *filesizehigh = info.nFileSizeHigh;
868 return info.nFileSizeLow;
872 /***********************************************************************
873 * GetFileTime (KERNEL32.@)
875 BOOL WINAPI GetFileTime( HANDLE hFile, FILETIME *lpCreationTime,
876 FILETIME *lpLastAccessTime,
877 FILETIME *lpLastWriteTime )
879 BY_HANDLE_FILE_INFORMATION info;
880 if (!GetFileInformationByHandle( hFile, &info )) return FALSE;
881 if (lpCreationTime) *lpCreationTime = info.ftCreationTime;
882 if (lpLastAccessTime) *lpLastAccessTime = info.ftLastAccessTime;
883 if (lpLastWriteTime) *lpLastWriteTime = info.ftLastWriteTime;
884 return TRUE;
887 /***********************************************************************
888 * CompareFileTime (KERNEL32.@)
890 INT WINAPI CompareFileTime( LPFILETIME x, LPFILETIME y )
892 if (!x || !y) return -1;
894 if (x->dwHighDateTime > y->dwHighDateTime)
895 return 1;
896 if (x->dwHighDateTime < y->dwHighDateTime)
897 return -1;
898 if (x->dwLowDateTime > y->dwLowDateTime)
899 return 1;
900 if (x->dwLowDateTime < y->dwLowDateTime)
901 return -1;
902 return 0;
905 /***********************************************************************
906 * FILE_GetTempFileName : utility for GetTempFileName
908 static UINT FILE_GetTempFileName( LPCSTR path, LPCSTR prefix, UINT unique,
909 LPSTR buffer, BOOL isWin16 )
911 static UINT unique_temp;
912 DOS_FULL_NAME full_name;
913 int i;
914 LPSTR p;
915 UINT num;
917 if ( !path || !prefix || !buffer ) return 0;
919 if (!unique_temp) unique_temp = time(NULL) & 0xffff;
920 num = unique ? (unique & 0xffff) : (unique_temp++ & 0xffff);
922 strcpy( buffer, path );
923 p = buffer + strlen(buffer);
925 /* add a \, if there isn't one and path is more than just the drive letter ... */
926 if ( !((strlen(buffer) == 2) && (buffer[1] == ':'))
927 && ((p == buffer) || (p[-1] != '\\'))) *p++ = '\\';
929 if (isWin16) *p++ = '~';
930 for (i = 3; (i > 0) && (*prefix); i--) *p++ = *prefix++;
931 sprintf( p, "%04x.tmp", num );
933 /* Now try to create it */
935 if (!unique)
939 HFILE handle = CreateFileA( buffer, GENERIC_WRITE, 0, NULL,
940 CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0 );
941 if (handle != INVALID_HANDLE_VALUE)
942 { /* We created it */
943 TRACE("created %s\n",
944 buffer);
945 CloseHandle( handle );
946 break;
948 if (GetLastError() != ERROR_FILE_EXISTS)
949 break; /* No need to go on */
950 num++;
951 sprintf( p, "%04x.tmp", num );
952 } while (num != (unique & 0xffff));
955 /* Get the full path name */
957 if (DOSFS_GetFullName( buffer, FALSE, &full_name ))
959 /* Check if we have write access in the directory */
960 if ((p = strrchr( full_name.long_name, '/' ))) *p = '\0';
961 if (access( full_name.long_name, W_OK ) == -1)
962 WARN("returns '%s', which doesn't seem to be writeable.\n",
963 buffer);
965 TRACE("returning %s\n", buffer );
966 return unique ? unique : num;
970 /***********************************************************************
971 * GetTempFileNameA (KERNEL32.@)
973 UINT WINAPI GetTempFileNameA( LPCSTR path, LPCSTR prefix, UINT unique,
974 LPSTR buffer)
976 return FILE_GetTempFileName(path, prefix, unique, buffer, FALSE);
979 /***********************************************************************
980 * GetTempFileNameW (KERNEL32.@)
982 UINT WINAPI GetTempFileNameW( LPCWSTR path, LPCWSTR prefix, UINT unique,
983 LPWSTR buffer )
985 LPSTR patha,prefixa;
986 char buffera[144];
987 UINT ret;
989 if (!path) return 0;
990 patha = HEAP_strdupWtoA( GetProcessHeap(), 0, path );
991 prefixa = HEAP_strdupWtoA( GetProcessHeap(), 0, prefix );
992 ret = FILE_GetTempFileName( patha, prefixa, unique, buffera, FALSE );
993 MultiByteToWideChar( CP_ACP, 0, buffera, -1, buffer, MAX_PATH );
994 HeapFree( GetProcessHeap(), 0, patha );
995 HeapFree( GetProcessHeap(), 0, prefixa );
996 return ret;
1000 /***********************************************************************
1001 * GetTempFileName (KERNEL.97)
1003 UINT16 WINAPI GetTempFileName16( BYTE drive, LPCSTR prefix, UINT16 unique,
1004 LPSTR buffer )
1006 char temppath[144];
1008 if (!(drive & ~TF_FORCEDRIVE)) /* drive 0 means current default drive */
1009 drive |= DRIVE_GetCurrentDrive() + 'A';
1011 if ((drive & TF_FORCEDRIVE) &&
1012 !DRIVE_IsValid( toupper(drive & ~TF_FORCEDRIVE) - 'A' ))
1014 drive &= ~TF_FORCEDRIVE;
1015 WARN("invalid drive %d specified\n", drive );
1018 if (drive & TF_FORCEDRIVE)
1019 sprintf(temppath,"%c:", drive & ~TF_FORCEDRIVE );
1020 else
1021 GetTempPathA( 132, temppath );
1022 return (UINT16)FILE_GetTempFileName( temppath, prefix, unique, buffer, TRUE );
1025 /***********************************************************************
1026 * FILE_DoOpenFile
1028 * Implementation of OpenFile16() and OpenFile32().
1030 static HFILE FILE_DoOpenFile( LPCSTR name, OFSTRUCT *ofs, UINT mode,
1031 BOOL win32 )
1033 HFILE hFileRet;
1034 FILETIME filetime;
1035 WORD filedatetime[2];
1036 DOS_FULL_NAME full_name;
1037 DWORD access, sharing;
1038 char *p;
1040 if (!ofs) return HFILE_ERROR;
1042 TRACE("%s %s %s %s%s%s%s%s%s%s%s%s\n",name,
1043 ((mode & 0x3 )==OF_READ)?"OF_READ":
1044 ((mode & 0x3 )==OF_WRITE)?"OF_WRITE":
1045 ((mode & 0x3 )==OF_READWRITE)?"OF_READWRITE":"unknown",
1046 ((mode & 0x70 )==OF_SHARE_COMPAT)?"OF_SHARE_COMPAT":
1047 ((mode & 0x70 )==OF_SHARE_DENY_NONE)?"OF_SHARE_DENY_NONE":
1048 ((mode & 0x70 )==OF_SHARE_DENY_READ)?"OF_SHARE_DENY_READ":
1049 ((mode & 0x70 )==OF_SHARE_DENY_WRITE)?"OF_SHARE_DENY_WRITE":
1050 ((mode & 0x70 )==OF_SHARE_EXCLUSIVE)?"OF_SHARE_EXCLUSIVE":"unknown",
1051 ((mode & OF_PARSE )==OF_PARSE)?"OF_PARSE ":"",
1052 ((mode & OF_DELETE )==OF_DELETE)?"OF_DELETE ":"",
1053 ((mode & OF_VERIFY )==OF_VERIFY)?"OF_VERIFY ":"",
1054 ((mode & OF_SEARCH )==OF_SEARCH)?"OF_SEARCH ":"",
1055 ((mode & OF_CANCEL )==OF_CANCEL)?"OF_CANCEL ":"",
1056 ((mode & OF_CREATE )==OF_CREATE)?"OF_CREATE ":"",
1057 ((mode & OF_PROMPT )==OF_PROMPT)?"OF_PROMPT ":"",
1058 ((mode & OF_EXIST )==OF_EXIST)?"OF_EXIST ":"",
1059 ((mode & OF_REOPEN )==OF_REOPEN)?"OF_REOPEN ":""
1063 ofs->cBytes = sizeof(OFSTRUCT);
1064 ofs->nErrCode = 0;
1065 if (mode & OF_REOPEN) name = ofs->szPathName;
1067 if (!name) {
1068 ERR("called with `name' set to NULL ! Please debug.\n");
1069 return HFILE_ERROR;
1072 TRACE("%s %04x\n", name, mode );
1074 /* the watcom 10.6 IDE relies on a valid path returned in ofs->szPathName
1075 Are there any cases where getting the path here is wrong?
1076 Uwe Bonnes 1997 Apr 2 */
1077 if (!GetFullPathNameA( name, sizeof(ofs->szPathName),
1078 ofs->szPathName, NULL )) goto error;
1079 FILE_ConvertOFMode( mode, &access, &sharing );
1081 /* OF_PARSE simply fills the structure */
1083 if (mode & OF_PARSE)
1085 ofs->fFixedDisk = (GetDriveType16( ofs->szPathName[0]-'A' )
1086 != DRIVE_REMOVABLE);
1087 TRACE("(%s): OF_PARSE, res = '%s'\n",
1088 name, ofs->szPathName );
1089 return 0;
1092 /* OF_CREATE is completely different from all other options, so
1093 handle it first */
1095 if (mode & OF_CREATE)
1097 if ((hFileRet = CreateFileA( name, GENERIC_READ | GENERIC_WRITE,
1098 sharing, NULL, CREATE_ALWAYS,
1099 FILE_ATTRIBUTE_NORMAL, 0 ))== INVALID_HANDLE_VALUE)
1100 goto error;
1101 goto success;
1104 /* If OF_SEARCH is set, ignore the given path */
1106 if ((mode & OF_SEARCH) && !(mode & OF_REOPEN))
1108 /* First try the file name as is */
1109 if (DOSFS_GetFullName( name, TRUE, &full_name )) goto found;
1110 /* Now remove the path */
1111 if (name[0] && (name[1] == ':')) name += 2;
1112 if ((p = strrchr( name, '\\' ))) name = p + 1;
1113 if ((p = strrchr( name, '/' ))) name = p + 1;
1114 if (!name[0]) goto not_found;
1117 /* Now look for the file */
1119 if (!DIR_SearchPath( NULL, name, NULL, &full_name, win32 )) goto not_found;
1121 found:
1122 TRACE("found %s = %s\n",
1123 full_name.long_name, full_name.short_name );
1124 lstrcpynA( ofs->szPathName, full_name.short_name,
1125 sizeof(ofs->szPathName) );
1127 if (mode & OF_SHARE_EXCLUSIVE)
1128 /* Some InstallShield version uses OF_SHARE_EXCLUSIVE
1129 on the file <tempdir>/_ins0432._mp to determine how
1130 far installation has proceeded.
1131 _ins0432._mp is an executable and while running the
1132 application expects the open with OF_SHARE_ to fail*/
1133 /* Probable FIXME:
1134 As our loader closes the files after loading the executable,
1135 we can't find the running executable with FILE_InUse.
1136 The loader should keep the file open, as Windows does that, too.
1139 char *last = strrchr(full_name.long_name,'/');
1140 if (!last)
1141 last = full_name.long_name - 1;
1142 if (GetModuleHandle16(last+1))
1144 TRACE("Denying shared open for %s\n",full_name.long_name);
1145 return HFILE_ERROR;
1149 if (mode & OF_DELETE)
1151 if (unlink( full_name.long_name ) == -1) goto not_found;
1152 TRACE("(%s): OF_DELETE return = OK\n", name);
1153 return 1;
1156 hFileRet = FILE_CreateFile( full_name.long_name, access, sharing,
1157 NULL, OPEN_EXISTING, 0, 0,
1158 DRIVE_GetFlags(full_name.drive) & DRIVE_FAIL_READ_ONLY,
1159 GetDriveTypeA( full_name.short_name ) );
1160 if (!hFileRet) goto not_found;
1162 GetFileTime( hFileRet, NULL, NULL, &filetime );
1163 FileTimeToDosDateTime( &filetime, &filedatetime[0], &filedatetime[1] );
1164 if ((mode & OF_VERIFY) && (mode & OF_REOPEN))
1166 if (memcmp( ofs->reserved, filedatetime, sizeof(ofs->reserved) ))
1168 CloseHandle( hFileRet );
1169 WARN("(%s): OF_VERIFY failed\n", name );
1170 /* FIXME: what error here? */
1171 SetLastError( ERROR_FILE_NOT_FOUND );
1172 goto error;
1175 memcpy( ofs->reserved, filedatetime, sizeof(ofs->reserved) );
1177 success: /* We get here if the open was successful */
1178 TRACE("(%s): OK, return = %d\n", name, hFileRet );
1179 if (win32)
1181 if (mode & OF_EXIST) /* Return the handle, but close it first */
1182 CloseHandle( hFileRet );
1184 else
1186 hFileRet = Win32HandleToDosFileHandle( hFileRet );
1187 if (hFileRet == HFILE_ERROR16) goto error;
1188 if (mode & OF_EXIST) /* Return the handle, but close it first */
1189 _lclose16( hFileRet );
1191 return hFileRet;
1193 not_found: /* We get here if the file does not exist */
1194 WARN("'%s' not found or sharing violation\n", name );
1195 SetLastError( ERROR_FILE_NOT_FOUND );
1196 /* fall through */
1198 error: /* We get here if there was an error opening the file */
1199 ofs->nErrCode = GetLastError();
1200 WARN("(%s): return = HFILE_ERROR error= %d\n",
1201 name,ofs->nErrCode );
1202 return HFILE_ERROR;
1206 /***********************************************************************
1207 * OpenFile (KERNEL.74)
1208 * OpenFileEx (KERNEL.360)
1210 HFILE16 WINAPI OpenFile16( LPCSTR name, OFSTRUCT *ofs, UINT16 mode )
1212 return FILE_DoOpenFile( name, ofs, mode, FALSE );
1216 /***********************************************************************
1217 * OpenFile (KERNEL32.@)
1219 HFILE WINAPI OpenFile( LPCSTR name, OFSTRUCT *ofs, UINT mode )
1221 return FILE_DoOpenFile( name, ofs, mode, TRUE );
1225 /***********************************************************************
1226 * FILE_InitProcessDosHandles
1228 * Allocates the default DOS handles for a process. Called either by
1229 * Win32HandleToDosFileHandle below or by the DOSVM stuff.
1231 static void FILE_InitProcessDosHandles( void )
1233 dos_handles[0] = GetStdHandle(STD_INPUT_HANDLE);
1234 dos_handles[1] = GetStdHandle(STD_OUTPUT_HANDLE);
1235 dos_handles[2] = GetStdHandle(STD_ERROR_HANDLE);
1236 dos_handles[3] = GetStdHandle(STD_ERROR_HANDLE);
1237 dos_handles[4] = GetStdHandle(STD_ERROR_HANDLE);
1240 /***********************************************************************
1241 * Win32HandleToDosFileHandle (KERNEL32.21)
1243 * Allocate a DOS handle for a Win32 handle. The Win32 handle is no
1244 * longer valid after this function (even on failure).
1246 * Note: this is not exactly right, since on Win95 the Win32 handles
1247 * are on top of DOS handles and we do it the other way
1248 * around. Should be good enough though.
1250 HFILE WINAPI Win32HandleToDosFileHandle( HANDLE handle )
1252 int i;
1254 if (!handle || (handle == INVALID_HANDLE_VALUE))
1255 return HFILE_ERROR;
1257 for (i = 5; i < DOS_TABLE_SIZE; i++)
1258 if (!dos_handles[i])
1260 dos_handles[i] = handle;
1261 TRACE("Got %d for h32 %d\n", i, handle );
1262 return (HFILE)i;
1264 CloseHandle( handle );
1265 SetLastError( ERROR_TOO_MANY_OPEN_FILES );
1266 return HFILE_ERROR;
1270 /***********************************************************************
1271 * DosFileHandleToWin32Handle (KERNEL32.20)
1273 * Return the Win32 handle for a DOS handle.
1275 * Note: this is not exactly right, since on Win95 the Win32 handles
1276 * are on top of DOS handles and we do it the other way
1277 * around. Should be good enough though.
1279 HANDLE WINAPI DosFileHandleToWin32Handle( HFILE handle )
1281 HFILE16 hfile = (HFILE16)handle;
1282 if (hfile < 5 && !dos_handles[hfile]) FILE_InitProcessDosHandles();
1283 if ((hfile >= DOS_TABLE_SIZE) || !dos_handles[hfile])
1285 SetLastError( ERROR_INVALID_HANDLE );
1286 return INVALID_HANDLE_VALUE;
1288 return dos_handles[hfile];
1292 /***********************************************************************
1293 * DisposeLZ32Handle (KERNEL32.22)
1295 * Note: this is not entirely correct, we should only close the
1296 * 32-bit handle and not the 16-bit one, but we cannot do
1297 * this because of the way our DOS handles are implemented.
1298 * It shouldn't break anything though.
1300 void WINAPI DisposeLZ32Handle( HANDLE handle )
1302 int i;
1304 if (!handle || (handle == INVALID_HANDLE_VALUE)) return;
1306 for (i = 5; i < DOS_TABLE_SIZE; i++)
1307 if (dos_handles[i] == handle)
1309 dos_handles[i] = 0;
1310 CloseHandle( handle );
1311 break;
1316 /***********************************************************************
1317 * FILE_Dup2
1319 * dup2() function for DOS handles.
1321 HFILE16 FILE_Dup2( HFILE16 hFile1, HFILE16 hFile2 )
1323 HANDLE new_handle;
1325 if (hFile1 < 5 && !dos_handles[hFile1]) FILE_InitProcessDosHandles();
1327 if ((hFile1 >= DOS_TABLE_SIZE) || (hFile2 >= DOS_TABLE_SIZE) || !dos_handles[hFile1])
1329 SetLastError( ERROR_INVALID_HANDLE );
1330 return HFILE_ERROR16;
1332 if (hFile2 < 5)
1334 FIXME("stdio handle closed, need proper conversion\n" );
1335 SetLastError( ERROR_INVALID_HANDLE );
1336 return HFILE_ERROR16;
1338 if (!DuplicateHandle( GetCurrentProcess(), dos_handles[hFile1],
1339 GetCurrentProcess(), &new_handle,
1340 0, FALSE, DUPLICATE_SAME_ACCESS ))
1341 return HFILE_ERROR16;
1342 if (dos_handles[hFile2]) CloseHandle( dos_handles[hFile2] );
1343 dos_handles[hFile2] = new_handle;
1344 return hFile2;
1348 /***********************************************************************
1349 * _lclose (KERNEL.81)
1351 HFILE16 WINAPI _lclose16( HFILE16 hFile )
1353 if (hFile < 5)
1355 FIXME("stdio handle closed, need proper conversion\n" );
1356 SetLastError( ERROR_INVALID_HANDLE );
1357 return HFILE_ERROR16;
1359 if ((hFile >= DOS_TABLE_SIZE) || !dos_handles[hFile])
1361 SetLastError( ERROR_INVALID_HANDLE );
1362 return HFILE_ERROR16;
1364 TRACE("%d (handle32=%d)\n", hFile, dos_handles[hFile] );
1365 CloseHandle( dos_handles[hFile] );
1366 dos_handles[hFile] = 0;
1367 return 0;
1371 /***********************************************************************
1372 * _lclose (KERNEL32.@)
1374 HFILE WINAPI _lclose( HFILE hFile )
1376 TRACE("handle %d\n", hFile );
1377 return CloseHandle( hFile ) ? 0 : HFILE_ERROR;
1380 /***********************************************************************
1381 * GetOverlappedResult (KERNEL32.@)
1383 * Check the result of an Asynchronous data transfer from a file.
1385 * RETURNS
1386 * TRUE on success
1387 * FALSE on failure
1389 * If successful (and relevant) lpTransferred will hold the number of
1390 * bytes transferred during the async operation.
1392 * BUGS
1394 * Currently only works for WaitCommEvent, ReadFile, WriteFile
1395 * with communications ports.
1398 BOOL WINAPI GetOverlappedResult(
1399 HANDLE hFile, /* [in] handle of file to check on */
1400 LPOVERLAPPED lpOverlapped, /* [in/out] pointer to overlapped */
1401 LPDWORD lpTransferred, /* [in/out] number of bytes transferred */
1402 BOOL bWait /* [in] wait for the transfer to complete ? */
1404 DWORD r;
1406 TRACE("(%d %p %p %x)\n", hFile, lpOverlapped, lpTransferred, bWait);
1408 if(lpOverlapped==NULL)
1410 ERR("lpOverlapped was null\n");
1411 return FALSE;
1413 if(!lpOverlapped->hEvent)
1415 ERR("lpOverlapped->hEvent was null\n");
1416 return FALSE;
1419 do {
1420 TRACE("waiting on %p\n",lpOverlapped);
1421 r = WaitForSingleObjectEx(lpOverlapped->hEvent, bWait?INFINITE:0, TRUE);
1422 TRACE("wait on %p returned %ld\n",lpOverlapped,r);
1423 } while (r==STATUS_USER_APC);
1425 if(lpTransferred)
1426 *lpTransferred = lpOverlapped->InternalHigh;
1428 SetLastError ( lpOverlapped->Internal == STATUS_PENDING ?
1429 ERROR_IO_INCOMPLETE : RtlNtStatusToDosError ( lpOverlapped->Internal ) );
1431 return (r==WAIT_OBJECT_0);
1434 /***********************************************************************
1435 * CancelIo (KERNEL32.@)
1437 BOOL WINAPI CancelIo(HANDLE handle)
1439 async_private *ovp,*t;
1441 TRACE("handle = %x\n",handle);
1443 for (ovp = NtCurrentTeb()->pending_list; ovp; ovp = t)
1445 t = ovp->next;
1446 if ( ovp->handle == handle )
1447 cancel_async ( ovp );
1449 WaitForMultipleObjectsEx(0,NULL,FALSE,1,TRUE);
1450 return TRUE;
1453 /***********************************************************************
1454 * FILE_AsyncReadService (INTERNAL)
1456 * This function is called while the client is waiting on the
1457 * server, so we can't make any server calls here.
1459 static void FILE_AsyncReadService(async_private *ovp)
1461 async_fileio *fileio = (async_fileio*) ovp;
1462 LPOVERLAPPED lpOverlapped = fileio->lpOverlapped;
1463 int result, r;
1464 int already = lpOverlapped->InternalHigh;
1466 TRACE("%p %p\n", lpOverlapped, fileio->buffer );
1468 /* check to see if the data is ready (non-blocking) */
1470 if ( fileio->fd_type == FD_TYPE_SOCKET )
1471 result = read (ovp->fd, &fileio->buffer[already], fileio->count - already);
1472 else
1474 result = pread (ovp->fd, &fileio->buffer[already], fileio->count - already,
1475 OVERLAPPED_OFFSET (lpOverlapped) + already);
1476 if ((result < 0) && (errno == ESPIPE))
1477 result = read (ovp->fd, &fileio->buffer[already], fileio->count - already);
1480 if ( (result<0) && ((errno == EAGAIN) || (errno == EINTR)))
1482 TRACE("Deferred read %d\n",errno);
1483 r = STATUS_PENDING;
1484 goto async_end;
1487 /* check to see if the transfer is complete */
1488 if(result<0)
1490 r = FILE_GetNtStatus ();
1491 goto async_end;
1494 lpOverlapped->InternalHigh += result;
1495 TRACE("read %d more bytes %ld/%d so far\n",result,lpOverlapped->InternalHigh,fileio->count);
1497 if(lpOverlapped->InternalHigh >= fileio->count || fileio->fd_type == FD_TYPE_SOCKET )
1498 r = STATUS_SUCCESS;
1499 else
1500 r = STATUS_PENDING;
1502 async_end:
1503 lpOverlapped->Internal = r;
1506 /***********************************************************************
1507 * FILE_ReadFileEx (INTERNAL)
1509 static BOOL FILE_ReadFileEx(HANDLE hFile, LPVOID buffer, DWORD bytesToRead,
1510 LPOVERLAPPED overlapped,
1511 LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine,
1512 HANDLE hEvent)
1514 async_fileio *ovp;
1515 int fd;
1516 int flags;
1517 enum fd_type type;
1519 TRACE("file %d to buf %p num %ld %p func %p\n",
1520 hFile, buffer, bytesToRead, overlapped, lpCompletionRoutine);
1522 /* check that there is an overlapped struct */
1523 if (overlapped==NULL)
1525 SetLastError(ERROR_INVALID_PARAMETER);
1526 return FALSE;
1529 fd = FILE_GetUnixHandleType ( hFile, GENERIC_READ, &type, &flags);
1530 if ( fd < 0 )
1532 WARN ( "Couldn't get FD\n" );
1533 return FALSE;
1536 ovp = (async_fileio*) HeapAlloc(GetProcessHeap(), 0, sizeof (async_fileio));
1537 if(!ovp)
1539 TRACE("HeapAlloc Failed\n");
1540 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1541 goto error;
1544 ovp->async.ops = ( lpCompletionRoutine ? &fileio_async_ops : &fileio_nocomp_async_ops );
1545 ovp->async.handle = hFile;
1546 ovp->async.fd = fd;
1547 ovp->async.type = ASYNC_TYPE_READ;
1548 ovp->async.func = FILE_AsyncReadService;
1549 ovp->async.event = hEvent;
1550 ovp->lpOverlapped = overlapped;
1551 ovp->count = bytesToRead;
1552 ovp->completion_func = lpCompletionRoutine;
1553 ovp->buffer = buffer;
1554 ovp->fd_type = type;
1556 return !register_new_async (&ovp->async);
1558 error:
1559 close (fd);
1560 return FALSE;
1564 /***********************************************************************
1565 * ReadFileEx (KERNEL32.@)
1567 BOOL WINAPI ReadFileEx(HANDLE hFile, LPVOID buffer, DWORD bytesToRead,
1568 LPOVERLAPPED overlapped,
1569 LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
1571 overlapped->InternalHigh = 0;
1572 return FILE_ReadFileEx(hFile,buffer,bytesToRead,overlapped,lpCompletionRoutine, INVALID_HANDLE_VALUE);
1575 static BOOL FILE_TimeoutRead(HANDLE hFile, LPVOID buffer, DWORD bytesToRead, LPDWORD bytesRead)
1577 OVERLAPPED ov;
1578 BOOL r = FALSE;
1580 TRACE("%d %p %ld %p\n", hFile, buffer, bytesToRead, bytesRead );
1582 ZeroMemory(&ov, sizeof (OVERLAPPED));
1583 if(STATUS_SUCCESS==NtCreateEvent(&ov.hEvent, SYNCHRONIZE, NULL, 0, 0))
1585 if(FILE_ReadFileEx(hFile, buffer, bytesToRead, &ov, NULL, ov.hEvent))
1587 r = GetOverlappedResult(hFile, &ov, bytesRead, TRUE);
1590 CloseHandle(ov.hEvent);
1591 return r;
1594 /***********************************************************************
1595 * ReadFile (KERNEL32.@)
1597 BOOL WINAPI ReadFile( HANDLE hFile, LPVOID buffer, DWORD bytesToRead,
1598 LPDWORD bytesRead, LPOVERLAPPED overlapped )
1600 int unix_handle, result, flags;
1601 enum fd_type type;
1603 TRACE("%d %p %ld %p %p\n", hFile, buffer, bytesToRead,
1604 bytesRead, overlapped );
1606 if (bytesRead) *bytesRead = 0; /* Do this before anything else */
1607 if (!bytesToRead) return TRUE;
1609 unix_handle = FILE_GetUnixHandleType( hFile, GENERIC_READ, &type, &flags );
1611 if (flags & FD_FLAG_OVERLAPPED)
1613 if (unix_handle == -1) return FALSE;
1614 if ( (overlapped==NULL) || NtResetEvent( overlapped->hEvent, NULL ) )
1616 TRACE("Overlapped not specified or invalid event flag\n");
1617 close(unix_handle);
1618 SetLastError(ERROR_INVALID_PARAMETER);
1619 return FALSE;
1622 close(unix_handle);
1623 overlapped->InternalHigh = 0;
1625 if(!FILE_ReadFileEx(hFile, buffer, bytesToRead, overlapped, NULL, overlapped->hEvent))
1626 return FALSE;
1628 if ( !GetOverlappedResult (hFile, overlapped, bytesRead, FALSE) )
1630 if ( GetLastError() == ERROR_IO_INCOMPLETE )
1631 SetLastError ( ERROR_IO_PENDING );
1632 return FALSE;
1635 return TRUE;
1637 if (flags & FD_FLAG_TIMEOUT)
1639 close(unix_handle);
1640 return FILE_TimeoutRead(hFile, buffer, bytesToRead, bytesRead);
1642 switch(type)
1644 case FD_TYPE_SMB:
1645 return SMB_ReadFile(hFile, buffer, bytesToRead, bytesRead, NULL);
1646 case FD_TYPE_CONSOLE:
1647 return ReadConsoleA(hFile, buffer, bytesToRead, bytesRead, NULL);
1649 default:
1650 /* normal unix files */
1651 if (unix_handle == -1)
1652 return FALSE;
1653 if (overlapped)
1655 close(unix_handle);
1656 SetLastError(ERROR_INVALID_PARAMETER);
1657 return FALSE;
1659 break;
1662 /* code for synchronous reads */
1663 while ((result = read( unix_handle, buffer, bytesToRead )) == -1)
1665 if ((errno == EAGAIN) || (errno == EINTR)) continue;
1666 if ((errno == EFAULT) && !IsBadWritePtr( buffer, bytesToRead )) continue;
1667 FILE_SetDosError();
1668 break;
1670 close( unix_handle );
1671 if (result == -1) return FALSE;
1672 if (bytesRead) *bytesRead = result;
1673 return TRUE;
1677 /***********************************************************************
1678 * FILE_AsyncWriteService (INTERNAL)
1680 * This function is called while the client is waiting on the
1681 * server, so we can't make any server calls here.
1683 static void FILE_AsyncWriteService(struct async_private *ovp)
1685 async_fileio *fileio = (async_fileio *) ovp;
1686 LPOVERLAPPED lpOverlapped = fileio->lpOverlapped;
1687 int result, r;
1688 int already = lpOverlapped->InternalHigh;
1690 TRACE("(%p %p)\n",lpOverlapped,fileio->buffer);
1692 /* write some data (non-blocking) */
1694 if ( fileio->fd_type == FD_TYPE_SOCKET )
1695 result = write(ovp->fd, &fileio->buffer[already], fileio->count - already);
1696 else
1698 result = pwrite(ovp->fd, &fileio->buffer[already], fileio->count - already,
1699 OVERLAPPED_OFFSET (lpOverlapped) + already);
1700 if ((result < 0) && (errno == ESPIPE))
1701 result = write(ovp->fd, &fileio->buffer[already], fileio->count - already);
1704 if ( (result<0) && ((errno == EAGAIN) || (errno == EINTR)))
1706 r = STATUS_PENDING;
1707 goto async_end;
1710 /* check to see if the transfer is complete */
1711 if(result<0)
1713 r = FILE_GetNtStatus ();
1714 goto async_end;
1717 lpOverlapped->InternalHigh += result;
1719 TRACE("wrote %d more bytes %ld/%d so far\n",result,lpOverlapped->InternalHigh,fileio->count);
1721 if(lpOverlapped->InternalHigh < fileio->count)
1722 r = STATUS_PENDING;
1723 else
1724 r = STATUS_SUCCESS;
1726 async_end:
1727 lpOverlapped->Internal = r;
1730 /***********************************************************************
1731 * FILE_WriteFileEx
1733 static BOOL FILE_WriteFileEx(HANDLE hFile, LPCVOID buffer, DWORD bytesToWrite,
1734 LPOVERLAPPED overlapped,
1735 LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine,
1736 HANDLE hEvent)
1738 async_fileio *ovp;
1739 int fd;
1740 int flags;
1741 enum fd_type type;
1743 TRACE("file %d to buf %p num %ld %p func %p handle %d\n",
1744 hFile, buffer, bytesToWrite, overlapped, lpCompletionRoutine, hEvent);
1746 if (overlapped == NULL)
1748 SetLastError(ERROR_INVALID_PARAMETER);
1749 return FALSE;
1752 fd = FILE_GetUnixHandleType ( hFile, GENERIC_WRITE, &type, &flags );
1753 if ( fd < 0 )
1755 TRACE( "Couldn't get FD\n" );
1756 return FALSE;
1759 ovp = (async_fileio*) HeapAlloc(GetProcessHeap(), 0, sizeof (async_fileio));
1760 if(!ovp)
1762 TRACE("HeapAlloc Failed\n");
1763 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1764 goto error;
1767 ovp->async.ops = ( lpCompletionRoutine ? &fileio_async_ops : &fileio_nocomp_async_ops );
1768 ovp->async.handle = hFile;
1769 ovp->async.fd = fd;
1770 ovp->async.type = ASYNC_TYPE_WRITE;
1771 ovp->async.func = FILE_AsyncWriteService;
1772 ovp->lpOverlapped = overlapped;
1773 ovp->async.event = hEvent;
1774 ovp->buffer = (LPVOID) buffer;
1775 ovp->count = bytesToWrite;
1776 ovp->completion_func = lpCompletionRoutine;
1777 ovp->fd_type = type;
1779 return !register_new_async (&ovp->async);
1781 error:
1782 close (fd);
1783 return FALSE;
1786 /***********************************************************************
1787 * WriteFileEx (KERNEL32.@)
1789 BOOL WINAPI WriteFileEx(HANDLE hFile, LPCVOID buffer, DWORD bytesToWrite,
1790 LPOVERLAPPED overlapped,
1791 LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
1793 overlapped->InternalHigh = 0;
1795 return FILE_WriteFileEx(hFile, buffer, bytesToWrite, overlapped, lpCompletionRoutine, INVALID_HANDLE_VALUE);
1798 /***********************************************************************
1799 * WriteFile (KERNEL32.@)
1801 BOOL WINAPI WriteFile( HANDLE hFile, LPCVOID buffer, DWORD bytesToWrite,
1802 LPDWORD bytesWritten, LPOVERLAPPED overlapped )
1804 int unix_handle, result, flags;
1805 enum fd_type type;
1807 TRACE("%d %p %ld %p %p\n", hFile, buffer, bytesToWrite,
1808 bytesWritten, overlapped );
1810 if (bytesWritten) *bytesWritten = 0; /* Do this before anything else */
1811 if (!bytesToWrite) return TRUE;
1813 unix_handle = FILE_GetUnixHandleType( hFile, GENERIC_WRITE, &type, &flags );
1815 if (flags & FD_FLAG_OVERLAPPED)
1817 if (unix_handle == -1) return FALSE;
1818 if ( (overlapped==NULL) || NtResetEvent( overlapped->hEvent, NULL ) )
1820 TRACE("Overlapped not specified or invalid event flag\n");
1821 close(unix_handle);
1822 SetLastError(ERROR_INVALID_PARAMETER);
1823 return FALSE;
1826 close(unix_handle);
1827 overlapped->InternalHigh = 0;
1829 if(!FILE_WriteFileEx(hFile, buffer, bytesToWrite, overlapped, NULL, overlapped->hEvent))
1830 return FALSE;
1832 if ( !GetOverlappedResult (hFile, overlapped, bytesWritten, FALSE) )
1834 if ( GetLastError() == ERROR_IO_INCOMPLETE )
1835 SetLastError ( ERROR_IO_PENDING );
1836 return FALSE;
1839 return TRUE;
1842 switch(type)
1844 case FD_TYPE_CONSOLE:
1845 TRACE("%d %s %ld %p %p\n", hFile, debugstr_an(buffer, bytesToWrite), bytesToWrite,
1846 bytesWritten, overlapped );
1847 return WriteConsoleA(hFile, buffer, bytesToWrite, bytesWritten, NULL);
1848 default:
1849 if (unix_handle == -1)
1850 return FALSE;
1853 /* synchronous file write */
1854 while ((result = write( unix_handle, buffer, bytesToWrite )) == -1)
1856 if ((errno == EAGAIN) || (errno == EINTR)) continue;
1857 if ((errno == EFAULT) && !IsBadReadPtr( buffer, bytesToWrite )) continue;
1858 if (errno == ENOSPC)
1859 SetLastError( ERROR_DISK_FULL );
1860 else
1861 FILE_SetDosError();
1862 break;
1864 close( unix_handle );
1865 if (result == -1) return FALSE;
1866 if (bytesWritten) *bytesWritten = result;
1867 return TRUE;
1871 /***********************************************************************
1872 * _hread (KERNEL.349)
1874 LONG WINAPI WIN16_hread( HFILE16 hFile, SEGPTR buffer, LONG count )
1876 LONG maxlen;
1878 TRACE("%d %08lx %ld\n",
1879 hFile, (DWORD)buffer, count );
1881 /* Some programs pass a count larger than the allocated buffer */
1882 maxlen = GetSelectorLimit16( SELECTOROF(buffer) ) - OFFSETOF(buffer) + 1;
1883 if (count > maxlen) count = maxlen;
1884 return _lread(DosFileHandleToWin32Handle(hFile), MapSL(buffer), count );
1888 /***********************************************************************
1889 * _lread (KERNEL.82)
1891 UINT16 WINAPI WIN16_lread( HFILE16 hFile, SEGPTR buffer, UINT16 count )
1893 return (UINT16)WIN16_hread( hFile, buffer, (LONG)count );
1897 /***********************************************************************
1898 * _lread (KERNEL32.@)
1900 UINT WINAPI _lread( HFILE handle, LPVOID buffer, UINT count )
1902 DWORD result;
1903 if (!ReadFile( handle, buffer, count, &result, NULL )) return -1;
1904 return result;
1908 /***********************************************************************
1909 * _lread16 (KERNEL.82)
1911 UINT16 WINAPI _lread16( HFILE16 hFile, LPVOID buffer, UINT16 count )
1913 return (UINT16)_lread(DosFileHandleToWin32Handle(hFile), buffer, (LONG)count );
1917 /***********************************************************************
1918 * _lcreat (KERNEL.83)
1920 HFILE16 WINAPI _lcreat16( LPCSTR path, INT16 attr )
1922 return Win32HandleToDosFileHandle( _lcreat( path, attr ) );
1926 /***********************************************************************
1927 * _lcreat (KERNEL32.@)
1929 HFILE WINAPI _lcreat( LPCSTR path, INT attr )
1931 /* Mask off all flags not explicitly allowed by the doc */
1932 attr &= FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM;
1933 TRACE("%s %02x\n", path, attr );
1934 return CreateFileA( path, GENERIC_READ | GENERIC_WRITE,
1935 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
1936 CREATE_ALWAYS, attr, 0 );
1940 /***********************************************************************
1941 * SetFilePointer (KERNEL32.@)
1943 DWORD WINAPI SetFilePointer( HANDLE hFile, LONG distance, LONG *highword,
1944 DWORD method )
1946 DWORD ret = 0xffffffff;
1948 TRACE("handle %d offset %ld high %ld origin %ld\n",
1949 hFile, distance, highword?*highword:0, method );
1951 SERVER_START_REQ( set_file_pointer )
1953 req->handle = hFile;
1954 req->low = distance;
1955 req->high = highword ? *highword : (distance >= 0) ? 0 : -1;
1956 /* FIXME: assumes 1:1 mapping between Windows and Unix seek constants */
1957 req->whence = method;
1958 SetLastError( 0 );
1959 if (!wine_server_call_err( req ))
1961 ret = reply->new_low;
1962 if (highword) *highword = reply->new_high;
1965 SERVER_END_REQ;
1966 return ret;
1970 /***********************************************************************
1971 * _llseek (KERNEL.84)
1973 * FIXME:
1974 * Seeking before the start of the file should be allowed for _llseek16,
1975 * but cause subsequent I/O operations to fail (cf. interrupt list)
1978 LONG WINAPI _llseek16( HFILE16 hFile, LONG lOffset, INT16 nOrigin )
1980 return SetFilePointer( DosFileHandleToWin32Handle(hFile), lOffset, NULL, nOrigin );
1984 /***********************************************************************
1985 * _llseek (KERNEL32.@)
1987 LONG WINAPI _llseek( HFILE hFile, LONG lOffset, INT nOrigin )
1989 return SetFilePointer( hFile, lOffset, NULL, nOrigin );
1993 /***********************************************************************
1994 * _lopen (KERNEL.85)
1996 HFILE16 WINAPI _lopen16( LPCSTR path, INT16 mode )
1998 return Win32HandleToDosFileHandle( _lopen( path, mode ) );
2002 /***********************************************************************
2003 * _lopen (KERNEL32.@)
2005 HFILE WINAPI _lopen( LPCSTR path, INT mode )
2007 DWORD access, sharing;
2009 TRACE("('%s',%04x)\n", path, mode );
2010 FILE_ConvertOFMode( mode, &access, &sharing );
2011 return CreateFileA( path, access, sharing, NULL, OPEN_EXISTING, 0, 0 );
2015 /***********************************************************************
2016 * _lwrite (KERNEL.86)
2018 UINT16 WINAPI _lwrite16( HFILE16 hFile, LPCSTR buffer, UINT16 count )
2020 return (UINT16)_hwrite( DosFileHandleToWin32Handle(hFile), buffer, (LONG)count );
2023 /***********************************************************************
2024 * _lwrite (KERNEL32.@)
2026 UINT WINAPI _lwrite( HFILE hFile, LPCSTR buffer, UINT count )
2028 return (UINT)_hwrite( hFile, buffer, (LONG)count );
2032 /***********************************************************************
2033 * _hread16 (KERNEL.349)
2035 LONG WINAPI _hread16( HFILE16 hFile, LPVOID buffer, LONG count)
2037 return _lread( DosFileHandleToWin32Handle(hFile), buffer, count );
2041 /***********************************************************************
2042 * _hread (KERNEL32.@)
2044 LONG WINAPI _hread( HFILE hFile, LPVOID buffer, LONG count)
2046 return _lread( hFile, buffer, count );
2050 /***********************************************************************
2051 * _hwrite (KERNEL.350)
2053 LONG WINAPI _hwrite16( HFILE16 hFile, LPCSTR buffer, LONG count )
2055 return _hwrite( DosFileHandleToWin32Handle(hFile), buffer, count );
2059 /***********************************************************************
2060 * _hwrite (KERNEL32.@)
2062 * experimentation yields that _lwrite:
2063 * o truncates the file at the current position with
2064 * a 0 len write
2065 * o returns 0 on a 0 length write
2066 * o works with console handles
2069 LONG WINAPI _hwrite( HFILE handle, LPCSTR buffer, LONG count )
2071 DWORD result;
2073 TRACE("%d %p %ld\n", handle, buffer, count );
2075 if (!count)
2077 /* Expand or truncate at current position */
2078 if (!SetEndOfFile( handle )) return HFILE_ERROR;
2079 return 0;
2081 if (!WriteFile( handle, buffer, count, &result, NULL ))
2082 return HFILE_ERROR;
2083 return result;
2087 /***********************************************************************
2088 * SetHandleCount (KERNEL.199)
2090 UINT16 WINAPI SetHandleCount16( UINT16 count )
2092 return SetHandleCount( count );
2096 /*************************************************************************
2097 * SetHandleCount (KERNEL32.@)
2099 UINT WINAPI SetHandleCount( UINT count )
2101 return min( 256, count );
2105 /***********************************************************************
2106 * FlushFileBuffers (KERNEL32.@)
2108 BOOL WINAPI FlushFileBuffers( HANDLE hFile )
2110 BOOL ret;
2111 SERVER_START_REQ( flush_file )
2113 req->handle = hFile;
2114 ret = !wine_server_call_err( req );
2116 SERVER_END_REQ;
2117 return ret;
2121 /**************************************************************************
2122 * SetEndOfFile (KERNEL32.@)
2124 BOOL WINAPI SetEndOfFile( HANDLE hFile )
2126 BOOL ret;
2127 SERVER_START_REQ( truncate_file )
2129 req->handle = hFile;
2130 ret = !wine_server_call_err( req );
2132 SERVER_END_REQ;
2133 return ret;
2137 /***********************************************************************
2138 * DeleteFile (KERNEL.146)
2140 BOOL16 WINAPI DeleteFile16( LPCSTR path )
2142 return DeleteFileA( path );
2146 /***********************************************************************
2147 * DeleteFileA (KERNEL32.@)
2149 BOOL WINAPI DeleteFileA( LPCSTR path )
2151 DOS_FULL_NAME full_name;
2152 HANDLE hFile;
2154 if (!path)
2156 SetLastError(ERROR_INVALID_PARAMETER);
2157 return FALSE;
2159 TRACE("'%s'\n", path );
2161 if (!*path)
2163 ERR("Empty path passed\n");
2164 return FALSE;
2166 if (DOSFS_GetDevice( path ))
2168 WARN("cannot remove DOS device '%s'!\n", path);
2169 SetLastError( ERROR_FILE_NOT_FOUND );
2170 return FALSE;
2173 if (!DOSFS_GetFullName( path, TRUE, &full_name )) return FALSE;
2175 /* check if we are allowed to delete the source */
2176 hFile = FILE_CreateFile( full_name.long_name, GENERIC_READ|GENERIC_WRITE, 0,
2177 NULL, OPEN_EXISTING, 0, 0, TRUE,
2178 GetDriveTypeA( full_name.short_name ) );
2179 if (!hFile) return FALSE;
2181 if (unlink( full_name.long_name ) == -1)
2183 FILE_SetDosError();
2184 CloseHandle(hFile);
2185 return FALSE;
2187 CloseHandle(hFile);
2188 return TRUE;
2192 /***********************************************************************
2193 * DeleteFileW (KERNEL32.@)
2195 BOOL WINAPI DeleteFileW( LPCWSTR path )
2197 LPSTR xpath = HEAP_strdupWtoA( GetProcessHeap(), 0, path );
2198 BOOL ret = DeleteFileA( xpath );
2199 HeapFree( GetProcessHeap(), 0, xpath );
2200 return ret;
2204 /***********************************************************************
2205 * GetFileType (KERNEL32.@)
2207 DWORD WINAPI GetFileType( HANDLE hFile )
2209 DWORD ret = FILE_TYPE_UNKNOWN;
2210 SERVER_START_REQ( get_file_info )
2212 req->handle = hFile;
2213 if (!wine_server_call_err( req )) ret = reply->type;
2215 SERVER_END_REQ;
2216 return ret;
2220 /* check if a file name is for an executable file (.exe or .com) */
2221 inline static BOOL is_executable( const char *name )
2223 int len = strlen(name);
2225 if (len < 4) return FALSE;
2226 return (!strcasecmp( name + len - 4, ".exe" ) ||
2227 !strcasecmp( name + len - 4, ".com" ));
2231 /***********************************************************************
2232 * FILE_AddBootRenameEntry
2234 * Adds an entry to the registry that is loaded when windows boots and
2235 * checks if there are some files to be removed or renamed/moved.
2236 * <fn1> has to be valid and <fn2> may be NULL. If both pointers are
2237 * non-NULL then the file is moved, otherwise it is deleted. The
2238 * entry of the registrykey is always appended with two zero
2239 * terminated strings. If <fn2> is NULL then the second entry is
2240 * simply a single 0-byte. Otherwise the second filename goes
2241 * there. The entries are prepended with \??\ before the path and the
2242 * second filename gets also a '!' as the first character if
2243 * MOVEFILE_REPLACE_EXISTING is set. After the final string another
2244 * 0-byte follows to indicate the end of the strings.
2245 * i.e.:
2246 * \??\D:\test\file1[0]
2247 * !\??\D:\test\file1_renamed[0]
2248 * \??\D:\Test|delete[0]
2249 * [0] <- file is to be deleted, second string empty
2250 * \??\D:\test\file2[0]
2251 * !\??\D:\test\file2_renamed[0]
2252 * [0] <- indicates end of strings
2254 * or:
2255 * \??\D:\test\file1[0]
2256 * !\??\D:\test\file1_renamed[0]
2257 * \??\D:\Test|delete[0]
2258 * [0] <- file is to be deleted, second string empty
2259 * [0] <- indicates end of strings
2262 static BOOL FILE_AddBootRenameEntry( const char *fn1, const char *fn2, DWORD flags )
2264 static const char PreString[] = "\\??\\";
2265 static const char ValueName[] = "PendingFileRenameOperations";
2267 BOOL rc = FALSE;
2268 HKEY Reboot = 0;
2269 DWORD Type, len1, len2, l;
2270 DWORD DataSize = 0;
2271 BYTE *Buffer = NULL;
2273 if(RegCreateKeyA(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Control\\Session Manager",
2274 &Reboot) != ERROR_SUCCESS)
2276 WARN("Error creating key for reboot managment [%s]\n",
2277 "SYSTEM\\CurrentControlSet\\Control\\Session Manager");
2278 return FALSE;
2281 l = strlen(PreString);
2282 len1 = strlen(fn1) + l + 1;
2283 if (fn2)
2285 len2 = strlen(fn2) + l + 1;
2286 if (flags & MOVEFILE_REPLACE_EXISTING) len2++; /* Plus 1 because of the leading '!' */
2288 else len2 = 1; /* minimum is the 0 byte for the empty second string */
2290 /* First we check if the key exists and if so how many bytes it already contains. */
2291 if (RegQueryValueExA(Reboot, ValueName, NULL, &Type, NULL, &DataSize) == ERROR_SUCCESS)
2293 if (Type != REG_MULTI_SZ) goto Quit;
2294 if (!(Buffer = HeapAlloc( GetProcessHeap(), 0, DataSize + len1 + len2 + 1 ))) goto Quit;
2295 if (RegQueryValueExA(Reboot, ValueName, NULL, &Type, Buffer, &DataSize) != ERROR_SUCCESS)
2296 goto Quit;
2297 if (DataSize) DataSize--; /* remove terminating null (will be added back later) */
2299 else
2301 if (!(Buffer = HeapAlloc( GetProcessHeap(), 0, len1 + len2 + 1 ))) goto Quit;
2302 DataSize = 0;
2304 sprintf( Buffer + DataSize, "%s%s", PreString, fn1 );
2305 DataSize += len1;
2306 if (fn2)
2308 sprintf( Buffer + DataSize, "%s%s%s",
2309 (flags & MOVEFILE_REPLACE_EXISTING) ? "!" : "", PreString, fn2 );
2310 DataSize += len2;
2312 else Buffer[DataSize++] = 0;
2314 Buffer[DataSize++] = 0; /* add final null */
2315 rc = !RegSetValueExA( Reboot, ValueName, 0, REG_MULTI_SZ, Buffer, DataSize );
2317 Quit:
2318 if (Reboot) RegCloseKey(Reboot);
2319 if (Buffer) HeapFree( GetProcessHeap(), 0, Buffer );
2320 return(rc);
2324 /**************************************************************************
2325 * MoveFileExA (KERNEL32.@)
2327 BOOL WINAPI MoveFileExA( LPCSTR fn1, LPCSTR fn2, DWORD flag )
2329 DOS_FULL_NAME full_name1, full_name2;
2330 HANDLE hFile;
2332 TRACE("(%s,%s,%04lx)\n", fn1, fn2, flag);
2334 /* FIXME: <Gerhard W. Gruber>sparhawk@gmx.at
2335 In case of W9x and lesser this function should return 120 (ERROR_CALL_NOT_IMPLEMENTED)
2336 to be really compatible. Most programs wont have any problems though. In case
2337 you encounter one, this is what you should return here. I don't know what's up
2338 with NT 3.5. Is this function available there or not?
2339 Does anybody really care about 3.5? :)
2342 /* Filename1 has to be always set to a valid path. Filename2 may be NULL
2343 if the source file has to be deleted.
2345 if (!fn1) {
2346 SetLastError(ERROR_INVALID_PARAMETER);
2347 return FALSE;
2350 /* This function has to be run through in order to process the name properly.
2351 If the BOOTDELAY flag is set, the file doesn't need to exist though. At least
2352 that is the behaviour on NT 4.0. The operation accepts the filenames as
2353 they are given but it can't reply with a reasonable returncode. Success
2354 means in that case success for entering the values into the registry.
2356 if(!DOSFS_GetFullName( fn1, TRUE, &full_name1 ))
2358 if(!(flag & MOVEFILE_DELAY_UNTIL_REBOOT))
2359 return FALSE;
2362 if (fn2) /* !fn2 means delete fn1 */
2364 if (DOSFS_GetFullName( fn2, TRUE, &full_name2 ))
2366 if(!(flag & MOVEFILE_DELAY_UNTIL_REBOOT))
2368 /* target exists, check if we may overwrite */
2369 if (!(flag & MOVEFILE_REPLACE_EXISTING))
2371 /* FIXME: Use right error code */
2372 SetLastError( ERROR_ACCESS_DENIED );
2373 return FALSE;
2377 else
2379 if (!DOSFS_GetFullName( fn2, FALSE, &full_name2 ))
2381 if(!(flag & MOVEFILE_DELAY_UNTIL_REBOOT))
2382 return FALSE;
2386 /* Source name and target path are valid */
2388 if (flag & MOVEFILE_DELAY_UNTIL_REBOOT)
2390 /* FIXME: (bon@elektron.ikp.physik.th-darmstadt.de 970706)
2391 Perhaps we should queue these command and execute it
2392 when exiting... What about using on_exit(2)
2394 FIXME("Please move existing file '%s' to file '%s' when Wine has finished\n",
2395 fn1, fn2);
2396 return FILE_AddBootRenameEntry( fn1, fn2, flag );
2399 /* check if we are allowed to delete the source */
2400 hFile = FILE_CreateFile( full_name1.long_name, GENERIC_READ|GENERIC_WRITE, 0,
2401 NULL, OPEN_EXISTING, 0, 0, TRUE,
2402 GetDriveTypeA( full_name1.short_name ) );
2403 if (!hFile) return FALSE;
2404 CloseHandle(hFile);
2406 /* check, if we are allowed to delete the destination,
2407 ** (but the file not being there is fine) */
2408 hFile = FILE_CreateFile( full_name2.long_name, GENERIC_READ|GENERIC_WRITE, 0,
2409 NULL, OPEN_EXISTING, 0, 0, TRUE,
2410 GetDriveTypeA( full_name2.short_name ) );
2411 if(!hFile && GetLastError() != ERROR_FILE_NOT_FOUND) return FALSE;
2412 CloseHandle(hFile);
2414 if (full_name1.drive != full_name2.drive)
2416 /* use copy, if allowed */
2417 if (!(flag & MOVEFILE_COPY_ALLOWED))
2419 /* FIXME: Use right error code */
2420 SetLastError( ERROR_FILE_EXISTS );
2421 return FALSE;
2423 return CopyFileA( fn1, fn2, !(flag & MOVEFILE_REPLACE_EXISTING) );
2425 if (rename( full_name1.long_name, full_name2.long_name ) == -1)
2427 FILE_SetDosError();
2428 return FALSE;
2430 if (is_executable( full_name1.long_name ) != is_executable( full_name2.long_name ))
2432 struct stat fstat;
2433 if (stat( full_name2.long_name, &fstat ) != -1)
2435 if (is_executable( full_name2.long_name ))
2436 /* set executable bit where read bit is set */
2437 fstat.st_mode |= (fstat.st_mode & 0444) >> 2;
2438 else
2439 fstat.st_mode &= ~0111;
2440 chmod( full_name2.long_name, fstat.st_mode );
2443 return TRUE;
2445 else /* fn2 == NULL means delete source */
2447 if (flag & MOVEFILE_DELAY_UNTIL_REBOOT)
2449 if (flag & MOVEFILE_COPY_ALLOWED) {
2450 WARN("Illegal flag\n");
2451 SetLastError( ERROR_GEN_FAILURE );
2452 return FALSE;
2454 /* FIXME: (bon@elektron.ikp.physik.th-darmstadt.de 970706)
2455 Perhaps we should queue these command and execute it
2456 when exiting... What about using on_exit(2)
2458 FIXME("Please delete file '%s' when Wine has finished\n", fn1);
2459 return FILE_AddBootRenameEntry( fn1, NULL, flag );
2462 if (unlink( full_name1.long_name ) == -1)
2464 FILE_SetDosError();
2465 return FALSE;
2467 return TRUE; /* successfully deleted */
2471 /**************************************************************************
2472 * MoveFileExW (KERNEL32.@)
2474 BOOL WINAPI MoveFileExW( LPCWSTR fn1, LPCWSTR fn2, DWORD flag )
2476 LPSTR afn1 = HEAP_strdupWtoA( GetProcessHeap(), 0, fn1 );
2477 LPSTR afn2 = HEAP_strdupWtoA( GetProcessHeap(), 0, fn2 );
2478 BOOL res = MoveFileExA( afn1, afn2, flag );
2479 HeapFree( GetProcessHeap(), 0, afn1 );
2480 HeapFree( GetProcessHeap(), 0, afn2 );
2481 return res;
2485 /**************************************************************************
2486 * MoveFileA (KERNEL32.@)
2488 * Move file or directory
2490 BOOL WINAPI MoveFileA( LPCSTR fn1, LPCSTR fn2 )
2492 DOS_FULL_NAME full_name1, full_name2;
2493 struct stat fstat;
2495 TRACE("(%s,%s)\n", fn1, fn2 );
2497 if (!DOSFS_GetFullName( fn1, TRUE, &full_name1 )) return FALSE;
2498 if (DOSFS_GetFullName( fn2, TRUE, &full_name2 )) {
2499 /* The new name must not already exist */
2500 SetLastError(ERROR_ALREADY_EXISTS);
2501 return FALSE;
2503 if (!DOSFS_GetFullName( fn2, FALSE, &full_name2 )) return FALSE;
2505 if (full_name1.drive == full_name2.drive) /* move */
2506 return MoveFileExA( fn1, fn2, MOVEFILE_COPY_ALLOWED );
2508 /* copy */
2509 if (stat( full_name1.long_name, &fstat ))
2511 WARN("Invalid source file %s\n",
2512 full_name1.long_name);
2513 FILE_SetDosError();
2514 return FALSE;
2516 if (S_ISDIR(fstat.st_mode)) {
2517 /* No Move for directories across file systems */
2518 /* FIXME: Use right error code */
2519 SetLastError( ERROR_GEN_FAILURE );
2520 return FALSE;
2522 return CopyFileA(fn1, fn2, TRUE); /*fail, if exist */
2526 /**************************************************************************
2527 * MoveFileW (KERNEL32.@)
2529 BOOL WINAPI MoveFileW( LPCWSTR fn1, LPCWSTR fn2 )
2531 LPSTR afn1 = HEAP_strdupWtoA( GetProcessHeap(), 0, fn1 );
2532 LPSTR afn2 = HEAP_strdupWtoA( GetProcessHeap(), 0, fn2 );
2533 BOOL res = MoveFileA( afn1, afn2 );
2534 HeapFree( GetProcessHeap(), 0, afn1 );
2535 HeapFree( GetProcessHeap(), 0, afn2 );
2536 return res;
2540 /**************************************************************************
2541 * CopyFileA (KERNEL32.@)
2543 BOOL WINAPI CopyFileA( LPCSTR source, LPCSTR dest, BOOL fail_if_exists )
2545 HFILE h1, h2;
2546 BY_HANDLE_FILE_INFORMATION info;
2547 UINT count;
2548 BOOL ret = FALSE;
2549 int mode;
2550 char buffer[2048];
2552 if ((h1 = _lopen( source, OF_READ )) == HFILE_ERROR) return FALSE;
2553 if (!GetFileInformationByHandle( h1, &info ))
2555 CloseHandle( h1 );
2556 return FALSE;
2558 mode = (info.dwFileAttributes & FILE_ATTRIBUTE_READONLY) ? 0444 : 0666;
2559 if ((h2 = CreateFileA( dest, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
2560 fail_if_exists ? CREATE_NEW : CREATE_ALWAYS,
2561 info.dwFileAttributes, h1 )) == INVALID_HANDLE_VALUE)
2563 CloseHandle( h1 );
2564 return FALSE;
2566 while ((count = _lread( h1, buffer, sizeof(buffer) )) > 0)
2568 char *p = buffer;
2569 while (count > 0)
2571 INT res = _lwrite( h2, p, count );
2572 if (res <= 0) goto done;
2573 p += res;
2574 count -= res;
2577 ret = TRUE;
2578 done:
2579 CloseHandle( h1 );
2580 CloseHandle( h2 );
2581 return ret;
2585 /**************************************************************************
2586 * CopyFileW (KERNEL32.@)
2588 BOOL WINAPI CopyFileW( LPCWSTR source, LPCWSTR dest, BOOL fail_if_exists)
2590 LPSTR sourceA = HEAP_strdupWtoA( GetProcessHeap(), 0, source );
2591 LPSTR destA = HEAP_strdupWtoA( GetProcessHeap(), 0, dest );
2592 BOOL ret = CopyFileA( sourceA, destA, fail_if_exists );
2593 HeapFree( GetProcessHeap(), 0, sourceA );
2594 HeapFree( GetProcessHeap(), 0, destA );
2595 return ret;
2599 /**************************************************************************
2600 * CopyFileExA (KERNEL32.@)
2602 * This implementation ignores most of the extra parameters passed-in into
2603 * the "ex" version of the method and calls the CopyFile method.
2604 * It will have to be fixed eventually.
2606 BOOL WINAPI CopyFileExA(LPCSTR sourceFilename,
2607 LPCSTR destFilename,
2608 LPPROGRESS_ROUTINE progressRoutine,
2609 LPVOID appData,
2610 LPBOOL cancelFlagPointer,
2611 DWORD copyFlags)
2613 BOOL failIfExists = FALSE;
2616 * Interpret the only flag that CopyFile can interpret.
2618 if ( (copyFlags & COPY_FILE_FAIL_IF_EXISTS) != 0)
2620 failIfExists = TRUE;
2623 return CopyFileA(sourceFilename, destFilename, failIfExists);
2626 /**************************************************************************
2627 * CopyFileExW (KERNEL32.@)
2629 BOOL WINAPI CopyFileExW(LPCWSTR sourceFilename,
2630 LPCWSTR destFilename,
2631 LPPROGRESS_ROUTINE progressRoutine,
2632 LPVOID appData,
2633 LPBOOL cancelFlagPointer,
2634 DWORD copyFlags)
2636 LPSTR sourceA = HEAP_strdupWtoA( GetProcessHeap(), 0, sourceFilename );
2637 LPSTR destA = HEAP_strdupWtoA( GetProcessHeap(), 0, destFilename );
2639 BOOL ret = CopyFileExA(sourceA,
2640 destA,
2641 progressRoutine,
2642 appData,
2643 cancelFlagPointer,
2644 copyFlags);
2646 HeapFree( GetProcessHeap(), 0, sourceA );
2647 HeapFree( GetProcessHeap(), 0, destA );
2649 return ret;
2653 /***********************************************************************
2654 * SetFileTime (KERNEL32.@)
2656 BOOL WINAPI SetFileTime( HANDLE hFile,
2657 const FILETIME *lpCreationTime,
2658 const FILETIME *lpLastAccessTime,
2659 const FILETIME *lpLastWriteTime )
2661 BOOL ret;
2662 SERVER_START_REQ( set_file_time )
2664 req->handle = hFile;
2665 if (lpLastAccessTime)
2666 RtlTimeToSecondsSince1970( lpLastAccessTime, (DWORD *)&req->access_time );
2667 else
2668 req->access_time = 0; /* FIXME */
2669 if (lpLastWriteTime)
2670 RtlTimeToSecondsSince1970( lpLastWriteTime, (DWORD *)&req->write_time );
2671 else
2672 req->write_time = 0; /* FIXME */
2673 ret = !wine_server_call_err( req );
2675 SERVER_END_REQ;
2676 return ret;
2680 /**************************************************************************
2681 * LockFile (KERNEL32.@)
2683 BOOL WINAPI LockFile( HANDLE hFile, DWORD dwFileOffsetLow, DWORD dwFileOffsetHigh,
2684 DWORD nNumberOfBytesToLockLow, DWORD nNumberOfBytesToLockHigh )
2686 BOOL ret;
2687 SERVER_START_REQ( lock_file )
2689 req->handle = hFile;
2690 req->offset_low = dwFileOffsetLow;
2691 req->offset_high = dwFileOffsetHigh;
2692 req->count_low = nNumberOfBytesToLockLow;
2693 req->count_high = nNumberOfBytesToLockHigh;
2694 ret = !wine_server_call_err( req );
2696 SERVER_END_REQ;
2697 return ret;
2700 /**************************************************************************
2701 * LockFileEx [KERNEL32.@]
2703 * Locks a byte range within an open file for shared or exclusive access.
2705 * RETURNS
2706 * success: TRUE
2707 * failure: FALSE
2709 * NOTES
2710 * Per Microsoft docs, the third parameter (reserved) must be set to 0.
2712 BOOL WINAPI LockFileEx( HANDLE hFile, DWORD flags, DWORD reserved,
2713 DWORD nNumberOfBytesToLockLow, DWORD nNumberOfBytesToLockHigh,
2714 LPOVERLAPPED pOverlapped )
2716 FIXME("hFile=%d,flags=%ld,reserved=%ld,lowbytes=%ld,highbytes=%ld,overlapped=%p: stub.\n",
2717 hFile, flags, reserved, nNumberOfBytesToLockLow, nNumberOfBytesToLockHigh,
2718 pOverlapped);
2719 if (reserved == 0)
2720 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2721 else
2723 ERR("reserved == %ld: Supposed to be 0??\n", reserved);
2724 SetLastError(ERROR_INVALID_PARAMETER);
2727 return FALSE;
2731 /**************************************************************************
2732 * UnlockFile (KERNEL32.@)
2734 BOOL WINAPI UnlockFile( HANDLE hFile, DWORD dwFileOffsetLow, DWORD dwFileOffsetHigh,
2735 DWORD nNumberOfBytesToUnlockLow, DWORD nNumberOfBytesToUnlockHigh )
2737 BOOL ret;
2738 SERVER_START_REQ( unlock_file )
2740 req->handle = hFile;
2741 req->offset_low = dwFileOffsetLow;
2742 req->offset_high = dwFileOffsetHigh;
2743 req->count_low = nNumberOfBytesToUnlockLow;
2744 req->count_high = nNumberOfBytesToUnlockHigh;
2745 ret = !wine_server_call_err( req );
2747 SERVER_END_REQ;
2748 return ret;
2752 /**************************************************************************
2753 * UnlockFileEx (KERNEL32.@)
2755 BOOL WINAPI UnlockFileEx(
2756 HFILE hFile,
2757 DWORD dwReserved,
2758 DWORD nNumberOfBytesToUnlockLow,
2759 DWORD nNumberOfBytesToUnlockHigh,
2760 LPOVERLAPPED lpOverlapped
2763 FIXME("hFile=%d,reserved=%ld,lowbytes=%ld,highbytes=%ld,overlapped=%p: stub.\n",
2764 hFile, dwReserved, nNumberOfBytesToUnlockLow, nNumberOfBytesToUnlockHigh,
2765 lpOverlapped);
2766 if (dwReserved == 0)
2767 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2768 else
2770 ERR("reserved == %ld: Supposed to be 0??\n", dwReserved);
2771 SetLastError(ERROR_INVALID_PARAMETER);
2774 return FALSE;
2778 #if 0
2780 struct DOS_FILE_LOCK {
2781 struct DOS_FILE_LOCK * next;
2782 DWORD base;
2783 DWORD len;
2784 DWORD processId;
2785 FILE_OBJECT * dos_file;
2786 /* char * unix_name;*/
2789 typedef struct DOS_FILE_LOCK DOS_FILE_LOCK;
2791 static DOS_FILE_LOCK *locks = NULL;
2792 static void DOS_RemoveFileLocks(FILE_OBJECT *file);
2795 /* Locks need to be mirrored because unix file locking is based
2796 * on the pid. Inside of wine there can be multiple WINE processes
2797 * that share the same unix pid.
2798 * Read's and writes should check these locks also - not sure
2799 * how critical that is at this point (FIXME).
2802 static BOOL DOS_AddLock(FILE_OBJECT *file, struct flock *f)
2804 DOS_FILE_LOCK *curr;
2805 DWORD processId;
2807 processId = GetCurrentProcessId();
2809 /* check if lock overlaps a current lock for the same file */
2810 #if 0
2811 for (curr = locks; curr; curr = curr->next) {
2812 if (strcmp(curr->unix_name, file->unix_name) == 0) {
2813 if ((f->l_start == curr->base) && (f->l_len == curr->len))
2814 return TRUE;/* region is identic */
2815 if ((f->l_start < (curr->base + curr->len)) &&
2816 ((f->l_start + f->l_len) > curr->base)) {
2817 /* region overlaps */
2818 return FALSE;
2822 #endif
2824 curr = HeapAlloc( GetProcessHeap(), 0, sizeof(DOS_FILE_LOCK) );
2825 curr->processId = GetCurrentProcessId();
2826 curr->base = f->l_start;
2827 curr->len = f->l_len;
2828 /* curr->unix_name = HEAP_strdupA( GetProcessHeap(), 0, file->unix_name);*/
2829 curr->next = locks;
2830 curr->dos_file = file;
2831 locks = curr;
2832 return TRUE;
2835 static void DOS_RemoveFileLocks(FILE_OBJECT *file)
2837 DWORD processId;
2838 DOS_FILE_LOCK **curr;
2839 DOS_FILE_LOCK *rem;
2841 processId = GetCurrentProcessId();
2842 curr = &locks;
2843 while (*curr) {
2844 if ((*curr)->dos_file == file) {
2845 rem = *curr;
2846 *curr = (*curr)->next;
2847 /* HeapFree( GetProcessHeap(), 0, rem->unix_name );*/
2848 HeapFree( GetProcessHeap(), 0, rem );
2850 else
2851 curr = &(*curr)->next;
2855 static BOOL DOS_RemoveLock(FILE_OBJECT *file, struct flock *f)
2857 DWORD processId;
2858 DOS_FILE_LOCK **curr;
2859 DOS_FILE_LOCK *rem;
2861 processId = GetCurrentProcessId();
2862 for (curr = &locks; *curr; curr = &(*curr)->next) {
2863 if ((*curr)->processId == processId &&
2864 (*curr)->dos_file == file &&
2865 (*curr)->base == f->l_start &&
2866 (*curr)->len == f->l_len) {
2867 /* this is the same lock */
2868 rem = *curr;
2869 *curr = (*curr)->next;
2870 /* HeapFree( GetProcessHeap(), 0, rem->unix_name );*/
2871 HeapFree( GetProcessHeap(), 0, rem );
2872 return TRUE;
2875 /* no matching lock found */
2876 return FALSE;
2880 /**************************************************************************
2881 * LockFile (KERNEL32.@)
2883 BOOL WINAPI LockFile(
2884 HFILE hFile,DWORD dwFileOffsetLow,DWORD dwFileOffsetHigh,
2885 DWORD nNumberOfBytesToLockLow,DWORD nNumberOfBytesToLockHigh )
2887 struct flock f;
2888 FILE_OBJECT *file;
2890 TRACE("handle %d offsetlow=%ld offsethigh=%ld nbyteslow=%ld nbyteshigh=%ld\n",
2891 hFile, dwFileOffsetLow, dwFileOffsetHigh,
2892 nNumberOfBytesToLockLow, nNumberOfBytesToLockHigh);
2894 if (dwFileOffsetHigh || nNumberOfBytesToLockHigh) {
2895 FIXME("Unimplemented bytes > 32bits\n");
2896 return FALSE;
2899 f.l_start = dwFileOffsetLow;
2900 f.l_len = nNumberOfBytesToLockLow;
2901 f.l_whence = SEEK_SET;
2902 f.l_pid = 0;
2903 f.l_type = F_WRLCK;
2905 if (!(file = FILE_GetFile(hFile,0,NULL))) return FALSE;
2907 /* shadow locks internally */
2908 if (!DOS_AddLock(file, &f)) {
2909 SetLastError( ERROR_LOCK_VIOLATION );
2910 return FALSE;
2913 /* FIXME: Unix locking commented out for now, doesn't work with Excel */
2914 #ifdef USE_UNIX_LOCKS
2915 if (fcntl(file->unix_handle, F_SETLK, &f) == -1) {
2916 if (errno == EACCES || errno == EAGAIN) {
2917 SetLastError( ERROR_LOCK_VIOLATION );
2919 else {
2920 FILE_SetDosError();
2922 /* remove our internal copy of the lock */
2923 DOS_RemoveLock(file, &f);
2924 return FALSE;
2926 #endif
2927 return TRUE;
2931 /**************************************************************************
2932 * UnlockFile (KERNEL32.@)
2934 BOOL WINAPI UnlockFile(
2935 HFILE hFile,DWORD dwFileOffsetLow,DWORD dwFileOffsetHigh,
2936 DWORD nNumberOfBytesToUnlockLow,DWORD nNumberOfBytesToUnlockHigh )
2938 FILE_OBJECT *file;
2939 struct flock f;
2941 TRACE("handle %d offsetlow=%ld offsethigh=%ld nbyteslow=%ld nbyteshigh=%ld\n",
2942 hFile, dwFileOffsetLow, dwFileOffsetHigh,
2943 nNumberOfBytesToUnlockLow, nNumberOfBytesToUnlockHigh);
2945 if (dwFileOffsetHigh || nNumberOfBytesToUnlockHigh) {
2946 WARN("Unimplemented bytes > 32bits\n");
2947 return FALSE;
2950 f.l_start = dwFileOffsetLow;
2951 f.l_len = nNumberOfBytesToUnlockLow;
2952 f.l_whence = SEEK_SET;
2953 f.l_pid = 0;
2954 f.l_type = F_UNLCK;
2956 if (!(file = FILE_GetFile(hFile,0,NULL))) return FALSE;
2958 DOS_RemoveLock(file, &f); /* ok if fails - may be another wine */
2960 /* FIXME: Unix locking commented out for now, doesn't work with Excel */
2961 #ifdef USE_UNIX_LOCKS
2962 if (fcntl(file->unix_handle, F_SETLK, &f) == -1) {
2963 FILE_SetDosError();
2964 return FALSE;
2966 #endif
2967 return TRUE;
2969 #endif
2971 /**************************************************************************
2972 * GetFileAttributesExA [KERNEL32.@]
2974 BOOL WINAPI GetFileAttributesExA(
2975 LPCSTR lpFileName, GET_FILEEX_INFO_LEVELS fInfoLevelId,
2976 LPVOID lpFileInformation)
2978 DOS_FULL_NAME full_name;
2979 BY_HANDLE_FILE_INFORMATION info;
2981 if (lpFileName == NULL) return FALSE;
2982 if (lpFileInformation == NULL) return FALSE;
2984 if (fInfoLevelId == GetFileExInfoStandard) {
2985 LPWIN32_FILE_ATTRIBUTE_DATA lpFad =
2986 (LPWIN32_FILE_ATTRIBUTE_DATA) lpFileInformation;
2987 if (!DOSFS_GetFullName( lpFileName, TRUE, &full_name )) return FALSE;
2988 if (!FILE_Stat( full_name.long_name, &info )) return FALSE;
2990 lpFad->dwFileAttributes = info.dwFileAttributes;
2991 lpFad->ftCreationTime = info.ftCreationTime;
2992 lpFad->ftLastAccessTime = info.ftLastAccessTime;
2993 lpFad->ftLastWriteTime = info.ftLastWriteTime;
2994 lpFad->nFileSizeHigh = info.nFileSizeHigh;
2995 lpFad->nFileSizeLow = info.nFileSizeLow;
2997 else {
2998 FIXME("invalid info level %d!\n", fInfoLevelId);
2999 return FALSE;
3002 return TRUE;
3006 /**************************************************************************
3007 * GetFileAttributesExW [KERNEL32.@]
3009 BOOL WINAPI GetFileAttributesExW(
3010 LPCWSTR lpFileName, GET_FILEEX_INFO_LEVELS fInfoLevelId,
3011 LPVOID lpFileInformation)
3013 LPSTR nameA = HEAP_strdupWtoA( GetProcessHeap(), 0, lpFileName );
3014 BOOL res =
3015 GetFileAttributesExA( nameA, fInfoLevelId, lpFileInformation);
3016 HeapFree( GetProcessHeap(), 0, nameA );
3017 return res;