Implemented file locking functions (partly based on my old Corel
patch). Added a few regression tests.
This commit is contained in:
parent
6fb0277699
commit
ce61349334
|
@ -674,6 +674,79 @@ void test_offset_in_overlapped_structure(void)
|
|||
ok(DeleteFileA(temp_fname), "DeleteFileA error %ld\n", GetLastError());
|
||||
}
|
||||
|
||||
static void test_LockFile(void)
|
||||
{
|
||||
HANDLE handle;
|
||||
DWORD written;
|
||||
OVERLAPPED overlapped;
|
||||
|
||||
handle = CreateFileA( filename, GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
|
||||
CREATE_ALWAYS, 0, 0 );
|
||||
if (handle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
ok(0,"couldn't create file \"%s\" (err=%ld)",filename,GetLastError());
|
||||
return;
|
||||
}
|
||||
ok( WriteFile( handle, sillytext, strlen(sillytext), &written, NULL ), "write failed" );
|
||||
|
||||
ok( LockFile( handle, 0, 0, 0, 0 ), "LockFile failed" );
|
||||
ok( UnlockFile( handle, 0, 0, 0, 0 ), "UnlockFile failed" );
|
||||
ok( !UnlockFile( handle, 0, 0, 0, 0 ), "UnlockFile succeeded" );
|
||||
|
||||
ok( LockFile( handle, 10, 0, 20, 0 ), "LockFile 10,20 failed" );
|
||||
/* overlapping locks must fail */
|
||||
ok( !LockFile( handle, 12, 0, 10, 0 ), "LockFile 12,10 succeeded" );
|
||||
ok( !LockFile( handle, 5, 0, 6, 0 ), "LockFile 5,6 succeeded" );
|
||||
/* non-overlapping locks must succeed */
|
||||
ok( LockFile( handle, 5, 0, 5, 0 ), "LockFile 5,5 failed" );
|
||||
|
||||
ok( !UnlockFile( handle, 10, 0, 10, 0 ), "UnlockFile 10,10 succeeded" );
|
||||
ok( UnlockFile( handle, 10, 0, 20, 0 ), "UnlockFile 10,20 failed" );
|
||||
ok( !UnlockFile( handle, 10, 0, 20, 0 ), "UnlockFile 10,20 again succeeded" );
|
||||
ok( UnlockFile( handle, 5, 0, 5, 0 ), "UnlockFile 5,5 failed" );
|
||||
|
||||
overlapped.Offset = 100;
|
||||
overlapped.OffsetHigh = 0;
|
||||
overlapped.hEvent = 0;
|
||||
ok( LockFileEx( handle, 0, 0, 100, 0, &overlapped ), "LockFileEx 100,100 failed" );
|
||||
/* overlapping shared locks are OK */
|
||||
overlapped.Offset = 150;
|
||||
ok( LockFileEx( handle, 0, 0, 100, 0, &overlapped ), "LockFileEx 150,100 failed" );
|
||||
/* but exclusive is not */
|
||||
ok( !LockFileEx( handle, LOCKFILE_EXCLUSIVE_LOCK|LOCKFILE_FAIL_IMMEDIATELY, 0, 50, 0, &overlapped ),
|
||||
"LockFileEx exclusive 150,50 succeeded" );
|
||||
ok( UnlockFileEx( handle, 0, 100, 0, &overlapped ), "UnlockFileEx 150,100 failed" );
|
||||
ok( !UnlockFileEx( handle, 0, 100, 0, &overlapped ), "UnlockFileEx 150,100 again succeeded" );
|
||||
overlapped.Offset = 100;
|
||||
ok( UnlockFileEx( handle, 0, 100, 0, &overlapped ), "UnlockFileEx 100,100 failed" );
|
||||
ok( !UnlockFileEx( handle, 0, 100, 0, &overlapped ), "UnlockFileEx 100,100 again succeeded" );
|
||||
|
||||
ok( LockFile( handle, 0, 0x10000000, 0, 0xf0000000 ), "LockFile failed" );
|
||||
ok( !LockFile( handle, ~0, ~0, 1, 0 ), "LockFile ~0,1 succeeded" );
|
||||
ok( !LockFile( handle, 0, 0x20000000, 20, 0 ), "LockFile 0x20000000,20 succeeded" );
|
||||
ok( UnlockFile( handle, 0, 0x10000000, 0, 0xf0000000 ), "UnlockFile failed" );
|
||||
|
||||
/* wrap-around lock should not do anything */
|
||||
/* (but still succeeds on NT4 so we don't check result) */
|
||||
LockFile( handle, 0, 0x10000000, 0, 0xf0000001 );
|
||||
ok( LockFile( handle, ~0, ~0, 1, 0 ), "LockFile ~0,1 failed" );
|
||||
ok( UnlockFile( handle, ~0, ~0, 1, 0 ), "Unlockfile ~0,1 failed" );
|
||||
|
||||
/* zero-byte lock */
|
||||
ok( LockFile( handle, 100, 0, 0, 0 ), "LockFile 100,0 failed" );
|
||||
ok( !LockFile( handle, 98, 0, 4, 0 ), "LockFile 98,4 succeeded" );
|
||||
ok( LockFile( handle, 90, 0, 10, 0 ), "LockFile 90,10 failed" );
|
||||
ok( LockFile( handle, 100, 0, 10, 0 ), "LockFile 100,10 failed" );
|
||||
ok( UnlockFile( handle, 90, 0, 10, 0 ), "UnlockFile 90,10 failed" );
|
||||
ok( UnlockFile( handle, 100, 0, 10, 0 ), "UnlockFile 100,10 failed" );
|
||||
ok( UnlockFile( handle, 100, 0, 0, 0 ), "UnlockFile 100,0 failed" );
|
||||
|
||||
CloseHandle( handle );
|
||||
DeleteFileA( filename );
|
||||
}
|
||||
|
||||
|
||||
START_TEST(file)
|
||||
{
|
||||
test__hread( );
|
||||
|
@ -690,5 +763,6 @@ START_TEST(file)
|
|||
test_CreateFileW();
|
||||
test_DeleteFileA();
|
||||
test_DeleteFileW();
|
||||
test_LockFile();
|
||||
test_offset_in_overlapped_structure();
|
||||
}
|
||||
|
|
301
files/file.c
301
files/file.c
|
@ -2995,20 +2995,22 @@ BOOL WINAPI SetFileTime( HANDLE hFile,
|
|||
/**************************************************************************
|
||||
* LockFile (KERNEL32.@)
|
||||
*/
|
||||
BOOL WINAPI LockFile( HANDLE hFile, DWORD dwFileOffsetLow, DWORD dwFileOffsetHigh,
|
||||
DWORD nNumberOfBytesToLockLow, DWORD nNumberOfBytesToLockHigh )
|
||||
BOOL WINAPI LockFile( HANDLE hFile, DWORD offset_low, DWORD offset_high,
|
||||
DWORD count_low, DWORD count_high )
|
||||
{
|
||||
BOOL ret;
|
||||
|
||||
FIXME("not implemented in server\n");
|
||||
TRACE( "%p %lx%08lx %lx%08lx\n", hFile, offset_high, offset_low, count_high, count_low );
|
||||
|
||||
SERVER_START_REQ( lock_file )
|
||||
{
|
||||
req->handle = hFile;
|
||||
req->offset_low = dwFileOffsetLow;
|
||||
req->offset_high = dwFileOffsetHigh;
|
||||
req->count_low = nNumberOfBytesToLockLow;
|
||||
req->count_high = nNumberOfBytesToLockHigh;
|
||||
req->offset_low = offset_low;
|
||||
req->offset_high = offset_high;
|
||||
req->count_low = count_low;
|
||||
req->count_high = count_high;
|
||||
req->shared = FALSE;
|
||||
req->wait = FALSE;
|
||||
ret = !wine_server_call_err( req );
|
||||
}
|
||||
SERVER_END_REQ;
|
||||
|
@ -3028,41 +3030,76 @@ BOOL WINAPI LockFile( HANDLE hFile, DWORD dwFileOffsetLow, DWORD dwFileOffsetHig
|
|||
* Per Microsoft docs, the third parameter (reserved) must be set to 0.
|
||||
*/
|
||||
BOOL WINAPI LockFileEx( HANDLE hFile, DWORD flags, DWORD reserved,
|
||||
DWORD nNumberOfBytesToLockLow, DWORD nNumberOfBytesToLockHigh,
|
||||
LPOVERLAPPED pOverlapped )
|
||||
DWORD count_low, DWORD count_high, LPOVERLAPPED overlapped )
|
||||
{
|
||||
FIXME("hFile=%p,flags=%ld,reserved=%ld,lowbytes=%ld,highbytes=%ld,overlapped=%p: stub.\n",
|
||||
hFile, flags, reserved, nNumberOfBytesToLockLow, nNumberOfBytesToLockHigh,
|
||||
pOverlapped);
|
||||
if (reserved == 0)
|
||||
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
|
||||
else
|
||||
NTSTATUS err;
|
||||
BOOL async;
|
||||
HANDLE handle;
|
||||
|
||||
if (reserved)
|
||||
{
|
||||
ERR("reserved == %ld: Supposed to be 0??\n", reserved);
|
||||
SetLastError(ERROR_INVALID_PARAMETER);
|
||||
SetLastError( ERROR_INVALID_PARAMETER );
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
TRACE( "%p %lx%08lx %lx%08lx flags %lx\n",
|
||||
hFile, overlapped->OffsetHigh, overlapped->Offset, count_high, count_low, flags );
|
||||
|
||||
for (;;)
|
||||
{
|
||||
SERVER_START_REQ( lock_file )
|
||||
{
|
||||
req->handle = hFile;
|
||||
req->offset_low = overlapped->Offset;
|
||||
req->offset_high = overlapped->OffsetHigh;
|
||||
req->count_low = count_low;
|
||||
req->count_high = count_high;
|
||||
req->shared = !(flags & LOCKFILE_EXCLUSIVE_LOCK);
|
||||
req->wait = !(flags & LOCKFILE_FAIL_IMMEDIATELY);
|
||||
err = wine_server_call( req );
|
||||
handle = reply->handle;
|
||||
async = reply->overlapped;
|
||||
}
|
||||
SERVER_END_REQ;
|
||||
if (err != STATUS_PENDING)
|
||||
{
|
||||
if (err) SetLastError( RtlNtStatusToDosError(err) );
|
||||
return !err;
|
||||
}
|
||||
if (async)
|
||||
{
|
||||
FIXME( "Async I/O lock wait not implemented, might deadlock\n" );
|
||||
if (handle) CloseHandle( handle );
|
||||
SetLastError( ERROR_IO_PENDING );
|
||||
return FALSE;
|
||||
}
|
||||
if (handle)
|
||||
{
|
||||
WaitForSingleObject( handle, INFINITE );
|
||||
CloseHandle( handle );
|
||||
}
|
||||
else Sleep(100); /* Unix lock conflict, sleep a bit and retry */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
* UnlockFile (KERNEL32.@)
|
||||
*/
|
||||
BOOL WINAPI UnlockFile( HANDLE hFile, DWORD dwFileOffsetLow, DWORD dwFileOffsetHigh,
|
||||
DWORD nNumberOfBytesToUnlockLow, DWORD nNumberOfBytesToUnlockHigh )
|
||||
BOOL WINAPI UnlockFile( HANDLE hFile, DWORD offset_low, DWORD offset_high,
|
||||
DWORD count_low, DWORD count_high )
|
||||
{
|
||||
BOOL ret;
|
||||
|
||||
FIXME("not implemented in server\n");
|
||||
TRACE( "%p %lx%08lx %lx%08lx\n", hFile, offset_high, offset_low, count_high, count_low );
|
||||
|
||||
SERVER_START_REQ( unlock_file )
|
||||
{
|
||||
req->handle = hFile;
|
||||
req->offset_low = dwFileOffsetLow;
|
||||
req->offset_high = dwFileOffsetHigh;
|
||||
req->count_low = nNumberOfBytesToUnlockLow;
|
||||
req->count_high = nNumberOfBytesToUnlockHigh;
|
||||
req->offset_low = offset_low;
|
||||
req->offset_high = offset_high;
|
||||
req->count_low = count_low;
|
||||
req->count_high = count_high;
|
||||
ret = !wine_server_call_err( req );
|
||||
}
|
||||
SERVER_END_REQ;
|
||||
|
@ -3073,222 +3110,18 @@ BOOL WINAPI UnlockFile( HANDLE hFile, DWORD dwFileOffsetLow, DWORD dwFileOffsetH
|
|||
/**************************************************************************
|
||||
* UnlockFileEx (KERNEL32.@)
|
||||
*/
|
||||
BOOL WINAPI UnlockFileEx(
|
||||
HANDLE hFile,
|
||||
DWORD dwReserved,
|
||||
DWORD nNumberOfBytesToUnlockLow,
|
||||
DWORD nNumberOfBytesToUnlockHigh,
|
||||
LPOVERLAPPED lpOverlapped
|
||||
)
|
||||
BOOL WINAPI UnlockFileEx( HANDLE hFile, DWORD reserved, DWORD count_low, DWORD count_high,
|
||||
LPOVERLAPPED overlapped )
|
||||
{
|
||||
FIXME("hFile=%p,reserved=%ld,lowbytes=%ld,highbytes=%ld,overlapped=%p: stub.\n",
|
||||
hFile, dwReserved, nNumberOfBytesToUnlockLow, nNumberOfBytesToUnlockHigh,
|
||||
lpOverlapped);
|
||||
if (dwReserved == 0)
|
||||
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
|
||||
else
|
||||
if (reserved)
|
||||
{
|
||||
ERR("reserved == %ld: Supposed to be 0??\n", dwReserved);
|
||||
SetLastError(ERROR_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
SetLastError( ERROR_INVALID_PARAMETER );
|
||||
return FALSE;
|
||||
}
|
||||
return UnlockFile( hFile, overlapped->Offset, overlapped->OffsetHigh, count_low, count_high );
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
|
||||
struct DOS_FILE_LOCK {
|
||||
struct DOS_FILE_LOCK * next;
|
||||
DWORD base;
|
||||
DWORD len;
|
||||
DWORD processId;
|
||||
FILE_OBJECT * dos_file;
|
||||
/* char * unix_name;*/
|
||||
};
|
||||
|
||||
typedef struct DOS_FILE_LOCK DOS_FILE_LOCK;
|
||||
|
||||
static DOS_FILE_LOCK *locks = NULL;
|
||||
static void DOS_RemoveFileLocks(FILE_OBJECT *file);
|
||||
|
||||
|
||||
/* Locks need to be mirrored because unix file locking is based
|
||||
* on the pid. Inside of wine there can be multiple WINE processes
|
||||
* that share the same unix pid.
|
||||
* Read's and writes should check these locks also - not sure
|
||||
* how critical that is at this point (FIXME).
|
||||
*/
|
||||
|
||||
static BOOL DOS_AddLock(FILE_OBJECT *file, struct flock *f)
|
||||
{
|
||||
DOS_FILE_LOCK *curr;
|
||||
DWORD processId;
|
||||
|
||||
processId = GetCurrentProcessId();
|
||||
|
||||
/* check if lock overlaps a current lock for the same file */
|
||||
#if 0
|
||||
for (curr = locks; curr; curr = curr->next) {
|
||||
if (strcmp(curr->unix_name, file->unix_name) == 0) {
|
||||
if ((f->l_start == curr->base) && (f->l_len == curr->len))
|
||||
return TRUE;/* region is identic */
|
||||
if ((f->l_start < (curr->base + curr->len)) &&
|
||||
((f->l_start + f->l_len) > curr->base)) {
|
||||
/* region overlaps */
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
curr = HeapAlloc( GetProcessHeap(), 0, sizeof(DOS_FILE_LOCK) );
|
||||
curr->processId = GetCurrentProcessId();
|
||||
curr->base = f->l_start;
|
||||
curr->len = f->l_len;
|
||||
/* curr->unix_name = HEAP_strdupA( GetProcessHeap(), 0, file->unix_name);*/
|
||||
curr->next = locks;
|
||||
curr->dos_file = file;
|
||||
locks = curr;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void DOS_RemoveFileLocks(FILE_OBJECT *file)
|
||||
{
|
||||
DWORD processId;
|
||||
DOS_FILE_LOCK **curr;
|
||||
DOS_FILE_LOCK *rem;
|
||||
|
||||
processId = GetCurrentProcessId();
|
||||
curr = &locks;
|
||||
while (*curr) {
|
||||
if ((*curr)->dos_file == file) {
|
||||
rem = *curr;
|
||||
*curr = (*curr)->next;
|
||||
/* HeapFree( GetProcessHeap(), 0, rem->unix_name );*/
|
||||
HeapFree( GetProcessHeap(), 0, rem );
|
||||
}
|
||||
else
|
||||
curr = &(*curr)->next;
|
||||
}
|
||||
}
|
||||
|
||||
static BOOL DOS_RemoveLock(FILE_OBJECT *file, struct flock *f)
|
||||
{
|
||||
DWORD processId;
|
||||
DOS_FILE_LOCK **curr;
|
||||
DOS_FILE_LOCK *rem;
|
||||
|
||||
processId = GetCurrentProcessId();
|
||||
for (curr = &locks; *curr; curr = &(*curr)->next) {
|
||||
if ((*curr)->processId == processId &&
|
||||
(*curr)->dos_file == file &&
|
||||
(*curr)->base == f->l_start &&
|
||||
(*curr)->len == f->l_len) {
|
||||
/* this is the same lock */
|
||||
rem = *curr;
|
||||
*curr = (*curr)->next;
|
||||
/* HeapFree( GetProcessHeap(), 0, rem->unix_name );*/
|
||||
HeapFree( GetProcessHeap(), 0, rem );
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
/* no matching lock found */
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
* LockFile (KERNEL32.@)
|
||||
*/
|
||||
BOOL WINAPI LockFile(
|
||||
HANDLE hFile,DWORD dwFileOffsetLow,DWORD dwFileOffsetHigh,
|
||||
DWORD nNumberOfBytesToLockLow,DWORD nNumberOfBytesToLockHigh )
|
||||
{
|
||||
struct flock f;
|
||||
FILE_OBJECT *file;
|
||||
|
||||
TRACE("handle %d offsetlow=%ld offsethigh=%ld nbyteslow=%ld nbyteshigh=%ld\n",
|
||||
hFile, dwFileOffsetLow, dwFileOffsetHigh,
|
||||
nNumberOfBytesToLockLow, nNumberOfBytesToLockHigh);
|
||||
|
||||
if (dwFileOffsetHigh || nNumberOfBytesToLockHigh) {
|
||||
FIXME("Unimplemented bytes > 32bits\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
f.l_start = dwFileOffsetLow;
|
||||
f.l_len = nNumberOfBytesToLockLow;
|
||||
f.l_whence = SEEK_SET;
|
||||
f.l_pid = 0;
|
||||
f.l_type = F_WRLCK;
|
||||
|
||||
if (!(file = FILE_GetFile(hFile,0,NULL))) return FALSE;
|
||||
|
||||
/* shadow locks internally */
|
||||
if (!DOS_AddLock(file, &f)) {
|
||||
SetLastError( ERROR_LOCK_VIOLATION );
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* FIXME: Unix locking commented out for now, doesn't work with Excel */
|
||||
#ifdef USE_UNIX_LOCKS
|
||||
if (fcntl(file->unix_handle, F_SETLK, &f) == -1) {
|
||||
if (errno == EACCES || errno == EAGAIN) {
|
||||
SetLastError( ERROR_LOCK_VIOLATION );
|
||||
}
|
||||
else {
|
||||
FILE_SetDosError();
|
||||
}
|
||||
/* remove our internal copy of the lock */
|
||||
DOS_RemoveLock(file, &f);
|
||||
return FALSE;
|
||||
}
|
||||
#endif
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
* UnlockFile (KERNEL32.@)
|
||||
*/
|
||||
BOOL WINAPI UnlockFile(
|
||||
HANDLE hFile,DWORD dwFileOffsetLow,DWORD dwFileOffsetHigh,
|
||||
DWORD nNumberOfBytesToUnlockLow,DWORD nNumberOfBytesToUnlockHigh )
|
||||
{
|
||||
FILE_OBJECT *file;
|
||||
struct flock f;
|
||||
|
||||
TRACE("handle %d offsetlow=%ld offsethigh=%ld nbyteslow=%ld nbyteshigh=%ld\n",
|
||||
hFile, dwFileOffsetLow, dwFileOffsetHigh,
|
||||
nNumberOfBytesToUnlockLow, nNumberOfBytesToUnlockHigh);
|
||||
|
||||
if (dwFileOffsetHigh || nNumberOfBytesToUnlockHigh) {
|
||||
WARN("Unimplemented bytes > 32bits\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
f.l_start = dwFileOffsetLow;
|
||||
f.l_len = nNumberOfBytesToUnlockLow;
|
||||
f.l_whence = SEEK_SET;
|
||||
f.l_pid = 0;
|
||||
f.l_type = F_UNLCK;
|
||||
|
||||
if (!(file = FILE_GetFile(hFile,0,NULL))) return FALSE;
|
||||
|
||||
DOS_RemoveLock(file, &f); /* ok if fails - may be another wine */
|
||||
|
||||
/* FIXME: Unix locking commented out for now, doesn't work with Excel */
|
||||
#ifdef USE_UNIX_LOCKS
|
||||
if (fcntl(file->unix_handle, F_SETLK, &f) == -1) {
|
||||
FILE_SetDosError();
|
||||
return FALSE;
|
||||
}
|
||||
#endif
|
||||
return TRUE;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**************************************************************************
|
||||
* GetFileAttributesExW (KERNEL32.@)
|
||||
*/
|
||||
|
|
|
@ -293,6 +293,8 @@ typedef struct _PROCESS_HEAP_ENTRY
|
|||
#define INVALID_SET_FILE_POINTER ((DWORD)-1)
|
||||
#define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
|
||||
|
||||
#define LOCKFILE_FAIL_IMMEDIATELY 1
|
||||
#define LOCKFILE_EXCLUSIVE_LOCK 2
|
||||
|
||||
#define TLS_OUT_OF_INDEXES ((DWORD)0xFFFFFFFF)
|
||||
|
||||
|
|
|
@ -899,10 +899,14 @@ struct lock_file_request
|
|||
unsigned int offset_high;
|
||||
unsigned int count_low;
|
||||
unsigned int count_high;
|
||||
int shared;
|
||||
int wait;
|
||||
};
|
||||
struct lock_file_reply
|
||||
{
|
||||
struct reply_header __header;
|
||||
obj_handle_t handle;
|
||||
int overlapped;
|
||||
};
|
||||
|
||||
|
||||
|
@ -3551,6 +3555,6 @@ union generic_reply
|
|||
struct get_next_hook_reply get_next_hook_reply;
|
||||
};
|
||||
|
||||
#define SERVER_PROTOCOL_VERSION 99
|
||||
#define SERVER_PROTOCOL_VERSION 100
|
||||
|
||||
#endif /* __WINE_WINE_SERVER_PROTOCOL_H */
|
||||
|
|
380
server/fd.c
380
server/fd.c
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* Server-side file descriptor management
|
||||
*
|
||||
* Copyright (C) 2003 Alexandre Julliard
|
||||
* Copyright (C) 2000, 2003 Alexandre Julliard
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
|
@ -22,7 +22,9 @@
|
|||
#include "config.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
@ -42,6 +44,11 @@
|
|||
#include "request.h"
|
||||
#include "console.h"
|
||||
|
||||
/* Because of the stupid Posix locking semantics, we need to keep
|
||||
* track of all file descriptors referencing a given file, and not
|
||||
* close a single one until all the locks are gone (sigh).
|
||||
*/
|
||||
|
||||
/* file descriptor object */
|
||||
|
||||
/* closed_fd is used to keep track of the unix fd belonging to a closed fd object */
|
||||
|
@ -59,6 +66,7 @@ struct fd
|
|||
struct list inode_entry; /* entry in inode fd list */
|
||||
struct closed_fd *closed; /* structure to store the unix fd at destroy time */
|
||||
struct object *user; /* object using this file descriptor */
|
||||
struct list locks; /* list of locks on this fd */
|
||||
int unix_fd; /* unix file descriptor */
|
||||
int poll_index; /* index of fd in poll array */
|
||||
};
|
||||
|
@ -88,6 +96,7 @@ struct inode
|
|||
dev_t dev; /* device number */
|
||||
ino_t ino; /* inode number */
|
||||
struct list open; /* list of open file descriptors */
|
||||
struct list locks; /* list of file locks */
|
||||
struct closed_fd *closed; /* list of file descriptors to close at destroy time */
|
||||
};
|
||||
|
||||
|
@ -106,6 +115,42 @@ static const struct object_ops inode_ops =
|
|||
inode_destroy /* destroy */
|
||||
};
|
||||
|
||||
/* file lock object */
|
||||
|
||||
struct file_lock
|
||||
{
|
||||
struct object obj; /* object header */
|
||||
struct fd *fd; /* fd owning this lock */
|
||||
struct list fd_entry; /* entry in list of locks on a given fd */
|
||||
struct list inode_entry; /* entry in inode list of locks */
|
||||
int shared; /* shared lock? */
|
||||
file_pos_t start; /* locked region is interval [start;end) */
|
||||
file_pos_t end;
|
||||
struct process *process; /* process owning this lock */
|
||||
struct list proc_entry; /* entry in list of locks owned by the process */
|
||||
};
|
||||
|
||||
static void file_lock_dump( struct object *obj, int verbose );
|
||||
static int file_lock_signaled( struct object *obj, struct thread *thread );
|
||||
|
||||
static const struct object_ops file_lock_ops =
|
||||
{
|
||||
sizeof(struct file_lock), /* size */
|
||||
file_lock_dump, /* dump */
|
||||
add_queue, /* add_queue */
|
||||
remove_queue, /* remove_queue */
|
||||
file_lock_signaled, /* signaled */
|
||||
no_satisfied, /* satisfied */
|
||||
no_get_fd, /* get_fd */
|
||||
no_destroy /* destroy */
|
||||
};
|
||||
|
||||
|
||||
#define OFF_T_MAX (~((file_pos_t)1 << (8*sizeof(off_t)-1)))
|
||||
#define FILE_POS_T_MAX (~(file_pos_t)0)
|
||||
|
||||
static file_pos_t max_unix_offset = OFF_T_MAX;
|
||||
|
||||
#define DUMP_LONG_LONG(val) do { \
|
||||
if (sizeof(val) > sizeof(unsigned long) && (val) > ~0UL) \
|
||||
fprintf( stderr, "%lx%08lx", (unsigned long)((val) >> 32), (unsigned long)(val) ); \
|
||||
|
@ -368,6 +413,18 @@ void main_loop(void)
|
|||
|
||||
static struct list inode_hash[HASH_SIZE];
|
||||
|
||||
/* close all pending file descriptors in the closed list */
|
||||
static void inode_close_pending( struct inode *inode )
|
||||
{
|
||||
while (inode->closed)
|
||||
{
|
||||
struct closed_fd *fd = inode->closed;
|
||||
inode->closed = fd->next;
|
||||
close( fd->fd );
|
||||
free( fd );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void inode_dump( struct object *obj, int verbose )
|
||||
{
|
||||
|
@ -383,16 +440,11 @@ static void inode_destroy( struct object *obj )
|
|||
{
|
||||
struct inode *inode = (struct inode *)obj;
|
||||
|
||||
assert( !list_head(&inode->open) );
|
||||
assert( list_empty(&inode->open) );
|
||||
assert( list_empty(&inode->locks) );
|
||||
|
||||
list_remove( &inode->entry );
|
||||
while (inode->closed)
|
||||
{
|
||||
struct closed_fd *fd = inode->closed;
|
||||
inode->closed = fd->next;
|
||||
close( fd->fd );
|
||||
free( fd );
|
||||
}
|
||||
inode_close_pending( inode );
|
||||
}
|
||||
|
||||
/* retrieve the inode object for a given fd, creating it if needed */
|
||||
|
@ -421,6 +473,7 @@ static struct inode *get_inode( dev_t dev, ino_t ino )
|
|||
inode->ino = ino;
|
||||
inode->closed = NULL;
|
||||
list_init( &inode->open );
|
||||
list_init( &inode->locks );
|
||||
list_add_head( &inode_hash[hash], &inode->entry );
|
||||
}
|
||||
return inode;
|
||||
|
@ -429,8 +482,315 @@ static struct inode *get_inode( dev_t dev, ino_t ino )
|
|||
/* add fd to the indoe list of file descriptors to close */
|
||||
static void inode_add_closed_fd( struct inode *inode, struct closed_fd *fd )
|
||||
{
|
||||
if (!list_empty( &inode->locks ))
|
||||
{
|
||||
fd->next = inode->closed;
|
||||
inode->closed = fd;
|
||||
}
|
||||
else /* no locks on this inode, we can close the fd right away */
|
||||
{
|
||||
close( fd->fd );
|
||||
free( fd );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************/
|
||||
/* file lock functions */
|
||||
|
||||
static void file_lock_dump( struct object *obj, int verbose )
|
||||
{
|
||||
struct file_lock *lock = (struct file_lock *)obj;
|
||||
fprintf( stderr, "Lock %s fd=%p proc=%p start=",
|
||||
lock->shared ? "shared" : "excl", lock->fd, lock->process );
|
||||
DUMP_LONG_LONG( lock->start );
|
||||
fprintf( stderr, " end=" );
|
||||
DUMP_LONG_LONG( lock->end );
|
||||
fprintf( stderr, "\n" );
|
||||
}
|
||||
|
||||
static int file_lock_signaled( struct object *obj, struct thread *thread )
|
||||
{
|
||||
struct file_lock *lock = (struct file_lock *)obj;
|
||||
/* lock is signaled if it has lost its owner */
|
||||
return !lock->process;
|
||||
}
|
||||
|
||||
/* set (or remove) a Unix lock if possible for the given range */
|
||||
static int set_unix_lock( const struct fd *fd, file_pos_t start, file_pos_t end, int type )
|
||||
{
|
||||
struct flock fl;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (start == end) return 1; /* can't set zero-byte lock */
|
||||
if (start > max_unix_offset) return 1; /* ignore it */
|
||||
fl.l_type = type;
|
||||
fl.l_whence = SEEK_SET;
|
||||
fl.l_start = start;
|
||||
if (!end || end > max_unix_offset) fl.l_len = 0;
|
||||
else fl.l_len = end - start;
|
||||
if (fcntl( fd->unix_fd, F_SETLK, &fl ) != -1) return 1;
|
||||
|
||||
switch(errno)
|
||||
{
|
||||
case EACCES:
|
||||
case EAGAIN:
|
||||
set_error( STATUS_FILE_LOCK_CONFLICT );
|
||||
return 0;
|
||||
case EOVERFLOW:
|
||||
/* this can happen if off_t is 64-bit but the kernel only supports 32-bit */
|
||||
/* in that case we shrink the limit and retry */
|
||||
if (max_unix_offset > INT_MAX)
|
||||
{
|
||||
max_unix_offset = INT_MAX;
|
||||
break; /* retry */
|
||||
}
|
||||
/* fall through */
|
||||
default:
|
||||
file_set_error();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* check if interval [start;end) overlaps the lock */
|
||||
inline static int lock_overlaps( struct file_lock *lock, file_pos_t start, file_pos_t end )
|
||||
{
|
||||
if (lock->end && start >= lock->end) return 0;
|
||||
if (end && lock->start >= end) return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* remove Unix locks for all bytes in the specified area that are no longer locked */
|
||||
static void remove_unix_locks( const struct fd *fd, file_pos_t start, file_pos_t end )
|
||||
{
|
||||
struct hole
|
||||
{
|
||||
struct hole *next;
|
||||
struct hole *prev;
|
||||
file_pos_t start;
|
||||
file_pos_t end;
|
||||
} *first, *cur, *next, *buffer;
|
||||
|
||||
struct list *ptr;
|
||||
int count = 0;
|
||||
|
||||
if (!fd->inode) return;
|
||||
if (start == end || start > max_unix_offset) return;
|
||||
if (!end || end > max_unix_offset) end = max_unix_offset + 1;
|
||||
|
||||
/* count the number of locks overlapping the specified area */
|
||||
|
||||
LIST_FOR_EACH( ptr, &fd->inode->locks )
|
||||
{
|
||||
struct file_lock *lock = LIST_ENTRY( ptr, struct file_lock, inode_entry );
|
||||
if (lock->start == lock->end) continue;
|
||||
if (lock_overlaps( lock, start, end )) count++;
|
||||
}
|
||||
|
||||
if (!count) /* no locks at all, we can unlock everything */
|
||||
{
|
||||
set_unix_lock( fd, start, end, F_UNLCK );
|
||||
return;
|
||||
}
|
||||
|
||||
/* allocate space for the list of holes */
|
||||
/* max. number of holes is number of locks + 1 */
|
||||
|
||||
if (!(buffer = malloc( sizeof(*buffer) * (count+1) ))) return;
|
||||
first = buffer;
|
||||
first->next = NULL;
|
||||
first->prev = NULL;
|
||||
first->start = start;
|
||||
first->end = end;
|
||||
next = first + 1;
|
||||
|
||||
/* build a sorted list of unlocked holes in the specified area */
|
||||
|
||||
LIST_FOR_EACH( ptr, &fd->inode->locks )
|
||||
{
|
||||
struct file_lock *lock = LIST_ENTRY( ptr, struct file_lock, inode_entry );
|
||||
if (lock->start == lock->end) continue;
|
||||
if (!lock_overlaps( lock, start, end )) continue;
|
||||
|
||||
/* go through all the holes touched by this lock */
|
||||
for (cur = first; cur; cur = cur->next)
|
||||
{
|
||||
if (cur->end <= lock->start) continue; /* hole is before start of lock */
|
||||
if (lock->end && cur->start >= lock->end) break; /* hole is after end of lock */
|
||||
|
||||
/* now we know that lock is overlapping hole */
|
||||
|
||||
if (cur->start >= lock->start) /* lock starts before hole, shrink from start */
|
||||
{
|
||||
cur->start = lock->end;
|
||||
if (cur->start && cur->start < cur->end) break; /* done with this lock */
|
||||
/* now hole is empty, remove it */
|
||||
if (cur->next) cur->next->prev = cur->prev;
|
||||
if (cur->prev) cur->prev->next = cur->next;
|
||||
else if (!(first = cur->next)) goto done; /* no more holes at all */
|
||||
}
|
||||
else if (!lock->end || cur->end <= lock->end) /* lock larger than hole, shrink from end */
|
||||
{
|
||||
cur->end = lock->start;
|
||||
assert( cur->start < cur->end );
|
||||
}
|
||||
else /* lock is in the middle of hole, split hole in two */
|
||||
{
|
||||
next->prev = cur;
|
||||
next->next = cur->next;
|
||||
cur->next = next;
|
||||
next->start = lock->end;
|
||||
next->end = cur->end;
|
||||
cur->end = lock->start;
|
||||
assert( next->start < next->end );
|
||||
assert( cur->end < next->start );
|
||||
next++;
|
||||
break; /* done with this lock */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* clear Unix locks for all the holes */
|
||||
|
||||
for (cur = first; cur; cur = cur->next)
|
||||
set_unix_lock( fd, cur->start, cur->end, F_UNLCK );
|
||||
|
||||
done:
|
||||
free( buffer );
|
||||
}
|
||||
|
||||
/* create a new lock on a fd */
|
||||
static struct file_lock *add_lock( struct fd *fd, int shared, file_pos_t start, file_pos_t end )
|
||||
{
|
||||
struct file_lock *lock;
|
||||
|
||||
if (!fd->inode) /* not a regular file */
|
||||
{
|
||||
set_error( STATUS_INVALID_HANDLE );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!(lock = alloc_object( &file_lock_ops ))) return NULL;
|
||||
lock->shared = shared;
|
||||
lock->start = start;
|
||||
lock->end = end;
|
||||
lock->fd = fd;
|
||||
lock->process = current->process;
|
||||
|
||||
/* now try to set a Unix lock */
|
||||
if (!set_unix_lock( lock->fd, lock->start, lock->end, lock->shared ? F_RDLCK : F_WRLCK ))
|
||||
{
|
||||
release_object( lock );
|
||||
return NULL;
|
||||
}
|
||||
list_add_head( &fd->locks, &lock->fd_entry );
|
||||
list_add_head( &fd->inode->locks, &lock->inode_entry );
|
||||
list_add_head( &lock->process->locks, &lock->proc_entry );
|
||||
return lock;
|
||||
}
|
||||
|
||||
/* remove an existing lock */
|
||||
static void remove_lock( struct file_lock *lock, int remove_unix )
|
||||
{
|
||||
struct inode *inode = lock->fd->inode;
|
||||
|
||||
list_remove( &lock->fd_entry );
|
||||
list_remove( &lock->inode_entry );
|
||||
list_remove( &lock->proc_entry );
|
||||
if (remove_unix) remove_unix_locks( lock->fd, lock->start, lock->end );
|
||||
if (list_empty( &inode->locks )) inode_close_pending( inode );
|
||||
lock->process = NULL;
|
||||
wake_up( &lock->obj, 0 );
|
||||
release_object( lock );
|
||||
}
|
||||
|
||||
/* remove all locks owned by a given process */
|
||||
void remove_process_locks( struct process *process )
|
||||
{
|
||||
struct list *ptr;
|
||||
|
||||
while ((ptr = list_head( &process->locks )))
|
||||
{
|
||||
struct file_lock *lock = LIST_ENTRY( ptr, struct file_lock, proc_entry );
|
||||
remove_lock( lock, 1 ); /* this removes it from the list */
|
||||
}
|
||||
}
|
||||
|
||||
/* remove all locks on a given fd */
|
||||
static void remove_fd_locks( struct fd *fd )
|
||||
{
|
||||
file_pos_t start = FILE_POS_T_MAX, end = 0;
|
||||
struct list *ptr;
|
||||
|
||||
while ((ptr = list_head( &fd->locks )))
|
||||
{
|
||||
struct file_lock *lock = LIST_ENTRY( ptr, struct file_lock, fd_entry );
|
||||
if (lock->start < start) start = lock->start;
|
||||
if (!lock->end || lock->end > end) end = lock->end - 1;
|
||||
remove_lock( lock, 0 );
|
||||
}
|
||||
if (start < end) remove_unix_locks( fd, start, end + 1 );
|
||||
}
|
||||
|
||||
/* add a lock on an fd */
|
||||
/* returns handle to wait on */
|
||||
obj_handle_t lock_fd( struct fd *fd, file_pos_t start, file_pos_t count, int shared, int wait )
|
||||
{
|
||||
struct list *ptr;
|
||||
file_pos_t end = start + count;
|
||||
|
||||
/* don't allow wrapping locks */
|
||||
if (end && end < start)
|
||||
{
|
||||
set_error( STATUS_INVALID_PARAMETER );
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* check if another lock on that file overlaps the area */
|
||||
LIST_FOR_EACH( ptr, &fd->inode->locks )
|
||||
{
|
||||
struct file_lock *lock = LIST_ENTRY( ptr, struct file_lock, inode_entry );
|
||||
if (!lock_overlaps( lock, start, end )) continue;
|
||||
if (lock->shared && shared) continue;
|
||||
/* found one */
|
||||
if (!wait)
|
||||
{
|
||||
set_error( STATUS_FILE_LOCK_CONFLICT );
|
||||
return 0;
|
||||
}
|
||||
set_error( STATUS_PENDING );
|
||||
return alloc_handle( current->process, lock, SYNCHRONIZE, 0 );
|
||||
}
|
||||
|
||||
/* not found, add it */
|
||||
if (add_lock( fd, shared, start, end )) return 0;
|
||||
if (get_error() == STATUS_FILE_LOCK_CONFLICT)
|
||||
{
|
||||
/* Unix lock conflict -> tell client to wait and retry */
|
||||
if (wait) set_error( STATUS_PENDING );
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* remove a lock on an fd */
|
||||
void unlock_fd( struct fd *fd, file_pos_t start, file_pos_t count )
|
||||
{
|
||||
struct list *ptr;
|
||||
file_pos_t end = start + count;
|
||||
|
||||
/* find an existing lock with the exact same parameters */
|
||||
LIST_FOR_EACH( ptr, &fd->locks )
|
||||
{
|
||||
struct file_lock *lock = LIST_ENTRY( ptr, struct file_lock, fd_entry );
|
||||
if ((lock->start == start) && (lock->end == end))
|
||||
{
|
||||
remove_lock( lock, 1 );
|
||||
return;
|
||||
}
|
||||
}
|
||||
set_error( STATUS_FILE_LOCK_CONFLICT );
|
||||
}
|
||||
|
||||
|
||||
|
@ -447,6 +807,7 @@ static void fd_destroy( struct object *obj )
|
|||
{
|
||||
struct fd *fd = (struct fd *)obj;
|
||||
|
||||
remove_fd_locks( fd );
|
||||
list_remove( &fd->inode_entry );
|
||||
if (fd->poll_index != -1) remove_poll_user( fd, fd->poll_index );
|
||||
if (fd->inode)
|
||||
|
@ -492,6 +853,7 @@ struct fd *alloc_fd( const struct fd_ops *fd_user_ops, struct object *user )
|
|||
fd->unix_fd = -1;
|
||||
fd->poll_index = -1;
|
||||
list_init( &fd->inode_entry );
|
||||
list_init( &fd->locks );
|
||||
|
||||
if ((fd->poll_index = add_poll_user( fd )) == -1)
|
||||
{
|
||||
|
|
|
@ -453,6 +453,7 @@ void file_set_error(void)
|
|||
case ESPIPE: set_error( 0xc0010000 | ERROR_SEEK /* FIXME */ ); break;
|
||||
case ENOTEMPTY: set_error( STATUS_DIRECTORY_NOT_EMPTY ); break;
|
||||
case EIO: set_error( STATUS_ACCESS_VIOLATION ); break;
|
||||
case EOVERFLOW: set_error( STATUS_INVALID_PARAMETER ); break;
|
||||
default: perror("file_set_error"); set_error( ERROR_UNKNOWN /* FIXME */ ); break;
|
||||
}
|
||||
}
|
||||
|
@ -581,20 +582,6 @@ static int set_file_time( obj_handle_t handle, time_t access_time, time_t write_
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int file_lock( struct file *file, int offset_high, int offset_low,
|
||||
int count_high, int count_low )
|
||||
{
|
||||
/* FIXME: implement this */
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int file_unlock( struct file *file, int offset_high, int offset_low,
|
||||
int count_high, int count_low )
|
||||
{
|
||||
/* FIXME: implement this */
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* create a file */
|
||||
DECL_HANDLER(create_file)
|
||||
{
|
||||
|
@ -661,11 +648,13 @@ DECL_HANDLER(set_file_time)
|
|||
DECL_HANDLER(lock_file)
|
||||
{
|
||||
struct file *file;
|
||||
file_pos_t offset = ((file_pos_t)req->offset_high << 32) | req->offset_low;
|
||||
file_pos_t count = ((file_pos_t)req->count_high << 32) | req->count_low;
|
||||
|
||||
if ((file = get_file_obj( current->process, req->handle, 0 )))
|
||||
{
|
||||
file_lock( file, req->offset_high, req->offset_low,
|
||||
req->count_high, req->count_low );
|
||||
reply->handle = lock_fd( file->fd, offset, count, req->shared, req->wait );
|
||||
reply->overlapped = (file->flags & FILE_FLAG_OVERLAPPED) != 0;
|
||||
release_object( file );
|
||||
}
|
||||
}
|
||||
|
@ -674,11 +663,12 @@ DECL_HANDLER(lock_file)
|
|||
DECL_HANDLER(unlock_file)
|
||||
{
|
||||
struct file *file;
|
||||
file_pos_t offset = ((file_pos_t)req->offset_high << 32) | req->offset_low;
|
||||
file_pos_t count = ((file_pos_t)req->count_high << 32) | req->count_low;
|
||||
|
||||
if ((file = get_file_obj( current->process, req->handle, 0 )))
|
||||
{
|
||||
file_unlock( file, req->offset_high, req->offset_low,
|
||||
req->count_high, req->count_low );
|
||||
unlock_fd( file->fd, offset, count );
|
||||
release_object( file );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,8 @@
|
|||
|
||||
struct fd;
|
||||
|
||||
typedef unsigned __int64 file_pos_t;
|
||||
|
||||
/* operations valid on file descriptor objects */
|
||||
struct fd_ops
|
||||
{
|
||||
|
@ -51,6 +53,8 @@ extern int get_unix_fd( struct fd *fd );
|
|||
extern void fd_poll_event( struct fd *fd, int event );
|
||||
extern int check_fd_events( struct fd *fd, int events );
|
||||
extern void set_fd_events( struct fd *fd, int events );
|
||||
extern obj_handle_t lock_fd( struct fd *fd, file_pos_t offset, file_pos_t count, int shared, int wait );
|
||||
extern void unlock_fd( struct fd *fd, file_pos_t offset, file_pos_t count );
|
||||
|
||||
extern int default_fd_add_queue( struct object *obj, struct wait_queue_entry *entry );
|
||||
extern void default_fd_remove_queue( struct object *obj, struct wait_queue_entry *entry );
|
||||
|
|
|
@ -80,6 +80,12 @@ inline static struct list *list_tail( struct list *list )
|
|||
return list_prev( list, list );
|
||||
}
|
||||
|
||||
/* check if a list is empty */
|
||||
inline static int list_empty( struct list *list )
|
||||
{
|
||||
return list->next == list;
|
||||
}
|
||||
|
||||
/* initialize a list */
|
||||
inline static void list_init( struct list *list )
|
||||
{
|
||||
|
|
|
@ -296,6 +296,7 @@ struct thread *create_process( int fd )
|
|||
process->exe.namelen = 0;
|
||||
process->exe.filename = NULL;
|
||||
process->group_id = 0;
|
||||
list_init( &process->locks );
|
||||
|
||||
gettimeofday( &process->start_time, NULL );
|
||||
if ((process->next = first_process) != NULL) process->next->prev = process;
|
||||
|
|
|
@ -67,6 +67,7 @@ struct process
|
|||
int affinity; /* process affinity mask */
|
||||
int suspend; /* global process suspend count */
|
||||
int create_flags; /* process creation flags */
|
||||
struct list locks; /* list of file locks owned by the process */
|
||||
struct console_input*console; /* console input */
|
||||
enum startup_state startup_state; /* startup state */
|
||||
struct startup_info *startup_info; /* startup info while init is in progress */
|
||||
|
|
|
@ -681,6 +681,11 @@ enum fd_type
|
|||
unsigned int offset_high; /* offset of start of lock */
|
||||
unsigned int count_low; /* count of bytes to lock */
|
||||
unsigned int count_high; /* count of bytes to lock */
|
||||
int shared; /* shared or exclusive lock? */
|
||||
int wait; /* do we want to wait? */
|
||||
@REPLY
|
||||
obj_handle_t handle; /* handle to wait on */
|
||||
int overlapped; /* is it an overlapped I/O handle? */
|
||||
@END
|
||||
|
||||
|
||||
|
|
|
@ -889,7 +889,15 @@ static void dump_lock_file_request( const struct lock_file_request *req )
|
|||
fprintf( stderr, " offset_low=%08x,", req->offset_low );
|
||||
fprintf( stderr, " offset_high=%08x,", req->offset_high );
|
||||
fprintf( stderr, " count_low=%08x,", req->count_low );
|
||||
fprintf( stderr, " count_high=%08x", req->count_high );
|
||||
fprintf( stderr, " count_high=%08x,", req->count_high );
|
||||
fprintf( stderr, " shared=%d,", req->shared );
|
||||
fprintf( stderr, " wait=%d", req->wait );
|
||||
}
|
||||
|
||||
static void dump_lock_file_reply( const struct lock_file_reply *req )
|
||||
{
|
||||
fprintf( stderr, " handle=%p,", req->handle );
|
||||
fprintf( stderr, " overlapped=%d", req->overlapped );
|
||||
}
|
||||
|
||||
static void dump_unlock_file_request( const struct unlock_file_request *req )
|
||||
|
@ -2632,7 +2640,7 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = {
|
|||
(dump_func)0,
|
||||
(dump_func)0,
|
||||
(dump_func)dump_get_file_info_reply,
|
||||
(dump_func)0,
|
||||
(dump_func)dump_lock_file_reply,
|
||||
(dump_func)0,
|
||||
(dump_func)dump_create_pipe_reply,
|
||||
(dump_func)dump_create_socket_reply,
|
||||
|
|
Loading…
Reference in New Issue