Use statvfs instead of statfs, and provide a default implementation in
[wine/multimedia.git] / files / file.c
blob4bb6e633e6cd6481629fc87bdcce82ce312aac84
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 "drive.h"
71 #include "file.h"
72 #include "wincon.h"
73 #include "kernel_private.h"
75 #include "smb.h"
76 #include "wine/unicode.h"
77 #include "wine/debug.h"
79 WINE_DEFAULT_DEBUG_CHANNEL(file);
81 #if defined(MAP_ANONYMOUS) && !defined(MAP_ANON)
82 #define MAP_ANON MAP_ANONYMOUS
83 #endif
85 #define IS_OPTION_TRUE(ch) ((ch) == 'y' || (ch) == 'Y' || (ch) == 't' || (ch) == 'T' || (ch) == '1')
87 mode_t FILE_umask;
89 /***********************************************************************
90 * FILE_ConvertOFMode
92 * Convert OF_* mode into flags for CreateFile.
94 void FILE_ConvertOFMode( INT mode, DWORD *access, DWORD *sharing )
96 switch(mode & 0x03)
98 case OF_READ: *access = GENERIC_READ; break;
99 case OF_WRITE: *access = GENERIC_WRITE; break;
100 case OF_READWRITE: *access = GENERIC_READ | GENERIC_WRITE; break;
101 default: *access = 0; break;
103 switch(mode & 0x70)
105 case OF_SHARE_EXCLUSIVE: *sharing = 0; break;
106 case OF_SHARE_DENY_WRITE: *sharing = FILE_SHARE_READ; break;
107 case OF_SHARE_DENY_READ: *sharing = FILE_SHARE_WRITE; break;
108 case OF_SHARE_DENY_NONE:
109 case OF_SHARE_COMPAT:
110 default: *sharing = FILE_SHARE_READ | FILE_SHARE_WRITE; break;
115 /***********************************************************************
116 * FILE_strcasecmp
118 * locale-independent case conversion for file I/O
120 int FILE_strcasecmp( const char *str1, const char *str2 )
122 int ret = 0;
123 for ( ; ; str1++, str2++)
124 if ((ret = FILE_toupper(*str1) - FILE_toupper(*str2)) || !*str1) break;
125 return ret;
129 /***********************************************************************
130 * FILE_strncasecmp
132 * locale-independent case conversion for file I/O
134 int FILE_strncasecmp( const char *str1, const char *str2, int len )
136 int ret = 0;
137 for ( ; len > 0; len--, str1++, str2++)
138 if ((ret = FILE_toupper(*str1) - FILE_toupper(*str2)) || !*str1) break;
139 return ret;
143 /***********************************************************************
144 * FILE_SetDosError
146 * Set the DOS error code from errno.
148 void FILE_SetDosError(void)
150 int save_errno = errno; /* errno gets overwritten by printf */
152 TRACE("errno = %d %s\n", errno, strerror(errno));
153 switch (save_errno)
155 case EAGAIN:
156 SetLastError( ERROR_SHARING_VIOLATION );
157 break;
158 case EBADF:
159 SetLastError( ERROR_INVALID_HANDLE );
160 break;
161 case ENOSPC:
162 SetLastError( ERROR_HANDLE_DISK_FULL );
163 break;
164 case EACCES:
165 case EPERM:
166 case EROFS:
167 SetLastError( ERROR_ACCESS_DENIED );
168 break;
169 case EBUSY:
170 SetLastError( ERROR_LOCK_VIOLATION );
171 break;
172 case ENOENT:
173 SetLastError( ERROR_FILE_NOT_FOUND );
174 break;
175 case EISDIR:
176 SetLastError( ERROR_CANNOT_MAKE );
177 break;
178 case ENFILE:
179 case EMFILE:
180 SetLastError( ERROR_NO_MORE_FILES );
181 break;
182 case EEXIST:
183 SetLastError( ERROR_FILE_EXISTS );
184 break;
185 case EINVAL:
186 case ESPIPE:
187 SetLastError( ERROR_SEEK );
188 break;
189 case ENOTEMPTY:
190 SetLastError( ERROR_DIR_NOT_EMPTY );
191 break;
192 case ENOEXEC:
193 SetLastError( ERROR_BAD_FORMAT );
194 break;
195 default:
196 WARN("unknown file error: %s\n", strerror(save_errno) );
197 SetLastError( ERROR_GEN_FAILURE );
198 break;
200 errno = save_errno;
204 /***********************************************************************
205 * FILE_CreateFile
207 * Implementation of CreateFile. Takes a Unix path name.
208 * Returns 0 on failure.
210 HANDLE FILE_CreateFile( LPCSTR filename, DWORD access, DWORD sharing,
211 LPSECURITY_ATTRIBUTES sa, DWORD creation,
212 DWORD attributes, HANDLE template, BOOL fail_read_only,
213 UINT drive_type )
215 unsigned int err;
216 HANDLE ret;
218 for (;;)
220 SERVER_START_REQ( create_file )
222 req->access = access;
223 req->inherit = (sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle);
224 req->sharing = sharing;
225 req->create = creation;
226 req->attrs = attributes;
227 req->removable = (drive_type == DRIVE_REMOVABLE || drive_type == DRIVE_CDROM);
228 wine_server_add_data( req, filename, strlen(filename) );
229 SetLastError(0);
230 err = wine_server_call( req );
231 ret = reply->handle;
233 SERVER_END_REQ;
235 /* If write access failed, retry without GENERIC_WRITE */
237 if (!ret && !fail_read_only && (access & GENERIC_WRITE))
239 if ((err == STATUS_MEDIA_WRITE_PROTECTED) || (err == STATUS_ACCESS_DENIED))
241 TRACE("Write access failed for file '%s', trying without "
242 "write access\n", filename);
243 access &= ~GENERIC_WRITE;
244 continue;
248 if (err)
250 /* In the case file creation was rejected due to CREATE_NEW flag
251 * was specified and file with that name already exists, correct
252 * last error is ERROR_FILE_EXISTS and not ERROR_ALREADY_EXISTS.
253 * Note: RtlNtStatusToDosError is not the subject to blame here.
255 if (err == STATUS_OBJECT_NAME_COLLISION)
256 SetLastError( ERROR_FILE_EXISTS );
257 else
258 SetLastError( RtlNtStatusToDosError(err) );
261 if (!ret) WARN("Unable to create file '%s' (GLE %ld)\n", filename, GetLastError());
262 return ret;
267 static HANDLE FILE_OpenPipe(LPCWSTR name, DWORD access, LPSECURITY_ATTRIBUTES sa )
269 HANDLE ret;
270 DWORD len = 0;
272 if (name && (len = strlenW(name)) > MAX_PATH)
274 SetLastError( ERROR_FILENAME_EXCED_RANGE );
275 return 0;
277 SERVER_START_REQ( open_named_pipe )
279 req->access = access;
280 req->inherit = (sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle);
281 SetLastError(0);
282 wine_server_add_data( req, name, len * sizeof(WCHAR) );
283 wine_server_call_err( req );
284 ret = reply->handle;
286 SERVER_END_REQ;
287 TRACE("Returned %p\n",ret);
288 return ret;
291 /*************************************************************************
292 * CreateFileW [KERNEL32.@] Creates or opens a file or other object
294 * Creates or opens an object, and returns a handle that can be used to
295 * access that object.
297 * PARAMS
299 * filename [in] pointer to filename to be accessed
300 * access [in] access mode requested
301 * sharing [in] share mode
302 * sa [in] pointer to security attributes
303 * creation [in] how to create the file
304 * attributes [in] attributes for newly created file
305 * template [in] handle to file with extended attributes to copy
307 * RETURNS
308 * Success: Open handle to specified file
309 * Failure: INVALID_HANDLE_VALUE
311 * NOTES
312 * Should call SetLastError() on failure.
314 * BUGS
316 * Doesn't support character devices, template files, or a
317 * lot of the 'attributes' flags yet.
319 HANDLE WINAPI CreateFileW( LPCWSTR filename, DWORD access, DWORD sharing,
320 LPSECURITY_ATTRIBUTES sa, DWORD creation,
321 DWORD attributes, HANDLE template )
323 DOS_FULL_NAME full_name;
324 HANDLE ret;
325 static const WCHAR bkslashes_with_question_markW[] = {'\\','\\','?','\\',0};
326 static const WCHAR bkslashes_with_dotW[] = {'\\','\\','.','\\',0};
327 static const WCHAR bkslashesW[] = {'\\','\\',0};
328 static const WCHAR coninW[] = {'C','O','N','I','N','$',0};
329 static const WCHAR conoutW[] = {'C','O','N','O','U','T','$',0};
331 if (!filename)
333 SetLastError( ERROR_INVALID_PARAMETER );
334 return INVALID_HANDLE_VALUE;
336 TRACE("%s %s%s%s%s%s%s%s attributes 0x%lx\n", debugstr_w(filename),
337 ((access & GENERIC_READ)==GENERIC_READ)?"GENERIC_READ ":"",
338 ((access & GENERIC_WRITE)==GENERIC_WRITE)?"GENERIC_WRITE ":"",
339 (!access)?"QUERY_ACCESS ":"",
340 ((sharing & FILE_SHARE_READ)==FILE_SHARE_READ)?"FILE_SHARE_READ ":"",
341 ((sharing & FILE_SHARE_WRITE)==FILE_SHARE_WRITE)?"FILE_SHARE_WRITE ":"",
342 ((sharing & FILE_SHARE_DELETE)==FILE_SHARE_DELETE)?"FILE_SHARE_DELETE ":"",
343 (creation ==CREATE_NEW)?"CREATE_NEW":
344 (creation ==CREATE_ALWAYS)?"CREATE_ALWAYS ":
345 (creation ==OPEN_EXISTING)?"OPEN_EXISTING ":
346 (creation ==OPEN_ALWAYS)?"OPEN_ALWAYS ":
347 (creation ==TRUNCATE_EXISTING)?"TRUNCATE_EXISTING ":"", attributes);
349 /* If the name starts with '\\?\', ignore the first 4 chars. */
350 if (!strncmpW(filename, bkslashes_with_question_markW, 4))
352 static const WCHAR uncW[] = {'U','N','C','\\',0};
353 filename += 4;
354 if (!strncmpiW(filename, uncW, 4))
356 FIXME("UNC name (%s) not supported.\n", debugstr_w(filename) );
357 SetLastError( ERROR_PATH_NOT_FOUND );
358 return INVALID_HANDLE_VALUE;
362 if (!strncmpW(filename, bkslashes_with_dotW, 4))
364 static const WCHAR pipeW[] = {'P','I','P','E','\\',0};
365 if(!strncmpiW(filename + 4, pipeW, 5))
367 TRACE("Opening a pipe: %s\n", debugstr_w(filename));
368 ret = FILE_OpenPipe( filename, access, sa );
369 goto done;
371 else if (isalphaW(filename[4]) && filename[5] == ':' && filename[6] == '\0')
373 const char *device = DRIVE_GetDevice( toupperW(filename[4]) - 'A' );
374 if (device)
376 ret = FILE_CreateFile( device, access, sharing, sa, creation,
377 attributes, template, TRUE, DRIVE_FIXED );
379 else
381 SetLastError( ERROR_ACCESS_DENIED );
382 ret = INVALID_HANDLE_VALUE;
384 goto done;
386 else if (!RtlIsDosDeviceName_U( filename ))
388 ret = VXD_Open( filename+4, access, sa );
389 goto done;
391 else
392 filename+=4; /* fall into DOSFS_Device case below */
395 /* If the name still starts with '\\', it's a UNC name. */
396 if (!strncmpW(filename, bkslashesW, 2))
398 ret = SMB_CreateFileW(filename, access, sharing, sa, creation, attributes, template );
399 goto done;
402 /* If the name contains a DOS wild card (* or ?), do no create a file */
403 if(strchrW(filename, '*') || strchrW(filename, '?'))
405 SetLastError(ERROR_BAD_PATHNAME);
406 return INVALID_HANDLE_VALUE;
409 /* Open a console for CONIN$ or CONOUT$ */
410 if (!strcmpiW(filename, coninW) || !strcmpiW(filename, conoutW))
412 ret = OpenConsoleW(filename, access, sa, creation);
413 goto done;
416 if (RtlIsDosDeviceName_U( filename ))
418 TRACE("opening device %s\n", debugstr_w(filename) );
420 if (!(ret = DOSFS_OpenDevice( filename, access, attributes, sa )))
422 /* Do not silence this please. It is a critical error. -MM */
423 ERR("Couldn't open device %s!\n", debugstr_w(filename));
424 SetLastError( ERROR_FILE_NOT_FOUND );
426 goto done;
429 /* check for filename, don't check for last entry if creating */
430 if (!DOSFS_GetFullName( filename,
431 (creation == OPEN_EXISTING) ||
432 (creation == TRUNCATE_EXISTING),
433 &full_name )) {
434 WARN("Unable to get full filename from %s (GLE %ld)\n",
435 debugstr_w(filename), GetLastError());
436 return INVALID_HANDLE_VALUE;
439 ret = FILE_CreateFile( full_name.long_name, access, sharing,
440 sa, creation, attributes, template,
441 DRIVE_GetFlags(full_name.drive) & DRIVE_FAIL_READ_ONLY,
442 GetDriveTypeW( full_name.short_name ) );
443 done:
444 if (!ret) ret = INVALID_HANDLE_VALUE;
445 TRACE("returning %p\n", ret);
446 return ret;
451 /*************************************************************************
452 * CreateFileA (KERNEL32.@)
454 HANDLE WINAPI CreateFileA( LPCSTR filename, DWORD access, DWORD sharing,
455 LPSECURITY_ATTRIBUTES sa, DWORD creation,
456 DWORD attributes, HANDLE template)
458 UNICODE_STRING filenameW;
459 HANDLE ret = INVALID_HANDLE_VALUE;
461 if (!filename)
463 SetLastError( ERROR_INVALID_PARAMETER );
464 return INVALID_HANDLE_VALUE;
467 if (RtlCreateUnicodeStringFromAsciiz(&filenameW, filename))
469 ret = CreateFileW(filenameW.Buffer, access, sharing, sa, creation,
470 attributes, template);
471 RtlFreeUnicodeString(&filenameW);
473 else
474 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
475 return ret;
479 /***********************************************************************
480 * FILE_FillInfo
482 * Fill a file information from a struct stat.
484 static void FILE_FillInfo( struct stat *st, BY_HANDLE_FILE_INFORMATION *info )
486 if (S_ISDIR(st->st_mode))
487 info->dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
488 else
489 info->dwFileAttributes = FILE_ATTRIBUTE_ARCHIVE;
490 if (!(st->st_mode & S_IWUSR))
491 info->dwFileAttributes |= FILE_ATTRIBUTE_READONLY;
493 RtlSecondsSince1970ToTime( st->st_mtime, (LARGE_INTEGER *)&info->ftCreationTime );
494 RtlSecondsSince1970ToTime( st->st_mtime, (LARGE_INTEGER *)&info->ftLastWriteTime );
495 RtlSecondsSince1970ToTime( st->st_atime, (LARGE_INTEGER *)&info->ftLastAccessTime );
497 info->dwVolumeSerialNumber = 0; /* FIXME */
498 info->nFileSizeHigh = 0;
499 info->nFileSizeLow = 0;
500 if (!S_ISDIR(st->st_mode)) {
501 info->nFileSizeHigh = st->st_size >> 32;
502 info->nFileSizeLow = st->st_size & 0xffffffff;
504 info->nNumberOfLinks = st->st_nlink;
505 info->nFileIndexHigh = 0;
506 info->nFileIndexLow = st->st_ino;
510 /***********************************************************************
511 * get_show_dot_files_option
513 static BOOL get_show_dot_files_option(void)
515 static const WCHAR WineW[] = {'M','a','c','h','i','n','e','\\',
516 'S','o','f','t','w','a','r','e','\\',
517 'W','i','n','e','\\','W','i','n','e','\\',
518 'C','o','n','f','i','g','\\','W','i','n','e',0};
519 static const WCHAR ShowDotFilesW[] = {'S','h','o','w','D','o','t','F','i','l','e','s',0};
521 char tmp[80];
522 HKEY hkey;
523 DWORD dummy;
524 OBJECT_ATTRIBUTES attr;
525 UNICODE_STRING nameW;
526 BOOL ret = FALSE;
528 attr.Length = sizeof(attr);
529 attr.RootDirectory = 0;
530 attr.ObjectName = &nameW;
531 attr.Attributes = 0;
532 attr.SecurityDescriptor = NULL;
533 attr.SecurityQualityOfService = NULL;
534 RtlInitUnicodeString( &nameW, WineW );
536 if (!NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr ))
538 RtlInitUnicodeString( &nameW, ShowDotFilesW );
539 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
541 WCHAR *str = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
542 ret = IS_OPTION_TRUE( str[0] );
544 NtClose( hkey );
546 return ret;
550 /***********************************************************************
551 * FILE_Stat
553 * Stat a Unix path name. Return TRUE if OK.
555 BOOL FILE_Stat( LPCSTR unixName, BY_HANDLE_FILE_INFORMATION *info, BOOL *is_symlink_ptr )
557 struct stat st;
558 int is_symlink;
559 LPCSTR p;
561 if (lstat( unixName, &st ) == -1)
563 FILE_SetDosError();
564 return FALSE;
566 is_symlink = S_ISLNK(st.st_mode);
567 if (is_symlink)
569 /* do a "real" stat to find out
570 about the type of the symlink destination */
571 if (stat( unixName, &st ) == -1)
573 FILE_SetDosError();
574 return FALSE;
578 /* fill in the information we gathered so far */
579 FILE_FillInfo( &st, info );
581 /* and now see if this is a hidden file, based on the name */
582 p = strrchr( unixName, '/');
583 p = p ? p + 1 : unixName;
584 if (*p == '.' && *(p+1) && (*(p+1) != '.' || *(p+2)))
586 static int show_dot_files = -1;
587 if (show_dot_files == -1)
588 show_dot_files = get_show_dot_files_option();
589 if (!show_dot_files)
590 info->dwFileAttributes |= FILE_ATTRIBUTE_HIDDEN;
592 if (is_symlink_ptr) *is_symlink_ptr = is_symlink;
593 return TRUE;
597 /***********************************************************************
598 * GetFileInformationByHandle (KERNEL32.@)
600 DWORD WINAPI GetFileInformationByHandle( HANDLE hFile,
601 BY_HANDLE_FILE_INFORMATION *info )
603 DWORD ret;
604 if (!info) return 0;
606 TRACE("%p\n", hFile);
608 SERVER_START_REQ( get_file_info )
610 req->handle = hFile;
611 if ((ret = !wine_server_call_err( req )))
613 /* FIXME: which file types are supported ?
614 * Serial ports (FILE_TYPE_CHAR) are not,
615 * and MSDN also says that pipes are not supported.
616 * FILE_TYPE_REMOTE seems to be supported according to
617 * MSDN q234741.txt */
618 if ((reply->type == FILE_TYPE_DISK) || (reply->type == FILE_TYPE_REMOTE))
620 RtlSecondsSince1970ToTime( reply->write_time, (LARGE_INTEGER *)&info->ftCreationTime );
621 RtlSecondsSince1970ToTime( reply->write_time, (LARGE_INTEGER *)&info->ftLastWriteTime );
622 RtlSecondsSince1970ToTime( reply->access_time, (LARGE_INTEGER *)&info->ftLastAccessTime );
623 info->dwFileAttributes = reply->attr;
624 info->dwVolumeSerialNumber = reply->serial;
625 info->nFileSizeHigh = reply->size_high;
626 info->nFileSizeLow = reply->size_low;
627 info->nNumberOfLinks = reply->links;
628 info->nFileIndexHigh = reply->index_high;
629 info->nFileIndexLow = reply->index_low;
631 else
633 SetLastError(ERROR_NOT_SUPPORTED);
634 ret = 0;
638 SERVER_END_REQ;
639 return ret;
643 /**************************************************************************
644 * GetFileAttributesW (KERNEL32.@)
646 DWORD WINAPI GetFileAttributesW( LPCWSTR name )
648 DOS_FULL_NAME full_name;
649 BY_HANDLE_FILE_INFORMATION info;
651 if (name == NULL)
653 SetLastError( ERROR_INVALID_PARAMETER );
654 return INVALID_FILE_ATTRIBUTES;
656 if (!DOSFS_GetFullName( name, TRUE, &full_name) )
657 return INVALID_FILE_ATTRIBUTES;
658 if (!FILE_Stat( full_name.long_name, &info, NULL ))
659 return INVALID_FILE_ATTRIBUTES;
660 return info.dwFileAttributes;
664 /**************************************************************************
665 * GetFileAttributesA (KERNEL32.@)
667 DWORD WINAPI GetFileAttributesA( LPCSTR name )
669 UNICODE_STRING nameW;
670 DWORD ret = INVALID_FILE_ATTRIBUTES;
672 if (!name)
674 SetLastError( ERROR_INVALID_PARAMETER );
675 return INVALID_FILE_ATTRIBUTES;
678 if (RtlCreateUnicodeStringFromAsciiz(&nameW, name))
680 ret = GetFileAttributesW(nameW.Buffer);
681 RtlFreeUnicodeString(&nameW);
683 else
684 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
685 return ret;
689 /**************************************************************************
690 * SetFileAttributesW (KERNEL32.@)
692 BOOL WINAPI SetFileAttributesW(LPCWSTR lpFileName, DWORD attributes)
694 struct stat buf;
695 DOS_FULL_NAME full_name;
697 if (!lpFileName)
699 SetLastError( ERROR_INVALID_PARAMETER );
700 return FALSE;
703 TRACE("(%s,%lx)\n", debugstr_w(lpFileName), attributes);
705 if (!DOSFS_GetFullName( lpFileName, TRUE, &full_name ))
706 return FALSE;
708 if(stat(full_name.long_name,&buf)==-1)
710 FILE_SetDosError();
711 return FALSE;
713 if (attributes & FILE_ATTRIBUTE_READONLY)
715 if(S_ISDIR(buf.st_mode))
716 /* FIXME */
717 WARN("FILE_ATTRIBUTE_READONLY ignored for directory.\n");
718 else
719 buf.st_mode &= ~0222; /* octal!, clear write permission bits */
720 attributes &= ~FILE_ATTRIBUTE_READONLY;
722 else
724 /* add write permission */
725 buf.st_mode |= (0600 | ((buf.st_mode & 044) >> 1)) & (~FILE_umask);
727 if (attributes & FILE_ATTRIBUTE_DIRECTORY)
729 if (!S_ISDIR(buf.st_mode))
730 FIXME("SetFileAttributes expected the file %s to be a directory\n",
731 debugstr_w(lpFileName));
732 attributes &= ~FILE_ATTRIBUTE_DIRECTORY;
734 attributes &= ~(FILE_ATTRIBUTE_NORMAL|FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN|
735 FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_TEMPORARY);
736 if (attributes)
737 FIXME("(%s):%lx attribute(s) not implemented.\n", debugstr_w(lpFileName), attributes);
738 if (-1==chmod(full_name.long_name,buf.st_mode))
740 if (GetDriveTypeW(lpFileName) == DRIVE_CDROM)
742 SetLastError( ERROR_ACCESS_DENIED );
743 return FALSE;
747 * FIXME: We don't return FALSE here because of differences between
748 * Linux and Windows privileges. Under Linux only the owner of
749 * the file is allowed to change file attributes. Under Windows,
750 * applications expect that if you can write to a file, you can also
751 * change its attributes (see GENERIC_WRITE). We could try to be
752 * clever here but that would break multi-user installations where
753 * users share read-only DLLs. This is because some installers like
754 * to change attributes of already installed DLLs.
756 FIXME("Couldn't set file attributes for existing file \"%s\".\n"
757 "Check permissions or set VFAT \"quiet\" mount flag\n", full_name.long_name);
759 return TRUE;
763 /**************************************************************************
764 * SetFileAttributesA (KERNEL32.@)
766 BOOL WINAPI SetFileAttributesA(LPCSTR lpFileName, DWORD attributes)
768 UNICODE_STRING filenameW;
769 BOOL ret = FALSE;
771 if (!lpFileName)
773 SetLastError( ERROR_INVALID_PARAMETER );
774 return FALSE;
777 if (RtlCreateUnicodeStringFromAsciiz(&filenameW, lpFileName))
779 ret = SetFileAttributesW(filenameW.Buffer, attributes);
780 RtlFreeUnicodeString(&filenameW);
782 else
783 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
784 return ret;
788 /******************************************************************************
789 * GetCompressedFileSizeA [KERNEL32.@]
791 DWORD WINAPI GetCompressedFileSizeA(
792 LPCSTR lpFileName,
793 LPDWORD lpFileSizeHigh)
795 UNICODE_STRING filenameW;
796 DWORD ret;
798 if (RtlCreateUnicodeStringFromAsciiz(&filenameW, lpFileName))
800 ret = GetCompressedFileSizeW(filenameW.Buffer, lpFileSizeHigh);
801 RtlFreeUnicodeString(&filenameW);
803 else
805 ret = INVALID_FILE_SIZE;
806 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
808 return ret;
812 /******************************************************************************
813 * GetCompressedFileSizeW [KERNEL32.@]
815 * RETURNS
816 * Success: Low-order doubleword of number of bytes
817 * Failure: INVALID_FILE_SIZE
819 DWORD WINAPI GetCompressedFileSizeW(
820 LPCWSTR lpFileName, /* [in] Pointer to name of file */
821 LPDWORD lpFileSizeHigh) /* [out] Receives high-order doubleword of size */
823 DOS_FULL_NAME full_name;
824 struct stat st;
825 DWORD low;
827 TRACE("(%s,%p)\n",debugstr_w(lpFileName),lpFileSizeHigh);
829 if (!DOSFS_GetFullName( lpFileName, TRUE, &full_name )) return INVALID_FILE_SIZE;
830 if (stat(full_name.long_name, &st) != 0)
832 FILE_SetDosError();
833 return INVALID_FILE_SIZE;
835 #if HAVE_STRUCT_STAT_ST_BLOCKS
836 /* blocks are 512 bytes long */
837 if (lpFileSizeHigh) *lpFileSizeHigh = (st.st_blocks >> 23);
838 low = (DWORD)(st.st_blocks << 9);
839 #else
840 if (lpFileSizeHigh) *lpFileSizeHigh = (st.st_size >> 32);
841 low = (DWORD)st.st_size;
842 #endif
843 return low;
847 /***********************************************************************
848 * GetFileTime (KERNEL32.@)
850 BOOL WINAPI GetFileTime( HANDLE hFile, FILETIME *lpCreationTime,
851 FILETIME *lpLastAccessTime,
852 FILETIME *lpLastWriteTime )
854 BY_HANDLE_FILE_INFORMATION info;
855 if (!GetFileInformationByHandle( hFile, &info )) return FALSE;
856 if (lpCreationTime) *lpCreationTime = info.ftCreationTime;
857 if (lpLastAccessTime) *lpLastAccessTime = info.ftLastAccessTime;
858 if (lpLastWriteTime) *lpLastWriteTime = info.ftLastWriteTime;
859 return TRUE;
863 /***********************************************************************
864 * GetTempFileNameA (KERNEL32.@)
866 UINT WINAPI GetTempFileNameA( LPCSTR path, LPCSTR prefix, UINT unique,
867 LPSTR buffer)
869 UNICODE_STRING pathW, prefixW;
870 WCHAR bufferW[MAX_PATH];
871 UINT ret;
873 if ( !path || !prefix || !buffer )
875 SetLastError( ERROR_INVALID_PARAMETER );
876 return 0;
879 RtlCreateUnicodeStringFromAsciiz(&pathW, path);
880 RtlCreateUnicodeStringFromAsciiz(&prefixW, prefix);
882 ret = GetTempFileNameW(pathW.Buffer, prefixW.Buffer, unique, bufferW);
883 if (ret)
884 WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buffer, MAX_PATH, NULL, NULL);
886 RtlFreeUnicodeString(&pathW);
887 RtlFreeUnicodeString(&prefixW);
888 return ret;
891 /***********************************************************************
892 * GetTempFileNameW (KERNEL32.@)
894 UINT WINAPI GetTempFileNameW( LPCWSTR path, LPCWSTR prefix, UINT unique,
895 LPWSTR buffer )
897 static const WCHAR formatW[] = {'%','x','.','t','m','p',0};
899 DOS_FULL_NAME full_name;
900 int i;
901 LPWSTR p;
903 if ( !path || !prefix || !buffer )
905 SetLastError( ERROR_INVALID_PARAMETER );
906 return 0;
909 strcpyW( buffer, path );
910 p = buffer + strlenW(buffer);
912 /* add a \, if there isn't one */
913 if ((p == buffer) || (p[-1] != '\\')) *p++ = '\\';
915 for (i = 3; (i > 0) && (*prefix); i--) *p++ = *prefix++;
917 unique &= 0xffff;
919 if (unique) sprintfW( p, formatW, unique );
920 else
922 /* get a "random" unique number and try to create the file */
923 HANDLE handle;
924 UINT num = GetTickCount() & 0xffff;
926 if (!num) num = 1;
927 unique = num;
930 sprintfW( p, formatW, unique );
931 handle = CreateFileW( buffer, GENERIC_WRITE, 0, NULL,
932 CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0 );
933 if (handle != INVALID_HANDLE_VALUE)
934 { /* We created it */
935 TRACE("created %s\n", debugstr_w(buffer) );
936 CloseHandle( handle );
937 break;
939 if (GetLastError() != ERROR_FILE_EXISTS &&
940 GetLastError() != ERROR_SHARING_VIOLATION)
941 break; /* No need to go on */
942 if (!(++unique & 0xffff)) unique = 1;
943 } while (unique != num);
946 /* Get the full path name */
948 if (DOSFS_GetFullName( buffer, FALSE, &full_name ))
950 char *slash;
951 /* Check if we have write access in the directory */
952 if ((slash = strrchr( full_name.long_name, '/' ))) *slash = '\0';
953 if (access( full_name.long_name, W_OK ) == -1)
954 WARN("returns %s, which doesn't seem to be writeable.\n",
955 debugstr_w(buffer) );
957 TRACE("returning %s\n", debugstr_w(buffer) );
958 return unique;
962 /***********************************************************************
963 * FILE_DoOpenFile
965 * Implementation of OpenFile16() and OpenFile32().
967 static HFILE FILE_DoOpenFile( LPCSTR name, OFSTRUCT *ofs, UINT mode, BOOL win32 )
969 HFILE hFileRet;
970 HANDLE handle;
971 FILETIME filetime;
972 WORD filedatetime[2];
973 DOS_FULL_NAME full_name;
974 DWORD access, sharing;
975 WCHAR *p;
976 WCHAR buffer[MAX_PATH];
977 LPWSTR nameW;
979 if (!ofs) return HFILE_ERROR;
981 TRACE("%s %s %s %s%s%s%s%s%s%s%s%s\n",name,
982 ((mode & 0x3 )==OF_READ)?"OF_READ":
983 ((mode & 0x3 )==OF_WRITE)?"OF_WRITE":
984 ((mode & 0x3 )==OF_READWRITE)?"OF_READWRITE":"unknown",
985 ((mode & 0x70 )==OF_SHARE_COMPAT)?"OF_SHARE_COMPAT":
986 ((mode & 0x70 )==OF_SHARE_DENY_NONE)?"OF_SHARE_DENY_NONE":
987 ((mode & 0x70 )==OF_SHARE_DENY_READ)?"OF_SHARE_DENY_READ":
988 ((mode & 0x70 )==OF_SHARE_DENY_WRITE)?"OF_SHARE_DENY_WRITE":
989 ((mode & 0x70 )==OF_SHARE_EXCLUSIVE)?"OF_SHARE_EXCLUSIVE":"unknown",
990 ((mode & OF_PARSE )==OF_PARSE)?"OF_PARSE ":"",
991 ((mode & OF_DELETE )==OF_DELETE)?"OF_DELETE ":"",
992 ((mode & OF_VERIFY )==OF_VERIFY)?"OF_VERIFY ":"",
993 ((mode & OF_SEARCH )==OF_SEARCH)?"OF_SEARCH ":"",
994 ((mode & OF_CANCEL )==OF_CANCEL)?"OF_CANCEL ":"",
995 ((mode & OF_CREATE )==OF_CREATE)?"OF_CREATE ":"",
996 ((mode & OF_PROMPT )==OF_PROMPT)?"OF_PROMPT ":"",
997 ((mode & OF_EXIST )==OF_EXIST)?"OF_EXIST ":"",
998 ((mode & OF_REOPEN )==OF_REOPEN)?"OF_REOPEN ":""
1002 ofs->cBytes = sizeof(OFSTRUCT);
1003 ofs->nErrCode = 0;
1004 if (mode & OF_REOPEN) name = ofs->szPathName;
1006 if (!name) {
1007 ERR("called with `name' set to NULL ! Please debug.\n");
1008 return HFILE_ERROR;
1011 TRACE("%s %04x\n", name, mode );
1013 /* the watcom 10.6 IDE relies on a valid path returned in ofs->szPathName
1014 Are there any cases where getting the path here is wrong?
1015 Uwe Bonnes 1997 Apr 2 */
1016 if (!GetFullPathNameA( name, sizeof(ofs->szPathName),
1017 ofs->szPathName, NULL )) goto error;
1018 FILE_ConvertOFMode( mode, &access, &sharing );
1020 /* OF_PARSE simply fills the structure */
1022 if (mode & OF_PARSE)
1024 ofs->fFixedDisk = (GetDriveType16( ofs->szPathName[0]-'A' )
1025 != DRIVE_REMOVABLE);
1026 TRACE("(%s): OF_PARSE, res = '%s'\n",
1027 name, ofs->szPathName );
1028 return 0;
1031 /* OF_CREATE is completely different from all other options, so
1032 handle it first */
1034 if (mode & OF_CREATE)
1036 if ((handle = CreateFileA( name, GENERIC_READ | GENERIC_WRITE,
1037 sharing, NULL, CREATE_ALWAYS,
1038 FILE_ATTRIBUTE_NORMAL, 0 ))== INVALID_HANDLE_VALUE)
1039 goto error;
1040 goto success;
1043 MultiByteToWideChar(CP_ACP, 0, name, -1, buffer, MAX_PATH);
1044 nameW = buffer;
1046 /* If OF_SEARCH is set, ignore the given path */
1048 if ((mode & OF_SEARCH) && !(mode & OF_REOPEN))
1050 /* First try the file name as is */
1051 if (DOSFS_GetFullName( nameW, TRUE, &full_name )) goto found;
1052 /* Now remove the path */
1053 if (nameW[0] && (nameW[1] == ':')) nameW += 2;
1054 if ((p = strrchrW( nameW, '\\' ))) nameW = p + 1;
1055 if ((p = strrchrW( nameW, '/' ))) nameW = p + 1;
1056 if (!nameW[0]) goto not_found;
1059 /* Now look for the file */
1061 if (!DIR_SearchPath( NULL, nameW, NULL, &full_name, win32 )) goto not_found;
1063 found:
1064 TRACE("found %s = %s\n",
1065 full_name.long_name, debugstr_w(full_name.short_name) );
1066 WideCharToMultiByte(CP_ACP, 0, full_name.short_name, -1,
1067 ofs->szPathName, sizeof(ofs->szPathName), NULL, NULL);
1069 if (mode & OF_DELETE)
1071 handle = FILE_CreateFile( full_name.long_name, GENERIC_READ|GENERIC_WRITE, 0,
1072 NULL, OPEN_EXISTING, 0, 0, TRUE,
1073 GetDriveTypeW( full_name.short_name ) );
1074 if (!handle) goto error;
1075 CloseHandle( handle );
1076 if (unlink( full_name.long_name ) == -1) goto not_found;
1077 TRACE("(%s): OF_DELETE return = OK\n", name);
1078 return 1;
1081 handle = FILE_CreateFile( full_name.long_name, access, sharing,
1082 NULL, OPEN_EXISTING, 0, 0,
1083 DRIVE_GetFlags(full_name.drive) & DRIVE_FAIL_READ_ONLY,
1084 GetDriveTypeW( full_name.short_name ) );
1085 if (!handle) goto not_found;
1087 GetFileTime( handle, NULL, NULL, &filetime );
1088 FileTimeToDosDateTime( &filetime, &filedatetime[0], &filedatetime[1] );
1089 if ((mode & OF_VERIFY) && (mode & OF_REOPEN))
1091 if (ofs->Reserved1 != filedatetime[0] || ofs->Reserved2 != filedatetime[1] )
1093 CloseHandle( handle );
1094 WARN("(%s): OF_VERIFY failed\n", name );
1095 /* FIXME: what error here? */
1096 SetLastError( ERROR_FILE_NOT_FOUND );
1097 goto error;
1100 ofs->Reserved1 = filedatetime[0];
1101 ofs->Reserved2 = filedatetime[1];
1103 success: /* We get here if the open was successful */
1104 TRACE("(%s): OK, return = %p\n", name, handle );
1105 if (win32)
1107 hFileRet = (HFILE)handle;
1108 if (mode & OF_EXIST) /* Return the handle, but close it first */
1109 CloseHandle( handle );
1111 else
1113 hFileRet = Win32HandleToDosFileHandle( handle );
1114 if (hFileRet == HFILE_ERROR16) goto error;
1115 if (mode & OF_EXIST) /* Return the handle, but close it first */
1116 _lclose16( hFileRet );
1118 return hFileRet;
1120 not_found: /* We get here if the file does not exist */
1121 WARN("'%s' not found or sharing violation\n", name );
1122 SetLastError( ERROR_FILE_NOT_FOUND );
1123 /* fall through */
1125 error: /* We get here if there was an error opening the file */
1126 ofs->nErrCode = GetLastError();
1127 WARN("(%s): return = HFILE_ERROR error= %d\n",
1128 name,ofs->nErrCode );
1129 return HFILE_ERROR;
1133 /***********************************************************************
1134 * OpenFile (KERNEL.74)
1135 * OpenFileEx (KERNEL.360)
1137 HFILE16 WINAPI OpenFile16( LPCSTR name, OFSTRUCT *ofs, UINT16 mode )
1139 return FILE_DoOpenFile( name, ofs, mode, FALSE );
1143 /***********************************************************************
1144 * OpenFile (KERNEL32.@)
1146 HFILE WINAPI OpenFile( LPCSTR name, OFSTRUCT *ofs, UINT mode )
1148 return FILE_DoOpenFile( name, ofs, mode, TRUE );
1152 /******************************************************************
1153 * FILE_ReadWriteApc (internal)
1157 static void WINAPI FILE_ReadWriteApc(void* apc_user, PIO_STATUS_BLOCK io_status, ULONG len)
1159 LPOVERLAPPED_COMPLETION_ROUTINE cr = (LPOVERLAPPED_COMPLETION_ROUTINE)apc_user;
1161 cr(RtlNtStatusToDosError(io_status->u.Status), len, (LPOVERLAPPED)io_status);
1164 /***********************************************************************
1165 * ReadFileEx (KERNEL32.@)
1167 BOOL WINAPI ReadFileEx(HANDLE hFile, LPVOID buffer, DWORD bytesToRead,
1168 LPOVERLAPPED overlapped,
1169 LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
1171 LARGE_INTEGER offset;
1172 NTSTATUS status;
1173 PIO_STATUS_BLOCK io_status;
1175 if (!overlapped)
1177 SetLastError(ERROR_INVALID_PARAMETER);
1178 return FALSE;
1181 offset.u.LowPart = overlapped->Offset;
1182 offset.u.HighPart = overlapped->OffsetHigh;
1183 io_status = (PIO_STATUS_BLOCK)overlapped;
1184 io_status->u.Status = STATUS_PENDING;
1186 status = NtReadFile(hFile, NULL, FILE_ReadWriteApc, lpCompletionRoutine,
1187 io_status, buffer, bytesToRead, &offset, NULL);
1189 if (status)
1191 SetLastError( RtlNtStatusToDosError(status) );
1192 return FALSE;
1194 return TRUE;
1197 /***********************************************************************
1198 * ReadFile (KERNEL32.@)
1200 BOOL WINAPI ReadFile( HANDLE hFile, LPVOID buffer, DWORD bytesToRead,
1201 LPDWORD bytesRead, LPOVERLAPPED overlapped )
1203 LARGE_INTEGER offset;
1204 PLARGE_INTEGER poffset = NULL;
1205 IO_STATUS_BLOCK iosb;
1206 PIO_STATUS_BLOCK io_status = &iosb;
1207 HANDLE hEvent = 0;
1208 NTSTATUS status;
1210 TRACE("%p %p %ld %p %p\n", hFile, buffer, bytesToRead,
1211 bytesRead, overlapped );
1213 if (bytesRead) *bytesRead = 0; /* Do this before anything else */
1214 if (!bytesToRead) return TRUE;
1216 if (IsBadReadPtr(buffer, bytesToRead))
1218 SetLastError(ERROR_WRITE_FAULT); /* FIXME */
1219 return FALSE;
1221 if (is_console_handle(hFile))
1222 return ReadConsoleA(hFile, buffer, bytesToRead, bytesRead, NULL);
1224 if (overlapped != NULL)
1226 offset.u.LowPart = overlapped->Offset;
1227 offset.u.HighPart = overlapped->OffsetHigh;
1228 poffset = &offset;
1229 hEvent = overlapped->hEvent;
1230 io_status = (PIO_STATUS_BLOCK)overlapped;
1232 io_status->u.Status = STATUS_PENDING;
1233 io_status->Information = 0;
1235 status = NtReadFile(hFile, hEvent, NULL, NULL, io_status, buffer, bytesToRead, poffset, NULL);
1237 if (status != STATUS_PENDING && bytesRead)
1238 *bytesRead = io_status->Information;
1240 if (status && status != STATUS_END_OF_FILE)
1242 SetLastError( RtlNtStatusToDosError(status) );
1243 return FALSE;
1245 return TRUE;
1249 /***********************************************************************
1250 * WriteFileEx (KERNEL32.@)
1252 BOOL WINAPI WriteFileEx(HANDLE hFile, LPCVOID buffer, DWORD bytesToWrite,
1253 LPOVERLAPPED overlapped,
1254 LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
1256 LARGE_INTEGER offset;
1257 NTSTATUS status;
1258 PIO_STATUS_BLOCK io_status;
1260 TRACE("%p %p %ld %p %p\n",
1261 hFile, buffer, bytesToWrite, overlapped, lpCompletionRoutine);
1263 if (overlapped == NULL)
1265 SetLastError(ERROR_INVALID_PARAMETER);
1266 return FALSE;
1268 offset.u.LowPart = overlapped->Offset;
1269 offset.u.HighPart = overlapped->OffsetHigh;
1271 io_status = (PIO_STATUS_BLOCK)overlapped;
1272 io_status->u.Status = STATUS_PENDING;
1274 status = NtWriteFile(hFile, NULL, FILE_ReadWriteApc, lpCompletionRoutine,
1275 io_status, buffer, bytesToWrite, &offset, NULL);
1277 if (status) SetLastError( RtlNtStatusToDosError(status) );
1278 return !status;
1281 /***********************************************************************
1282 * WriteFile (KERNEL32.@)
1284 BOOL WINAPI WriteFile( HANDLE hFile, LPCVOID buffer, DWORD bytesToWrite,
1285 LPDWORD bytesWritten, LPOVERLAPPED overlapped )
1287 HANDLE hEvent = NULL;
1288 LARGE_INTEGER offset;
1289 PLARGE_INTEGER poffset = NULL;
1290 NTSTATUS status;
1291 IO_STATUS_BLOCK iosb;
1292 PIO_STATUS_BLOCK piosb = &iosb;
1294 TRACE("%p %p %ld %p %p\n",
1295 hFile, buffer, bytesToWrite, bytesWritten, overlapped );
1297 if (is_console_handle(hFile))
1298 return WriteConsoleA(hFile, buffer, bytesToWrite, bytesWritten, NULL);
1300 if (IsBadReadPtr(buffer, bytesToWrite))
1302 SetLastError(ERROR_READ_FAULT); /* FIXME */
1303 return FALSE;
1306 if (overlapped)
1308 offset.u.LowPart = overlapped->Offset;
1309 offset.u.HighPart = overlapped->OffsetHigh;
1310 poffset = &offset;
1311 hEvent = overlapped->hEvent;
1312 piosb = (PIO_STATUS_BLOCK)overlapped;
1314 piosb->u.Status = STATUS_PENDING;
1315 piosb->Information = 0;
1317 status = NtWriteFile(hFile, hEvent, NULL, NULL, piosb,
1318 buffer, bytesToWrite, poffset, NULL);
1319 if (status)
1321 SetLastError( RtlNtStatusToDosError(status) );
1322 return FALSE;
1324 if (bytesWritten) *bytesWritten = piosb->Information;
1326 return TRUE;
1330 /***********************************************************************
1331 * SetFilePointer (KERNEL32.@)
1333 DWORD WINAPI SetFilePointer( HANDLE hFile, LONG distance, LONG *highword,
1334 DWORD method )
1336 DWORD ret = INVALID_SET_FILE_POINTER;
1338 TRACE("handle %p offset %ld high %ld origin %ld\n",
1339 hFile, distance, highword?*highword:0, method );
1341 SERVER_START_REQ( set_file_pointer )
1343 req->handle = hFile;
1344 req->low = distance;
1345 req->high = highword ? *highword : (distance >= 0) ? 0 : -1;
1346 /* FIXME: assumes 1:1 mapping between Windows and Unix seek constants */
1347 req->whence = method;
1348 SetLastError( 0 );
1349 if (!wine_server_call_err( req ))
1351 ret = reply->new_low;
1352 if (highword) *highword = reply->new_high;
1355 SERVER_END_REQ;
1356 return ret;
1360 /*************************************************************************
1361 * SetHandleCount (KERNEL32.@)
1363 UINT WINAPI SetHandleCount( UINT count )
1365 return min( 256, count );
1369 /**************************************************************************
1370 * SetEndOfFile (KERNEL32.@)
1372 BOOL WINAPI SetEndOfFile( HANDLE hFile )
1374 BOOL ret;
1375 SERVER_START_REQ( truncate_file )
1377 req->handle = hFile;
1378 ret = !wine_server_call_err( req );
1380 SERVER_END_REQ;
1381 return ret;
1385 /***********************************************************************
1386 * DeleteFileW (KERNEL32.@)
1388 BOOL WINAPI DeleteFileW( LPCWSTR path )
1390 DOS_FULL_NAME full_name;
1391 HANDLE hFile;
1393 TRACE("%s\n", debugstr_w(path) );
1394 if (!path || !*path)
1396 SetLastError(ERROR_PATH_NOT_FOUND);
1397 return FALSE;
1399 if (RtlIsDosDeviceName_U( path ))
1401 WARN("cannot remove DOS device %s!\n", debugstr_w(path));
1402 SetLastError( ERROR_FILE_NOT_FOUND );
1403 return FALSE;
1406 if (!DOSFS_GetFullName( path, TRUE, &full_name )) return FALSE;
1408 /* check if we are allowed to delete the source */
1409 hFile = FILE_CreateFile( full_name.long_name, GENERIC_READ|GENERIC_WRITE, 0,
1410 NULL, OPEN_EXISTING, 0, 0, TRUE,
1411 GetDriveTypeW( full_name.short_name ) );
1412 if (!hFile) return FALSE;
1414 if (unlink( full_name.long_name ) == -1)
1416 FILE_SetDosError();
1417 CloseHandle(hFile);
1418 return FALSE;
1420 CloseHandle(hFile);
1421 return TRUE;
1425 /***********************************************************************
1426 * DeleteFileA (KERNEL32.@)
1428 BOOL WINAPI DeleteFileA( LPCSTR path )
1430 UNICODE_STRING pathW;
1431 BOOL ret = FALSE;
1433 if (!path)
1435 SetLastError(ERROR_INVALID_PARAMETER);
1436 return FALSE;
1439 if (RtlCreateUnicodeStringFromAsciiz(&pathW, path))
1441 ret = DeleteFileW(pathW.Buffer);
1442 RtlFreeUnicodeString(&pathW);
1444 else
1445 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1446 return ret;
1450 /***********************************************************************
1451 * GetFileType (KERNEL32.@)
1453 DWORD WINAPI GetFileType( HANDLE hFile )
1455 DWORD ret = FILE_TYPE_UNKNOWN;
1457 if (is_console_handle( hFile ))
1458 return FILE_TYPE_CHAR;
1460 SERVER_START_REQ( get_file_info )
1462 req->handle = hFile;
1463 if (!wine_server_call_err( req )) ret = reply->type;
1465 SERVER_END_REQ;
1466 return ret;
1470 /* check if a file name is for an executable file (.exe or .com) */
1471 inline static BOOL is_executable( const char *name )
1473 int len = strlen(name);
1475 if (len < 4) return FALSE;
1476 return (!strcasecmp( name + len - 4, ".exe" ) ||
1477 !strcasecmp( name + len - 4, ".com" ));
1481 /***********************************************************************
1482 * FILE_AddBootRenameEntry
1484 * Adds an entry to the registry that is loaded when windows boots and
1485 * checks if there are some files to be removed or renamed/moved.
1486 * <fn1> has to be valid and <fn2> may be NULL. If both pointers are
1487 * non-NULL then the file is moved, otherwise it is deleted. The
1488 * entry of the registrykey is always appended with two zero
1489 * terminated strings. If <fn2> is NULL then the second entry is
1490 * simply a single 0-byte. Otherwise the second filename goes
1491 * there. The entries are prepended with \??\ before the path and the
1492 * second filename gets also a '!' as the first character if
1493 * MOVEFILE_REPLACE_EXISTING is set. After the final string another
1494 * 0-byte follows to indicate the end of the strings.
1495 * i.e.:
1496 * \??\D:\test\file1[0]
1497 * !\??\D:\test\file1_renamed[0]
1498 * \??\D:\Test|delete[0]
1499 * [0] <- file is to be deleted, second string empty
1500 * \??\D:\test\file2[0]
1501 * !\??\D:\test\file2_renamed[0]
1502 * [0] <- indicates end of strings
1504 * or:
1505 * \??\D:\test\file1[0]
1506 * !\??\D:\test\file1_renamed[0]
1507 * \??\D:\Test|delete[0]
1508 * [0] <- file is to be deleted, second string empty
1509 * [0] <- indicates end of strings
1512 static BOOL FILE_AddBootRenameEntry( LPCWSTR fn1, LPCWSTR fn2, DWORD flags )
1514 static const WCHAR PreString[] = {'\\','?','?','\\',0};
1515 static const WCHAR ValueName[] = {'P','e','n','d','i','n','g',
1516 'F','i','l','e','R','e','n','a','m','e',
1517 'O','p','e','r','a','t','i','o','n','s',0};
1518 static const WCHAR SessionW[] = {'M','a','c','h','i','n','e','\\',
1519 'S','y','s','t','e','m','\\',
1520 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
1521 'C','o','n','t','r','o','l','\\',
1522 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r',0};
1523 static const int info_size = FIELD_OFFSET( KEY_VALUE_PARTIAL_INFORMATION, Data );
1525 OBJECT_ATTRIBUTES attr;
1526 UNICODE_STRING nameW;
1527 KEY_VALUE_PARTIAL_INFORMATION *info;
1528 BOOL rc = FALSE;
1529 HKEY Reboot = 0;
1530 DWORD len0, len1, len2;
1531 DWORD DataSize = 0;
1532 BYTE *Buffer = NULL;
1533 WCHAR *p;
1535 attr.Length = sizeof(attr);
1536 attr.RootDirectory = 0;
1537 attr.ObjectName = &nameW;
1538 attr.Attributes = 0;
1539 attr.SecurityDescriptor = NULL;
1540 attr.SecurityQualityOfService = NULL;
1541 RtlInitUnicodeString( &nameW, SessionW );
1543 if (NtCreateKey( &Reboot, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ) != STATUS_SUCCESS)
1545 WARN("Error creating key for reboot managment [%s]\n",
1546 "SYSTEM\\CurrentControlSet\\Control\\Session Manager");
1547 return FALSE;
1550 len0 = strlenW(PreString);
1551 len1 = strlenW(fn1) + len0 + 1;
1552 if (fn2)
1554 len2 = strlenW(fn2) + len0 + 1;
1555 if (flags & MOVEFILE_REPLACE_EXISTING) len2++; /* Plus 1 because of the leading '!' */
1557 else len2 = 1; /* minimum is the 0 characters for the empty second string */
1559 /* convert characters to bytes */
1560 len0 *= sizeof(WCHAR);
1561 len1 *= sizeof(WCHAR);
1562 len2 *= sizeof(WCHAR);
1564 RtlInitUnicodeString( &nameW, ValueName );
1566 /* First we check if the key exists and if so how many bytes it already contains. */
1567 if (NtQueryValueKey( Reboot, &nameW, KeyValuePartialInformation,
1568 NULL, 0, &DataSize ) == STATUS_BUFFER_OVERFLOW)
1570 if (!(Buffer = HeapAlloc( GetProcessHeap(), 0, DataSize + len1 + len2 + sizeof(WCHAR) )))
1571 goto Quit;
1572 if (NtQueryValueKey( Reboot, &nameW, KeyValuePartialInformation,
1573 Buffer, DataSize, &DataSize )) goto Quit;
1574 info = (KEY_VALUE_PARTIAL_INFORMATION *)Buffer;
1575 if (info->Type != REG_MULTI_SZ) goto Quit;
1576 if (DataSize > sizeof(info)) DataSize -= sizeof(WCHAR); /* remove terminating null (will be added back later) */
1578 else
1580 DataSize = info_size;
1581 if (!(Buffer = HeapAlloc( GetProcessHeap(), 0, DataSize + len1 + len2 + sizeof(WCHAR) )))
1582 goto Quit;
1585 p = (WCHAR *)(Buffer + DataSize);
1586 strcpyW( p, PreString );
1587 strcatW( p, fn1 );
1588 DataSize += len1;
1589 if (fn2)
1591 p = (WCHAR *)(Buffer + DataSize);
1592 if (flags & MOVEFILE_REPLACE_EXISTING)
1593 *p++ = '!';
1594 strcpyW( p, PreString );
1595 strcatW( p, fn2 );
1596 DataSize += len2;
1598 else
1600 p = (WCHAR *)(Buffer + DataSize);
1601 *p = 0;
1602 DataSize += sizeof(WCHAR);
1605 /* add final null */
1606 p = (WCHAR *)(Buffer + DataSize);
1607 *p = 0;
1608 DataSize += sizeof(WCHAR);
1610 rc = !NtSetValueKey(Reboot, &nameW, 0, REG_MULTI_SZ, Buffer + info_size, DataSize - info_size);
1612 Quit:
1613 if (Reboot) NtClose(Reboot);
1614 if (Buffer) HeapFree( GetProcessHeap(), 0, Buffer );
1615 return(rc);
1619 /**************************************************************************
1620 * MoveFileExW (KERNEL32.@)
1622 BOOL WINAPI MoveFileExW( LPCWSTR fn1, LPCWSTR fn2, DWORD flag )
1624 DOS_FULL_NAME full_name1, full_name2;
1625 HANDLE hFile;
1626 DWORD attr = INVALID_FILE_ATTRIBUTES;
1628 TRACE("(%s,%s,%04lx)\n", debugstr_w(fn1), debugstr_w(fn2), flag);
1630 /* FIXME: <Gerhard W. Gruber>sparhawk@gmx.at
1631 In case of W9x and lesser this function should return 120 (ERROR_CALL_NOT_IMPLEMENTED)
1632 to be really compatible. Most programs won't have any problems though. In case
1633 you encounter one, this is what you should return here. I don't know what's up
1634 with NT 3.5. Is this function available there or not?
1635 Does anybody really care about 3.5? :)
1638 /* Filename1 has to be always set to a valid path. Filename2 may be NULL
1639 if the source file has to be deleted.
1641 if (!fn1) {
1642 SetLastError(ERROR_INVALID_PARAMETER);
1643 return FALSE;
1646 /* This function has to be run through in order to process the name properly.
1647 If the BOOTDELAY flag is set, the file doesn't need to exist though. At least
1648 that is the behaviour on NT 4.0. The operation accepts the filenames as
1649 they are given but it can't reply with a reasonable returncode. Success
1650 means in that case success for entering the values into the registry.
1652 if(!DOSFS_GetFullName( fn1, TRUE, &full_name1 ))
1654 if(!(flag & MOVEFILE_DELAY_UNTIL_REBOOT))
1655 return FALSE;
1658 if (fn2) /* !fn2 means delete fn1 */
1660 if (DOSFS_GetFullName( fn2, TRUE, &full_name2 ))
1662 if(!(flag & MOVEFILE_DELAY_UNTIL_REBOOT))
1664 /* target exists, check if we may overwrite */
1665 if (!(flag & MOVEFILE_REPLACE_EXISTING))
1667 SetLastError( ERROR_ALREADY_EXISTS );
1668 return FALSE;
1672 else
1674 if (!DOSFS_GetFullName( fn2, FALSE, &full_name2 ))
1676 if(!(flag & MOVEFILE_DELAY_UNTIL_REBOOT))
1677 return FALSE;
1681 /* Source name and target path are valid */
1683 if (flag & MOVEFILE_DELAY_UNTIL_REBOOT)
1685 return FILE_AddBootRenameEntry( fn1, fn2, flag );
1688 attr = GetFileAttributesW( fn1 );
1689 if ( attr == INVALID_FILE_ATTRIBUTES ) return FALSE;
1691 /* check if we are allowed to rename the source */
1692 hFile = FILE_CreateFile( full_name1.long_name, 0, 0,
1693 NULL, OPEN_EXISTING, 0, 0, TRUE,
1694 GetDriveTypeW( full_name1.short_name ) );
1695 if (!hFile)
1697 if (GetLastError() != ERROR_ACCESS_DENIED) return FALSE;
1698 if ( !(attr & FILE_ATTRIBUTE_DIRECTORY) ) return FALSE;
1699 /* if it's a directory we can continue */
1701 else CloseHandle(hFile);
1703 /* check, if we are allowed to delete the destination,
1704 ** (but the file not being there is fine) */
1705 hFile = FILE_CreateFile( full_name2.long_name, GENERIC_READ|GENERIC_WRITE, 0,
1706 NULL, OPEN_EXISTING, 0, 0, TRUE,
1707 GetDriveTypeW( full_name2.short_name ) );
1708 if(!hFile && GetLastError() != ERROR_FILE_NOT_FOUND) return FALSE;
1709 CloseHandle(hFile);
1711 if (full_name1.drive != full_name2.drive)
1713 if (!(flag & MOVEFILE_COPY_ALLOWED))
1715 SetLastError( ERROR_NOT_SAME_DEVICE );
1716 return FALSE;
1718 else if ( attr & FILE_ATTRIBUTE_DIRECTORY )
1720 /* Strange, but that's what Windows returns */
1721 SetLastError ( ERROR_ACCESS_DENIED );
1722 return FALSE;
1725 if (rename( full_name1.long_name, full_name2.long_name ) == -1)
1726 /* Try copy/delete unless it's a directory. */
1727 /* FIXME: This does not handle the (unlikely) case that the two locations
1728 are on the same Wine drive, but on different Unix file systems. */
1730 if ( attr & FILE_ATTRIBUTE_DIRECTORY )
1732 FILE_SetDosError();
1733 return FALSE;
1735 else
1737 if ( ! CopyFileW( fn1, fn2, !(flag & MOVEFILE_REPLACE_EXISTING) ))
1738 return FALSE;
1739 if ( ! DeleteFileW ( fn1 ) )
1740 return FALSE;
1743 if (is_executable( full_name1.long_name ) != is_executable( full_name2.long_name ))
1745 struct stat fstat;
1746 if (stat( full_name2.long_name, &fstat ) != -1)
1748 if (is_executable( full_name2.long_name ))
1749 /* set executable bit where read bit is set */
1750 fstat.st_mode |= (fstat.st_mode & 0444) >> 2;
1751 else
1752 fstat.st_mode &= ~0111;
1753 chmod( full_name2.long_name, fstat.st_mode );
1756 return TRUE;
1758 else /* fn2 == NULL means delete source */
1760 if (flag & MOVEFILE_DELAY_UNTIL_REBOOT)
1762 if (flag & MOVEFILE_COPY_ALLOWED) {
1763 WARN("Illegal flag\n");
1764 SetLastError( ERROR_GEN_FAILURE );
1765 return FALSE;
1768 return FILE_AddBootRenameEntry( fn1, NULL, flag );
1771 if (unlink( full_name1.long_name ) == -1)
1773 FILE_SetDosError();
1774 return FALSE;
1776 return TRUE; /* successfully deleted */
1780 /**************************************************************************
1781 * MoveFileExA (KERNEL32.@)
1783 BOOL WINAPI MoveFileExA( LPCSTR fn1, LPCSTR fn2, DWORD flag )
1785 UNICODE_STRING fn1W, fn2W;
1786 BOOL ret;
1788 if (!fn1)
1790 SetLastError(ERROR_INVALID_PARAMETER);
1791 return FALSE;
1794 RtlCreateUnicodeStringFromAsciiz(&fn1W, fn1);
1795 if (fn2) RtlCreateUnicodeStringFromAsciiz(&fn2W, fn2);
1796 else fn2W.Buffer = NULL;
1798 ret = MoveFileExW( fn1W.Buffer, fn2W.Buffer, flag );
1800 RtlFreeUnicodeString(&fn1W);
1801 RtlFreeUnicodeString(&fn2W);
1802 return ret;
1806 /**************************************************************************
1807 * MoveFileW (KERNEL32.@)
1809 * Move file or directory
1811 BOOL WINAPI MoveFileW( LPCWSTR fn1, LPCWSTR fn2 )
1813 return MoveFileExW( fn1, fn2, MOVEFILE_COPY_ALLOWED );
1817 /**************************************************************************
1818 * MoveFileA (KERNEL32.@)
1820 BOOL WINAPI MoveFileA( LPCSTR fn1, LPCSTR fn2 )
1822 return MoveFileExA( fn1, fn2, MOVEFILE_COPY_ALLOWED );
1826 /**************************************************************************
1827 * CopyFileW (KERNEL32.@)
1829 BOOL WINAPI CopyFileW( LPCWSTR source, LPCWSTR dest, BOOL fail_if_exists )
1831 HANDLE h1, h2;
1832 BY_HANDLE_FILE_INFORMATION info;
1833 DWORD count;
1834 BOOL ret = FALSE;
1835 char buffer[2048];
1837 if (!source || !dest)
1839 SetLastError(ERROR_INVALID_PARAMETER);
1840 return FALSE;
1843 TRACE("%s -> %s\n", debugstr_w(source), debugstr_w(dest));
1845 if ((h1 = CreateFileW(source, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
1846 NULL, OPEN_EXISTING, 0, 0)) == INVALID_HANDLE_VALUE)
1848 WARN("Unable to open source %s\n", debugstr_w(source));
1849 return FALSE;
1852 if (!GetFileInformationByHandle( h1, &info ))
1854 WARN("GetFileInformationByHandle returned error for %s\n", debugstr_w(source));
1855 CloseHandle( h1 );
1856 return FALSE;
1859 if ((h2 = CreateFileW( dest, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
1860 fail_if_exists ? CREATE_NEW : CREATE_ALWAYS,
1861 info.dwFileAttributes, h1 )) == INVALID_HANDLE_VALUE)
1863 WARN("Unable to open dest %s\n", debugstr_w(dest));
1864 CloseHandle( h1 );
1865 return FALSE;
1868 while (ReadFile( h1, buffer, sizeof(buffer), &count, NULL ) && count)
1870 char *p = buffer;
1871 while (count != 0)
1873 DWORD res;
1874 if (!WriteFile( h2, p, count, &res, NULL ) || !res) goto done;
1875 p += res;
1876 count -= res;
1879 ret = TRUE;
1880 done:
1881 CloseHandle( h1 );
1882 CloseHandle( h2 );
1883 return ret;
1887 /**************************************************************************
1888 * CopyFileA (KERNEL32.@)
1890 BOOL WINAPI CopyFileA( LPCSTR source, LPCSTR dest, BOOL fail_if_exists)
1892 UNICODE_STRING sourceW, destW;
1893 BOOL ret;
1895 if (!source || !dest)
1897 SetLastError(ERROR_INVALID_PARAMETER);
1898 return FALSE;
1901 RtlCreateUnicodeStringFromAsciiz(&sourceW, source);
1902 RtlCreateUnicodeStringFromAsciiz(&destW, dest);
1904 ret = CopyFileW(sourceW.Buffer, destW.Buffer, fail_if_exists);
1906 RtlFreeUnicodeString(&sourceW);
1907 RtlFreeUnicodeString(&destW);
1908 return ret;
1912 /**************************************************************************
1913 * CopyFileExW (KERNEL32.@)
1915 * This implementation ignores most of the extra parameters passed-in into
1916 * the "ex" version of the method and calls the CopyFile method.
1917 * It will have to be fixed eventually.
1919 BOOL WINAPI CopyFileExW(LPCWSTR sourceFilename, LPCWSTR destFilename,
1920 LPPROGRESS_ROUTINE progressRoutine, LPVOID appData,
1921 LPBOOL cancelFlagPointer, DWORD copyFlags)
1924 * Interpret the only flag that CopyFile can interpret.
1926 return CopyFileW(sourceFilename, destFilename, (copyFlags & COPY_FILE_FAIL_IF_EXISTS) != 0);
1930 /**************************************************************************
1931 * CopyFileExA (KERNEL32.@)
1933 BOOL WINAPI CopyFileExA(LPCSTR sourceFilename, LPCSTR destFilename,
1934 LPPROGRESS_ROUTINE progressRoutine, LPVOID appData,
1935 LPBOOL cancelFlagPointer, DWORD copyFlags)
1937 UNICODE_STRING sourceW, destW;
1938 BOOL ret;
1940 if (!sourceFilename || !destFilename)
1942 SetLastError(ERROR_INVALID_PARAMETER);
1943 return FALSE;
1946 RtlCreateUnicodeStringFromAsciiz(&sourceW, sourceFilename);
1947 RtlCreateUnicodeStringFromAsciiz(&destW, destFilename);
1949 ret = CopyFileExW(sourceW.Buffer, destW.Buffer, progressRoutine, appData,
1950 cancelFlagPointer, copyFlags);
1952 RtlFreeUnicodeString(&sourceW);
1953 RtlFreeUnicodeString(&destW);
1954 return ret;
1958 /***********************************************************************
1959 * SetFileTime (KERNEL32.@)
1961 BOOL WINAPI SetFileTime( HANDLE hFile,
1962 const FILETIME *lpCreationTime,
1963 const FILETIME *lpLastAccessTime,
1964 const FILETIME *lpLastWriteTime )
1966 BOOL ret;
1967 SERVER_START_REQ( set_file_time )
1969 req->handle = hFile;
1970 if (lpLastAccessTime)
1971 RtlTimeToSecondsSince1970( (PLARGE_INTEGER) lpLastAccessTime, (DWORD *)&req->access_time );
1972 else
1973 req->access_time = 0; /* FIXME */
1974 if (lpLastWriteTime)
1975 RtlTimeToSecondsSince1970( (PLARGE_INTEGER) lpLastWriteTime, (DWORD *)&req->write_time );
1976 else
1977 req->write_time = 0; /* FIXME */
1978 ret = !wine_server_call_err( req );
1980 SERVER_END_REQ;
1981 return ret;
1985 /**************************************************************************
1986 * GetFileAttributesExW (KERNEL32.@)
1988 BOOL WINAPI GetFileAttributesExW(
1989 LPCWSTR lpFileName, GET_FILEEX_INFO_LEVELS fInfoLevelId,
1990 LPVOID lpFileInformation)
1992 DOS_FULL_NAME full_name;
1993 BY_HANDLE_FILE_INFORMATION info;
1995 if (!lpFileName || !lpFileInformation)
1997 SetLastError(ERROR_INVALID_PARAMETER);
1998 return FALSE;
2001 if (fInfoLevelId == GetFileExInfoStandard) {
2002 LPWIN32_FILE_ATTRIBUTE_DATA lpFad =
2003 (LPWIN32_FILE_ATTRIBUTE_DATA) lpFileInformation;
2004 if (!DOSFS_GetFullName( lpFileName, TRUE, &full_name )) return FALSE;
2005 if (!FILE_Stat( full_name.long_name, &info, NULL )) return FALSE;
2007 lpFad->dwFileAttributes = info.dwFileAttributes;
2008 lpFad->ftCreationTime = info.ftCreationTime;
2009 lpFad->ftLastAccessTime = info.ftLastAccessTime;
2010 lpFad->ftLastWriteTime = info.ftLastWriteTime;
2011 lpFad->nFileSizeHigh = info.nFileSizeHigh;
2012 lpFad->nFileSizeLow = info.nFileSizeLow;
2014 else {
2015 FIXME("invalid info level %d!\n", fInfoLevelId);
2016 return FALSE;
2019 return TRUE;
2023 /**************************************************************************
2024 * GetFileAttributesExA (KERNEL32.@)
2026 BOOL WINAPI GetFileAttributesExA(
2027 LPCSTR filename, GET_FILEEX_INFO_LEVELS fInfoLevelId,
2028 LPVOID lpFileInformation)
2030 UNICODE_STRING filenameW;
2031 BOOL ret = FALSE;
2033 if (!filename || !lpFileInformation)
2035 SetLastError(ERROR_INVALID_PARAMETER);
2036 return FALSE;
2039 if (RtlCreateUnicodeStringFromAsciiz(&filenameW, filename))
2041 ret = GetFileAttributesExW(filenameW.Buffer, fInfoLevelId, lpFileInformation);
2042 RtlFreeUnicodeString(&filenameW);
2044 else
2045 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2046 return ret;