Skip to content
Snippets Groups Projects
file.c 98 KiB
Newer Older
Alexandre Julliard's avatar
Alexandre Julliard committed
/*
 * File handling functions
 *
 * Copyright 1993 John Burton
 * Copyright 1996 Alexandre Julliard
 * 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
 *
Andreas Mohr's avatar
Andreas Mohr committed
 *    Fix the CopyFileEx methods to implement the "extended" functionality.
 *    Right now, they simply call the CopyFile method.
Alexandre Julliard's avatar
Alexandre Julliard committed
 */

#include "wine/port.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
#include <assert.h>
Alexandre Julliard's avatar
Alexandre Julliard committed
#include <ctype.h>
Alexandre Julliard's avatar
Alexandre Julliard committed
#include <errno.h>
#include <fcntl.h>
Alexandre Julliard's avatar
Alexandre Julliard committed
#include <stdlib.h>
Alexandre Julliard's avatar
Alexandre Julliard committed
#include <string.h>
Alexandre Julliard's avatar
Alexandre Julliard committed
#include <sys/errno.h>
Alexandre Julliard's avatar
Alexandre Julliard committed
#include <sys/types.h>
Alexandre Julliard's avatar
Alexandre Julliard committed
#include <sys/stat.h>
Alexandre Julliard's avatar
Alexandre Julliard committed
#include <sys/mman.h>
#ifdef HAVE_SYS_TIME_H
# include <sys/time.h>
#endif
#ifdef HAVE_SYS_POLL_H
# include <sys/poll.h>
#endif
Alexandre Julliard's avatar
Alexandre Julliard committed
#include <time.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#ifdef HAVE_UTIME_H
# include <utime.h>
#endif
Alexandre Julliard's avatar
Alexandre Julliard committed

Alexandre Julliard's avatar
Alexandre Julliard committed
#include "winerror.h"
#include "windef.h"
#include "winbase.h"
#include "winreg.h"
#include "ntddk.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
#include "drive.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
#include "file.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
#include "heap.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
#include "msdos.h"
#include "wincon.h"

#include "smb.h"
#include "wine/unicode.h"
Alexandre Julliard's avatar
Alexandre Julliard committed

Alexandre Julliard's avatar
Alexandre Julliard committed
#if defined(MAP_ANONYMOUS) && !defined(MAP_ANON)
#define MAP_ANON MAP_ANONYMOUS
#endif
Alexandre Julliard's avatar
Alexandre Julliard committed

/* Size of per-process table of DOS handles */
#define DOS_TABLE_SIZE 256
Alexandre Julliard's avatar
Alexandre Julliard committed

/* 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];

extern HANDLE WINAPI FILE_SmbOpen(LPCSTR name);
Alexandre Julliard's avatar
Alexandre Julliard committed

/***********************************************************************
 *                  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);
Martin Wilck's avatar
Martin Wilck committed
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 */
Martin Wilck's avatar
Martin Wilck committed
    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;
} 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);

Martin Wilck's avatar
Martin Wilck committed
    ovp->completion_func( ovp->lpOverlapped->Internal,
                          ovp->lpOverlapped->InternalHigh,
                          ovp->lpOverlapped );

    fileio_async_cleanup ( &ovp->async );
}
Martin Wilck's avatar
Martin Wilck committed
static void fileio_async_cleanup ( struct async_private *ovp )
{
    HeapFree ( GetProcessHeap(), 0, ovp );
/***********************************************************************
 * 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;
    }
}


/***********************************************************************
 * 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;
}
Alexandre Julliard's avatar
Alexandre Julliard committed

/***********************************************************************
 *           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;
}

Alexandre Julliard's avatar
Alexandre Julliard committed
/***********************************************************************
 *           FILE_SetDosError
 *
 * Set the DOS error code from errno.
 */
void FILE_SetDosError(void)
{
Alexandre Julliard's avatar
Alexandre Julliard committed
    int save_errno = errno; /* errno gets overwritten by printf */

    TRACE("errno = %d %s\n", errno, strerror(errno));
Alexandre Julliard's avatar
Alexandre Julliard committed
    switch (save_errno)
Alexandre Julliard's avatar
Alexandre Julliard committed
    {
    case EAGAIN:
        SetLastError( ERROR_SHARING_VIOLATION );
Alexandre Julliard's avatar
Alexandre Julliard committed
        break;
    case EBADF:
        SetLastError( ERROR_INVALID_HANDLE );
Alexandre Julliard's avatar
Alexandre Julliard committed
        break;
    case ENOSPC:
        SetLastError( ERROR_HANDLE_DISK_FULL );
Alexandre Julliard's avatar
Alexandre Julliard committed
        break;
    case EACCES:
    case EPERM:
    case EROFS:
        SetLastError( ERROR_ACCESS_DENIED );
Alexandre Julliard's avatar
Alexandre Julliard committed
        break;
    case EBUSY:
        SetLastError( ERROR_LOCK_VIOLATION );
Alexandre Julliard's avatar
Alexandre Julliard committed
        break;
    case ENOENT:
        SetLastError( ERROR_FILE_NOT_FOUND );
Alexandre Julliard's avatar
Alexandre Julliard committed
        break;
    case EISDIR:
        SetLastError( ERROR_CANNOT_MAKE );
Alexandre Julliard's avatar
Alexandre Julliard committed
        break;
    case ENFILE:
    case EMFILE:
        SetLastError( ERROR_NO_MORE_FILES );
Alexandre Julliard's avatar
Alexandre Julliard committed
        break;
    case EEXIST:
        SetLastError( ERROR_FILE_EXISTS );
Alexandre Julliard's avatar
Alexandre Julliard committed
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
    case EINVAL:
Alexandre Julliard's avatar
Alexandre Julliard committed
    case ESPIPE:
Alexandre Julliard's avatar
Alexandre Julliard committed
        break;
    case ENOTEMPTY:
        SetLastError( ERROR_DIR_NOT_EMPTY );
Alexandre Julliard's avatar
Alexandre Julliard committed
        break;
    case ENOEXEC:
        SetLastError( ERROR_BAD_FORMAT );
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
    default:
Andreas Mohr's avatar
Andreas Mohr committed
        WARN("unknown file error: %s\n", strerror(save_errno) );
        SetLastError( ERROR_GEN_FAILURE );
Alexandre Julliard's avatar
Alexandre Julliard committed
        break;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
    errno = save_errno;
/***********************************************************************
 *           FILE_GetUnixHandleType
 *
 * Retrieve the Unix handle corresponding to a file handle.
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;
    }
/***********************************************************************
 *           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.
static HANDLE FILE_OpenConsole( BOOL output, DWORD access, DWORD sharing, LPSECURITY_ATTRIBUTES sa )
        req->from    = output;
	req->share   = sharing;
        req->inherit = (sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle);
        SetLastError(0);
        wine_server_call_err( req );
        ret = reply->handle;
/* 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 */
Alexandre Julliard's avatar
Alexandre Julliard committed
/***********************************************************************
 *           FILE_CreateFile
 *
 * Implementation of CreateFile. Takes a Unix path name.
Alexandre Julliard's avatar
Alexandre Julliard committed
 */
Jim Aston's avatar
Jim Aston committed
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 )
Alexandre Julliard's avatar
Alexandre Julliard committed
{
        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
HANDLE FILE_CreateDevice( int client_id, DWORD access, LPSECURITY_ATTRIBUTES sa )
    {
        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;
static HANDLE FILE_OpenPipe(LPCWSTR name, DWORD access)
    if (name && (len = strlenW(name)) > MAX_PATH)
    {
        SetLastError( ERROR_FILENAME_EXCED_RANGE );
        return 0;
    }
    {
        req->access = access;
        SetLastError(0);
        wine_server_add_data( req, name, len * sizeof(WCHAR) );
        wine_server_call_err( req );
        ret = reply->handle;
    TRACE("Returned %d\n",ret);
    return ret;
}
Alexandre Julliard's avatar
Alexandre Julliard committed

/*************************************************************************
 * 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
Uwe Bonnes's avatar
Uwe Bonnes committed
 *
 * 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
Uwe Bonnes's avatar
Uwe Bonnes committed
 *
 * 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.
Alexandre Julliard's avatar
Alexandre Julliard committed
 */
HANDLE WINAPI CreateFileW( LPCWSTR filename, DWORD access, DWORD sharing,
                              LPSECURITY_ATTRIBUTES sa, DWORD creation,
                              DWORD attributes, HANDLE template )
Alexandre Julliard's avatar
Alexandre Julliard committed
{
Alexandre Julliard's avatar
Alexandre Julliard committed
    DOS_FULL_NAME full_name;
    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 );
    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);
Alexandre Julliard's avatar
Alexandre Julliard committed

    /* 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 );
Alexandre Julliard's avatar
Alexandre Julliard committed

    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 );
        else if (!DOSFS_GetDevice( filename ))
        {
            ret = DEVICE_Open( filename+4, access, sa );
            goto done;
        }
	else
        	filename+=4; /* fall into DOSFS_Device case below */
    }
Alexandre Julliard's avatar
Alexandre Julliard committed

    /* 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, '?'))
    /* Open a console for CONIN$ or CONOUT$ */
    if (!strcmpiW(filename, coninW))
        ret = FILE_OpenConsole( FALSE, access, sharing, sa );
    if (!strcmpiW(filename, conoutW))
        ret = FILE_OpenConsole( TRUE, access, sharing, sa );

    if (DOSFS_GetDevice( filename ))
Alexandre Julliard's avatar
Alexandre Julliard committed
    {
        TRACE("opening device %s\n", debugstr_w(filename) );
Alexandre Julliard's avatar
Alexandre Julliard committed

        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;
Alexandre Julliard's avatar
Alexandre Julliard committed
    }
Alexandre Julliard's avatar
Alexandre Julliard committed

    /* 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());
Alexandre Julliard's avatar
Alexandre Julliard committed

    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);
Alexandre Julliard's avatar
Alexandre Julliard committed

Alexandre Julliard's avatar
Alexandre Julliard committed

Alexandre Julliard's avatar
Alexandre Julliard committed

/*************************************************************************
 *              CreateFileA              (KERNEL32.@)
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;
Alexandre Julliard's avatar
Alexandre Julliard committed
}


/***********************************************************************
 *           FILE_FillInfo
 *
 * Fill a file information from a struct stat.
 */
static void FILE_FillInfo( struct stat *st, BY_HANDLE_FILE_INFORMATION *info )
Alexandre Julliard's avatar
Alexandre Julliard committed
{
    if (S_ISDIR(st->st_mode))
Alexandre Julliard's avatar
Alexandre Julliard committed
        info->dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
    else
        info->dwFileAttributes = FILE_ATTRIBUTE_ARCHIVE;
Alexandre Julliard's avatar
Alexandre Julliard committed
    if (!(st->st_mode & S_IWUSR))
        info->dwFileAttributes |= FILE_ATTRIBUTE_READONLY;
Alexandre Julliard's avatar
Alexandre Julliard committed

    RtlSecondsSince1970ToTime( st->st_mtime, &info->ftCreationTime );
    RtlSecondsSince1970ToTime( st->st_mtime, &info->ftLastWriteTime );
    RtlSecondsSince1970ToTime( st->st_atime, &info->ftLastAccessTime );
Alexandre Julliard's avatar
Alexandre Julliard committed

    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;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
    info->nNumberOfLinks = st->st_nlink;
    info->nFileIndexHigh = 0;
    info->nFileIndexLow  = st->st_ino;
Alexandre Julliard's avatar
Alexandre Julliard committed
}


/***********************************************************************
 *           FILE_Stat
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
 * Stat a Unix path name. Return TRUE if OK.
Alexandre Julliard's avatar
Alexandre Julliard committed
 */
BOOL FILE_Stat( LPCSTR unixName, BY_HANDLE_FILE_INFORMATION *info )
Alexandre Julliard's avatar
Alexandre Julliard committed
{
Alexandre Julliard's avatar
Alexandre Julliard committed

    if (lstat( unixName, &st ) == -1)
Alexandre Julliard's avatar
Alexandre Julliard committed
    {
        FILE_SetDosError();
Alexandre Julliard's avatar
Alexandre Julliard committed
        return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
    }
    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 */
        {
            FILE_SetDosError();
            return FALSE;
        }
        FILE_FillInfo( &st, info );
        info->dwFileAttributes |= FILE_ATTRIBUTE_SYMLINK;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
    return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
}


/***********************************************************************
 *             GetFileInformationByHandle   (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
 */
Jim Aston's avatar
Jim Aston committed
DWORD WINAPI GetFileInformationByHandle( HANDLE hFile,
Alexandre Julliard's avatar
Alexandre Julliard committed
                                         BY_HANDLE_FILE_INFORMATION *info )
Alexandre Julliard's avatar
Alexandre Julliard committed
{
Alexandre Julliard's avatar
Alexandre Julliard committed
    if (!info) return 0;
    TRACE("%08x\n", 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;
            }
Alexandre Julliard's avatar
Alexandre Julliard committed
}


/**************************************************************************
Patrik Stridvall's avatar
Patrik Stridvall committed
 *           GetFileAttributes   (KERNEL.420)
Alexandre Julliard's avatar
Alexandre Julliard committed
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
DWORD WINAPI GetFileAttributes16( LPCSTR name )
Alexandre Julliard's avatar
Alexandre Julliard committed
{
    return GetFileAttributesA( name );
Alexandre Julliard's avatar
Alexandre Julliard committed
}


/**************************************************************************
 *           GetFileAttributesW   (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
 */
DWORD WINAPI GetFileAttributesW( LPCWSTR name )
Alexandre Julliard's avatar
Alexandre Julliard committed
{
Alexandre Julliard's avatar
Alexandre Julliard committed
    DOS_FULL_NAME full_name;
Alexandre Julliard's avatar
Alexandre Julliard committed
    BY_HANDLE_FILE_INFORMATION info;

    if (name == NULL)
    {
        SetLastError( ERROR_INVALID_PARAMETER );
        return -1;
    }
    if (!DOSFS_GetFullName( name, TRUE, &full_name) )
Alexandre Julliard's avatar
Alexandre Julliard committed
    if (!FILE_Stat( full_name.long_name, &info )) return -1;
Alexandre Julliard's avatar
Alexandre Julliard committed
    return info.dwFileAttributes;
}


/**************************************************************************
 *           GetFileAttributesA   (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
 */
DWORD WINAPI GetFileAttributesA( LPCSTR name )
Alexandre Julliard's avatar
Alexandre Julliard committed
{
    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)
    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);
        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;
Alexandre Julliard's avatar
Alexandre Julliard committed
/***********************************************************************
 *           GetFileSize   (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
 */
Jim Aston's avatar
Jim Aston committed
DWORD WINAPI GetFileSize( HANDLE hFile, LPDWORD filesizehigh )
Alexandre Julliard's avatar
Alexandre Julliard committed
{
Alexandre Julliard's avatar
Alexandre Julliard committed
    BY_HANDLE_FILE_INFORMATION info;
    if (!GetFileInformationByHandle( hFile, &info )) return -1;
Alexandre Julliard's avatar
Alexandre Julliard committed
    if (filesizehigh) *filesizehigh = info.nFileSizeHigh;
    return info.nFileSizeLow;
}
Alexandre Julliard's avatar
Alexandre Julliard committed

Alexandre Julliard's avatar
Alexandre Julliard committed

/***********************************************************************
 *           GetFileTime   (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
 */
Jim Aston's avatar
Jim Aston committed
BOOL WINAPI GetFileTime( HANDLE hFile, FILETIME *lpCreationTime,
Alexandre Julliard's avatar
Alexandre Julliard committed
                           FILETIME *lpLastAccessTime,
                           FILETIME *lpLastWriteTime )
Alexandre Julliard's avatar
Alexandre Julliard committed
{
    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;
Alexandre Julliard's avatar
Alexandre Julliard committed
/***********************************************************************
 *           CompareFileTime   (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
 */
INT WINAPI CompareFileTime( LPFILETIME x, LPFILETIME y )
Alexandre Julliard's avatar
Alexandre Julliard committed
{