ifdef guard TCHAR declaration like PSDK does.
[wine.git] / files / file.c
blobc41bc9fd1c864110fca5ca9bd3c53875322613b1
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, UINT drive_type )
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 req->removable = (drive_type == DRIVE_REMOVABLE || drive_type == DRIVE_CDROM);
224 wine_server_add_data( req, filename, strlen(filename) );
225 SetLastError(0);
226 err = wine_server_call( req );
227 ret = reply->handle;
229 SERVER_END_REQ;
231 if (err)
233 /* In the case file creation was rejected due to CREATE_NEW flag
234 * was specified and file with that name already exists, correct
235 * last error is ERROR_FILE_EXISTS and not ERROR_ALREADY_EXISTS.
236 * Note: RtlNtStatusToDosError is not the subject to blame here.
238 if (err == STATUS_OBJECT_NAME_COLLISION)
239 SetLastError( ERROR_FILE_EXISTS );
240 else
241 SetLastError( RtlNtStatusToDosError(err) );
244 if (!ret) WARN("Unable to create file '%s' (GLE %ld)\n", filename, GetLastError());
245 return ret;
249 static HANDLE FILE_OpenPipe(LPCWSTR name, DWORD access, LPSECURITY_ATTRIBUTES sa )
251 HANDLE ret;
252 DWORD len = 0;
254 if (name && (len = strlenW(name)) > MAX_PATH)
256 SetLastError( ERROR_FILENAME_EXCED_RANGE );
257 return 0;
259 SERVER_START_REQ( open_named_pipe )
261 req->access = access;
262 req->inherit = (sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle);
263 SetLastError(0);
264 wine_server_add_data( req, name, len * sizeof(WCHAR) );
265 wine_server_call_err( req );
266 ret = reply->handle;
268 SERVER_END_REQ;
269 TRACE("Returned %p\n",ret);
270 return ret;
273 /*************************************************************************
274 * CreateFileW [KERNEL32.@] Creates or opens a file or other object
276 * Creates or opens an object, and returns a handle that can be used to
277 * access that object.
279 * PARAMS
281 * filename [in] pointer to filename to be accessed
282 * access [in] access mode requested
283 * sharing [in] share mode
284 * sa [in] pointer to security attributes
285 * creation [in] how to create the file
286 * attributes [in] attributes for newly created file
287 * template [in] handle to file with extended attributes to copy
289 * RETURNS
290 * Success: Open handle to specified file
291 * Failure: INVALID_HANDLE_VALUE
293 * NOTES
294 * Should call SetLastError() on failure.
296 * BUGS
298 * Doesn't support character devices, template files, or a
299 * lot of the 'attributes' flags yet.
301 HANDLE WINAPI CreateFileW( LPCWSTR filename, DWORD access, DWORD sharing,
302 LPSECURITY_ATTRIBUTES sa, DWORD creation,
303 DWORD attributes, HANDLE template )
305 DOS_FULL_NAME full_name;
306 HANDLE ret;
307 DWORD dosdev;
308 static const WCHAR bkslashes_with_question_markW[] = {'\\','\\','?','\\',0};
309 static const WCHAR bkslashes_with_dotW[] = {'\\','\\','.','\\',0};
310 static const WCHAR bkslashesW[] = {'\\','\\',0};
311 static const WCHAR coninW[] = {'C','O','N','I','N','$',0};
312 static const WCHAR conoutW[] = {'C','O','N','O','U','T','$',0};
314 if (!filename || !filename[0])
316 SetLastError( ERROR_PATH_NOT_FOUND );
317 return INVALID_HANDLE_VALUE;
320 TRACE("%s %s%s%s%s%s%s%s attributes 0x%lx\n", debugstr_w(filename),
321 ((access & GENERIC_READ)==GENERIC_READ)?"GENERIC_READ ":"",
322 ((access & GENERIC_WRITE)==GENERIC_WRITE)?"GENERIC_WRITE ":"",
323 (!access)?"QUERY_ACCESS ":"",
324 ((sharing & FILE_SHARE_READ)==FILE_SHARE_READ)?"FILE_SHARE_READ ":"",
325 ((sharing & FILE_SHARE_WRITE)==FILE_SHARE_WRITE)?"FILE_SHARE_WRITE ":"",
326 ((sharing & FILE_SHARE_DELETE)==FILE_SHARE_DELETE)?"FILE_SHARE_DELETE ":"",
327 (creation ==CREATE_NEW)?"CREATE_NEW":
328 (creation ==CREATE_ALWAYS)?"CREATE_ALWAYS ":
329 (creation ==OPEN_EXISTING)?"OPEN_EXISTING ":
330 (creation ==OPEN_ALWAYS)?"OPEN_ALWAYS ":
331 (creation ==TRUNCATE_EXISTING)?"TRUNCATE_EXISTING ":"", attributes);
333 /* Open a console for CONIN$ or CONOUT$ */
334 if (!strcmpiW(filename, coninW) || !strcmpiW(filename, conoutW))
336 ret = OpenConsoleW(filename, access, (sa && sa->bInheritHandle), creation);
337 goto done;
340 /* If the name starts with '\\?\', ignore the first 4 chars. */
341 if (!strncmpW(filename, bkslashes_with_question_markW, 4))
343 static const WCHAR uncW[] = {'U','N','C','\\',0};
344 filename += 4;
345 if (!strncmpiW(filename, uncW, 4))
347 FIXME("UNC name (%s) not supported.\n", debugstr_w(filename) );
348 SetLastError( ERROR_PATH_NOT_FOUND );
349 return INVALID_HANDLE_VALUE;
353 if (!strncmpW(filename, bkslashes_with_dotW, 4))
355 static const WCHAR pipeW[] = {'P','I','P','E','\\',0};
356 if(!strncmpiW(filename + 4, pipeW, 5))
358 TRACE("Opening a pipe: %s\n", debugstr_w(filename));
359 ret = FILE_OpenPipe( filename, access, sa );
360 goto done;
362 else if (isalphaW(filename[4]) && filename[5] == ':' && filename[6] == '\0')
364 const char *device = DRIVE_GetDevice( toupperW(filename[4]) - 'A' );
365 if (device)
367 ret = FILE_CreateFile( device, access, sharing, sa, creation,
368 attributes, template, DRIVE_FIXED );
370 else
372 SetLastError( ERROR_ACCESS_DENIED );
373 ret = INVALID_HANDLE_VALUE;
375 goto done;
377 else if ((dosdev = RtlIsDosDeviceName_U( filename + 4 )))
379 dosdev += MAKELONG( 0, 4*sizeof(WCHAR) ); /* adjust position to start of filename */
381 else
383 ret = VXD_Open( filename+4, access, sa );
384 goto done;
387 else dosdev = RtlIsDosDeviceName_U( filename );
389 if (dosdev)
391 static const WCHAR conW[] = {'C','O','N',0};
392 WCHAR dev[5];
394 memcpy( dev, filename + HIWORD(dosdev)/sizeof(WCHAR), LOWORD(dosdev) );
395 dev[LOWORD(dosdev)/sizeof(WCHAR)] = 0;
397 TRACE("opening device %s\n", debugstr_w(dev) );
399 if (!strcmpiW( dev, conW ))
401 switch (access & (GENERIC_READ|GENERIC_WRITE))
403 case GENERIC_READ:
404 ret = OpenConsoleW(coninW, access, (sa && sa->bInheritHandle), creation);
405 goto done;
406 case GENERIC_WRITE:
407 ret = OpenConsoleW(conoutW, access, (sa && sa->bInheritHandle), creation);
408 goto done;
409 default:
410 FIXME("can't open CON read/write\n");
411 SetLastError( ERROR_FILE_NOT_FOUND );
412 return INVALID_HANDLE_VALUE;
416 ret = VOLUME_OpenDevice( dev, access, sharing, sa, attributes );
417 goto done;
420 /* If the name still starts with '\\', it's a UNC name. */
421 if (!strncmpW(filename, bkslashesW, 2))
423 ret = SMB_CreateFileW(filename, access, sharing, sa, creation, attributes, template );
424 goto done;
427 /* If the name contains a DOS wild card (* or ?), do no create a file */
428 if(strchrW(filename, '*') || strchrW(filename, '?'))
430 SetLastError(ERROR_BAD_PATHNAME);
431 return INVALID_HANDLE_VALUE;
434 /* check for filename, don't check for last entry if creating */
435 if (!DOSFS_GetFullName( filename,
436 (creation == OPEN_EXISTING) ||
437 (creation == TRUNCATE_EXISTING),
438 &full_name )) {
439 WARN("Unable to get full filename from %s (GLE %ld)\n",
440 debugstr_w(filename), GetLastError());
441 return INVALID_HANDLE_VALUE;
444 ret = FILE_CreateFile( full_name.long_name, access, sharing,
445 sa, creation, attributes, template,
446 GetDriveTypeW( full_name.short_name ) );
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 info->nFileSizeHigh = 0;
503 info->nFileSizeLow = 0;
504 if (!S_ISDIR(st->st_mode)) {
505 info->nFileSizeHigh = st->st_size >> 32;
506 info->nFileSizeLow = st->st_size & 0xffffffff;
508 info->nNumberOfLinks = st->st_nlink;
509 info->nFileIndexHigh = 0;
510 info->nFileIndexLow = st->st_ino;
514 /***********************************************************************
515 * get_show_dot_files_option
517 static BOOL get_show_dot_files_option(void)
519 static const WCHAR WineW[] = {'M','a','c','h','i','n','e','\\',
520 'S','o','f','t','w','a','r','e','\\',
521 'W','i','n','e','\\','W','i','n','e','\\',
522 'C','o','n','f','i','g','\\','W','i','n','e',0};
523 static const WCHAR ShowDotFilesW[] = {'S','h','o','w','D','o','t','F','i','l','e','s',0};
525 char tmp[80];
526 HKEY hkey;
527 DWORD dummy;
528 OBJECT_ATTRIBUTES attr;
529 UNICODE_STRING nameW;
530 BOOL ret = FALSE;
532 attr.Length = sizeof(attr);
533 attr.RootDirectory = 0;
534 attr.ObjectName = &nameW;
535 attr.Attributes = 0;
536 attr.SecurityDescriptor = NULL;
537 attr.SecurityQualityOfService = NULL;
538 RtlInitUnicodeString( &nameW, WineW );
540 if (!NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr ))
542 RtlInitUnicodeString( &nameW, ShowDotFilesW );
543 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
545 WCHAR *str = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
546 ret = IS_OPTION_TRUE( str[0] );
548 NtClose( hkey );
550 return ret;
554 /***********************************************************************
555 * FILE_Stat
557 * Stat a Unix path name. Return TRUE if OK.
559 BOOL FILE_Stat( LPCSTR unixName, BY_HANDLE_FILE_INFORMATION *info, BOOL *is_symlink_ptr )
561 struct stat st;
562 int is_symlink;
563 LPCSTR p;
565 if (lstat( unixName, &st ) == -1)
567 FILE_SetDosError();
568 return FALSE;
570 is_symlink = S_ISLNK(st.st_mode);
571 if (is_symlink)
573 /* do a "real" stat to find out
574 about the type of the symlink destination */
575 if (stat( unixName, &st ) == -1)
577 FILE_SetDosError();
578 return FALSE;
582 /* fill in the information we gathered so far */
583 FILE_FillInfo( &st, info );
585 /* and now see if this is a hidden file, based on the name */
586 p = strrchr( unixName, '/');
587 p = p ? p + 1 : unixName;
588 if (*p == '.' && *(p+1) && (*(p+1) != '.' || *(p+2)))
590 static int show_dot_files = -1;
591 if (show_dot_files == -1)
592 show_dot_files = get_show_dot_files_option();
593 if (!show_dot_files)
594 info->dwFileAttributes |= FILE_ATTRIBUTE_HIDDEN;
596 if (is_symlink_ptr) *is_symlink_ptr = is_symlink;
597 return TRUE;
601 /***********************************************************************
602 * GetFileInformationByHandle (KERNEL32.@)
604 DWORD WINAPI GetFileInformationByHandle( HANDLE hFile,
605 BY_HANDLE_FILE_INFORMATION *info )
607 DWORD ret;
608 if (!info) return 0;
610 TRACE("%p\n", hFile);
612 SERVER_START_REQ( get_file_info )
614 req->handle = hFile;
615 if ((ret = !wine_server_call_err( req )))
617 /* FIXME: which file types are supported ?
618 * Serial ports (FILE_TYPE_CHAR) are not,
619 * and MSDN also says that pipes are not supported.
620 * FILE_TYPE_REMOTE seems to be supported according to
621 * MSDN q234741.txt */
622 if ((reply->type == FILE_TYPE_DISK) || (reply->type == FILE_TYPE_REMOTE))
624 RtlSecondsSince1970ToTime( reply->write_time, (LARGE_INTEGER *)&info->ftCreationTime );
625 RtlSecondsSince1970ToTime( reply->write_time, (LARGE_INTEGER *)&info->ftLastWriteTime );
626 RtlSecondsSince1970ToTime( reply->access_time, (LARGE_INTEGER *)&info->ftLastAccessTime );
627 info->dwFileAttributes = reply->attr;
628 info->dwVolumeSerialNumber = reply->serial;
629 info->nFileSizeHigh = reply->size_high;
630 info->nFileSizeLow = reply->size_low;
631 info->nNumberOfLinks = reply->links;
632 info->nFileIndexHigh = reply->index_high;
633 info->nFileIndexLow = reply->index_low;
635 else
637 SetLastError(ERROR_NOT_SUPPORTED);
638 ret = 0;
642 SERVER_END_REQ;
643 return ret;
647 /**************************************************************************
648 * GetFileAttributesW (KERNEL32.@)
650 DWORD WINAPI GetFileAttributesW( LPCWSTR name )
652 DOS_FULL_NAME full_name;
653 BY_HANDLE_FILE_INFORMATION info;
655 if (name == NULL)
657 SetLastError( ERROR_INVALID_PARAMETER );
658 return INVALID_FILE_ATTRIBUTES;
660 if (!DOSFS_GetFullName( name, TRUE, &full_name) )
661 return INVALID_FILE_ATTRIBUTES;
662 if (!FILE_Stat( full_name.long_name, &info, NULL ))
663 return INVALID_FILE_ATTRIBUTES;
664 return info.dwFileAttributes;
668 /**************************************************************************
669 * GetFileAttributesA (KERNEL32.@)
671 DWORD WINAPI GetFileAttributesA( LPCSTR name )
673 UNICODE_STRING nameW;
674 DWORD ret = INVALID_FILE_ATTRIBUTES;
676 if (!name)
678 SetLastError( ERROR_INVALID_PARAMETER );
679 return INVALID_FILE_ATTRIBUTES;
682 if (RtlCreateUnicodeStringFromAsciiz(&nameW, name))
684 ret = GetFileAttributesW(nameW.Buffer);
685 RtlFreeUnicodeString(&nameW);
687 else
688 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
689 return ret;
693 /**************************************************************************
694 * SetFileAttributesW (KERNEL32.@)
696 BOOL WINAPI SetFileAttributesW(LPCWSTR lpFileName, DWORD attributes)
698 struct stat buf;
699 DOS_FULL_NAME full_name;
701 if (!lpFileName)
703 SetLastError( ERROR_INVALID_PARAMETER );
704 return FALSE;
707 TRACE("(%s,%lx)\n", debugstr_w(lpFileName), attributes);
709 if (!DOSFS_GetFullName( lpFileName, TRUE, &full_name ))
710 return FALSE;
712 if(stat(full_name.long_name,&buf)==-1)
714 FILE_SetDosError();
715 return FALSE;
717 if (attributes & FILE_ATTRIBUTE_READONLY)
719 if(S_ISDIR(buf.st_mode))
720 /* FIXME */
721 WARN("FILE_ATTRIBUTE_READONLY ignored for directory.\n");
722 else
723 buf.st_mode &= ~0222; /* octal!, clear write permission bits */
724 attributes &= ~FILE_ATTRIBUTE_READONLY;
726 else
728 /* add write permission */
729 buf.st_mode |= (0600 | ((buf.st_mode & 044) >> 1)) & (~FILE_umask);
731 if (attributes & FILE_ATTRIBUTE_DIRECTORY)
733 if (!S_ISDIR(buf.st_mode))
734 FIXME("SetFileAttributes expected the file %s to be a directory\n",
735 debugstr_w(lpFileName));
736 attributes &= ~FILE_ATTRIBUTE_DIRECTORY;
738 attributes &= ~(FILE_ATTRIBUTE_NORMAL|FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN|
739 FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_TEMPORARY);
740 if (attributes)
741 FIXME("(%s):%lx attribute(s) not implemented.\n", debugstr_w(lpFileName), attributes);
742 if (-1==chmod(full_name.long_name,buf.st_mode))
744 if (GetDriveTypeW(lpFileName) == DRIVE_CDROM)
746 SetLastError( ERROR_ACCESS_DENIED );
747 return FALSE;
751 * FIXME: We don't return FALSE here because of differences between
752 * Linux and Windows privileges. Under Linux only the owner of
753 * the file is allowed to change file attributes. Under Windows,
754 * applications expect that if you can write to a file, you can also
755 * change its attributes (see GENERIC_WRITE). We could try to be
756 * clever here but that would break multi-user installations where
757 * users share read-only DLLs. This is because some installers like
758 * to change attributes of already installed DLLs.
760 FIXME("Couldn't set file attributes for existing file \"%s\".\n"
761 "Check permissions or set VFAT \"quiet\" mount flag\n", full_name.long_name);
763 return TRUE;
767 /**************************************************************************
768 * SetFileAttributesA (KERNEL32.@)
770 BOOL WINAPI SetFileAttributesA(LPCSTR lpFileName, DWORD attributes)
772 UNICODE_STRING filenameW;
773 BOOL ret = FALSE;
775 if (!lpFileName)
777 SetLastError( ERROR_INVALID_PARAMETER );
778 return FALSE;
781 if (RtlCreateUnicodeStringFromAsciiz(&filenameW, lpFileName))
783 ret = SetFileAttributesW(filenameW.Buffer, attributes);
784 RtlFreeUnicodeString(&filenameW);
786 else
787 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
788 return ret;
792 /******************************************************************************
793 * GetCompressedFileSizeA [KERNEL32.@]
795 DWORD WINAPI GetCompressedFileSizeA(
796 LPCSTR lpFileName,
797 LPDWORD lpFileSizeHigh)
799 UNICODE_STRING filenameW;
800 DWORD ret;
802 if (RtlCreateUnicodeStringFromAsciiz(&filenameW, lpFileName))
804 ret = GetCompressedFileSizeW(filenameW.Buffer, lpFileSizeHigh);
805 RtlFreeUnicodeString(&filenameW);
807 else
809 ret = INVALID_FILE_SIZE;
810 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
812 return ret;
816 /******************************************************************************
817 * GetCompressedFileSizeW [KERNEL32.@]
819 * RETURNS
820 * Success: Low-order doubleword of number of bytes
821 * Failure: INVALID_FILE_SIZE
823 DWORD WINAPI GetCompressedFileSizeW(
824 LPCWSTR lpFileName, /* [in] Pointer to name of file */
825 LPDWORD lpFileSizeHigh) /* [out] Receives high-order doubleword of size */
827 DOS_FULL_NAME full_name;
828 struct stat st;
829 DWORD low;
831 TRACE("(%s,%p)\n",debugstr_w(lpFileName),lpFileSizeHigh);
833 if (!DOSFS_GetFullName( lpFileName, TRUE, &full_name )) return INVALID_FILE_SIZE;
834 if (stat(full_name.long_name, &st) != 0)
836 FILE_SetDosError();
837 return INVALID_FILE_SIZE;
839 #if HAVE_STRUCT_STAT_ST_BLOCKS
840 /* blocks are 512 bytes long */
841 if (lpFileSizeHigh) *lpFileSizeHigh = (st.st_blocks >> 23);
842 low = (DWORD)(st.st_blocks << 9);
843 #else
844 if (lpFileSizeHigh) *lpFileSizeHigh = (st.st_size >> 32);
845 low = (DWORD)st.st_size;
846 #endif
847 return low;
851 /***********************************************************************
852 * GetFileTime (KERNEL32.@)
854 BOOL WINAPI GetFileTime( HANDLE hFile, FILETIME *lpCreationTime,
855 FILETIME *lpLastAccessTime,
856 FILETIME *lpLastWriteTime )
858 BY_HANDLE_FILE_INFORMATION info;
859 if (!GetFileInformationByHandle( hFile, &info )) return FALSE;
860 if (lpCreationTime) *lpCreationTime = info.ftCreationTime;
861 if (lpLastAccessTime) *lpLastAccessTime = info.ftLastAccessTime;
862 if (lpLastWriteTime) *lpLastWriteTime = info.ftLastWriteTime;
863 return TRUE;
867 /***********************************************************************
868 * GetTempFileNameA (KERNEL32.@)
870 UINT WINAPI GetTempFileNameA( LPCSTR path, LPCSTR prefix, UINT unique,
871 LPSTR buffer)
873 UNICODE_STRING pathW, prefixW;
874 WCHAR bufferW[MAX_PATH];
875 UINT ret;
877 if ( !path || !prefix || !buffer )
879 SetLastError( ERROR_INVALID_PARAMETER );
880 return 0;
883 RtlCreateUnicodeStringFromAsciiz(&pathW, path);
884 RtlCreateUnicodeStringFromAsciiz(&prefixW, prefix);
886 ret = GetTempFileNameW(pathW.Buffer, prefixW.Buffer, unique, bufferW);
887 if (ret)
888 WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buffer, MAX_PATH, NULL, NULL);
890 RtlFreeUnicodeString(&pathW);
891 RtlFreeUnicodeString(&prefixW);
892 return ret;
895 /***********************************************************************
896 * GetTempFileNameW (KERNEL32.@)
898 UINT WINAPI GetTempFileNameW( LPCWSTR path, LPCWSTR prefix, UINT unique,
899 LPWSTR buffer )
901 static const WCHAR formatW[] = {'%','x','.','t','m','p',0};
903 DOS_FULL_NAME full_name;
904 int i;
905 LPWSTR p;
907 if ( !path || !prefix || !buffer )
909 SetLastError( ERROR_INVALID_PARAMETER );
910 return 0;
913 strcpyW( buffer, path );
914 p = buffer + strlenW(buffer);
916 /* add a \, if there isn't one */
917 if ((p == buffer) || (p[-1] != '\\')) *p++ = '\\';
919 for (i = 3; (i > 0) && (*prefix); i--) *p++ = *prefix++;
921 unique &= 0xffff;
923 if (unique) sprintfW( p, formatW, unique );
924 else
926 /* get a "random" unique number and try to create the file */
927 HANDLE handle;
928 UINT num = GetTickCount() & 0xffff;
930 if (!num) num = 1;
931 unique = num;
934 sprintfW( p, formatW, unique );
935 handle = CreateFileW( buffer, GENERIC_WRITE, 0, NULL,
936 CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0 );
937 if (handle != INVALID_HANDLE_VALUE)
938 { /* We created it */
939 TRACE("created %s\n", debugstr_w(buffer) );
940 CloseHandle( handle );
941 break;
943 if (GetLastError() != ERROR_FILE_EXISTS &&
944 GetLastError() != ERROR_SHARING_VIOLATION)
945 break; /* No need to go on */
946 if (!(++unique & 0xffff)) unique = 1;
947 } while (unique != num);
950 /* Get the full path name */
952 if (DOSFS_GetFullName( buffer, FALSE, &full_name ))
954 char *slash;
955 /* Check if we have write access in the directory */
956 if ((slash = strrchr( full_name.long_name, '/' ))) *slash = '\0';
957 if (access( full_name.long_name, W_OK ) == -1)
958 WARN("returns %s, which doesn't seem to be writeable.\n",
959 debugstr_w(buffer) );
961 TRACE("returning %s\n", debugstr_w(buffer) );
962 return unique;
966 /******************************************************************
967 * FILE_ReadWriteApc (internal)
971 static void WINAPI FILE_ReadWriteApc(void* apc_user, PIO_STATUS_BLOCK io_status, ULONG len)
973 LPOVERLAPPED_COMPLETION_ROUTINE cr = (LPOVERLAPPED_COMPLETION_ROUTINE)apc_user;
975 cr(RtlNtStatusToDosError(io_status->u.Status), len, (LPOVERLAPPED)io_status);
978 /***********************************************************************
979 * ReadFileEx (KERNEL32.@)
981 BOOL WINAPI ReadFileEx(HANDLE hFile, LPVOID buffer, DWORD bytesToRead,
982 LPOVERLAPPED overlapped,
983 LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
985 LARGE_INTEGER offset;
986 NTSTATUS status;
987 PIO_STATUS_BLOCK io_status;
989 if (!overlapped)
991 SetLastError(ERROR_INVALID_PARAMETER);
992 return FALSE;
995 offset.u.LowPart = overlapped->Offset;
996 offset.u.HighPart = overlapped->OffsetHigh;
997 io_status = (PIO_STATUS_BLOCK)overlapped;
998 io_status->u.Status = STATUS_PENDING;
1000 status = NtReadFile(hFile, NULL, FILE_ReadWriteApc, lpCompletionRoutine,
1001 io_status, buffer, bytesToRead, &offset, NULL);
1003 if (status)
1005 SetLastError( RtlNtStatusToDosError(status) );
1006 return FALSE;
1008 return TRUE;
1011 /***********************************************************************
1012 * ReadFile (KERNEL32.@)
1014 BOOL WINAPI ReadFile( HANDLE hFile, LPVOID buffer, DWORD bytesToRead,
1015 LPDWORD bytesRead, LPOVERLAPPED overlapped )
1017 LARGE_INTEGER offset;
1018 PLARGE_INTEGER poffset = NULL;
1019 IO_STATUS_BLOCK iosb;
1020 PIO_STATUS_BLOCK io_status = &iosb;
1021 HANDLE hEvent = 0;
1022 NTSTATUS status;
1024 TRACE("%p %p %ld %p %p\n", hFile, buffer, bytesToRead,
1025 bytesRead, overlapped );
1027 if (bytesRead) *bytesRead = 0; /* Do this before anything else */
1028 if (!bytesToRead) return TRUE;
1030 if (IsBadReadPtr(buffer, bytesToRead))
1032 SetLastError(ERROR_WRITE_FAULT); /* FIXME */
1033 return FALSE;
1035 if (is_console_handle(hFile))
1036 return ReadConsoleA(hFile, buffer, bytesToRead, bytesRead, NULL);
1038 if (overlapped != NULL)
1040 offset.u.LowPart = overlapped->Offset;
1041 offset.u.HighPart = overlapped->OffsetHigh;
1042 poffset = &offset;
1043 hEvent = overlapped->hEvent;
1044 io_status = (PIO_STATUS_BLOCK)overlapped;
1046 io_status->u.Status = STATUS_PENDING;
1047 io_status->Information = 0;
1049 status = NtReadFile(hFile, hEvent, NULL, NULL, io_status, buffer, bytesToRead, poffset, NULL);
1051 if (status != STATUS_PENDING && bytesRead)
1052 *bytesRead = io_status->Information;
1054 if (status && status != STATUS_END_OF_FILE)
1056 SetLastError( RtlNtStatusToDosError(status) );
1057 return FALSE;
1059 return TRUE;
1063 /***********************************************************************
1064 * WriteFileEx (KERNEL32.@)
1066 BOOL WINAPI WriteFileEx(HANDLE hFile, LPCVOID buffer, DWORD bytesToWrite,
1067 LPOVERLAPPED overlapped,
1068 LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
1070 LARGE_INTEGER offset;
1071 NTSTATUS status;
1072 PIO_STATUS_BLOCK io_status;
1074 TRACE("%p %p %ld %p %p\n",
1075 hFile, buffer, bytesToWrite, overlapped, lpCompletionRoutine);
1077 if (overlapped == NULL)
1079 SetLastError(ERROR_INVALID_PARAMETER);
1080 return FALSE;
1082 offset.u.LowPart = overlapped->Offset;
1083 offset.u.HighPart = overlapped->OffsetHigh;
1085 io_status = (PIO_STATUS_BLOCK)overlapped;
1086 io_status->u.Status = STATUS_PENDING;
1088 status = NtWriteFile(hFile, NULL, FILE_ReadWriteApc, lpCompletionRoutine,
1089 io_status, buffer, bytesToWrite, &offset, NULL);
1091 if (status) SetLastError( RtlNtStatusToDosError(status) );
1092 return !status;
1095 /***********************************************************************
1096 * WriteFile (KERNEL32.@)
1098 BOOL WINAPI WriteFile( HANDLE hFile, LPCVOID buffer, DWORD bytesToWrite,
1099 LPDWORD bytesWritten, LPOVERLAPPED overlapped )
1101 HANDLE hEvent = NULL;
1102 LARGE_INTEGER offset;
1103 PLARGE_INTEGER poffset = NULL;
1104 NTSTATUS status;
1105 IO_STATUS_BLOCK iosb;
1106 PIO_STATUS_BLOCK piosb = &iosb;
1108 TRACE("%p %p %ld %p %p\n",
1109 hFile, buffer, bytesToWrite, bytesWritten, overlapped );
1111 if (is_console_handle(hFile))
1112 return WriteConsoleA(hFile, buffer, bytesToWrite, bytesWritten, NULL);
1114 if (IsBadReadPtr(buffer, bytesToWrite))
1116 SetLastError(ERROR_READ_FAULT); /* FIXME */
1117 return FALSE;
1120 if (overlapped)
1122 offset.u.LowPart = overlapped->Offset;
1123 offset.u.HighPart = overlapped->OffsetHigh;
1124 poffset = &offset;
1125 hEvent = overlapped->hEvent;
1126 piosb = (PIO_STATUS_BLOCK)overlapped;
1128 piosb->u.Status = STATUS_PENDING;
1129 piosb->Information = 0;
1131 status = NtWriteFile(hFile, hEvent, NULL, NULL, piosb,
1132 buffer, bytesToWrite, poffset, NULL);
1133 if (status)
1135 SetLastError( RtlNtStatusToDosError(status) );
1136 return FALSE;
1138 if (bytesWritten) *bytesWritten = piosb->Information;
1140 return TRUE;
1144 /***********************************************************************
1145 * SetFilePointer (KERNEL32.@)
1147 DWORD WINAPI SetFilePointer( HANDLE hFile, LONG distance, LONG *highword,
1148 DWORD method )
1150 DWORD ret = INVALID_SET_FILE_POINTER;
1152 TRACE("handle %p offset %ld high %ld origin %ld\n",
1153 hFile, distance, highword?*highword:0, method );
1155 SERVER_START_REQ( set_file_pointer )
1157 req->handle = hFile;
1158 req->low = distance;
1159 req->high = highword ? *highword : (distance >= 0) ? 0 : -1;
1160 /* FIXME: assumes 1:1 mapping between Windows and Unix seek constants */
1161 req->whence = method;
1162 SetLastError( 0 );
1163 if (!wine_server_call_err( req ))
1165 ret = reply->new_low;
1166 if (highword) *highword = reply->new_high;
1169 SERVER_END_REQ;
1170 return ret;
1174 /*************************************************************************
1175 * SetHandleCount (KERNEL32.@)
1177 UINT WINAPI SetHandleCount( UINT count )
1179 return min( 256, count );
1183 /**************************************************************************
1184 * SetEndOfFile (KERNEL32.@)
1186 BOOL WINAPI SetEndOfFile( HANDLE hFile )
1188 BOOL ret;
1189 SERVER_START_REQ( truncate_file )
1191 req->handle = hFile;
1192 ret = !wine_server_call_err( req );
1194 SERVER_END_REQ;
1195 return ret;
1199 /***********************************************************************
1200 * GetFileType (KERNEL32.@)
1202 DWORD WINAPI GetFileType( HANDLE hFile )
1204 DWORD ret = FILE_TYPE_UNKNOWN;
1206 if (is_console_handle( hFile ))
1207 return FILE_TYPE_CHAR;
1209 SERVER_START_REQ( get_file_info )
1211 req->handle = hFile;
1212 if (!wine_server_call_err( req )) ret = reply->type;
1214 SERVER_END_REQ;
1215 return ret;
1219 /* check if a file name is for an executable file (.exe or .com) */
1220 inline static BOOL is_executable( const char *name )
1222 int len = strlen(name);
1224 if (len < 4) return FALSE;
1225 return (!strcasecmp( name + len - 4, ".exe" ) ||
1226 !strcasecmp( name + len - 4, ".com" ));
1230 /***********************************************************************
1231 * FILE_AddBootRenameEntry
1233 * Adds an entry to the registry that is loaded when windows boots and
1234 * checks if there are some files to be removed or renamed/moved.
1235 * <fn1> has to be valid and <fn2> may be NULL. If both pointers are
1236 * non-NULL then the file is moved, otherwise it is deleted. The
1237 * entry of the registrykey is always appended with two zero
1238 * terminated strings. If <fn2> is NULL then the second entry is
1239 * simply a single 0-byte. Otherwise the second filename goes
1240 * there. The entries are prepended with \??\ before the path and the
1241 * second filename gets also a '!' as the first character if
1242 * MOVEFILE_REPLACE_EXISTING is set. After the final string another
1243 * 0-byte follows to indicate the end of the strings.
1244 * i.e.:
1245 * \??\D:\test\file1[0]
1246 * !\??\D:\test\file1_renamed[0]
1247 * \??\D:\Test|delete[0]
1248 * [0] <- file is to be deleted, second string empty
1249 * \??\D:\test\file2[0]
1250 * !\??\D:\test\file2_renamed[0]
1251 * [0] <- indicates end of strings
1253 * or:
1254 * \??\D:\test\file1[0]
1255 * !\??\D:\test\file1_renamed[0]
1256 * \??\D:\Test|delete[0]
1257 * [0] <- file is to be deleted, second string empty
1258 * [0] <- indicates end of strings
1261 static BOOL FILE_AddBootRenameEntry( LPCWSTR fn1, LPCWSTR fn2, DWORD flags )
1263 static const WCHAR PreString[] = {'\\','?','?','\\',0};
1264 static const WCHAR ValueName[] = {'P','e','n','d','i','n','g',
1265 'F','i','l','e','R','e','n','a','m','e',
1266 'O','p','e','r','a','t','i','o','n','s',0};
1267 static const WCHAR SessionW[] = {'M','a','c','h','i','n','e','\\',
1268 'S','y','s','t','e','m','\\',
1269 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
1270 'C','o','n','t','r','o','l','\\',
1271 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r',0};
1272 static const int info_size = FIELD_OFFSET( KEY_VALUE_PARTIAL_INFORMATION, Data );
1274 OBJECT_ATTRIBUTES attr;
1275 UNICODE_STRING nameW;
1276 KEY_VALUE_PARTIAL_INFORMATION *info;
1277 BOOL rc = FALSE;
1278 HKEY Reboot = 0;
1279 DWORD len0, len1, len2;
1280 DWORD DataSize = 0;
1281 BYTE *Buffer = NULL;
1282 WCHAR *p;
1284 attr.Length = sizeof(attr);
1285 attr.RootDirectory = 0;
1286 attr.ObjectName = &nameW;
1287 attr.Attributes = 0;
1288 attr.SecurityDescriptor = NULL;
1289 attr.SecurityQualityOfService = NULL;
1290 RtlInitUnicodeString( &nameW, SessionW );
1292 if (NtCreateKey( &Reboot, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ) != STATUS_SUCCESS)
1294 WARN("Error creating key for reboot managment [%s]\n",
1295 "SYSTEM\\CurrentControlSet\\Control\\Session Manager");
1296 return FALSE;
1299 len0 = strlenW(PreString);
1300 len1 = strlenW(fn1) + len0 + 1;
1301 if (fn2)
1303 len2 = strlenW(fn2) + len0 + 1;
1304 if (flags & MOVEFILE_REPLACE_EXISTING) len2++; /* Plus 1 because of the leading '!' */
1306 else len2 = 1; /* minimum is the 0 characters for the empty second string */
1308 /* convert characters to bytes */
1309 len0 *= sizeof(WCHAR);
1310 len1 *= sizeof(WCHAR);
1311 len2 *= sizeof(WCHAR);
1313 RtlInitUnicodeString( &nameW, ValueName );
1315 /* First we check if the key exists and if so how many bytes it already contains. */
1316 if (NtQueryValueKey( Reboot, &nameW, KeyValuePartialInformation,
1317 NULL, 0, &DataSize ) == STATUS_BUFFER_OVERFLOW)
1319 if (!(Buffer = HeapAlloc( GetProcessHeap(), 0, DataSize + len1 + len2 + sizeof(WCHAR) )))
1320 goto Quit;
1321 if (NtQueryValueKey( Reboot, &nameW, KeyValuePartialInformation,
1322 Buffer, DataSize, &DataSize )) goto Quit;
1323 info = (KEY_VALUE_PARTIAL_INFORMATION *)Buffer;
1324 if (info->Type != REG_MULTI_SZ) goto Quit;
1325 if (DataSize > sizeof(info)) DataSize -= sizeof(WCHAR); /* remove terminating null (will be added back later) */
1327 else
1329 DataSize = info_size;
1330 if (!(Buffer = HeapAlloc( GetProcessHeap(), 0, DataSize + len1 + len2 + sizeof(WCHAR) )))
1331 goto Quit;
1334 p = (WCHAR *)(Buffer + DataSize);
1335 strcpyW( p, PreString );
1336 strcatW( p, fn1 );
1337 DataSize += len1;
1338 if (fn2)
1340 p = (WCHAR *)(Buffer + DataSize);
1341 if (flags & MOVEFILE_REPLACE_EXISTING)
1342 *p++ = '!';
1343 strcpyW( p, PreString );
1344 strcatW( p, fn2 );
1345 DataSize += len2;
1347 else
1349 p = (WCHAR *)(Buffer + DataSize);
1350 *p = 0;
1351 DataSize += sizeof(WCHAR);
1354 /* add final null */
1355 p = (WCHAR *)(Buffer + DataSize);
1356 *p = 0;
1357 DataSize += sizeof(WCHAR);
1359 rc = !NtSetValueKey(Reboot, &nameW, 0, REG_MULTI_SZ, Buffer + info_size, DataSize - info_size);
1361 Quit:
1362 if (Reboot) NtClose(Reboot);
1363 if (Buffer) HeapFree( GetProcessHeap(), 0, Buffer );
1364 return(rc);
1368 /**************************************************************************
1369 * MoveFileExW (KERNEL32.@)
1371 BOOL WINAPI MoveFileExW( LPCWSTR fn1, LPCWSTR fn2, DWORD flag )
1373 DOS_FULL_NAME full_name1, full_name2;
1374 HANDLE hFile;
1375 DWORD attr = INVALID_FILE_ATTRIBUTES;
1377 TRACE("(%s,%s,%04lx)\n", debugstr_w(fn1), debugstr_w(fn2), flag);
1379 /* FIXME: <Gerhard W. Gruber>sparhawk@gmx.at
1380 In case of W9x and lesser this function should return 120 (ERROR_CALL_NOT_IMPLEMENTED)
1381 to be really compatible. Most programs won't have any problems though. In case
1382 you encounter one, this is what you should return here. I don't know what's up
1383 with NT 3.5. Is this function available there or not?
1384 Does anybody really care about 3.5? :)
1387 /* Filename1 has to be always set to a valid path. Filename2 may be NULL
1388 if the source file has to be deleted.
1390 if (!fn1) {
1391 SetLastError(ERROR_INVALID_PARAMETER);
1392 return FALSE;
1395 /* This function has to be run through in order to process the name properly.
1396 If the BOOTDELAY flag is set, the file doesn't need to exist though. At least
1397 that is the behaviour on NT 4.0. The operation accepts the filenames as
1398 they are given but it can't reply with a reasonable returncode. Success
1399 means in that case success for entering the values into the registry.
1401 if(!DOSFS_GetFullName( fn1, TRUE, &full_name1 ))
1403 if(!(flag & MOVEFILE_DELAY_UNTIL_REBOOT))
1404 return FALSE;
1407 if (fn2) /* !fn2 means delete fn1 */
1409 if (DOSFS_GetFullName( fn2, TRUE, &full_name2 ))
1411 if(!(flag & MOVEFILE_DELAY_UNTIL_REBOOT))
1413 /* target exists, check if we may overwrite */
1414 if (!(flag & MOVEFILE_REPLACE_EXISTING))
1416 SetLastError( ERROR_ALREADY_EXISTS );
1417 return FALSE;
1421 else
1423 if (!DOSFS_GetFullName( fn2, FALSE, &full_name2 ))
1425 if(!(flag & MOVEFILE_DELAY_UNTIL_REBOOT))
1426 return FALSE;
1430 /* Source name and target path are valid */
1432 if (flag & MOVEFILE_DELAY_UNTIL_REBOOT)
1434 return FILE_AddBootRenameEntry( fn1, fn2, flag );
1437 attr = GetFileAttributesW( fn1 );
1438 if ( attr == INVALID_FILE_ATTRIBUTES ) return FALSE;
1440 /* check if we are allowed to rename the source */
1441 hFile = FILE_CreateFile( full_name1.long_name, 0, 0,
1442 NULL, OPEN_EXISTING, 0, 0,
1443 GetDriveTypeW( full_name1.short_name ) );
1444 if (!hFile)
1446 if (GetLastError() != ERROR_ACCESS_DENIED) return FALSE;
1447 if ( !(attr & FILE_ATTRIBUTE_DIRECTORY) ) return FALSE;
1448 /* if it's a directory we can continue */
1450 else CloseHandle(hFile);
1452 /* check, if we are allowed to delete the destination,
1453 ** (but the file not being there is fine) */
1454 hFile = FILE_CreateFile( full_name2.long_name, GENERIC_READ|GENERIC_WRITE, 0,
1455 NULL, OPEN_EXISTING, 0, 0,
1456 GetDriveTypeW( full_name2.short_name ) );
1457 if(!hFile && GetLastError() != ERROR_FILE_NOT_FOUND) return FALSE;
1458 CloseHandle(hFile);
1460 if (full_name1.drive != full_name2.drive)
1462 if (!(flag & MOVEFILE_COPY_ALLOWED))
1464 SetLastError( ERROR_NOT_SAME_DEVICE );
1465 return FALSE;
1467 else if ( attr & FILE_ATTRIBUTE_DIRECTORY )
1469 /* Strange, but that's what Windows returns */
1470 SetLastError ( ERROR_ACCESS_DENIED );
1471 return FALSE;
1474 if (rename( full_name1.long_name, full_name2.long_name ) == -1)
1475 /* Try copy/delete unless it's a directory. */
1476 /* FIXME: This does not handle the (unlikely) case that the two locations
1477 are on the same Wine drive, but on different Unix file systems. */
1479 if ( attr & FILE_ATTRIBUTE_DIRECTORY )
1481 FILE_SetDosError();
1482 return FALSE;
1484 else
1486 if ( ! CopyFileW( fn1, fn2, !(flag & MOVEFILE_REPLACE_EXISTING) ))
1487 return FALSE;
1488 if ( ! DeleteFileW ( fn1 ) )
1489 return FALSE;
1492 if (is_executable( full_name1.long_name ) != is_executable( full_name2.long_name ))
1494 struct stat fstat;
1495 if (stat( full_name2.long_name, &fstat ) != -1)
1497 if (is_executable( full_name2.long_name ))
1498 /* set executable bit where read bit is set */
1499 fstat.st_mode |= (fstat.st_mode & 0444) >> 2;
1500 else
1501 fstat.st_mode &= ~0111;
1502 chmod( full_name2.long_name, fstat.st_mode );
1505 return TRUE;
1507 else /* fn2 == NULL means delete source */
1509 if (flag & MOVEFILE_DELAY_UNTIL_REBOOT)
1511 if (flag & MOVEFILE_COPY_ALLOWED) {
1512 WARN("Illegal flag\n");
1513 SetLastError( ERROR_GEN_FAILURE );
1514 return FALSE;
1517 return FILE_AddBootRenameEntry( fn1, NULL, flag );
1520 if (unlink( full_name1.long_name ) == -1)
1522 FILE_SetDosError();
1523 return FALSE;
1525 return TRUE; /* successfully deleted */
1529 /**************************************************************************
1530 * MoveFileExA (KERNEL32.@)
1532 BOOL WINAPI MoveFileExA( LPCSTR fn1, LPCSTR fn2, DWORD flag )
1534 UNICODE_STRING fn1W, fn2W;
1535 BOOL ret;
1537 if (!fn1)
1539 SetLastError(ERROR_INVALID_PARAMETER);
1540 return FALSE;
1543 RtlCreateUnicodeStringFromAsciiz(&fn1W, fn1);
1544 if (fn2) RtlCreateUnicodeStringFromAsciiz(&fn2W, fn2);
1545 else fn2W.Buffer = NULL;
1547 ret = MoveFileExW( fn1W.Buffer, fn2W.Buffer, flag );
1549 RtlFreeUnicodeString(&fn1W);
1550 RtlFreeUnicodeString(&fn2W);
1551 return ret;
1555 /**************************************************************************
1556 * MoveFileW (KERNEL32.@)
1558 * Move file or directory
1560 BOOL WINAPI MoveFileW( LPCWSTR fn1, LPCWSTR fn2 )
1562 return MoveFileExW( fn1, fn2, MOVEFILE_COPY_ALLOWED );
1566 /**************************************************************************
1567 * MoveFileA (KERNEL32.@)
1569 BOOL WINAPI MoveFileA( LPCSTR fn1, LPCSTR fn2 )
1571 return MoveFileExA( fn1, fn2, MOVEFILE_COPY_ALLOWED );
1575 /**************************************************************************
1576 * CopyFileW (KERNEL32.@)
1578 BOOL WINAPI CopyFileW( LPCWSTR source, LPCWSTR dest, BOOL fail_if_exists )
1580 HANDLE h1, h2;
1581 BY_HANDLE_FILE_INFORMATION info;
1582 DWORD count;
1583 BOOL ret = FALSE;
1584 char buffer[2048];
1586 if (!source || !dest)
1588 SetLastError(ERROR_INVALID_PARAMETER);
1589 return FALSE;
1592 TRACE("%s -> %s\n", debugstr_w(source), debugstr_w(dest));
1594 if ((h1 = CreateFileW(source, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
1595 NULL, OPEN_EXISTING, 0, 0)) == INVALID_HANDLE_VALUE)
1597 WARN("Unable to open source %s\n", debugstr_w(source));
1598 return FALSE;
1601 if (!GetFileInformationByHandle( h1, &info ))
1603 WARN("GetFileInformationByHandle returned error for %s\n", debugstr_w(source));
1604 CloseHandle( h1 );
1605 return FALSE;
1608 if ((h2 = CreateFileW( dest, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
1609 fail_if_exists ? CREATE_NEW : CREATE_ALWAYS,
1610 info.dwFileAttributes, h1 )) == INVALID_HANDLE_VALUE)
1612 WARN("Unable to open dest %s\n", debugstr_w(dest));
1613 CloseHandle( h1 );
1614 return FALSE;
1617 while (ReadFile( h1, buffer, sizeof(buffer), &count, NULL ) && count)
1619 char *p = buffer;
1620 while (count != 0)
1622 DWORD res;
1623 if (!WriteFile( h2, p, count, &res, NULL ) || !res) goto done;
1624 p += res;
1625 count -= res;
1628 ret = TRUE;
1629 done:
1630 CloseHandle( h1 );
1631 CloseHandle( h2 );
1632 return ret;
1636 /**************************************************************************
1637 * CopyFileA (KERNEL32.@)
1639 BOOL WINAPI CopyFileA( LPCSTR source, LPCSTR dest, BOOL fail_if_exists)
1641 UNICODE_STRING sourceW, destW;
1642 BOOL ret;
1644 if (!source || !dest)
1646 SetLastError(ERROR_INVALID_PARAMETER);
1647 return FALSE;
1650 RtlCreateUnicodeStringFromAsciiz(&sourceW, source);
1651 RtlCreateUnicodeStringFromAsciiz(&destW, dest);
1653 ret = CopyFileW(sourceW.Buffer, destW.Buffer, fail_if_exists);
1655 RtlFreeUnicodeString(&sourceW);
1656 RtlFreeUnicodeString(&destW);
1657 return ret;
1661 /**************************************************************************
1662 * CopyFileExW (KERNEL32.@)
1664 * This implementation ignores most of the extra parameters passed-in into
1665 * the "ex" version of the method and calls the CopyFile method.
1666 * It will have to be fixed eventually.
1668 BOOL WINAPI CopyFileExW(LPCWSTR sourceFilename, LPCWSTR destFilename,
1669 LPPROGRESS_ROUTINE progressRoutine, LPVOID appData,
1670 LPBOOL cancelFlagPointer, DWORD copyFlags)
1673 * Interpret the only flag that CopyFile can interpret.
1675 return CopyFileW(sourceFilename, destFilename, (copyFlags & COPY_FILE_FAIL_IF_EXISTS) != 0);
1679 /**************************************************************************
1680 * CopyFileExA (KERNEL32.@)
1682 BOOL WINAPI CopyFileExA(LPCSTR sourceFilename, LPCSTR destFilename,
1683 LPPROGRESS_ROUTINE progressRoutine, LPVOID appData,
1684 LPBOOL cancelFlagPointer, DWORD copyFlags)
1686 UNICODE_STRING sourceW, destW;
1687 BOOL ret;
1689 if (!sourceFilename || !destFilename)
1691 SetLastError(ERROR_INVALID_PARAMETER);
1692 return FALSE;
1695 RtlCreateUnicodeStringFromAsciiz(&sourceW, sourceFilename);
1696 RtlCreateUnicodeStringFromAsciiz(&destW, destFilename);
1698 ret = CopyFileExW(sourceW.Buffer, destW.Buffer, progressRoutine, appData,
1699 cancelFlagPointer, copyFlags);
1701 RtlFreeUnicodeString(&sourceW);
1702 RtlFreeUnicodeString(&destW);
1703 return ret;
1707 /***********************************************************************
1708 * SetFileTime (KERNEL32.@)
1710 BOOL WINAPI SetFileTime( HANDLE hFile,
1711 const FILETIME *ctime,
1712 const FILETIME *atime,
1713 const FILETIME *mtime )
1715 #ifdef HAVE_FUTIMES
1716 BOOL ret = FALSE;
1717 NTSTATUS status;
1718 int fd;
1719 ULONGLONG sec, nsec;
1721 if (!(status = wine_server_handle_to_fd( hFile, GENERIC_WRITE, &fd, NULL, NULL )))
1723 struct timeval tv[2];
1725 if (!atime || !mtime)
1727 struct stat st;
1729 tv[0].tv_sec = tv[0].tv_usec = 0;
1730 tv[1].tv_sec = tv[1].tv_usec = 0;
1731 if (!fstat( fd, &st ))
1733 tv[0].tv_sec = st.st_atime;
1734 tv[1].tv_sec = st.st_mtime;
1737 if (atime)
1739 sec = ((ULONGLONG)atime->dwHighDateTime << 32) | atime->dwLowDateTime;
1740 sec = RtlLargeIntegerDivide( sec, 10000000, &nsec );
1741 tv[0].tv_sec = sec - SECS_1601_TO_1970;
1742 tv[0].tv_usec = (UINT)nsec / 10;
1744 if (mtime)
1746 sec = ((ULONGLONG)mtime->dwHighDateTime << 32) | mtime->dwLowDateTime;
1747 sec = RtlLargeIntegerDivide( sec, 10000000, &nsec );
1748 tv[1].tv_sec = sec - SECS_1601_TO_1970;
1749 tv[1].tv_usec = (UINT)nsec / 10;
1752 if (!futimes( fd, tv )) ret = TRUE;
1753 else FILE_SetDosError();
1754 wine_server_release_fd( hFile, fd );
1756 else SetLastError( RtlNtStatusToDosError(status) );
1757 return ret;
1758 #else
1759 SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
1760 return FALSE;
1761 #endif /* HAVE_FUTIMES */
1765 /**************************************************************************
1766 * GetFileAttributesExW (KERNEL32.@)
1768 BOOL WINAPI GetFileAttributesExW(
1769 LPCWSTR lpFileName, GET_FILEEX_INFO_LEVELS fInfoLevelId,
1770 LPVOID lpFileInformation)
1772 DOS_FULL_NAME full_name;
1773 BY_HANDLE_FILE_INFORMATION info;
1775 if (!lpFileName || !lpFileInformation)
1777 SetLastError(ERROR_INVALID_PARAMETER);
1778 return FALSE;
1781 if (fInfoLevelId == GetFileExInfoStandard) {
1782 LPWIN32_FILE_ATTRIBUTE_DATA lpFad =
1783 (LPWIN32_FILE_ATTRIBUTE_DATA) lpFileInformation;
1784 if (!DOSFS_GetFullName( lpFileName, TRUE, &full_name )) return FALSE;
1785 if (!FILE_Stat( full_name.long_name, &info, NULL )) return FALSE;
1787 lpFad->dwFileAttributes = info.dwFileAttributes;
1788 lpFad->ftCreationTime = info.ftCreationTime;
1789 lpFad->ftLastAccessTime = info.ftLastAccessTime;
1790 lpFad->ftLastWriteTime = info.ftLastWriteTime;
1791 lpFad->nFileSizeHigh = info.nFileSizeHigh;
1792 lpFad->nFileSizeLow = info.nFileSizeLow;
1794 else {
1795 FIXME("invalid info level %d!\n", fInfoLevelId);
1796 return FALSE;
1799 return TRUE;
1803 /**************************************************************************
1804 * GetFileAttributesExA (KERNEL32.@)
1806 BOOL WINAPI GetFileAttributesExA(
1807 LPCSTR filename, GET_FILEEX_INFO_LEVELS fInfoLevelId,
1808 LPVOID lpFileInformation)
1810 UNICODE_STRING filenameW;
1811 BOOL ret = FALSE;
1813 if (!filename || !lpFileInformation)
1815 SetLastError(ERROR_INVALID_PARAMETER);
1816 return FALSE;
1819 if (RtlCreateUnicodeStringFromAsciiz(&filenameW, filename))
1821 ret = GetFileAttributesExW(filenameW.Buffer, fInfoLevelId, lpFileInformation);
1822 RtlFreeUnicodeString(&filenameW);
1824 else
1825 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1826 return ret;