file.c 93 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
/*
 * Copyright 1999, 2000 Juergen Schmied
 *
 * 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
16
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17 18
 */

19 20 21
#include "config.h"
#include "wine/port.h"

Juergen Schmied's avatar
Juergen Schmied committed
22 23
#include <stdlib.h>
#include <string.h>
24 25
#include <stdio.h>
#include <errno.h>
26
#include <assert.h>
27 28 29
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
30 31 32
#ifdef HAVE_SYS_ERRNO_H
#include <sys/errno.h>
#endif
33 34 35
#ifdef HAVE_LINUX_MAJOR_H
# include <linux/major.h>
#endif
36 37 38 39 40 41
#ifdef HAVE_SYS_STATVFS_H
# include <sys/statvfs.h>
#endif
#ifdef HAVE_SYS_PARAM_H
# include <sys/param.h>
#endif
42 43 44
#ifdef HAVE_SYS_TIME_H
# include <sys/time.h>
#endif
45 46 47
#ifdef HAVE_SYS_IOCTL_H
#include <sys/ioctl.h>
#endif
48 49 50
#ifdef HAVE_SYS_FILIO_H
# include <sys/filio.h>
#endif
51 52 53 54 55 56 57 58 59
#ifdef HAVE_POLL_H
#include <poll.h>
#endif
#ifdef HAVE_SYS_POLL_H
#include <sys/poll.h>
#endif
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
60 61 62
#ifdef HAVE_UTIME_H
# include <utime.h>
#endif
63
#ifdef HAVE_SYS_VFS_H
64
# include <sys/vfs.h>
65 66 67 68 69 70
#endif
#ifdef HAVE_SYS_MOUNT_H
# include <sys/mount.h>
#endif
#ifdef HAVE_SYS_STATFS_H
# include <sys/statfs.h>
71
#endif
72 73 74

#define NONAMELESSUNION
#define NONAMELESSSTRUCT
75 76
#include "ntstatus.h"
#define WIN32_NO_STATUS
77
#include "wine/unicode.h"
78
#include "wine/debug.h"
79
#include "wine/server.h"
80
#include "ntdll_misc.h"
Juergen Schmied's avatar
Juergen Schmied committed
81

82
#include "winternl.h"
83
#include "winioctl.h"
84
#include "ddk/ntddser.h"
Juergen Schmied's avatar
Juergen Schmied committed
85

86
WINE_DEFAULT_DEBUG_CHANNEL(ntdll);
87

88 89 90 91 92
mode_t FILE_umask = 0;

#define SECSPERDAY         86400
#define SECS_1601_TO_1970  ((369 * 365 + 89) * (ULONGLONG)SECSPERDAY)

Juergen Schmied's avatar
Juergen Schmied committed
93
/**************************************************************************
94
 *                 NtOpenFile				[NTDLL.@]
Patrik Stridvall's avatar
Patrik Stridvall committed
95
 *                 ZwOpenFile				[NTDLL.@]
Jon Griffiths's avatar
Jon Griffiths committed
96 97 98 99
 *
 * Open a file.
 *
 * PARAMS
100 101
 *  handle    [O] Variable that receives the file handle on return
 *  access    [I] Access desired by the caller to the file
102
 *  attr      [I] Structure describing the file to be opened
103 104 105
 *  io        [O] Receives details about the result of the operation
 *  sharing   [I] Type of shared access the caller requires
 *  options   [I] Options for the file open
Jon Griffiths's avatar
Jon Griffiths committed
106 107 108 109
 *
 * RETURNS
 *  Success: 0. FileHandle and IoStatusBlock are updated.
 *  Failure: An NTSTATUS error code describing the error.
Juergen Schmied's avatar
Juergen Schmied committed
110
 */
111 112 113
NTSTATUS WINAPI NtOpenFile( PHANDLE handle, ACCESS_MASK access,
                            POBJECT_ATTRIBUTES attr, PIO_STATUS_BLOCK io,
                            ULONG sharing, ULONG options )
Juergen Schmied's avatar
Juergen Schmied committed
114
{
115 116
    return NtCreateFile( handle, access, attr, io, NULL, 0,
                         sharing, FILE_OPEN, options, NULL, 0 );
Juergen Schmied's avatar
Juergen Schmied committed
117 118 119
}

/**************************************************************************
120
 *		NtCreateFile				[NTDLL.@]
Patrik Stridvall's avatar
Patrik Stridvall committed
121
 *		ZwCreateFile				[NTDLL.@]
Jon Griffiths's avatar
Jon Griffiths committed
122 123 124 125 126
 *
 * Either create a new file or directory, or open an existing file, device,
 * directory or volume.
 *
 * PARAMS
127 128 129 130 131 132 133 134 135
 *	handle       [O] Points to a variable which receives the file handle on return
 *	access       [I] Desired access to the file
 *	attr         [I] Structure describing the file
 *	io           [O] Receives information about the operation on return
 *	alloc_size   [I] Initial size of the file in bytes
 *	attributes   [I] Attributes to create the file with
 *	sharing      [I] Type of shared access the caller would like to the file
 *	disposition  [I] Specifies what to do, depending on whether the file already exists
 *	options      [I] Options for creating a new file
136
 *	ea_buffer    [I] Pointer to an extended attributes buffer
137
 *	ea_length    [I] Length of ea_buffer
Jon Griffiths's avatar
Jon Griffiths committed
138 139
 *
 * RETURNS
140
 *  Success: 0. handle and io are updated.
Jon Griffiths's avatar
Jon Griffiths committed
141
 *  Failure: An NTSTATUS error code describing the error.
Juergen Schmied's avatar
Juergen Schmied committed
142
 */
143 144 145 146
NTSTATUS WINAPI NtCreateFile( PHANDLE handle, ACCESS_MASK access, POBJECT_ATTRIBUTES attr,
                              PIO_STATUS_BLOCK io, PLARGE_INTEGER alloc_size,
                              ULONG attributes, ULONG sharing, ULONG disposition,
                              ULONG options, PVOID ea_buffer, ULONG ea_length )
Juergen Schmied's avatar
Juergen Schmied committed
147
{
148
    ANSI_STRING unix_name;
149
    int created = FALSE;
150

151 152
    TRACE("handle=%p access=%08x name=%s objattr=%08x root=%p sec=%p io=%p alloc_size=%p\n"
          "attr=%08x sharing=%08x disp=%d options=%08x ea=%p.0x%08x\n",
153 154 155 156
          handle, access, debugstr_us(attr->ObjectName), attr->Attributes,
          attr->RootDirectory, attr->SecurityDescriptor, io, alloc_size,
          attributes, sharing, disposition, options, ea_buffer, ea_length );

157 158
    if (!attr || !attr->ObjectName) return STATUS_INVALID_PARAMETER;

159 160
    if (alloc_size) FIXME( "alloc_size not supported\n" );

161 162 163
    if (attr->RootDirectory ||
        (io->u.Status = wine_nt_to_unix_file_name( attr->ObjectName, &unix_name, disposition,
                                 !(attr->Attributes & OBJ_CASE_INSENSITIVE) )) == STATUS_BAD_DEVICE_TYPE)
164 165 166 167 168
    {
        SERVER_START_REQ( open_file_object )
        {
            req->access     = access;
            req->attributes = attr->Attributes;
169
            req->rootdir    = wine_server_obj_handle( attr->RootDirectory );
170
            req->sharing    = sharing;
171
            req->options    = options;
172 173
            wine_server_add_data( req, attr->ObjectName->Buffer, attr->ObjectName->Length );
            io->u.Status = wine_server_call( req );
174
            *handle = wine_server_ptr_handle( reply->handle );
175 176
        }
        SERVER_END_REQ;
177
        if (io->u.Status == STATUS_SUCCESS) io->Information = FILE_OPENED;
178 179 180
        return io->u.Status;
    }

181 182
    if (io->u.Status == STATUS_NO_SUCH_FILE &&
        disposition != FILE_OPEN && disposition != FILE_OVERWRITE)
183 184 185 186 187 188
    {
        created = TRUE;
        io->u.Status = STATUS_SUCCESS;
    }

    if (io->u.Status == STATUS_SUCCESS)
189
    {
190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205
        struct security_descriptor *sd = NULL;
        struct object_attributes objattr;

        objattr.rootdir = 0;
        objattr.sd_len = 0;
        objattr.name_len = 0;
        if (attr)
        {
            io->u.Status = NTDLL_create_struct_sd( attr->SecurityDescriptor, &sd, &objattr.sd_len );
            if (io->u.Status != STATUS_SUCCESS)
            {
                RtlFreeAnsiString( &unix_name );
                return io->u.Status;
            }
        }

206 207 208
        SERVER_START_REQ( create_file )
        {
            req->access     = access;
209
            req->attributes = attr->Attributes;
210 211 212 213
            req->sharing    = sharing;
            req->create     = disposition;
            req->options    = options;
            req->attrs      = attributes;
214 215
            wine_server_add_data( req, &objattr, sizeof(objattr) );
            if (objattr.sd_len) wine_server_add_data( req, sd, objattr.sd_len );
216 217
            wine_server_add_data( req, unix_name.Buffer, unix_name.Length );
            io->u.Status = wine_server_call( req );
218
            *handle = wine_server_ptr_handle( reply->handle );
219 220
        }
        SERVER_END_REQ;
221
        NTDLL_free_struct_sd( sd );
222 223
        RtlFreeAnsiString( &unix_name );
    }
224
    else WARN("%s not found (%x)\n", debugstr_us(attr->ObjectName), io->u.Status );
225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247

    if (io->u.Status == STATUS_SUCCESS)
    {
        if (created) io->Information = FILE_CREATED;
        else switch(disposition)
        {
        case FILE_SUPERSEDE:
            io->Information = FILE_SUPERSEDED;
            break;
        case FILE_CREATE:
            io->Information = FILE_CREATED;
            break;
        case FILE_OPEN:
        case FILE_OPEN_IF:
            io->Information = FILE_OPENED;
            break;
        case FILE_OVERWRITE:
        case FILE_OVERWRITE_IF:
            io->Information = FILE_OVERWRITTEN;
            break;
        }
    }

248
    return io->u.Status;
Juergen Schmied's avatar
Juergen Schmied committed
249 250
}

251 252 253 254
/***********************************************************************
 *                  Asynchronous file I/O                              *
 */

255
struct async_fileio
256
{
257
    HANDLE              handle;
258 259 260 261 262 263 264
    PIO_APC_ROUTINE     apc;
    void               *apc_arg;
};

typedef struct
{
    struct async_fileio io;
265
    char*               buffer;
266
    unsigned int        already;
267 268
    unsigned int        count;
    BOOL                avail_mode;
269
} async_fileio_read;
270

271
typedef struct
272
{
273
    struct async_fileio io;
274 275 276 277
    const char         *buffer;
    unsigned int        already;
    unsigned int        count;
} async_fileio_write;
278

279

280 281 282 283 284 285 286 287
/* callback for file I/O user APC */
static void WINAPI fileio_apc( void *arg, IO_STATUS_BLOCK *io, ULONG reserved )
{
    struct async_fileio *async = arg;
    if (async->apc) async->apc( async->apc_arg, io, reserved );
    RtlFreeHeap( GetProcessHeap(), 0, async );
}

288 289 290 291 292 293
/***********************************************************************
 *           FILE_GetNtStatus(void)
 *
 * Retrieve the Nt Status code from errno.
 * Try to be consistent with FILE_SetDosError().
 */
294
NTSTATUS FILE_GetNtStatus(void)
295 296 297 298 299 300
{
    int err = errno;

    TRACE( "errno = %d\n", errno );
    switch (err)
    {
301 302
    case EAGAIN:    return STATUS_SHARING_VIOLATION;
    case EBADF:     return STATUS_INVALID_HANDLE;
303
    case EBUSY:     return STATUS_DEVICE_BUSY;
304
    case ENOSPC:    return STATUS_DISK_FULL;
305 306
    case EPERM:
    case EROFS:
307 308 309 310
    case EACCES:    return STATUS_ACCESS_DENIED;
    case ENOTDIR:   return STATUS_OBJECT_PATH_NOT_FOUND;
    case ENOENT:    return STATUS_OBJECT_NAME_NOT_FOUND;
    case EISDIR:    return STATUS_FILE_IS_A_DIRECTORY;
311
    case EMFILE:
312 313 314
    case ENFILE:    return STATUS_TOO_MANY_OPENED_FILES;
    case EINVAL:    return STATUS_INVALID_PARAMETER;
    case ENOTEMPTY: return STATUS_DIRECTORY_NOT_EMPTY;
315
    case EPIPE:     return STATUS_PIPE_DISCONNECTED;
316
    case EIO:       return STATUS_DEVICE_NOT_READY;
317 318 319
#ifdef ENOMEDIUM
    case ENOMEDIUM: return STATUS_NO_MEDIA_IN_DEVICE;
#endif
320
    case ENXIO:     return STATUS_NO_SUCH_DEVICE;
321 322
    case ENOTTY:
    case EOPNOTSUPP:return STATUS_NOT_SUPPORTED;
323
    case ECONNRESET:return STATUS_PIPE_DISCONNECTED;
324
    case EFAULT:    return STATUS_ACCESS_VIOLATION;
325
    case ESPIPE:    return STATUS_ILLEGAL_FUNCTION;
326 327
    case ENOEXEC:   /* ?? */
    case EEXIST:    /* ?? */
328 329
    default:
        FIXME( "Converting errno %d to STATUS_UNSUCCESSFUL\n", err );
330
        return STATUS_UNSUCCESSFUL;
331 332 333 334 335 336
    }
}

/***********************************************************************
 *             FILE_AsyncReadService      (INTERNAL)
 */
337
static NTSTATUS FILE_AsyncReadService(void *user, PIO_STATUS_BLOCK iosb, NTSTATUS status, void **apc)
338
{
339
    async_fileio_read *fileio = user;
340
    int fd, needs_close, result;
341

342
    switch (status)
343
    {
344 345
    case STATUS_ALERTED: /* got some new data */
        /* check to see if the data is ready (non-blocking) */
346
        if ((status = server_get_unix_fd( fileio->io.handle, FILE_READ_DATA, &fd,
347
                                          &needs_close, NULL, NULL )))
348
            break;
349

350
        result = read(fd, &fileio->buffer[fileio->already], fileio->count - fileio->already);
351
        if (needs_close) close( fd );
352

353 354 355
        if (result < 0)
        {
            if (errno == EAGAIN || errno == EINTR)
356
                status = STATUS_PENDING;
357
            else /* check to see if the transfer is complete */
358
                status = FILE_GetNtStatus();
359 360 361
        }
        else if (result == 0)
        {
362
            status = fileio->already ? STATUS_SUCCESS : STATUS_PIPE_BROKEN;
363 364 365
        }
        else
        {
366 367
            fileio->already += result;
            if (fileio->already >= fileio->count || fileio->avail_mode)
368
                status = STATUS_SUCCESS;
369 370 371 372 373 374
            else
            {
                /* if we only have to read the available data, and none is available,
                 * simply cancel the request. If data was available, it has been read
                 * while in by previous call (NtDelayExecution)
                 */
375
                status = (fileio->avail_mode) ? STATUS_SUCCESS : STATUS_PENDING;
376 377 378
            }
        }
        break;
379 380 381 382

    case STATUS_TIMEOUT:
    case STATUS_IO_TIMEOUT:
        if (fileio->already) status = STATUS_SUCCESS;
383
        break;
384
    }
385 386 387
    if (status != STATUS_PENDING)
    {
        iosb->u.Status = status;
388 389
        iosb->Information = fileio->already;
        *apc = fileio_apc;
390
    }
391
    return status;
392 393
}

394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432
struct io_timeouts
{
    int interval;   /* max interval between two bytes */
    int total;      /* total timeout for the whole operation */
    int end_time;   /* absolute time of end of operation */
};

/* retrieve the I/O timeouts to use for a given handle */
static NTSTATUS get_io_timeouts( HANDLE handle, enum server_fd_type type, ULONG count, BOOL is_read,
                                 struct io_timeouts *timeouts )
{
    NTSTATUS status = STATUS_SUCCESS;

    timeouts->interval = timeouts->total = -1;

    switch(type)
    {
    case FD_TYPE_SERIAL:
        {
            /* GetCommTimeouts */
            SERIAL_TIMEOUTS st;
            IO_STATUS_BLOCK io;

            status = NtDeviceIoControlFile( handle, NULL, NULL, NULL, &io,
                                            IOCTL_SERIAL_GET_TIMEOUTS, NULL, 0, &st, sizeof(st) );
            if (status) break;

            if (is_read)
            {
                if (st.ReadIntervalTimeout)
                    timeouts->interval = st.ReadIntervalTimeout;

                if (st.ReadTotalTimeoutMultiplier || st.ReadTotalTimeoutConstant)
                {
                    timeouts->total = st.ReadTotalTimeoutConstant;
                    if (st.ReadTotalTimeoutMultiplier != MAXDWORD)
                        timeouts->total += count * st.ReadTotalTimeoutMultiplier;
                }
                else if (st.ReadIntervalTimeout == MAXDWORD)
433
                    timeouts->interval = timeouts->total = 0;
434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451
            }
            else  /* write */
            {
                if (st.WriteTotalTimeoutMultiplier || st.WriteTotalTimeoutConstant)
                {
                    timeouts->total = st.WriteTotalTimeoutConstant;
                    if (st.WriteTotalTimeoutMultiplier != MAXDWORD)
                        timeouts->total += count * st.WriteTotalTimeoutMultiplier;
                }
            }
        }
        break;
    case FD_TYPE_MAILSLOT:
        if (is_read)
        {
            timeouts->interval = 0;  /* return as soon as we got something */
            SERVER_START_REQ( set_mailslot_info )
            {
452
                req->handle = wine_server_obj_handle( handle );
453
                req->flags = 0;
454 455 456
                if (!(status = wine_server_call( req )) &&
                    reply->read_timeout != TIMEOUT_INFINITE)
                    timeouts->total = reply->read_timeout / -10000;
457 458 459 460 461 462
            }
            SERVER_END_REQ;
        }
        break;
    case FD_TYPE_SOCKET:
    case FD_TYPE_PIPE:
463
    case FD_TYPE_CHAR:
464 465 466 467 468 469 470 471 472 473 474
        if (is_read) timeouts->interval = 0;  /* return as soon as we got something */
        break;
    default:
        break;
    }
    if (timeouts->total != -1) timeouts->end_time = NtGetTickCount() + timeouts->total;
    return STATUS_SUCCESS;
}


/* retrieve the timeout for the next wait, in milliseconds */
475
static inline int get_next_io_timeout( const struct io_timeouts *timeouts, ULONG already )
476 477 478 479 480 481 482 483 484 485 486 487 488 489 490
{
    int ret = -1;

    if (timeouts->total != -1)
    {
        ret = timeouts->end_time - NtGetTickCount();
        if (ret < 0) ret = 0;
    }
    if (already && timeouts->interval != -1)
    {
        if (ret == -1 || ret > timeouts->interval) ret = timeouts->interval;
    }
    return ret;
}

491

492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515
/* retrieve the avail_mode flag for async reads */
static NTSTATUS get_io_avail_mode( HANDLE handle, enum server_fd_type type, BOOL *avail_mode )
{
    NTSTATUS status = STATUS_SUCCESS;

    switch(type)
    {
    case FD_TYPE_SERIAL:
        {
            /* GetCommTimeouts */
            SERIAL_TIMEOUTS st;
            IO_STATUS_BLOCK io;

            status = NtDeviceIoControlFile( handle, NULL, NULL, NULL, &io,
                                            IOCTL_SERIAL_GET_TIMEOUTS, NULL, 0, &st, sizeof(st) );
            if (status) break;
            *avail_mode = (!st.ReadTotalTimeoutMultiplier &&
                           !st.ReadTotalTimeoutConstant &&
                           st.ReadIntervalTimeout == MAXDWORD);
        }
        break;
    case FD_TYPE_MAILSLOT:
    case FD_TYPE_SOCKET:
    case FD_TYPE_PIPE:
516
    case FD_TYPE_CHAR:
517 518 519 520 521 522 523 524 525 526
        *avail_mode = TRUE;
        break;
    default:
        *avail_mode = FALSE;
        break;
    }
    return status;
}


Juergen Schmied's avatar
Juergen Schmied committed
527
/******************************************************************************
528
 *  NtReadFile					[NTDLL.@]
Patrik Stridvall's avatar
Patrik Stridvall committed
529
 *  ZwReadFile					[NTDLL.@]
Juergen Schmied's avatar
Juergen Schmied committed
530
 *
Jon Griffiths's avatar
Jon Griffiths committed
531
 * Read from an open file handle.
532
 *
Jon Griffiths's avatar
Jon Griffiths committed
533 534 535 536 537 538 539 540 541 542 543 544 545 546 547
 * PARAMS
 *  FileHandle    [I] Handle returned from ZwOpenFile() or ZwCreateFile()
 *  Event         [I] Event to signal upon completion (or NULL)
 *  ApcRoutine    [I] Callback to call upon completion (or NULL)
 *  ApcContext    [I] Context for ApcRoutine (or NULL)
 *  IoStatusBlock [O] Receives information about the operation on return
 *  Buffer        [O] Destination for the data read
 *  Length        [I] Size of Buffer
 *  ByteOffset    [O] Destination for the new file pointer position (or NULL)
 *  Key           [O] Function unknown (may be NULL)
 *
 * RETURNS
 *  Success: 0. IoStatusBlock is updated, and the Information member contains
 *           The number of bytes read.
 *  Failure: An NTSTATUS error code describing the error.
Juergen Schmied's avatar
Juergen Schmied committed
548
 */
Jon Griffiths's avatar
Jon Griffiths committed
549 550
NTSTATUS WINAPI NtReadFile(HANDLE hFile, HANDLE hEvent,
                           PIO_APC_ROUTINE apc, void* apc_user,
551 552
                           PIO_STATUS_BLOCK io_status, void* buffer, ULONG length,
                           PLARGE_INTEGER offset, PULONG key)
553
{
554 555
    int result, unix_handle, needs_close, timeout_init_done = 0;
    unsigned int options;
556
    struct io_timeouts timeouts;
557
    NTSTATUS status;
558
    ULONG total = 0;
559
    enum server_fd_type type;
560
    ULONG_PTR cvalue = apc ? 0 : (ULONG_PTR)apc_user;
561

562
    TRACE("(%p,%p,%p,%p,%p,%p,0x%08x,%p,%p),partial stub!\n",
563 564
          hFile,hEvent,apc,apc_user,io_status,buffer,length,offset,key);

565 566
    if (!io_status) return STATUS_ACCESS_VIOLATION;

567
    status = server_get_unix_fd( hFile, FILE_READ_DATA, &unix_handle,
568
                                 &needs_close, &type, &options );
569
    if (status) return status;
570

571 572 573 574 575 576
    if (!virtual_check_buffer_for_write( buffer, length ))
    {
        status = STATUS_ACCESS_VIOLATION;
        goto done;
    }

577
    if (type == FD_TYPE_FILE && offset && offset->QuadPart != (LONGLONG)-2 /* FILE_USE_FILE_POINTER_POSITION */ )
578 579
    {
        /* async I/O doesn't make sense on regular files */
580
        while ((result = pread( unix_handle, buffer, length, offset->QuadPart )) == -1)
581
        {
582
            if (errno != EINTR)
583
            {
584
                status = FILE_GetNtStatus();
585 586
                goto done;
            }
587
        }
588 589
        if (options & (FILE_SYNCHRONOUS_IO_ALERT | FILE_SYNCHRONOUS_IO_NONALERT))
            /* update file pointer position */
590 591 592 593 594 595 596 597 598 599 600 601 602
            lseek( unix_handle, offset->QuadPart + result, SEEK_SET );

        total = result;
        status = total ? STATUS_SUCCESS : STATUS_END_OF_FILE;
        goto done;
    }

    for (;;)
    {
        if ((result = read( unix_handle, (char *)buffer + total, length - total )) >= 0)
        {
            total += result;
            if (!result || total == length)
603
            {
604
                if (total)
605
                {
606
                    status = STATUS_SUCCESS;
607 608 609 610 611 612 613 614 615 616 617 618 619 620
                    goto done;
                }
                switch (type)
                {
                case FD_TYPE_FILE:
                case FD_TYPE_CHAR:
                    status = STATUS_END_OF_FILE;
                    goto done;
                case FD_TYPE_SERIAL:
                    break;
                default:
                    status = STATUS_PIPE_BROKEN;
                    goto done;
                }
621
            }
622
            else if (type == FD_TYPE_FILE) continue;  /* no async I/O on regular files */
623
        }
624
        else if (errno != EAGAIN)
625
        {
626
            if (errno == EINTR) continue;
627 628
            if (!total) status = FILE_GetNtStatus();
            goto done;
629
        }
630

631
        if (!(options & (FILE_SYNCHRONOUS_IO_ALERT | FILE_SYNCHRONOUS_IO_NONALERT)))
632
        {
633
            async_fileio_read *fileio;
634
            BOOL avail_mode;
Jon Griffiths's avatar
Jon Griffiths committed
635

636
            if ((status = get_io_avail_mode( hFile, type, &avail_mode )))
637
                goto err;
638
            if (total && avail_mode)
639 640 641 642
            {
                status = STATUS_SUCCESS;
                goto done;
            }
643

644
            if (!(fileio = RtlAllocateHeap(GetProcessHeap(), 0, sizeof(*fileio))))
645 646
            {
                status = STATUS_NO_MEMORY;
647
                goto err;
648
            }
649 650 651
            fileio->io.handle  = hFile;
            fileio->io.apc     = apc;
            fileio->io.apc_arg = apc_user;
652 653 654
            fileio->already = total;
            fileio->count = length;
            fileio->buffer = buffer;
655
            fileio->avail_mode = avail_mode;
656 657 658 659 660

            SERVER_START_REQ( register_async )
            {
                req->type   = ASYNC_TYPE_READ;
                req->count  = length;
661 662
                req->async.handle   = wine_server_obj_handle( hFile );
                req->async.event    = wine_server_obj_handle( hEvent );
663 664 665
                req->async.callback = wine_server_client_ptr( FILE_AsyncReadService );
                req->async.iosb     = wine_server_client_ptr( io_status );
                req->async.arg      = wine_server_client_ptr( fileio );
666
                req->async.cvalue   = cvalue;
667 668 669 670 671
                status = wine_server_call( req );
            }
            SERVER_END_REQ;

            if (status != STATUS_PENDING) RtlFreeHeap( GetProcessHeap(), 0, fileio );
672
            goto err;
673
        }
674
        else  /* synchronous read, wait for the fd to become ready */
675
        {
676 677 678 679 680 681 682
            struct pollfd pfd;
            int ret, timeout;

            if (!timeout_init_done)
            {
                timeout_init_done = 1;
                if ((status = get_io_timeouts( hFile, type, length, TRUE, &timeouts )))
683
                    goto err;
684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704
                if (hEvent) NtResetEvent( hEvent, NULL );
            }
            timeout = get_next_io_timeout( &timeouts, total );

            pfd.fd = unix_handle;
            pfd.events = POLLIN;

            if (!timeout || !(ret = poll( &pfd, 1, timeout )))
            {
                if (total)  /* return with what we got so far */
                    status = STATUS_SUCCESS;
                else
                    status = (type == FD_TYPE_MAILSLOT) ? STATUS_IO_TIMEOUT : STATUS_TIMEOUT;
                goto done;
            }
            if (ret == -1 && errno != EINTR)
            {
                status = FILE_GetNtStatus();
                goto done;
            }
            /* will now restart the read */
705
        }
706
    }
707

708
done:
709 710 711
    if (cvalue) NTDLL_AddCompletion( hFile, cvalue, status, total );

err:
712 713
    if (needs_close) close( unix_handle );
    if (status == STATUS_SUCCESS)
714
    {
715 716 717 718 719 720
        io_status->u.Status = status;
        io_status->Information = total;
        TRACE("= SUCCESS (%u)\n", total);
        if (hEvent) NtSetEvent( hEvent, NULL );
        if (apc) NtQueueApcThread( GetCurrentThread(), (PNTAPCFUNC)apc,
                                   (ULONG_PTR)apc_user, (ULONG_PTR)io_status, 0 );
721
    }
722
    else
723
    {
724 725
        TRACE("= 0x%08x\n", status);
        if (status != STATUS_PENDING && hEvent) NtResetEvent( hEvent, NULL );
726
    }
727
    return status;
728
}
729

730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814

/******************************************************************************
 *  NtReadFileScatter   [NTDLL.@]
 *  ZwReadFileScatter   [NTDLL.@]
 */
NTSTATUS WINAPI NtReadFileScatter( HANDLE file, HANDLE event, PIO_APC_ROUTINE apc, void *apc_user,
                                   PIO_STATUS_BLOCK io_status, FILE_SEGMENT_ELEMENT *segments,
                                   ULONG length, PLARGE_INTEGER offset, PULONG key )
{
    size_t page_size = getpagesize();
    int result, unix_handle, needs_close;
    unsigned int options;
    NTSTATUS status;
    ULONG pos = 0, total = 0;
    enum server_fd_type type;
    ULONG_PTR cvalue = apc ? 0 : (ULONG_PTR)apc_user;

    TRACE( "(%p,%p,%p,%p,%p,%p,0x%08x,%p,%p),partial stub!\n",
           file, event, apc, apc_user, io_status, segments, length, offset, key);

    if (length % page_size) return STATUS_INVALID_PARAMETER;
    if (!io_status) return STATUS_ACCESS_VIOLATION;

    status = server_get_unix_fd( file, FILE_READ_DATA, &unix_handle,
                                 &needs_close, &type, &options );
    if (status) return status;

    if ((type != FD_TYPE_FILE) ||
        (options & (FILE_SYNCHRONOUS_IO_ALERT | FILE_SYNCHRONOUS_IO_NONALERT)) ||
        !(options & FILE_NO_INTERMEDIATE_BUFFERING))
    {
        status = STATUS_INVALID_PARAMETER;
        goto error;
    }

    while (length)
    {
        if (offset && offset->QuadPart != (LONGLONG)-2 /* FILE_USE_FILE_POINTER_POSITION */)
            result = pread( unix_handle, (char *)segments->Buffer + pos,
                            page_size - pos, offset->QuadPart + total );
        else
            result = read( unix_handle, (char *)segments->Buffer + pos, page_size - pos );

        if (result == -1)
        {
            if (errno == EINTR) continue;
            status = FILE_GetNtStatus();
            break;
        }
        if (!result)
        {
            status = STATUS_END_OF_FILE;
            break;
        }
        total += result;
        length -= result;
        if ((pos += result) == page_size)
        {
            pos = 0;
            segments++;
        }
    }

    if (cvalue) NTDLL_AddCompletion( file, cvalue, status, total );

 error:
    if (needs_close) close( unix_handle );
    if (status == STATUS_SUCCESS)
    {
        io_status->u.Status = status;
        io_status->Information = total;
        TRACE("= SUCCESS (%u)\n", total);
        if (event) NtSetEvent( event, NULL );
        if (apc) NtQueueApcThread( GetCurrentThread(), (PNTAPCFUNC)apc,
                                   (ULONG_PTR)apc_user, (ULONG_PTR)io_status, 0 );
    }
    else
    {
        TRACE("= 0x%08x\n", status);
        if (status != STATUS_PENDING && event) NtResetEvent( event, NULL );
    }
    return status;
}


815 816 817
/***********************************************************************
 *             FILE_AsyncWriteService      (INTERNAL)
 */
818
static NTSTATUS FILE_AsyncWriteService(void *user, IO_STATUS_BLOCK *iosb, NTSTATUS status, void **apc)
819
{
820
    async_fileio_write *fileio = user;
821
    int result, fd, needs_close;
822
    enum server_fd_type type;
823

824
    switch (status)
825
    {
826 827
    case STATUS_ALERTED:
        /* write some data (non-blocking) */
828
        if ((status = server_get_unix_fd( fileio->io.handle, FILE_WRITE_DATA, &fd,
829
                                          &needs_close, &type, NULL )))
830
            break;
831

832 833 834 835 836
        if (!fileio->count && (type == FD_TYPE_MAILSLOT || type == FD_TYPE_PIPE || type == FD_TYPE_SOCKET))
            result = send( fd, fileio->buffer, 0, 0 );
        else
            result = write( fd, &fileio->buffer[fileio->already], fileio->count - fileio->already );

837
        if (needs_close) close( fd );
838

839 840
        if (result < 0)
        {
841 842
            if (errno == EAGAIN || errno == EINTR) status = STATUS_PENDING;
            else status = FILE_GetNtStatus();
843 844 845
        }
        else
        {
846 847
            fileio->already += result;
            status = (fileio->already < fileio->count) ? STATUS_PENDING : STATUS_SUCCESS;
848 849
        }
        break;
850 851 852 853

    case STATUS_TIMEOUT:
    case STATUS_IO_TIMEOUT:
        if (fileio->already) status = STATUS_SUCCESS;
854
        break;
855
    }
856 857 858
    if (status != STATUS_PENDING)
    {
        iosb->u.Status = status;
859 860
        iosb->Information = fileio->already;
        *apc = fileio_apc;
861
    }
862
    return status;
863 864 865 866 867 868
}

/******************************************************************************
 *  NtWriteFile					[NTDLL.@]
 *  ZwWriteFile					[NTDLL.@]
 *
Jon Griffiths's avatar
Jon Griffiths committed
869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885
 * Write to an open file handle.
 *
 * PARAMS
 *  FileHandle    [I] Handle returned from ZwOpenFile() or ZwCreateFile()
 *  Event         [I] Event to signal upon completion (or NULL)
 *  ApcRoutine    [I] Callback to call upon completion (or NULL)
 *  ApcContext    [I] Context for ApcRoutine (or NULL)
 *  IoStatusBlock [O] Receives information about the operation on return
 *  Buffer        [I] Source for the data to write
 *  Length        [I] Size of Buffer
 *  ByteOffset    [O] Destination for the new file pointer position (or NULL)
 *  Key           [O] Function unknown (may be NULL)
 *
 * RETURNS
 *  Success: 0. IoStatusBlock is updated, and the Information member contains
 *           The number of bytes written.
 *  Failure: An NTSTATUS error code describing the error.
886
 */
887 888 889 890 891
NTSTATUS WINAPI NtWriteFile(HANDLE hFile, HANDLE hEvent,
                            PIO_APC_ROUTINE apc, void* apc_user,
                            PIO_STATUS_BLOCK io_status, 
                            const void* buffer, ULONG length,
                            PLARGE_INTEGER offset, PULONG key)
Juergen Schmied's avatar
Juergen Schmied committed
892
{
893 894
    int result, unix_handle, needs_close, timeout_init_done = 0;
    unsigned int options;
895
    struct io_timeouts timeouts;
896
    NTSTATUS status;
897
    ULONG total = 0;
898
    enum server_fd_type type;
899
    ULONG_PTR cvalue = apc ? 0 : (ULONG_PTR)apc_user;
900

901
    TRACE("(%p,%p,%p,%p,%p,%p,0x%08x,%p,%p)!\n",
902 903
          hFile,hEvent,apc,apc_user,io_status,buffer,length,offset,key);

904 905
    if (!io_status) return STATUS_ACCESS_VIOLATION;

906
    status = server_get_unix_fd( hFile, FILE_WRITE_DATA, &unix_handle,
907
                                 &needs_close, &type, &options );
908
    if (status) return status;
909

910 911 912 913 914 915
    if (!virtual_check_buffer_for_read( buffer, length ))
    {
        status = STATUS_INVALID_USER_BUFFER;
        goto done;
    }

916
    if (type == FD_TYPE_FILE && offset && offset->QuadPart != (LONGLONG)-2 /* FILE_USE_FILE_POINTER_POSITION */ )
917
    {
918 919
        /* async I/O doesn't make sense on regular files */
        while ((result = pwrite( unix_handle, buffer, length, offset->QuadPart )) == -1)
920
        {
921
            if (errno != EINTR)
922
            {
923 924
                if (errno == EFAULT) status = STATUS_INVALID_USER_BUFFER;
                else status = FILE_GetNtStatus();
925 926
                goto done;
            }
927 928
        }

929 930
        if (options & (FILE_SYNCHRONOUS_IO_ALERT | FILE_SYNCHRONOUS_IO_NONALERT))
            /* update file pointer position */
931 932 933 934 935 936 937 938 939
            lseek( unix_handle, offset->QuadPart + result, SEEK_SET );

        total = result;
        status = STATUS_SUCCESS;
        goto done;
    }

    for (;;)
    {
940 941 942 943 944 945 946
        /* zero-length writes on sockets may not work with plain write(2) */
        if (!length && (type == FD_TYPE_MAILSLOT || type == FD_TYPE_PIPE || type == FD_TYPE_SOCKET))
            result = send( unix_handle, buffer, 0, 0 );
        else
            result = write( unix_handle, (const char *)buffer + total, length - total );

        if (result >= 0)
947 948 949
        {
            total += result;
            if (total == length)
950
            {
951
                status = STATUS_SUCCESS;
952 953
                goto done;
            }
954
            if (type == FD_TYPE_FILE) continue;  /* no async I/O on regular files */
955
        }
956
        else if (errno != EAGAIN)
957
        {
958
            if (errno == EINTR) continue;
959
            if (!total)
960
            {
961 962
                if (errno == EFAULT) status = STATUS_INVALID_USER_BUFFER;
                else status = FILE_GetNtStatus();
963
            }
964
            goto done;
965
        }
966

967
        if (!(options & (FILE_SYNCHRONOUS_IO_ALERT | FILE_SYNCHRONOUS_IO_NONALERT)))
968
        {
969
            async_fileio_write *fileio;
970

971
            if (!(fileio = RtlAllocateHeap(GetProcessHeap(), 0, sizeof(*fileio))))
972 973
            {
                status = STATUS_NO_MEMORY;
974
                goto err;
975
            }
976 977 978
            fileio->io.handle  = hFile;
            fileio->io.apc     = apc;
            fileio->io.apc_arg = apc_user;
979 980
            fileio->already = total;
            fileio->count = length;
981 982 983 984 985 986
            fileio->buffer = buffer;

            SERVER_START_REQ( register_async )
            {
                req->type   = ASYNC_TYPE_WRITE;
                req->count  = length;
987 988
                req->async.handle   = wine_server_obj_handle( hFile );
                req->async.event    = wine_server_obj_handle( hEvent );
989 990 991
                req->async.callback = wine_server_client_ptr( FILE_AsyncWriteService );
                req->async.iosb     = wine_server_client_ptr( io_status );
                req->async.arg      = wine_server_client_ptr( fileio );
992
                req->async.cvalue   = cvalue;
993 994 995 996 997
                status = wine_server_call( req );
            }
            SERVER_END_REQ;

            if (status != STATUS_PENDING) RtlFreeHeap( GetProcessHeap(), 0, fileio );
998
            goto err;
999
        }
1000
        else  /* synchronous write, wait for the fd to become ready */
1001
        {
1002 1003 1004 1005 1006 1007 1008
            struct pollfd pfd;
            int ret, timeout;

            if (!timeout_init_done)
            {
                timeout_init_done = 1;
                if ((status = get_io_timeouts( hFile, type, length, FALSE, &timeouts )))
1009
                    goto err;
1010 1011 1012 1013 1014
                if (hEvent) NtResetEvent( hEvent, NULL );
            }
            timeout = get_next_io_timeout( &timeouts, total );

            pfd.fd = unix_handle;
1015
            pfd.events = POLLOUT;
1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028

            if (!timeout || !(ret = poll( &pfd, 1, timeout )))
            {
                /* return with what we got so far */
                status = total ? STATUS_SUCCESS : STATUS_TIMEOUT;
                goto done;
            }
            if (ret == -1 && errno != EINTR)
            {
                status = FILE_GetNtStatus();
                goto done;
            }
            /* will now restart the write */
1029
        }
1030 1031
    }

1032
done:
1033 1034 1035
    if (cvalue) NTDLL_AddCompletion( hFile, cvalue, status, total );

err:
1036
    if (needs_close) close( unix_handle );
1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050
    if (status == STATUS_SUCCESS)
    {
        io_status->u.Status = status;
        io_status->Information = total;
        TRACE("= SUCCESS (%u)\n", total);
        if (hEvent) NtSetEvent( hEvent, NULL );
        if (apc) NtQueueApcThread( GetCurrentThread(), (PNTAPCFUNC)apc,
                                   (ULONG_PTR)apc_user, (ULONG_PTR)io_status, 0 );
    }
    else
    {
        TRACE("= 0x%08x\n", status);
        if (status != STATUS_PENDING && hEvent) NtResetEvent( hEvent, NULL );
    }
1051
    return status;
Juergen Schmied's avatar
Juergen Schmied committed
1052 1053
}

1054

1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143
/******************************************************************************
 *  NtWriteFileGather   [NTDLL.@]
 *  ZwWriteFileGather   [NTDLL.@]
 */
NTSTATUS WINAPI NtWriteFileGather( HANDLE file, HANDLE event, PIO_APC_ROUTINE apc, void *apc_user,
                                   PIO_STATUS_BLOCK io_status, FILE_SEGMENT_ELEMENT *segments,
                                   ULONG length, PLARGE_INTEGER offset, PULONG key )
{
    size_t page_size = getpagesize();
    int result, unix_handle, needs_close;
    unsigned int options;
    NTSTATUS status;
    ULONG pos = 0, total = 0;
    enum server_fd_type type;
    ULONG_PTR cvalue = apc ? 0 : (ULONG_PTR)apc_user;

    TRACE( "(%p,%p,%p,%p,%p,%p,0x%08x,%p,%p),partial stub!\n",
           file, event, apc, apc_user, io_status, segments, length, offset, key);

    if (length % page_size) return STATUS_INVALID_PARAMETER;
    if (!io_status) return STATUS_ACCESS_VIOLATION;

    status = server_get_unix_fd( file, FILE_WRITE_DATA, &unix_handle,
                                 &needs_close, &type, &options );
    if (status) return status;

    if ((type != FD_TYPE_FILE) ||
        (options & (FILE_SYNCHRONOUS_IO_ALERT | FILE_SYNCHRONOUS_IO_NONALERT)) ||
        !(options & FILE_NO_INTERMEDIATE_BUFFERING))
    {
        status = STATUS_INVALID_PARAMETER;
        goto error;
    }

    while (length)
    {
        if (offset && offset->QuadPart != (LONGLONG)-2 /* FILE_USE_FILE_POINTER_POSITION */)
            result = pwrite( unix_handle, (char *)segments->Buffer + pos,
                             page_size - pos, offset->QuadPart + total );
        else
            result = write( unix_handle, (char *)segments->Buffer + pos, page_size - pos );

        if (result == -1)
        {
            if (errno == EINTR) continue;
            if (errno == EFAULT)
            {
                status = STATUS_INVALID_USER_BUFFER;
                goto error;
            }
            status = FILE_GetNtStatus();
            break;
        }
        if (!result)
        {
            status = STATUS_DISK_FULL;
            break;
        }
        total += result;
        length -= result;
        if ((pos += result) == page_size)
        {
            pos = 0;
            segments++;
        }
    }

    if (cvalue) NTDLL_AddCompletion( file, cvalue, status, total );

 error:
    if (needs_close) close( unix_handle );
    if (status == STATUS_SUCCESS)
    {
        io_status->u.Status = status;
        io_status->Information = total;
        TRACE("= SUCCESS (%u)\n", total);
        if (event) NtSetEvent( event, NULL );
        if (apc) NtQueueApcThread( GetCurrentThread(), (PNTAPCFUNC)apc,
                                   (ULONG_PTR)apc_user, (ULONG_PTR)io_status, 0 );
    }
    else
    {
        TRACE("= 0x%08x\n", status);
        if (status != STATUS_PENDING && event) NtResetEvent( event, NULL );
    }
    return status;
}


1144 1145
struct async_ioctl
{
1146
    HANDLE          handle;   /* handle to the device */
1147
    HANDLE          event;    /* async event */
1148 1149 1150 1151
    void           *buffer;   /* buffer for output */
    ULONG           size;     /* size of buffer */
    PIO_APC_ROUTINE apc;      /* user apc params */
    void           *apc_arg;
1152 1153
};

1154 1155 1156 1157 1158 1159 1160 1161
/* callback for ioctl user APC */
static void WINAPI ioctl_apc( void *arg, IO_STATUS_BLOCK *io, ULONG reserved )
{
    struct async_ioctl *async = arg;
    if (async->apc) async->apc( async->apc_arg, io, reserved );
    RtlFreeHeap( GetProcessHeap(), 0, async );
}

1162
/* callback for ioctl async I/O completion */
1163
static NTSTATUS ioctl_completion( void *arg, IO_STATUS_BLOCK *io, NTSTATUS status, void **apc )
1164
{
1165 1166 1167 1168 1169 1170
    struct async_ioctl *async = arg;

    if (status == STATUS_ALERTED)
    {
        SERVER_START_REQ( get_ioctl_result )
        {
1171
            req->handle   = wine_server_obj_handle( async->handle );
1172
            req->user_arg = wine_server_client_ptr( async );
1173 1174
            wine_server_set_reply( req, async->buffer, async->size );
            if (!(status = wine_server_call( req )))
1175
                io->Information = wine_server_reply_size( reply );
1176 1177 1178
        }
        SERVER_END_REQ;
    }
1179 1180 1181 1182 1183
    if (status != STATUS_PENDING)
    {
        io->u.Status = status;
        if (async->apc || async->event) *apc = ioctl_apc;
    }
1184 1185 1186 1187 1188 1189 1190
    return status;
}

/* do a ioctl call through the server */
static NTSTATUS server_ioctl_file( HANDLE handle, HANDLE event,
                                   PIO_APC_ROUTINE apc, PVOID apc_context,
                                   IO_STATUS_BLOCK *io, ULONG code,
1191
                                   const void *in_buffer, ULONG in_size,
1192 1193
                                   PVOID out_buffer, ULONG out_size )
{
1194
    struct async_ioctl *async;
1195
    NTSTATUS status;
1196 1197
    HANDLE wait_handle;
    ULONG options;
1198
    ULONG_PTR cvalue = apc ? 0 : (ULONG_PTR)apc_context;
1199

1200 1201
    if (!(async = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*async) )))
        return STATUS_NO_MEMORY;
1202
    async->handle  = handle;
1203
    async->event   = event;
1204 1205 1206 1207
    async->buffer  = out_buffer;
    async->size    = out_size;
    async->apc     = apc;
    async->apc_arg = apc_context;
1208

1209 1210 1211
    SERVER_START_REQ( ioctl )
    {
        req->code           = code;
1212
        req->blocking       = !apc && !event;
1213
        req->async.handle   = wine_server_obj_handle( handle );
1214 1215 1216
        req->async.callback = wine_server_client_ptr( ioctl_completion );
        req->async.iosb     = wine_server_client_ptr( io );
        req->async.arg      = wine_server_client_ptr( async );
1217
        req->async.event    = wine_server_obj_handle( event );
1218
        req->async.cvalue   = cvalue;
1219 1220 1221 1222
        wine_server_add_data( req, in_buffer, in_size );
        wine_server_set_reply( req, out_buffer, out_size );
        if (!(status = wine_server_call( req )))
            io->Information = wine_server_reply_size( reply );
1223
        wait_handle = wine_server_ptr_handle( reply->wait );
1224
        options     = reply->options;
1225 1226 1227 1228 1229 1230 1231
    }
    SERVER_END_REQ;

    if (status == STATUS_NOT_SUPPORTED)
        FIXME("Unsupported ioctl %x (device=%x access=%x func=%x method=%x)\n",
              code, code >> 16, (code >> 14) & 3, (code >> 2) & 0xfff, code & 3);

1232 1233
    if (status != STATUS_PENDING) RtlFreeHeap( GetProcessHeap(), 0, async );

1234 1235 1236 1237 1238
    if (wait_handle)
    {
        NtWaitForSingleObject( wait_handle, (options & FILE_SYNCHRONOUS_IO_ALERT), NULL );
        status = io->u.Status;
        NtClose( wait_handle );
1239
        RtlFreeHeap( GetProcessHeap(), 0, async );
1240 1241
    }

1242 1243 1244 1245
    return status;
}


Juergen Schmied's avatar
Juergen Schmied committed
1246
/**************************************************************************
1247
 *		NtDeviceIoControlFile			[NTDLL.@]
Patrik Stridvall's avatar
Patrik Stridvall committed
1248
 *		ZwDeviceIoControlFile			[NTDLL.@]
Jon Griffiths's avatar
Jon Griffiths committed
1249 1250 1251 1252
 *
 * Perform an I/O control operation on an open file handle.
 *
 * PARAMS
1253 1254 1255 1256 1257 1258 1259 1260 1261 1262
 *  handle         [I] Handle returned from ZwOpenFile() or ZwCreateFile()
 *  event          [I] Event to signal upon completion (or NULL)
 *  apc            [I] Callback to call upon completion (or NULL)
 *  apc_context    [I] Context for ApcRoutine (or NULL)
 *  io             [O] Receives information about the operation on return
 *  code           [I] Control code for the operation to perform
 *  in_buffer      [I] Source for any input data required (or NULL)
 *  in_size        [I] Size of InputBuffer
 *  out_buffer     [O] Source for any output data returned (or NULL)
 *  out_size       [I] Size of OutputBuffer
Jon Griffiths's avatar
Jon Griffiths committed
1263 1264 1265 1266
 *
 * RETURNS
 *  Success: 0. IoStatusBlock is updated.
 *  Failure: An NTSTATUS error code describing the error.
Juergen Schmied's avatar
Juergen Schmied committed
1267
 */
1268 1269 1270 1271 1272
NTSTATUS WINAPI NtDeviceIoControlFile(HANDLE handle, HANDLE event,
                                      PIO_APC_ROUTINE apc, PVOID apc_context,
                                      PIO_STATUS_BLOCK io, ULONG code,
                                      PVOID in_buffer, ULONG in_size,
                                      PVOID out_buffer, ULONG out_size)
Juergen Schmied's avatar
Juergen Schmied committed
1273
{
1274
    ULONG device = (code >> 16);
1275
    NTSTATUS status = STATUS_NOT_SUPPORTED;
1276

1277
    TRACE("(%p,%p,%p,%p,%p,0x%08x,%p,0x%08x,%p,0x%08x)\n",
1278 1279 1280 1281
          handle, event, apc, apc_context, io, code,
          in_buffer, in_size, out_buffer, out_size);

    switch(device)
1282
    {
1283 1284 1285 1286 1287
    case FILE_DEVICE_DISK:
    case FILE_DEVICE_CD_ROM:
    case FILE_DEVICE_DVD:
    case FILE_DEVICE_CONTROLLER:
    case FILE_DEVICE_MASS_STORAGE:
1288 1289
        status = CDROM_DeviceIoControl(handle, event, apc, apc_context, io, code,
                                       in_buffer, in_size, out_buffer, out_size);
1290 1291
        break;
    case FILE_DEVICE_SERIAL_PORT:
1292 1293
        status = COMM_DeviceIoControl(handle, event, apc, apc_context, io, code,
                                      in_buffer, in_size, out_buffer, out_size);
1294
        break;
1295
    case FILE_DEVICE_TAPE:
1296 1297
        status = TAPE_DeviceIoControl(handle, event, apc, apc_context, io, code,
                                      in_buffer, in_size, out_buffer, out_size);
1298
        break;
1299 1300
    }

1301
    if (status == STATUS_NOT_SUPPORTED || status == STATUS_BAD_DEVICE_TYPE)
1302 1303
        status = server_ioctl_file( handle, event, apc, apc_context, io, code,
                                    in_buffer, in_size, out_buffer, out_size );
1304

1305 1306
    if (status != STATUS_PENDING) io->u.Status = status;
    return status;
Juergen Schmied's avatar
Juergen Schmied committed
1307 1308
}

1309 1310 1311 1312 1313 1314 1315 1316

/**************************************************************************
 *              NtFsControlFile                 [NTDLL.@]
 *              ZwFsControlFile                 [NTDLL.@]
 *
 * Perform a file system control operation on an open file handle.
 *
 * PARAMS
1317 1318 1319 1320 1321 1322 1323 1324 1325 1326
 *  handle         [I] Handle returned from ZwOpenFile() or ZwCreateFile()
 *  event          [I] Event to signal upon completion (or NULL)
 *  apc            [I] Callback to call upon completion (or NULL)
 *  apc_context    [I] Context for ApcRoutine (or NULL)
 *  io             [O] Receives information about the operation on return
 *  code           [I] Control code for the operation to perform
 *  in_buffer      [I] Source for any input data required (or NULL)
 *  in_size        [I] Size of InputBuffer
 *  out_buffer     [O] Source for any output data returned (or NULL)
 *  out_size       [I] Size of OutputBuffer
1327 1328 1329 1330
 *
 * RETURNS
 *  Success: 0. IoStatusBlock is updated.
 *  Failure: An NTSTATUS error code describing the error.
Juergen Schmied's avatar
Juergen Schmied committed
1331
 */
1332 1333 1334
NTSTATUS WINAPI NtFsControlFile(HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc,
                                PVOID apc_context, PIO_STATUS_BLOCK io, ULONG code,
                                PVOID in_buffer, ULONG in_size, PVOID out_buffer, ULONG out_size)
Juergen Schmied's avatar
Juergen Schmied committed
1335
{
1336 1337
    NTSTATUS status;

1338
    TRACE("(%p,%p,%p,%p,%p,0x%08x,%p,0x%08x,%p,0x%08x)\n",
1339 1340
          handle, event, apc, apc_context, io, code,
          in_buffer, in_size, out_buffer, out_size);
1341

1342
    if (!io) return STATUS_INVALID_PARAMETER;
1343

1344
    switch(code)
1345
    {
1346
    case FSCTL_DISMOUNT_VOLUME:
1347 1348 1349
        status = server_ioctl_file( handle, event, apc, apc_context, io, code,
                                    in_buffer, in_size, out_buffer, out_size );
        if (!status) status = DIR_unmount_device( handle );
1350
        break;
1351

1352 1353 1354
    case FSCTL_PIPE_PEEK:
        {
            FILE_PIPE_PEEK_BUFFER *buffer = out_buffer;
1355
            int avail = 0, fd, needs_close;
1356 1357 1358

            if (out_size < FIELD_OFFSET( FILE_PIPE_PEEK_BUFFER, Data ))
            {
1359
                status = STATUS_INFO_LENGTH_MISMATCH;
1360 1361 1362
                break;
            }

1363
            if ((status = server_get_unix_fd( handle, FILE_READ_DATA, &fd, &needs_close, NULL, NULL )))
1364 1365 1366 1367 1368 1369
                break;

#ifdef FIONREAD
            if (ioctl( fd, FIONREAD, &avail ) != 0)
            {
                TRACE("FIONREAD failed reason: %s\n",strerror(errno));
1370
                if (needs_close) close( fd );
1371
                status = FILE_GetNtStatus();
1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385
                break;
            }
#endif
            if (!avail)  /* check for closed pipe */
            {
                struct pollfd pollfd;
                int ret;

                pollfd.fd = fd;
                pollfd.events = POLLIN;
                pollfd.revents = 0;
                ret = poll( &pollfd, 1, 0 );
                if (ret == -1 || (ret == 1 && (pollfd.revents & (POLLHUP|POLLERR))))
                {
1386
                    if (needs_close) close( fd );
1387
                    status = STATUS_PIPE_BROKEN;
1388 1389 1390 1391 1392 1393 1394 1395
                    break;
                }
            }
            buffer->NamedPipeState    = 0;  /* FIXME */
            buffer->ReadDataAvailable = avail;
            buffer->NumberOfMessages  = 0;  /* FIXME */
            buffer->MessageLength     = 0;  /* FIXME */
            io->Information = FIELD_OFFSET( FILE_PIPE_PEEK_BUFFER, Data );
1396
            status = STATUS_SUCCESS;
1397 1398 1399 1400 1401 1402 1403 1404 1405
            if (avail)
            {
                ULONG data_size = out_size - FIELD_OFFSET( FILE_PIPE_PEEK_BUFFER, Data );
                if (data_size)
                {
                    int res = recv( fd, buffer->Data, data_size, MSG_PEEK );
                    if (res >= 0) io->Information += res;
                }
            }
1406
            if (needs_close) close( fd );
1407 1408 1409
        }
        break;

1410
    case FSCTL_PIPE_DISCONNECT:
1411 1412 1413
        status = server_ioctl_file( handle, event, apc, apc_context, io, code,
                                    in_buffer, in_size, out_buffer, out_size );
        if (!status)
1414
        {
1415 1416
            int fd = server_remove_fd_from_cache( handle );
            if (fd != -1) close( fd );
1417 1418 1419
        }
        break;

1420
    case FSCTL_PIPE_IMPERSONATE:
1421
        FIXME("FSCTL_PIPE_IMPERSONATE: impersonating self\n");
1422 1423 1424
        status = RtlImpersonateSelf( SecurityImpersonation );
        break;

1425 1426
    case FSCTL_LOCK_VOLUME:
    case FSCTL_UNLOCK_VOLUME:
1427
        FIXME("stub! return success - Unsupported fsctl %x (device=%x access=%x func=%x method=%x)\n",
1428
              code, code >> 16, (code >> 14) & 3, (code >> 2) & 0xfff, code & 3);
1429
        status = STATUS_SUCCESS;
1430 1431
        break;

1432
    case FSCTL_PIPE_LISTEN:
1433
    case FSCTL_PIPE_WAIT:
1434
    default:
1435 1436
        status = server_ioctl_file( handle, event, apc, apc_context, io, code,
                                    in_buffer, in_size, out_buffer, out_size );
1437
        break;
1438
    }
1439 1440 1441

    if (status != STATUS_PENDING) io->u.Status = status;
    return status;
Juergen Schmied's avatar
Juergen Schmied committed
1442 1443 1444
}

/******************************************************************************
1445
 *  NtSetVolumeInformationFile		[NTDLL.@]
Patrik Stridvall's avatar
Patrik Stridvall committed
1446
 *  ZwSetVolumeInformationFile		[NTDLL.@]
Jon Griffiths's avatar
Jon Griffiths committed
1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459
 *
 * Set volume information for an open file handle.
 *
 * PARAMS
 *  FileHandle         [I] Handle returned from ZwOpenFile() or ZwCreateFile()
 *  IoStatusBlock      [O] Receives information about the operation on return
 *  FsInformation      [I] Source for volume information
 *  Length             [I] Size of FsInformation
 *  FsInformationClass [I] Type of volume information to set
 *
 * RETURNS
 *  Success: 0. IoStatusBlock is updated.
 *  Failure: An NTSTATUS error code describing the error.
Juergen Schmied's avatar
Juergen Schmied committed
1460 1461 1462
 */
NTSTATUS WINAPI NtSetVolumeInformationFile(
	IN HANDLE FileHandle,
1463 1464 1465
	PIO_STATUS_BLOCK IoStatusBlock,
	PVOID FsInformation,
        ULONG Length,
1466
	FS_INFORMATION_CLASS FsInformationClass)
Juergen Schmied's avatar
Juergen Schmied committed
1467
{
1468
	FIXME("(%p,%p,%p,0x%08x,0x%08x) stub\n",
1469
	FileHandle,IoStatusBlock,FsInformation,Length,FsInformationClass);
Juergen Schmied's avatar
Juergen Schmied committed
1470 1471 1472 1473
	return 0;
}

/******************************************************************************
1474
 *  NtQueryInformationFile		[NTDLL.@]
Patrik Stridvall's avatar
Patrik Stridvall committed
1475
 *  ZwQueryInformationFile		[NTDLL.@]
Jon Griffiths's avatar
Jon Griffiths committed
1476 1477 1478 1479
 *
 * Get information about an open file handle.
 *
 * PARAMS
1480 1481 1482 1483 1484
 *  hFile    [I] Handle returned from ZwOpenFile() or ZwCreateFile()
 *  io       [O] Receives information about the operation on return
 *  ptr      [O] Destination for file information
 *  len      [I] Size of FileInformation
 *  class    [I] Type of file information to get
Jon Griffiths's avatar
Jon Griffiths committed
1485 1486 1487 1488
 *
 * RETURNS
 *  Success: 0. IoStatusBlock and FileInformation are updated.
 *  Failure: An NTSTATUS error code describing the error.
Juergen Schmied's avatar
Juergen Schmied committed
1489
 */
1490 1491
NTSTATUS WINAPI NtQueryInformationFile( HANDLE hFile, PIO_STATUS_BLOCK io,
                                        PVOID ptr, LONG len, FILE_INFORMATION_CLASS class )
Juergen Schmied's avatar
Juergen Schmied committed
1492
{
1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518
    static const size_t info_sizes[] =
    {
        0,
        sizeof(FILE_DIRECTORY_INFORMATION),            /* FileDirectoryInformation */
        sizeof(FILE_FULL_DIRECTORY_INFORMATION),       /* FileFullDirectoryInformation */
        sizeof(FILE_BOTH_DIRECTORY_INFORMATION),       /* FileBothDirectoryInformation */
        sizeof(FILE_BASIC_INFORMATION),                /* FileBasicInformation */
        sizeof(FILE_STANDARD_INFORMATION),             /* FileStandardInformation */
        sizeof(FILE_INTERNAL_INFORMATION),             /* FileInternalInformation */
        sizeof(FILE_EA_INFORMATION),                   /* FileEaInformation */
        sizeof(FILE_ACCESS_INFORMATION),               /* FileAccessInformation */
        sizeof(FILE_NAME_INFORMATION)-sizeof(WCHAR),   /* FileNameInformation */
        sizeof(FILE_RENAME_INFORMATION)-sizeof(WCHAR), /* FileRenameInformation */
        0,                                             /* FileLinkInformation */
        sizeof(FILE_NAMES_INFORMATION)-sizeof(WCHAR),  /* FileNamesInformation */
        sizeof(FILE_DISPOSITION_INFORMATION),          /* FileDispositionInformation */
        sizeof(FILE_POSITION_INFORMATION),             /* FilePositionInformation */
        sizeof(FILE_FULL_EA_INFORMATION),              /* FileFullEaInformation */
        sizeof(FILE_MODE_INFORMATION),                 /* FileModeInformation */
        sizeof(FILE_ALIGNMENT_INFORMATION),            /* FileAlignmentInformation */
        sizeof(FILE_ALL_INFORMATION)-sizeof(WCHAR),    /* FileAllInformation */
        sizeof(FILE_ALLOCATION_INFORMATION),           /* FileAllocationInformation */
        sizeof(FILE_END_OF_FILE_INFORMATION),          /* FileEndOfFileInformation */
        0,                                             /* FileAlternateNameInformation */
        sizeof(FILE_STREAM_INFORMATION)-sizeof(WCHAR), /* FileStreamInformation */
        0,                                             /* FilePipeInformation */
1519
        sizeof(FILE_PIPE_LOCAL_INFORMATION),           /* FilePipeLocalInformation */
1520
        0,                                             /* FilePipeRemoteInformation */
1521
        sizeof(FILE_MAILSLOT_QUERY_INFORMATION),       /* FileMailslotQueryInformation */
1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534
        0,                                             /* FileMailslotSetInformation */
        0,                                             /* FileCompressionInformation */
        0,                                             /* FileObjectIdInformation */
        0,                                             /* FileCompletionInformation */
        0,                                             /* FileMoveClusterInformation */
        0,                                             /* FileQuotaInformation */
        0,                                             /* FileReparsePointInformation */
        0,                                             /* FileNetworkOpenInformation */
        0,                                             /* FileAttributeTagInformation */
        0                                              /* FileTrackingInformation */
    };

    struct stat st;
1535
    int fd, needs_close = FALSE;
1536

1537
    TRACE("(%p,%p,%p,0x%08x,0x%08x)\n", hFile, io, ptr, len, class);
1538

1539
    io->Information = 0;
1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550

    if (class <= 0 || class >= FileMaximumInformation)
        return io->u.Status = STATUS_INVALID_INFO_CLASS;
    if (!info_sizes[class])
    {
        FIXME("Unsupported class (%d)\n", class);
        return io->u.Status = STATUS_NOT_IMPLEMENTED;
    }
    if (len < info_sizes[class])
        return io->u.Status = STATUS_INFO_LENGTH_MISMATCH;

1551 1552
    if (class != FilePipeLocalInformation)
    {
1553
        if ((io->u.Status = server_get_unix_fd( hFile, 0, &fd, &needs_close, NULL, NULL )))
1554
            return io->u.Status;
1555
    }
1556 1557 1558 1559 1560

    switch (class)
    {
    case FileBasicInformation:
        {
1561
            FILE_BASIC_INFORMATION *info = ptr;
1562

1563 1564 1565 1566
            if (fstat( fd, &st ) == -1)
                io->u.Status = FILE_GetNtStatus();
            else if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode))
                io->u.Status = STATUS_INVALID_INFO_CLASS;
1567
            else
1568
            {
1569 1570
                if (S_ISDIR(st.st_mode)) info->FileAttributes = FILE_ATTRIBUTE_DIRECTORY;
                else info->FileAttributes = FILE_ATTRIBUTE_ARCHIVE;
1571 1572
                if (!(st.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)))
                    info->FileAttributes |= FILE_ATTRIBUTE_READONLY;
1573 1574 1575 1576
                RtlSecondsSince1970ToTime( st.st_mtime, &info->CreationTime);
                RtlSecondsSince1970ToTime( st.st_mtime, &info->LastWriteTime);
                RtlSecondsSince1970ToTime( st.st_ctime, &info->ChangeTime);
                RtlSecondsSince1970ToTime( st.st_atime, &info->LastAccessTime);
1577 1578 1579 1580 1581 1582 1583 1584 1585 1586
#ifdef HAVE_STRUCT_STAT_ST_MTIM
                info->CreationTime.QuadPart += st.st_mtim.tv_nsec / 100;
                info->LastWriteTime.QuadPart += st.st_mtim.tv_nsec / 100;
#endif
#ifdef HAVE_STRUCT_STAT_ST_CTIM
                info->ChangeTime.QuadPart += st.st_ctim.tv_nsec / 100;
#endif
#ifdef HAVE_STRUCT_STAT_ST_ATIM
                info->LastAccessTime.QuadPart += st.st_atim.tv_nsec / 100;
#endif
1587 1588 1589 1590 1591
            }
        }
        break;
    case FileStandardInformation:
        {
1592
            FILE_STANDARD_INFORMATION *info = ptr;
1593

1594
            if (fstat( fd, &st ) == -1) io->u.Status = FILE_GetNtStatus();
1595
            else
1596
            {
1597 1598 1599 1600 1601 1602 1603
                if ((info->Directory = S_ISDIR(st.st_mode)))
                {
                    info->AllocationSize.QuadPart = 0;
                    info->EndOfFile.QuadPart      = 0;
                    info->NumberOfLinks           = 1;
                    info->DeletePending           = FALSE;
                }
1604
                else
1605
                {
1606 1607 1608 1609
                    info->AllocationSize.QuadPart = (ULONGLONG)st.st_blocks * 512;
                    info->EndOfFile.QuadPart      = st.st_size;
                    info->NumberOfLinks           = st.st_nlink;
                    info->DeletePending           = FALSE; /* FIXME */
1610 1611 1612 1613 1614 1615
                }
            }
        }
        break;
    case FilePositionInformation:
        {
1616
            FILE_POSITION_INFORMATION *info = ptr;
1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638
            off_t res = lseek( fd, 0, SEEK_CUR );
            if (res == (off_t)-1) io->u.Status = FILE_GetNtStatus();
            else info->CurrentByteOffset.QuadPart = res;
        }
        break;
    case FileInternalInformation:
        {
            FILE_INTERNAL_INFORMATION *info = ptr;

            if (fstat( fd, &st ) == -1) io->u.Status = FILE_GetNtStatus();
            else info->IndexNumber.QuadPart = st.st_ino;
        }
        break;
    case FileEaInformation:
        {
            FILE_EA_INFORMATION *info = ptr;
            info->EaSize = 0;
        }
        break;
    case FileEndOfFileInformation:
        {
            FILE_END_OF_FILE_INFORMATION *info = ptr;
1639

1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650
            if (fstat( fd, &st ) == -1) io->u.Status = FILE_GetNtStatus();
            else info->EndOfFile.QuadPart = S_ISDIR(st.st_mode) ? 0 : st.st_size;
        }
        break;
    case FileAllInformation:
        {
            FILE_ALL_INFORMATION *info = ptr;

            if (fstat( fd, &st ) == -1) io->u.Status = FILE_GetNtStatus();
            else if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode))
                io->u.Status = STATUS_INVALID_INFO_CLASS;
1651
            else
1652
            {
1653 1654 1655 1656 1657 1658 1659 1660
                if ((info->StandardInformation.Directory = S_ISDIR(st.st_mode)))
                {
                    info->BasicInformation.FileAttributes = FILE_ATTRIBUTE_DIRECTORY;
                    info->StandardInformation.AllocationSize.QuadPart = 0;
                    info->StandardInformation.EndOfFile.QuadPart      = 0;
                    info->StandardInformation.NumberOfLinks           = 1;
                    info->StandardInformation.DeletePending           = FALSE;
                }
1661
                else
1662
                {
1663 1664 1665 1666 1667
                    info->BasicInformation.FileAttributes = FILE_ATTRIBUTE_ARCHIVE;
                    info->StandardInformation.AllocationSize.QuadPart = (ULONGLONG)st.st_blocks * 512;
                    info->StandardInformation.EndOfFile.QuadPart      = st.st_size;
                    info->StandardInformation.NumberOfLinks           = st.st_nlink;
                    info->StandardInformation.DeletePending           = FALSE; /* FIXME */
1668
                }
1669
                if (!(st.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)))
1670 1671 1672 1673 1674
                    info->BasicInformation.FileAttributes |= FILE_ATTRIBUTE_READONLY;
                RtlSecondsSince1970ToTime( st.st_mtime, &info->BasicInformation.CreationTime);
                RtlSecondsSince1970ToTime( st.st_mtime, &info->BasicInformation.LastWriteTime);
                RtlSecondsSince1970ToTime( st.st_ctime, &info->BasicInformation.ChangeTime);
                RtlSecondsSince1970ToTime( st.st_atime, &info->BasicInformation.LastAccessTime);
1675 1676 1677 1678 1679 1680 1681 1682 1683 1684
#ifdef HAVE_STRUCT_STAT_ST_MTIM
                info->BasicInformation.CreationTime.QuadPart += st.st_mtim.tv_nsec / 100;
                info->BasicInformation.LastWriteTime.QuadPart += st.st_mtim.tv_nsec / 100;
#endif
#ifdef HAVE_STRUCT_STAT_ST_CTIM
                info->BasicInformation.ChangeTime.QuadPart += st.st_ctim.tv_nsec / 100;
#endif
#ifdef HAVE_STRUCT_STAT_ST_ATIM
                info->BasicInformation.LastAccessTime.QuadPart += st.st_atim.tv_nsec / 100;
#endif
1685 1686 1687 1688 1689 1690 1691 1692
                info->InternalInformation.IndexNumber.QuadPart = st.st_ino;
                info->EaInformation.EaSize = 0;
                info->AccessInformation.AccessFlags = 0;  /* FIXME */
                info->PositionInformation.CurrentByteOffset.QuadPart = lseek( fd, 0, SEEK_CUR );
                info->ModeInformation.Mode = 0;  /* FIXME */
                info->AlignmentInformation.AlignmentRequirement = 1;  /* FIXME */
                info->NameInformation.FileNameLength = 0;
                io->Information = sizeof(*info) - sizeof(WCHAR);
1693 1694 1695
            }
        }
        break;
1696 1697 1698 1699 1700 1701
    case FileMailslotQueryInformation:
        {
            FILE_MAILSLOT_QUERY_INFORMATION *info = ptr;

            SERVER_START_REQ( set_mailslot_info )
            {
1702
                req->handle = wine_server_obj_handle( hFile );
1703 1704 1705 1706 1707 1708
                req->flags = 0;
                io->u.Status = wine_server_call( req );
                if( io->u.Status == STATUS_SUCCESS )
                {
                    info->MaximumMessageSize = reply->max_msgsize;
                    info->MailslotQuota = 0;
1709 1710
                    info->NextMessageSize = 0;
                    info->MessagesAvailable = 0;
1711
                    info->ReadTimeout.QuadPart = reply->read_timeout;
1712 1713 1714
                }
            }
            SERVER_END_REQ;
1715 1716
            if (!io->u.Status)
            {
1717
                char *tmpbuf;
1718
                ULONG size = info->MaximumMessageSize ? info->MaximumMessageSize : 0x10000;
1719 1720
                if (size > 0x10000) size = 0x10000;
                if ((tmpbuf = RtlAllocateHeap( GetProcessHeap(), 0, size )))
1721 1722
                {
                    int fd, needs_close;
1723
                    if (!server_get_unix_fd( hFile, FILE_READ_DATA, &fd, &needs_close, NULL, NULL ))
1724 1725 1726 1727 1728 1729 1730 1731 1732
                    {
                        int res = recv( fd, tmpbuf, size, MSG_PEEK );
                        info->MessagesAvailable = (res > 0);
                        info->NextMessageSize = (res >= 0) ? res : MAILSLOT_NO_MESSAGE;
                        if (needs_close) close( fd );
                    }
                    RtlFreeHeap( GetProcessHeap(), 0, tmpbuf );
                }
            }
1733 1734
        }
        break;
1735 1736 1737 1738 1739 1740
    case FilePipeLocalInformation:
        {
            FILE_PIPE_LOCAL_INFORMATION* pli = ptr;

            SERVER_START_REQ( get_named_pipe_info )
            {
1741
                req->handle = wine_server_obj_handle( hFile );
1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760
                if (!(io->u.Status = wine_server_call( req )))
                {
                    pli->NamedPipeType = (reply->flags & NAMED_PIPE_MESSAGE_STREAM_WRITE) ? 
                        FILE_PIPE_TYPE_MESSAGE : FILE_PIPE_TYPE_BYTE;
                    pli->NamedPipeConfiguration = 0; /* FIXME */
                    pli->MaximumInstances = reply->maxinstances;
                    pli->CurrentInstances = reply->instances;
                    pli->InboundQuota = reply->insize;
                    pli->ReadDataAvailable = 0; /* FIXME */
                    pli->OutboundQuota = reply->outsize;
                    pli->WriteQuotaAvailable = 0; /* FIXME */
                    pli->NamedPipeState = 0; /* FIXME */
                    pli->NamedPipeEnd = (reply->flags & NAMED_PIPE_SERVER_END) ?
                        FILE_PIPE_SERVER_END : FILE_PIPE_CLIENT_END;
                }
            }
            SERVER_END_REQ;
        }
        break;
1761 1762
    default:
        FIXME("Unsupported class (%d)\n", class);
1763 1764
        io->u.Status = STATUS_NOT_IMPLEMENTED;
        break;
1765
    }
1766
    if (needs_close) close( fd );
1767
    if (io->u.Status == STATUS_SUCCESS && !io->Information) io->Information = info_sizes[class];
1768
    return io->u.Status;
Juergen Schmied's avatar
Juergen Schmied committed
1769 1770 1771
}

/******************************************************************************
1772
 *  NtSetInformationFile		[NTDLL.@]
Patrik Stridvall's avatar
Patrik Stridvall committed
1773
 *  ZwSetInformationFile		[NTDLL.@]
Jon Griffiths's avatar
Jon Griffiths committed
1774 1775 1776 1777
 *
 * Set information about an open file handle.
 *
 * PARAMS
1778 1779 1780 1781 1782
 *  handle  [I] Handle returned from ZwOpenFile() or ZwCreateFile()
 *  io      [O] Receives information about the operation on return
 *  ptr     [I] Source for file information
 *  len     [I] Size of FileInformation
 *  class   [I] Type of file information to set
Jon Griffiths's avatar
Jon Griffiths committed
1783 1784
 *
 * RETURNS
1785
 *  Success: 0. io is updated.
Jon Griffiths's avatar
Jon Griffiths committed
1786
 *  Failure: An NTSTATUS error code describing the error.
Juergen Schmied's avatar
Juergen Schmied committed
1787
 */
1788 1789
NTSTATUS WINAPI NtSetInformationFile(HANDLE handle, PIO_STATUS_BLOCK io,
                                     PVOID ptr, ULONG len, FILE_INFORMATION_CLASS class)
Juergen Schmied's avatar
Juergen Schmied committed
1790
{
1791
    int fd, needs_close;
1792

1793
    TRACE("(%p,%p,%p,0x%08x,0x%08x)\n", handle, io, ptr, len, class);
1794 1795

    io->u.Status = STATUS_SUCCESS;
1796
    switch (class)
Jon Griffiths's avatar
Jon Griffiths committed
1797
    {
1798 1799
    case FileBasicInformation:
        if (len >= sizeof(FILE_BASIC_INFORMATION))
1800
        {
1801 1802
            struct stat st;
            const FILE_BASIC_INFORMATION *info = ptr;
1803

1804 1805 1806
            if ((io->u.Status = server_get_unix_fd( handle, 0, &fd, &needs_close, NULL, NULL )))
                return io->u.Status;

1807
            if (info->LastAccessTime.QuadPart || info->LastWriteTime.QuadPart)
1808
            {
1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835
                ULONGLONG sec, nsec;
                struct timeval tv[2];

                if (!info->LastAccessTime.QuadPart || !info->LastWriteTime.QuadPart)
                {

                    tv[0].tv_sec = tv[0].tv_usec = 0;
                    tv[1].tv_sec = tv[1].tv_usec = 0;
                    if (!fstat( fd, &st ))
                    {
                        tv[0].tv_sec = st.st_atime;
                        tv[1].tv_sec = st.st_mtime;
                    }
                }
                if (info->LastAccessTime.QuadPart)
                {
                    sec = RtlLargeIntegerDivide( info->LastAccessTime.QuadPart, 10000000, &nsec );
                    tv[0].tv_sec = sec - SECS_1601_TO_1970;
                    tv[0].tv_usec = (UINT)nsec / 10;
                }
                if (info->LastWriteTime.QuadPart)
                {
                    sec = RtlLargeIntegerDivide( info->LastWriteTime.QuadPart, 10000000, &nsec );
                    tv[1].tv_sec = sec - SECS_1601_TO_1970;
                    tv[1].tv_usec = (UINT)nsec / 10;
                }
                if (futimes( fd, tv ) == -1) io->u.Status = FILE_GetNtStatus();
1836
            }
1837

1838 1839 1840 1841 1842 1843 1844
            if (io->u.Status == STATUS_SUCCESS && info->FileAttributes)
            {
                if (fstat( fd, &st ) == -1) io->u.Status = FILE_GetNtStatus();
                else
                {
                    if (info->FileAttributes & FILE_ATTRIBUTE_READONLY)
                    {
1845 1846 1847 1848
                        if (S_ISDIR( st.st_mode))
                            WARN("FILE_ATTRIBUTE_READONLY ignored for directory.\n");
                        else
                            st.st_mode &= ~0222; /* clear write permission bits */
1849 1850 1851 1852 1853 1854 1855 1856 1857
                    }
                    else
                    {
                        /* add write permission only where we already have read permission */
                        st.st_mode |= (0600 | ((st.st_mode & 044) >> 1)) & (~FILE_umask);
                    }
                    if (fchmod( fd, st.st_mode ) == -1) io->u.Status = FILE_GetNtStatus();
                }
            }
1858 1859

            if (needs_close) close( fd );
1860 1861 1862 1863 1864 1865 1866 1867 1868
        }
        else io->u.Status = STATUS_INVALID_PARAMETER_3;
        break;

    case FilePositionInformation:
        if (len >= sizeof(FILE_POSITION_INFORMATION))
        {
            const FILE_POSITION_INFORMATION *info = ptr;

1869 1870 1871
            if ((io->u.Status = server_get_unix_fd( handle, 0, &fd, &needs_close, NULL, NULL )))
                return io->u.Status;

1872 1873
            if (lseek( fd, info->CurrentByteOffset.QuadPart, SEEK_SET ) == (off_t)-1)
                io->u.Status = FILE_GetNtStatus();
1874 1875

            if (needs_close) close( fd );
1876
        }
1877
        else io->u.Status = STATUS_INVALID_PARAMETER_3;
1878
        break;
1879

1880 1881 1882
    case FileEndOfFileInformation:
        if (len >= sizeof(FILE_END_OF_FILE_INFORMATION))
        {
1883
            struct stat st;
1884 1885
            const FILE_END_OF_FILE_INFORMATION *info = ptr;

1886 1887 1888
            if ((io->u.Status = server_get_unix_fd( handle, 0, &fd, &needs_close, NULL, NULL )))
                return io->u.Status;

1889 1890 1891 1892 1893
            /* first try normal truncate */
            if (ftruncate( fd, (off_t)info->EndOfFile.QuadPart ) != -1) break;

            /* now check for the need to extend the file */
            if (fstat( fd, &st ) != -1 && (off_t)info->EndOfFile.QuadPart > st.st_size)
1894 1895 1896 1897 1898
            {
                static const char zero;

                /* extend the file one byte beyond the requested size and then truncate it */
                /* this should work around ftruncate implementations that can't extend files */
1899 1900
                if (pwrite( fd, &zero, 1, (off_t)info->EndOfFile.QuadPart ) != -1 &&
                    ftruncate( fd, (off_t)info->EndOfFile.QuadPart ) != -1) break;
1901
            }
1902
            io->u.Status = FILE_GetNtStatus();
1903 1904

            if (needs_close) close( fd );
1905 1906 1907 1908
        }
        else io->u.Status = STATUS_INVALID_PARAMETER_3;
        break;

1909 1910 1911 1912 1913 1914
    case FileMailslotSetInformation:
        {
            FILE_MAILSLOT_SET_INFORMATION *info = ptr;

            SERVER_START_REQ( set_mailslot_info )
            {
1915
                req->handle = wine_server_obj_handle( handle );
1916
                req->flags = MAILSLOT_SET_READ_TIMEOUT;
1917
                req->read_timeout = info->ReadTimeout.QuadPart;
1918 1919 1920 1921 1922 1923
                io->u.Status = wine_server_call( req );
            }
            SERVER_END_REQ;
        }
        break;

1924 1925 1926
    case FileCompletionInformation:
        if (len >= sizeof(FILE_COMPLETION_INFORMATION))
        {
1927
            FILE_COMPLETION_INFORMATION *info = ptr;
1928 1929 1930

            SERVER_START_REQ( set_completion_info )
            {
1931 1932
                req->handle   = wine_server_obj_handle( handle );
                req->chandle  = wine_server_obj_handle( info->CompletionPort );
1933 1934 1935 1936 1937 1938 1939 1940
                req->ckey     = info->CompletionKey;
                io->u.Status  = wine_server_call( req );
            }
            SERVER_END_REQ;
        } else
            io->u.Status = STATUS_INVALID_PARAMETER_3;
        break;

1941 1942
    default:
        FIXME("Unsupported class (%d)\n", class);
1943 1944
        io->u.Status = STATUS_NOT_IMPLEMENTED;
        break;
1945
    }
1946 1947 1948 1949 1950 1951
    io->Information = 0;
    return io->u.Status;
}


/******************************************************************************
1952
 *              NtQueryFullAttributesFile   (NTDLL.@)
1953
 */
1954 1955
NTSTATUS WINAPI NtQueryFullAttributesFile( const OBJECT_ATTRIBUTES *attr,
                                           FILE_NETWORK_OPEN_INFORMATION *info )
1956 1957 1958 1959
{
    ANSI_STRING unix_name;
    NTSTATUS status;

1960 1961
    if (!(status = wine_nt_to_unix_file_name( attr->ObjectName, &unix_name, FILE_OPEN,
                                              !(attr->Attributes & OBJ_CASE_INSENSITIVE) )))
1962 1963 1964 1965 1966 1967 1968 1969 1970
    {
        struct stat st;

        if (stat( unix_name.Buffer, &st ) == -1)
            status = FILE_GetNtStatus();
        else if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode))
            status = STATUS_INVALID_INFO_CLASS;
        else
        {
1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982
            if (S_ISDIR(st.st_mode))
            {
                info->FileAttributes          = FILE_ATTRIBUTE_DIRECTORY;
                info->AllocationSize.QuadPart = 0;
                info->EndOfFile.QuadPart      = 0;
            }
            else
            {
                info->FileAttributes          = FILE_ATTRIBUTE_ARCHIVE;
                info->AllocationSize.QuadPart = (ULONGLONG)st.st_blocks * 512;
                info->EndOfFile.QuadPart      = st.st_size;
            }
1983 1984
            if (!(st.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)))
                info->FileAttributes |= FILE_ATTRIBUTE_READONLY;
1985 1986 1987 1988 1989 1990 1991 1992 1993
            RtlSecondsSince1970ToTime( st.st_mtime, &info->CreationTime );
            RtlSecondsSince1970ToTime( st.st_mtime, &info->LastWriteTime );
            RtlSecondsSince1970ToTime( st.st_ctime, &info->ChangeTime );
            RtlSecondsSince1970ToTime( st.st_atime, &info->LastAccessTime );
            if (DIR_is_hidden_file( attr->ObjectName ))
                info->FileAttributes |= FILE_ATTRIBUTE_HIDDEN;
        }
        RtlFreeAnsiString( &unix_name );
    }
1994
    else WARN("%s not found (%x)\n", debugstr_us(attr->ObjectName), status );
1995
    return status;
Juergen Schmied's avatar
Juergen Schmied committed
1996 1997
}

1998

1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019
/******************************************************************************
 *              NtQueryAttributesFile   (NTDLL.@)
 *              ZwQueryAttributesFile   (NTDLL.@)
 */
NTSTATUS WINAPI NtQueryAttributesFile( const OBJECT_ATTRIBUTES *attr, FILE_BASIC_INFORMATION *info )
{
    FILE_NETWORK_OPEN_INFORMATION full_info;
    NTSTATUS status;

    if (!(status = NtQueryFullAttributesFile( attr, &full_info )))
    {
        info->CreationTime.QuadPart   = full_info.CreationTime.QuadPart;
        info->LastAccessTime.QuadPart = full_info.LastAccessTime.QuadPart;
        info->LastWriteTime.QuadPart  = full_info.LastWriteTime.QuadPart;
        info->ChangeTime.QuadPart     = full_info.ChangeTime.QuadPart;
        info->FileAttributes          = full_info.FileAttributes;
    }
    return status;
}


2020
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__)
2021
/* helper for FILE_GetDeviceInfo to hide some platform differences in fstatfs */
2022
static inline void get_device_info_fstatfs( FILE_FS_DEVICE_INFORMATION *info, const char *fstypename,
2023
                                            unsigned int flags )
2024
{
2025
    if (!strcmp("cd9660", fstypename) || !strcmp("udf", fstypename))
2026 2027 2028 2029 2030
    {
        info->DeviceType = FILE_DEVICE_CD_ROM_FILE_SYSTEM;
        /* Don't assume read-only, let the mount options set it below */
        info->Characteristics |= FILE_REMOVABLE_MEDIA;
    }
2031 2032
    else if (!strcmp("nfs", fstypename) || !strcmp("nwfs", fstypename) ||
             !strcmp("smbfs", fstypename) || !strcmp("afpfs", fstypename))
2033 2034 2035 2036
    {
        info->DeviceType = FILE_DEVICE_NETWORK_FILE_SYSTEM;
        info->Characteristics |= FILE_REMOTE_DEVICE;
    }
2037
    else if (!strcmp("procfs", fstypename))
2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052
        info->DeviceType = FILE_DEVICE_VIRTUAL_DISK;
    else
        info->DeviceType = FILE_DEVICE_DISK_FILE_SYSTEM;

    if (flags & MNT_RDONLY)
        info->Characteristics |= FILE_READ_ONLY_DEVICE;

    if (!(flags & MNT_LOCAL))
    {
        info->DeviceType = FILE_DEVICE_NETWORK_FILE_SYSTEM;
        info->Characteristics |= FILE_REMOTE_DEVICE;
    }
}
#endif

2053 2054 2055 2056 2057 2058 2059 2060 2061 2062
static inline int is_device_placeholder( int fd )
{
    static const char wine_placeholder[] = "Wine device placeholder";
    char buffer[sizeof(wine_placeholder)-1];

    if (pread( fd, buffer, sizeof(wine_placeholder) - 1, 0 ) != sizeof(wine_placeholder) - 1)
        return 0;
    return !memcmp( buffer, wine_placeholder, sizeof(wine_placeholder) - 1 );
}

2063
/******************************************************************************
2064
 *              get_device_info
2065 2066 2067
 *
 * Implementation of the FileFsDeviceInformation query for NtQueryVolumeInformationFile.
 */
2068
static NTSTATUS get_device_info( int fd, FILE_FS_DEVICE_INFORMATION *info )
2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088
{
    struct stat st;

    info->Characteristics = 0;
    if (fstat( fd, &st ) < 0) return FILE_GetNtStatus();
    if (S_ISCHR( st.st_mode ))
    {
        info->DeviceType = FILE_DEVICE_UNKNOWN;
#ifdef linux
        switch(major(st.st_rdev))
        {
        case MEM_MAJOR:
            info->DeviceType = FILE_DEVICE_NULL;
            break;
        case TTY_MAJOR:
            info->DeviceType = FILE_DEVICE_SERIAL_PORT;
            break;
        case LP_MAJOR:
            info->DeviceType = FILE_DEVICE_PARALLEL_PORT;
            break;
2089 2090 2091
        case SCSI_TAPE_MAJOR:
            info->DeviceType = FILE_DEVICE_TAPE;
            break;
2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102
        }
#endif
    }
    else if (S_ISBLK( st.st_mode ))
    {
        info->DeviceType = FILE_DEVICE_DISK;
    }
    else if (S_ISFIFO( st.st_mode ) || S_ISSOCK( st.st_mode ))
    {
        info->DeviceType = FILE_DEVICE_NAMED_PIPE;
    }
2103 2104 2105 2106
    else if (is_device_placeholder( fd ))
    {
        info->DeviceType = FILE_DEVICE_DISK;
    }
2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119
    else  /* regular file or directory */
    {
#if defined(linux) && defined(HAVE_FSTATFS)
        struct statfs stfs;

        /* check for floppy disk */
        if (major(st.st_dev) == FLOPPY_MAJOR)
            info->Characteristics |= FILE_REMOVABLE_MEDIA;

        if (fstatfs( fd, &stfs ) < 0) stfs.f_type = 0;
        switch (stfs.f_type)
        {
        case 0x9660:      /* iso9660 */
2120
        case 0x9fa1:      /* supermount */
2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140
        case 0x15013346:  /* udf */
            info->DeviceType = FILE_DEVICE_CD_ROM_FILE_SYSTEM;
            info->Characteristics |= FILE_REMOVABLE_MEDIA|FILE_READ_ONLY_DEVICE;
            break;
        case 0x6969:  /* nfs */
        case 0x517B:  /* smbfs */
        case 0x564c:  /* ncpfs */
            info->DeviceType = FILE_DEVICE_NETWORK_FILE_SYSTEM;
            info->Characteristics |= FILE_REMOTE_DEVICE;
            break;
        case 0x01021994:  /* tmpfs */
        case 0x28cd3d45:  /* cramfs */
        case 0x1373:      /* devfs */
        case 0x9fa0:      /* procfs */
            info->DeviceType = FILE_DEVICE_VIRTUAL_DISK;
            break;
        default:
            info->DeviceType = FILE_DEVICE_DISK_FILE_SYSTEM;
            break;
        }
2141
#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__) || defined(__APPLE__)
2142 2143 2144 2145 2146
        struct statfs stfs;

        if (fstatfs( fd, &stfs ) < 0)
            info->DeviceType = FILE_DEVICE_DISK_FILE_SYSTEM;
        else
2147
            get_device_info_fstatfs( info, stfs.f_fstypename, stfs.f_flags );
2148 2149 2150 2151
#elif defined(__NetBSD__)
        struct statvfs stfs;

        if (fstatvfs( fd, &stfs) < 0)
2152
            info->DeviceType = FILE_DEVICE_DISK_FILE_SYSTEM;
2153
        else
2154
            get_device_info_fstatfs( info, stfs.f_fstypename, stfs.f_flag );
2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196
#elif defined(sun)
        /* Use dkio to work out device types */
        {
# include <sys/dkio.h>
# include <sys/vtoc.h>
            struct dk_cinfo dkinf;
            int retval = ioctl(fd, DKIOCINFO, &dkinf);
            if(retval==-1){
                WARN("Unable to get disk device type information - assuming a disk like device\n");
                info->DeviceType = FILE_DEVICE_DISK_FILE_SYSTEM;
            }
            switch (dkinf.dki_ctype)
            {
            case DKC_CDROM:
                info->DeviceType = FILE_DEVICE_CD_ROM_FILE_SYSTEM;
                info->Characteristics |= FILE_REMOVABLE_MEDIA|FILE_READ_ONLY_DEVICE;
                break;
            case DKC_NCRFLOPPY:
            case DKC_SMSFLOPPY:
            case DKC_INTEL82072:
            case DKC_INTEL82077:
                info->DeviceType = FILE_DEVICE_DISK_FILE_SYSTEM;
                info->Characteristics |= FILE_REMOVABLE_MEDIA;
                break;
            case DKC_MD:
                info->DeviceType = FILE_DEVICE_VIRTUAL_DISK;
                break;
            default:
                info->DeviceType = FILE_DEVICE_DISK_FILE_SYSTEM;
            }
        }
#else
        static int warned;
        if (!warned++) FIXME( "device info not properly supported on this platform\n" );
        info->DeviceType = FILE_DEVICE_DISK_FILE_SYSTEM;
#endif
        info->Characteristics |= FILE_DEVICE_IS_MOUNTED;
    }
    return STATUS_SUCCESS;
}


2197
/******************************************************************************
2198
 *  NtQueryVolumeInformationFile		[NTDLL.@]
Patrik Stridvall's avatar
Patrik Stridvall committed
2199
 *  ZwQueryVolumeInformationFile		[NTDLL.@]
Jon Griffiths's avatar
Jon Griffiths committed
2200 2201 2202 2203
 *
 * Get volume information for an open file handle.
 *
 * PARAMS
2204 2205 2206 2207 2208
 *  handle      [I] Handle returned from ZwOpenFile() or ZwCreateFile()
 *  io          [O] Receives information about the operation on return
 *  buffer      [O] Destination for volume information
 *  length      [I] Size of FsInformation
 *  info_class  [I] Type of volume information to set
Jon Griffiths's avatar
Jon Griffiths committed
2209 2210
 *
 * RETURNS
2211
 *  Success: 0. io and buffer are updated.
Jon Griffiths's avatar
Jon Griffiths committed
2212
 *  Failure: An NTSTATUS error code describing the error.
2213
 */
2214 2215 2216
NTSTATUS WINAPI NtQueryVolumeInformationFile( HANDLE handle, PIO_STATUS_BLOCK io,
                                              PVOID buffer, ULONG length,
                                              FS_INFORMATION_CLASS info_class )
2217
{
2218
    int fd, needs_close;
2219
    struct stat st;
2220

2221
    if ((io->u.Status = server_get_unix_fd( handle, 0, &fd, &needs_close, NULL, NULL )) != STATUS_SUCCESS)
2222
        return io->u.Status;
2223

2224 2225
    io->u.Status = STATUS_NOT_IMPLEMENTED;
    io->Information = 0;
2226

2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240
    switch( info_class )
    {
    case FileFsVolumeInformation:
        FIXME( "%p: volume info not supported\n", handle );
        break;
    case FileFsLabelInformation:
        FIXME( "%p: label info not supported\n", handle );
        break;
    case FileFsSizeInformation:
        if (length < sizeof(FILE_FS_SIZE_INFORMATION))
            io->u.Status = STATUS_BUFFER_TOO_SMALL;
        else
        {
            FILE_FS_SIZE_INFORMATION *info = buffer;
2241

2242 2243 2244 2245 2246 2247 2248 2249 2250
            if (fstat( fd, &st ) < 0)
            {
                io->u.Status = FILE_GetNtStatus();
                break;
            }
            if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode))
            {
                io->u.Status = STATUS_INVALID_DEVICE_REQUEST;
            }
2251 2252
            else
            {
2253
                ULONGLONG bsize;
2254 2255 2256 2257 2258 2259 2260 2261 2262
                /* Linux's fstatvfs is buggy */
#if !defined(linux) || !defined(HAVE_FSTATFS)
                struct statvfs stfs;

                if (fstatvfs( fd, &stfs ) < 0)
                {
                    io->u.Status = FILE_GetNtStatus();
                    break;
                }
2263
                bsize = stfs.f_frsize;
2264 2265 2266 2267 2268 2269 2270
#else
                struct statfs stfs;
                if (fstatfs( fd, &stfs ) < 0)
                {
                    io->u.Status = FILE_GetNtStatus();
                    break;
                }
2271
                bsize = stfs.f_bsize;
2272
#endif
2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284
                if (bsize == 2048)  /* assume CD-ROM */
                {
                    info->BytesPerSector = 2048;
                    info->SectorsPerAllocationUnit = 1;
                }
                else
                {
                    info->BytesPerSector = 512;
                    info->SectorsPerAllocationUnit = 8;
                }
                info->TotalAllocationUnits.QuadPart = bsize * stfs.f_blocks / (info->BytesPerSector * info->SectorsPerAllocationUnit);
                info->AvailableAllocationUnits.QuadPart = bsize * stfs.f_bavail / (info->BytesPerSector * info->SectorsPerAllocationUnit);
2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296
                io->Information = sizeof(*info);
                io->u.Status = STATUS_SUCCESS;
            }
        }
        break;
    case FileFsDeviceInformation:
        if (length < sizeof(FILE_FS_DEVICE_INFORMATION))
            io->u.Status = STATUS_BUFFER_TOO_SMALL;
        else
        {
            FILE_FS_DEVICE_INFORMATION *info = buffer;

2297
            if ((io->u.Status = get_device_info( fd, info )) == STATUS_SUCCESS)
2298
                io->Information = sizeof(*info);
2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319
        }
        break;
    case FileFsAttributeInformation:
        FIXME( "%p: attribute info not supported\n", handle );
        break;
    case FileFsControlInformation:
        FIXME( "%p: control info not supported\n", handle );
        break;
    case FileFsFullSizeInformation:
        FIXME( "%p: full size info not supported\n", handle );
        break;
    case FileFsObjectIdInformation:
        FIXME( "%p: object id info not supported\n", handle );
        break;
    case FileFsMaximumInformation:
        FIXME( "%p: maximum info not supported\n", handle );
        break;
    default:
        io->u.Status = STATUS_INVALID_PARAMETER;
        break;
    }
2320
    if (needs_close) close( fd );
2321
    return io->u.Status;
2322
}
2323

2324

2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377
/******************************************************************
 *		NtQueryEaFile  (NTDLL.@)
 *
 * Read extended attributes from NTFS files.
 *
 * PARAMS
 *  hFile         [I] File handle, must be opened with FILE_READ_EA access
 *  iosb          [O] Receives information about the operation on return
 *  buffer        [O] Output buffer
 *  length        [I] Length of output buffer
 *  single_entry  [I] Only read and return one entry
 *  ea_list       [I] Optional list with names of EAs to return
 *  ea_list_len   [I] Length of ea_list in bytes
 *  ea_index      [I] Optional pointer to 1-based index of attribute to return
 *  restart       [I] restart EA scan
 *
 * RETURNS
 *  Success: 0. Atrributes read into buffer
 *  Failure: An NTSTATUS error code describing the error.
 */
NTSTATUS WINAPI NtQueryEaFile( HANDLE hFile, PIO_STATUS_BLOCK iosb, PVOID buffer, ULONG length,
                               BOOLEAN single_entry, PVOID ea_list, ULONG ea_list_len,
                               PULONG ea_index, BOOLEAN restart )
{
    FIXME("(%p,%p,%p,%d,%d,%p,%d,%p,%d) stub\n",
            hFile, iosb, buffer, length, single_entry, ea_list,
            ea_list_len, ea_index, restart);
    return STATUS_ACCESS_DENIED;
}


/******************************************************************
 *		NtSetEaFile  (NTDLL.@)
 *
 * Update extended attributes for NTFS files.
 *
 * PARAMS
 *  hFile         [I] File handle, must be opened with FILE_READ_EA access
 *  iosb          [O] Receives information about the operation on return
 *  buffer        [I] Buffer with EA information
 *  length        [I] Length of buffer
 *
 * RETURNS
 *  Success: 0. Attributes are updated
 *  Failure: An NTSTATUS error code describing the error.
 */
NTSTATUS WINAPI NtSetEaFile( HANDLE hFile, PIO_STATUS_BLOCK iosb, PVOID buffer, ULONG length )
{
    FIXME("(%p,%p,%p,%d) stub\n", hFile, iosb, buffer, length);
    return STATUS_ACCESS_DENIED;
}


2378 2379
/******************************************************************
 *		NtFlushBuffersFile  (NTDLL.@)
Jon Griffiths's avatar
Jon Griffiths committed
2380 2381 2382 2383 2384 2385 2386 2387 2388 2389
 *
 * Flush any buffered data on an open file handle.
 *
 * PARAMS
 *  FileHandle         [I] Handle returned from ZwOpenFile() or ZwCreateFile()
 *  IoStatusBlock      [O] Receives information about the operation on return
 *
 * RETURNS
 *  Success: 0. IoStatusBlock is updated.
 *  Failure: An NTSTATUS error code describing the error.
2390 2391 2392 2393
 */
NTSTATUS WINAPI NtFlushBuffersFile( HANDLE hFile, IO_STATUS_BLOCK* IoStatusBlock )
{
    NTSTATUS ret;
2394 2395
    HANDLE hEvent = NULL;

2396 2397
    SERVER_START_REQ( flush_file )
    {
2398
        req->handle = wine_server_obj_handle( hFile );
2399
        ret = wine_server_call( req );
2400
        hEvent = wine_server_ptr_handle( reply->event );
2401 2402
    }
    SERVER_END_REQ;
2403
    if (!ret && hEvent)
2404 2405 2406 2407
    {
        ret = NtWaitForSingleObject( hEvent, FALSE, NULL );
        NtClose( hEvent );
    }
2408 2409
    return ret;
}
2410 2411 2412 2413 2414 2415 2416

/******************************************************************
 *		NtLockFile       (NTDLL.@)
 *
 *
 */
NTSTATUS WINAPI NtLockFile( HANDLE hFile, HANDLE lock_granted_event,
Jon Griffiths's avatar
Jon Griffiths committed
2417 2418
                            PIO_APC_ROUTINE apc, void* apc_user,
                            PIO_STATUS_BLOCK io_status, PLARGE_INTEGER offset,
2419 2420 2421 2422 2423 2424
                            PLARGE_INTEGER count, ULONG* key, BOOLEAN dont_wait,
                            BOOLEAN exclusive )
{
    NTSTATUS    ret;
    HANDLE      handle;
    BOOLEAN     async;
2425
    static BOOLEAN     warn = TRUE;
2426 2427 2428 2429 2430 2431 2432

    if (apc || io_status || key)
    {
        FIXME("Unimplemented yet parameter\n");
        return STATUS_NOT_IMPLEMENTED;
    }

2433 2434 2435 2436 2437
    if (apc_user && warn)
    {
        FIXME("I/O completion on lock not implemented yet\n");
        warn = FALSE;
    }
2438

2439 2440 2441 2442
    for (;;)
    {
        SERVER_START_REQ( lock_file )
        {
2443
            req->handle      = wine_server_obj_handle( hFile );
2444 2445
            req->offset      = offset->QuadPart;
            req->count       = count->QuadPart;
2446 2447 2448
            req->shared      = !exclusive;
            req->wait        = !dont_wait;
            ret = wine_server_call( req );
2449
            handle = wine_server_ptr_handle( reply->handle );
2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476
            async  = reply->overlapped;
        }
        SERVER_END_REQ;
        if (ret != STATUS_PENDING)
        {
            if (!ret && lock_granted_event) NtSetEvent(lock_granted_event, NULL);
            return ret;
        }

        if (async)
        {
            FIXME( "Async I/O lock wait not implemented, might deadlock\n" );
            if (handle) NtClose( handle );
            return STATUS_PENDING;
        }
        if (handle)
        {
            NtWaitForSingleObject( handle, FALSE, NULL );
            NtClose( handle );
        }
        else
        {
            LARGE_INTEGER time;
    
            /* Unix lock conflict, sleep a bit and retry */
            time.QuadPart = 100 * (ULONGLONG)10000;
            time.QuadPart = -time.QuadPart;
2477
            NtDelayExecution( FALSE, &time );
2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493
        }
    }
}


/******************************************************************
 *		NtUnlockFile    (NTDLL.@)
 *
 *
 */
NTSTATUS WINAPI NtUnlockFile( HANDLE hFile, PIO_STATUS_BLOCK io_status,
                              PLARGE_INTEGER offset, PLARGE_INTEGER count,
                              PULONG key )
{
    NTSTATUS status;

2494
    TRACE( "%p %x%08x %x%08x\n",
2495
           hFile, offset->u.HighPart, offset->u.LowPart, count->u.HighPart, count->u.LowPart );
2496 2497 2498 2499 2500 2501 2502 2503 2504

    if (io_status || key)
    {
        FIXME("Unimplemented yet parameter\n");
        return STATUS_NOT_IMPLEMENTED;
    }

    SERVER_START_REQ( unlock_file )
    {
2505
        req->handle = wine_server_obj_handle( hFile );
2506 2507
        req->offset = offset->QuadPart;
        req->count  = count->QuadPart;
2508 2509 2510 2511 2512
        status = wine_server_call( req );
    }
    SERVER_END_REQ;
    return status;
}
2513 2514 2515 2516 2517 2518

/******************************************************************
 *		NtCreateNamedPipeFile    (NTDLL.@)
 *
 *
 */
2519
NTSTATUS WINAPI NtCreateNamedPipeFile( PHANDLE handle, ULONG access,
2520
                                       POBJECT_ATTRIBUTES attr, PIO_STATUS_BLOCK iosb,
2521 2522 2523 2524 2525
                                       ULONG sharing, ULONG dispo, ULONG options,
                                       ULONG pipe_type, ULONG read_mode, 
                                       ULONG completion_mode, ULONG max_inst,
                                       ULONG inbound_quota, ULONG outbound_quota,
                                       PLARGE_INTEGER timeout)
2526
{
2527 2528
    NTSTATUS    status;

2529
    TRACE("(%p %x %s %p %x %d %x %d %d %d %d %d %d %p)\n",
2530
          handle, access, debugstr_w(attr->ObjectName->Buffer), iosb, sharing, dispo,
2531 2532
          options, pipe_type, read_mode, completion_mode, max_inst, inbound_quota,
          outbound_quota, timeout);
2533

2534 2535
    /* assume we only get relative timeout */
    if (timeout->QuadPart > 0)
2536 2537 2538 2539
        FIXME("Wrong time %s\n", wine_dbgstr_longlong(timeout->QuadPart));

    SERVER_START_REQ( create_named_pipe )
    {
2540
        req->access  = access;
2541
        req->attributes = attr->Attributes;
2542
        req->rootdir = wine_server_obj_handle( attr->RootDirectory );
2543
        req->options = options;
2544 2545 2546 2547 2548 2549 2550
        req->flags = 
            (pipe_type) ? NAMED_PIPE_MESSAGE_STREAM_WRITE : 0 |
            (read_mode) ? NAMED_PIPE_MESSAGE_STREAM_READ  : 0 |
            (completion_mode) ? NAMED_PIPE_NONBLOCKING_MODE  : 0;
        req->maxinstances = max_inst;
        req->outsize = outbound_quota;
        req->insize  = inbound_quota;
2551
        req->timeout = timeout->QuadPart;
2552 2553
        wine_server_add_data( req, attr->ObjectName->Buffer,
                              attr->ObjectName->Length );
2554
        status = wine_server_call( req );
2555
        if (!status) *handle = wine_server_ptr_handle( reply->handle );
2556 2557 2558
    }
    SERVER_END_REQ;
    return status;
2559
}
2560 2561 2562 2563 2564 2565 2566 2567

/******************************************************************
 *		NtDeleteFile    (NTDLL.@)
 *
 *
 */
NTSTATUS WINAPI NtDeleteFile( POBJECT_ATTRIBUTES ObjectAttributes )
{
2568 2569 2570 2571 2572
    NTSTATUS status;
    HANDLE hFile;
    IO_STATUS_BLOCK io;

    TRACE("%p\n", ObjectAttributes);
2573 2574
    status = NtCreateFile( &hFile, GENERIC_READ | GENERIC_WRITE | DELETE,
                           ObjectAttributes, &io, NULL, 0,
2575 2576 2577 2578
                           FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 
                           FILE_OPEN, FILE_DELETE_ON_CLOSE, NULL, 0 );
    if (status == STATUS_SUCCESS) status = NtClose(hFile);
    return status;
2579 2580 2581 2582 2583 2584 2585
}

/******************************************************************
 *		NtCancelIoFile    (NTDLL.@)
 *
 *
 */
2586
NTSTATUS WINAPI NtCancelIoFile( HANDLE hFile, PIO_STATUS_BLOCK io_status )
2587
{
2588 2589 2590 2591 2592 2593
    LARGE_INTEGER timeout;

    TRACE("%p %p\n", hFile, io_status );

    SERVER_START_REQ( cancel_async )
    {
2594
        req->handle = wine_server_obj_handle( hFile );
2595 2596 2597 2598 2599 2600 2601 2602 2603 2604
        wine_server_call( req );
    }
    SERVER_END_REQ;
    /* Let some APC be run, so that we can run the remaining APCs on hFile
     * either the cancelation of the pending one, but also the execution
     * of the queued APC, but not yet run. This is needed to ensure proper
     * clean-up of allocated data.
     */
    timeout.u.LowPart = timeout.u.HighPart = 0;
    return io_status->u.Status = NtDelayExecution( TRUE, &timeout );
2605
}
2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628

/******************************************************************************
 *  NtCreateMailslotFile	[NTDLL.@]
 *  ZwCreateMailslotFile	[NTDLL.@]
 *
 * PARAMS
 *  pHandle          [O] pointer to receive the handle created
 *  DesiredAccess    [I] access mode (read, write, etc)
 *  ObjectAttributes [I] fully qualified NT path of the mailslot
 *  IoStatusBlock    [O] receives completion status and other info
 *  CreateOptions    [I]
 *  MailslotQuota    [I]
 *  MaxMessageSize   [I]
 *  TimeOut          [I]
 *
 * RETURNS
 *  An NT status code
 */
NTSTATUS WINAPI NtCreateMailslotFile(PHANDLE pHandle, ULONG DesiredAccess,
     POBJECT_ATTRIBUTES attr, PIO_STATUS_BLOCK IoStatusBlock,
     ULONG CreateOptions, ULONG MailslotQuota, ULONG MaxMessageSize,
     PLARGE_INTEGER TimeOut)
{
2629
    LARGE_INTEGER timeout;
2630 2631
    NTSTATUS ret;

2632
    TRACE("%p %08x %p %p %08x %08x %08x %p\n",
2633 2634 2635
              pHandle, DesiredAccess, attr, IoStatusBlock,
              CreateOptions, MailslotQuota, MaxMessageSize, TimeOut);

2636
    if (!pHandle) return STATUS_ACCESS_VIOLATION;
2637 2638 2639
    if (!attr) return STATUS_INVALID_PARAMETER;
    if (!attr->ObjectName) return STATUS_OBJECT_PATH_SYNTAX_BAD;

2640 2641 2642 2643 2644 2645 2646 2647
    /*
     *  For a NULL TimeOut pointer set the default timeout value
     */
    if  (!TimeOut)
        timeout.QuadPart = -1;
    else
        timeout.QuadPart = TimeOut->QuadPart;

Mike McCormack's avatar
Mike McCormack committed
2648 2649
    SERVER_START_REQ( create_mailslot )
    {
2650
        req->access = DesiredAccess;
2651
        req->attributes = attr->Attributes;
2652
        req->rootdir = wine_server_obj_handle( attr->RootDirectory );
Mike McCormack's avatar
Mike McCormack committed
2653
        req->max_msgsize = MaxMessageSize;
2654
        req->read_timeout = timeout.QuadPart;
2655 2656
        wine_server_add_data( req, attr->ObjectName->Buffer,
                              attr->ObjectName->Length );
Mike McCormack's avatar
Mike McCormack committed
2657 2658
        ret = wine_server_call( req );
        if( ret == STATUS_SUCCESS )
2659
            *pHandle = wine_server_ptr_handle( reply->handle );
Mike McCormack's avatar
Mike McCormack committed
2660 2661 2662
    }
    SERVER_END_REQ;
 
2663 2664
    return ret;
}