Automatically detect whether the entry point is main or WinMain
[wine/multimedia.git] / files / file.c
blobb57b9a040204fdbb03257e3fb00e5286dd50f753
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 <stdarg.h>
35 #include <stdio.h>
36 #include <string.h>
37 #ifdef HAVE_SYS_ERRNO_H
38 #include <sys/errno.h>
39 #endif
40 #include <sys/types.h>
41 #include <sys/stat.h>
42 #ifdef HAVE_SYS_MMAN_H
43 #include <sys/mman.h>
44 #endif
45 #ifdef HAVE_SYS_TIME_H
46 # include <sys/time.h>
47 #endif
48 #ifdef HAVE_SYS_POLL_H
49 # include <sys/poll.h>
50 #endif
51 #include <time.h>
52 #ifdef HAVE_UNISTD_H
53 # include <unistd.h>
54 #endif
55 #ifdef HAVE_UTIME_H
56 # include <utime.h>
57 #endif
59 #define NONAMELESSUNION
60 #define NONAMELESSSTRUCT
61 #include "winerror.h"
62 #include "ntstatus.h"
63 #include "windef.h"
64 #include "winbase.h"
65 #include "winreg.h"
66 #include "winternl.h"
67 #include "wine/winbase16.h"
68 #include "wine/server.h"
70 #include "file.h"
71 #include "wincon.h"
72 #include "kernel_private.h"
74 #include "smb.h"
75 #include "wine/unicode.h"
76 #include "wine/debug.h"
78 WINE_DEFAULT_DEBUG_CHANNEL(file);
80 #if defined(MAP_ANONYMOUS) && !defined(MAP_ANON)
81 #define MAP_ANON MAP_ANONYMOUS
82 #endif
84 #define IS_OPTION_TRUE(ch) ((ch) == 'y' || (ch) == 'Y' || (ch) == 't' || (ch) == 'T' || (ch) == '1')
86 #define SECSPERDAY 86400
87 #define SECS_1601_TO_1970 ((369 * 365 + 89) * (ULONGLONG)SECSPERDAY)
89 mode_t FILE_umask;
91 /***********************************************************************
92 * FILE_ConvertOFMode
94 * Convert OF_* mode into flags for CreateFile.
96 void FILE_ConvertOFMode( INT mode, DWORD *access, DWORD *sharing )
98 switch(mode & 0x03)
100 case OF_READ: *access = GENERIC_READ; break;
101 case OF_WRITE: *access = GENERIC_WRITE; break;
102 case OF_READWRITE: *access = GENERIC_READ | GENERIC_WRITE; break;
103 default: *access = 0; break;
105 switch(mode & 0x70)
107 case OF_SHARE_EXCLUSIVE: *sharing = 0; break;
108 case OF_SHARE_DENY_WRITE: *sharing = FILE_SHARE_READ; break;
109 case OF_SHARE_DENY_READ: *sharing = FILE_SHARE_WRITE; break;
110 case OF_SHARE_DENY_NONE:
111 case OF_SHARE_COMPAT:
112 default: *sharing = FILE_SHARE_READ | FILE_SHARE_WRITE; break;
117 /***********************************************************************
118 * FILE_SetDosError
120 * Set the DOS error code from errno.
122 void FILE_SetDosError(void)
124 int save_errno = errno; /* errno gets overwritten by printf */
126 TRACE("errno = %d %s\n", errno, strerror(errno));
127 switch (save_errno)
129 case EAGAIN:
130 SetLastError( ERROR_SHARING_VIOLATION );
131 break;
132 case EBADF:
133 SetLastError( ERROR_INVALID_HANDLE );
134 break;
135 case ENOSPC:
136 SetLastError( ERROR_HANDLE_DISK_FULL );
137 break;
138 case EACCES:
139 case EPERM:
140 case EROFS:
141 SetLastError( ERROR_ACCESS_DENIED );
142 break;
143 case EBUSY:
144 SetLastError( ERROR_LOCK_VIOLATION );
145 break;
146 case ENOENT:
147 SetLastError( ERROR_FILE_NOT_FOUND );
148 break;
149 case EISDIR:
150 SetLastError( ERROR_CANNOT_MAKE );
151 break;
152 case ENFILE:
153 case EMFILE:
154 SetLastError( ERROR_NO_MORE_FILES );
155 break;
156 case EEXIST:
157 SetLastError( ERROR_FILE_EXISTS );
158 break;
159 case EINVAL:
160 case ESPIPE:
161 SetLastError( ERROR_SEEK );
162 break;
163 case ENOTEMPTY:
164 SetLastError( ERROR_DIR_NOT_EMPTY );
165 break;
166 case ENOEXEC:
167 SetLastError( ERROR_BAD_FORMAT );
168 break;
169 default:
170 WARN("unknown file error: %s\n", strerror(save_errno) );
171 SetLastError( ERROR_GEN_FAILURE );
172 break;
174 errno = save_errno;
178 /***********************************************************************
179 * FILE_CreateFile
181 * Implementation of CreateFile. Takes a Unix path name.
182 * Returns 0 on failure.
184 HANDLE FILE_CreateFile( LPCSTR filename, DWORD access, DWORD sharing,
185 LPSECURITY_ATTRIBUTES sa, DWORD creation,
186 DWORD attributes, HANDLE template )
188 unsigned int err;
189 UINT disp, options;
190 HANDLE ret;
192 switch (creation)
194 case CREATE_ALWAYS: disp = FILE_OVERWRITE_IF; break;
195 case CREATE_NEW: disp = FILE_CREATE; break;
196 case OPEN_ALWAYS: disp = FILE_OPEN_IF; break;
197 case OPEN_EXISTING: disp = FILE_OPEN; break;
198 case TRUNCATE_EXISTING: disp = FILE_OVERWRITE; break;
199 default:
200 SetLastError( ERROR_INVALID_PARAMETER );
201 return 0;
204 options = 0;
205 if (attributes & FILE_FLAG_BACKUP_SEMANTICS)
206 options |= FILE_OPEN_FOR_BACKUP_INTENT;
207 if (attributes & FILE_FLAG_DELETE_ON_CLOSE)
208 options |= FILE_DELETE_ON_CLOSE;
209 if (!(attributes & FILE_FLAG_OVERLAPPED))
210 options |= FILE_SYNCHRONOUS_IO_ALERT;
211 if (attributes & FILE_FLAG_RANDOM_ACCESS)
212 options |= FILE_RANDOM_ACCESS;
213 attributes &= FILE_ATTRIBUTE_VALID_FLAGS;
215 SERVER_START_REQ( create_file )
217 req->access = access;
218 req->inherit = (sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle);
219 req->sharing = sharing;
220 req->create = disp;
221 req->options = options;
222 req->attrs = attributes;
223 wine_server_add_data( req, filename, strlen(filename) );
224 SetLastError(0);
225 err = wine_server_call( req );
226 ret = reply->handle;
228 SERVER_END_REQ;
230 if (err)
232 /* In the case file creation was rejected due to CREATE_NEW flag
233 * was specified and file with that name already exists, correct
234 * last error is ERROR_FILE_EXISTS and not ERROR_ALREADY_EXISTS.
235 * Note: RtlNtStatusToDosError is not the subject to blame here.
237 if (err == STATUS_OBJECT_NAME_COLLISION)
238 SetLastError( ERROR_FILE_EXISTS );
239 else
240 SetLastError( RtlNtStatusToDosError(err) );
243 if (!ret) WARN("Unable to create file '%s' (GLE %ld)\n", filename, GetLastError());
244 return ret;
248 static HANDLE FILE_OpenPipe(LPCWSTR name, DWORD access, LPSECURITY_ATTRIBUTES sa )
250 HANDLE ret;
251 DWORD len = 0;
253 if (name && (len = strlenW(name)) > MAX_PATH)
255 SetLastError( ERROR_FILENAME_EXCED_RANGE );
256 return 0;
258 SERVER_START_REQ( open_named_pipe )
260 req->access = access;
261 req->inherit = (sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle);
262 SetLastError(0);
263 wine_server_add_data( req, name, len * sizeof(WCHAR) );
264 wine_server_call_err( req );
265 ret = reply->handle;
267 SERVER_END_REQ;
268 TRACE("Returned %p\n",ret);
269 return ret;
272 /*************************************************************************
273 * CreateFileW [KERNEL32.@] Creates or opens a file or other object
275 * Creates or opens an object, and returns a handle that can be used to
276 * access that object.
278 * PARAMS
280 * filename [in] pointer to filename to be accessed
281 * access [in] access mode requested
282 * sharing [in] share mode
283 * sa [in] pointer to security attributes
284 * creation [in] how to create the file
285 * attributes [in] attributes for newly created file
286 * template [in] handle to file with extended attributes to copy
288 * RETURNS
289 * Success: Open handle to specified file
290 * Failure: INVALID_HANDLE_VALUE
292 * NOTES
293 * Should call SetLastError() on failure.
295 * BUGS
297 * Doesn't support character devices, template files, or a
298 * lot of the 'attributes' flags yet.
300 HANDLE WINAPI CreateFileW( LPCWSTR filename, DWORD access, DWORD sharing,
301 LPSECURITY_ATTRIBUTES sa, DWORD creation,
302 DWORD attributes, HANDLE template )
304 DOS_FULL_NAME full_name;
305 HANDLE ret;
306 DWORD dosdev;
307 static const WCHAR bkslashes_with_question_markW[] = {'\\','\\','?','\\',0};
308 static const WCHAR bkslashes_with_dotW[] = {'\\','\\','.','\\',0};
309 static const WCHAR bkslashesW[] = {'\\','\\',0};
310 static const WCHAR coninW[] = {'C','O','N','I','N','$',0};
311 static const WCHAR conoutW[] = {'C','O','N','O','U','T','$',0};
313 if (!filename || !filename[0])
315 SetLastError( ERROR_PATH_NOT_FOUND );
316 return INVALID_HANDLE_VALUE;
319 TRACE("%s %s%s%s%s%s%s%s attributes 0x%lx\n", debugstr_w(filename),
320 ((access & GENERIC_READ)==GENERIC_READ)?"GENERIC_READ ":"",
321 ((access & GENERIC_WRITE)==GENERIC_WRITE)?"GENERIC_WRITE ":"",
322 (!access)?"QUERY_ACCESS ":"",
323 ((sharing & FILE_SHARE_READ)==FILE_SHARE_READ)?"FILE_SHARE_READ ":"",
324 ((sharing & FILE_SHARE_WRITE)==FILE_SHARE_WRITE)?"FILE_SHARE_WRITE ":"",
325 ((sharing & FILE_SHARE_DELETE)==FILE_SHARE_DELETE)?"FILE_SHARE_DELETE ":"",
326 (creation ==CREATE_NEW)?"CREATE_NEW":
327 (creation ==CREATE_ALWAYS)?"CREATE_ALWAYS ":
328 (creation ==OPEN_EXISTING)?"OPEN_EXISTING ":
329 (creation ==OPEN_ALWAYS)?"OPEN_ALWAYS ":
330 (creation ==TRUNCATE_EXISTING)?"TRUNCATE_EXISTING ":"", attributes);
332 /* Open a console for CONIN$ or CONOUT$ */
333 if (!strcmpiW(filename, coninW) || !strcmpiW(filename, conoutW))
335 ret = OpenConsoleW(filename, access, (sa && sa->bInheritHandle), creation);
336 goto done;
339 /* If the name starts with '\\?\', ignore the first 4 chars. */
340 if (!strncmpW(filename, bkslashes_with_question_markW, 4))
342 static const WCHAR uncW[] = {'U','N','C','\\',0};
343 filename += 4;
344 if (!strncmpiW(filename, uncW, 4))
346 FIXME("UNC name (%s) not supported.\n", debugstr_w(filename) );
347 SetLastError( ERROR_PATH_NOT_FOUND );
348 return INVALID_HANDLE_VALUE;
352 if (!strncmpW(filename, bkslashes_with_dotW, 4))
354 static const WCHAR pipeW[] = {'P','I','P','E','\\',0};
355 if(!strncmpiW(filename + 4, pipeW, 5))
357 TRACE("Opening a pipe: %s\n", debugstr_w(filename));
358 ret = FILE_OpenPipe( filename, access, sa );
359 goto done;
361 else if (isalphaW(filename[4]) && filename[5] == ':' && filename[6] == '\0')
363 const char *device = DRIVE_GetDevice( toupperW(filename[4]) - 'A' );
364 if (device)
366 ret = FILE_CreateFile( device, access, sharing, sa, creation,
367 attributes, template );
369 else
371 SetLastError( ERROR_ACCESS_DENIED );
372 ret = INVALID_HANDLE_VALUE;
374 goto done;
376 else if ((dosdev = RtlIsDosDeviceName_U( filename + 4 )))
378 dosdev += MAKELONG( 0, 4*sizeof(WCHAR) ); /* adjust position to start of filename */
380 else
382 ret = VXD_Open( filename+4, access, sa );
383 goto done;
386 else dosdev = RtlIsDosDeviceName_U( filename );
388 if (dosdev)
390 static const WCHAR conW[] = {'C','O','N',0};
391 WCHAR dev[5];
393 memcpy( dev, filename + HIWORD(dosdev)/sizeof(WCHAR), LOWORD(dosdev) );
394 dev[LOWORD(dosdev)/sizeof(WCHAR)] = 0;
396 TRACE("opening device %s\n", debugstr_w(dev) );
398 if (!strcmpiW( dev, conW ))
400 switch (access & (GENERIC_READ|GENERIC_WRITE))
402 case GENERIC_READ:
403 ret = OpenConsoleW(coninW, access, (sa && sa->bInheritHandle), creation);
404 goto done;
405 case GENERIC_WRITE:
406 ret = OpenConsoleW(conoutW, access, (sa && sa->bInheritHandle), creation);
407 goto done;
408 default:
409 FIXME("can't open CON read/write\n");
410 SetLastError( ERROR_FILE_NOT_FOUND );
411 return INVALID_HANDLE_VALUE;
415 ret = VOLUME_OpenDevice( dev, access, sharing, sa, attributes );
416 goto done;
419 /* If the name still starts with '\\', it's a UNC name. */
420 if (!strncmpW(filename, bkslashesW, 2))
422 ret = SMB_CreateFileW(filename, access, sharing, sa, creation, attributes, template );
423 goto done;
426 /* If the name contains a DOS wild card (* or ?), do no create a file */
427 if(strchrW(filename, '*') || strchrW(filename, '?'))
429 SetLastError(ERROR_BAD_PATHNAME);
430 return INVALID_HANDLE_VALUE;
433 /* check for filename, don't check for last entry if creating */
434 if (!DOSFS_GetFullName( filename,
435 (creation == OPEN_EXISTING) ||
436 (creation == TRUNCATE_EXISTING),
437 &full_name )) {
438 WARN("Unable to get full filename from %s (GLE %ld)\n",
439 debugstr_w(filename), GetLastError());
440 return INVALID_HANDLE_VALUE;
443 ret = FILE_CreateFile( full_name.long_name, access, sharing,
444 sa, creation, attributes, template );
445 done:
446 if (!ret) ret = INVALID_HANDLE_VALUE;
447 TRACE("returning %p\n", ret);
448 return ret;
453 /*************************************************************************
454 * CreateFileA (KERNEL32.@)
456 HANDLE WINAPI CreateFileA( LPCSTR filename, DWORD access, DWORD sharing,
457 LPSECURITY_ATTRIBUTES sa, DWORD creation,
458 DWORD attributes, HANDLE template)
460 UNICODE_STRING filenameW;
461 HANDLE ret = INVALID_HANDLE_VALUE;
463 if (!filename)
465 SetLastError( ERROR_INVALID_PARAMETER );
466 return INVALID_HANDLE_VALUE;
469 if (RtlCreateUnicodeStringFromAsciiz(&filenameW, filename))
471 ret = CreateFileW(filenameW.Buffer, access, sharing, sa, creation,
472 attributes, template);
473 RtlFreeUnicodeString(&filenameW);
475 else
476 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
477 return ret;
481 /***********************************************************************
482 * FILE_FillInfo
484 * Fill a file information from a struct stat.
486 static void FILE_FillInfo( struct stat *st, BY_HANDLE_FILE_INFORMATION *info )
488 if (S_ISDIR(st->st_mode))
489 info->dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
490 else
491 info->dwFileAttributes = FILE_ATTRIBUTE_ARCHIVE;
492 if (!(st->st_mode & S_IWUSR))
493 info->dwFileAttributes |= FILE_ATTRIBUTE_READONLY;
495 RtlSecondsSince1970ToTime( st->st_mtime, (LARGE_INTEGER *)&info->ftCreationTime );
496 RtlSecondsSince1970ToTime( st->st_mtime, (LARGE_INTEGER *)&info->ftLastWriteTime );
497 RtlSecondsSince1970ToTime( st->st_atime, (LARGE_INTEGER *)&info->ftLastAccessTime );
499 info->dwVolumeSerialNumber = 0; /* FIXME */
500 if (S_ISDIR(st->st_mode))
502 info->nFileSizeHigh = 0;
503 info->nFileSizeLow = 0;
504 info->nNumberOfLinks = 1;
506 else
508 info->nFileSizeHigh = st->st_size >> 32;
509 info->nFileSizeLow = (DWORD)st->st_size;
510 info->nNumberOfLinks = st->st_nlink;
512 info->nFileIndexHigh = st->st_ino >> 32;
513 info->nFileIndexLow = (DWORD)st->st_ino;
517 /***********************************************************************
518 * get_show_dot_files_option
520 static BOOL get_show_dot_files_option(void)
522 static const WCHAR WineW[] = {'M','a','c','h','i','n','e','\\',
523 'S','o','f','t','w','a','r','e','\\',
524 'W','i','n','e','\\','W','i','n','e','\\',
525 'C','o','n','f','i','g','\\','W','i','n','e',0};
526 static const WCHAR ShowDotFilesW[] = {'S','h','o','w','D','o','t','F','i','l','e','s',0};
528 char tmp[80];
529 HKEY hkey;
530 DWORD dummy;
531 OBJECT_ATTRIBUTES attr;
532 UNICODE_STRING nameW;
533 BOOL ret = FALSE;
535 attr.Length = sizeof(attr);
536 attr.RootDirectory = 0;
537 attr.ObjectName = &nameW;
538 attr.Attributes = 0;
539 attr.SecurityDescriptor = NULL;
540 attr.SecurityQualityOfService = NULL;
541 RtlInitUnicodeString( &nameW, WineW );
543 if (!NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr ))
545 RtlInitUnicodeString( &nameW, ShowDotFilesW );
546 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
548 WCHAR *str = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
549 ret = IS_OPTION_TRUE( str[0] );
551 NtClose( hkey );
553 return ret;
557 /***********************************************************************
558 * FILE_Stat
560 * Stat a Unix path name. Return TRUE if OK.
562 BOOL FILE_Stat( LPCSTR unixName, BY_HANDLE_FILE_INFORMATION *info, BOOL *is_symlink_ptr )
564 struct stat st;
565 int is_symlink;
566 LPCSTR p;
568 if (lstat( unixName, &st ) == -1)
570 FILE_SetDosError();
571 return FALSE;
573 is_symlink = S_ISLNK(st.st_mode);
574 if (is_symlink)
576 /* do a "real" stat to find out
577 about the type of the symlink destination */
578 if (stat( unixName, &st ) == -1)
580 FILE_SetDosError();
581 return FALSE;
585 /* fill in the information we gathered so far */
586 FILE_FillInfo( &st, info );
588 /* and now see if this is a hidden file, based on the name */
589 p = strrchr( unixName, '/');
590 p = p ? p + 1 : unixName;
591 if (*p == '.' && *(p+1) && (*(p+1) != '.' || *(p+2)))
593 static int show_dot_files = -1;
594 if (show_dot_files == -1)
595 show_dot_files = get_show_dot_files_option();
596 if (!show_dot_files)
597 info->dwFileAttributes |= FILE_ATTRIBUTE_HIDDEN;
599 if (is_symlink_ptr) *is_symlink_ptr = is_symlink;
600 return TRUE;
604 /***********************************************************************
605 * GetFileInformationByHandle (KERNEL32.@)
607 BOOL WINAPI GetFileInformationByHandle( HANDLE hFile, BY_HANDLE_FILE_INFORMATION *info )
609 NTSTATUS status;
610 int fd;
611 BOOL ret = FALSE;
613 TRACE("%p,%p\n", hFile, info);
615 if (!info) return 0;
617 if (!(status = wine_server_handle_to_fd( hFile, 0, &fd, NULL, NULL )))
619 struct stat st;
621 if (fstat( fd, &st ) == -1)
622 FILE_SetDosError();
623 else if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode))
624 SetLastError( ERROR_INVALID_FUNCTION );
625 else
627 FILE_FillInfo( &st, info );
628 ret = TRUE;
630 wine_server_release_fd( hFile, fd );
632 else SetLastError( RtlNtStatusToDosError(status) );
634 return ret;
638 /**************************************************************************
639 * GetFileAttributesW (KERNEL32.@)
641 DWORD WINAPI GetFileAttributesW( LPCWSTR name )
643 DOS_FULL_NAME full_name;
644 BY_HANDLE_FILE_INFORMATION info;
646 if (name == NULL)
648 SetLastError( ERROR_INVALID_PARAMETER );
649 return INVALID_FILE_ATTRIBUTES;
651 if (!DOSFS_GetFullName( name, TRUE, &full_name) )
652 return INVALID_FILE_ATTRIBUTES;
653 if (!FILE_Stat( full_name.long_name, &info, NULL ))
654 return INVALID_FILE_ATTRIBUTES;
655 return info.dwFileAttributes;
659 /**************************************************************************
660 * GetFileAttributesA (KERNEL32.@)
662 DWORD WINAPI GetFileAttributesA( LPCSTR name )
664 UNICODE_STRING nameW;
665 DWORD ret = INVALID_FILE_ATTRIBUTES;
667 if (!name)
669 SetLastError( ERROR_INVALID_PARAMETER );
670 return INVALID_FILE_ATTRIBUTES;
673 if (RtlCreateUnicodeStringFromAsciiz(&nameW, name))
675 ret = GetFileAttributesW(nameW.Buffer);
676 RtlFreeUnicodeString(&nameW);
678 else
679 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
680 return ret;
684 /**************************************************************************
685 * SetFileAttributesW (KERNEL32.@)
687 BOOL WINAPI SetFileAttributesW(LPCWSTR lpFileName, DWORD attributes)
689 struct stat buf;
690 DOS_FULL_NAME full_name;
692 if (!lpFileName)
694 SetLastError( ERROR_INVALID_PARAMETER );
695 return FALSE;
698 TRACE("(%s,%lx)\n", debugstr_w(lpFileName), attributes);
700 if (!DOSFS_GetFullName( lpFileName, TRUE, &full_name ))
701 return FALSE;
703 if(stat(full_name.long_name,&buf)==-1)
705 FILE_SetDosError();
706 return FALSE;
708 if (attributes & FILE_ATTRIBUTE_READONLY)
710 if(S_ISDIR(buf.st_mode))
711 /* FIXME */
712 WARN("FILE_ATTRIBUTE_READONLY ignored for directory.\n");
713 else
714 buf.st_mode &= ~0222; /* octal!, clear write permission bits */
715 attributes &= ~FILE_ATTRIBUTE_READONLY;
717 else
719 /* add write permission */
720 buf.st_mode |= (0600 | ((buf.st_mode & 044) >> 1)) & (~FILE_umask);
722 if (attributes & FILE_ATTRIBUTE_DIRECTORY)
724 if (!S_ISDIR(buf.st_mode))
725 FIXME("SetFileAttributes expected the file %s to be a directory\n",
726 debugstr_w(lpFileName));
727 attributes &= ~FILE_ATTRIBUTE_DIRECTORY;
729 attributes &= ~(FILE_ATTRIBUTE_NORMAL|FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN|
730 FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_TEMPORARY);
731 if (attributes)
732 FIXME("(%s):%lx attribute(s) not implemented.\n", debugstr_w(lpFileName), attributes);
733 if (-1==chmod(full_name.long_name,buf.st_mode))
735 if (GetDriveTypeW(lpFileName) == DRIVE_CDROM)
737 SetLastError( ERROR_ACCESS_DENIED );
738 return FALSE;
742 * FIXME: We don't return FALSE here because of differences between
743 * Linux and Windows privileges. Under Linux only the owner of
744 * the file is allowed to change file attributes. Under Windows,
745 * applications expect that if you can write to a file, you can also
746 * change its attributes (see GENERIC_WRITE). We could try to be
747 * clever here but that would break multi-user installations where
748 * users share read-only DLLs. This is because some installers like
749 * to change attributes of already installed DLLs.
751 FIXME("Couldn't set file attributes for existing file \"%s\".\n"
752 "Check permissions or set VFAT \"quiet\" mount flag\n", full_name.long_name);
754 return TRUE;
758 /**************************************************************************
759 * SetFileAttributesA (KERNEL32.@)
761 BOOL WINAPI SetFileAttributesA(LPCSTR lpFileName, DWORD attributes)
763 UNICODE_STRING filenameW;
764 BOOL ret = FALSE;
766 if (!lpFileName)
768 SetLastError( ERROR_INVALID_PARAMETER );
769 return FALSE;
772 if (RtlCreateUnicodeStringFromAsciiz(&filenameW, lpFileName))
774 ret = SetFileAttributesW(filenameW.Buffer, attributes);
775 RtlFreeUnicodeString(&filenameW);
777 else
778 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
779 return ret;
783 /******************************************************************************
784 * GetCompressedFileSizeA [KERNEL32.@]
786 DWORD WINAPI GetCompressedFileSizeA(
787 LPCSTR lpFileName,
788 LPDWORD lpFileSizeHigh)
790 UNICODE_STRING filenameW;
791 DWORD ret;
793 if (RtlCreateUnicodeStringFromAsciiz(&filenameW, lpFileName))
795 ret = GetCompressedFileSizeW(filenameW.Buffer, lpFileSizeHigh);
796 RtlFreeUnicodeString(&filenameW);
798 else
800 ret = INVALID_FILE_SIZE;
801 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
803 return ret;
807 /******************************************************************************
808 * GetCompressedFileSizeW [KERNEL32.@]
810 * RETURNS
811 * Success: Low-order doubleword of number of bytes
812 * Failure: INVALID_FILE_SIZE
814 DWORD WINAPI GetCompressedFileSizeW(
815 LPCWSTR lpFileName, /* [in] Pointer to name of file */
816 LPDWORD lpFileSizeHigh) /* [out] Receives high-order doubleword of size */
818 DOS_FULL_NAME full_name;
819 struct stat st;
820 DWORD low;
822 TRACE("(%s,%p)\n",debugstr_w(lpFileName),lpFileSizeHigh);
824 if (!DOSFS_GetFullName( lpFileName, TRUE, &full_name )) return INVALID_FILE_SIZE;
825 if (stat(full_name.long_name, &st) != 0)
827 FILE_SetDosError();
828 return INVALID_FILE_SIZE;
830 #if HAVE_STRUCT_STAT_ST_BLOCKS
831 /* blocks are 512 bytes long */
832 if (lpFileSizeHigh) *lpFileSizeHigh = (st.st_blocks >> 23);
833 low = (DWORD)(st.st_blocks << 9);
834 #else
835 if (lpFileSizeHigh) *lpFileSizeHigh = (st.st_size >> 32);
836 low = (DWORD)st.st_size;
837 #endif
838 return low;
842 /***********************************************************************
843 * GetFileTime (KERNEL32.@)
845 BOOL WINAPI GetFileTime( HANDLE hFile, FILETIME *lpCreationTime,
846 FILETIME *lpLastAccessTime,
847 FILETIME *lpLastWriteTime )
849 BY_HANDLE_FILE_INFORMATION info;
850 if (!GetFileInformationByHandle( hFile, &info )) return FALSE;
851 if (lpCreationTime) *lpCreationTime = info.ftCreationTime;
852 if (lpLastAccessTime) *lpLastAccessTime = info.ftLastAccessTime;
853 if (lpLastWriteTime) *lpLastWriteTime = info.ftLastWriteTime;
854 return TRUE;
858 /***********************************************************************
859 * GetTempFileNameA (KERNEL32.@)
861 UINT WINAPI GetTempFileNameA( LPCSTR path, LPCSTR prefix, UINT unique,
862 LPSTR buffer)
864 UNICODE_STRING pathW, prefixW;
865 WCHAR bufferW[MAX_PATH];
866 UINT ret;
868 if ( !path || !prefix || !buffer )
870 SetLastError( ERROR_INVALID_PARAMETER );
871 return 0;
874 RtlCreateUnicodeStringFromAsciiz(&pathW, path);
875 RtlCreateUnicodeStringFromAsciiz(&prefixW, prefix);
877 ret = GetTempFileNameW(pathW.Buffer, prefixW.Buffer, unique, bufferW);
878 if (ret)
879 WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buffer, MAX_PATH, NULL, NULL);
881 RtlFreeUnicodeString(&pathW);
882 RtlFreeUnicodeString(&prefixW);
883 return ret;
886 /***********************************************************************
887 * GetTempFileNameW (KERNEL32.@)
889 UINT WINAPI GetTempFileNameW( LPCWSTR path, LPCWSTR prefix, UINT unique,
890 LPWSTR buffer )
892 static const WCHAR formatW[] = {'%','x','.','t','m','p',0};
894 DOS_FULL_NAME full_name;
895 int i;
896 LPWSTR p;
898 if ( !path || !prefix || !buffer )
900 SetLastError( ERROR_INVALID_PARAMETER );
901 return 0;
904 strcpyW( buffer, path );
905 p = buffer + strlenW(buffer);
907 /* add a \, if there isn't one */
908 if ((p == buffer) || (p[-1] != '\\')) *p++ = '\\';
910 for (i = 3; (i > 0) && (*prefix); i--) *p++ = *prefix++;
912 unique &= 0xffff;
914 if (unique) sprintfW( p, formatW, unique );
915 else
917 /* get a "random" unique number and try to create the file */
918 HANDLE handle;
919 UINT num = GetTickCount() & 0xffff;
921 if (!num) num = 1;
922 unique = num;
925 sprintfW( p, formatW, unique );
926 handle = CreateFileW( buffer, GENERIC_WRITE, 0, NULL,
927 CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0 );
928 if (handle != INVALID_HANDLE_VALUE)
929 { /* We created it */
930 TRACE("created %s\n", debugstr_w(buffer) );
931 CloseHandle( handle );
932 break;
934 if (GetLastError() != ERROR_FILE_EXISTS &&
935 GetLastError() != ERROR_SHARING_VIOLATION)
936 break; /* No need to go on */
937 if (!(++unique & 0xffff)) unique = 1;
938 } while (unique != num);
941 /* Get the full path name */
943 if (DOSFS_GetFullName( buffer, FALSE, &full_name ))
945 char *slash;
946 /* Check if we have write access in the directory */
947 if ((slash = strrchr( full_name.long_name, '/' ))) *slash = '\0';
948 if (access( full_name.long_name, W_OK ) == -1)
949 WARN("returns %s, which doesn't seem to be writeable.\n",
950 debugstr_w(buffer) );
952 TRACE("returning %s\n", debugstr_w(buffer) );
953 return unique;
957 /******************************************************************
958 * FILE_ReadWriteApc (internal)
962 static void WINAPI FILE_ReadWriteApc(void* apc_user, PIO_STATUS_BLOCK io_status, ULONG len)
964 LPOVERLAPPED_COMPLETION_ROUTINE cr = (LPOVERLAPPED_COMPLETION_ROUTINE)apc_user;
966 cr(RtlNtStatusToDosError(io_status->u.Status), len, (LPOVERLAPPED)io_status);
969 /***********************************************************************
970 * ReadFileEx (KERNEL32.@)
972 BOOL WINAPI ReadFileEx(HANDLE hFile, LPVOID buffer, DWORD bytesToRead,
973 LPOVERLAPPED overlapped,
974 LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
976 LARGE_INTEGER offset;
977 NTSTATUS status;
978 PIO_STATUS_BLOCK io_status;
980 if (!overlapped)
982 SetLastError(ERROR_INVALID_PARAMETER);
983 return FALSE;
986 offset.u.LowPart = overlapped->Offset;
987 offset.u.HighPart = overlapped->OffsetHigh;
988 io_status = (PIO_STATUS_BLOCK)overlapped;
989 io_status->u.Status = STATUS_PENDING;
991 status = NtReadFile(hFile, NULL, FILE_ReadWriteApc, lpCompletionRoutine,
992 io_status, buffer, bytesToRead, &offset, NULL);
994 if (status)
996 SetLastError( RtlNtStatusToDosError(status) );
997 return FALSE;
999 return TRUE;
1002 /***********************************************************************
1003 * ReadFile (KERNEL32.@)
1005 BOOL WINAPI ReadFile( HANDLE hFile, LPVOID buffer, DWORD bytesToRead,
1006 LPDWORD bytesRead, LPOVERLAPPED overlapped )
1008 LARGE_INTEGER offset;
1009 PLARGE_INTEGER poffset = NULL;
1010 IO_STATUS_BLOCK iosb;
1011 PIO_STATUS_BLOCK io_status = &iosb;
1012 HANDLE hEvent = 0;
1013 NTSTATUS status;
1015 TRACE("%p %p %ld %p %p\n", hFile, buffer, bytesToRead,
1016 bytesRead, overlapped );
1018 if (bytesRead) *bytesRead = 0; /* Do this before anything else */
1019 if (!bytesToRead) return TRUE;
1021 if (IsBadReadPtr(buffer, bytesToRead))
1023 SetLastError(ERROR_WRITE_FAULT); /* FIXME */
1024 return FALSE;
1026 if (is_console_handle(hFile))
1027 return ReadConsoleA(hFile, buffer, bytesToRead, bytesRead, NULL);
1029 if (overlapped != NULL)
1031 offset.u.LowPart = overlapped->Offset;
1032 offset.u.HighPart = overlapped->OffsetHigh;
1033 poffset = &offset;
1034 hEvent = overlapped->hEvent;
1035 io_status = (PIO_STATUS_BLOCK)overlapped;
1037 io_status->u.Status = STATUS_PENDING;
1038 io_status->Information = 0;
1040 status = NtReadFile(hFile, hEvent, NULL, NULL, io_status, buffer, bytesToRead, poffset, NULL);
1042 if (status != STATUS_PENDING && bytesRead)
1043 *bytesRead = io_status->Information;
1045 if (status && status != STATUS_END_OF_FILE)
1047 SetLastError( RtlNtStatusToDosError(status) );
1048 return FALSE;
1050 return TRUE;
1054 /***********************************************************************
1055 * WriteFileEx (KERNEL32.@)
1057 BOOL WINAPI WriteFileEx(HANDLE hFile, LPCVOID buffer, DWORD bytesToWrite,
1058 LPOVERLAPPED overlapped,
1059 LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
1061 LARGE_INTEGER offset;
1062 NTSTATUS status;
1063 PIO_STATUS_BLOCK io_status;
1065 TRACE("%p %p %ld %p %p\n",
1066 hFile, buffer, bytesToWrite, overlapped, lpCompletionRoutine);
1068 if (overlapped == NULL)
1070 SetLastError(ERROR_INVALID_PARAMETER);
1071 return FALSE;
1073 offset.u.LowPart = overlapped->Offset;
1074 offset.u.HighPart = overlapped->OffsetHigh;
1076 io_status = (PIO_STATUS_BLOCK)overlapped;
1077 io_status->u.Status = STATUS_PENDING;
1079 status = NtWriteFile(hFile, NULL, FILE_ReadWriteApc, lpCompletionRoutine,
1080 io_status, buffer, bytesToWrite, &offset, NULL);
1082 if (status) SetLastError( RtlNtStatusToDosError(status) );
1083 return !status;
1086 /***********************************************************************
1087 * WriteFile (KERNEL32.@)
1089 BOOL WINAPI WriteFile( HANDLE hFile, LPCVOID buffer, DWORD bytesToWrite,
1090 LPDWORD bytesWritten, LPOVERLAPPED overlapped )
1092 HANDLE hEvent = NULL;
1093 LARGE_INTEGER offset;
1094 PLARGE_INTEGER poffset = NULL;
1095 NTSTATUS status;
1096 IO_STATUS_BLOCK iosb;
1097 PIO_STATUS_BLOCK piosb = &iosb;
1099 TRACE("%p %p %ld %p %p\n",
1100 hFile, buffer, bytesToWrite, bytesWritten, overlapped );
1102 if (is_console_handle(hFile))
1103 return WriteConsoleA(hFile, buffer, bytesToWrite, bytesWritten, NULL);
1105 if (IsBadReadPtr(buffer, bytesToWrite))
1107 SetLastError(ERROR_READ_FAULT); /* FIXME */
1108 return FALSE;
1111 if (overlapped)
1113 offset.u.LowPart = overlapped->Offset;
1114 offset.u.HighPart = overlapped->OffsetHigh;
1115 poffset = &offset;
1116 hEvent = overlapped->hEvent;
1117 piosb = (PIO_STATUS_BLOCK)overlapped;
1119 piosb->u.Status = STATUS_PENDING;
1120 piosb->Information = 0;
1122 status = NtWriteFile(hFile, hEvent, NULL, NULL, piosb,
1123 buffer, bytesToWrite, poffset, NULL);
1124 if (status)
1126 SetLastError( RtlNtStatusToDosError(status) );
1127 return FALSE;
1129 if (bytesWritten) *bytesWritten = piosb->Information;
1131 return TRUE;
1135 /***********************************************************************
1136 * SetFilePointer (KERNEL32.@)
1138 DWORD WINAPI SetFilePointer( HANDLE hFile, LONG distance, LONG *highword,
1139 DWORD method )
1141 static const int whence[3] = { SEEK_SET, SEEK_CUR, SEEK_END };
1142 DWORD ret = INVALID_SET_FILE_POINTER;
1143 NTSTATUS status;
1144 int fd;
1146 TRACE("handle %p offset %ld high %ld origin %ld\n",
1147 hFile, distance, highword?*highword:0, method );
1149 if (method > FILE_END)
1151 SetLastError( ERROR_INVALID_PARAMETER );
1152 return ret;
1155 if (!(status = wine_server_handle_to_fd( hFile, 0, &fd, NULL, NULL )))
1157 off_t pos, res;
1159 if (highword) pos = ((off_t)*highword << 32) | (ULONG)distance;
1160 else pos = (off_t)distance;
1161 if ((res = lseek( fd, pos, whence[method] )) == (off_t)-1)
1163 /* also check EPERM due to SuSE7 2.2.16 lseek() EPERM kernel bug */
1164 if (((errno == EINVAL) || (errno == EPERM)) && (method != FILE_BEGIN) && (pos < 0))
1165 SetLastError( ERROR_NEGATIVE_SEEK );
1166 else
1167 FILE_SetDosError();
1169 else
1171 ret = (DWORD)res;
1172 if (highword) *highword = (res >> 32);
1173 if (ret == INVALID_SET_FILE_POINTER) SetLastError( 0 );
1175 wine_server_release_fd( hFile, fd );
1177 else SetLastError( RtlNtStatusToDosError(status) );
1179 return ret;
1183 /*************************************************************************
1184 * SetHandleCount (KERNEL32.@)
1186 UINT WINAPI SetHandleCount( UINT count )
1188 return min( 256, count );
1192 /**************************************************************************
1193 * SetEndOfFile (KERNEL32.@)
1195 BOOL WINAPI SetEndOfFile( HANDLE hFile )
1197 BOOL ret;
1198 SERVER_START_REQ( truncate_file )
1200 req->handle = hFile;
1201 ret = !wine_server_call_err( req );
1203 SERVER_END_REQ;
1204 return ret;
1208 /***********************************************************************
1209 * GetFileType (KERNEL32.@)
1211 DWORD WINAPI GetFileType( HANDLE hFile )
1213 NTSTATUS status;
1214 int fd;
1215 DWORD ret = FILE_TYPE_UNKNOWN;
1217 if (is_console_handle( hFile ))
1218 return FILE_TYPE_CHAR;
1220 if (!(status = wine_server_handle_to_fd( hFile, 0, &fd, NULL, NULL )))
1222 struct stat st;
1224 if (fstat( fd, &st ) == -1)
1225 FILE_SetDosError();
1226 else if (S_ISFIFO(st.st_mode) || S_ISSOCK(st.st_mode))
1227 ret = FILE_TYPE_PIPE;
1228 else if (S_ISCHR(st.st_mode))
1229 ret = FILE_TYPE_CHAR;
1230 else
1231 ret = FILE_TYPE_DISK;
1232 wine_server_release_fd( hFile, fd );
1234 else SetLastError( RtlNtStatusToDosError(status) );
1236 return ret;
1240 /* check if a file name is for an executable file (.exe or .com) */
1241 inline static BOOL is_executable( const char *name )
1243 int len = strlen(name);
1245 if (len < 4) return FALSE;
1246 return (!strcasecmp( name + len - 4, ".exe" ) ||
1247 !strcasecmp( name + len - 4, ".com" ));
1251 /***********************************************************************
1252 * FILE_AddBootRenameEntry
1254 * Adds an entry to the registry that is loaded when windows boots and
1255 * checks if there are some files to be removed or renamed/moved.
1256 * <fn1> has to be valid and <fn2> may be NULL. If both pointers are
1257 * non-NULL then the file is moved, otherwise it is deleted. The
1258 * entry of the registrykey is always appended with two zero
1259 * terminated strings. If <fn2> is NULL then the second entry is
1260 * simply a single 0-byte. Otherwise the second filename goes
1261 * there. The entries are prepended with \??\ before the path and the
1262 * second filename gets also a '!' as the first character if
1263 * MOVEFILE_REPLACE_EXISTING is set. After the final string another
1264 * 0-byte follows to indicate the end of the strings.
1265 * i.e.:
1266 * \??\D:\test\file1[0]
1267 * !\??\D:\test\file1_renamed[0]
1268 * \??\D:\Test|delete[0]
1269 * [0] <- file is to be deleted, second string empty
1270 * \??\D:\test\file2[0]
1271 * !\??\D:\test\file2_renamed[0]
1272 * [0] <- indicates end of strings
1274 * or:
1275 * \??\D:\test\file1[0]
1276 * !\??\D:\test\file1_renamed[0]
1277 * \??\D:\Test|delete[0]
1278 * [0] <- file is to be deleted, second string empty
1279 * [0] <- indicates end of strings
1282 static BOOL FILE_AddBootRenameEntry( LPCWSTR fn1, LPCWSTR fn2, DWORD flags )
1284 static const WCHAR PreString[] = {'\\','?','?','\\',0};
1285 static const WCHAR ValueName[] = {'P','e','n','d','i','n','g',
1286 'F','i','l','e','R','e','n','a','m','e',
1287 'O','p','e','r','a','t','i','o','n','s',0};
1288 static const WCHAR SessionW[] = {'M','a','c','h','i','n','e','\\',
1289 'S','y','s','t','e','m','\\',
1290 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
1291 'C','o','n','t','r','o','l','\\',
1292 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r',0};
1293 static const int info_size = FIELD_OFFSET( KEY_VALUE_PARTIAL_INFORMATION, Data );
1295 OBJECT_ATTRIBUTES attr;
1296 UNICODE_STRING nameW;
1297 KEY_VALUE_PARTIAL_INFORMATION *info;
1298 BOOL rc = FALSE;
1299 HKEY Reboot = 0;
1300 DWORD len0, len1, len2;
1301 DWORD DataSize = 0;
1302 BYTE *Buffer = NULL;
1303 WCHAR *p;
1305 attr.Length = sizeof(attr);
1306 attr.RootDirectory = 0;
1307 attr.ObjectName = &nameW;
1308 attr.Attributes = 0;
1309 attr.SecurityDescriptor = NULL;
1310 attr.SecurityQualityOfService = NULL;
1311 RtlInitUnicodeString( &nameW, SessionW );
1313 if (NtCreateKey( &Reboot, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ) != STATUS_SUCCESS)
1315 WARN("Error creating key for reboot managment [%s]\n",
1316 "SYSTEM\\CurrentControlSet\\Control\\Session Manager");
1317 return FALSE;
1320 len0 = strlenW(PreString);
1321 len1 = strlenW(fn1) + len0 + 1;
1322 if (fn2)
1324 len2 = strlenW(fn2) + len0 + 1;
1325 if (flags & MOVEFILE_REPLACE_EXISTING) len2++; /* Plus 1 because of the leading '!' */
1327 else len2 = 1; /* minimum is the 0 characters for the empty second string */
1329 /* convert characters to bytes */
1330 len0 *= sizeof(WCHAR);
1331 len1 *= sizeof(WCHAR);
1332 len2 *= sizeof(WCHAR);
1334 RtlInitUnicodeString( &nameW, ValueName );
1336 /* First we check if the key exists and if so how many bytes it already contains. */
1337 if (NtQueryValueKey( Reboot, &nameW, KeyValuePartialInformation,
1338 NULL, 0, &DataSize ) == STATUS_BUFFER_OVERFLOW)
1340 if (!(Buffer = HeapAlloc( GetProcessHeap(), 0, DataSize + len1 + len2 + sizeof(WCHAR) )))
1341 goto Quit;
1342 if (NtQueryValueKey( Reboot, &nameW, KeyValuePartialInformation,
1343 Buffer, DataSize, &DataSize )) goto Quit;
1344 info = (KEY_VALUE_PARTIAL_INFORMATION *)Buffer;
1345 if (info->Type != REG_MULTI_SZ) goto Quit;
1346 if (DataSize > sizeof(info)) DataSize -= sizeof(WCHAR); /* remove terminating null (will be added back later) */
1348 else
1350 DataSize = info_size;
1351 if (!(Buffer = HeapAlloc( GetProcessHeap(), 0, DataSize + len1 + len2 + sizeof(WCHAR) )))
1352 goto Quit;
1355 p = (WCHAR *)(Buffer + DataSize);
1356 strcpyW( p, PreString );
1357 strcatW( p, fn1 );
1358 DataSize += len1;
1359 if (fn2)
1361 p = (WCHAR *)(Buffer + DataSize);
1362 if (flags & MOVEFILE_REPLACE_EXISTING)
1363 *p++ = '!';
1364 strcpyW( p, PreString );
1365 strcatW( p, fn2 );
1366 DataSize += len2;
1368 else
1370 p = (WCHAR *)(Buffer + DataSize);
1371 *p = 0;
1372 DataSize += sizeof(WCHAR);
1375 /* add final null */
1376 p = (WCHAR *)(Buffer + DataSize);
1377 *p = 0;
1378 DataSize += sizeof(WCHAR);
1380 rc = !NtSetValueKey(Reboot, &nameW, 0, REG_MULTI_SZ, Buffer + info_size, DataSize - info_size);
1382 Quit:
1383 if (Reboot) NtClose(Reboot);
1384 if (Buffer) HeapFree( GetProcessHeap(), 0, Buffer );
1385 return(rc);
1389 /**************************************************************************
1390 * MoveFileExW (KERNEL32.@)
1392 BOOL WINAPI MoveFileExW( LPCWSTR fn1, LPCWSTR fn2, DWORD flag )
1394 DOS_FULL_NAME full_name1, full_name2;
1395 HANDLE hFile;
1396 DWORD attr = INVALID_FILE_ATTRIBUTES;
1398 TRACE("(%s,%s,%04lx)\n", debugstr_w(fn1), debugstr_w(fn2), flag);
1400 /* FIXME: <Gerhard W. Gruber>sparhawk@gmx.at
1401 In case of W9x and lesser this function should return 120 (ERROR_CALL_NOT_IMPLEMENTED)
1402 to be really compatible. Most programs won't have any problems though. In case
1403 you encounter one, this is what you should return here. I don't know what's up
1404 with NT 3.5. Is this function available there or not?
1405 Does anybody really care about 3.5? :)
1408 /* Filename1 has to be always set to a valid path. Filename2 may be NULL
1409 if the source file has to be deleted.
1411 if (!fn1) {
1412 SetLastError(ERROR_INVALID_PARAMETER);
1413 return FALSE;
1416 /* This function has to be run through in order to process the name properly.
1417 If the BOOTDELAY flag is set, the file doesn't need to exist though. At least
1418 that is the behaviour on NT 4.0. The operation accepts the filenames as
1419 they are given but it can't reply with a reasonable returncode. Success
1420 means in that case success for entering the values into the registry.
1422 if(!DOSFS_GetFullName( fn1, TRUE, &full_name1 ))
1424 if(!(flag & MOVEFILE_DELAY_UNTIL_REBOOT))
1425 return FALSE;
1428 if (fn2) /* !fn2 means delete fn1 */
1430 if (DOSFS_GetFullName( fn2, TRUE, &full_name2 ))
1432 if(!(flag & MOVEFILE_DELAY_UNTIL_REBOOT))
1434 /* target exists, check if we may overwrite */
1435 if (!(flag & MOVEFILE_REPLACE_EXISTING))
1437 SetLastError( ERROR_ALREADY_EXISTS );
1438 return FALSE;
1442 else
1444 if (!DOSFS_GetFullName( fn2, FALSE, &full_name2 ))
1446 if(!(flag & MOVEFILE_DELAY_UNTIL_REBOOT))
1447 return FALSE;
1451 /* Source name and target path are valid */
1453 if (flag & MOVEFILE_DELAY_UNTIL_REBOOT)
1455 return FILE_AddBootRenameEntry( fn1, fn2, flag );
1458 attr = GetFileAttributesW( fn1 );
1459 if ( attr == INVALID_FILE_ATTRIBUTES ) return FALSE;
1461 /* check if we are allowed to rename the source */
1462 hFile = FILE_CreateFile( full_name1.long_name, 0, 0,
1463 NULL, OPEN_EXISTING, 0, 0 );
1464 if (!hFile)
1466 if (GetLastError() != ERROR_ACCESS_DENIED) return FALSE;
1467 if ( !(attr & FILE_ATTRIBUTE_DIRECTORY) ) return FALSE;
1468 /* if it's a directory we can continue */
1470 else CloseHandle(hFile);
1472 /* check, if we are allowed to delete the destination,
1473 ** (but the file not being there is fine) */
1474 hFile = FILE_CreateFile( full_name2.long_name, GENERIC_READ|GENERIC_WRITE, 0,
1475 NULL, OPEN_EXISTING, 0, 0 );
1476 if(!hFile && GetLastError() != ERROR_FILE_NOT_FOUND) return FALSE;
1477 CloseHandle(hFile);
1479 if (full_name1.drive != full_name2.drive)
1481 if (!(flag & MOVEFILE_COPY_ALLOWED))
1483 SetLastError( ERROR_NOT_SAME_DEVICE );
1484 return FALSE;
1486 else if ( attr & FILE_ATTRIBUTE_DIRECTORY )
1488 /* Strange, but that's what Windows returns */
1489 SetLastError ( ERROR_ACCESS_DENIED );
1490 return FALSE;
1493 if (rename( full_name1.long_name, full_name2.long_name ) == -1)
1494 /* Try copy/delete unless it's a directory. */
1495 /* FIXME: This does not handle the (unlikely) case that the two locations
1496 are on the same Wine drive, but on different Unix file systems. */
1498 if ( attr & FILE_ATTRIBUTE_DIRECTORY )
1500 FILE_SetDosError();
1501 return FALSE;
1503 else
1505 if ( ! CopyFileW( fn1, fn2, !(flag & MOVEFILE_REPLACE_EXISTING) ))
1506 return FALSE;
1507 if ( ! DeleteFileW ( fn1 ) )
1508 return FALSE;
1511 if (is_executable( full_name1.long_name ) != is_executable( full_name2.long_name ))
1513 struct stat fstat;
1514 if (stat( full_name2.long_name, &fstat ) != -1)
1516 if (is_executable( full_name2.long_name ))
1517 /* set executable bit where read bit is set */
1518 fstat.st_mode |= (fstat.st_mode & 0444) >> 2;
1519 else
1520 fstat.st_mode &= ~0111;
1521 chmod( full_name2.long_name, fstat.st_mode );
1524 return TRUE;
1526 else /* fn2 == NULL means delete source */
1528 if (flag & MOVEFILE_DELAY_UNTIL_REBOOT)
1530 if (flag & MOVEFILE_COPY_ALLOWED) {
1531 WARN("Illegal flag\n");
1532 SetLastError( ERROR_GEN_FAILURE );
1533 return FALSE;
1536 return FILE_AddBootRenameEntry( fn1, NULL, flag );
1539 if (unlink( full_name1.long_name ) == -1)
1541 FILE_SetDosError();
1542 return FALSE;
1544 return TRUE; /* successfully deleted */
1548 /**************************************************************************
1549 * MoveFileExA (KERNEL32.@)
1551 BOOL WINAPI MoveFileExA( LPCSTR fn1, LPCSTR fn2, DWORD flag )
1553 UNICODE_STRING fn1W, fn2W;
1554 BOOL ret;
1556 if (!fn1)
1558 SetLastError(ERROR_INVALID_PARAMETER);
1559 return FALSE;
1562 RtlCreateUnicodeStringFromAsciiz(&fn1W, fn1);
1563 if (fn2) RtlCreateUnicodeStringFromAsciiz(&fn2W, fn2);
1564 else fn2W.Buffer = NULL;
1566 ret = MoveFileExW( fn1W.Buffer, fn2W.Buffer, flag );
1568 RtlFreeUnicodeString(&fn1W);
1569 RtlFreeUnicodeString(&fn2W);
1570 return ret;
1574 /**************************************************************************
1575 * MoveFileW (KERNEL32.@)
1577 * Move file or directory
1579 BOOL WINAPI MoveFileW( LPCWSTR fn1, LPCWSTR fn2 )
1581 return MoveFileExW( fn1, fn2, MOVEFILE_COPY_ALLOWED );
1585 /**************************************************************************
1586 * MoveFileA (KERNEL32.@)
1588 BOOL WINAPI MoveFileA( LPCSTR fn1, LPCSTR fn2 )
1590 return MoveFileExA( fn1, fn2, MOVEFILE_COPY_ALLOWED );
1594 /**************************************************************************
1595 * CopyFileW (KERNEL32.@)
1597 BOOL WINAPI CopyFileW( LPCWSTR source, LPCWSTR dest, BOOL fail_if_exists )
1599 HANDLE h1, h2;
1600 BY_HANDLE_FILE_INFORMATION info;
1601 DWORD count;
1602 BOOL ret = FALSE;
1603 char buffer[2048];
1605 if (!source || !dest)
1607 SetLastError(ERROR_INVALID_PARAMETER);
1608 return FALSE;
1611 TRACE("%s -> %s\n", debugstr_w(source), debugstr_w(dest));
1613 if ((h1 = CreateFileW(source, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
1614 NULL, OPEN_EXISTING, 0, 0)) == INVALID_HANDLE_VALUE)
1616 WARN("Unable to open source %s\n", debugstr_w(source));
1617 return FALSE;
1620 if (!GetFileInformationByHandle( h1, &info ))
1622 WARN("GetFileInformationByHandle returned error for %s\n", debugstr_w(source));
1623 CloseHandle( h1 );
1624 return FALSE;
1627 if ((h2 = CreateFileW( dest, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
1628 fail_if_exists ? CREATE_NEW : CREATE_ALWAYS,
1629 info.dwFileAttributes, h1 )) == INVALID_HANDLE_VALUE)
1631 WARN("Unable to open dest %s\n", debugstr_w(dest));
1632 CloseHandle( h1 );
1633 return FALSE;
1636 while (ReadFile( h1, buffer, sizeof(buffer), &count, NULL ) && count)
1638 char *p = buffer;
1639 while (count != 0)
1641 DWORD res;
1642 if (!WriteFile( h2, p, count, &res, NULL ) || !res) goto done;
1643 p += res;
1644 count -= res;
1647 ret = TRUE;
1648 done:
1649 CloseHandle( h1 );
1650 CloseHandle( h2 );
1651 return ret;
1655 /**************************************************************************
1656 * CopyFileA (KERNEL32.@)
1658 BOOL WINAPI CopyFileA( LPCSTR source, LPCSTR dest, BOOL fail_if_exists)
1660 UNICODE_STRING sourceW, destW;
1661 BOOL ret;
1663 if (!source || !dest)
1665 SetLastError(ERROR_INVALID_PARAMETER);
1666 return FALSE;
1669 RtlCreateUnicodeStringFromAsciiz(&sourceW, source);
1670 RtlCreateUnicodeStringFromAsciiz(&destW, dest);
1672 ret = CopyFileW(sourceW.Buffer, destW.Buffer, fail_if_exists);
1674 RtlFreeUnicodeString(&sourceW);
1675 RtlFreeUnicodeString(&destW);
1676 return ret;
1680 /**************************************************************************
1681 * CopyFileExW (KERNEL32.@)
1683 * This implementation ignores most of the extra parameters passed-in into
1684 * the "ex" version of the method and calls the CopyFile method.
1685 * It will have to be fixed eventually.
1687 BOOL WINAPI CopyFileExW(LPCWSTR sourceFilename, LPCWSTR destFilename,
1688 LPPROGRESS_ROUTINE progressRoutine, LPVOID appData,
1689 LPBOOL cancelFlagPointer, DWORD copyFlags)
1692 * Interpret the only flag that CopyFile can interpret.
1694 return CopyFileW(sourceFilename, destFilename, (copyFlags & COPY_FILE_FAIL_IF_EXISTS) != 0);
1698 /**************************************************************************
1699 * CopyFileExA (KERNEL32.@)
1701 BOOL WINAPI CopyFileExA(LPCSTR sourceFilename, LPCSTR destFilename,
1702 LPPROGRESS_ROUTINE progressRoutine, LPVOID appData,
1703 LPBOOL cancelFlagPointer, DWORD copyFlags)
1705 UNICODE_STRING sourceW, destW;
1706 BOOL ret;
1708 if (!sourceFilename || !destFilename)
1710 SetLastError(ERROR_INVALID_PARAMETER);
1711 return FALSE;
1714 RtlCreateUnicodeStringFromAsciiz(&sourceW, sourceFilename);
1715 RtlCreateUnicodeStringFromAsciiz(&destW, destFilename);
1717 ret = CopyFileExW(sourceW.Buffer, destW.Buffer, progressRoutine, appData,
1718 cancelFlagPointer, copyFlags);
1720 RtlFreeUnicodeString(&sourceW);
1721 RtlFreeUnicodeString(&destW);
1722 return ret;
1726 /***********************************************************************
1727 * SetFileTime (KERNEL32.@)
1729 BOOL WINAPI SetFileTime( HANDLE hFile,
1730 const FILETIME *ctime,
1731 const FILETIME *atime,
1732 const FILETIME *mtime )
1734 BOOL ret = FALSE;
1735 NTSTATUS status;
1736 int fd;
1738 if (!(status = wine_server_handle_to_fd( hFile, GENERIC_WRITE, &fd, NULL, NULL )))
1740 #ifdef HAVE_FUTIMES
1741 ULONGLONG sec, nsec;
1742 struct timeval tv[2];
1744 if (!atime || !mtime)
1746 struct stat st;
1748 tv[0].tv_sec = tv[0].tv_usec = 0;
1749 tv[1].tv_sec = tv[1].tv_usec = 0;
1750 if (!fstat( fd, &st ))
1752 tv[0].tv_sec = st.st_atime;
1753 tv[1].tv_sec = st.st_mtime;
1756 if (atime)
1758 sec = ((ULONGLONG)atime->dwHighDateTime << 32) | atime->dwLowDateTime;
1759 sec = RtlLargeIntegerDivide( sec, 10000000, &nsec );
1760 tv[0].tv_sec = sec - SECS_1601_TO_1970;
1761 tv[0].tv_usec = (UINT)nsec / 10;
1763 if (mtime)
1765 sec = ((ULONGLONG)mtime->dwHighDateTime << 32) | mtime->dwLowDateTime;
1766 sec = RtlLargeIntegerDivide( sec, 10000000, &nsec );
1767 tv[1].tv_sec = sec - SECS_1601_TO_1970;
1768 tv[1].tv_usec = (UINT)nsec / 10;
1771 if (!futimes( fd, tv )) ret = TRUE;
1772 else FILE_SetDosError();
1773 #else
1774 ret = TRUE; /* pretend it succeeded */
1775 #endif
1776 wine_server_release_fd( hFile, fd );
1778 else SetLastError( RtlNtStatusToDosError(status) );
1779 return ret;
1783 /**************************************************************************
1784 * GetFileAttributesExW (KERNEL32.@)
1786 BOOL WINAPI GetFileAttributesExW(
1787 LPCWSTR lpFileName, GET_FILEEX_INFO_LEVELS fInfoLevelId,
1788 LPVOID lpFileInformation)
1790 DOS_FULL_NAME full_name;
1791 BY_HANDLE_FILE_INFORMATION info;
1793 if (!lpFileName || !lpFileInformation)
1795 SetLastError(ERROR_INVALID_PARAMETER);
1796 return FALSE;
1799 if (fInfoLevelId == GetFileExInfoStandard) {
1800 LPWIN32_FILE_ATTRIBUTE_DATA lpFad =
1801 (LPWIN32_FILE_ATTRIBUTE_DATA) lpFileInformation;
1802 if (!DOSFS_GetFullName( lpFileName, TRUE, &full_name )) return FALSE;
1803 if (!FILE_Stat( full_name.long_name, &info, NULL )) return FALSE;
1805 lpFad->dwFileAttributes = info.dwFileAttributes;
1806 lpFad->ftCreationTime = info.ftCreationTime;
1807 lpFad->ftLastAccessTime = info.ftLastAccessTime;
1808 lpFad->ftLastWriteTime = info.ftLastWriteTime;
1809 lpFad->nFileSizeHigh = info.nFileSizeHigh;
1810 lpFad->nFileSizeLow = info.nFileSizeLow;
1812 else {
1813 FIXME("invalid info level %d!\n", fInfoLevelId);
1814 return FALSE;
1817 return TRUE;
1821 /**************************************************************************
1822 * GetFileAttributesExA (KERNEL32.@)
1824 BOOL WINAPI GetFileAttributesExA(
1825 LPCSTR filename, GET_FILEEX_INFO_LEVELS fInfoLevelId,
1826 LPVOID lpFileInformation)
1828 UNICODE_STRING filenameW;
1829 BOOL ret = FALSE;
1831 if (!filename || !lpFileInformation)
1833 SetLastError(ERROR_INVALID_PARAMETER);
1834 return FALSE;
1837 if (RtlCreateUnicodeStringFromAsciiz(&filenameW, filename))
1839 ret = GetFileAttributesExW(filenameW.Buffer, fInfoLevelId, lpFileInformation);
1840 RtlFreeUnicodeString(&filenameW);
1842 else
1843 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1844 return ret;