BSTR of length 0 is allowed.
[wine.git] / files / file.c
blobe3946436bf656a5183712bd4288af5a3d7b277c5
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 DWORD dosdev;
322 static const WCHAR bkslashes_with_question_markW[] = {'\\','\\','?','\\',0};
323 static const WCHAR bkslashes_with_dotW[] = {'\\','\\','.','\\',0};
324 static const WCHAR bkslashesW[] = {'\\','\\',0};
325 static const WCHAR coninW[] = {'C','O','N','I','N','$',0};
326 static const WCHAR conoutW[] = {'C','O','N','O','U','T','$',0};
328 if (!filename)
330 SetLastError( ERROR_INVALID_PARAMETER );
331 return INVALID_HANDLE_VALUE;
333 TRACE("%s %s%s%s%s%s%s%s attributes 0x%lx\n", debugstr_w(filename),
334 ((access & GENERIC_READ)==GENERIC_READ)?"GENERIC_READ ":"",
335 ((access & GENERIC_WRITE)==GENERIC_WRITE)?"GENERIC_WRITE ":"",
336 (!access)?"QUERY_ACCESS ":"",
337 ((sharing & FILE_SHARE_READ)==FILE_SHARE_READ)?"FILE_SHARE_READ ":"",
338 ((sharing & FILE_SHARE_WRITE)==FILE_SHARE_WRITE)?"FILE_SHARE_WRITE ":"",
339 ((sharing & FILE_SHARE_DELETE)==FILE_SHARE_DELETE)?"FILE_SHARE_DELETE ":"",
340 (creation ==CREATE_NEW)?"CREATE_NEW":
341 (creation ==CREATE_ALWAYS)?"CREATE_ALWAYS ":
342 (creation ==OPEN_EXISTING)?"OPEN_EXISTING ":
343 (creation ==OPEN_ALWAYS)?"OPEN_ALWAYS ":
344 (creation ==TRUNCATE_EXISTING)?"TRUNCATE_EXISTING ":"", attributes);
346 /* Open a console for CONIN$ or CONOUT$ */
347 if (!strcmpiW(filename, coninW) || !strcmpiW(filename, conoutW))
349 ret = OpenConsoleW(filename, access, (sa && sa->bInheritHandle), creation);
350 goto done;
353 /* If the name starts with '\\?\', ignore the first 4 chars. */
354 if (!strncmpW(filename, bkslashes_with_question_markW, 4))
356 static const WCHAR uncW[] = {'U','N','C','\\',0};
357 filename += 4;
358 if (!strncmpiW(filename, uncW, 4))
360 FIXME("UNC name (%s) not supported.\n", debugstr_w(filename) );
361 SetLastError( ERROR_PATH_NOT_FOUND );
362 return INVALID_HANDLE_VALUE;
366 if (!strncmpW(filename, bkslashes_with_dotW, 4))
368 static const WCHAR pipeW[] = {'P','I','P','E','\\',0};
369 if(!strncmpiW(filename + 4, pipeW, 5))
371 TRACE("Opening a pipe: %s\n", debugstr_w(filename));
372 ret = FILE_OpenPipe( filename, access, sa );
373 goto done;
375 else if (isalphaW(filename[4]) && filename[5] == ':' && filename[6] == '\0')
377 const char *device = DRIVE_GetDevice( toupperW(filename[4]) - 'A' );
378 if (device)
380 ret = FILE_CreateFile( device, access, sharing, sa, creation,
381 attributes, template, TRUE, DRIVE_FIXED );
383 else
385 SetLastError( ERROR_ACCESS_DENIED );
386 ret = INVALID_HANDLE_VALUE;
388 goto done;
390 else if ((dosdev = RtlIsDosDeviceName_U( filename + 4 )))
392 dosdev += MAKELONG( 0, 4*sizeof(WCHAR) ); /* adjust position to start of filename */
394 else
396 ret = VXD_Open( filename+4, access, sa );
397 goto done;
400 else dosdev = RtlIsDosDeviceName_U( filename );
402 if (dosdev)
404 static const WCHAR conW[] = {'C','O','N',0};
405 WCHAR dev[5];
407 memcpy( dev, filename + HIWORD(dosdev)/sizeof(WCHAR), LOWORD(dosdev) );
408 dev[LOWORD(dosdev)/sizeof(WCHAR)] = 0;
410 TRACE("opening device %s\n", debugstr_w(dev) );
412 if (!strcmpiW( dev, conW ))
414 switch (access & (GENERIC_READ|GENERIC_WRITE))
416 case GENERIC_READ:
417 ret = OpenConsoleW(coninW, access, (sa && sa->bInheritHandle), creation);
418 goto done;
419 case GENERIC_WRITE:
420 ret = OpenConsoleW(conoutW, access, (sa && sa->bInheritHandle), creation);
421 goto done;
422 default:
423 FIXME("can't open CON read/write\n");
424 SetLastError( ERROR_FILE_NOT_FOUND );
425 return INVALID_HANDLE_VALUE;
429 ret = VOLUME_OpenDevice( dev, access, sharing, sa, attributes );
430 goto done;
433 /* If the name still starts with '\\', it's a UNC name. */
434 if (!strncmpW(filename, bkslashesW, 2))
436 ret = SMB_CreateFileW(filename, access, sharing, sa, creation, attributes, template );
437 goto done;
440 /* If the name contains a DOS wild card (* or ?), do no create a file */
441 if(strchrW(filename, '*') || strchrW(filename, '?'))
443 SetLastError(ERROR_BAD_PATHNAME);
444 return INVALID_HANDLE_VALUE;
447 /* check for filename, don't check for last entry if creating */
448 if (!DOSFS_GetFullName( filename,
449 (creation == OPEN_EXISTING) ||
450 (creation == TRUNCATE_EXISTING),
451 &full_name )) {
452 WARN("Unable to get full filename from %s (GLE %ld)\n",
453 debugstr_w(filename), GetLastError());
454 return INVALID_HANDLE_VALUE;
457 ret = FILE_CreateFile( full_name.long_name, access, sharing,
458 sa, creation, attributes, template,
459 DRIVE_GetFlags(full_name.drive) & DRIVE_FAIL_READ_ONLY,
460 GetDriveTypeW( full_name.short_name ) );
461 done:
462 if (!ret) ret = INVALID_HANDLE_VALUE;
463 TRACE("returning %p\n", ret);
464 return ret;
469 /*************************************************************************
470 * CreateFileA (KERNEL32.@)
472 HANDLE WINAPI CreateFileA( LPCSTR filename, DWORD access, DWORD sharing,
473 LPSECURITY_ATTRIBUTES sa, DWORD creation,
474 DWORD attributes, HANDLE template)
476 UNICODE_STRING filenameW;
477 HANDLE ret = INVALID_HANDLE_VALUE;
479 if (!filename)
481 SetLastError( ERROR_INVALID_PARAMETER );
482 return INVALID_HANDLE_VALUE;
485 if (RtlCreateUnicodeStringFromAsciiz(&filenameW, filename))
487 ret = CreateFileW(filenameW.Buffer, access, sharing, sa, creation,
488 attributes, template);
489 RtlFreeUnicodeString(&filenameW);
491 else
492 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
493 return ret;
497 /***********************************************************************
498 * FILE_FillInfo
500 * Fill a file information from a struct stat.
502 static void FILE_FillInfo( struct stat *st, BY_HANDLE_FILE_INFORMATION *info )
504 if (S_ISDIR(st->st_mode))
505 info->dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
506 else
507 info->dwFileAttributes = FILE_ATTRIBUTE_ARCHIVE;
508 if (!(st->st_mode & S_IWUSR))
509 info->dwFileAttributes |= FILE_ATTRIBUTE_READONLY;
511 RtlSecondsSince1970ToTime( st->st_mtime, (LARGE_INTEGER *)&info->ftCreationTime );
512 RtlSecondsSince1970ToTime( st->st_mtime, (LARGE_INTEGER *)&info->ftLastWriteTime );
513 RtlSecondsSince1970ToTime( st->st_atime, (LARGE_INTEGER *)&info->ftLastAccessTime );
515 info->dwVolumeSerialNumber = 0; /* FIXME */
516 info->nFileSizeHigh = 0;
517 info->nFileSizeLow = 0;
518 if (!S_ISDIR(st->st_mode)) {
519 info->nFileSizeHigh = st->st_size >> 32;
520 info->nFileSizeLow = st->st_size & 0xffffffff;
522 info->nNumberOfLinks = st->st_nlink;
523 info->nFileIndexHigh = 0;
524 info->nFileIndexLow = st->st_ino;
528 /***********************************************************************
529 * get_show_dot_files_option
531 static BOOL get_show_dot_files_option(void)
533 static const WCHAR WineW[] = {'M','a','c','h','i','n','e','\\',
534 'S','o','f','t','w','a','r','e','\\',
535 'W','i','n','e','\\','W','i','n','e','\\',
536 'C','o','n','f','i','g','\\','W','i','n','e',0};
537 static const WCHAR ShowDotFilesW[] = {'S','h','o','w','D','o','t','F','i','l','e','s',0};
539 char tmp[80];
540 HKEY hkey;
541 DWORD dummy;
542 OBJECT_ATTRIBUTES attr;
543 UNICODE_STRING nameW;
544 BOOL ret = FALSE;
546 attr.Length = sizeof(attr);
547 attr.RootDirectory = 0;
548 attr.ObjectName = &nameW;
549 attr.Attributes = 0;
550 attr.SecurityDescriptor = NULL;
551 attr.SecurityQualityOfService = NULL;
552 RtlInitUnicodeString( &nameW, WineW );
554 if (!NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr ))
556 RtlInitUnicodeString( &nameW, ShowDotFilesW );
557 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
559 WCHAR *str = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
560 ret = IS_OPTION_TRUE( str[0] );
562 NtClose( hkey );
564 return ret;
568 /***********************************************************************
569 * FILE_Stat
571 * Stat a Unix path name. Return TRUE if OK.
573 BOOL FILE_Stat( LPCSTR unixName, BY_HANDLE_FILE_INFORMATION *info, BOOL *is_symlink_ptr )
575 struct stat st;
576 int is_symlink;
577 LPCSTR p;
579 if (lstat( unixName, &st ) == -1)
581 FILE_SetDosError();
582 return FALSE;
584 is_symlink = S_ISLNK(st.st_mode);
585 if (is_symlink)
587 /* do a "real" stat to find out
588 about the type of the symlink destination */
589 if (stat( unixName, &st ) == -1)
591 FILE_SetDosError();
592 return FALSE;
596 /* fill in the information we gathered so far */
597 FILE_FillInfo( &st, info );
599 /* and now see if this is a hidden file, based on the name */
600 p = strrchr( unixName, '/');
601 p = p ? p + 1 : unixName;
602 if (*p == '.' && *(p+1) && (*(p+1) != '.' || *(p+2)))
604 static int show_dot_files = -1;
605 if (show_dot_files == -1)
606 show_dot_files = get_show_dot_files_option();
607 if (!show_dot_files)
608 info->dwFileAttributes |= FILE_ATTRIBUTE_HIDDEN;
610 if (is_symlink_ptr) *is_symlink_ptr = is_symlink;
611 return TRUE;
615 /***********************************************************************
616 * GetFileInformationByHandle (KERNEL32.@)
618 DWORD WINAPI GetFileInformationByHandle( HANDLE hFile,
619 BY_HANDLE_FILE_INFORMATION *info )
621 DWORD ret;
622 if (!info) return 0;
624 TRACE("%p\n", hFile);
626 SERVER_START_REQ( get_file_info )
628 req->handle = hFile;
629 if ((ret = !wine_server_call_err( req )))
631 /* FIXME: which file types are supported ?
632 * Serial ports (FILE_TYPE_CHAR) are not,
633 * and MSDN also says that pipes are not supported.
634 * FILE_TYPE_REMOTE seems to be supported according to
635 * MSDN q234741.txt */
636 if ((reply->type == FILE_TYPE_DISK) || (reply->type == FILE_TYPE_REMOTE))
638 RtlSecondsSince1970ToTime( reply->write_time, (LARGE_INTEGER *)&info->ftCreationTime );
639 RtlSecondsSince1970ToTime( reply->write_time, (LARGE_INTEGER *)&info->ftLastWriteTime );
640 RtlSecondsSince1970ToTime( reply->access_time, (LARGE_INTEGER *)&info->ftLastAccessTime );
641 info->dwFileAttributes = reply->attr;
642 info->dwVolumeSerialNumber = reply->serial;
643 info->nFileSizeHigh = reply->size_high;
644 info->nFileSizeLow = reply->size_low;
645 info->nNumberOfLinks = reply->links;
646 info->nFileIndexHigh = reply->index_high;
647 info->nFileIndexLow = reply->index_low;
649 else
651 SetLastError(ERROR_NOT_SUPPORTED);
652 ret = 0;
656 SERVER_END_REQ;
657 return ret;
661 /**************************************************************************
662 * GetFileAttributesW (KERNEL32.@)
664 DWORD WINAPI GetFileAttributesW( LPCWSTR name )
666 DOS_FULL_NAME full_name;
667 BY_HANDLE_FILE_INFORMATION info;
669 if (name == NULL)
671 SetLastError( ERROR_INVALID_PARAMETER );
672 return INVALID_FILE_ATTRIBUTES;
674 if (!DOSFS_GetFullName( name, TRUE, &full_name) )
675 return INVALID_FILE_ATTRIBUTES;
676 if (!FILE_Stat( full_name.long_name, &info, NULL ))
677 return INVALID_FILE_ATTRIBUTES;
678 return info.dwFileAttributes;
682 /**************************************************************************
683 * GetFileAttributesA (KERNEL32.@)
685 DWORD WINAPI GetFileAttributesA( LPCSTR name )
687 UNICODE_STRING nameW;
688 DWORD ret = INVALID_FILE_ATTRIBUTES;
690 if (!name)
692 SetLastError( ERROR_INVALID_PARAMETER );
693 return INVALID_FILE_ATTRIBUTES;
696 if (RtlCreateUnicodeStringFromAsciiz(&nameW, name))
698 ret = GetFileAttributesW(nameW.Buffer);
699 RtlFreeUnicodeString(&nameW);
701 else
702 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
703 return ret;
707 /**************************************************************************
708 * SetFileAttributesW (KERNEL32.@)
710 BOOL WINAPI SetFileAttributesW(LPCWSTR lpFileName, DWORD attributes)
712 struct stat buf;
713 DOS_FULL_NAME full_name;
715 if (!lpFileName)
717 SetLastError( ERROR_INVALID_PARAMETER );
718 return FALSE;
721 TRACE("(%s,%lx)\n", debugstr_w(lpFileName), attributes);
723 if (!DOSFS_GetFullName( lpFileName, TRUE, &full_name ))
724 return FALSE;
726 if(stat(full_name.long_name,&buf)==-1)
728 FILE_SetDosError();
729 return FALSE;
731 if (attributes & FILE_ATTRIBUTE_READONLY)
733 if(S_ISDIR(buf.st_mode))
734 /* FIXME */
735 WARN("FILE_ATTRIBUTE_READONLY ignored for directory.\n");
736 else
737 buf.st_mode &= ~0222; /* octal!, clear write permission bits */
738 attributes &= ~FILE_ATTRIBUTE_READONLY;
740 else
742 /* add write permission */
743 buf.st_mode |= (0600 | ((buf.st_mode & 044) >> 1)) & (~FILE_umask);
745 if (attributes & FILE_ATTRIBUTE_DIRECTORY)
747 if (!S_ISDIR(buf.st_mode))
748 FIXME("SetFileAttributes expected the file %s to be a directory\n",
749 debugstr_w(lpFileName));
750 attributes &= ~FILE_ATTRIBUTE_DIRECTORY;
752 attributes &= ~(FILE_ATTRIBUTE_NORMAL|FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN|
753 FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_TEMPORARY);
754 if (attributes)
755 FIXME("(%s):%lx attribute(s) not implemented.\n", debugstr_w(lpFileName), attributes);
756 if (-1==chmod(full_name.long_name,buf.st_mode))
758 if (GetDriveTypeW(lpFileName) == DRIVE_CDROM)
760 SetLastError( ERROR_ACCESS_DENIED );
761 return FALSE;
765 * FIXME: We don't return FALSE here because of differences between
766 * Linux and Windows privileges. Under Linux only the owner of
767 * the file is allowed to change file attributes. Under Windows,
768 * applications expect that if you can write to a file, you can also
769 * change its attributes (see GENERIC_WRITE). We could try to be
770 * clever here but that would break multi-user installations where
771 * users share read-only DLLs. This is because some installers like
772 * to change attributes of already installed DLLs.
774 FIXME("Couldn't set file attributes for existing file \"%s\".\n"
775 "Check permissions or set VFAT \"quiet\" mount flag\n", full_name.long_name);
777 return TRUE;
781 /**************************************************************************
782 * SetFileAttributesA (KERNEL32.@)
784 BOOL WINAPI SetFileAttributesA(LPCSTR lpFileName, DWORD attributes)
786 UNICODE_STRING filenameW;
787 BOOL ret = FALSE;
789 if (!lpFileName)
791 SetLastError( ERROR_INVALID_PARAMETER );
792 return FALSE;
795 if (RtlCreateUnicodeStringFromAsciiz(&filenameW, lpFileName))
797 ret = SetFileAttributesW(filenameW.Buffer, attributes);
798 RtlFreeUnicodeString(&filenameW);
800 else
801 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
802 return ret;
806 /******************************************************************************
807 * GetCompressedFileSizeA [KERNEL32.@]
809 DWORD WINAPI GetCompressedFileSizeA(
810 LPCSTR lpFileName,
811 LPDWORD lpFileSizeHigh)
813 UNICODE_STRING filenameW;
814 DWORD ret;
816 if (RtlCreateUnicodeStringFromAsciiz(&filenameW, lpFileName))
818 ret = GetCompressedFileSizeW(filenameW.Buffer, lpFileSizeHigh);
819 RtlFreeUnicodeString(&filenameW);
821 else
823 ret = INVALID_FILE_SIZE;
824 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
826 return ret;
830 /******************************************************************************
831 * GetCompressedFileSizeW [KERNEL32.@]
833 * RETURNS
834 * Success: Low-order doubleword of number of bytes
835 * Failure: INVALID_FILE_SIZE
837 DWORD WINAPI GetCompressedFileSizeW(
838 LPCWSTR lpFileName, /* [in] Pointer to name of file */
839 LPDWORD lpFileSizeHigh) /* [out] Receives high-order doubleword of size */
841 DOS_FULL_NAME full_name;
842 struct stat st;
843 DWORD low;
845 TRACE("(%s,%p)\n",debugstr_w(lpFileName),lpFileSizeHigh);
847 if (!DOSFS_GetFullName( lpFileName, TRUE, &full_name )) return INVALID_FILE_SIZE;
848 if (stat(full_name.long_name, &st) != 0)
850 FILE_SetDosError();
851 return INVALID_FILE_SIZE;
853 #if HAVE_STRUCT_STAT_ST_BLOCKS
854 /* blocks are 512 bytes long */
855 if (lpFileSizeHigh) *lpFileSizeHigh = (st.st_blocks >> 23);
856 low = (DWORD)(st.st_blocks << 9);
857 #else
858 if (lpFileSizeHigh) *lpFileSizeHigh = (st.st_size >> 32);
859 low = (DWORD)st.st_size;
860 #endif
861 return low;
865 /***********************************************************************
866 * GetFileTime (KERNEL32.@)
868 BOOL WINAPI GetFileTime( HANDLE hFile, FILETIME *lpCreationTime,
869 FILETIME *lpLastAccessTime,
870 FILETIME *lpLastWriteTime )
872 BY_HANDLE_FILE_INFORMATION info;
873 if (!GetFileInformationByHandle( hFile, &info )) return FALSE;
874 if (lpCreationTime) *lpCreationTime = info.ftCreationTime;
875 if (lpLastAccessTime) *lpLastAccessTime = info.ftLastAccessTime;
876 if (lpLastWriteTime) *lpLastWriteTime = info.ftLastWriteTime;
877 return TRUE;
881 /***********************************************************************
882 * GetTempFileNameA (KERNEL32.@)
884 UINT WINAPI GetTempFileNameA( LPCSTR path, LPCSTR prefix, UINT unique,
885 LPSTR buffer)
887 UNICODE_STRING pathW, prefixW;
888 WCHAR bufferW[MAX_PATH];
889 UINT ret;
891 if ( !path || !prefix || !buffer )
893 SetLastError( ERROR_INVALID_PARAMETER );
894 return 0;
897 RtlCreateUnicodeStringFromAsciiz(&pathW, path);
898 RtlCreateUnicodeStringFromAsciiz(&prefixW, prefix);
900 ret = GetTempFileNameW(pathW.Buffer, prefixW.Buffer, unique, bufferW);
901 if (ret)
902 WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buffer, MAX_PATH, NULL, NULL);
904 RtlFreeUnicodeString(&pathW);
905 RtlFreeUnicodeString(&prefixW);
906 return ret;
909 /***********************************************************************
910 * GetTempFileNameW (KERNEL32.@)
912 UINT WINAPI GetTempFileNameW( LPCWSTR path, LPCWSTR prefix, UINT unique,
913 LPWSTR buffer )
915 static const WCHAR formatW[] = {'%','x','.','t','m','p',0};
917 DOS_FULL_NAME full_name;
918 int i;
919 LPWSTR p;
921 if ( !path || !prefix || !buffer )
923 SetLastError( ERROR_INVALID_PARAMETER );
924 return 0;
927 strcpyW( buffer, path );
928 p = buffer + strlenW(buffer);
930 /* add a \, if there isn't one */
931 if ((p == buffer) || (p[-1] != '\\')) *p++ = '\\';
933 for (i = 3; (i > 0) && (*prefix); i--) *p++ = *prefix++;
935 unique &= 0xffff;
937 if (unique) sprintfW( p, formatW, unique );
938 else
940 /* get a "random" unique number and try to create the file */
941 HANDLE handle;
942 UINT num = GetTickCount() & 0xffff;
944 if (!num) num = 1;
945 unique = num;
948 sprintfW( p, formatW, unique );
949 handle = CreateFileW( buffer, GENERIC_WRITE, 0, NULL,
950 CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0 );
951 if (handle != INVALID_HANDLE_VALUE)
952 { /* We created it */
953 TRACE("created %s\n", debugstr_w(buffer) );
954 CloseHandle( handle );
955 break;
957 if (GetLastError() != ERROR_FILE_EXISTS &&
958 GetLastError() != ERROR_SHARING_VIOLATION)
959 break; /* No need to go on */
960 if (!(++unique & 0xffff)) unique = 1;
961 } while (unique != num);
964 /* Get the full path name */
966 if (DOSFS_GetFullName( buffer, FALSE, &full_name ))
968 char *slash;
969 /* Check if we have write access in the directory */
970 if ((slash = strrchr( full_name.long_name, '/' ))) *slash = '\0';
971 if (access( full_name.long_name, W_OK ) == -1)
972 WARN("returns %s, which doesn't seem to be writeable.\n",
973 debugstr_w(buffer) );
975 TRACE("returning %s\n", debugstr_w(buffer) );
976 return unique;
980 /***********************************************************************
981 * FILE_DoOpenFile
983 * Implementation of OpenFile16() and OpenFile32().
985 static HFILE FILE_DoOpenFile( LPCSTR name, OFSTRUCT *ofs, UINT mode, BOOL win32 )
987 HFILE hFileRet;
988 HANDLE handle;
989 FILETIME filetime;
990 WORD filedatetime[2];
991 DOS_FULL_NAME full_name;
992 DWORD access, sharing;
993 WCHAR *p;
994 WCHAR buffer[MAX_PATH];
995 LPWSTR nameW;
997 if (!ofs) return HFILE_ERROR;
999 TRACE("%s %s %s %s%s%s%s%s%s%s%s%s\n",name,
1000 ((mode & 0x3 )==OF_READ)?"OF_READ":
1001 ((mode & 0x3 )==OF_WRITE)?"OF_WRITE":
1002 ((mode & 0x3 )==OF_READWRITE)?"OF_READWRITE":"unknown",
1003 ((mode & 0x70 )==OF_SHARE_COMPAT)?"OF_SHARE_COMPAT":
1004 ((mode & 0x70 )==OF_SHARE_DENY_NONE)?"OF_SHARE_DENY_NONE":
1005 ((mode & 0x70 )==OF_SHARE_DENY_READ)?"OF_SHARE_DENY_READ":
1006 ((mode & 0x70 )==OF_SHARE_DENY_WRITE)?"OF_SHARE_DENY_WRITE":
1007 ((mode & 0x70 )==OF_SHARE_EXCLUSIVE)?"OF_SHARE_EXCLUSIVE":"unknown",
1008 ((mode & OF_PARSE )==OF_PARSE)?"OF_PARSE ":"",
1009 ((mode & OF_DELETE )==OF_DELETE)?"OF_DELETE ":"",
1010 ((mode & OF_VERIFY )==OF_VERIFY)?"OF_VERIFY ":"",
1011 ((mode & OF_SEARCH )==OF_SEARCH)?"OF_SEARCH ":"",
1012 ((mode & OF_CANCEL )==OF_CANCEL)?"OF_CANCEL ":"",
1013 ((mode & OF_CREATE )==OF_CREATE)?"OF_CREATE ":"",
1014 ((mode & OF_PROMPT )==OF_PROMPT)?"OF_PROMPT ":"",
1015 ((mode & OF_EXIST )==OF_EXIST)?"OF_EXIST ":"",
1016 ((mode & OF_REOPEN )==OF_REOPEN)?"OF_REOPEN ":""
1020 ofs->cBytes = sizeof(OFSTRUCT);
1021 ofs->nErrCode = 0;
1022 if (mode & OF_REOPEN) name = ofs->szPathName;
1024 if (!name) {
1025 ERR("called with `name' set to NULL ! Please debug.\n");
1026 return HFILE_ERROR;
1029 TRACE("%s %04x\n", name, mode );
1031 /* the watcom 10.6 IDE relies on a valid path returned in ofs->szPathName
1032 Are there any cases where getting the path here is wrong?
1033 Uwe Bonnes 1997 Apr 2 */
1034 if (!GetFullPathNameA( name, sizeof(ofs->szPathName),
1035 ofs->szPathName, NULL )) goto error;
1036 FILE_ConvertOFMode( mode, &access, &sharing );
1038 /* OF_PARSE simply fills the structure */
1040 if (mode & OF_PARSE)
1042 ofs->fFixedDisk = (GetDriveType16( ofs->szPathName[0]-'A' )
1043 != DRIVE_REMOVABLE);
1044 TRACE("(%s): OF_PARSE, res = '%s'\n",
1045 name, ofs->szPathName );
1046 return 0;
1049 /* OF_CREATE is completely different from all other options, so
1050 handle it first */
1052 if (mode & OF_CREATE)
1054 if ((handle = CreateFileA( name, GENERIC_READ | GENERIC_WRITE,
1055 sharing, NULL, CREATE_ALWAYS,
1056 FILE_ATTRIBUTE_NORMAL, 0 ))== INVALID_HANDLE_VALUE)
1057 goto error;
1058 goto success;
1061 MultiByteToWideChar(CP_ACP, 0, name, -1, buffer, MAX_PATH);
1062 nameW = buffer;
1064 /* If OF_SEARCH is set, ignore the given path */
1066 if ((mode & OF_SEARCH) && !(mode & OF_REOPEN))
1068 /* First try the file name as is */
1069 if (DOSFS_GetFullName( nameW, TRUE, &full_name )) goto found;
1070 /* Now remove the path */
1071 if (nameW[0] && (nameW[1] == ':')) nameW += 2;
1072 if ((p = strrchrW( nameW, '\\' ))) nameW = p + 1;
1073 if ((p = strrchrW( nameW, '/' ))) nameW = p + 1;
1074 if (!nameW[0]) goto not_found;
1077 /* Now look for the file */
1079 if (!DIR_SearchPath( NULL, nameW, NULL, &full_name, win32 )) goto not_found;
1081 found:
1082 TRACE("found %s = %s\n",
1083 full_name.long_name, debugstr_w(full_name.short_name) );
1084 WideCharToMultiByte(CP_ACP, 0, full_name.short_name, -1,
1085 ofs->szPathName, sizeof(ofs->szPathName), NULL, NULL);
1087 if (mode & OF_DELETE)
1089 handle = FILE_CreateFile( full_name.long_name, GENERIC_READ|GENERIC_WRITE, 0,
1090 NULL, OPEN_EXISTING, 0, 0, TRUE,
1091 GetDriveTypeW( full_name.short_name ) );
1092 if (!handle) goto error;
1093 CloseHandle( handle );
1094 if (unlink( full_name.long_name ) == -1) goto not_found;
1095 TRACE("(%s): OF_DELETE return = OK\n", name);
1096 return 1;
1099 handle = FILE_CreateFile( full_name.long_name, access, sharing,
1100 NULL, OPEN_EXISTING, 0, 0,
1101 DRIVE_GetFlags(full_name.drive) & DRIVE_FAIL_READ_ONLY,
1102 GetDriveTypeW( full_name.short_name ) );
1103 if (!handle) goto not_found;
1105 GetFileTime( handle, NULL, NULL, &filetime );
1106 FileTimeToDosDateTime( &filetime, &filedatetime[0], &filedatetime[1] );
1107 if ((mode & OF_VERIFY) && (mode & OF_REOPEN))
1109 if (ofs->Reserved1 != filedatetime[0] || ofs->Reserved2 != filedatetime[1] )
1111 CloseHandle( handle );
1112 WARN("(%s): OF_VERIFY failed\n", name );
1113 /* FIXME: what error here? */
1114 SetLastError( ERROR_FILE_NOT_FOUND );
1115 goto error;
1118 ofs->Reserved1 = filedatetime[0];
1119 ofs->Reserved2 = filedatetime[1];
1121 success: /* We get here if the open was successful */
1122 TRACE("(%s): OK, return = %p\n", name, handle );
1123 if (win32)
1125 if (mode & OF_EXIST) /* Return TRUE instead of a handle */
1127 CloseHandle( handle );
1128 hFileRet = TRUE;
1130 else hFileRet = (HFILE)handle;
1132 else
1134 hFileRet = Win32HandleToDosFileHandle( handle );
1135 if (hFileRet == HFILE_ERROR16) goto error;
1136 if (mode & OF_EXIST) /* Return the handle, but close it first */
1137 _lclose16( hFileRet );
1139 return hFileRet;
1141 not_found: /* We get here if the file does not exist */
1142 WARN("'%s' not found or sharing violation\n", name );
1143 SetLastError( ERROR_FILE_NOT_FOUND );
1144 /* fall through */
1146 error: /* We get here if there was an error opening the file */
1147 ofs->nErrCode = GetLastError();
1148 WARN("(%s): return = HFILE_ERROR error= %d\n",
1149 name,ofs->nErrCode );
1150 return HFILE_ERROR;
1154 /***********************************************************************
1155 * OpenFile (KERNEL.74)
1156 * OpenFileEx (KERNEL.360)
1158 HFILE16 WINAPI OpenFile16( LPCSTR name, OFSTRUCT *ofs, UINT16 mode )
1160 return FILE_DoOpenFile( name, ofs, mode, FALSE );
1164 /***********************************************************************
1165 * OpenFile (KERNEL32.@)
1167 HFILE WINAPI OpenFile( LPCSTR name, OFSTRUCT *ofs, UINT mode )
1169 return FILE_DoOpenFile( name, ofs, mode, TRUE );
1173 /******************************************************************
1174 * FILE_ReadWriteApc (internal)
1178 static void WINAPI FILE_ReadWriteApc(void* apc_user, PIO_STATUS_BLOCK io_status, ULONG len)
1180 LPOVERLAPPED_COMPLETION_ROUTINE cr = (LPOVERLAPPED_COMPLETION_ROUTINE)apc_user;
1182 cr(RtlNtStatusToDosError(io_status->u.Status), len, (LPOVERLAPPED)io_status);
1185 /***********************************************************************
1186 * ReadFileEx (KERNEL32.@)
1188 BOOL WINAPI ReadFileEx(HANDLE hFile, LPVOID buffer, DWORD bytesToRead,
1189 LPOVERLAPPED overlapped,
1190 LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
1192 LARGE_INTEGER offset;
1193 NTSTATUS status;
1194 PIO_STATUS_BLOCK io_status;
1196 if (!overlapped)
1198 SetLastError(ERROR_INVALID_PARAMETER);
1199 return FALSE;
1202 offset.u.LowPart = overlapped->Offset;
1203 offset.u.HighPart = overlapped->OffsetHigh;
1204 io_status = (PIO_STATUS_BLOCK)overlapped;
1205 io_status->u.Status = STATUS_PENDING;
1207 status = NtReadFile(hFile, NULL, FILE_ReadWriteApc, lpCompletionRoutine,
1208 io_status, buffer, bytesToRead, &offset, NULL);
1210 if (status)
1212 SetLastError( RtlNtStatusToDosError(status) );
1213 return FALSE;
1215 return TRUE;
1218 /***********************************************************************
1219 * ReadFile (KERNEL32.@)
1221 BOOL WINAPI ReadFile( HANDLE hFile, LPVOID buffer, DWORD bytesToRead,
1222 LPDWORD bytesRead, LPOVERLAPPED overlapped )
1224 LARGE_INTEGER offset;
1225 PLARGE_INTEGER poffset = NULL;
1226 IO_STATUS_BLOCK iosb;
1227 PIO_STATUS_BLOCK io_status = &iosb;
1228 HANDLE hEvent = 0;
1229 NTSTATUS status;
1231 TRACE("%p %p %ld %p %p\n", hFile, buffer, bytesToRead,
1232 bytesRead, overlapped );
1234 if (bytesRead) *bytesRead = 0; /* Do this before anything else */
1235 if (!bytesToRead) return TRUE;
1237 if (IsBadReadPtr(buffer, bytesToRead))
1239 SetLastError(ERROR_WRITE_FAULT); /* FIXME */
1240 return FALSE;
1242 if (is_console_handle(hFile))
1243 return ReadConsoleA(hFile, buffer, bytesToRead, bytesRead, NULL);
1245 if (overlapped != NULL)
1247 offset.u.LowPart = overlapped->Offset;
1248 offset.u.HighPart = overlapped->OffsetHigh;
1249 poffset = &offset;
1250 hEvent = overlapped->hEvent;
1251 io_status = (PIO_STATUS_BLOCK)overlapped;
1253 io_status->u.Status = STATUS_PENDING;
1254 io_status->Information = 0;
1256 status = NtReadFile(hFile, hEvent, NULL, NULL, io_status, buffer, bytesToRead, poffset, NULL);
1258 if (status != STATUS_PENDING && bytesRead)
1259 *bytesRead = io_status->Information;
1261 if (status && status != STATUS_END_OF_FILE)
1263 SetLastError( RtlNtStatusToDosError(status) );
1264 return FALSE;
1266 return TRUE;
1270 /***********************************************************************
1271 * WriteFileEx (KERNEL32.@)
1273 BOOL WINAPI WriteFileEx(HANDLE hFile, LPCVOID buffer, DWORD bytesToWrite,
1274 LPOVERLAPPED overlapped,
1275 LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
1277 LARGE_INTEGER offset;
1278 NTSTATUS status;
1279 PIO_STATUS_BLOCK io_status;
1281 TRACE("%p %p %ld %p %p\n",
1282 hFile, buffer, bytesToWrite, overlapped, lpCompletionRoutine);
1284 if (overlapped == NULL)
1286 SetLastError(ERROR_INVALID_PARAMETER);
1287 return FALSE;
1289 offset.u.LowPart = overlapped->Offset;
1290 offset.u.HighPart = overlapped->OffsetHigh;
1292 io_status = (PIO_STATUS_BLOCK)overlapped;
1293 io_status->u.Status = STATUS_PENDING;
1295 status = NtWriteFile(hFile, NULL, FILE_ReadWriteApc, lpCompletionRoutine,
1296 io_status, buffer, bytesToWrite, &offset, NULL);
1298 if (status) SetLastError( RtlNtStatusToDosError(status) );
1299 return !status;
1302 /***********************************************************************
1303 * WriteFile (KERNEL32.@)
1305 BOOL WINAPI WriteFile( HANDLE hFile, LPCVOID buffer, DWORD bytesToWrite,
1306 LPDWORD bytesWritten, LPOVERLAPPED overlapped )
1308 HANDLE hEvent = NULL;
1309 LARGE_INTEGER offset;
1310 PLARGE_INTEGER poffset = NULL;
1311 NTSTATUS status;
1312 IO_STATUS_BLOCK iosb;
1313 PIO_STATUS_BLOCK piosb = &iosb;
1315 TRACE("%p %p %ld %p %p\n",
1316 hFile, buffer, bytesToWrite, bytesWritten, overlapped );
1318 if (is_console_handle(hFile))
1319 return WriteConsoleA(hFile, buffer, bytesToWrite, bytesWritten, NULL);
1321 if (IsBadReadPtr(buffer, bytesToWrite))
1323 SetLastError(ERROR_READ_FAULT); /* FIXME */
1324 return FALSE;
1327 if (overlapped)
1329 offset.u.LowPart = overlapped->Offset;
1330 offset.u.HighPart = overlapped->OffsetHigh;
1331 poffset = &offset;
1332 hEvent = overlapped->hEvent;
1333 piosb = (PIO_STATUS_BLOCK)overlapped;
1335 piosb->u.Status = STATUS_PENDING;
1336 piosb->Information = 0;
1338 status = NtWriteFile(hFile, hEvent, NULL, NULL, piosb,
1339 buffer, bytesToWrite, poffset, NULL);
1340 if (status)
1342 SetLastError( RtlNtStatusToDosError(status) );
1343 return FALSE;
1345 if (bytesWritten) *bytesWritten = piosb->Information;
1347 return TRUE;
1351 /***********************************************************************
1352 * SetFilePointer (KERNEL32.@)
1354 DWORD WINAPI SetFilePointer( HANDLE hFile, LONG distance, LONG *highword,
1355 DWORD method )
1357 DWORD ret = INVALID_SET_FILE_POINTER;
1359 TRACE("handle %p offset %ld high %ld origin %ld\n",
1360 hFile, distance, highword?*highword:0, method );
1362 SERVER_START_REQ( set_file_pointer )
1364 req->handle = hFile;
1365 req->low = distance;
1366 req->high = highword ? *highword : (distance >= 0) ? 0 : -1;
1367 /* FIXME: assumes 1:1 mapping between Windows and Unix seek constants */
1368 req->whence = method;
1369 SetLastError( 0 );
1370 if (!wine_server_call_err( req ))
1372 ret = reply->new_low;
1373 if (highword) *highword = reply->new_high;
1376 SERVER_END_REQ;
1377 return ret;
1381 /*************************************************************************
1382 * SetHandleCount (KERNEL32.@)
1384 UINT WINAPI SetHandleCount( UINT count )
1386 return min( 256, count );
1390 /**************************************************************************
1391 * SetEndOfFile (KERNEL32.@)
1393 BOOL WINAPI SetEndOfFile( HANDLE hFile )
1395 BOOL ret;
1396 SERVER_START_REQ( truncate_file )
1398 req->handle = hFile;
1399 ret = !wine_server_call_err( req );
1401 SERVER_END_REQ;
1402 return ret;
1406 /***********************************************************************
1407 * DeleteFileW (KERNEL32.@)
1409 BOOL WINAPI DeleteFileW( LPCWSTR path )
1411 DOS_FULL_NAME full_name;
1412 HANDLE hFile;
1414 TRACE("%s\n", debugstr_w(path) );
1415 if (!path || !*path)
1417 SetLastError(ERROR_PATH_NOT_FOUND);
1418 return FALSE;
1420 if (RtlIsDosDeviceName_U( path ))
1422 WARN("cannot remove DOS device %s!\n", debugstr_w(path));
1423 SetLastError( ERROR_FILE_NOT_FOUND );
1424 return FALSE;
1427 if (!DOSFS_GetFullName( path, TRUE, &full_name )) return FALSE;
1429 /* check if we are allowed to delete the source */
1430 hFile = FILE_CreateFile( full_name.long_name, GENERIC_READ|GENERIC_WRITE, 0,
1431 NULL, OPEN_EXISTING, 0, 0, TRUE,
1432 GetDriveTypeW( full_name.short_name ) );
1433 if (!hFile) return FALSE;
1435 if (unlink( full_name.long_name ) == -1)
1437 FILE_SetDosError();
1438 CloseHandle(hFile);
1439 return FALSE;
1441 CloseHandle(hFile);
1442 return TRUE;
1446 /***********************************************************************
1447 * DeleteFileA (KERNEL32.@)
1449 BOOL WINAPI DeleteFileA( LPCSTR path )
1451 UNICODE_STRING pathW;
1452 BOOL ret = FALSE;
1454 if (!path)
1456 SetLastError(ERROR_INVALID_PARAMETER);
1457 return FALSE;
1460 if (RtlCreateUnicodeStringFromAsciiz(&pathW, path))
1462 ret = DeleteFileW(pathW.Buffer);
1463 RtlFreeUnicodeString(&pathW);
1465 else
1466 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1467 return ret;
1471 /***********************************************************************
1472 * GetFileType (KERNEL32.@)
1474 DWORD WINAPI GetFileType( HANDLE hFile )
1476 DWORD ret = FILE_TYPE_UNKNOWN;
1478 if (is_console_handle( hFile ))
1479 return FILE_TYPE_CHAR;
1481 SERVER_START_REQ( get_file_info )
1483 req->handle = hFile;
1484 if (!wine_server_call_err( req )) ret = reply->type;
1486 SERVER_END_REQ;
1487 return ret;
1491 /* check if a file name is for an executable file (.exe or .com) */
1492 inline static BOOL is_executable( const char *name )
1494 int len = strlen(name);
1496 if (len < 4) return FALSE;
1497 return (!strcasecmp( name + len - 4, ".exe" ) ||
1498 !strcasecmp( name + len - 4, ".com" ));
1502 /***********************************************************************
1503 * FILE_AddBootRenameEntry
1505 * Adds an entry to the registry that is loaded when windows boots and
1506 * checks if there are some files to be removed or renamed/moved.
1507 * <fn1> has to be valid and <fn2> may be NULL. If both pointers are
1508 * non-NULL then the file is moved, otherwise it is deleted. The
1509 * entry of the registrykey is always appended with two zero
1510 * terminated strings. If <fn2> is NULL then the second entry is
1511 * simply a single 0-byte. Otherwise the second filename goes
1512 * there. The entries are prepended with \??\ before the path and the
1513 * second filename gets also a '!' as the first character if
1514 * MOVEFILE_REPLACE_EXISTING is set. After the final string another
1515 * 0-byte follows to indicate the end of the strings.
1516 * i.e.:
1517 * \??\D:\test\file1[0]
1518 * !\??\D:\test\file1_renamed[0]
1519 * \??\D:\Test|delete[0]
1520 * [0] <- file is to be deleted, second string empty
1521 * \??\D:\test\file2[0]
1522 * !\??\D:\test\file2_renamed[0]
1523 * [0] <- indicates end of strings
1525 * or:
1526 * \??\D:\test\file1[0]
1527 * !\??\D:\test\file1_renamed[0]
1528 * \??\D:\Test|delete[0]
1529 * [0] <- file is to be deleted, second string empty
1530 * [0] <- indicates end of strings
1533 static BOOL FILE_AddBootRenameEntry( LPCWSTR fn1, LPCWSTR fn2, DWORD flags )
1535 static const WCHAR PreString[] = {'\\','?','?','\\',0};
1536 static const WCHAR ValueName[] = {'P','e','n','d','i','n','g',
1537 'F','i','l','e','R','e','n','a','m','e',
1538 'O','p','e','r','a','t','i','o','n','s',0};
1539 static const WCHAR SessionW[] = {'M','a','c','h','i','n','e','\\',
1540 'S','y','s','t','e','m','\\',
1541 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
1542 'C','o','n','t','r','o','l','\\',
1543 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r',0};
1544 static const int info_size = FIELD_OFFSET( KEY_VALUE_PARTIAL_INFORMATION, Data );
1546 OBJECT_ATTRIBUTES attr;
1547 UNICODE_STRING nameW;
1548 KEY_VALUE_PARTIAL_INFORMATION *info;
1549 BOOL rc = FALSE;
1550 HKEY Reboot = 0;
1551 DWORD len0, len1, len2;
1552 DWORD DataSize = 0;
1553 BYTE *Buffer = NULL;
1554 WCHAR *p;
1556 attr.Length = sizeof(attr);
1557 attr.RootDirectory = 0;
1558 attr.ObjectName = &nameW;
1559 attr.Attributes = 0;
1560 attr.SecurityDescriptor = NULL;
1561 attr.SecurityQualityOfService = NULL;
1562 RtlInitUnicodeString( &nameW, SessionW );
1564 if (NtCreateKey( &Reboot, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ) != STATUS_SUCCESS)
1566 WARN("Error creating key for reboot managment [%s]\n",
1567 "SYSTEM\\CurrentControlSet\\Control\\Session Manager");
1568 return FALSE;
1571 len0 = strlenW(PreString);
1572 len1 = strlenW(fn1) + len0 + 1;
1573 if (fn2)
1575 len2 = strlenW(fn2) + len0 + 1;
1576 if (flags & MOVEFILE_REPLACE_EXISTING) len2++; /* Plus 1 because of the leading '!' */
1578 else len2 = 1; /* minimum is the 0 characters for the empty second string */
1580 /* convert characters to bytes */
1581 len0 *= sizeof(WCHAR);
1582 len1 *= sizeof(WCHAR);
1583 len2 *= sizeof(WCHAR);
1585 RtlInitUnicodeString( &nameW, ValueName );
1587 /* First we check if the key exists and if so how many bytes it already contains. */
1588 if (NtQueryValueKey( Reboot, &nameW, KeyValuePartialInformation,
1589 NULL, 0, &DataSize ) == STATUS_BUFFER_OVERFLOW)
1591 if (!(Buffer = HeapAlloc( GetProcessHeap(), 0, DataSize + len1 + len2 + sizeof(WCHAR) )))
1592 goto Quit;
1593 if (NtQueryValueKey( Reboot, &nameW, KeyValuePartialInformation,
1594 Buffer, DataSize, &DataSize )) goto Quit;
1595 info = (KEY_VALUE_PARTIAL_INFORMATION *)Buffer;
1596 if (info->Type != REG_MULTI_SZ) goto Quit;
1597 if (DataSize > sizeof(info)) DataSize -= sizeof(WCHAR); /* remove terminating null (will be added back later) */
1599 else
1601 DataSize = info_size;
1602 if (!(Buffer = HeapAlloc( GetProcessHeap(), 0, DataSize + len1 + len2 + sizeof(WCHAR) )))
1603 goto Quit;
1606 p = (WCHAR *)(Buffer + DataSize);
1607 strcpyW( p, PreString );
1608 strcatW( p, fn1 );
1609 DataSize += len1;
1610 if (fn2)
1612 p = (WCHAR *)(Buffer + DataSize);
1613 if (flags & MOVEFILE_REPLACE_EXISTING)
1614 *p++ = '!';
1615 strcpyW( p, PreString );
1616 strcatW( p, fn2 );
1617 DataSize += len2;
1619 else
1621 p = (WCHAR *)(Buffer + DataSize);
1622 *p = 0;
1623 DataSize += sizeof(WCHAR);
1626 /* add final null */
1627 p = (WCHAR *)(Buffer + DataSize);
1628 *p = 0;
1629 DataSize += sizeof(WCHAR);
1631 rc = !NtSetValueKey(Reboot, &nameW, 0, REG_MULTI_SZ, Buffer + info_size, DataSize - info_size);
1633 Quit:
1634 if (Reboot) NtClose(Reboot);
1635 if (Buffer) HeapFree( GetProcessHeap(), 0, Buffer );
1636 return(rc);
1640 /**************************************************************************
1641 * MoveFileExW (KERNEL32.@)
1643 BOOL WINAPI MoveFileExW( LPCWSTR fn1, LPCWSTR fn2, DWORD flag )
1645 DOS_FULL_NAME full_name1, full_name2;
1646 HANDLE hFile;
1647 DWORD attr = INVALID_FILE_ATTRIBUTES;
1649 TRACE("(%s,%s,%04lx)\n", debugstr_w(fn1), debugstr_w(fn2), flag);
1651 /* FIXME: <Gerhard W. Gruber>sparhawk@gmx.at
1652 In case of W9x and lesser this function should return 120 (ERROR_CALL_NOT_IMPLEMENTED)
1653 to be really compatible. Most programs won't have any problems though. In case
1654 you encounter one, this is what you should return here. I don't know what's up
1655 with NT 3.5. Is this function available there or not?
1656 Does anybody really care about 3.5? :)
1659 /* Filename1 has to be always set to a valid path. Filename2 may be NULL
1660 if the source file has to be deleted.
1662 if (!fn1) {
1663 SetLastError(ERROR_INVALID_PARAMETER);
1664 return FALSE;
1667 /* This function has to be run through in order to process the name properly.
1668 If the BOOTDELAY flag is set, the file doesn't need to exist though. At least
1669 that is the behaviour on NT 4.0. The operation accepts the filenames as
1670 they are given but it can't reply with a reasonable returncode. Success
1671 means in that case success for entering the values into the registry.
1673 if(!DOSFS_GetFullName( fn1, TRUE, &full_name1 ))
1675 if(!(flag & MOVEFILE_DELAY_UNTIL_REBOOT))
1676 return FALSE;
1679 if (fn2) /* !fn2 means delete fn1 */
1681 if (DOSFS_GetFullName( fn2, TRUE, &full_name2 ))
1683 if(!(flag & MOVEFILE_DELAY_UNTIL_REBOOT))
1685 /* target exists, check if we may overwrite */
1686 if (!(flag & MOVEFILE_REPLACE_EXISTING))
1688 SetLastError( ERROR_ALREADY_EXISTS );
1689 return FALSE;
1693 else
1695 if (!DOSFS_GetFullName( fn2, FALSE, &full_name2 ))
1697 if(!(flag & MOVEFILE_DELAY_UNTIL_REBOOT))
1698 return FALSE;
1702 /* Source name and target path are valid */
1704 if (flag & MOVEFILE_DELAY_UNTIL_REBOOT)
1706 return FILE_AddBootRenameEntry( fn1, fn2, flag );
1709 attr = GetFileAttributesW( fn1 );
1710 if ( attr == INVALID_FILE_ATTRIBUTES ) return FALSE;
1712 /* check if we are allowed to rename the source */
1713 hFile = FILE_CreateFile( full_name1.long_name, 0, 0,
1714 NULL, OPEN_EXISTING, 0, 0, TRUE,
1715 GetDriveTypeW( full_name1.short_name ) );
1716 if (!hFile)
1718 if (GetLastError() != ERROR_ACCESS_DENIED) return FALSE;
1719 if ( !(attr & FILE_ATTRIBUTE_DIRECTORY) ) return FALSE;
1720 /* if it's a directory we can continue */
1722 else CloseHandle(hFile);
1724 /* check, if we are allowed to delete the destination,
1725 ** (but the file not being there is fine) */
1726 hFile = FILE_CreateFile( full_name2.long_name, GENERIC_READ|GENERIC_WRITE, 0,
1727 NULL, OPEN_EXISTING, 0, 0, TRUE,
1728 GetDriveTypeW( full_name2.short_name ) );
1729 if(!hFile && GetLastError() != ERROR_FILE_NOT_FOUND) return FALSE;
1730 CloseHandle(hFile);
1732 if (full_name1.drive != full_name2.drive)
1734 if (!(flag & MOVEFILE_COPY_ALLOWED))
1736 SetLastError( ERROR_NOT_SAME_DEVICE );
1737 return FALSE;
1739 else if ( attr & FILE_ATTRIBUTE_DIRECTORY )
1741 /* Strange, but that's what Windows returns */
1742 SetLastError ( ERROR_ACCESS_DENIED );
1743 return FALSE;
1746 if (rename( full_name1.long_name, full_name2.long_name ) == -1)
1747 /* Try copy/delete unless it's a directory. */
1748 /* FIXME: This does not handle the (unlikely) case that the two locations
1749 are on the same Wine drive, but on different Unix file systems. */
1751 if ( attr & FILE_ATTRIBUTE_DIRECTORY )
1753 FILE_SetDosError();
1754 return FALSE;
1756 else
1758 if ( ! CopyFileW( fn1, fn2, !(flag & MOVEFILE_REPLACE_EXISTING) ))
1759 return FALSE;
1760 if ( ! DeleteFileW ( fn1 ) )
1761 return FALSE;
1764 if (is_executable( full_name1.long_name ) != is_executable( full_name2.long_name ))
1766 struct stat fstat;
1767 if (stat( full_name2.long_name, &fstat ) != -1)
1769 if (is_executable( full_name2.long_name ))
1770 /* set executable bit where read bit is set */
1771 fstat.st_mode |= (fstat.st_mode & 0444) >> 2;
1772 else
1773 fstat.st_mode &= ~0111;
1774 chmod( full_name2.long_name, fstat.st_mode );
1777 return TRUE;
1779 else /* fn2 == NULL means delete source */
1781 if (flag & MOVEFILE_DELAY_UNTIL_REBOOT)
1783 if (flag & MOVEFILE_COPY_ALLOWED) {
1784 WARN("Illegal flag\n");
1785 SetLastError( ERROR_GEN_FAILURE );
1786 return FALSE;
1789 return FILE_AddBootRenameEntry( fn1, NULL, flag );
1792 if (unlink( full_name1.long_name ) == -1)
1794 FILE_SetDosError();
1795 return FALSE;
1797 return TRUE; /* successfully deleted */
1801 /**************************************************************************
1802 * MoveFileExA (KERNEL32.@)
1804 BOOL WINAPI MoveFileExA( LPCSTR fn1, LPCSTR fn2, DWORD flag )
1806 UNICODE_STRING fn1W, fn2W;
1807 BOOL ret;
1809 if (!fn1)
1811 SetLastError(ERROR_INVALID_PARAMETER);
1812 return FALSE;
1815 RtlCreateUnicodeStringFromAsciiz(&fn1W, fn1);
1816 if (fn2) RtlCreateUnicodeStringFromAsciiz(&fn2W, fn2);
1817 else fn2W.Buffer = NULL;
1819 ret = MoveFileExW( fn1W.Buffer, fn2W.Buffer, flag );
1821 RtlFreeUnicodeString(&fn1W);
1822 RtlFreeUnicodeString(&fn2W);
1823 return ret;
1827 /**************************************************************************
1828 * MoveFileW (KERNEL32.@)
1830 * Move file or directory
1832 BOOL WINAPI MoveFileW( LPCWSTR fn1, LPCWSTR fn2 )
1834 return MoveFileExW( fn1, fn2, MOVEFILE_COPY_ALLOWED );
1838 /**************************************************************************
1839 * MoveFileA (KERNEL32.@)
1841 BOOL WINAPI MoveFileA( LPCSTR fn1, LPCSTR fn2 )
1843 return MoveFileExA( fn1, fn2, MOVEFILE_COPY_ALLOWED );
1847 /**************************************************************************
1848 * CopyFileW (KERNEL32.@)
1850 BOOL WINAPI CopyFileW( LPCWSTR source, LPCWSTR dest, BOOL fail_if_exists )
1852 HANDLE h1, h2;
1853 BY_HANDLE_FILE_INFORMATION info;
1854 DWORD count;
1855 BOOL ret = FALSE;
1856 char buffer[2048];
1858 if (!source || !dest)
1860 SetLastError(ERROR_INVALID_PARAMETER);
1861 return FALSE;
1864 TRACE("%s -> %s\n", debugstr_w(source), debugstr_w(dest));
1866 if ((h1 = CreateFileW(source, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
1867 NULL, OPEN_EXISTING, 0, 0)) == INVALID_HANDLE_VALUE)
1869 WARN("Unable to open source %s\n", debugstr_w(source));
1870 return FALSE;
1873 if (!GetFileInformationByHandle( h1, &info ))
1875 WARN("GetFileInformationByHandle returned error for %s\n", debugstr_w(source));
1876 CloseHandle( h1 );
1877 return FALSE;
1880 if ((h2 = CreateFileW( dest, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
1881 fail_if_exists ? CREATE_NEW : CREATE_ALWAYS,
1882 info.dwFileAttributes, h1 )) == INVALID_HANDLE_VALUE)
1884 WARN("Unable to open dest %s\n", debugstr_w(dest));
1885 CloseHandle( h1 );
1886 return FALSE;
1889 while (ReadFile( h1, buffer, sizeof(buffer), &count, NULL ) && count)
1891 char *p = buffer;
1892 while (count != 0)
1894 DWORD res;
1895 if (!WriteFile( h2, p, count, &res, NULL ) || !res) goto done;
1896 p += res;
1897 count -= res;
1900 ret = TRUE;
1901 done:
1902 CloseHandle( h1 );
1903 CloseHandle( h2 );
1904 return ret;
1908 /**************************************************************************
1909 * CopyFileA (KERNEL32.@)
1911 BOOL WINAPI CopyFileA( LPCSTR source, LPCSTR dest, BOOL fail_if_exists)
1913 UNICODE_STRING sourceW, destW;
1914 BOOL ret;
1916 if (!source || !dest)
1918 SetLastError(ERROR_INVALID_PARAMETER);
1919 return FALSE;
1922 RtlCreateUnicodeStringFromAsciiz(&sourceW, source);
1923 RtlCreateUnicodeStringFromAsciiz(&destW, dest);
1925 ret = CopyFileW(sourceW.Buffer, destW.Buffer, fail_if_exists);
1927 RtlFreeUnicodeString(&sourceW);
1928 RtlFreeUnicodeString(&destW);
1929 return ret;
1933 /**************************************************************************
1934 * CopyFileExW (KERNEL32.@)
1936 * This implementation ignores most of the extra parameters passed-in into
1937 * the "ex" version of the method and calls the CopyFile method.
1938 * It will have to be fixed eventually.
1940 BOOL WINAPI CopyFileExW(LPCWSTR sourceFilename, LPCWSTR destFilename,
1941 LPPROGRESS_ROUTINE progressRoutine, LPVOID appData,
1942 LPBOOL cancelFlagPointer, DWORD copyFlags)
1945 * Interpret the only flag that CopyFile can interpret.
1947 return CopyFileW(sourceFilename, destFilename, (copyFlags & COPY_FILE_FAIL_IF_EXISTS) != 0);
1951 /**************************************************************************
1952 * CopyFileExA (KERNEL32.@)
1954 BOOL WINAPI CopyFileExA(LPCSTR sourceFilename, LPCSTR destFilename,
1955 LPPROGRESS_ROUTINE progressRoutine, LPVOID appData,
1956 LPBOOL cancelFlagPointer, DWORD copyFlags)
1958 UNICODE_STRING sourceW, destW;
1959 BOOL ret;
1961 if (!sourceFilename || !destFilename)
1963 SetLastError(ERROR_INVALID_PARAMETER);
1964 return FALSE;
1967 RtlCreateUnicodeStringFromAsciiz(&sourceW, sourceFilename);
1968 RtlCreateUnicodeStringFromAsciiz(&destW, destFilename);
1970 ret = CopyFileExW(sourceW.Buffer, destW.Buffer, progressRoutine, appData,
1971 cancelFlagPointer, copyFlags);
1973 RtlFreeUnicodeString(&sourceW);
1974 RtlFreeUnicodeString(&destW);
1975 return ret;
1979 /***********************************************************************
1980 * SetFileTime (KERNEL32.@)
1982 BOOL WINAPI SetFileTime( HANDLE hFile,
1983 const FILETIME *lpCreationTime,
1984 const FILETIME *lpLastAccessTime,
1985 const FILETIME *lpLastWriteTime )
1987 BOOL ret;
1988 SERVER_START_REQ( set_file_time )
1990 req->handle = hFile;
1991 if (lpLastAccessTime)
1992 RtlTimeToSecondsSince1970( (PLARGE_INTEGER) lpLastAccessTime, (DWORD *)&req->access_time );
1993 else
1994 req->access_time = 0; /* FIXME */
1995 if (lpLastWriteTime)
1996 RtlTimeToSecondsSince1970( (PLARGE_INTEGER) lpLastWriteTime, (DWORD *)&req->write_time );
1997 else
1998 req->write_time = 0; /* FIXME */
1999 ret = !wine_server_call_err( req );
2001 SERVER_END_REQ;
2002 return ret;
2006 /**************************************************************************
2007 * GetFileAttributesExW (KERNEL32.@)
2009 BOOL WINAPI GetFileAttributesExW(
2010 LPCWSTR lpFileName, GET_FILEEX_INFO_LEVELS fInfoLevelId,
2011 LPVOID lpFileInformation)
2013 DOS_FULL_NAME full_name;
2014 BY_HANDLE_FILE_INFORMATION info;
2016 if (!lpFileName || !lpFileInformation)
2018 SetLastError(ERROR_INVALID_PARAMETER);
2019 return FALSE;
2022 if (fInfoLevelId == GetFileExInfoStandard) {
2023 LPWIN32_FILE_ATTRIBUTE_DATA lpFad =
2024 (LPWIN32_FILE_ATTRIBUTE_DATA) lpFileInformation;
2025 if (!DOSFS_GetFullName( lpFileName, TRUE, &full_name )) return FALSE;
2026 if (!FILE_Stat( full_name.long_name, &info, NULL )) return FALSE;
2028 lpFad->dwFileAttributes = info.dwFileAttributes;
2029 lpFad->ftCreationTime = info.ftCreationTime;
2030 lpFad->ftLastAccessTime = info.ftLastAccessTime;
2031 lpFad->ftLastWriteTime = info.ftLastWriteTime;
2032 lpFad->nFileSizeHigh = info.nFileSizeHigh;
2033 lpFad->nFileSizeLow = info.nFileSizeLow;
2035 else {
2036 FIXME("invalid info level %d!\n", fInfoLevelId);
2037 return FALSE;
2040 return TRUE;
2044 /**************************************************************************
2045 * GetFileAttributesExA (KERNEL32.@)
2047 BOOL WINAPI GetFileAttributesExA(
2048 LPCSTR filename, GET_FILEEX_INFO_LEVELS fInfoLevelId,
2049 LPVOID lpFileInformation)
2051 UNICODE_STRING filenameW;
2052 BOOL ret = FALSE;
2054 if (!filename || !lpFileInformation)
2056 SetLastError(ERROR_INVALID_PARAMETER);
2057 return FALSE;
2060 if (RtlCreateUnicodeStringFromAsciiz(&filenameW, filename))
2062 ret = GetFileAttributesExW(filenameW.Buffer, fInfoLevelId, lpFileInformation);
2063 RtlFreeUnicodeString(&filenameW);
2065 else
2066 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2067 return ret;