Added support for FILE_DIRECTORY_FILE and FILE_NON_DIRECTORY_FILE open
[wine/multimedia.git] / files / file.c
blob5c673de1fabde9e3521ca78558b2d3f564cb9b63
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 else
208 options |= FILE_NON_DIRECTORY_FILE;
209 if (attributes & FILE_FLAG_DELETE_ON_CLOSE)
210 options |= FILE_DELETE_ON_CLOSE;
211 if (!(attributes & FILE_FLAG_OVERLAPPED))
212 options |= FILE_SYNCHRONOUS_IO_ALERT;
213 if (attributes & FILE_FLAG_RANDOM_ACCESS)
214 options |= FILE_RANDOM_ACCESS;
215 attributes &= FILE_ATTRIBUTE_VALID_FLAGS;
217 SERVER_START_REQ( create_file )
219 req->access = access;
220 req->inherit = (sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle);
221 req->sharing = sharing;
222 req->create = disp;
223 req->options = options;
224 req->attrs = attributes;
225 wine_server_add_data( req, filename, strlen(filename) );
226 SetLastError(0);
227 err = wine_server_call( req );
228 ret = reply->handle;
230 SERVER_END_REQ;
232 if (err)
234 /* In the case file creation was rejected due to CREATE_NEW flag
235 * was specified and file with that name already exists, correct
236 * last error is ERROR_FILE_EXISTS and not ERROR_ALREADY_EXISTS.
237 * Note: RtlNtStatusToDosError is not the subject to blame here.
239 if (err == STATUS_OBJECT_NAME_COLLISION)
240 SetLastError( ERROR_FILE_EXISTS );
241 else
242 SetLastError( RtlNtStatusToDosError(err) );
245 if (!ret) WARN("Unable to create file '%s' (GLE %ld)\n", filename, GetLastError());
246 return ret;
250 static HANDLE FILE_OpenPipe(LPCWSTR name, DWORD access, LPSECURITY_ATTRIBUTES sa )
252 HANDLE ret;
253 DWORD len = 0;
255 if (name && (len = strlenW(name)) > MAX_PATH)
257 SetLastError( ERROR_FILENAME_EXCED_RANGE );
258 return 0;
260 SERVER_START_REQ( open_named_pipe )
262 req->access = access;
263 req->inherit = (sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle);
264 SetLastError(0);
265 wine_server_add_data( req, name, len * sizeof(WCHAR) );
266 wine_server_call_err( req );
267 ret = reply->handle;
269 SERVER_END_REQ;
270 TRACE("Returned %p\n",ret);
271 return ret;
274 /*************************************************************************
275 * CreateFileW [KERNEL32.@] Creates or opens a file or other object
277 * Creates or opens an object, and returns a handle that can be used to
278 * access that object.
280 * PARAMS
282 * filename [in] pointer to filename to be accessed
283 * access [in] access mode requested
284 * sharing [in] share mode
285 * sa [in] pointer to security attributes
286 * creation [in] how to create the file
287 * attributes [in] attributes for newly created file
288 * template [in] handle to file with extended attributes to copy
290 * RETURNS
291 * Success: Open handle to specified file
292 * Failure: INVALID_HANDLE_VALUE
294 * NOTES
295 * Should call SetLastError() on failure.
297 * BUGS
299 * Doesn't support character devices, template files, or a
300 * lot of the 'attributes' flags yet.
302 HANDLE WINAPI CreateFileW( LPCWSTR filename, DWORD access, DWORD sharing,
303 LPSECURITY_ATTRIBUTES sa, DWORD creation,
304 DWORD attributes, HANDLE template )
306 DOS_FULL_NAME full_name;
307 HANDLE ret;
308 DWORD dosdev;
309 static const WCHAR bkslashes_with_question_markW[] = {'\\','\\','?','\\',0};
310 static const WCHAR bkslashes_with_dotW[] = {'\\','\\','.','\\',0};
311 static const WCHAR bkslashesW[] = {'\\','\\',0};
312 static const WCHAR coninW[] = {'C','O','N','I','N','$',0};
313 static const WCHAR conoutW[] = {'C','O','N','O','U','T','$',0};
315 if (!filename || !filename[0])
317 SetLastError( ERROR_PATH_NOT_FOUND );
318 return INVALID_HANDLE_VALUE;
321 TRACE("%s %s%s%s%s%s%s%s attributes 0x%lx\n", debugstr_w(filename),
322 ((access & GENERIC_READ)==GENERIC_READ)?"GENERIC_READ ":"",
323 ((access & GENERIC_WRITE)==GENERIC_WRITE)?"GENERIC_WRITE ":"",
324 (!access)?"QUERY_ACCESS ":"",
325 ((sharing & FILE_SHARE_READ)==FILE_SHARE_READ)?"FILE_SHARE_READ ":"",
326 ((sharing & FILE_SHARE_WRITE)==FILE_SHARE_WRITE)?"FILE_SHARE_WRITE ":"",
327 ((sharing & FILE_SHARE_DELETE)==FILE_SHARE_DELETE)?"FILE_SHARE_DELETE ":"",
328 (creation ==CREATE_NEW)?"CREATE_NEW":
329 (creation ==CREATE_ALWAYS)?"CREATE_ALWAYS ":
330 (creation ==OPEN_EXISTING)?"OPEN_EXISTING ":
331 (creation ==OPEN_ALWAYS)?"OPEN_ALWAYS ":
332 (creation ==TRUNCATE_EXISTING)?"TRUNCATE_EXISTING ":"", attributes);
334 /* Open a console for CONIN$ or CONOUT$ */
335 if (!strcmpiW(filename, coninW) || !strcmpiW(filename, conoutW))
337 ret = OpenConsoleW(filename, access, (sa && sa->bInheritHandle), creation);
338 goto done;
341 /* If the name starts with '\\?\', ignore the first 4 chars. */
342 if (!strncmpW(filename, bkslashes_with_question_markW, 4))
344 static const WCHAR uncW[] = {'U','N','C','\\',0};
345 filename += 4;
346 if (!strncmpiW(filename, uncW, 4))
348 FIXME("UNC name (%s) not supported.\n", debugstr_w(filename) );
349 SetLastError( ERROR_PATH_NOT_FOUND );
350 return INVALID_HANDLE_VALUE;
354 if (!strncmpW(filename, bkslashes_with_dotW, 4))
356 static const WCHAR pipeW[] = {'P','I','P','E','\\',0};
357 if(!strncmpiW(filename + 4, pipeW, 5))
359 TRACE("Opening a pipe: %s\n", debugstr_w(filename));
360 ret = FILE_OpenPipe( filename, access, sa );
361 goto done;
363 else if (isalphaW(filename[4]) && filename[5] == ':' && filename[6] == '\0')
365 const char *device = DRIVE_GetDevice( toupperW(filename[4]) - 'A' );
366 if (device)
368 ret = FILE_CreateFile( device, access, sharing, sa, creation,
369 attributes, template );
371 else
373 SetLastError( ERROR_ACCESS_DENIED );
374 ret = INVALID_HANDLE_VALUE;
376 goto done;
378 else if ((dosdev = RtlIsDosDeviceName_U( filename + 4 )))
380 dosdev += MAKELONG( 0, 4*sizeof(WCHAR) ); /* adjust position to start of filename */
382 else
384 ret = VXD_Open( filename+4, access, sa );
385 goto done;
388 else dosdev = RtlIsDosDeviceName_U( filename );
390 if (dosdev)
392 static const WCHAR conW[] = {'C','O','N',0};
393 WCHAR dev[5];
395 memcpy( dev, filename + HIWORD(dosdev)/sizeof(WCHAR), LOWORD(dosdev) );
396 dev[LOWORD(dosdev)/sizeof(WCHAR)] = 0;
398 TRACE("opening device %s\n", debugstr_w(dev) );
400 if (!strcmpiW( dev, conW ))
402 switch (access & (GENERIC_READ|GENERIC_WRITE))
404 case GENERIC_READ:
405 ret = OpenConsoleW(coninW, access, (sa && sa->bInheritHandle), creation);
406 goto done;
407 case GENERIC_WRITE:
408 ret = OpenConsoleW(conoutW, access, (sa && sa->bInheritHandle), creation);
409 goto done;
410 default:
411 FIXME("can't open CON read/write\n");
412 SetLastError( ERROR_FILE_NOT_FOUND );
413 return INVALID_HANDLE_VALUE;
417 ret = VOLUME_OpenDevice( dev, access, sharing, sa, attributes );
418 goto done;
421 /* If the name still starts with '\\', it's a UNC name. */
422 if (!strncmpW(filename, bkslashesW, 2))
424 ret = SMB_CreateFileW(filename, access, sharing, sa, creation, attributes, template );
425 goto done;
428 /* If the name contains a DOS wild card (* or ?), do no create a file */
429 if(strchrW(filename, '*') || strchrW(filename, '?'))
431 SetLastError(ERROR_BAD_PATHNAME);
432 return INVALID_HANDLE_VALUE;
435 /* check for filename, don't check for last entry if creating */
436 if (!DOSFS_GetFullName( filename,
437 (creation == OPEN_EXISTING) ||
438 (creation == TRUNCATE_EXISTING),
439 &full_name )) {
440 WARN("Unable to get full filename from %s (GLE %ld)\n",
441 debugstr_w(filename), GetLastError());
442 return INVALID_HANDLE_VALUE;
445 ret = FILE_CreateFile( full_name.long_name, access, sharing,
446 sa, creation, attributes, template );
447 done:
448 if (!ret) ret = INVALID_HANDLE_VALUE;
449 TRACE("returning %p\n", ret);
450 return ret;
455 /*************************************************************************
456 * CreateFileA (KERNEL32.@)
458 HANDLE WINAPI CreateFileA( LPCSTR filename, DWORD access, DWORD sharing,
459 LPSECURITY_ATTRIBUTES sa, DWORD creation,
460 DWORD attributes, HANDLE template)
462 UNICODE_STRING filenameW;
463 HANDLE ret = INVALID_HANDLE_VALUE;
465 if (!filename)
467 SetLastError( ERROR_INVALID_PARAMETER );
468 return INVALID_HANDLE_VALUE;
471 if (RtlCreateUnicodeStringFromAsciiz(&filenameW, filename))
473 ret = CreateFileW(filenameW.Buffer, access, sharing, sa, creation,
474 attributes, template);
475 RtlFreeUnicodeString(&filenameW);
477 else
478 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
479 return ret;
483 /***********************************************************************
484 * FILE_FillInfo
486 * Fill a file information from a struct stat.
488 static void FILE_FillInfo( struct stat *st, BY_HANDLE_FILE_INFORMATION *info )
490 if (S_ISDIR(st->st_mode))
491 info->dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
492 else
493 info->dwFileAttributes = FILE_ATTRIBUTE_ARCHIVE;
494 if (!(st->st_mode & S_IWUSR))
495 info->dwFileAttributes |= FILE_ATTRIBUTE_READONLY;
497 RtlSecondsSince1970ToTime( st->st_mtime, (LARGE_INTEGER *)&info->ftCreationTime );
498 RtlSecondsSince1970ToTime( st->st_mtime, (LARGE_INTEGER *)&info->ftLastWriteTime );
499 RtlSecondsSince1970ToTime( st->st_atime, (LARGE_INTEGER *)&info->ftLastAccessTime );
501 info->dwVolumeSerialNumber = 0; /* FIXME */
502 if (S_ISDIR(st->st_mode))
504 info->nFileSizeHigh = 0;
505 info->nFileSizeLow = 0;
506 info->nNumberOfLinks = 1;
508 else
510 info->nFileSizeHigh = st->st_size >> 32;
511 info->nFileSizeLow = (DWORD)st->st_size;
512 info->nNumberOfLinks = st->st_nlink;
514 info->nFileIndexHigh = st->st_ino >> 32;
515 info->nFileIndexLow = (DWORD)st->st_ino;
519 /***********************************************************************
520 * get_show_dot_files_option
522 static BOOL get_show_dot_files_option(void)
524 static const WCHAR WineW[] = {'M','a','c','h','i','n','e','\\',
525 'S','o','f','t','w','a','r','e','\\',
526 'W','i','n','e','\\','W','i','n','e','\\',
527 'C','o','n','f','i','g','\\','W','i','n','e',0};
528 static const WCHAR ShowDotFilesW[] = {'S','h','o','w','D','o','t','F','i','l','e','s',0};
530 char tmp[80];
531 HKEY hkey;
532 DWORD dummy;
533 OBJECT_ATTRIBUTES attr;
534 UNICODE_STRING nameW;
535 BOOL ret = FALSE;
537 attr.Length = sizeof(attr);
538 attr.RootDirectory = 0;
539 attr.ObjectName = &nameW;
540 attr.Attributes = 0;
541 attr.SecurityDescriptor = NULL;
542 attr.SecurityQualityOfService = NULL;
543 RtlInitUnicodeString( &nameW, WineW );
545 if (!NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr ))
547 RtlInitUnicodeString( &nameW, ShowDotFilesW );
548 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
550 WCHAR *str = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
551 ret = IS_OPTION_TRUE( str[0] );
553 NtClose( hkey );
555 return ret;
559 /***********************************************************************
560 * FILE_Stat
562 * Stat a Unix path name. Return TRUE if OK.
564 BOOL FILE_Stat( LPCSTR unixName, BY_HANDLE_FILE_INFORMATION *info, BOOL *is_symlink_ptr )
566 struct stat st;
567 int is_symlink;
568 LPCSTR p;
570 if (lstat( unixName, &st ) == -1)
572 FILE_SetDosError();
573 return FALSE;
575 is_symlink = S_ISLNK(st.st_mode);
576 if (is_symlink)
578 /* do a "real" stat to find out
579 about the type of the symlink destination */
580 if (stat( unixName, &st ) == -1)
582 FILE_SetDosError();
583 return FALSE;
587 /* fill in the information we gathered so far */
588 FILE_FillInfo( &st, info );
590 /* and now see if this is a hidden file, based on the name */
591 p = strrchr( unixName, '/');
592 p = p ? p + 1 : unixName;
593 if (*p == '.' && *(p+1) && (*(p+1) != '.' || *(p+2)))
595 static int show_dot_files = -1;
596 if (show_dot_files == -1)
597 show_dot_files = get_show_dot_files_option();
598 if (!show_dot_files)
599 info->dwFileAttributes |= FILE_ATTRIBUTE_HIDDEN;
601 if (is_symlink_ptr) *is_symlink_ptr = is_symlink;
602 return TRUE;
606 /***********************************************************************
607 * GetFileInformationByHandle (KERNEL32.@)
609 BOOL WINAPI GetFileInformationByHandle( HANDLE hFile, BY_HANDLE_FILE_INFORMATION *info )
611 NTSTATUS status;
612 int fd;
613 BOOL ret = FALSE;
615 TRACE("%p,%p\n", hFile, info);
617 if (!info) return 0;
619 if (!(status = wine_server_handle_to_fd( hFile, 0, &fd, NULL, NULL )))
621 struct stat st;
623 if (fstat( fd, &st ) == -1)
624 FILE_SetDosError();
625 else if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode))
626 SetLastError( ERROR_INVALID_FUNCTION );
627 else
629 FILE_FillInfo( &st, info );
630 ret = TRUE;
632 wine_server_release_fd( hFile, fd );
634 else SetLastError( RtlNtStatusToDosError(status) );
636 return ret;
640 /**************************************************************************
641 * GetFileAttributesW (KERNEL32.@)
643 DWORD WINAPI GetFileAttributesW( LPCWSTR name )
645 DOS_FULL_NAME full_name;
646 BY_HANDLE_FILE_INFORMATION info;
648 if (name == NULL)
650 SetLastError( ERROR_INVALID_PARAMETER );
651 return INVALID_FILE_ATTRIBUTES;
653 if (!DOSFS_GetFullName( name, TRUE, &full_name) )
654 return INVALID_FILE_ATTRIBUTES;
655 if (!FILE_Stat( full_name.long_name, &info, NULL ))
656 return INVALID_FILE_ATTRIBUTES;
657 return info.dwFileAttributes;
661 /**************************************************************************
662 * GetFileAttributesA (KERNEL32.@)
664 DWORD WINAPI GetFileAttributesA( LPCSTR name )
666 UNICODE_STRING nameW;
667 DWORD ret = INVALID_FILE_ATTRIBUTES;
669 if (!name)
671 SetLastError( ERROR_INVALID_PARAMETER );
672 return INVALID_FILE_ATTRIBUTES;
675 if (RtlCreateUnicodeStringFromAsciiz(&nameW, name))
677 ret = GetFileAttributesW(nameW.Buffer);
678 RtlFreeUnicodeString(&nameW);
680 else
681 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
682 return ret;
686 /**************************************************************************
687 * SetFileAttributesW (KERNEL32.@)
689 BOOL WINAPI SetFileAttributesW(LPCWSTR lpFileName, DWORD attributes)
691 struct stat buf;
692 DOS_FULL_NAME full_name;
694 if (!lpFileName)
696 SetLastError( ERROR_INVALID_PARAMETER );
697 return FALSE;
700 TRACE("(%s,%lx)\n", debugstr_w(lpFileName), attributes);
702 if (!DOSFS_GetFullName( lpFileName, TRUE, &full_name ))
703 return FALSE;
705 if(stat(full_name.long_name,&buf)==-1)
707 FILE_SetDosError();
708 return FALSE;
710 if (attributes & FILE_ATTRIBUTE_READONLY)
712 if(S_ISDIR(buf.st_mode))
713 /* FIXME */
714 WARN("FILE_ATTRIBUTE_READONLY ignored for directory.\n");
715 else
716 buf.st_mode &= ~0222; /* octal!, clear write permission bits */
717 attributes &= ~FILE_ATTRIBUTE_READONLY;
719 else
721 /* add write permission */
722 buf.st_mode |= (0600 | ((buf.st_mode & 044) >> 1)) & (~FILE_umask);
724 if (attributes & FILE_ATTRIBUTE_DIRECTORY)
726 if (!S_ISDIR(buf.st_mode))
727 FIXME("SetFileAttributes expected the file %s to be a directory\n",
728 debugstr_w(lpFileName));
729 attributes &= ~FILE_ATTRIBUTE_DIRECTORY;
731 attributes &= ~(FILE_ATTRIBUTE_NORMAL|FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN|
732 FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_TEMPORARY);
733 if (attributes)
734 FIXME("(%s):%lx attribute(s) not implemented.\n", debugstr_w(lpFileName), attributes);
735 if (-1==chmod(full_name.long_name,buf.st_mode))
737 if (GetDriveTypeW(lpFileName) == DRIVE_CDROM)
739 SetLastError( ERROR_ACCESS_DENIED );
740 return FALSE;
744 * FIXME: We don't return FALSE here because of differences between
745 * Linux and Windows privileges. Under Linux only the owner of
746 * the file is allowed to change file attributes. Under Windows,
747 * applications expect that if you can write to a file, you can also
748 * change its attributes (see GENERIC_WRITE). We could try to be
749 * clever here but that would break multi-user installations where
750 * users share read-only DLLs. This is because some installers like
751 * to change attributes of already installed DLLs.
753 FIXME("Couldn't set file attributes for existing file \"%s\".\n"
754 "Check permissions or set VFAT \"quiet\" mount flag\n", full_name.long_name);
756 return TRUE;
760 /**************************************************************************
761 * SetFileAttributesA (KERNEL32.@)
763 BOOL WINAPI SetFileAttributesA(LPCSTR lpFileName, DWORD attributes)
765 UNICODE_STRING filenameW;
766 BOOL ret = FALSE;
768 if (!lpFileName)
770 SetLastError( ERROR_INVALID_PARAMETER );
771 return FALSE;
774 if (RtlCreateUnicodeStringFromAsciiz(&filenameW, lpFileName))
776 ret = SetFileAttributesW(filenameW.Buffer, attributes);
777 RtlFreeUnicodeString(&filenameW);
779 else
780 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
781 return ret;
785 /******************************************************************************
786 * GetCompressedFileSizeA [KERNEL32.@]
788 DWORD WINAPI GetCompressedFileSizeA(
789 LPCSTR lpFileName,
790 LPDWORD lpFileSizeHigh)
792 UNICODE_STRING filenameW;
793 DWORD ret;
795 if (RtlCreateUnicodeStringFromAsciiz(&filenameW, lpFileName))
797 ret = GetCompressedFileSizeW(filenameW.Buffer, lpFileSizeHigh);
798 RtlFreeUnicodeString(&filenameW);
800 else
802 ret = INVALID_FILE_SIZE;
803 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
805 return ret;
809 /******************************************************************************
810 * GetCompressedFileSizeW [KERNEL32.@]
812 * RETURNS
813 * Success: Low-order doubleword of number of bytes
814 * Failure: INVALID_FILE_SIZE
816 DWORD WINAPI GetCompressedFileSizeW(
817 LPCWSTR lpFileName, /* [in] Pointer to name of file */
818 LPDWORD lpFileSizeHigh) /* [out] Receives high-order doubleword of size */
820 DOS_FULL_NAME full_name;
821 struct stat st;
822 DWORD low;
824 TRACE("(%s,%p)\n",debugstr_w(lpFileName),lpFileSizeHigh);
826 if (!DOSFS_GetFullName( lpFileName, TRUE, &full_name )) return INVALID_FILE_SIZE;
827 if (stat(full_name.long_name, &st) != 0)
829 FILE_SetDosError();
830 return INVALID_FILE_SIZE;
832 #if HAVE_STRUCT_STAT_ST_BLOCKS
833 /* blocks are 512 bytes long */
834 if (lpFileSizeHigh) *lpFileSizeHigh = (st.st_blocks >> 23);
835 low = (DWORD)(st.st_blocks << 9);
836 #else
837 if (lpFileSizeHigh) *lpFileSizeHigh = (st.st_size >> 32);
838 low = (DWORD)st.st_size;
839 #endif
840 return low;
844 /***********************************************************************
845 * GetFileTime (KERNEL32.@)
847 BOOL WINAPI GetFileTime( HANDLE hFile, FILETIME *lpCreationTime,
848 FILETIME *lpLastAccessTime,
849 FILETIME *lpLastWriteTime )
851 BY_HANDLE_FILE_INFORMATION info;
852 if (!GetFileInformationByHandle( hFile, &info )) return FALSE;
853 if (lpCreationTime) *lpCreationTime = info.ftCreationTime;
854 if (lpLastAccessTime) *lpLastAccessTime = info.ftLastAccessTime;
855 if (lpLastWriteTime) *lpLastWriteTime = info.ftLastWriteTime;
856 return TRUE;
860 /***********************************************************************
861 * GetTempFileNameA (KERNEL32.@)
863 UINT WINAPI GetTempFileNameA( LPCSTR path, LPCSTR prefix, UINT unique,
864 LPSTR buffer)
866 UNICODE_STRING pathW, prefixW;
867 WCHAR bufferW[MAX_PATH];
868 UINT ret;
870 if ( !path || !prefix || !buffer )
872 SetLastError( ERROR_INVALID_PARAMETER );
873 return 0;
876 RtlCreateUnicodeStringFromAsciiz(&pathW, path);
877 RtlCreateUnicodeStringFromAsciiz(&prefixW, prefix);
879 ret = GetTempFileNameW(pathW.Buffer, prefixW.Buffer, unique, bufferW);
880 if (ret)
881 WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buffer, MAX_PATH, NULL, NULL);
883 RtlFreeUnicodeString(&pathW);
884 RtlFreeUnicodeString(&prefixW);
885 return ret;
888 /***********************************************************************
889 * GetTempFileNameW (KERNEL32.@)
891 UINT WINAPI GetTempFileNameW( LPCWSTR path, LPCWSTR prefix, UINT unique,
892 LPWSTR buffer )
894 static const WCHAR formatW[] = {'%','x','.','t','m','p',0};
896 DOS_FULL_NAME full_name;
897 int i;
898 LPWSTR p;
900 if ( !path || !prefix || !buffer )
902 SetLastError( ERROR_INVALID_PARAMETER );
903 return 0;
906 strcpyW( buffer, path );
907 p = buffer + strlenW(buffer);
909 /* add a \, if there isn't one */
910 if ((p == buffer) || (p[-1] != '\\')) *p++ = '\\';
912 for (i = 3; (i > 0) && (*prefix); i--) *p++ = *prefix++;
914 unique &= 0xffff;
916 if (unique) sprintfW( p, formatW, unique );
917 else
919 /* get a "random" unique number and try to create the file */
920 HANDLE handle;
921 UINT num = GetTickCount() & 0xffff;
923 if (!num) num = 1;
924 unique = num;
927 sprintfW( p, formatW, unique );
928 handle = CreateFileW( buffer, GENERIC_WRITE, 0, NULL,
929 CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0 );
930 if (handle != INVALID_HANDLE_VALUE)
931 { /* We created it */
932 TRACE("created %s\n", debugstr_w(buffer) );
933 CloseHandle( handle );
934 break;
936 if (GetLastError() != ERROR_FILE_EXISTS &&
937 GetLastError() != ERROR_SHARING_VIOLATION)
938 break; /* No need to go on */
939 if (!(++unique & 0xffff)) unique = 1;
940 } while (unique != num);
943 /* Get the full path name */
945 if (DOSFS_GetFullName( buffer, FALSE, &full_name ))
947 char *slash;
948 /* Check if we have write access in the directory */
949 if ((slash = strrchr( full_name.long_name, '/' ))) *slash = '\0';
950 if (access( full_name.long_name, W_OK ) == -1)
951 WARN("returns %s, which doesn't seem to be writeable.\n",
952 debugstr_w(buffer) );
954 TRACE("returning %s\n", debugstr_w(buffer) );
955 return unique;
959 /******************************************************************
960 * FILE_ReadWriteApc (internal)
964 static void WINAPI FILE_ReadWriteApc(void* apc_user, PIO_STATUS_BLOCK io_status, ULONG len)
966 LPOVERLAPPED_COMPLETION_ROUTINE cr = (LPOVERLAPPED_COMPLETION_ROUTINE)apc_user;
968 cr(RtlNtStatusToDosError(io_status->u.Status), len, (LPOVERLAPPED)io_status);
971 /***********************************************************************
972 * ReadFileEx (KERNEL32.@)
974 BOOL WINAPI ReadFileEx(HANDLE hFile, LPVOID buffer, DWORD bytesToRead,
975 LPOVERLAPPED overlapped,
976 LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
978 LARGE_INTEGER offset;
979 NTSTATUS status;
980 PIO_STATUS_BLOCK io_status;
982 if (!overlapped)
984 SetLastError(ERROR_INVALID_PARAMETER);
985 return FALSE;
988 offset.u.LowPart = overlapped->Offset;
989 offset.u.HighPart = overlapped->OffsetHigh;
990 io_status = (PIO_STATUS_BLOCK)overlapped;
991 io_status->u.Status = STATUS_PENDING;
993 status = NtReadFile(hFile, NULL, FILE_ReadWriteApc, lpCompletionRoutine,
994 io_status, buffer, bytesToRead, &offset, NULL);
996 if (status)
998 SetLastError( RtlNtStatusToDosError(status) );
999 return FALSE;
1001 return TRUE;
1004 /***********************************************************************
1005 * ReadFile (KERNEL32.@)
1007 BOOL WINAPI ReadFile( HANDLE hFile, LPVOID buffer, DWORD bytesToRead,
1008 LPDWORD bytesRead, LPOVERLAPPED overlapped )
1010 LARGE_INTEGER offset;
1011 PLARGE_INTEGER poffset = NULL;
1012 IO_STATUS_BLOCK iosb;
1013 PIO_STATUS_BLOCK io_status = &iosb;
1014 HANDLE hEvent = 0;
1015 NTSTATUS status;
1017 TRACE("%p %p %ld %p %p\n", hFile, buffer, bytesToRead,
1018 bytesRead, overlapped );
1020 if (bytesRead) *bytesRead = 0; /* Do this before anything else */
1021 if (!bytesToRead) return TRUE;
1023 if (IsBadReadPtr(buffer, bytesToRead))
1025 SetLastError(ERROR_WRITE_FAULT); /* FIXME */
1026 return FALSE;
1028 if (is_console_handle(hFile))
1029 return ReadConsoleA(hFile, buffer, bytesToRead, bytesRead, NULL);
1031 if (overlapped != NULL)
1033 offset.u.LowPart = overlapped->Offset;
1034 offset.u.HighPart = overlapped->OffsetHigh;
1035 poffset = &offset;
1036 hEvent = overlapped->hEvent;
1037 io_status = (PIO_STATUS_BLOCK)overlapped;
1039 io_status->u.Status = STATUS_PENDING;
1040 io_status->Information = 0;
1042 status = NtReadFile(hFile, hEvent, NULL, NULL, io_status, buffer, bytesToRead, poffset, NULL);
1044 if (status != STATUS_PENDING && bytesRead)
1045 *bytesRead = io_status->Information;
1047 if (status && status != STATUS_END_OF_FILE)
1049 SetLastError( RtlNtStatusToDosError(status) );
1050 return FALSE;
1052 return TRUE;
1056 /***********************************************************************
1057 * WriteFileEx (KERNEL32.@)
1059 BOOL WINAPI WriteFileEx(HANDLE hFile, LPCVOID buffer, DWORD bytesToWrite,
1060 LPOVERLAPPED overlapped,
1061 LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
1063 LARGE_INTEGER offset;
1064 NTSTATUS status;
1065 PIO_STATUS_BLOCK io_status;
1067 TRACE("%p %p %ld %p %p\n",
1068 hFile, buffer, bytesToWrite, overlapped, lpCompletionRoutine);
1070 if (overlapped == NULL)
1072 SetLastError(ERROR_INVALID_PARAMETER);
1073 return FALSE;
1075 offset.u.LowPart = overlapped->Offset;
1076 offset.u.HighPart = overlapped->OffsetHigh;
1078 io_status = (PIO_STATUS_BLOCK)overlapped;
1079 io_status->u.Status = STATUS_PENDING;
1081 status = NtWriteFile(hFile, NULL, FILE_ReadWriteApc, lpCompletionRoutine,
1082 io_status, buffer, bytesToWrite, &offset, NULL);
1084 if (status) SetLastError( RtlNtStatusToDosError(status) );
1085 return !status;
1088 /***********************************************************************
1089 * WriteFile (KERNEL32.@)
1091 BOOL WINAPI WriteFile( HANDLE hFile, LPCVOID buffer, DWORD bytesToWrite,
1092 LPDWORD bytesWritten, LPOVERLAPPED overlapped )
1094 HANDLE hEvent = NULL;
1095 LARGE_INTEGER offset;
1096 PLARGE_INTEGER poffset = NULL;
1097 NTSTATUS status;
1098 IO_STATUS_BLOCK iosb;
1099 PIO_STATUS_BLOCK piosb = &iosb;
1101 TRACE("%p %p %ld %p %p\n",
1102 hFile, buffer, bytesToWrite, bytesWritten, overlapped );
1104 if (is_console_handle(hFile))
1105 return WriteConsoleA(hFile, buffer, bytesToWrite, bytesWritten, NULL);
1107 if (IsBadReadPtr(buffer, bytesToWrite))
1109 SetLastError(ERROR_READ_FAULT); /* FIXME */
1110 return FALSE;
1113 if (overlapped)
1115 offset.u.LowPart = overlapped->Offset;
1116 offset.u.HighPart = overlapped->OffsetHigh;
1117 poffset = &offset;
1118 hEvent = overlapped->hEvent;
1119 piosb = (PIO_STATUS_BLOCK)overlapped;
1121 piosb->u.Status = STATUS_PENDING;
1122 piosb->Information = 0;
1124 status = NtWriteFile(hFile, hEvent, NULL, NULL, piosb,
1125 buffer, bytesToWrite, poffset, NULL);
1126 if (status)
1128 SetLastError( RtlNtStatusToDosError(status) );
1129 return FALSE;
1131 if (bytesWritten) *bytesWritten = piosb->Information;
1133 return TRUE;
1137 /***********************************************************************
1138 * SetFilePointer (KERNEL32.@)
1140 DWORD WINAPI SetFilePointer( HANDLE hFile, LONG distance, LONG *highword,
1141 DWORD method )
1143 static const int whence[3] = { SEEK_SET, SEEK_CUR, SEEK_END };
1144 DWORD ret = INVALID_SET_FILE_POINTER;
1145 NTSTATUS status;
1146 int fd;
1148 TRACE("handle %p offset %ld high %ld origin %ld\n",
1149 hFile, distance, highword?*highword:0, method );
1151 if (method > FILE_END)
1153 SetLastError( ERROR_INVALID_PARAMETER );
1154 return ret;
1157 if (!(status = wine_server_handle_to_fd( hFile, 0, &fd, NULL, NULL )))
1159 off_t pos, res;
1161 if (highword) pos = ((off_t)*highword << 32) | (ULONG)distance;
1162 else pos = (off_t)distance;
1163 if ((res = lseek( fd, pos, whence[method] )) == (off_t)-1)
1165 /* also check EPERM due to SuSE7 2.2.16 lseek() EPERM kernel bug */
1166 if (((errno == EINVAL) || (errno == EPERM)) && (method != FILE_BEGIN) && (pos < 0))
1167 SetLastError( ERROR_NEGATIVE_SEEK );
1168 else
1169 FILE_SetDosError();
1171 else
1173 ret = (DWORD)res;
1174 if (highword) *highword = (res >> 32);
1175 if (ret == INVALID_SET_FILE_POINTER) SetLastError( 0 );
1177 wine_server_release_fd( hFile, fd );
1179 else SetLastError( RtlNtStatusToDosError(status) );
1181 return ret;
1185 /*************************************************************************
1186 * SetHandleCount (KERNEL32.@)
1188 UINT WINAPI SetHandleCount( UINT count )
1190 return min( 256, count );
1194 /**************************************************************************
1195 * SetEndOfFile (KERNEL32.@)
1197 BOOL WINAPI SetEndOfFile( HANDLE hFile )
1199 BOOL ret;
1200 SERVER_START_REQ( truncate_file )
1202 req->handle = hFile;
1203 ret = !wine_server_call_err( req );
1205 SERVER_END_REQ;
1206 return ret;
1210 /***********************************************************************
1211 * GetFileType (KERNEL32.@)
1213 DWORD WINAPI GetFileType( HANDLE hFile )
1215 NTSTATUS status;
1216 int fd;
1217 DWORD ret = FILE_TYPE_UNKNOWN;
1219 if (is_console_handle( hFile ))
1220 return FILE_TYPE_CHAR;
1222 if (!(status = wine_server_handle_to_fd( hFile, 0, &fd, NULL, NULL )))
1224 struct stat st;
1226 if (fstat( fd, &st ) == -1)
1227 FILE_SetDosError();
1228 else if (S_ISFIFO(st.st_mode) || S_ISSOCK(st.st_mode))
1229 ret = FILE_TYPE_PIPE;
1230 else if (S_ISCHR(st.st_mode))
1231 ret = FILE_TYPE_CHAR;
1232 else
1233 ret = FILE_TYPE_DISK;
1234 wine_server_release_fd( hFile, fd );
1236 else SetLastError( RtlNtStatusToDosError(status) );
1238 return ret;
1242 /* check if a file name is for an executable file (.exe or .com) */
1243 inline static BOOL is_executable( const char *name )
1245 int len = strlen(name);
1247 if (len < 4) return FALSE;
1248 return (!strcasecmp( name + len - 4, ".exe" ) ||
1249 !strcasecmp( name + len - 4, ".com" ));
1253 /***********************************************************************
1254 * FILE_AddBootRenameEntry
1256 * Adds an entry to the registry that is loaded when windows boots and
1257 * checks if there are some files to be removed or renamed/moved.
1258 * <fn1> has to be valid and <fn2> may be NULL. If both pointers are
1259 * non-NULL then the file is moved, otherwise it is deleted. The
1260 * entry of the registrykey is always appended with two zero
1261 * terminated strings. If <fn2> is NULL then the second entry is
1262 * simply a single 0-byte. Otherwise the second filename goes
1263 * there. The entries are prepended with \??\ before the path and the
1264 * second filename gets also a '!' as the first character if
1265 * MOVEFILE_REPLACE_EXISTING is set. After the final string another
1266 * 0-byte follows to indicate the end of the strings.
1267 * i.e.:
1268 * \??\D:\test\file1[0]
1269 * !\??\D:\test\file1_renamed[0]
1270 * \??\D:\Test|delete[0]
1271 * [0] <- file is to be deleted, second string empty
1272 * \??\D:\test\file2[0]
1273 * !\??\D:\test\file2_renamed[0]
1274 * [0] <- indicates end of strings
1276 * or:
1277 * \??\D:\test\file1[0]
1278 * !\??\D:\test\file1_renamed[0]
1279 * \??\D:\Test|delete[0]
1280 * [0] <- file is to be deleted, second string empty
1281 * [0] <- indicates end of strings
1284 static BOOL FILE_AddBootRenameEntry( LPCWSTR fn1, LPCWSTR fn2, DWORD flags )
1286 static const WCHAR PreString[] = {'\\','?','?','\\',0};
1287 static const WCHAR ValueName[] = {'P','e','n','d','i','n','g',
1288 'F','i','l','e','R','e','n','a','m','e',
1289 'O','p','e','r','a','t','i','o','n','s',0};
1290 static const WCHAR SessionW[] = {'M','a','c','h','i','n','e','\\',
1291 'S','y','s','t','e','m','\\',
1292 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
1293 'C','o','n','t','r','o','l','\\',
1294 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r',0};
1295 static const int info_size = FIELD_OFFSET( KEY_VALUE_PARTIAL_INFORMATION, Data );
1297 OBJECT_ATTRIBUTES attr;
1298 UNICODE_STRING nameW;
1299 KEY_VALUE_PARTIAL_INFORMATION *info;
1300 BOOL rc = FALSE;
1301 HKEY Reboot = 0;
1302 DWORD len0, len1, len2;
1303 DWORD DataSize = 0;
1304 BYTE *Buffer = NULL;
1305 WCHAR *p;
1307 attr.Length = sizeof(attr);
1308 attr.RootDirectory = 0;
1309 attr.ObjectName = &nameW;
1310 attr.Attributes = 0;
1311 attr.SecurityDescriptor = NULL;
1312 attr.SecurityQualityOfService = NULL;
1313 RtlInitUnicodeString( &nameW, SessionW );
1315 if (NtCreateKey( &Reboot, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ) != STATUS_SUCCESS)
1317 WARN("Error creating key for reboot managment [%s]\n",
1318 "SYSTEM\\CurrentControlSet\\Control\\Session Manager");
1319 return FALSE;
1322 len0 = strlenW(PreString);
1323 len1 = strlenW(fn1) + len0 + 1;
1324 if (fn2)
1326 len2 = strlenW(fn2) + len0 + 1;
1327 if (flags & MOVEFILE_REPLACE_EXISTING) len2++; /* Plus 1 because of the leading '!' */
1329 else len2 = 1; /* minimum is the 0 characters for the empty second string */
1331 /* convert characters to bytes */
1332 len0 *= sizeof(WCHAR);
1333 len1 *= sizeof(WCHAR);
1334 len2 *= sizeof(WCHAR);
1336 RtlInitUnicodeString( &nameW, ValueName );
1338 /* First we check if the key exists and if so how many bytes it already contains. */
1339 if (NtQueryValueKey( Reboot, &nameW, KeyValuePartialInformation,
1340 NULL, 0, &DataSize ) == STATUS_BUFFER_OVERFLOW)
1342 if (!(Buffer = HeapAlloc( GetProcessHeap(), 0, DataSize + len1 + len2 + sizeof(WCHAR) )))
1343 goto Quit;
1344 if (NtQueryValueKey( Reboot, &nameW, KeyValuePartialInformation,
1345 Buffer, DataSize, &DataSize )) goto Quit;
1346 info = (KEY_VALUE_PARTIAL_INFORMATION *)Buffer;
1347 if (info->Type != REG_MULTI_SZ) goto Quit;
1348 if (DataSize > sizeof(info)) DataSize -= sizeof(WCHAR); /* remove terminating null (will be added back later) */
1350 else
1352 DataSize = info_size;
1353 if (!(Buffer = HeapAlloc( GetProcessHeap(), 0, DataSize + len1 + len2 + sizeof(WCHAR) )))
1354 goto Quit;
1357 p = (WCHAR *)(Buffer + DataSize);
1358 strcpyW( p, PreString );
1359 strcatW( p, fn1 );
1360 DataSize += len1;
1361 if (fn2)
1363 p = (WCHAR *)(Buffer + DataSize);
1364 if (flags & MOVEFILE_REPLACE_EXISTING)
1365 *p++ = '!';
1366 strcpyW( p, PreString );
1367 strcatW( p, fn2 );
1368 DataSize += len2;
1370 else
1372 p = (WCHAR *)(Buffer + DataSize);
1373 *p = 0;
1374 DataSize += sizeof(WCHAR);
1377 /* add final null */
1378 p = (WCHAR *)(Buffer + DataSize);
1379 *p = 0;
1380 DataSize += sizeof(WCHAR);
1382 rc = !NtSetValueKey(Reboot, &nameW, 0, REG_MULTI_SZ, Buffer + info_size, DataSize - info_size);
1384 Quit:
1385 if (Reboot) NtClose(Reboot);
1386 if (Buffer) HeapFree( GetProcessHeap(), 0, Buffer );
1387 return(rc);
1391 /**************************************************************************
1392 * MoveFileExW (KERNEL32.@)
1394 BOOL WINAPI MoveFileExW( LPCWSTR fn1, LPCWSTR fn2, DWORD flag )
1396 DOS_FULL_NAME full_name1, full_name2;
1397 HANDLE hFile;
1398 DWORD attr = INVALID_FILE_ATTRIBUTES;
1400 TRACE("(%s,%s,%04lx)\n", debugstr_w(fn1), debugstr_w(fn2), flag);
1402 /* FIXME: <Gerhard W. Gruber>sparhawk@gmx.at
1403 In case of W9x and lesser this function should return 120 (ERROR_CALL_NOT_IMPLEMENTED)
1404 to be really compatible. Most programs won't have any problems though. In case
1405 you encounter one, this is what you should return here. I don't know what's up
1406 with NT 3.5. Is this function available there or not?
1407 Does anybody really care about 3.5? :)
1410 /* Filename1 has to be always set to a valid path. Filename2 may be NULL
1411 if the source file has to be deleted.
1413 if (!fn1) {
1414 SetLastError(ERROR_INVALID_PARAMETER);
1415 return FALSE;
1418 /* This function has to be run through in order to process the name properly.
1419 If the BOOTDELAY flag is set, the file doesn't need to exist though. At least
1420 that is the behaviour on NT 4.0. The operation accepts the filenames as
1421 they are given but it can't reply with a reasonable returncode. Success
1422 means in that case success for entering the values into the registry.
1424 if(!DOSFS_GetFullName( fn1, TRUE, &full_name1 ))
1426 if(!(flag & MOVEFILE_DELAY_UNTIL_REBOOT))
1427 return FALSE;
1430 if (fn2) /* !fn2 means delete fn1 */
1432 if (DOSFS_GetFullName( fn2, TRUE, &full_name2 ))
1434 if(!(flag & MOVEFILE_DELAY_UNTIL_REBOOT))
1436 /* target exists, check if we may overwrite */
1437 if (!(flag & MOVEFILE_REPLACE_EXISTING))
1439 SetLastError( ERROR_ALREADY_EXISTS );
1440 return FALSE;
1444 else
1446 if (!DOSFS_GetFullName( fn2, FALSE, &full_name2 ))
1448 if(!(flag & MOVEFILE_DELAY_UNTIL_REBOOT))
1449 return FALSE;
1453 /* Source name and target path are valid */
1455 if (flag & MOVEFILE_DELAY_UNTIL_REBOOT)
1457 return FILE_AddBootRenameEntry( fn1, fn2, flag );
1460 attr = GetFileAttributesW( fn1 );
1461 if ( attr == INVALID_FILE_ATTRIBUTES ) return FALSE;
1463 /* check if we are allowed to rename the source */
1464 hFile = FILE_CreateFile( full_name1.long_name, 0, 0,
1465 NULL, OPEN_EXISTING, 0, 0 );
1466 if (!hFile)
1468 if (GetLastError() != ERROR_ACCESS_DENIED) return FALSE;
1469 if ( !(attr & FILE_ATTRIBUTE_DIRECTORY) ) return FALSE;
1470 /* if it's a directory we can continue */
1472 else CloseHandle(hFile);
1474 /* check, if we are allowed to delete the destination,
1475 ** (but the file not being there is fine) */
1476 hFile = FILE_CreateFile( full_name2.long_name, GENERIC_READ|GENERIC_WRITE, 0,
1477 NULL, OPEN_EXISTING, 0, 0 );
1478 if(!hFile && GetLastError() != ERROR_FILE_NOT_FOUND) return FALSE;
1479 CloseHandle(hFile);
1481 if (full_name1.drive != full_name2.drive)
1483 if (!(flag & MOVEFILE_COPY_ALLOWED))
1485 SetLastError( ERROR_NOT_SAME_DEVICE );
1486 return FALSE;
1488 else if ( attr & FILE_ATTRIBUTE_DIRECTORY )
1490 /* Strange, but that's what Windows returns */
1491 SetLastError ( ERROR_ACCESS_DENIED );
1492 return FALSE;
1495 if (rename( full_name1.long_name, full_name2.long_name ) == -1)
1496 /* Try copy/delete unless it's a directory. */
1497 /* FIXME: This does not handle the (unlikely) case that the two locations
1498 are on the same Wine drive, but on different Unix file systems. */
1500 if ( attr & FILE_ATTRIBUTE_DIRECTORY )
1502 FILE_SetDosError();
1503 return FALSE;
1505 else
1507 if ( ! CopyFileW( fn1, fn2, !(flag & MOVEFILE_REPLACE_EXISTING) ))
1508 return FALSE;
1509 if ( ! DeleteFileW ( fn1 ) )
1510 return FALSE;
1513 if (is_executable( full_name1.long_name ) != is_executable( full_name2.long_name ))
1515 struct stat fstat;
1516 if (stat( full_name2.long_name, &fstat ) != -1)
1518 if (is_executable( full_name2.long_name ))
1519 /* set executable bit where read bit is set */
1520 fstat.st_mode |= (fstat.st_mode & 0444) >> 2;
1521 else
1522 fstat.st_mode &= ~0111;
1523 chmod( full_name2.long_name, fstat.st_mode );
1526 return TRUE;
1528 else /* fn2 == NULL means delete source */
1530 if (flag & MOVEFILE_DELAY_UNTIL_REBOOT)
1532 if (flag & MOVEFILE_COPY_ALLOWED) {
1533 WARN("Illegal flag\n");
1534 SetLastError( ERROR_GEN_FAILURE );
1535 return FALSE;
1538 return FILE_AddBootRenameEntry( fn1, NULL, flag );
1541 if (unlink( full_name1.long_name ) == -1)
1543 FILE_SetDosError();
1544 return FALSE;
1546 return TRUE; /* successfully deleted */
1550 /**************************************************************************
1551 * MoveFileExA (KERNEL32.@)
1553 BOOL WINAPI MoveFileExA( LPCSTR fn1, LPCSTR fn2, DWORD flag )
1555 UNICODE_STRING fn1W, fn2W;
1556 BOOL ret;
1558 if (!fn1)
1560 SetLastError(ERROR_INVALID_PARAMETER);
1561 return FALSE;
1564 RtlCreateUnicodeStringFromAsciiz(&fn1W, fn1);
1565 if (fn2) RtlCreateUnicodeStringFromAsciiz(&fn2W, fn2);
1566 else fn2W.Buffer = NULL;
1568 ret = MoveFileExW( fn1W.Buffer, fn2W.Buffer, flag );
1570 RtlFreeUnicodeString(&fn1W);
1571 RtlFreeUnicodeString(&fn2W);
1572 return ret;
1576 /**************************************************************************
1577 * MoveFileW (KERNEL32.@)
1579 * Move file or directory
1581 BOOL WINAPI MoveFileW( LPCWSTR fn1, LPCWSTR fn2 )
1583 return MoveFileExW( fn1, fn2, MOVEFILE_COPY_ALLOWED );
1587 /**************************************************************************
1588 * MoveFileA (KERNEL32.@)
1590 BOOL WINAPI MoveFileA( LPCSTR fn1, LPCSTR fn2 )
1592 return MoveFileExA( fn1, fn2, MOVEFILE_COPY_ALLOWED );
1596 /**************************************************************************
1597 * CopyFileW (KERNEL32.@)
1599 BOOL WINAPI CopyFileW( LPCWSTR source, LPCWSTR dest, BOOL fail_if_exists )
1601 HANDLE h1, h2;
1602 BY_HANDLE_FILE_INFORMATION info;
1603 DWORD count;
1604 BOOL ret = FALSE;
1605 char buffer[2048];
1607 if (!source || !dest)
1609 SetLastError(ERROR_INVALID_PARAMETER);
1610 return FALSE;
1613 TRACE("%s -> %s\n", debugstr_w(source), debugstr_w(dest));
1615 if ((h1 = CreateFileW(source, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
1616 NULL, OPEN_EXISTING, 0, 0)) == INVALID_HANDLE_VALUE)
1618 WARN("Unable to open source %s\n", debugstr_w(source));
1619 return FALSE;
1622 if (!GetFileInformationByHandle( h1, &info ))
1624 WARN("GetFileInformationByHandle returned error for %s\n", debugstr_w(source));
1625 CloseHandle( h1 );
1626 return FALSE;
1629 if ((h2 = CreateFileW( dest, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
1630 fail_if_exists ? CREATE_NEW : CREATE_ALWAYS,
1631 info.dwFileAttributes, h1 )) == INVALID_HANDLE_VALUE)
1633 WARN("Unable to open dest %s\n", debugstr_w(dest));
1634 CloseHandle( h1 );
1635 return FALSE;
1638 while (ReadFile( h1, buffer, sizeof(buffer), &count, NULL ) && count)
1640 char *p = buffer;
1641 while (count != 0)
1643 DWORD res;
1644 if (!WriteFile( h2, p, count, &res, NULL ) || !res) goto done;
1645 p += res;
1646 count -= res;
1649 ret = TRUE;
1650 done:
1651 CloseHandle( h1 );
1652 CloseHandle( h2 );
1653 return ret;
1657 /**************************************************************************
1658 * CopyFileA (KERNEL32.@)
1660 BOOL WINAPI CopyFileA( LPCSTR source, LPCSTR dest, BOOL fail_if_exists)
1662 UNICODE_STRING sourceW, destW;
1663 BOOL ret;
1665 if (!source || !dest)
1667 SetLastError(ERROR_INVALID_PARAMETER);
1668 return FALSE;
1671 RtlCreateUnicodeStringFromAsciiz(&sourceW, source);
1672 RtlCreateUnicodeStringFromAsciiz(&destW, dest);
1674 ret = CopyFileW(sourceW.Buffer, destW.Buffer, fail_if_exists);
1676 RtlFreeUnicodeString(&sourceW);
1677 RtlFreeUnicodeString(&destW);
1678 return ret;
1682 /**************************************************************************
1683 * CopyFileExW (KERNEL32.@)
1685 * This implementation ignores most of the extra parameters passed-in into
1686 * the "ex" version of the method and calls the CopyFile method.
1687 * It will have to be fixed eventually.
1689 BOOL WINAPI CopyFileExW(LPCWSTR sourceFilename, LPCWSTR destFilename,
1690 LPPROGRESS_ROUTINE progressRoutine, LPVOID appData,
1691 LPBOOL cancelFlagPointer, DWORD copyFlags)
1694 * Interpret the only flag that CopyFile can interpret.
1696 return CopyFileW(sourceFilename, destFilename, (copyFlags & COPY_FILE_FAIL_IF_EXISTS) != 0);
1700 /**************************************************************************
1701 * CopyFileExA (KERNEL32.@)
1703 BOOL WINAPI CopyFileExA(LPCSTR sourceFilename, LPCSTR destFilename,
1704 LPPROGRESS_ROUTINE progressRoutine, LPVOID appData,
1705 LPBOOL cancelFlagPointer, DWORD copyFlags)
1707 UNICODE_STRING sourceW, destW;
1708 BOOL ret;
1710 if (!sourceFilename || !destFilename)
1712 SetLastError(ERROR_INVALID_PARAMETER);
1713 return FALSE;
1716 RtlCreateUnicodeStringFromAsciiz(&sourceW, sourceFilename);
1717 RtlCreateUnicodeStringFromAsciiz(&destW, destFilename);
1719 ret = CopyFileExW(sourceW.Buffer, destW.Buffer, progressRoutine, appData,
1720 cancelFlagPointer, copyFlags);
1722 RtlFreeUnicodeString(&sourceW);
1723 RtlFreeUnicodeString(&destW);
1724 return ret;
1728 /***********************************************************************
1729 * SetFileTime (KERNEL32.@)
1731 BOOL WINAPI SetFileTime( HANDLE hFile,
1732 const FILETIME *ctime,
1733 const FILETIME *atime,
1734 const FILETIME *mtime )
1736 BOOL ret = FALSE;
1737 NTSTATUS status;
1738 int fd;
1740 if (!(status = wine_server_handle_to_fd( hFile, GENERIC_WRITE, &fd, NULL, NULL )))
1742 #ifdef HAVE_FUTIMES
1743 ULONGLONG sec, nsec;
1744 struct timeval tv[2];
1746 if (!atime || !mtime)
1748 struct stat st;
1750 tv[0].tv_sec = tv[0].tv_usec = 0;
1751 tv[1].tv_sec = tv[1].tv_usec = 0;
1752 if (!fstat( fd, &st ))
1754 tv[0].tv_sec = st.st_atime;
1755 tv[1].tv_sec = st.st_mtime;
1758 if (atime)
1760 sec = ((ULONGLONG)atime->dwHighDateTime << 32) | atime->dwLowDateTime;
1761 sec = RtlLargeIntegerDivide( sec, 10000000, &nsec );
1762 tv[0].tv_sec = sec - SECS_1601_TO_1970;
1763 tv[0].tv_usec = (UINT)nsec / 10;
1765 if (mtime)
1767 sec = ((ULONGLONG)mtime->dwHighDateTime << 32) | mtime->dwLowDateTime;
1768 sec = RtlLargeIntegerDivide( sec, 10000000, &nsec );
1769 tv[1].tv_sec = sec - SECS_1601_TO_1970;
1770 tv[1].tv_usec = (UINT)nsec / 10;
1773 if (!futimes( fd, tv )) ret = TRUE;
1774 else FILE_SetDosError();
1775 #else
1776 ret = TRUE; /* pretend it succeeded */
1777 #endif
1778 wine_server_release_fd( hFile, fd );
1780 else SetLastError( RtlNtStatusToDosError(status) );
1781 return ret;
1785 /**************************************************************************
1786 * GetFileAttributesExW (KERNEL32.@)
1788 BOOL WINAPI GetFileAttributesExW(
1789 LPCWSTR lpFileName, GET_FILEEX_INFO_LEVELS fInfoLevelId,
1790 LPVOID lpFileInformation)
1792 DOS_FULL_NAME full_name;
1793 BY_HANDLE_FILE_INFORMATION info;
1795 if (!lpFileName || !lpFileInformation)
1797 SetLastError(ERROR_INVALID_PARAMETER);
1798 return FALSE;
1801 if (fInfoLevelId == GetFileExInfoStandard) {
1802 LPWIN32_FILE_ATTRIBUTE_DATA lpFad =
1803 (LPWIN32_FILE_ATTRIBUTE_DATA) lpFileInformation;
1804 if (!DOSFS_GetFullName( lpFileName, TRUE, &full_name )) return FALSE;
1805 if (!FILE_Stat( full_name.long_name, &info, NULL )) return FALSE;
1807 lpFad->dwFileAttributes = info.dwFileAttributes;
1808 lpFad->ftCreationTime = info.ftCreationTime;
1809 lpFad->ftLastAccessTime = info.ftLastAccessTime;
1810 lpFad->ftLastWriteTime = info.ftLastWriteTime;
1811 lpFad->nFileSizeHigh = info.nFileSizeHigh;
1812 lpFad->nFileSizeLow = info.nFileSizeLow;
1814 else {
1815 FIXME("invalid info level %d!\n", fInfoLevelId);
1816 return FALSE;
1819 return TRUE;
1823 /**************************************************************************
1824 * GetFileAttributesExA (KERNEL32.@)
1826 BOOL WINAPI GetFileAttributesExA(
1827 LPCSTR filename, GET_FILEEX_INFO_LEVELS fInfoLevelId,
1828 LPVOID lpFileInformation)
1830 UNICODE_STRING filenameW;
1831 BOOL ret = FALSE;
1833 if (!filename || !lpFileInformation)
1835 SetLastError(ERROR_INVALID_PARAMETER);
1836 return FALSE;
1839 if (RtlCreateUnicodeStringFromAsciiz(&filenameW, filename))
1841 ret = GetFileAttributesExW(filenameW.Buffer, fInfoLevelId, lpFileInformation);
1842 RtlFreeUnicodeString(&filenameW);
1844 else
1845 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1846 return ret;