Newer
Older
/*
* File handling functions
*
* Copyright 1993 John Burton
* Copyright 1996 Alexandre Julliard

Alexandre Julliard
committed
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*

Alexandre Julliard
committed
* TODO:
* Fix the CopyFileEx methods to implement the "extended" functionality.

Alexandre Julliard
committed
* Right now, they simply call the CopyFile method.
#include "config.h"
#ifdef HAVE_SYS_ERRNO_H
#ifdef HAVE_SYS_MMAN_H
#ifdef HAVE_SYS_TIME_H
# include <sys/time.h>
#endif
#ifdef HAVE_SYS_POLL_H
# include <sys/poll.h>
#endif
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#ifdef HAVE_UTIME_H
# include <utime.h>
#endif
#include "windef.h"
#include "winbase.h"
#include "winreg.h"
#include "ntddk.h"
#include "wine/winbase16.h"
#include "wine/server.h"
#include "async.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(file);
#if defined(MAP_ANONYMOUS) && !defined(MAP_ANON)
#define MAP_ANON MAP_ANONYMOUS
#endif
/* Size of per-process table of DOS handles */
#define DOS_TABLE_SIZE 256
/* Macro to derive file offset from OVERLAPPED struct */
#define OVERLAPPED_OFFSET(overlapped) ((off_t) (overlapped)->Offset + ((off_t) (overlapped)->OffsetHigh << 32))
static HANDLE dos_handles[DOS_TABLE_SIZE];
mode_t FILE_umask;
extern HANDLE WINAPI FILE_SmbOpen(LPCSTR name);
/***********************************************************************
* Asynchronous file I/O *
*/
static DWORD fileio_get_async_status (const async_private *ovp);
static DWORD fileio_get_async_count (const async_private *ovp);
static void fileio_set_async_status (async_private *ovp, const DWORD status);
static void CALLBACK fileio_call_completion_func (ULONG_PTR data);
static void fileio_async_cleanup (async_private *ovp);
static async_ops fileio_async_ops =
{
fileio_get_async_status, /* get_status */
fileio_set_async_status, /* set_status */
fileio_get_async_count, /* get_count */
fileio_call_completion_func, /* call_completion */
fileio_async_cleanup /* cleanup */
};
static async_ops fileio_nocomp_async_ops =
{
fileio_get_async_status, /* get_status */
fileio_set_async_status, /* set_status */
fileio_get_async_count, /* get_count */
NULL, /* call_completion */
fileio_async_cleanup /* cleanup */
};
typedef struct async_fileio
{
struct async_private async;
LPOVERLAPPED lpOverlapped;
LPOVERLAPPED_COMPLETION_ROUTINE completion_func;
char *buffer;
int count;
enum fd_type fd_type;
} async_fileio;
static DWORD fileio_get_async_status (const struct async_private *ovp)
{
return ((async_fileio*) ovp)->lpOverlapped->Internal;
}
static void fileio_set_async_status (async_private *ovp, const DWORD status)
{
((async_fileio*) ovp)->lpOverlapped->Internal = status;
}
static DWORD fileio_get_async_count (const struct async_private *ovp)
{
async_fileio *fileio = (async_fileio*) ovp;
DWORD ret = fileio->count - fileio->lpOverlapped->InternalHigh;
return (ret < 0 ? 0 : ret);
}
static void CALLBACK fileio_call_completion_func (ULONG_PTR data)
{
async_fileio *ovp = (async_fileio*) data;
TRACE ("data: %p\n", ovp);
ovp->completion_func( ovp->lpOverlapped->Internal,
ovp->lpOverlapped->InternalHigh,
ovp->lpOverlapped );
fileio_async_cleanup ( &ovp->async );
}
static void fileio_async_cleanup ( struct async_private *ovp )
{
HeapFree ( GetProcessHeap(), 0, ovp );
}
/***********************************************************************
* FILE_ConvertOFMode
* Convert OF_* mode into flags for CreateFile.
static void FILE_ConvertOFMode( INT mode, DWORD *access, DWORD *sharing )
switch(mode & 0x03)
{
case OF_READ: *access = GENERIC_READ; break;
case OF_WRITE: *access = GENERIC_WRITE; break;
case OF_READWRITE: *access = GENERIC_READ | GENERIC_WRITE; break;
default: *access = 0; break;
}
switch(mode & 0x70)
{
case OF_SHARE_EXCLUSIVE: *sharing = 0; break;
case OF_SHARE_DENY_WRITE: *sharing = FILE_SHARE_READ; break;
case OF_SHARE_DENY_READ: *sharing = FILE_SHARE_WRITE; break;
case OF_SHARE_DENY_NONE:
case OF_SHARE_COMPAT:
default: *sharing = FILE_SHARE_READ | FILE_SHARE_WRITE; break;
}
}
/***********************************************************************
* FILE_strcasecmp
* locale-independent case conversion for file I/O
int FILE_strcasecmp( const char *str1, const char *str2 )
int ret = 0;
for ( ; ; str1++, str2++)
if ((ret = FILE_toupper(*str1) - FILE_toupper(*str2)) || !*str1) break;
return ret;
/***********************************************************************
* FILE_strncasecmp
*
* locale-independent case conversion for file I/O
*/
int FILE_strncasecmp( const char *str1, const char *str2, int len )
{
int ret = 0;
for ( ; len > 0; len--, str1++, str2++)
if ((ret = FILE_toupper(*str1) - FILE_toupper(*str2)) || !*str1) break;
return ret;
}
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
/***********************************************************************
* FILE_GetNtStatus(void)
*
* Retrieve the Nt Status code from errno.
* Try to be consistent with FILE_SetDosError().
*/
DWORD FILE_GetNtStatus(void)
{
int err = errno;
DWORD nt;
TRACE ( "errno = %d\n", errno );
switch ( err )
{
case EAGAIN: nt = STATUS_SHARING_VIOLATION; break;
case EBADF: nt = STATUS_INVALID_HANDLE; break;
case ENOSPC: nt = STATUS_DISK_FULL; break;
case EPERM:
case EROFS:
case EACCES: nt = STATUS_ACCESS_DENIED; break;
case ENOENT: nt = STATUS_SHARING_VIOLATION; break;
case EISDIR: nt = STATUS_FILE_IS_A_DIRECTORY; break;
case EMFILE:
case ENFILE: nt = STATUS_NO_MORE_FILES; break;
case EINVAL:
case ENOTEMPTY: nt = STATUS_DIRECTORY_NOT_EMPTY; break;
case EPIPE: nt = STATUS_PIPE_BROKEN; break;
case ENOEXEC: /* ?? */
case ESPIPE: /* ?? */
case EEXIST: /* ?? */
default:
FIXME ( "Converting errno %d to STATUS_UNSUCCESSFUL\n", err );
nt = STATUS_UNSUCCESSFUL;
}
return nt;
}
/***********************************************************************
* FILE_SetDosError
*
* Set the DOS error code from errno.
*/
void FILE_SetDosError(void)
{
int save_errno = errno; /* errno gets overwritten by printf */
TRACE("errno = %d %s\n", errno, strerror(errno));
SetLastError( ERROR_SHARING_VIOLATION );
SetLastError( ERROR_INVALID_HANDLE );
SetLastError( ERROR_HANDLE_DISK_FULL );
break;
case EACCES:
case EPERM:
case EROFS:
SetLastError( ERROR_ACCESS_DENIED );
SetLastError( ERROR_LOCK_VIOLATION );
SetLastError( ERROR_FILE_NOT_FOUND );
SetLastError( ERROR_CANNOT_MAKE );
SetLastError( ERROR_NO_MORE_FILES );
SetLastError( ERROR_FILE_EXISTS );
SetLastError( ERROR_SEEK );
SetLastError( ERROR_DIR_NOT_EMPTY );
case ENOEXEC:
SetLastError( ERROR_BAD_FORMAT );
break;
WARN("unknown file error: %s\n", strerror(save_errno) );
SetLastError( ERROR_GEN_FAILURE );
/***********************************************************************
*
* Retrieve the Unix handle corresponding to a file handle.
* Returns -1 on failure.
*/
static int FILE_GetUnixHandleType( HANDLE handle, DWORD access, enum fd_type *type, int *flags_ptr )
int ret, flags, fd = -1;
ret = wine_server_handle_to_fd( handle, access, &fd, type, &flags );
if (flags_ptr) *flags_ptr = flags;
if (ret) SetLastError( RtlNtStatusToDosError(ret) );
else if (((access & GENERIC_READ) && (flags & FD_FLAG_RECV_SHUTDOWN)) ||
((access & GENERIC_WRITE) && (flags & FD_FLAG_SEND_SHUTDOWN)))
{
close (fd);
SetLastError ( ERROR_PIPE_NOT_CONNECTED );
return -1;
}
return fd;
}
/***********************************************************************
* FILE_GetUnixHandle
*
* Retrieve the Unix handle corresponding to a file handle.
* Returns -1 on failure.
*/
int FILE_GetUnixHandle( HANDLE handle, DWORD access )
{
return FILE_GetUnixHandleType( handle, access, NULL, NULL );
/*************************************************************************
* FILE_OpenConsole
*
* Open a handle to the current process console.
* Returns 0 on failure.
*/
static HANDLE FILE_OpenConsole( BOOL output, DWORD access, DWORD sharing, LPSECURITY_ATTRIBUTES sa )
HANDLE ret;
SERVER_START_REQ( open_console )
req->access = access;
req->inherit = (sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle);
SetLastError(0);
wine_server_call_err( req );
ret = reply->handle;
}
SERVER_END_REQ;
return ret;
}
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
/* FIXME: those routines defined as pointers are needed, because this file is
* currently compiled into NTDLL whereas it belongs to kernel32.
* this shall go away once all the DLL separation process is done
*/
typedef BOOL (WINAPI* pRW)(HANDLE, const void*, DWORD, DWORD*, void*);
static BOOL FILE_ReadConsole(HANDLE hCon, void* buf, DWORD nb, DWORD* nr, void* p)
{
static HANDLE hKernel /* = 0 */;
static pRW pReadConsole /* = 0 */;
if ((!hKernel && !(hKernel = LoadLibraryA("kernel32"))) ||
(!pReadConsole &&
!(pReadConsole = GetProcAddress(hKernel, "ReadConsoleA"))))
{
*nr = 0;
return 0;
}
return (pReadConsole)(hCon, buf, nb, nr, p);
}
static BOOL FILE_WriteConsole(HANDLE hCon, const void* buf, DWORD nb, DWORD* nr, void* p)
{
static HANDLE hKernel /* = 0 */;
static pRW pWriteConsole /* = 0 */;
if ((!hKernel && !(hKernel = LoadLibraryA("kernel32"))) ||
(!pWriteConsole &&
!(pWriteConsole = GetProcAddress(hKernel, "WriteConsoleA"))))
{
*nr = 0;
return 0;
}
return (pWriteConsole)(hCon, buf, nb, nr, p);
}
/* end of FIXME */
/***********************************************************************
* FILE_CreateFile
*
* Implementation of CreateFile. Takes a Unix path name.
* Returns 0 on failure.
HANDLE FILE_CreateFile( LPCSTR filename, DWORD access, DWORD sharing,
LPSECURITY_ATTRIBUTES sa, DWORD creation,
DWORD attributes, HANDLE template, BOOL fail_read_only,
UINT drive_type )
unsigned int err;
HANDLE ret;
for (;;)
SERVER_START_REQ( create_file )
{
req->access = access;
req->inherit = (sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle);
req->sharing = sharing;
req->create = creation;
req->attrs = attributes;
req->drive_type = drive_type;
wine_server_add_data( req, filename, strlen(filename) );
SetLastError(0);
err = wine_server_call( req );
ret = reply->handle;
}
SERVER_END_REQ;
/* If write access failed, retry without GENERIC_WRITE */
if (!ret && !fail_read_only && (access & GENERIC_WRITE))
if ((err == STATUS_MEDIA_WRITE_PROTECTED) || (err == STATUS_ACCESS_DENIED))
{
TRACE("Write access failed for file '%s', trying without "
"write access\n", filename);
access &= ~GENERIC_WRITE;
continue;
}
if (err)
{
/* In the case file creation was rejected due to CREATE_NEW flag
* was specified and file with that name already exists, correct
* last error is ERROR_FILE_EXISTS and not ERROR_ALREADY_EXISTS.
* Note: RtlNtStatusToDosError is not the subject to blame here.
*/
if (err == STATUS_OBJECT_NAME_COLLISION)
SetLastError( ERROR_FILE_EXISTS );
else
SetLastError( RtlNtStatusToDosError(err) );
}
if (!ret) WARN("Unable to create file '%s' (GLE %ld)\n", filename, GetLastError());
return ret;
}
}
/***********************************************************************
* FILE_CreateDevice
*
* Same as FILE_CreateFile but for a device
* Returns 0 on failure.
HANDLE FILE_CreateDevice( int client_id, DWORD access, LPSECURITY_ATTRIBUTES sa )
HANDLE ret;
SERVER_START_REQ( create_device )
{
req->access = access;
req->inherit = (sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle);
req->id = client_id;
SetLastError(0);
wine_server_call_err( req );
ret = reply->handle;
}
SERVER_END_REQ;
return ret;
static HANDLE FILE_OpenPipe(LPCWSTR name, DWORD access)
DWORD len = 0;
if (name && (len = strlenW(name)) > MAX_PATH)
{
SetLastError( ERROR_FILENAME_EXCED_RANGE );
return 0;
}
SERVER_START_REQ( open_named_pipe )
{
req->access = access;
SetLastError(0);
wine_server_add_data( req, name, len * sizeof(WCHAR) );
wine_server_call_err( req );
ret = reply->handle;
SERVER_END_REQ;
TRACE("Returned %d\n",ret);
return ret;
}
/*************************************************************************
* CreateFileW [KERNEL32.@] Creates or opens a file or other object
*
* Creates or opens an object, and returns a handle that can be used to
* access that object.
*
* PARAMS
* filename [in] pointer to filename to be accessed
* access [in] access mode requested
* sharing [in] share mode
* sa [in] pointer to security attributes
* creation [in] how to create the file
* attributes [in] attributes for newly created file
* template [in] handle to file with extended attributes to copy
* RETURNS
* Success: Open handle to specified file
* Failure: INVALID_HANDLE_VALUE
*
* NOTES
* Should call SetLastError() on failure.
*
* BUGS
*
* Doesn't support character devices, template files, or a
* lot of the 'attributes' flags yet.
HANDLE WINAPI CreateFileW( LPCWSTR filename, DWORD access, DWORD sharing,
LPSECURITY_ATTRIBUTES sa, DWORD creation,
DWORD attributes, HANDLE template )
HANDLE ret;
static const WCHAR bkslashes_with_question_markW[] = {'\\','\\','?','\\',0};
static const WCHAR bkslashes_with_dotW[] = {'\\','\\','.','\\',0};
static const WCHAR bkslashesW[] = {'\\','\\',0};
static const WCHAR coninW[] = {'C','O','N','I','N','$',0};
static const WCHAR conoutW[] = {'C','O','N','O','U','T','$',0};
if (!filename)
{
SetLastError( ERROR_INVALID_PARAMETER );
return INVALID_HANDLE_VALUE;
TRACE("%s %s%s%s%s%s%s%s attributes 0x%lx\n", debugstr_w(filename),
((access & GENERIC_READ)==GENERIC_READ)?"GENERIC_READ ":"",
((access & GENERIC_WRITE)==GENERIC_WRITE)?"GENERIC_WRITE ":"",
(!access)?"QUERY_ACCESS ":"",
((sharing & FILE_SHARE_READ)==FILE_SHARE_READ)?"FILE_SHARE_READ ":"",
((sharing & FILE_SHARE_WRITE)==FILE_SHARE_WRITE)?"FILE_SHARE_WRITE ":"",
((sharing & FILE_SHARE_DELETE)==FILE_SHARE_DELETE)?"FILE_SHARE_DELETE ":"",
(creation ==CREATE_NEW)?"CREATE_NEW":
(creation ==CREATE_ALWAYS)?"CREATE_ALWAYS ":
(creation ==OPEN_EXISTING)?"OPEN_EXISTING ":
(creation ==OPEN_ALWAYS)?"OPEN_ALWAYS ":
(creation ==TRUNCATE_EXISTING)?"TRUNCATE_EXISTING ":"", attributes);
/* If the name starts with '\\?\', ignore the first 4 chars. */
if (!strncmpW(filename, bkslashes_with_question_markW, 4))
static const WCHAR uncW[] = {'U','N','C','\\',0};
if (!strncmpiW(filename, uncW, 4))
FIXME("UNC name (%s) not supported.\n", debugstr_w(filename) );
SetLastError( ERROR_PATH_NOT_FOUND );
return INVALID_HANDLE_VALUE;
if (!strncmpW(filename, bkslashes_with_dotW, 4))
{
static const WCHAR pipeW[] = {'P','I','P','E','\\',0};
if(!strncmpiW(filename + 4, pipeW, 5))
TRACE("Opening a pipe: %s\n", debugstr_w(filename));
ret = FILE_OpenPipe(filename,access);
goto done;
else if (isalphaW(filename[4]) && filename[5] == ':' && filename[6] == '\0')
ret = FILE_CreateDevice( (toupperW(filename[4]) - 'A') | 0x20000, access, sa );
goto done;
}
else if (!DOSFS_GetDevice( filename ))
{
ret = DEVICE_Open( filename+4, access, sa );
goto done;
}
else
filename+=4; /* fall into DOSFS_Device case below */
}
/* If the name still starts with '\\', it's a UNC name. */
if (!strncmpW(filename, bkslashesW, 2))
ret = SMB_CreateFileW(filename, access, sharing, sa, creation, attributes, template );
/* If the name contains a DOS wild card (* or ?), do no create a file */
if(strchrW(filename, '*') || strchrW(filename, '?'))
return INVALID_HANDLE_VALUE;
/* Open a console for CONIN$ or CONOUT$ */
if (!strcmpiW(filename, coninW))
{
ret = FILE_OpenConsole( FALSE, access, sharing, sa );
goto done;
}
if (!strcmpiW(filename, conoutW))
{
ret = FILE_OpenConsole( TRUE, access, sharing, sa );
goto done;
}
if (DOSFS_GetDevice( filename ))
TRACE("opening device %s\n", debugstr_w(filename) );
if (!(ret = DOSFS_OpenDevice( filename, access, attributes, sa )))
{
/* Do not silence this please. It is a critical error. -MM */
ERR("Couldn't open device %s!\n", debugstr_w(filename));
SetLastError( ERROR_FILE_NOT_FOUND );
}
goto done;
/* check for filename, don't check for last entry if creating */
if (!DOSFS_GetFullName( filename,
(creation == OPEN_EXISTING) ||
(creation == TRUNCATE_EXISTING),
&full_name )) {
WARN("Unable to get full filename from %s (GLE %ld)\n",
debugstr_w(filename), GetLastError());
return INVALID_HANDLE_VALUE;
ret = FILE_CreateFile( full_name.long_name, access, sharing,
sa, creation, attributes, template,
DRIVE_GetFlags(full_name.drive) & DRIVE_FAIL_READ_ONLY,
GetDriveTypeW( full_name.short_name ) );
done:
if (!ret) ret = INVALID_HANDLE_VALUE;
TRACE("returning %08x\n", ret);
return ret;
/*************************************************************************
HANDLE WINAPI CreateFileA( LPCSTR filename, DWORD access, DWORD sharing,
LPSECURITY_ATTRIBUTES sa, DWORD creation,
DWORD attributes, HANDLE template)
UNICODE_STRING filenameW;
HANDLE ret = INVALID_HANDLE_VALUE;
if (!filename)
{
SetLastError( ERROR_INVALID_PARAMETER );
return INVALID_HANDLE_VALUE;
}
if (RtlCreateUnicodeStringFromAsciiz(&filenameW, filename))
{
ret = CreateFileW(filenameW.Buffer, access, sharing, sa, creation,
attributes, template);
RtlFreeUnicodeString(&filenameW);
}
else
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return ret;
}
/***********************************************************************
* FILE_FillInfo
*
* Fill a file information from a struct stat.
*/
static void FILE_FillInfo( struct stat *st, BY_HANDLE_FILE_INFORMATION *info )
info->dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
else
info->dwFileAttributes = FILE_ATTRIBUTE_ARCHIVE;
if (!(st->st_mode & S_IWUSR))
info->dwFileAttributes |= FILE_ATTRIBUTE_READONLY;
RtlSecondsSince1970ToTime( st->st_mtime, &info->ftCreationTime );
RtlSecondsSince1970ToTime( st->st_mtime, &info->ftLastWriteTime );
RtlSecondsSince1970ToTime( st->st_atime, &info->ftLastAccessTime );
info->dwVolumeSerialNumber = 0; /* FIXME */
info->nFileSizeHigh = 0;
info->nFileSizeLow = 0;
if (!S_ISDIR(st->st_mode)) {
info->nFileSizeHigh = st->st_size >> 32;
info->nFileSizeLow = st->st_size & 0xffffffff;
}
info->nNumberOfLinks = st->st_nlink;
info->nFileIndexHigh = 0;
info->nFileIndexLow = st->st_ino;
}
/***********************************************************************
* FILE_Stat
*
BOOL FILE_Stat( LPCSTR unixName, BY_HANDLE_FILE_INFORMATION *info )
struct stat st;
if (lstat( unixName, &st ) == -1)
if (!S_ISLNK(st.st_mode)) FILE_FillInfo( &st, info );
else
{
/* do a "real" stat to find out
about the type of the symlink destination */
if (stat( unixName, &st ) == -1)
{
FILE_SetDosError();
return FALSE;
}
FILE_FillInfo( &st, info );
info->dwFileAttributes |= FILE_ATTRIBUTE_SYMLINK;
}
}
/***********************************************************************
* GetFileInformationByHandle (KERNEL32.@)
DWORD WINAPI GetFileInformationByHandle( HANDLE hFile,
DWORD ret;
TRACE("%08x\n", hFile);
SERVER_START_REQ( get_file_info )
{
req->handle = hFile;
if ((ret = !wine_server_call_err( req )))
/* FIXME: which file types are supported ?
* Serial ports (FILE_TYPE_CHAR) are not,
* and MSDN also says that pipes are not supported.
* FILE_TYPE_REMOTE seems to be supported according to
* MSDN q234741.txt */
if ((reply->type == FILE_TYPE_DISK) || (reply->type == FILE_TYPE_REMOTE))
{
RtlSecondsSince1970ToTime( reply->write_time, &info->ftCreationTime );
RtlSecondsSince1970ToTime( reply->write_time, &info->ftLastWriteTime );
RtlSecondsSince1970ToTime( reply->access_time, &info->ftLastAccessTime );
info->dwFileAttributes = reply->attr;
info->dwVolumeSerialNumber = reply->serial;
info->nFileSizeHigh = reply->size_high;
info->nFileSizeLow = reply->size_low;
info->nNumberOfLinks = reply->links;
info->nFileIndexHigh = reply->index_high;
info->nFileIndexLow = reply->index_low;
}
else
{
SetLastError(ERROR_NOT_SUPPORTED);
ret = 0;
}
}
}
SERVER_END_REQ;
return ret;
}
/**************************************************************************
return GetFileAttributesA( name );
}
/**************************************************************************
* GetFileAttributesW (KERNEL32.@)
DWORD WINAPI GetFileAttributesW( LPCWSTR name )
if (name == NULL)
{
SetLastError( ERROR_INVALID_PARAMETER );
return -1;
}
if (!DOSFS_GetFullName( name, TRUE, &full_name) )
if (!FILE_Stat( full_name.long_name, &info )) return -1;
return info.dwFileAttributes;
}
/**************************************************************************
* GetFileAttributesA (KERNEL32.@)
DWORD WINAPI GetFileAttributesA( LPCSTR name )
UNICODE_STRING nameW;
DWORD ret = (DWORD)-1;
if (!name)
{
SetLastError( ERROR_INVALID_PARAMETER );
return (DWORD)-1;
}
if (RtlCreateUnicodeStringFromAsciiz(&nameW, name))
{
ret = GetFileAttributesW(nameW.Buffer);
RtlFreeUnicodeString(&nameW);
}
else
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return ret;
/**************************************************************************
* SetFileAttributes (KERNEL.421)
*/
BOOL16 WINAPI SetFileAttributes16( LPCSTR lpFileName, DWORD attributes )
{
return SetFileAttributesA( lpFileName, attributes );
}
/**************************************************************************
* SetFileAttributesW (KERNEL32.@)
BOOL WINAPI SetFileAttributesW(LPCWSTR lpFileName, DWORD attributes)
{
struct stat buf;
DOS_FULL_NAME full_name;
if (!lpFileName)
{
SetLastError( ERROR_INVALID_PARAMETER );
return FALSE;
}
TRACE("(%s,%lx)\n", debugstr_w(lpFileName), attributes);
if (!DOSFS_GetFullName( lpFileName, TRUE, &full_name ))
return FALSE;
if(stat(full_name.long_name,&buf)==-1)
{
FILE_SetDosError();
return FALSE;
}
if (attributes & FILE_ATTRIBUTE_READONLY)
{
if(S_ISDIR(buf.st_mode))
/* FIXME */
WARN("FILE_ATTRIBUTE_READONLY ignored for directory.\n");
else
buf.st_mode &= ~0222; /* octal!, clear write permission bits */
attributes &= ~FILE_ATTRIBUTE_READONLY;
}
else
{
/* add write permission */
buf.st_mode |= (0600 | ((buf.st_mode & 044) >> 1)) & (~FILE_umask);
}
if (attributes & FILE_ATTRIBUTE_DIRECTORY)
{
if (!S_ISDIR(buf.st_mode))
FIXME("SetFileAttributes expected the file %s to be a directory\n",
debugstr_w(lpFileName));
attributes &= ~FILE_ATTRIBUTE_DIRECTORY;
}
attributes &= ~(FILE_ATTRIBUTE_NORMAL|FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM);
if (attributes)
FIXME("(%s):%lx attribute(s) not implemented.\n", debugstr_w(lpFileName), attributes);
if (-1==chmod(full_name.long_name,buf.st_mode))
{
if (GetDriveTypeW(lpFileName) == DRIVE_CDROM)
{
SetLastError( ERROR_ACCESS_DENIED );
return FALSE;
}
/*
* FIXME: We don't return FALSE here because of differences between
* Linux and Windows privileges. Under Linux only the owner of
* the file is allowed to change file attributes. Under Windows,
* applications expect that if you can write to a file, you can also
* change its attributes (see GENERIC_WRITE). We could try to be
* clever here but that would break multi-user installations where
* users share read-only DLLs. This is because some installers like
* to change attributes of already installed DLLs.
*/
FIXME("Couldn't set file attributes for existing file \"%s\".\n"
"Check permissions or set VFAT \"quiet\" mount flag\n", full_name.long_name);
}
return TRUE;
}
/**************************************************************************
* SetFileAttributesA (KERNEL32.@)
BOOL WINAPI SetFileAttributesA(LPCSTR lpFileName, DWORD attributes)
UNICODE_STRING filenameW;
HANDLE ret = FALSE;
if (!lpFileName)
{
SetLastError( ERROR_INVALID_PARAMETER );
return FALSE;
}
if (RtlCreateUnicodeStringFromAsciiz(&filenameW, lpFileName))
{
ret = SetFileAttributesW(filenameW.Buffer, attributes);
RtlFreeUnicodeString(&filenameW);
}
else
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return ret;
}
/***********************************************************************
* GetFileSize (KERNEL32.@)
DWORD WINAPI GetFileSize( HANDLE hFile, LPDWORD filesizehigh )
if (!GetFileInformationByHandle( hFile, &info )) return -1;
if (filesizehigh) *filesizehigh = info.nFileSizeHigh;
return info.nFileSizeLow;
}
/***********************************************************************
* GetFileTime (KERNEL32.@)
BOOL WINAPI GetFileTime( HANDLE hFile, FILETIME *lpCreationTime,
FILETIME *lpLastAccessTime,
FILETIME *lpLastWriteTime )
{
BY_HANDLE_FILE_INFORMATION info;
if (!GetFileInformationByHandle( hFile, &info )) return FALSE;
if (lpCreationTime) *lpCreationTime = info.ftCreationTime;
if (lpLastAccessTime) *lpLastAccessTime = info.ftLastAccessTime;
if (lpLastWriteTime) *lpLastWriteTime = info.ftLastWriteTime;
return TRUE;
/***********************************************************************
* CompareFileTime (KERNEL32.@)
INT WINAPI CompareFileTime( LPFILETIME x, LPFILETIME y )