2 * Copyright 1999, 2000 Juergen Schmied
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 #include "wine/port.h"
30 #ifdef HAVE_SYS_ERRNO_H
31 #include <sys/errno.h>
33 #ifdef HAVE_LINUX_MAJOR_H
34 # include <linux/major.h>
36 #ifdef HAVE_SYS_STATVFS_H
37 # include <sys/statvfs.h>
39 #ifdef HAVE_SYS_PARAM_H
40 # include <sys/param.h>
42 #ifdef HAVE_SYS_TIME_H
43 # include <sys/time.h>
48 #ifdef STATFS_DEFINED_BY_SYS_VFS
51 # ifdef STATFS_DEFINED_BY_SYS_MOUNT
52 # include <sys/mount.h>
54 # ifdef STATFS_DEFINED_BY_SYS_STATFS
55 # include <sys/statfs.h>
60 #define NONAMELESSUNION
61 #define NONAMELESSSTRUCT
62 #include "wine/unicode.h"
63 #include "wine/debug.h"
64 #include "wine/server.h"
66 #include "ntdll_misc.h"
71 WINE_DEFAULT_DEBUG_CHANNEL(ntdll
);
73 mode_t FILE_umask
= 0;
75 #define SECSPERDAY 86400
76 #define SECS_1601_TO_1970 ((369 * 365 + 89) * (ULONGLONG)SECSPERDAY)
78 /**************************************************************************
79 * NtOpenFile [NTDLL.@]
80 * ZwOpenFile [NTDLL.@]
85 * handle [O] Variable that receives the file handle on return
86 * access [I] Access desired by the caller to the file
87 * attr [I] Structue describing the file to be opened
88 * io [O] Receives details about the result of the operation
89 * sharing [I] Type of shared access the caller requires
90 * options [I] Options for the file open
93 * Success: 0. FileHandle and IoStatusBlock are updated.
94 * Failure: An NTSTATUS error code describing the error.
96 NTSTATUS WINAPI
NtOpenFile( PHANDLE handle
, ACCESS_MASK access
,
97 POBJECT_ATTRIBUTES attr
, PIO_STATUS_BLOCK io
,
98 ULONG sharing
, ULONG options
)
100 return NtCreateFile( handle
, access
, attr
, io
, NULL
, 0,
101 sharing
, FILE_OPEN
, options
, NULL
, 0 );
104 /**************************************************************************
105 * NtCreateFile [NTDLL.@]
106 * ZwCreateFile [NTDLL.@]
108 * Either create a new file or directory, or open an existing file, device,
109 * directory or volume.
112 * handle [O] Points to a variable which receives the file handle on return
113 * access [I] Desired access to the file
114 * attr [I] Structure describing the file
115 * io [O] Receives information about the operation on return
116 * alloc_size [I] Initial size of the file in bytes
117 * attributes [I] Attributes to create the file with
118 * sharing [I] Type of shared access the caller would like to the file
119 * disposition [I] Specifies what to do, depending on whether the file already exists
120 * options [I] Options for creating a new file
121 * ea_buffer [I] Undocumented
122 * ea_length [I] Undocumented
125 * Success: 0. handle and io are updated.
126 * Failure: An NTSTATUS error code describing the error.
128 NTSTATUS WINAPI
NtCreateFile( PHANDLE handle
, ACCESS_MASK access
, POBJECT_ATTRIBUTES attr
,
129 PIO_STATUS_BLOCK io
, PLARGE_INTEGER alloc_size
,
130 ULONG attributes
, ULONG sharing
, ULONG disposition
,
131 ULONG options
, PVOID ea_buffer
, ULONG ea_length
)
133 ANSI_STRING unix_name
;
134 int check_last
, created
= FALSE
;
136 TRACE("handle=%p access=%08lx name=%s objattr=%08lx root=%p sec=%p io=%p alloc_size=%p\n"
137 "attr=%08lx sharing=%08lx disp=%ld options=%08lx ea=%p.0x%08lx\n",
138 handle
, access
, debugstr_us(attr
->ObjectName
), attr
->Attributes
,
139 attr
->RootDirectory
, attr
->SecurityDescriptor
, io
, alloc_size
,
140 attributes
, sharing
, disposition
, options
, ea_buffer
, ea_length
);
142 if (attr
->RootDirectory
)
144 FIXME( "RootDirectory %p not supported\n", attr
->RootDirectory
);
145 return STATUS_OBJECT_NAME_NOT_FOUND
;
147 if (alloc_size
) FIXME( "alloc_size not supported\n" );
149 check_last
= (disposition
== FILE_OPEN
|| disposition
== FILE_OVERWRITE
);
151 io
->u
.Status
= wine_nt_to_unix_file_name( attr
->ObjectName
, &unix_name
, check_last
,
152 !(attr
->Attributes
& OBJ_CASE_INSENSITIVE
) );
154 if (!check_last
&& io
->u
.Status
== STATUS_NO_SUCH_FILE
)
157 io
->u
.Status
= STATUS_SUCCESS
;
160 if (io
->u
.Status
== STATUS_SUCCESS
)
162 SERVER_START_REQ( create_file
)
164 req
->access
= access
;
165 req
->inherit
= (attr
->Attributes
& OBJ_INHERIT
) != 0;
166 req
->sharing
= sharing
;
167 req
->create
= disposition
;
168 req
->options
= options
;
169 req
->attrs
= attributes
;
170 wine_server_add_data( req
, unix_name
.Buffer
, unix_name
.Length
);
171 io
->u
.Status
= wine_server_call( req
);
172 *handle
= reply
->handle
;
175 RtlFreeAnsiString( &unix_name
);
177 else WARN("%s not found (%lx)\n", debugstr_us(attr
->ObjectName
), io
->u
.Status
);
179 if (io
->u
.Status
== STATUS_SUCCESS
)
181 if (created
) io
->Information
= FILE_CREATED
;
182 else switch(disposition
)
185 io
->Information
= FILE_SUPERSEDED
;
188 io
->Information
= FILE_CREATED
;
192 io
->Information
= FILE_OPENED
;
195 case FILE_OVERWRITE_IF
:
196 io
->Information
= FILE_OVERWRITTEN
;
204 /***********************************************************************
205 * Asynchronous file I/O *
207 static DWORD
fileio_get_async_count(const async_private
*ovp
);
208 static void CALLBACK
fileio_call_completion_func(ULONG_PTR data
);
209 static void fileio_async_cleanup(async_private
*ovp
);
211 static async_ops fileio_async_ops
=
213 fileio_get_async_count
, /* get_count */
214 fileio_call_completion_func
, /* call_completion */
215 fileio_async_cleanup
/* cleanup */
218 static async_ops fileio_nocomp_async_ops
=
220 fileio_get_async_count
, /* get_count */
221 NULL
, /* call_completion */
222 fileio_async_cleanup
/* cleanup */
225 typedef struct async_fileio
227 struct async_private async
;
232 unsigned long offset
;
233 enum fd_type fd_type
;
236 static DWORD
fileio_get_async_count(const struct async_private
*ovp
)
238 async_fileio
*fileio
= (async_fileio
*) ovp
;
240 if (fileio
->count
< fileio
->async
.iosb
->Information
)
242 return fileio
->count
- fileio
->async
.iosb
->Information
;
245 static void CALLBACK
fileio_call_completion_func(ULONG_PTR data
)
247 async_fileio
*ovp
= (async_fileio
*) data
;
248 TRACE("data: %p\n", ovp
);
250 ovp
->apc( ovp
->apc_user
, ovp
->async
.iosb
, ovp
->async
.iosb
->Information
);
252 fileio_async_cleanup( &ovp
->async
);
255 static void fileio_async_cleanup( struct async_private
*ovp
)
257 RtlFreeHeap( GetProcessHeap(), 0, ovp
);
260 /***********************************************************************
261 * FILE_GetNtStatus(void)
263 * Retrieve the Nt Status code from errno.
264 * Try to be consistent with FILE_SetDosError().
266 NTSTATUS
FILE_GetNtStatus(void)
270 TRACE( "errno = %d\n", errno
);
273 case EAGAIN
: return STATUS_SHARING_VIOLATION
;
274 case EBADF
: return STATUS_INVALID_HANDLE
;
275 case ENOSPC
: return STATUS_DISK_FULL
;
278 case EACCES
: return STATUS_ACCESS_DENIED
;
279 case ENOTDIR
: return STATUS_OBJECT_PATH_NOT_FOUND
;
280 case ENOENT
: return STATUS_OBJECT_NAME_NOT_FOUND
;
281 case EISDIR
: return STATUS_FILE_IS_A_DIRECTORY
;
283 case ENFILE
: return STATUS_TOO_MANY_OPENED_FILES
;
284 case EINVAL
: return STATUS_INVALID_PARAMETER
;
285 case ENOTEMPTY
: return STATUS_DIRECTORY_NOT_EMPTY
;
286 case EPIPE
: return STATUS_PIPE_BROKEN
;
287 case EIO
: return STATUS_DEVICE_NOT_READY
;
288 case ENOEXEC
: /* ?? */
289 case ESPIPE
: /* ?? */
290 case EEXIST
: /* ?? */
292 FIXME( "Converting errno %d to STATUS_UNSUCCESSFUL\n", err
);
293 return STATUS_UNSUCCESSFUL
;
297 /***********************************************************************
298 * FILE_AsyncReadService (INTERNAL)
300 * This function is called while the client is waiting on the
301 * server, so we can't make any server calls here.
303 static void FILE_AsyncReadService(async_private
*ovp
)
305 async_fileio
*fileio
= (async_fileio
*) ovp
;
306 IO_STATUS_BLOCK
* io_status
= fileio
->async
.iosb
;
308 int already
= io_status
->Information
;
310 TRACE("%p %p\n", io_status
, fileio
->buffer
);
312 /* check to see if the data is ready (non-blocking) */
314 if ( fileio
->fd_type
== FD_TYPE_SOCKET
)
315 result
= read(ovp
->fd
, &fileio
->buffer
[already
], fileio
->count
- already
);
318 result
= pread(ovp
->fd
, &fileio
->buffer
[already
], fileio
->count
- already
,
319 fileio
->offset
+ already
);
320 if ((result
< 0) && (errno
== ESPIPE
))
321 result
= read(ovp
->fd
, &fileio
->buffer
[already
], fileio
->count
- already
);
324 if ((result
< 0) && ((errno
== EAGAIN
) || (errno
== EINTR
)))
326 TRACE("Deferred read %d\n",errno
);
327 io_status
->u
.Status
= STATUS_PENDING
;
331 /* check to see if the transfer is complete */
334 io_status
->u
.Status
= FILE_GetNtStatus();
337 else if (result
== 0)
339 io_status
->u
.Status
= io_status
->Information
? STATUS_SUCCESS
: STATUS_END_OF_FILE
;
343 io_status
->Information
+= result
;
344 if (io_status
->Information
>= fileio
->count
|| fileio
->fd_type
== FD_TYPE_SOCKET
)
345 io_status
->u
.Status
= STATUS_SUCCESS
;
347 io_status
->u
.Status
= STATUS_PENDING
;
349 TRACE("read %d more bytes %ld/%d so far\n",
350 result
, io_status
->Information
, fileio
->count
);
354 /******************************************************************************
355 * NtReadFile [NTDLL.@]
356 * ZwReadFile [NTDLL.@]
358 * Read from an open file handle.
361 * FileHandle [I] Handle returned from ZwOpenFile() or ZwCreateFile()
362 * Event [I] Event to signal upon completion (or NULL)
363 * ApcRoutine [I] Callback to call upon completion (or NULL)
364 * ApcContext [I] Context for ApcRoutine (or NULL)
365 * IoStatusBlock [O] Receives information about the operation on return
366 * Buffer [O] Destination for the data read
367 * Length [I] Size of Buffer
368 * ByteOffset [O] Destination for the new file pointer position (or NULL)
369 * Key [O] Function unknown (may be NULL)
372 * Success: 0. IoStatusBlock is updated, and the Information member contains
373 * The number of bytes read.
374 * Failure: An NTSTATUS error code describing the error.
376 NTSTATUS WINAPI
NtReadFile(HANDLE hFile
, HANDLE hEvent
,
377 PIO_APC_ROUTINE apc
, void* apc_user
,
378 PIO_STATUS_BLOCK io_status
, void* buffer
, ULONG length
,
379 PLARGE_INTEGER offset
, PULONG key
)
381 int unix_handle
, flags
;
384 TRACE("(%p,%p,%p,%p,%p,%p,0x%08lx,%p,%p),partial stub!\n",
385 hFile
,hEvent
,apc
,apc_user
,io_status
,buffer
,length
,offset
,key
);
387 io_status
->Information
= 0;
388 io_status
->u
.Status
= wine_server_handle_to_fd( hFile
, GENERIC_READ
, &unix_handle
, &type
, &flags
);
389 if (io_status
->u
.Status
) return io_status
->u
.Status
;
391 if (flags
& FD_FLAG_RECV_SHUTDOWN
)
393 wine_server_release_fd( hFile
, unix_handle
);
394 return STATUS_PIPE_DISCONNECTED
;
397 if (flags
& FD_FLAG_TIMEOUT
)
401 /* this shouldn't happen, but check it */
402 FIXME("NIY-hEvent\n");
403 wine_server_release_fd( hFile
, unix_handle
);
404 return STATUS_NOT_IMPLEMENTED
;
406 io_status
->u
.Status
= NtCreateEvent(&hEvent
, SYNCHRONIZE
, NULL
, 0, 0);
407 if (io_status
->u
.Status
)
409 wine_server_release_fd( hFile
, unix_handle
);
410 return io_status
->u
.Status
;
414 if (flags
& (FD_FLAG_OVERLAPPED
|FD_FLAG_TIMEOUT
))
419 if (!(ovp
= RtlAllocateHeap(GetProcessHeap(), 0, sizeof(async_fileio
))))
421 wine_server_release_fd( hFile
, unix_handle
);
422 return STATUS_NO_MEMORY
;
424 ovp
->async
.ops
= (apc
? &fileio_async_ops
: &fileio_nocomp_async_ops
);
425 ovp
->async
.handle
= hFile
;
426 ovp
->async
.fd
= unix_handle
; /* FIXME */
427 ovp
->async
.type
= ASYNC_TYPE_READ
;
428 ovp
->async
.func
= FILE_AsyncReadService
;
429 ovp
->async
.event
= hEvent
;
430 ovp
->async
.iosb
= io_status
;
432 if ( offset
== NULL
)
436 ovp
->offset
= offset
->u
.LowPart
;
437 if (offset
->u
.HighPart
) FIXME("NIY-high part\n");
440 ovp
->apc_user
= apc_user
;
441 ovp
->buffer
= buffer
;
444 io_status
->Information
= 0;
445 ret
= register_new_async(&ovp
->async
);
446 if (ret
!= STATUS_SUCCESS
)
448 if (flags
& FD_FLAG_TIMEOUT
)
450 NtWaitForSingleObject(hEvent
, TRUE
, NULL
);
455 LARGE_INTEGER timeout
;
457 /* let some APC be run, this will read some already pending data */
458 timeout
.u
.LowPart
= timeout
.u
.HighPart
= 0;
459 NtDelayExecution( TRUE
, &timeout
);
461 return io_status
->u
.Status
;
468 /* return SMB_ReadFile(hFile, unix_handle, buffer, length, io_status); */
469 wine_server_release_fd( hFile
, unix_handle
);
470 return STATUS_INVALID_HANDLE
;
472 case FD_TYPE_DEFAULT
:
473 /* normal unix file */
477 FIXME("Unsupported type of fd %d\n", type
);
478 wine_server_release_fd( hFile
, unix_handle
);
479 return STATUS_INVALID_HANDLE
;
484 FILE_POSITION_INFORMATION fpi
;
486 fpi
.CurrentByteOffset
= *offset
;
487 io_status
->u
.Status
= NtSetInformationFile(hFile
, io_status
, &fpi
, sizeof(fpi
),
488 FilePositionInformation
);
489 if (io_status
->u
.Status
)
491 wine_server_release_fd( hFile
, unix_handle
);
492 return io_status
->u
.Status
;
495 /* code for synchronous reads */
496 while ((io_status
->Information
= read( unix_handle
, buffer
, length
)) == -1)
498 if ((errno
== EAGAIN
) || (errno
== EINTR
)) continue;
499 if (errno
== EFAULT
) FIXME( "EFAULT handling broken for now\n" );
500 io_status
->u
.Status
= FILE_GetNtStatus();
503 wine_server_release_fd( hFile
, unix_handle
);
504 return io_status
->u
.Status
;
507 /***********************************************************************
508 * FILE_AsyncWriteService (INTERNAL)
510 * This function is called while the client is waiting on the
511 * server, so we can't make any server calls here.
513 static void FILE_AsyncWriteService(struct async_private
*ovp
)
515 async_fileio
*fileio
= (async_fileio
*) ovp
;
516 PIO_STATUS_BLOCK io_status
= fileio
->async
.iosb
;
518 int already
= io_status
->Information
;
520 TRACE("(%p %p)\n",io_status
,fileio
->buffer
);
522 /* write some data (non-blocking) */
524 if ( fileio
->fd_type
== FD_TYPE_SOCKET
)
525 result
= write(ovp
->fd
, &fileio
->buffer
[already
], fileio
->count
- already
);
528 result
= pwrite(ovp
->fd
, &fileio
->buffer
[already
], fileio
->count
- already
,
529 fileio
->offset
+ already
);
530 if ((result
< 0) && (errno
== ESPIPE
))
531 result
= write(ovp
->fd
, &fileio
->buffer
[already
], fileio
->count
- already
);
534 if ((result
< 0) && ((errno
== EAGAIN
) || (errno
== EINTR
)))
536 io_status
->u
.Status
= STATUS_PENDING
;
540 /* check to see if the transfer is complete */
543 io_status
->u
.Status
= FILE_GetNtStatus();
547 io_status
->Information
+= result
;
548 io_status
->u
.Status
= (io_status
->Information
< fileio
->count
) ? STATUS_PENDING
: STATUS_SUCCESS
;
549 TRACE("wrote %d more bytes %ld/%d so far\n",result
,io_status
->Information
,fileio
->count
);
552 /******************************************************************************
553 * NtWriteFile [NTDLL.@]
554 * ZwWriteFile [NTDLL.@]
556 * Write to an open file handle.
559 * FileHandle [I] Handle returned from ZwOpenFile() or ZwCreateFile()
560 * Event [I] Event to signal upon completion (or NULL)
561 * ApcRoutine [I] Callback to call upon completion (or NULL)
562 * ApcContext [I] Context for ApcRoutine (or NULL)
563 * IoStatusBlock [O] Receives information about the operation on return
564 * Buffer [I] Source for the data to write
565 * Length [I] Size of Buffer
566 * ByteOffset [O] Destination for the new file pointer position (or NULL)
567 * Key [O] Function unknown (may be NULL)
570 * Success: 0. IoStatusBlock is updated, and the Information member contains
571 * The number of bytes written.
572 * Failure: An NTSTATUS error code describing the error.
574 NTSTATUS WINAPI
NtWriteFile(HANDLE hFile
, HANDLE hEvent
,
575 PIO_APC_ROUTINE apc
, void* apc_user
,
576 PIO_STATUS_BLOCK io_status
,
577 const void* buffer
, ULONG length
,
578 PLARGE_INTEGER offset
, PULONG key
)
580 int unix_handle
, flags
;
583 TRACE("(%p,%p,%p,%p,%p,%p,0x%08lx,%p,%p)!\n",
584 hFile
,hEvent
,apc
,apc_user
,io_status
,buffer
,length
,offset
,key
);
586 TRACE("(%p,%p,%p,%p,%p,%p,0x%08lx,%p,%p),partial stub!\n",
587 hFile
,hEvent
,apc
,apc_user
,io_status
,buffer
,length
,offset
,key
);
589 io_status
->Information
= 0;
590 io_status
->u
.Status
= wine_server_handle_to_fd( hFile
, GENERIC_WRITE
, &unix_handle
, &type
, &flags
);
591 if (io_status
->u
.Status
) return io_status
->u
.Status
;
593 if (flags
& FD_FLAG_SEND_SHUTDOWN
)
595 wine_server_release_fd( hFile
, unix_handle
);
596 return STATUS_PIPE_DISCONNECTED
;
599 if (flags
& (FD_FLAG_OVERLAPPED
|FD_FLAG_TIMEOUT
))
604 if (!(ovp
= RtlAllocateHeap(GetProcessHeap(), 0, sizeof(async_fileio
))))
606 wine_server_release_fd( hFile
, unix_handle
);
607 return STATUS_NO_MEMORY
;
609 ovp
->async
.ops
= (apc
? &fileio_async_ops
: &fileio_nocomp_async_ops
);
610 ovp
->async
.handle
= hFile
;
611 ovp
->async
.fd
= unix_handle
; /* FIXME */
612 ovp
->async
.type
= ASYNC_TYPE_WRITE
;
613 ovp
->async
.func
= FILE_AsyncWriteService
;
614 ovp
->async
.event
= hEvent
;
615 ovp
->async
.iosb
= io_status
;
618 ovp
->offset
= offset
->u
.LowPart
;
619 if (offset
->u
.HighPart
) FIXME("NIY-high part\n");
624 ovp
->apc_user
= apc_user
;
625 ovp
->buffer
= (void*)buffer
;
628 io_status
->Information
= 0;
629 ret
= register_new_async(&ovp
->async
);
630 if (ret
!= STATUS_SUCCESS
)
632 if (flags
& FD_FLAG_TIMEOUT
)
634 NtWaitForSingleObject(hEvent
, TRUE
, NULL
);
639 LARGE_INTEGER timeout
;
641 /* let some APC be run, this will write as much data as possible */
642 timeout
.u
.LowPart
= timeout
.u
.HighPart
= 0;
643 NtDelayExecution( TRUE
, &timeout
);
645 return io_status
->u
.Status
;
651 wine_server_release_fd( hFile
, unix_handle
);
652 return STATUS_NOT_IMPLEMENTED
;
654 case FD_TYPE_DEFAULT
:
655 /* normal unix files */
656 if (unix_handle
== -1) return STATUS_INVALID_HANDLE
;
660 FIXME("Unsupported type of fd %d\n", type
);
661 wine_server_release_fd( hFile
, unix_handle
);
662 return STATUS_INVALID_HANDLE
;
667 FILE_POSITION_INFORMATION fpi
;
669 fpi
.CurrentByteOffset
= *offset
;
670 io_status
->u
.Status
= NtSetInformationFile(hFile
, io_status
, &fpi
, sizeof(fpi
),
671 FilePositionInformation
);
672 if (io_status
->u
.Status
)
674 wine_server_release_fd( hFile
, unix_handle
);
675 return io_status
->u
.Status
;
679 /* synchronous file write */
680 while ((io_status
->Information
= write( unix_handle
, buffer
, length
)) == -1)
682 if ((errno
== EAGAIN
) || (errno
== EINTR
)) continue;
683 if (errno
== EFAULT
) FIXME( "EFAULT handling broken for now\n" );
684 if (errno
== ENOSPC
) io_status
->u
.Status
= STATUS_DISK_FULL
;
685 else io_status
->u
.Status
= FILE_GetNtStatus();
688 wine_server_release_fd( hFile
, unix_handle
);
689 return io_status
->u
.Status
;
692 /**************************************************************************
693 * NtDeviceIoControlFile [NTDLL.@]
694 * ZwDeviceIoControlFile [NTDLL.@]
696 * Perform an I/O control operation on an open file handle.
699 * DeviceHandle [I] Handle returned from ZwOpenFile() or ZwCreateFile()
700 * Event [I] Event to signal upon completion (or NULL)
701 * ApcRoutine [I] Callback to call upon completion (or NULL)
702 * ApcContext [I] Context for ApcRoutine (or NULL)
703 * IoStatusBlock [O] Receives information about the operation on return
704 * IoControlCode [I] Control code for the operation to perform
705 * InputBuffer [I] Source for any input data required (or NULL)
706 * InputBufferSize [I] Size of InputBuffer
707 * OutputBuffer [O] Source for any output data returned (or NULL)
708 * OutputBufferSize [I] Size of OutputBuffer
711 * Success: 0. IoStatusBlock is updated.
712 * Failure: An NTSTATUS error code describing the error.
714 NTSTATUS WINAPI
NtDeviceIoControlFile(HANDLE DeviceHandle
, HANDLE hEvent
,
715 PIO_APC_ROUTINE UserApcRoutine
,
716 PVOID UserApcContext
,
717 PIO_STATUS_BLOCK IoStatusBlock
,
720 ULONG InputBufferSize
,
722 ULONG OutputBufferSize
)
724 TRACE("(%p,%p,%p,%p,%p,0x%08lx,%p,0x%08lx,%p,0x%08lx)\n",
725 DeviceHandle
, hEvent
, UserApcRoutine
, UserApcContext
,
726 IoStatusBlock
, IoControlCode
,
727 InputBuffer
, InputBufferSize
, OutputBuffer
, OutputBufferSize
);
729 if (CDROM_DeviceIoControl(DeviceHandle
, hEvent
,
730 UserApcRoutine
, UserApcContext
,
731 IoStatusBlock
, IoControlCode
,
732 InputBuffer
, InputBufferSize
,
733 OutputBuffer
, OutputBufferSize
) == STATUS_NO_SUCH_DEVICE
)
735 /* it wasn't a CDROM */
736 FIXME("Unimplemented dwIoControlCode=%08lx\n", IoControlCode
);
737 IoStatusBlock
->u
.Status
= STATUS_NOT_IMPLEMENTED
;
738 IoStatusBlock
->Information
= 0;
739 if (hEvent
) NtSetEvent(hEvent
, NULL
);
741 return IoStatusBlock
->u
.Status
;
744 /******************************************************************************
745 * NtFsControlFile [NTDLL.@]
746 * ZwFsControlFile [NTDLL.@]
748 NTSTATUS WINAPI
NtFsControlFile(
749 IN HANDLE DeviceHandle
,
750 IN HANDLE Event OPTIONAL
,
751 IN PIO_APC_ROUTINE ApcRoutine OPTIONAL
,
752 IN PVOID ApcContext OPTIONAL
,
753 OUT PIO_STATUS_BLOCK IoStatusBlock
,
754 IN ULONG IoControlCode
,
755 IN PVOID InputBuffer
,
756 IN ULONG InputBufferSize
,
757 OUT PVOID OutputBuffer
,
758 IN ULONG OutputBufferSize
)
760 FIXME("(%p,%p,%p,%p,%p,0x%08lx,%p,0x%08lx,%p,0x%08lx): stub\n",
761 DeviceHandle
,Event
,ApcRoutine
,ApcContext
,IoStatusBlock
,IoControlCode
,
762 InputBuffer
,InputBufferSize
,OutputBuffer
,OutputBufferSize
);
766 /******************************************************************************
767 * NtSetVolumeInformationFile [NTDLL.@]
768 * ZwSetVolumeInformationFile [NTDLL.@]
770 * Set volume information for an open file handle.
773 * FileHandle [I] Handle returned from ZwOpenFile() or ZwCreateFile()
774 * IoStatusBlock [O] Receives information about the operation on return
775 * FsInformation [I] Source for volume information
776 * Length [I] Size of FsInformation
777 * FsInformationClass [I] Type of volume information to set
780 * Success: 0. IoStatusBlock is updated.
781 * Failure: An NTSTATUS error code describing the error.
783 NTSTATUS WINAPI
NtSetVolumeInformationFile(
784 IN HANDLE FileHandle
,
785 PIO_STATUS_BLOCK IoStatusBlock
,
788 FS_INFORMATION_CLASS FsInformationClass
)
790 FIXME("(%p,%p,%p,0x%08lx,0x%08x) stub\n",
791 FileHandle
,IoStatusBlock
,FsInformation
,Length
,FsInformationClass
);
795 /******************************************************************************
796 * NtQueryInformationFile [NTDLL.@]
797 * ZwQueryInformationFile [NTDLL.@]
799 * Get information about an open file handle.
802 * hFile [I] Handle returned from ZwOpenFile() or ZwCreateFile()
803 * io [O] Receives information about the operation on return
804 * ptr [O] Destination for file information
805 * len [I] Size of FileInformation
806 * class [I] Type of file information to get
809 * Success: 0. IoStatusBlock and FileInformation are updated.
810 * Failure: An NTSTATUS error code describing the error.
812 NTSTATUS WINAPI
NtQueryInformationFile( HANDLE hFile
, PIO_STATUS_BLOCK io
,
813 PVOID ptr
, LONG len
, FILE_INFORMATION_CLASS
class )
817 TRACE("(%p,%p,%p,0x%08lx,0x%08x)\n", hFile
, io
, ptr
, len
, class);
820 if ((io
->u
.Status
= wine_server_handle_to_fd( hFile
, 0, &fd
, NULL
, NULL
)))
825 case FileBasicInformation
:
827 FILE_BASIC_INFORMATION
*info
= ptr
;
829 if (len
< sizeof(*info
)) io
->u
.Status
= STATUS_INFO_LENGTH_MISMATCH
;
834 if (fstat( fd
, &st
) == -1)
835 io
->u
.Status
= FILE_GetNtStatus();
836 else if (!S_ISREG(st
.st_mode
) && !S_ISDIR(st
.st_mode
))
837 io
->u
.Status
= STATUS_INVALID_INFO_CLASS
;
840 if (S_ISDIR(st
.st_mode
)) info
->FileAttributes
= FILE_ATTRIBUTE_DIRECTORY
;
841 else info
->FileAttributes
= FILE_ATTRIBUTE_ARCHIVE
;
842 if (!(st
.st_mode
& S_IWUSR
)) info
->FileAttributes
|= FILE_ATTRIBUTE_READONLY
;
843 RtlSecondsSince1970ToTime( st
.st_mtime
, &info
->CreationTime
);
844 RtlSecondsSince1970ToTime( st
.st_mtime
, &info
->LastWriteTime
);
845 RtlSecondsSince1970ToTime( st
.st_ctime
, &info
->ChangeTime
);
846 RtlSecondsSince1970ToTime( st
.st_atime
, &info
->LastAccessTime
);
851 case FileStandardInformation
:
853 FILE_STANDARD_INFORMATION
*info
= ptr
;
855 if (len
< sizeof(*info
)) io
->u
.Status
= STATUS_INFO_LENGTH_MISMATCH
;
860 if (fstat( fd
, &st
) == -1) io
->u
.Status
= FILE_GetNtStatus();
863 if ((info
->Directory
= S_ISDIR(st
.st_mode
)))
865 info
->AllocationSize
.QuadPart
= 0;
866 info
->EndOfFile
.QuadPart
= 0;
867 info
->NumberOfLinks
= 1;
868 info
->DeletePending
= FALSE
;
872 info
->AllocationSize
.QuadPart
= (ULONGLONG
)st
.st_blocks
* 512;
873 info
->EndOfFile
.QuadPart
= st
.st_size
;
874 info
->NumberOfLinks
= st
.st_nlink
;
875 info
->DeletePending
= FALSE
; /* FIXME */
877 io
->Information
= sizeof(*info
);
882 case FilePositionInformation
:
884 FILE_POSITION_INFORMATION
*info
= ptr
;
886 if (len
< sizeof(*info
)) io
->u
.Status
= STATUS_INFO_LENGTH_MISMATCH
;
889 off_t res
= lseek( fd
, 0, SEEK_CUR
);
890 if (res
== (off_t
)-1) io
->u
.Status
= FILE_GetNtStatus();
893 info
->CurrentByteOffset
.QuadPart
= res
;
894 io
->Information
= sizeof(*info
);
900 FIXME("Unsupported class (%d)\n", class);
901 io
->u
.Status
= STATUS_NOT_IMPLEMENTED
;
904 wine_server_release_fd( hFile
, fd
);
908 /******************************************************************************
909 * NtSetInformationFile [NTDLL.@]
910 * ZwSetInformationFile [NTDLL.@]
912 * Set information about an open file handle.
915 * handle [I] Handle returned from ZwOpenFile() or ZwCreateFile()
916 * io [O] Receives information about the operation on return
917 * ptr [I] Source for file information
918 * len [I] Size of FileInformation
919 * class [I] Type of file information to set
922 * Success: 0. io is updated.
923 * Failure: An NTSTATUS error code describing the error.
925 NTSTATUS WINAPI
NtSetInformationFile(HANDLE handle
, PIO_STATUS_BLOCK io
,
926 PVOID ptr
, ULONG len
, FILE_INFORMATION_CLASS
class)
930 TRACE("(%p,%p,%p,0x%08lx,0x%08x)\n", handle
, io
, ptr
, len
, class);
932 if ((io
->u
.Status
= wine_server_handle_to_fd( handle
, 0, &fd
, NULL
, NULL
)))
935 io
->u
.Status
= STATUS_SUCCESS
;
938 case FileBasicInformation
:
939 if (len
>= sizeof(FILE_BASIC_INFORMATION
))
942 const FILE_BASIC_INFORMATION
*info
= ptr
;
945 if (info
->LastAccessTime
.QuadPart
|| info
->LastWriteTime
.QuadPart
)
948 struct timeval tv
[2];
950 if (!info
->LastAccessTime
.QuadPart
|| !info
->LastWriteTime
.QuadPart
)
953 tv
[0].tv_sec
= tv
[0].tv_usec
= 0;
954 tv
[1].tv_sec
= tv
[1].tv_usec
= 0;
955 if (!fstat( fd
, &st
))
957 tv
[0].tv_sec
= st
.st_atime
;
958 tv
[1].tv_sec
= st
.st_mtime
;
961 if (info
->LastAccessTime
.QuadPart
)
963 sec
= RtlLargeIntegerDivide( info
->LastAccessTime
.QuadPart
, 10000000, &nsec
);
964 tv
[0].tv_sec
= sec
- SECS_1601_TO_1970
;
965 tv
[0].tv_usec
= (UINT
)nsec
/ 10;
967 if (info
->LastWriteTime
.QuadPart
)
969 sec
= RtlLargeIntegerDivide( info
->LastWriteTime
.QuadPart
, 10000000, &nsec
);
970 tv
[1].tv_sec
= sec
- SECS_1601_TO_1970
;
971 tv
[1].tv_usec
= (UINT
)nsec
/ 10;
973 if (futimes( fd
, tv
) == -1) io
->u
.Status
= FILE_GetNtStatus();
975 #endif /* HAVE_FUTIMES */
976 if (io
->u
.Status
== STATUS_SUCCESS
&& info
->FileAttributes
)
978 if (fstat( fd
, &st
) == -1) io
->u
.Status
= FILE_GetNtStatus();
981 if (info
->FileAttributes
& FILE_ATTRIBUTE_READONLY
)
983 st
.st_mode
&= ~0222; /* clear write permission bits */
987 /* add write permission only where we already have read permission */
988 st
.st_mode
|= (0600 | ((st
.st_mode
& 044) >> 1)) & (~FILE_umask
);
990 if (fchmod( fd
, st
.st_mode
) == -1) io
->u
.Status
= FILE_GetNtStatus();
994 else io
->u
.Status
= STATUS_INVALID_PARAMETER_3
;
997 case FilePositionInformation
:
998 if (len
>= sizeof(FILE_POSITION_INFORMATION
))
1000 const FILE_POSITION_INFORMATION
*info
= ptr
;
1002 if (lseek( fd
, info
->CurrentByteOffset
.QuadPart
, SEEK_SET
) == (off_t
)-1)
1003 io
->u
.Status
= FILE_GetNtStatus();
1005 else io
->u
.Status
= STATUS_INVALID_PARAMETER_3
;
1009 FIXME("Unsupported class (%d)\n", class);
1010 io
->u
.Status
= STATUS_NOT_IMPLEMENTED
;
1013 wine_server_release_fd( handle
, fd
);
1014 io
->Information
= 0;
1015 return io
->u
.Status
;
1019 /******************************************************************************
1020 * NtQueryFullAttributesFile (NTDLL.@)
1022 NTSTATUS WINAPI
NtQueryFullAttributesFile( const OBJECT_ATTRIBUTES
*attr
,
1023 FILE_NETWORK_OPEN_INFORMATION
*info
)
1025 ANSI_STRING unix_name
;
1028 if (!(status
= wine_nt_to_unix_file_name( attr
->ObjectName
, &unix_name
,
1029 TRUE
, !(attr
->Attributes
& OBJ_CASE_INSENSITIVE
) )))
1033 if (stat( unix_name
.Buffer
, &st
) == -1)
1034 status
= FILE_GetNtStatus();
1035 else if (!S_ISREG(st
.st_mode
) && !S_ISDIR(st
.st_mode
))
1036 status
= STATUS_INVALID_INFO_CLASS
;
1039 if (S_ISDIR(st
.st_mode
))
1041 info
->FileAttributes
= FILE_ATTRIBUTE_DIRECTORY
;
1042 info
->AllocationSize
.QuadPart
= 0;
1043 info
->EndOfFile
.QuadPart
= 0;
1047 info
->FileAttributes
= FILE_ATTRIBUTE_ARCHIVE
;
1048 info
->AllocationSize
.QuadPart
= (ULONGLONG
)st
.st_blocks
* 512;
1049 info
->EndOfFile
.QuadPart
= st
.st_size
;
1051 if (!(st
.st_mode
& S_IWUSR
)) info
->FileAttributes
|= FILE_ATTRIBUTE_READONLY
;
1052 RtlSecondsSince1970ToTime( st
.st_mtime
, &info
->CreationTime
);
1053 RtlSecondsSince1970ToTime( st
.st_mtime
, &info
->LastWriteTime
);
1054 RtlSecondsSince1970ToTime( st
.st_ctime
, &info
->ChangeTime
);
1055 RtlSecondsSince1970ToTime( st
.st_atime
, &info
->LastAccessTime
);
1056 if (DIR_is_hidden_file( attr
->ObjectName
))
1057 info
->FileAttributes
|= FILE_ATTRIBUTE_HIDDEN
;
1059 RtlFreeAnsiString( &unix_name
);
1061 else WARN("%s not found (%lx)\n", debugstr_us(attr
->ObjectName
), status
);
1066 /******************************************************************************
1067 * NtQueryAttributesFile (NTDLL.@)
1068 * ZwQueryAttributesFile (NTDLL.@)
1070 NTSTATUS WINAPI
NtQueryAttributesFile( const OBJECT_ATTRIBUTES
*attr
, FILE_BASIC_INFORMATION
*info
)
1072 FILE_NETWORK_OPEN_INFORMATION full_info
;
1075 if (!(status
= NtQueryFullAttributesFile( attr
, &full_info
)))
1077 info
->CreationTime
.QuadPart
= full_info
.CreationTime
.QuadPart
;
1078 info
->LastAccessTime
.QuadPart
= full_info
.LastAccessTime
.QuadPart
;
1079 info
->LastWriteTime
.QuadPart
= full_info
.LastWriteTime
.QuadPart
;
1080 info
->ChangeTime
.QuadPart
= full_info
.ChangeTime
.QuadPart
;
1081 info
->FileAttributes
= full_info
.FileAttributes
;
1087 /******************************************************************************
1088 * NtQueryVolumeInformationFile [NTDLL.@]
1089 * ZwQueryVolumeInformationFile [NTDLL.@]
1091 * Get volume information for an open file handle.
1094 * handle [I] Handle returned from ZwOpenFile() or ZwCreateFile()
1095 * io [O] Receives information about the operation on return
1096 * buffer [O] Destination for volume information
1097 * length [I] Size of FsInformation
1098 * info_class [I] Type of volume information to set
1101 * Success: 0. io and buffer are updated.
1102 * Failure: An NTSTATUS error code describing the error.
1104 NTSTATUS WINAPI
NtQueryVolumeInformationFile( HANDLE handle
, PIO_STATUS_BLOCK io
,
1105 PVOID buffer
, ULONG length
,
1106 FS_INFORMATION_CLASS info_class
)
1111 if ((io
->u
.Status
= wine_server_handle_to_fd( handle
, 0, &fd
, NULL
, NULL
)) != STATUS_SUCCESS
)
1112 return io
->u
.Status
;
1114 io
->u
.Status
= STATUS_NOT_IMPLEMENTED
;
1115 io
->Information
= 0;
1117 switch( info_class
)
1119 case FileFsVolumeInformation
:
1120 FIXME( "%p: volume info not supported\n", handle
);
1122 case FileFsLabelInformation
:
1123 FIXME( "%p: label info not supported\n", handle
);
1125 case FileFsSizeInformation
:
1126 if (length
< sizeof(FILE_FS_SIZE_INFORMATION
))
1127 io
->u
.Status
= STATUS_BUFFER_TOO_SMALL
;
1130 FILE_FS_SIZE_INFORMATION
*info
= buffer
;
1131 struct statvfs stvfs
;
1133 if (fstat( fd
, &st
) < 0)
1135 io
->u
.Status
= FILE_GetNtStatus();
1138 if (!S_ISREG(st
.st_mode
) && !S_ISDIR(st
.st_mode
))
1140 io
->u
.Status
= STATUS_INVALID_DEVICE_REQUEST
;
1143 if (fstatvfs( fd
, &stvfs
) < 0) io
->u
.Status
= FILE_GetNtStatus();
1146 info
->TotalAllocationUnits
.QuadPart
= stvfs
.f_blocks
;
1147 info
->AvailableAllocationUnits
.QuadPart
= stvfs
.f_bavail
;
1148 info
->SectorsPerAllocationUnit
= 1;
1149 info
->BytesPerSector
= stvfs
.f_frsize
;
1150 io
->Information
= sizeof(*info
);
1151 io
->u
.Status
= STATUS_SUCCESS
;
1155 case FileFsDeviceInformation
:
1156 if (length
< sizeof(FILE_FS_DEVICE_INFORMATION
))
1157 io
->u
.Status
= STATUS_BUFFER_TOO_SMALL
;
1160 FILE_FS_DEVICE_INFORMATION
*info
= buffer
;
1162 #if defined(linux) && defined(HAVE_FSTATFS)
1165 info
->Characteristics
= 0;
1167 if (fstat( fd
, &st
) < 0)
1169 io
->u
.Status
= FILE_GetNtStatus();
1172 if (S_ISCHR( st
.st_mode
))
1174 switch(major(st
.st_rdev
))
1177 info
->DeviceType
= FILE_DEVICE_NULL
;
1180 info
->DeviceType
= FILE_DEVICE_SERIAL_PORT
;
1183 info
->DeviceType
= FILE_DEVICE_PARALLEL_PORT
;
1186 info
->DeviceType
= FILE_DEVICE_UNKNOWN
;
1190 else if (S_ISBLK( st
.st_mode
))
1192 info
->DeviceType
= FILE_DEVICE_DISK
;
1194 else if (S_ISFIFO( st
.st_mode
) || S_ISSOCK( st
.st_mode
))
1196 info
->DeviceType
= FILE_DEVICE_NAMED_PIPE
;
1198 else /* regular file or directory */
1200 info
->Characteristics
|= FILE_DEVICE_IS_MOUNTED
;
1202 /* check for floppy disk */
1203 if (major(st
.st_dev
) == FLOPPY_MAJOR
)
1204 info
->Characteristics
|= FILE_REMOVABLE_MEDIA
;
1206 if (fstatfs( fd
, &stfs
) < 0) stfs
.f_type
= 0;
1207 switch (stfs
.f_type
)
1209 case 0x9660: /* iso9660 */
1210 case 0x15013346: /* udf */
1211 info
->DeviceType
= FILE_DEVICE_CD_ROM_FILE_SYSTEM
;
1212 info
->Characteristics
|= FILE_REMOVABLE_MEDIA
|FILE_READ_ONLY_DEVICE
;
1214 case 0x6969: /* nfs */
1215 case 0x517B: /* smbfs */
1216 case 0x564c: /* ncpfs */
1217 info
->DeviceType
= FILE_DEVICE_NETWORK_FILE_SYSTEM
;
1218 info
->Characteristics
|= FILE_REMOTE_DEVICE
;
1220 case 0x01021994: /* tmpfs */
1221 case 0x28cd3d45: /* cramfs */
1222 case 0x1373: /* devfs */
1223 case 0x9fa0: /* procfs */
1224 info
->DeviceType
= FILE_DEVICE_VIRTUAL_DISK
;
1227 info
->DeviceType
= FILE_DEVICE_DISK_FILE_SYSTEM
;
1233 if (!warned
++) FIXME( "device info not supported on this platform\n" );
1234 info
->DeviceType
= FILE_DEVICE_DISK_FILE_SYSTEM
;
1235 info
->Characteristics
= 0;
1237 io
->Information
= sizeof(*info
);
1238 io
->u
.Status
= STATUS_SUCCESS
;
1241 case FileFsAttributeInformation
:
1242 FIXME( "%p: attribute info not supported\n", handle
);
1244 case FileFsControlInformation
:
1245 FIXME( "%p: control info not supported\n", handle
);
1247 case FileFsFullSizeInformation
:
1248 FIXME( "%p: full size info not supported\n", handle
);
1250 case FileFsObjectIdInformation
:
1251 FIXME( "%p: object id info not supported\n", handle
);
1253 case FileFsMaximumInformation
:
1254 FIXME( "%p: maximum info not supported\n", handle
);
1257 io
->u
.Status
= STATUS_INVALID_PARAMETER
;
1260 wine_server_release_fd( handle
, fd
);
1261 return io
->u
.Status
;
1265 /******************************************************************
1266 * NtFlushBuffersFile (NTDLL.@)
1268 * Flush any buffered data on an open file handle.
1271 * FileHandle [I] Handle returned from ZwOpenFile() or ZwCreateFile()
1272 * IoStatusBlock [O] Receives information about the operation on return
1275 * Success: 0. IoStatusBlock is updated.
1276 * Failure: An NTSTATUS error code describing the error.
1278 NTSTATUS WINAPI
NtFlushBuffersFile( HANDLE hFile
, IO_STATUS_BLOCK
* IoStatusBlock
)
1281 HANDLE hEvent
= NULL
;
1283 SERVER_START_REQ( flush_file
)
1285 req
->handle
= hFile
;
1286 ret
= wine_server_call( req
);
1287 hEvent
= reply
->event
;
1292 ret
= NtWaitForSingleObject( hEvent
, FALSE
, NULL
);
1298 /******************************************************************
1299 * NtLockFile (NTDLL.@)
1303 NTSTATUS WINAPI
NtLockFile( HANDLE hFile
, HANDLE lock_granted_event
,
1304 PIO_APC_ROUTINE apc
, void* apc_user
,
1305 PIO_STATUS_BLOCK io_status
, PLARGE_INTEGER offset
,
1306 PLARGE_INTEGER count
, ULONG
* key
, BOOLEAN dont_wait
,
1313 if (apc
|| io_status
|| key
)
1315 FIXME("Unimplemented yet parameter\n");
1316 return STATUS_NOT_IMPLEMENTED
;
1321 SERVER_START_REQ( lock_file
)
1323 req
->handle
= hFile
;
1324 req
->offset_low
= offset
->u
.LowPart
;
1325 req
->offset_high
= offset
->u
.HighPart
;
1326 req
->count_low
= count
->u
.LowPart
;
1327 req
->count_high
= count
->u
.HighPart
;
1328 req
->shared
= !exclusive
;
1329 req
->wait
= !dont_wait
;
1330 ret
= wine_server_call( req
);
1331 handle
= reply
->handle
;
1332 async
= reply
->overlapped
;
1335 if (ret
!= STATUS_PENDING
)
1337 if (!ret
&& lock_granted_event
) NtSetEvent(lock_granted_event
, NULL
);
1343 FIXME( "Async I/O lock wait not implemented, might deadlock\n" );
1344 if (handle
) NtClose( handle
);
1345 return STATUS_PENDING
;
1349 NtWaitForSingleObject( handle
, FALSE
, NULL
);
1356 /* Unix lock conflict, sleep a bit and retry */
1357 time
.QuadPart
= 100 * (ULONGLONG
)10000;
1358 time
.QuadPart
= -time
.QuadPart
;
1359 NtDelayExecution( FALSE
, &time
);
1365 /******************************************************************
1366 * NtUnlockFile (NTDLL.@)
1370 NTSTATUS WINAPI
NtUnlockFile( HANDLE hFile
, PIO_STATUS_BLOCK io_status
,
1371 PLARGE_INTEGER offset
, PLARGE_INTEGER count
,
1376 TRACE( "%p %lx%08lx %lx%08lx\n",
1377 hFile
, offset
->u
.HighPart
, offset
->u
.LowPart
, count
->u
.HighPart
, count
->u
.LowPart
);
1379 if (io_status
|| key
)
1381 FIXME("Unimplemented yet parameter\n");
1382 return STATUS_NOT_IMPLEMENTED
;
1385 SERVER_START_REQ( unlock_file
)
1387 req
->handle
= hFile
;
1388 req
->offset_low
= offset
->u
.LowPart
;
1389 req
->offset_high
= offset
->u
.HighPart
;
1390 req
->count_low
= count
->u
.LowPart
;
1391 req
->count_high
= count
->u
.HighPart
;
1392 status
= wine_server_call( req
);