Rewrote the collapsing of . and .. in RtlGetFullPathName_U for better
[wine.git] / files / file.c
blob8ec467a58a76ca3dfa4091819fc669a15de437d0
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 mode_t FILE_umask;
88 /***********************************************************************
89 * FILE_ConvertOFMode
91 * Convert OF_* mode into flags for CreateFile.
93 void FILE_ConvertOFMode( INT mode, DWORD *access, DWORD *sharing )
95 switch(mode & 0x03)
97 case OF_READ: *access = GENERIC_READ; break;
98 case OF_WRITE: *access = GENERIC_WRITE; break;
99 case OF_READWRITE: *access = GENERIC_READ | GENERIC_WRITE; break;
100 default: *access = 0; break;
102 switch(mode & 0x70)
104 case OF_SHARE_EXCLUSIVE: *sharing = 0; break;
105 case OF_SHARE_DENY_WRITE: *sharing = FILE_SHARE_READ; break;
106 case OF_SHARE_DENY_READ: *sharing = FILE_SHARE_WRITE; break;
107 case OF_SHARE_DENY_NONE:
108 case OF_SHARE_COMPAT:
109 default: *sharing = FILE_SHARE_READ | FILE_SHARE_WRITE; break;
114 /***********************************************************************
115 * FILE_SetDosError
117 * Set the DOS error code from errno.
119 void FILE_SetDosError(void)
121 int save_errno = errno; /* errno gets overwritten by printf */
123 TRACE("errno = %d %s\n", errno, strerror(errno));
124 switch (save_errno)
126 case EAGAIN:
127 SetLastError( ERROR_SHARING_VIOLATION );
128 break;
129 case EBADF:
130 SetLastError( ERROR_INVALID_HANDLE );
131 break;
132 case ENOSPC:
133 SetLastError( ERROR_HANDLE_DISK_FULL );
134 break;
135 case EACCES:
136 case EPERM:
137 case EROFS:
138 SetLastError( ERROR_ACCESS_DENIED );
139 break;
140 case EBUSY:
141 SetLastError( ERROR_LOCK_VIOLATION );
142 break;
143 case ENOENT:
144 SetLastError( ERROR_FILE_NOT_FOUND );
145 break;
146 case EISDIR:
147 SetLastError( ERROR_CANNOT_MAKE );
148 break;
149 case ENFILE:
150 case EMFILE:
151 SetLastError( ERROR_NO_MORE_FILES );
152 break;
153 case EEXIST:
154 SetLastError( ERROR_FILE_EXISTS );
155 break;
156 case EINVAL:
157 case ESPIPE:
158 SetLastError( ERROR_SEEK );
159 break;
160 case ENOTEMPTY:
161 SetLastError( ERROR_DIR_NOT_EMPTY );
162 break;
163 case ENOEXEC:
164 SetLastError( ERROR_BAD_FORMAT );
165 break;
166 default:
167 WARN("unknown file error: %s\n", strerror(save_errno) );
168 SetLastError( ERROR_GEN_FAILURE );
169 break;
171 errno = save_errno;
175 /***********************************************************************
176 * FILE_CreateFile
178 * Implementation of CreateFile. Takes a Unix path name.
179 * Returns 0 on failure.
181 HANDLE FILE_CreateFile( LPCSTR filename, DWORD access, DWORD sharing,
182 LPSECURITY_ATTRIBUTES sa, DWORD creation,
183 DWORD attributes, HANDLE template, BOOL fail_read_only,
184 UINT drive_type )
186 unsigned int err;
187 UINT disp, options;
188 HANDLE ret;
190 switch (creation)
192 case CREATE_ALWAYS: disp = FILE_OVERWRITE_IF; break;
193 case CREATE_NEW: disp = FILE_CREATE; break;
194 case OPEN_ALWAYS: disp = FILE_OPEN_IF; break;
195 case OPEN_EXISTING: disp = FILE_OPEN; break;
196 case TRUNCATE_EXISTING: disp = FILE_OVERWRITE; break;
197 default:
198 SetLastError( ERROR_INVALID_PARAMETER );
199 return 0;
202 options = 0;
203 if (attributes & FILE_FLAG_BACKUP_SEMANTICS)
204 options |= FILE_OPEN_FOR_BACKUP_INTENT;
205 if (attributes & FILE_FLAG_DELETE_ON_CLOSE)
206 options |= FILE_DELETE_ON_CLOSE;
207 if (!(attributes & FILE_FLAG_OVERLAPPED))
208 options |= FILE_SYNCHRONOUS_IO_ALERT;
209 if (attributes & FILE_FLAG_RANDOM_ACCESS)
210 options |= FILE_RANDOM_ACCESS;
211 attributes &= FILE_ATTRIBUTE_VALID_FLAGS;
213 for (;;)
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 write access failed, retry without GENERIC_WRITE */
233 if (!ret && !fail_read_only && (access & GENERIC_WRITE))
235 if ((err == STATUS_MEDIA_WRITE_PROTECTED) || (err == STATUS_ACCESS_DENIED))
237 TRACE("Write access failed for file '%s', trying without "
238 "write access\n", filename);
239 access &= ~GENERIC_WRITE;
240 continue;
244 if (err)
246 /* In the case file creation was rejected due to CREATE_NEW flag
247 * was specified and file with that name already exists, correct
248 * last error is ERROR_FILE_EXISTS and not ERROR_ALREADY_EXISTS.
249 * Note: RtlNtStatusToDosError is not the subject to blame here.
251 if (err == STATUS_OBJECT_NAME_COLLISION)
252 SetLastError( ERROR_FILE_EXISTS );
253 else
254 SetLastError( RtlNtStatusToDosError(err) );
257 if (!ret) WARN("Unable to create file '%s' (GLE %ld)\n", filename, GetLastError());
258 return ret;
263 static HANDLE FILE_OpenPipe(LPCWSTR name, DWORD access, LPSECURITY_ATTRIBUTES sa )
265 HANDLE ret;
266 DWORD len = 0;
268 if (name && (len = strlenW(name)) > MAX_PATH)
270 SetLastError( ERROR_FILENAME_EXCED_RANGE );
271 return 0;
273 SERVER_START_REQ( open_named_pipe )
275 req->access = access;
276 req->inherit = (sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle);
277 SetLastError(0);
278 wine_server_add_data( req, name, len * sizeof(WCHAR) );
279 wine_server_call_err( req );
280 ret = reply->handle;
282 SERVER_END_REQ;
283 TRACE("Returned %p\n",ret);
284 return ret;
287 /*************************************************************************
288 * CreateFileW [KERNEL32.@] Creates or opens a file or other object
290 * Creates or opens an object, and returns a handle that can be used to
291 * access that object.
293 * PARAMS
295 * filename [in] pointer to filename to be accessed
296 * access [in] access mode requested
297 * sharing [in] share mode
298 * sa [in] pointer to security attributes
299 * creation [in] how to create the file
300 * attributes [in] attributes for newly created file
301 * template [in] handle to file with extended attributes to copy
303 * RETURNS
304 * Success: Open handle to specified file
305 * Failure: INVALID_HANDLE_VALUE
307 * NOTES
308 * Should call SetLastError() on failure.
310 * BUGS
312 * Doesn't support character devices, template files, or a
313 * lot of the 'attributes' flags yet.
315 HANDLE WINAPI CreateFileW( LPCWSTR filename, DWORD access, DWORD sharing,
316 LPSECURITY_ATTRIBUTES sa, DWORD creation,
317 DWORD attributes, HANDLE template )
319 DOS_FULL_NAME full_name;
320 HANDLE ret;
321 static const WCHAR bkslashes_with_question_markW[] = {'\\','\\','?','\\',0};
322 static const WCHAR bkslashes_with_dotW[] = {'\\','\\','.','\\',0};
323 static const WCHAR bkslashesW[] = {'\\','\\',0};
324 static const WCHAR coninW[] = {'C','O','N','I','N','$',0};
325 static const WCHAR conoutW[] = {'C','O','N','O','U','T','$',0};
327 if (!filename)
329 SetLastError( ERROR_INVALID_PARAMETER );
330 return INVALID_HANDLE_VALUE;
332 TRACE("%s %s%s%s%s%s%s%s attributes 0x%lx\n", debugstr_w(filename),
333 ((access & GENERIC_READ)==GENERIC_READ)?"GENERIC_READ ":"",
334 ((access & GENERIC_WRITE)==GENERIC_WRITE)?"GENERIC_WRITE ":"",
335 (!access)?"QUERY_ACCESS ":"",
336 ((sharing & FILE_SHARE_READ)==FILE_SHARE_READ)?"FILE_SHARE_READ ":"",
337 ((sharing & FILE_SHARE_WRITE)==FILE_SHARE_WRITE)?"FILE_SHARE_WRITE ":"",
338 ((sharing & FILE_SHARE_DELETE)==FILE_SHARE_DELETE)?"FILE_SHARE_DELETE ":"",
339 (creation ==CREATE_NEW)?"CREATE_NEW":
340 (creation ==CREATE_ALWAYS)?"CREATE_ALWAYS ":
341 (creation ==OPEN_EXISTING)?"OPEN_EXISTING ":
342 (creation ==OPEN_ALWAYS)?"OPEN_ALWAYS ":
343 (creation ==TRUNCATE_EXISTING)?"TRUNCATE_EXISTING ":"", attributes);
345 /* If the name starts with '\\?\', ignore the first 4 chars. */
346 if (!strncmpW(filename, bkslashes_with_question_markW, 4))
348 static const WCHAR uncW[] = {'U','N','C','\\',0};
349 filename += 4;
350 if (!strncmpiW(filename, uncW, 4))
352 FIXME("UNC name (%s) not supported.\n", debugstr_w(filename) );
353 SetLastError( ERROR_PATH_NOT_FOUND );
354 return INVALID_HANDLE_VALUE;
358 if (!strncmpW(filename, bkslashes_with_dotW, 4))
360 static const WCHAR pipeW[] = {'P','I','P','E','\\',0};
361 if(!strncmpiW(filename + 4, pipeW, 5))
363 TRACE("Opening a pipe: %s\n", debugstr_w(filename));
364 ret = FILE_OpenPipe( filename, access, sa );
365 goto done;
367 else if (isalphaW(filename[4]) && filename[5] == ':' && filename[6] == '\0')
369 const char *device = DRIVE_GetDevice( toupperW(filename[4]) - 'A' );
370 if (device)
372 ret = FILE_CreateFile( device, access, sharing, sa, creation,
373 attributes, template, TRUE, DRIVE_FIXED );
375 else
377 SetLastError( ERROR_ACCESS_DENIED );
378 ret = INVALID_HANDLE_VALUE;
380 goto done;
382 else if (!RtlIsDosDeviceName_U( filename + 4 ))
384 ret = VXD_Open( filename+4, access, sa );
385 goto done;
387 else
388 filename+=4; /* fall into DOSFS_Device case below */
391 /* If the name still starts with '\\', it's a UNC name. */
392 if (!strncmpW(filename, bkslashesW, 2))
394 ret = SMB_CreateFileW(filename, access, sharing, sa, creation, attributes, template );
395 goto done;
398 /* If the name contains a DOS wild card (* or ?), do no create a file */
399 if(strchrW(filename, '*') || strchrW(filename, '?'))
401 SetLastError(ERROR_BAD_PATHNAME);
402 return INVALID_HANDLE_VALUE;
405 /* Open a console for CONIN$ or CONOUT$ */
406 if (!strcmpiW(filename, coninW) || !strcmpiW(filename, conoutW))
408 ret = OpenConsoleW(filename, access, (sa && sa->bInheritHandle), creation);
409 goto done;
412 if (RtlIsDosDeviceName_U( filename ))
414 TRACE("opening device %s\n", debugstr_w(filename) );
416 if (!(ret = DOSFS_OpenDevice( filename, access, attributes, sa )))
418 /* Do not silence this please. It is a critical error. -MM */
419 ERR("Couldn't open device %s!\n", debugstr_w(filename));
420 SetLastError( ERROR_FILE_NOT_FOUND );
422 goto done;
425 /* check for filename, don't check for last entry if creating */
426 if (!DOSFS_GetFullName( filename,
427 (creation == OPEN_EXISTING) ||
428 (creation == TRUNCATE_EXISTING),
429 &full_name )) {
430 WARN("Unable to get full filename from %s (GLE %ld)\n",
431 debugstr_w(filename), GetLastError());
432 return INVALID_HANDLE_VALUE;
435 ret = FILE_CreateFile( full_name.long_name, access, sharing,
436 sa, creation, attributes, template,
437 DRIVE_GetFlags(full_name.drive) & DRIVE_FAIL_READ_ONLY,
438 GetDriveTypeW( full_name.short_name ) );
439 done:
440 if (!ret) ret = INVALID_HANDLE_VALUE;
441 TRACE("returning %p\n", ret);
442 return ret;
447 /*************************************************************************
448 * CreateFileA (KERNEL32.@)
450 HANDLE WINAPI CreateFileA( LPCSTR filename, DWORD access, DWORD sharing,
451 LPSECURITY_ATTRIBUTES sa, DWORD creation,
452 DWORD attributes, HANDLE template)
454 UNICODE_STRING filenameW;
455 HANDLE ret = INVALID_HANDLE_VALUE;
457 if (!filename)
459 SetLastError( ERROR_INVALID_PARAMETER );
460 return INVALID_HANDLE_VALUE;
463 if (RtlCreateUnicodeStringFromAsciiz(&filenameW, filename))
465 ret = CreateFileW(filenameW.Buffer, access, sharing, sa, creation,
466 attributes, template);
467 RtlFreeUnicodeString(&filenameW);
469 else
470 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
471 return ret;
475 /***********************************************************************
476 * FILE_FillInfo
478 * Fill a file information from a struct stat.
480 static void FILE_FillInfo( struct stat *st, BY_HANDLE_FILE_INFORMATION *info )
482 if (S_ISDIR(st->st_mode))
483 info->dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
484 else
485 info->dwFileAttributes = FILE_ATTRIBUTE_ARCHIVE;
486 if (!(st->st_mode & S_IWUSR))
487 info->dwFileAttributes |= FILE_ATTRIBUTE_READONLY;
489 RtlSecondsSince1970ToTime( st->st_mtime, (LARGE_INTEGER *)&info->ftCreationTime );
490 RtlSecondsSince1970ToTime( st->st_mtime, (LARGE_INTEGER *)&info->ftLastWriteTime );
491 RtlSecondsSince1970ToTime( st->st_atime, (LARGE_INTEGER *)&info->ftLastAccessTime );
493 info->dwVolumeSerialNumber = 0; /* FIXME */
494 info->nFileSizeHigh = 0;
495 info->nFileSizeLow = 0;
496 if (!S_ISDIR(st->st_mode)) {
497 info->nFileSizeHigh = st->st_size >> 32;
498 info->nFileSizeLow = st->st_size & 0xffffffff;
500 info->nNumberOfLinks = st->st_nlink;
501 info->nFileIndexHigh = 0;
502 info->nFileIndexLow = st->st_ino;
506 /***********************************************************************
507 * get_show_dot_files_option
509 static BOOL get_show_dot_files_option(void)
511 static const WCHAR WineW[] = {'M','a','c','h','i','n','e','\\',
512 'S','o','f','t','w','a','r','e','\\',
513 'W','i','n','e','\\','W','i','n','e','\\',
514 'C','o','n','f','i','g','\\','W','i','n','e',0};
515 static const WCHAR ShowDotFilesW[] = {'S','h','o','w','D','o','t','F','i','l','e','s',0};
517 char tmp[80];
518 HKEY hkey;
519 DWORD dummy;
520 OBJECT_ATTRIBUTES attr;
521 UNICODE_STRING nameW;
522 BOOL ret = FALSE;
524 attr.Length = sizeof(attr);
525 attr.RootDirectory = 0;
526 attr.ObjectName = &nameW;
527 attr.Attributes = 0;
528 attr.SecurityDescriptor = NULL;
529 attr.SecurityQualityOfService = NULL;
530 RtlInitUnicodeString( &nameW, WineW );
532 if (!NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr ))
534 RtlInitUnicodeString( &nameW, ShowDotFilesW );
535 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
537 WCHAR *str = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
538 ret = IS_OPTION_TRUE( str[0] );
540 NtClose( hkey );
542 return ret;
546 /***********************************************************************
547 * FILE_Stat
549 * Stat a Unix path name. Return TRUE if OK.
551 BOOL FILE_Stat( LPCSTR unixName, BY_HANDLE_FILE_INFORMATION *info, BOOL *is_symlink_ptr )
553 struct stat st;
554 int is_symlink;
555 LPCSTR p;
557 if (lstat( unixName, &st ) == -1)
559 FILE_SetDosError();
560 return FALSE;
562 is_symlink = S_ISLNK(st.st_mode);
563 if (is_symlink)
565 /* do a "real" stat to find out
566 about the type of the symlink destination */
567 if (stat( unixName, &st ) == -1)
569 FILE_SetDosError();
570 return FALSE;
574 /* fill in the information we gathered so far */
575 FILE_FillInfo( &st, info );
577 /* and now see if this is a hidden file, based on the name */
578 p = strrchr( unixName, '/');
579 p = p ? p + 1 : unixName;
580 if (*p == '.' && *(p+1) && (*(p+1) != '.' || *(p+2)))
582 static int show_dot_files = -1;
583 if (show_dot_files == -1)
584 show_dot_files = get_show_dot_files_option();
585 if (!show_dot_files)
586 info->dwFileAttributes |= FILE_ATTRIBUTE_HIDDEN;
588 if (is_symlink_ptr) *is_symlink_ptr = is_symlink;
589 return TRUE;
593 /***********************************************************************
594 * GetFileInformationByHandle (KERNEL32.@)
596 DWORD WINAPI GetFileInformationByHandle( HANDLE hFile,
597 BY_HANDLE_FILE_INFORMATION *info )
599 DWORD ret;
600 if (!info) return 0;
602 TRACE("%p\n", hFile);
604 SERVER_START_REQ( get_file_info )
606 req->handle = hFile;
607 if ((ret = !wine_server_call_err( req )))
609 /* FIXME: which file types are supported ?
610 * Serial ports (FILE_TYPE_CHAR) are not,
611 * and MSDN also says that pipes are not supported.
612 * FILE_TYPE_REMOTE seems to be supported according to
613 * MSDN q234741.txt */
614 if ((reply->type == FILE_TYPE_DISK) || (reply->type == FILE_TYPE_REMOTE))
616 RtlSecondsSince1970ToTime( reply->write_time, (LARGE_INTEGER *)&info->ftCreationTime );
617 RtlSecondsSince1970ToTime( reply->write_time, (LARGE_INTEGER *)&info->ftLastWriteTime );
618 RtlSecondsSince1970ToTime( reply->access_time, (LARGE_INTEGER *)&info->ftLastAccessTime );
619 info->dwFileAttributes = reply->attr;
620 info->dwVolumeSerialNumber = reply->serial;
621 info->nFileSizeHigh = reply->size_high;
622 info->nFileSizeLow = reply->size_low;
623 info->nNumberOfLinks = reply->links;
624 info->nFileIndexHigh = reply->index_high;
625 info->nFileIndexLow = reply->index_low;
627 else
629 SetLastError(ERROR_NOT_SUPPORTED);
630 ret = 0;
634 SERVER_END_REQ;
635 return ret;
639 /**************************************************************************
640 * GetFileAttributesW (KERNEL32.@)
642 DWORD WINAPI GetFileAttributesW( LPCWSTR name )
644 DOS_FULL_NAME full_name;
645 BY_HANDLE_FILE_INFORMATION info;
647 if (name == NULL)
649 SetLastError( ERROR_INVALID_PARAMETER );
650 return INVALID_FILE_ATTRIBUTES;
652 if (!DOSFS_GetFullName( name, TRUE, &full_name) )
653 return INVALID_FILE_ATTRIBUTES;
654 if (!FILE_Stat( full_name.long_name, &info, NULL ))
655 return INVALID_FILE_ATTRIBUTES;
656 return info.dwFileAttributes;
660 /**************************************************************************
661 * GetFileAttributesA (KERNEL32.@)
663 DWORD WINAPI GetFileAttributesA( LPCSTR name )
665 UNICODE_STRING nameW;
666 DWORD ret = INVALID_FILE_ATTRIBUTES;
668 if (!name)
670 SetLastError( ERROR_INVALID_PARAMETER );
671 return INVALID_FILE_ATTRIBUTES;
674 if (RtlCreateUnicodeStringFromAsciiz(&nameW, name))
676 ret = GetFileAttributesW(nameW.Buffer);
677 RtlFreeUnicodeString(&nameW);
679 else
680 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
681 return ret;
685 /**************************************************************************
686 * SetFileAttributesW (KERNEL32.@)
688 BOOL WINAPI SetFileAttributesW(LPCWSTR lpFileName, DWORD attributes)
690 struct stat buf;
691 DOS_FULL_NAME full_name;
693 if (!lpFileName)
695 SetLastError( ERROR_INVALID_PARAMETER );
696 return FALSE;
699 TRACE("(%s,%lx)\n", debugstr_w(lpFileName), attributes);
701 if (!DOSFS_GetFullName( lpFileName, TRUE, &full_name ))
702 return FALSE;
704 if(stat(full_name.long_name,&buf)==-1)
706 FILE_SetDosError();
707 return FALSE;
709 if (attributes & FILE_ATTRIBUTE_READONLY)
711 if(S_ISDIR(buf.st_mode))
712 /* FIXME */
713 WARN("FILE_ATTRIBUTE_READONLY ignored for directory.\n");
714 else
715 buf.st_mode &= ~0222; /* octal!, clear write permission bits */
716 attributes &= ~FILE_ATTRIBUTE_READONLY;
718 else
720 /* add write permission */
721 buf.st_mode |= (0600 | ((buf.st_mode & 044) >> 1)) & (~FILE_umask);
723 if (attributes & FILE_ATTRIBUTE_DIRECTORY)
725 if (!S_ISDIR(buf.st_mode))
726 FIXME("SetFileAttributes expected the file %s to be a directory\n",
727 debugstr_w(lpFileName));
728 attributes &= ~FILE_ATTRIBUTE_DIRECTORY;
730 attributes &= ~(FILE_ATTRIBUTE_NORMAL|FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN|
731 FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_TEMPORARY);
732 if (attributes)
733 FIXME("(%s):%lx attribute(s) not implemented.\n", debugstr_w(lpFileName), attributes);
734 if (-1==chmod(full_name.long_name,buf.st_mode))
736 if (GetDriveTypeW(lpFileName) == DRIVE_CDROM)
738 SetLastError( ERROR_ACCESS_DENIED );
739 return FALSE;
743 * FIXME: We don't return FALSE here because of differences between
744 * Linux and Windows privileges. Under Linux only the owner of
745 * the file is allowed to change file attributes. Under Windows,
746 * applications expect that if you can write to a file, you can also
747 * change its attributes (see GENERIC_WRITE). We could try to be
748 * clever here but that would break multi-user installations where
749 * users share read-only DLLs. This is because some installers like
750 * to change attributes of already installed DLLs.
752 FIXME("Couldn't set file attributes for existing file \"%s\".\n"
753 "Check permissions or set VFAT \"quiet\" mount flag\n", full_name.long_name);
755 return TRUE;
759 /**************************************************************************
760 * SetFileAttributesA (KERNEL32.@)
762 BOOL WINAPI SetFileAttributesA(LPCSTR lpFileName, DWORD attributes)
764 UNICODE_STRING filenameW;
765 BOOL ret = FALSE;
767 if (!lpFileName)
769 SetLastError( ERROR_INVALID_PARAMETER );
770 return FALSE;
773 if (RtlCreateUnicodeStringFromAsciiz(&filenameW, lpFileName))
775 ret = SetFileAttributesW(filenameW.Buffer, attributes);
776 RtlFreeUnicodeString(&filenameW);
778 else
779 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
780 return ret;
784 /******************************************************************************
785 * GetCompressedFileSizeA [KERNEL32.@]
787 DWORD WINAPI GetCompressedFileSizeA(
788 LPCSTR lpFileName,
789 LPDWORD lpFileSizeHigh)
791 UNICODE_STRING filenameW;
792 DWORD ret;
794 if (RtlCreateUnicodeStringFromAsciiz(&filenameW, lpFileName))
796 ret = GetCompressedFileSizeW(filenameW.Buffer, lpFileSizeHigh);
797 RtlFreeUnicodeString(&filenameW);
799 else
801 ret = INVALID_FILE_SIZE;
802 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
804 return ret;
808 /******************************************************************************
809 * GetCompressedFileSizeW [KERNEL32.@]
811 * RETURNS
812 * Success: Low-order doubleword of number of bytes
813 * Failure: INVALID_FILE_SIZE
815 DWORD WINAPI GetCompressedFileSizeW(
816 LPCWSTR lpFileName, /* [in] Pointer to name of file */
817 LPDWORD lpFileSizeHigh) /* [out] Receives high-order doubleword of size */
819 DOS_FULL_NAME full_name;
820 struct stat st;
821 DWORD low;
823 TRACE("(%s,%p)\n",debugstr_w(lpFileName),lpFileSizeHigh);
825 if (!DOSFS_GetFullName( lpFileName, TRUE, &full_name )) return INVALID_FILE_SIZE;
826 if (stat(full_name.long_name, &st) != 0)
828 FILE_SetDosError();
829 return INVALID_FILE_SIZE;
831 #if HAVE_STRUCT_STAT_ST_BLOCKS
832 /* blocks are 512 bytes long */
833 if (lpFileSizeHigh) *lpFileSizeHigh = (st.st_blocks >> 23);
834 low = (DWORD)(st.st_blocks << 9);
835 #else
836 if (lpFileSizeHigh) *lpFileSizeHigh = (st.st_size >> 32);
837 low = (DWORD)st.st_size;
838 #endif
839 return low;
843 /***********************************************************************
844 * GetFileTime (KERNEL32.@)
846 BOOL WINAPI GetFileTime( HANDLE hFile, FILETIME *lpCreationTime,
847 FILETIME *lpLastAccessTime,
848 FILETIME *lpLastWriteTime )
850 BY_HANDLE_FILE_INFORMATION info;
851 if (!GetFileInformationByHandle( hFile, &info )) return FALSE;
852 if (lpCreationTime) *lpCreationTime = info.ftCreationTime;
853 if (lpLastAccessTime) *lpLastAccessTime = info.ftLastAccessTime;
854 if (lpLastWriteTime) *lpLastWriteTime = info.ftLastWriteTime;
855 return TRUE;
859 /***********************************************************************
860 * GetTempFileNameA (KERNEL32.@)
862 UINT WINAPI GetTempFileNameA( LPCSTR path, LPCSTR prefix, UINT unique,
863 LPSTR buffer)
865 UNICODE_STRING pathW, prefixW;
866 WCHAR bufferW[MAX_PATH];
867 UINT ret;
869 if ( !path || !prefix || !buffer )
871 SetLastError( ERROR_INVALID_PARAMETER );
872 return 0;
875 RtlCreateUnicodeStringFromAsciiz(&pathW, path);
876 RtlCreateUnicodeStringFromAsciiz(&prefixW, prefix);
878 ret = GetTempFileNameW(pathW.Buffer, prefixW.Buffer, unique, bufferW);
879 if (ret)
880 WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buffer, MAX_PATH, NULL, NULL);
882 RtlFreeUnicodeString(&pathW);
883 RtlFreeUnicodeString(&prefixW);
884 return ret;
887 /***********************************************************************
888 * GetTempFileNameW (KERNEL32.@)
890 UINT WINAPI GetTempFileNameW( LPCWSTR path, LPCWSTR prefix, UINT unique,
891 LPWSTR buffer )
893 static const WCHAR formatW[] = {'%','x','.','t','m','p',0};
895 DOS_FULL_NAME full_name;
896 int i;
897 LPWSTR p;
899 if ( !path || !prefix || !buffer )
901 SetLastError( ERROR_INVALID_PARAMETER );
902 return 0;
905 strcpyW( buffer, path );
906 p = buffer + strlenW(buffer);
908 /* add a \, if there isn't one */
909 if ((p == buffer) || (p[-1] != '\\')) *p++ = '\\';
911 for (i = 3; (i > 0) && (*prefix); i--) *p++ = *prefix++;
913 unique &= 0xffff;
915 if (unique) sprintfW( p, formatW, unique );
916 else
918 /* get a "random" unique number and try to create the file */
919 HANDLE handle;
920 UINT num = GetTickCount() & 0xffff;
922 if (!num) num = 1;
923 unique = num;
926 sprintfW( p, formatW, unique );
927 handle = CreateFileW( buffer, GENERIC_WRITE, 0, NULL,
928 CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0 );
929 if (handle != INVALID_HANDLE_VALUE)
930 { /* We created it */
931 TRACE("created %s\n", debugstr_w(buffer) );
932 CloseHandle( handle );
933 break;
935 if (GetLastError() != ERROR_FILE_EXISTS &&
936 GetLastError() != ERROR_SHARING_VIOLATION)
937 break; /* No need to go on */
938 if (!(++unique & 0xffff)) unique = 1;
939 } while (unique != num);
942 /* Get the full path name */
944 if (DOSFS_GetFullName( buffer, FALSE, &full_name ))
946 char *slash;
947 /* Check if we have write access in the directory */
948 if ((slash = strrchr( full_name.long_name, '/' ))) *slash = '\0';
949 if (access( full_name.long_name, W_OK ) == -1)
950 WARN("returns %s, which doesn't seem to be writeable.\n",
951 debugstr_w(buffer) );
953 TRACE("returning %s\n", debugstr_w(buffer) );
954 return unique;
958 /***********************************************************************
959 * FILE_DoOpenFile
961 * Implementation of OpenFile16() and OpenFile32().
963 static HFILE FILE_DoOpenFile( LPCSTR name, OFSTRUCT *ofs, UINT mode, BOOL win32 )
965 HFILE hFileRet;
966 HANDLE handle;
967 FILETIME filetime;
968 WORD filedatetime[2];
969 DOS_FULL_NAME full_name;
970 DWORD access, sharing;
971 WCHAR *p;
972 WCHAR buffer[MAX_PATH];
973 LPWSTR nameW;
975 if (!ofs) return HFILE_ERROR;
977 TRACE("%s %s %s %s%s%s%s%s%s%s%s%s\n",name,
978 ((mode & 0x3 )==OF_READ)?"OF_READ":
979 ((mode & 0x3 )==OF_WRITE)?"OF_WRITE":
980 ((mode & 0x3 )==OF_READWRITE)?"OF_READWRITE":"unknown",
981 ((mode & 0x70 )==OF_SHARE_COMPAT)?"OF_SHARE_COMPAT":
982 ((mode & 0x70 )==OF_SHARE_DENY_NONE)?"OF_SHARE_DENY_NONE":
983 ((mode & 0x70 )==OF_SHARE_DENY_READ)?"OF_SHARE_DENY_READ":
984 ((mode & 0x70 )==OF_SHARE_DENY_WRITE)?"OF_SHARE_DENY_WRITE":
985 ((mode & 0x70 )==OF_SHARE_EXCLUSIVE)?"OF_SHARE_EXCLUSIVE":"unknown",
986 ((mode & OF_PARSE )==OF_PARSE)?"OF_PARSE ":"",
987 ((mode & OF_DELETE )==OF_DELETE)?"OF_DELETE ":"",
988 ((mode & OF_VERIFY )==OF_VERIFY)?"OF_VERIFY ":"",
989 ((mode & OF_SEARCH )==OF_SEARCH)?"OF_SEARCH ":"",
990 ((mode & OF_CANCEL )==OF_CANCEL)?"OF_CANCEL ":"",
991 ((mode & OF_CREATE )==OF_CREATE)?"OF_CREATE ":"",
992 ((mode & OF_PROMPT )==OF_PROMPT)?"OF_PROMPT ":"",
993 ((mode & OF_EXIST )==OF_EXIST)?"OF_EXIST ":"",
994 ((mode & OF_REOPEN )==OF_REOPEN)?"OF_REOPEN ":""
998 ofs->cBytes = sizeof(OFSTRUCT);
999 ofs->nErrCode = 0;
1000 if (mode & OF_REOPEN) name = ofs->szPathName;
1002 if (!name) {
1003 ERR("called with `name' set to NULL ! Please debug.\n");
1004 return HFILE_ERROR;
1007 TRACE("%s %04x\n", name, mode );
1009 /* the watcom 10.6 IDE relies on a valid path returned in ofs->szPathName
1010 Are there any cases where getting the path here is wrong?
1011 Uwe Bonnes 1997 Apr 2 */
1012 if (!GetFullPathNameA( name, sizeof(ofs->szPathName),
1013 ofs->szPathName, NULL )) goto error;
1014 FILE_ConvertOFMode( mode, &access, &sharing );
1016 /* OF_PARSE simply fills the structure */
1018 if (mode & OF_PARSE)
1020 ofs->fFixedDisk = (GetDriveType16( ofs->szPathName[0]-'A' )
1021 != DRIVE_REMOVABLE);
1022 TRACE("(%s): OF_PARSE, res = '%s'\n",
1023 name, ofs->szPathName );
1024 return 0;
1027 /* OF_CREATE is completely different from all other options, so
1028 handle it first */
1030 if (mode & OF_CREATE)
1032 if ((handle = CreateFileA( name, GENERIC_READ | GENERIC_WRITE,
1033 sharing, NULL, CREATE_ALWAYS,
1034 FILE_ATTRIBUTE_NORMAL, 0 ))== INVALID_HANDLE_VALUE)
1035 goto error;
1036 goto success;
1039 MultiByteToWideChar(CP_ACP, 0, name, -1, buffer, MAX_PATH);
1040 nameW = buffer;
1042 /* If OF_SEARCH is set, ignore the given path */
1044 if ((mode & OF_SEARCH) && !(mode & OF_REOPEN))
1046 /* First try the file name as is */
1047 if (DOSFS_GetFullName( nameW, TRUE, &full_name )) goto found;
1048 /* Now remove the path */
1049 if (nameW[0] && (nameW[1] == ':')) nameW += 2;
1050 if ((p = strrchrW( nameW, '\\' ))) nameW = p + 1;
1051 if ((p = strrchrW( nameW, '/' ))) nameW = p + 1;
1052 if (!nameW[0]) goto not_found;
1055 /* Now look for the file */
1057 if (!DIR_SearchPath( NULL, nameW, NULL, &full_name, win32 )) goto not_found;
1059 found:
1060 TRACE("found %s = %s\n",
1061 full_name.long_name, debugstr_w(full_name.short_name) );
1062 WideCharToMultiByte(CP_ACP, 0, full_name.short_name, -1,
1063 ofs->szPathName, sizeof(ofs->szPathName), NULL, NULL);
1065 if (mode & OF_DELETE)
1067 handle = FILE_CreateFile( full_name.long_name, GENERIC_READ|GENERIC_WRITE, 0,
1068 NULL, OPEN_EXISTING, 0, 0, TRUE,
1069 GetDriveTypeW( full_name.short_name ) );
1070 if (!handle) goto error;
1071 CloseHandle( handle );
1072 if (unlink( full_name.long_name ) == -1) goto not_found;
1073 TRACE("(%s): OF_DELETE return = OK\n", name);
1074 return 1;
1077 handle = FILE_CreateFile( full_name.long_name, access, sharing,
1078 NULL, OPEN_EXISTING, 0, 0,
1079 DRIVE_GetFlags(full_name.drive) & DRIVE_FAIL_READ_ONLY,
1080 GetDriveTypeW( full_name.short_name ) );
1081 if (!handle) goto not_found;
1083 GetFileTime( handle, NULL, NULL, &filetime );
1084 FileTimeToDosDateTime( &filetime, &filedatetime[0], &filedatetime[1] );
1085 if ((mode & OF_VERIFY) && (mode & OF_REOPEN))
1087 if (ofs->Reserved1 != filedatetime[0] || ofs->Reserved2 != filedatetime[1] )
1089 CloseHandle( handle );
1090 WARN("(%s): OF_VERIFY failed\n", name );
1091 /* FIXME: what error here? */
1092 SetLastError( ERROR_FILE_NOT_FOUND );
1093 goto error;
1096 ofs->Reserved1 = filedatetime[0];
1097 ofs->Reserved2 = filedatetime[1];
1099 success: /* We get here if the open was successful */
1100 TRACE("(%s): OK, return = %p\n", name, handle );
1101 if (win32)
1103 hFileRet = (HFILE)handle;
1104 if (mode & OF_EXIST) /* Return the handle, but close it first */
1105 CloseHandle( handle );
1107 else
1109 hFileRet = Win32HandleToDosFileHandle( handle );
1110 if (hFileRet == HFILE_ERROR16) goto error;
1111 if (mode & OF_EXIST) /* Return the handle, but close it first */
1112 _lclose16( hFileRet );
1114 return hFileRet;
1116 not_found: /* We get here if the file does not exist */
1117 WARN("'%s' not found or sharing violation\n", name );
1118 SetLastError( ERROR_FILE_NOT_FOUND );
1119 /* fall through */
1121 error: /* We get here if there was an error opening the file */
1122 ofs->nErrCode = GetLastError();
1123 WARN("(%s): return = HFILE_ERROR error= %d\n",
1124 name,ofs->nErrCode );
1125 return HFILE_ERROR;
1129 /***********************************************************************
1130 * OpenFile (KERNEL.74)
1131 * OpenFileEx (KERNEL.360)
1133 HFILE16 WINAPI OpenFile16( LPCSTR name, OFSTRUCT *ofs, UINT16 mode )
1135 return FILE_DoOpenFile( name, ofs, mode, FALSE );
1139 /***********************************************************************
1140 * OpenFile (KERNEL32.@)
1142 HFILE WINAPI OpenFile( LPCSTR name, OFSTRUCT *ofs, UINT mode )
1144 return FILE_DoOpenFile( name, ofs, mode, TRUE );
1148 /******************************************************************
1149 * FILE_ReadWriteApc (internal)
1153 static void WINAPI FILE_ReadWriteApc(void* apc_user, PIO_STATUS_BLOCK io_status, ULONG len)
1155 LPOVERLAPPED_COMPLETION_ROUTINE cr = (LPOVERLAPPED_COMPLETION_ROUTINE)apc_user;
1157 cr(RtlNtStatusToDosError(io_status->u.Status), len, (LPOVERLAPPED)io_status);
1160 /***********************************************************************
1161 * ReadFileEx (KERNEL32.@)
1163 BOOL WINAPI ReadFileEx(HANDLE hFile, LPVOID buffer, DWORD bytesToRead,
1164 LPOVERLAPPED overlapped,
1165 LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
1167 LARGE_INTEGER offset;
1168 NTSTATUS status;
1169 PIO_STATUS_BLOCK io_status;
1171 if (!overlapped)
1173 SetLastError(ERROR_INVALID_PARAMETER);
1174 return FALSE;
1177 offset.u.LowPart = overlapped->Offset;
1178 offset.u.HighPart = overlapped->OffsetHigh;
1179 io_status = (PIO_STATUS_BLOCK)overlapped;
1180 io_status->u.Status = STATUS_PENDING;
1182 status = NtReadFile(hFile, NULL, FILE_ReadWriteApc, lpCompletionRoutine,
1183 io_status, buffer, bytesToRead, &offset, NULL);
1185 if (status)
1187 SetLastError( RtlNtStatusToDosError(status) );
1188 return FALSE;
1190 return TRUE;
1193 /***********************************************************************
1194 * ReadFile (KERNEL32.@)
1196 BOOL WINAPI ReadFile( HANDLE hFile, LPVOID buffer, DWORD bytesToRead,
1197 LPDWORD bytesRead, LPOVERLAPPED overlapped )
1199 LARGE_INTEGER offset;
1200 PLARGE_INTEGER poffset = NULL;
1201 IO_STATUS_BLOCK iosb;
1202 PIO_STATUS_BLOCK io_status = &iosb;
1203 HANDLE hEvent = 0;
1204 NTSTATUS status;
1206 TRACE("%p %p %ld %p %p\n", hFile, buffer, bytesToRead,
1207 bytesRead, overlapped );
1209 if (bytesRead) *bytesRead = 0; /* Do this before anything else */
1210 if (!bytesToRead) return TRUE;
1212 if (IsBadReadPtr(buffer, bytesToRead))
1214 SetLastError(ERROR_WRITE_FAULT); /* FIXME */
1215 return FALSE;
1217 if (is_console_handle(hFile))
1218 return ReadConsoleA(hFile, buffer, bytesToRead, bytesRead, NULL);
1220 if (overlapped != NULL)
1222 offset.u.LowPart = overlapped->Offset;
1223 offset.u.HighPart = overlapped->OffsetHigh;
1224 poffset = &offset;
1225 hEvent = overlapped->hEvent;
1226 io_status = (PIO_STATUS_BLOCK)overlapped;
1228 io_status->u.Status = STATUS_PENDING;
1229 io_status->Information = 0;
1231 status = NtReadFile(hFile, hEvent, NULL, NULL, io_status, buffer, bytesToRead, poffset, NULL);
1233 if (status != STATUS_PENDING && bytesRead)
1234 *bytesRead = io_status->Information;
1236 if (status && status != STATUS_END_OF_FILE)
1238 SetLastError( RtlNtStatusToDosError(status) );
1239 return FALSE;
1241 return TRUE;
1245 /***********************************************************************
1246 * WriteFileEx (KERNEL32.@)
1248 BOOL WINAPI WriteFileEx(HANDLE hFile, LPCVOID buffer, DWORD bytesToWrite,
1249 LPOVERLAPPED overlapped,
1250 LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
1252 LARGE_INTEGER offset;
1253 NTSTATUS status;
1254 PIO_STATUS_BLOCK io_status;
1256 TRACE("%p %p %ld %p %p\n",
1257 hFile, buffer, bytesToWrite, overlapped, lpCompletionRoutine);
1259 if (overlapped == NULL)
1261 SetLastError(ERROR_INVALID_PARAMETER);
1262 return FALSE;
1264 offset.u.LowPart = overlapped->Offset;
1265 offset.u.HighPart = overlapped->OffsetHigh;
1267 io_status = (PIO_STATUS_BLOCK)overlapped;
1268 io_status->u.Status = STATUS_PENDING;
1270 status = NtWriteFile(hFile, NULL, FILE_ReadWriteApc, lpCompletionRoutine,
1271 io_status, buffer, bytesToWrite, &offset, NULL);
1273 if (status) SetLastError( RtlNtStatusToDosError(status) );
1274 return !status;
1277 /***********************************************************************
1278 * WriteFile (KERNEL32.@)
1280 BOOL WINAPI WriteFile( HANDLE hFile, LPCVOID buffer, DWORD bytesToWrite,
1281 LPDWORD bytesWritten, LPOVERLAPPED overlapped )
1283 HANDLE hEvent = NULL;
1284 LARGE_INTEGER offset;
1285 PLARGE_INTEGER poffset = NULL;
1286 NTSTATUS status;
1287 IO_STATUS_BLOCK iosb;
1288 PIO_STATUS_BLOCK piosb = &iosb;
1290 TRACE("%p %p %ld %p %p\n",
1291 hFile, buffer, bytesToWrite, bytesWritten, overlapped );
1293 if (is_console_handle(hFile))
1294 return WriteConsoleA(hFile, buffer, bytesToWrite, bytesWritten, NULL);
1296 if (IsBadReadPtr(buffer, bytesToWrite))
1298 SetLastError(ERROR_READ_FAULT); /* FIXME */
1299 return FALSE;
1302 if (overlapped)
1304 offset.u.LowPart = overlapped->Offset;
1305 offset.u.HighPart = overlapped->OffsetHigh;
1306 poffset = &offset;
1307 hEvent = overlapped->hEvent;
1308 piosb = (PIO_STATUS_BLOCK)overlapped;
1310 piosb->u.Status = STATUS_PENDING;
1311 piosb->Information = 0;
1313 status = NtWriteFile(hFile, hEvent, NULL, NULL, piosb,
1314 buffer, bytesToWrite, poffset, NULL);
1315 if (status)
1317 SetLastError( RtlNtStatusToDosError(status) );
1318 return FALSE;
1320 if (bytesWritten) *bytesWritten = piosb->Information;
1322 return TRUE;
1326 /***********************************************************************
1327 * SetFilePointer (KERNEL32.@)
1329 DWORD WINAPI SetFilePointer( HANDLE hFile, LONG distance, LONG *highword,
1330 DWORD method )
1332 DWORD ret = INVALID_SET_FILE_POINTER;
1334 TRACE("handle %p offset %ld high %ld origin %ld\n",
1335 hFile, distance, highword?*highword:0, method );
1337 SERVER_START_REQ( set_file_pointer )
1339 req->handle = hFile;
1340 req->low = distance;
1341 req->high = highword ? *highword : (distance >= 0) ? 0 : -1;
1342 /* FIXME: assumes 1:1 mapping between Windows and Unix seek constants */
1343 req->whence = method;
1344 SetLastError( 0 );
1345 if (!wine_server_call_err( req ))
1347 ret = reply->new_low;
1348 if (highword) *highword = reply->new_high;
1351 SERVER_END_REQ;
1352 return ret;
1356 /*************************************************************************
1357 * SetHandleCount (KERNEL32.@)
1359 UINT WINAPI SetHandleCount( UINT count )
1361 return min( 256, count );
1365 /**************************************************************************
1366 * SetEndOfFile (KERNEL32.@)
1368 BOOL WINAPI SetEndOfFile( HANDLE hFile )
1370 BOOL ret;
1371 SERVER_START_REQ( truncate_file )
1373 req->handle = hFile;
1374 ret = !wine_server_call_err( req );
1376 SERVER_END_REQ;
1377 return ret;
1381 /***********************************************************************
1382 * DeleteFileW (KERNEL32.@)
1384 BOOL WINAPI DeleteFileW( LPCWSTR path )
1386 DOS_FULL_NAME full_name;
1387 HANDLE hFile;
1389 TRACE("%s\n", debugstr_w(path) );
1390 if (!path || !*path)
1392 SetLastError(ERROR_PATH_NOT_FOUND);
1393 return FALSE;
1395 if (RtlIsDosDeviceName_U( path ))
1397 WARN("cannot remove DOS device %s!\n", debugstr_w(path));
1398 SetLastError( ERROR_FILE_NOT_FOUND );
1399 return FALSE;
1402 if (!DOSFS_GetFullName( path, TRUE, &full_name )) return FALSE;
1404 /* check if we are allowed to delete the source */
1405 hFile = FILE_CreateFile( full_name.long_name, GENERIC_READ|GENERIC_WRITE, 0,
1406 NULL, OPEN_EXISTING, 0, 0, TRUE,
1407 GetDriveTypeW( full_name.short_name ) );
1408 if (!hFile) return FALSE;
1410 if (unlink( full_name.long_name ) == -1)
1412 FILE_SetDosError();
1413 CloseHandle(hFile);
1414 return FALSE;
1416 CloseHandle(hFile);
1417 return TRUE;
1421 /***********************************************************************
1422 * DeleteFileA (KERNEL32.@)
1424 BOOL WINAPI DeleteFileA( LPCSTR path )
1426 UNICODE_STRING pathW;
1427 BOOL ret = FALSE;
1429 if (!path)
1431 SetLastError(ERROR_INVALID_PARAMETER);
1432 return FALSE;
1435 if (RtlCreateUnicodeStringFromAsciiz(&pathW, path))
1437 ret = DeleteFileW(pathW.Buffer);
1438 RtlFreeUnicodeString(&pathW);
1440 else
1441 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1442 return ret;
1446 /***********************************************************************
1447 * GetFileType (KERNEL32.@)
1449 DWORD WINAPI GetFileType( HANDLE hFile )
1451 DWORD ret = FILE_TYPE_UNKNOWN;
1453 if (is_console_handle( hFile ))
1454 return FILE_TYPE_CHAR;
1456 SERVER_START_REQ( get_file_info )
1458 req->handle = hFile;
1459 if (!wine_server_call_err( req )) ret = reply->type;
1461 SERVER_END_REQ;
1462 return ret;
1466 /* check if a file name is for an executable file (.exe or .com) */
1467 inline static BOOL is_executable( const char *name )
1469 int len = strlen(name);
1471 if (len < 4) return FALSE;
1472 return (!strcasecmp( name + len - 4, ".exe" ) ||
1473 !strcasecmp( name + len - 4, ".com" ));
1477 /***********************************************************************
1478 * FILE_AddBootRenameEntry
1480 * Adds an entry to the registry that is loaded when windows boots and
1481 * checks if there are some files to be removed or renamed/moved.
1482 * <fn1> has to be valid and <fn2> may be NULL. If both pointers are
1483 * non-NULL then the file is moved, otherwise it is deleted. The
1484 * entry of the registrykey is always appended with two zero
1485 * terminated strings. If <fn2> is NULL then the second entry is
1486 * simply a single 0-byte. Otherwise the second filename goes
1487 * there. The entries are prepended with \??\ before the path and the
1488 * second filename gets also a '!' as the first character if
1489 * MOVEFILE_REPLACE_EXISTING is set. After the final string another
1490 * 0-byte follows to indicate the end of the strings.
1491 * i.e.:
1492 * \??\D:\test\file1[0]
1493 * !\??\D:\test\file1_renamed[0]
1494 * \??\D:\Test|delete[0]
1495 * [0] <- file is to be deleted, second string empty
1496 * \??\D:\test\file2[0]
1497 * !\??\D:\test\file2_renamed[0]
1498 * [0] <- indicates end of strings
1500 * or:
1501 * \??\D:\test\file1[0]
1502 * !\??\D:\test\file1_renamed[0]
1503 * \??\D:\Test|delete[0]
1504 * [0] <- file is to be deleted, second string empty
1505 * [0] <- indicates end of strings
1508 static BOOL FILE_AddBootRenameEntry( LPCWSTR fn1, LPCWSTR fn2, DWORD flags )
1510 static const WCHAR PreString[] = {'\\','?','?','\\',0};
1511 static const WCHAR ValueName[] = {'P','e','n','d','i','n','g',
1512 'F','i','l','e','R','e','n','a','m','e',
1513 'O','p','e','r','a','t','i','o','n','s',0};
1514 static const WCHAR SessionW[] = {'M','a','c','h','i','n','e','\\',
1515 'S','y','s','t','e','m','\\',
1516 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
1517 'C','o','n','t','r','o','l','\\',
1518 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r',0};
1519 static const int info_size = FIELD_OFFSET( KEY_VALUE_PARTIAL_INFORMATION, Data );
1521 OBJECT_ATTRIBUTES attr;
1522 UNICODE_STRING nameW;
1523 KEY_VALUE_PARTIAL_INFORMATION *info;
1524 BOOL rc = FALSE;
1525 HKEY Reboot = 0;
1526 DWORD len0, len1, len2;
1527 DWORD DataSize = 0;
1528 BYTE *Buffer = NULL;
1529 WCHAR *p;
1531 attr.Length = sizeof(attr);
1532 attr.RootDirectory = 0;
1533 attr.ObjectName = &nameW;
1534 attr.Attributes = 0;
1535 attr.SecurityDescriptor = NULL;
1536 attr.SecurityQualityOfService = NULL;
1537 RtlInitUnicodeString( &nameW, SessionW );
1539 if (NtCreateKey( &Reboot, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ) != STATUS_SUCCESS)
1541 WARN("Error creating key for reboot managment [%s]\n",
1542 "SYSTEM\\CurrentControlSet\\Control\\Session Manager");
1543 return FALSE;
1546 len0 = strlenW(PreString);
1547 len1 = strlenW(fn1) + len0 + 1;
1548 if (fn2)
1550 len2 = strlenW(fn2) + len0 + 1;
1551 if (flags & MOVEFILE_REPLACE_EXISTING) len2++; /* Plus 1 because of the leading '!' */
1553 else len2 = 1; /* minimum is the 0 characters for the empty second string */
1555 /* convert characters to bytes */
1556 len0 *= sizeof(WCHAR);
1557 len1 *= sizeof(WCHAR);
1558 len2 *= sizeof(WCHAR);
1560 RtlInitUnicodeString( &nameW, ValueName );
1562 /* First we check if the key exists and if so how many bytes it already contains. */
1563 if (NtQueryValueKey( Reboot, &nameW, KeyValuePartialInformation,
1564 NULL, 0, &DataSize ) == STATUS_BUFFER_OVERFLOW)
1566 if (!(Buffer = HeapAlloc( GetProcessHeap(), 0, DataSize + len1 + len2 + sizeof(WCHAR) )))
1567 goto Quit;
1568 if (NtQueryValueKey( Reboot, &nameW, KeyValuePartialInformation,
1569 Buffer, DataSize, &DataSize )) goto Quit;
1570 info = (KEY_VALUE_PARTIAL_INFORMATION *)Buffer;
1571 if (info->Type != REG_MULTI_SZ) goto Quit;
1572 if (DataSize > sizeof(info)) DataSize -= sizeof(WCHAR); /* remove terminating null (will be added back later) */
1574 else
1576 DataSize = info_size;
1577 if (!(Buffer = HeapAlloc( GetProcessHeap(), 0, DataSize + len1 + len2 + sizeof(WCHAR) )))
1578 goto Quit;
1581 p = (WCHAR *)(Buffer + DataSize);
1582 strcpyW( p, PreString );
1583 strcatW( p, fn1 );
1584 DataSize += len1;
1585 if (fn2)
1587 p = (WCHAR *)(Buffer + DataSize);
1588 if (flags & MOVEFILE_REPLACE_EXISTING)
1589 *p++ = '!';
1590 strcpyW( p, PreString );
1591 strcatW( p, fn2 );
1592 DataSize += len2;
1594 else
1596 p = (WCHAR *)(Buffer + DataSize);
1597 *p = 0;
1598 DataSize += sizeof(WCHAR);
1601 /* add final null */
1602 p = (WCHAR *)(Buffer + DataSize);
1603 *p = 0;
1604 DataSize += sizeof(WCHAR);
1606 rc = !NtSetValueKey(Reboot, &nameW, 0, REG_MULTI_SZ, Buffer + info_size, DataSize - info_size);
1608 Quit:
1609 if (Reboot) NtClose(Reboot);
1610 if (Buffer) HeapFree( GetProcessHeap(), 0, Buffer );
1611 return(rc);
1615 /**************************************************************************
1616 * MoveFileExW (KERNEL32.@)
1618 BOOL WINAPI MoveFileExW( LPCWSTR fn1, LPCWSTR fn2, DWORD flag )
1620 DOS_FULL_NAME full_name1, full_name2;
1621 HANDLE hFile;
1622 DWORD attr = INVALID_FILE_ATTRIBUTES;
1624 TRACE("(%s,%s,%04lx)\n", debugstr_w(fn1), debugstr_w(fn2), flag);
1626 /* FIXME: <Gerhard W. Gruber>sparhawk@gmx.at
1627 In case of W9x and lesser this function should return 120 (ERROR_CALL_NOT_IMPLEMENTED)
1628 to be really compatible. Most programs won't have any problems though. In case
1629 you encounter one, this is what you should return here. I don't know what's up
1630 with NT 3.5. Is this function available there or not?
1631 Does anybody really care about 3.5? :)
1634 /* Filename1 has to be always set to a valid path. Filename2 may be NULL
1635 if the source file has to be deleted.
1637 if (!fn1) {
1638 SetLastError(ERROR_INVALID_PARAMETER);
1639 return FALSE;
1642 /* This function has to be run through in order to process the name properly.
1643 If the BOOTDELAY flag is set, the file doesn't need to exist though. At least
1644 that is the behaviour on NT 4.0. The operation accepts the filenames as
1645 they are given but it can't reply with a reasonable returncode. Success
1646 means in that case success for entering the values into the registry.
1648 if(!DOSFS_GetFullName( fn1, TRUE, &full_name1 ))
1650 if(!(flag & MOVEFILE_DELAY_UNTIL_REBOOT))
1651 return FALSE;
1654 if (fn2) /* !fn2 means delete fn1 */
1656 if (DOSFS_GetFullName( fn2, TRUE, &full_name2 ))
1658 if(!(flag & MOVEFILE_DELAY_UNTIL_REBOOT))
1660 /* target exists, check if we may overwrite */
1661 if (!(flag & MOVEFILE_REPLACE_EXISTING))
1663 SetLastError( ERROR_ALREADY_EXISTS );
1664 return FALSE;
1668 else
1670 if (!DOSFS_GetFullName( fn2, FALSE, &full_name2 ))
1672 if(!(flag & MOVEFILE_DELAY_UNTIL_REBOOT))
1673 return FALSE;
1677 /* Source name and target path are valid */
1679 if (flag & MOVEFILE_DELAY_UNTIL_REBOOT)
1681 return FILE_AddBootRenameEntry( fn1, fn2, flag );
1684 attr = GetFileAttributesW( fn1 );
1685 if ( attr == INVALID_FILE_ATTRIBUTES ) return FALSE;
1687 /* check if we are allowed to rename the source */
1688 hFile = FILE_CreateFile( full_name1.long_name, 0, 0,
1689 NULL, OPEN_EXISTING, 0, 0, TRUE,
1690 GetDriveTypeW( full_name1.short_name ) );
1691 if (!hFile)
1693 if (GetLastError() != ERROR_ACCESS_DENIED) return FALSE;
1694 if ( !(attr & FILE_ATTRIBUTE_DIRECTORY) ) return FALSE;
1695 /* if it's a directory we can continue */
1697 else CloseHandle(hFile);
1699 /* check, if we are allowed to delete the destination,
1700 ** (but the file not being there is fine) */
1701 hFile = FILE_CreateFile( full_name2.long_name, GENERIC_READ|GENERIC_WRITE, 0,
1702 NULL, OPEN_EXISTING, 0, 0, TRUE,
1703 GetDriveTypeW( full_name2.short_name ) );
1704 if(!hFile && GetLastError() != ERROR_FILE_NOT_FOUND) return FALSE;
1705 CloseHandle(hFile);
1707 if (full_name1.drive != full_name2.drive)
1709 if (!(flag & MOVEFILE_COPY_ALLOWED))
1711 SetLastError( ERROR_NOT_SAME_DEVICE );
1712 return FALSE;
1714 else if ( attr & FILE_ATTRIBUTE_DIRECTORY )
1716 /* Strange, but that's what Windows returns */
1717 SetLastError ( ERROR_ACCESS_DENIED );
1718 return FALSE;
1721 if (rename( full_name1.long_name, full_name2.long_name ) == -1)
1722 /* Try copy/delete unless it's a directory. */
1723 /* FIXME: This does not handle the (unlikely) case that the two locations
1724 are on the same Wine drive, but on different Unix file systems. */
1726 if ( attr & FILE_ATTRIBUTE_DIRECTORY )
1728 FILE_SetDosError();
1729 return FALSE;
1731 else
1733 if ( ! CopyFileW( fn1, fn2, !(flag & MOVEFILE_REPLACE_EXISTING) ))
1734 return FALSE;
1735 if ( ! DeleteFileW ( fn1 ) )
1736 return FALSE;
1739 if (is_executable( full_name1.long_name ) != is_executable( full_name2.long_name ))
1741 struct stat fstat;
1742 if (stat( full_name2.long_name, &fstat ) != -1)
1744 if (is_executable( full_name2.long_name ))
1745 /* set executable bit where read bit is set */
1746 fstat.st_mode |= (fstat.st_mode & 0444) >> 2;
1747 else
1748 fstat.st_mode &= ~0111;
1749 chmod( full_name2.long_name, fstat.st_mode );
1752 return TRUE;
1754 else /* fn2 == NULL means delete source */
1756 if (flag & MOVEFILE_DELAY_UNTIL_REBOOT)
1758 if (flag & MOVEFILE_COPY_ALLOWED) {
1759 WARN("Illegal flag\n");
1760 SetLastError( ERROR_GEN_FAILURE );
1761 return FALSE;
1764 return FILE_AddBootRenameEntry( fn1, NULL, flag );
1767 if (unlink( full_name1.long_name ) == -1)
1769 FILE_SetDosError();
1770 return FALSE;
1772 return TRUE; /* successfully deleted */
1776 /**************************************************************************
1777 * MoveFileExA (KERNEL32.@)
1779 BOOL WINAPI MoveFileExA( LPCSTR fn1, LPCSTR fn2, DWORD flag )
1781 UNICODE_STRING fn1W, fn2W;
1782 BOOL ret;
1784 if (!fn1)
1786 SetLastError(ERROR_INVALID_PARAMETER);
1787 return FALSE;
1790 RtlCreateUnicodeStringFromAsciiz(&fn1W, fn1);
1791 if (fn2) RtlCreateUnicodeStringFromAsciiz(&fn2W, fn2);
1792 else fn2W.Buffer = NULL;
1794 ret = MoveFileExW( fn1W.Buffer, fn2W.Buffer, flag );
1796 RtlFreeUnicodeString(&fn1W);
1797 RtlFreeUnicodeString(&fn2W);
1798 return ret;
1802 /**************************************************************************
1803 * MoveFileW (KERNEL32.@)
1805 * Move file or directory
1807 BOOL WINAPI MoveFileW( LPCWSTR fn1, LPCWSTR fn2 )
1809 return MoveFileExW( fn1, fn2, MOVEFILE_COPY_ALLOWED );
1813 /**************************************************************************
1814 * MoveFileA (KERNEL32.@)
1816 BOOL WINAPI MoveFileA( LPCSTR fn1, LPCSTR fn2 )
1818 return MoveFileExA( fn1, fn2, MOVEFILE_COPY_ALLOWED );
1822 /**************************************************************************
1823 * CopyFileW (KERNEL32.@)
1825 BOOL WINAPI CopyFileW( LPCWSTR source, LPCWSTR dest, BOOL fail_if_exists )
1827 HANDLE h1, h2;
1828 BY_HANDLE_FILE_INFORMATION info;
1829 DWORD count;
1830 BOOL ret = FALSE;
1831 char buffer[2048];
1833 if (!source || !dest)
1835 SetLastError(ERROR_INVALID_PARAMETER);
1836 return FALSE;
1839 TRACE("%s -> %s\n", debugstr_w(source), debugstr_w(dest));
1841 if ((h1 = CreateFileW(source, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
1842 NULL, OPEN_EXISTING, 0, 0)) == INVALID_HANDLE_VALUE)
1844 WARN("Unable to open source %s\n", debugstr_w(source));
1845 return FALSE;
1848 if (!GetFileInformationByHandle( h1, &info ))
1850 WARN("GetFileInformationByHandle returned error for %s\n", debugstr_w(source));
1851 CloseHandle( h1 );
1852 return FALSE;
1855 if ((h2 = CreateFileW( dest, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
1856 fail_if_exists ? CREATE_NEW : CREATE_ALWAYS,
1857 info.dwFileAttributes, h1 )) == INVALID_HANDLE_VALUE)
1859 WARN("Unable to open dest %s\n", debugstr_w(dest));
1860 CloseHandle( h1 );
1861 return FALSE;
1864 while (ReadFile( h1, buffer, sizeof(buffer), &count, NULL ) && count)
1866 char *p = buffer;
1867 while (count != 0)
1869 DWORD res;
1870 if (!WriteFile( h2, p, count, &res, NULL ) || !res) goto done;
1871 p += res;
1872 count -= res;
1875 ret = TRUE;
1876 done:
1877 CloseHandle( h1 );
1878 CloseHandle( h2 );
1879 return ret;
1883 /**************************************************************************
1884 * CopyFileA (KERNEL32.@)
1886 BOOL WINAPI CopyFileA( LPCSTR source, LPCSTR dest, BOOL fail_if_exists)
1888 UNICODE_STRING sourceW, destW;
1889 BOOL ret;
1891 if (!source || !dest)
1893 SetLastError(ERROR_INVALID_PARAMETER);
1894 return FALSE;
1897 RtlCreateUnicodeStringFromAsciiz(&sourceW, source);
1898 RtlCreateUnicodeStringFromAsciiz(&destW, dest);
1900 ret = CopyFileW(sourceW.Buffer, destW.Buffer, fail_if_exists);
1902 RtlFreeUnicodeString(&sourceW);
1903 RtlFreeUnicodeString(&destW);
1904 return ret;
1908 /**************************************************************************
1909 * CopyFileExW (KERNEL32.@)
1911 * This implementation ignores most of the extra parameters passed-in into
1912 * the "ex" version of the method and calls the CopyFile method.
1913 * It will have to be fixed eventually.
1915 BOOL WINAPI CopyFileExW(LPCWSTR sourceFilename, LPCWSTR destFilename,
1916 LPPROGRESS_ROUTINE progressRoutine, LPVOID appData,
1917 LPBOOL cancelFlagPointer, DWORD copyFlags)
1920 * Interpret the only flag that CopyFile can interpret.
1922 return CopyFileW(sourceFilename, destFilename, (copyFlags & COPY_FILE_FAIL_IF_EXISTS) != 0);
1926 /**************************************************************************
1927 * CopyFileExA (KERNEL32.@)
1929 BOOL WINAPI CopyFileExA(LPCSTR sourceFilename, LPCSTR destFilename,
1930 LPPROGRESS_ROUTINE progressRoutine, LPVOID appData,
1931 LPBOOL cancelFlagPointer, DWORD copyFlags)
1933 UNICODE_STRING sourceW, destW;
1934 BOOL ret;
1936 if (!sourceFilename || !destFilename)
1938 SetLastError(ERROR_INVALID_PARAMETER);
1939 return FALSE;
1942 RtlCreateUnicodeStringFromAsciiz(&sourceW, sourceFilename);
1943 RtlCreateUnicodeStringFromAsciiz(&destW, destFilename);
1945 ret = CopyFileExW(sourceW.Buffer, destW.Buffer, progressRoutine, appData,
1946 cancelFlagPointer, copyFlags);
1948 RtlFreeUnicodeString(&sourceW);
1949 RtlFreeUnicodeString(&destW);
1950 return ret;
1954 /***********************************************************************
1955 * SetFileTime (KERNEL32.@)
1957 BOOL WINAPI SetFileTime( HANDLE hFile,
1958 const FILETIME *lpCreationTime,
1959 const FILETIME *lpLastAccessTime,
1960 const FILETIME *lpLastWriteTime )
1962 BOOL ret;
1963 SERVER_START_REQ( set_file_time )
1965 req->handle = hFile;
1966 if (lpLastAccessTime)
1967 RtlTimeToSecondsSince1970( (PLARGE_INTEGER) lpLastAccessTime, (DWORD *)&req->access_time );
1968 else
1969 req->access_time = 0; /* FIXME */
1970 if (lpLastWriteTime)
1971 RtlTimeToSecondsSince1970( (PLARGE_INTEGER) lpLastWriteTime, (DWORD *)&req->write_time );
1972 else
1973 req->write_time = 0; /* FIXME */
1974 ret = !wine_server_call_err( req );
1976 SERVER_END_REQ;
1977 return ret;
1981 /**************************************************************************
1982 * GetFileAttributesExW (KERNEL32.@)
1984 BOOL WINAPI GetFileAttributesExW(
1985 LPCWSTR lpFileName, GET_FILEEX_INFO_LEVELS fInfoLevelId,
1986 LPVOID lpFileInformation)
1988 DOS_FULL_NAME full_name;
1989 BY_HANDLE_FILE_INFORMATION info;
1991 if (!lpFileName || !lpFileInformation)
1993 SetLastError(ERROR_INVALID_PARAMETER);
1994 return FALSE;
1997 if (fInfoLevelId == GetFileExInfoStandard) {
1998 LPWIN32_FILE_ATTRIBUTE_DATA lpFad =
1999 (LPWIN32_FILE_ATTRIBUTE_DATA) lpFileInformation;
2000 if (!DOSFS_GetFullName( lpFileName, TRUE, &full_name )) return FALSE;
2001 if (!FILE_Stat( full_name.long_name, &info, NULL )) return FALSE;
2003 lpFad->dwFileAttributes = info.dwFileAttributes;
2004 lpFad->ftCreationTime = info.ftCreationTime;
2005 lpFad->ftLastAccessTime = info.ftLastAccessTime;
2006 lpFad->ftLastWriteTime = info.ftLastWriteTime;
2007 lpFad->nFileSizeHigh = info.nFileSizeHigh;
2008 lpFad->nFileSizeLow = info.nFileSizeLow;
2010 else {
2011 FIXME("invalid info level %d!\n", fInfoLevelId);
2012 return FALSE;
2015 return TRUE;
2019 /**************************************************************************
2020 * GetFileAttributesExA (KERNEL32.@)
2022 BOOL WINAPI GetFileAttributesExA(
2023 LPCSTR filename, GET_FILEEX_INFO_LEVELS fInfoLevelId,
2024 LPVOID lpFileInformation)
2026 UNICODE_STRING filenameW;
2027 BOOL ret = FALSE;
2029 if (!filename || !lpFileInformation)
2031 SetLastError(ERROR_INVALID_PARAMETER);
2032 return FALSE;
2035 if (RtlCreateUnicodeStringFromAsciiz(&filenameW, filename))
2037 ret = GetFileAttributesExW(filenameW.Buffer, fInfoLevelId, lpFileInformation);
2038 RtlFreeUnicodeString(&filenameW);
2040 else
2041 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2042 return ret;