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());
|
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)
|
START_TEST(file)
|
||||||
{
|
{
|
||||||
test__hread( );
|
test__hread( );
|
||||||
|
@ -690,5 +763,6 @@ START_TEST(file)
|
||||||
test_CreateFileW();
|
test_CreateFileW();
|
||||||
test_DeleteFileA();
|
test_DeleteFileA();
|
||||||
test_DeleteFileW();
|
test_DeleteFileW();
|
||||||
|
test_LockFile();
|
||||||
test_offset_in_overlapped_structure();
|
test_offset_in_overlapped_structure();
|
||||||
}
|
}
|
||||||
|
|
305
files/file.c
305
files/file.c
|
@ -2995,20 +2995,22 @@ BOOL WINAPI SetFileTime( HANDLE hFile,
|
||||||
/**************************************************************************
|
/**************************************************************************
|
||||||
* LockFile (KERNEL32.@)
|
* LockFile (KERNEL32.@)
|
||||||
*/
|
*/
|
||||||
BOOL WINAPI LockFile( HANDLE hFile, DWORD dwFileOffsetLow, DWORD dwFileOffsetHigh,
|
BOOL WINAPI LockFile( HANDLE hFile, DWORD offset_low, DWORD offset_high,
|
||||||
DWORD nNumberOfBytesToLockLow, DWORD nNumberOfBytesToLockHigh )
|
DWORD count_low, DWORD count_high )
|
||||||
{
|
{
|
||||||
BOOL ret;
|
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 )
|
SERVER_START_REQ( lock_file )
|
||||||
{
|
{
|
||||||
req->handle = hFile;
|
req->handle = hFile;
|
||||||
req->offset_low = dwFileOffsetLow;
|
req->offset_low = offset_low;
|
||||||
req->offset_high = dwFileOffsetHigh;
|
req->offset_high = offset_high;
|
||||||
req->count_low = nNumberOfBytesToLockLow;
|
req->count_low = count_low;
|
||||||
req->count_high = nNumberOfBytesToLockHigh;
|
req->count_high = count_high;
|
||||||
|
req->shared = FALSE;
|
||||||
|
req->wait = FALSE;
|
||||||
ret = !wine_server_call_err( req );
|
ret = !wine_server_call_err( req );
|
||||||
}
|
}
|
||||||
SERVER_END_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.
|
* Per Microsoft docs, the third parameter (reserved) must be set to 0.
|
||||||
*/
|
*/
|
||||||
BOOL WINAPI LockFileEx( HANDLE hFile, DWORD flags, DWORD reserved,
|
BOOL WINAPI LockFileEx( HANDLE hFile, DWORD flags, DWORD reserved,
|
||||||
DWORD nNumberOfBytesToLockLow, DWORD nNumberOfBytesToLockHigh,
|
DWORD count_low, DWORD count_high, LPOVERLAPPED overlapped )
|
||||||
LPOVERLAPPED pOverlapped )
|
|
||||||
{
|
{
|
||||||
FIXME("hFile=%p,flags=%ld,reserved=%ld,lowbytes=%ld,highbytes=%ld,overlapped=%p: stub.\n",
|
NTSTATUS err;
|
||||||
hFile, flags, reserved, nNumberOfBytesToLockLow, nNumberOfBytesToLockHigh,
|
BOOL async;
|
||||||
pOverlapped);
|
HANDLE handle;
|
||||||
if (reserved == 0)
|
|
||||||
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
|
if (reserved)
|
||||||
else
|
|
||||||
{
|
{
|
||||||
ERR("reserved == %ld: Supposed to be 0??\n", reserved);
|
SetLastError( ERROR_INVALID_PARAMETER );
|
||||||
SetLastError(ERROR_INVALID_PARAMETER);
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
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.@)
|
* UnlockFile (KERNEL32.@)
|
||||||
*/
|
*/
|
||||||
BOOL WINAPI UnlockFile( HANDLE hFile, DWORD dwFileOffsetLow, DWORD dwFileOffsetHigh,
|
BOOL WINAPI UnlockFile( HANDLE hFile, DWORD offset_low, DWORD offset_high,
|
||||||
DWORD nNumberOfBytesToUnlockLow, DWORD nNumberOfBytesToUnlockHigh )
|
DWORD count_low, DWORD count_high )
|
||||||
{
|
{
|
||||||
BOOL ret;
|
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 )
|
SERVER_START_REQ( unlock_file )
|
||||||
{
|
{
|
||||||
req->handle = hFile;
|
req->handle = hFile;
|
||||||
req->offset_low = dwFileOffsetLow;
|
req->offset_low = offset_low;
|
||||||
req->offset_high = dwFileOffsetHigh;
|
req->offset_high = offset_high;
|
||||||
req->count_low = nNumberOfBytesToUnlockLow;
|
req->count_low = count_low;
|
||||||
req->count_high = nNumberOfBytesToUnlockHigh;
|
req->count_high = count_high;
|
||||||
ret = !wine_server_call_err( req );
|
ret = !wine_server_call_err( req );
|
||||||
}
|
}
|
||||||
SERVER_END_REQ;
|
SERVER_END_REQ;
|
||||||
|
@ -3073,221 +3110,17 @@ BOOL WINAPI UnlockFile( HANDLE hFile, DWORD dwFileOffsetLow, DWORD dwFileOffsetH
|
||||||
/**************************************************************************
|
/**************************************************************************
|
||||||
* UnlockFileEx (KERNEL32.@)
|
* UnlockFileEx (KERNEL32.@)
|
||||||
*/
|
*/
|
||||||
BOOL WINAPI UnlockFileEx(
|
BOOL WINAPI UnlockFileEx( HANDLE hFile, DWORD reserved, DWORD count_low, DWORD count_high,
|
||||||
HANDLE hFile,
|
LPOVERLAPPED overlapped )
|
||||||
DWORD dwReserved,
|
|
||||||
DWORD nNumberOfBytesToUnlockLow,
|
|
||||||
DWORD nNumberOfBytesToUnlockHigh,
|
|
||||||
LPOVERLAPPED lpOverlapped
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
FIXME("hFile=%p,reserved=%ld,lowbytes=%ld,highbytes=%ld,overlapped=%p: stub.\n",
|
if (reserved)
|
||||||
hFile, dwReserved, nNumberOfBytesToUnlockLow, nNumberOfBytesToUnlockHigh,
|
{
|
||||||
lpOverlapped);
|
SetLastError( ERROR_INVALID_PARAMETER );
|
||||||
if (dwReserved == 0)
|
return FALSE;
|
||||||
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ERR("reserved == %ld: Supposed to be 0??\n", dwReserved);
|
|
||||||
SetLastError(ERROR_INVALID_PARAMETER);
|
|
||||||
}
|
|
||||||
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
return UnlockFile( hFile, overlapped->Offset, overlapped->OffsetHigh, count_low, count_high );
|
||||||
#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.@)
|
* GetFileAttributesExW (KERNEL32.@)
|
||||||
|
|
|
@ -293,6 +293,8 @@ typedef struct _PROCESS_HEAP_ENTRY
|
||||||
#define INVALID_SET_FILE_POINTER ((DWORD)-1)
|
#define INVALID_SET_FILE_POINTER ((DWORD)-1)
|
||||||
#define INVALID_FILE_ATTRIBUTES ((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)
|
#define TLS_OUT_OF_INDEXES ((DWORD)0xFFFFFFFF)
|
||||||
|
|
||||||
|
|
|
@ -899,10 +899,14 @@ struct lock_file_request
|
||||||
unsigned int offset_high;
|
unsigned int offset_high;
|
||||||
unsigned int count_low;
|
unsigned int count_low;
|
||||||
unsigned int count_high;
|
unsigned int count_high;
|
||||||
|
int shared;
|
||||||
|
int wait;
|
||||||
};
|
};
|
||||||
struct lock_file_reply
|
struct lock_file_reply
|
||||||
{
|
{
|
||||||
struct reply_header __header;
|
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;
|
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 */
|
#endif /* __WINE_WINE_SERVER_PROTOCOL_H */
|
||||||
|
|
384
server/fd.c
384
server/fd.c
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* Server-side file descriptor management
|
* 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
|
* This library is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
@ -22,7 +22,9 @@
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
#include <limits.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
@ -42,6 +44,11 @@
|
||||||
#include "request.h"
|
#include "request.h"
|
||||||
#include "console.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 */
|
/* file descriptor object */
|
||||||
|
|
||||||
/* closed_fd is used to keep track of the unix fd belonging to a closed fd 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 list inode_entry; /* entry in inode fd list */
|
||||||
struct closed_fd *closed; /* structure to store the unix fd at destroy time */
|
struct closed_fd *closed; /* structure to store the unix fd at destroy time */
|
||||||
struct object *user; /* object using this file descriptor */
|
struct object *user; /* object using this file descriptor */
|
||||||
|
struct list locks; /* list of locks on this fd */
|
||||||
int unix_fd; /* unix file descriptor */
|
int unix_fd; /* unix file descriptor */
|
||||||
int poll_index; /* index of fd in poll array */
|
int poll_index; /* index of fd in poll array */
|
||||||
};
|
};
|
||||||
|
@ -88,6 +96,7 @@ struct inode
|
||||||
dev_t dev; /* device number */
|
dev_t dev; /* device number */
|
||||||
ino_t ino; /* inode number */
|
ino_t ino; /* inode number */
|
||||||
struct list open; /* list of open file descriptors */
|
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 */
|
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 */
|
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 { \
|
#define DUMP_LONG_LONG(val) do { \
|
||||||
if (sizeof(val) > sizeof(unsigned long) && (val) > ~0UL) \
|
if (sizeof(val) > sizeof(unsigned long) && (val) > ~0UL) \
|
||||||
fprintf( stderr, "%lx%08lx", (unsigned long)((val) >> 32), (unsigned long)(val) ); \
|
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];
|
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 )
|
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;
|
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 );
|
list_remove( &inode->entry );
|
||||||
while (inode->closed)
|
inode_close_pending( inode );
|
||||||
{
|
|
||||||
struct closed_fd *fd = inode->closed;
|
|
||||||
inode->closed = fd->next;
|
|
||||||
close( fd->fd );
|
|
||||||
free( fd );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* retrieve the inode object for a given fd, creating it if needed */
|
/* 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->ino = ino;
|
||||||
inode->closed = NULL;
|
inode->closed = NULL;
|
||||||
list_init( &inode->open );
|
list_init( &inode->open );
|
||||||
|
list_init( &inode->locks );
|
||||||
list_add_head( &inode_hash[hash], &inode->entry );
|
list_add_head( &inode_hash[hash], &inode->entry );
|
||||||
}
|
}
|
||||||
return inode;
|
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 */
|
/* add fd to the indoe list of file descriptors to close */
|
||||||
static void inode_add_closed_fd( struct inode *inode, struct closed_fd *fd )
|
static void inode_add_closed_fd( struct inode *inode, struct closed_fd *fd )
|
||||||
{
|
{
|
||||||
fd->next = inode->closed;
|
if (!list_empty( &inode->locks ))
|
||||||
inode->closed = fd;
|
{
|
||||||
|
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;
|
struct fd *fd = (struct fd *)obj;
|
||||||
|
|
||||||
|
remove_fd_locks( fd );
|
||||||
list_remove( &fd->inode_entry );
|
list_remove( &fd->inode_entry );
|
||||||
if (fd->poll_index != -1) remove_poll_user( fd, fd->poll_index );
|
if (fd->poll_index != -1) remove_poll_user( fd, fd->poll_index );
|
||||||
if (fd->inode)
|
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->unix_fd = -1;
|
||||||
fd->poll_index = -1;
|
fd->poll_index = -1;
|
||||||
list_init( &fd->inode_entry );
|
list_init( &fd->inode_entry );
|
||||||
|
list_init( &fd->locks );
|
||||||
|
|
||||||
if ((fd->poll_index = add_poll_user( fd )) == -1)
|
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 ESPIPE: set_error( 0xc0010000 | ERROR_SEEK /* FIXME */ ); break;
|
||||||
case ENOTEMPTY: set_error( STATUS_DIRECTORY_NOT_EMPTY ); break;
|
case ENOTEMPTY: set_error( STATUS_DIRECTORY_NOT_EMPTY ); break;
|
||||||
case EIO: set_error( STATUS_ACCESS_VIOLATION ); 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;
|
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;
|
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 */
|
/* create a file */
|
||||||
DECL_HANDLER(create_file)
|
DECL_HANDLER(create_file)
|
||||||
{
|
{
|
||||||
|
@ -661,11 +648,13 @@ DECL_HANDLER(set_file_time)
|
||||||
DECL_HANDLER(lock_file)
|
DECL_HANDLER(lock_file)
|
||||||
{
|
{
|
||||||
struct file *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 )))
|
if ((file = get_file_obj( current->process, req->handle, 0 )))
|
||||||
{
|
{
|
||||||
file_lock( file, req->offset_high, req->offset_low,
|
reply->handle = lock_fd( file->fd, offset, count, req->shared, req->wait );
|
||||||
req->count_high, req->count_low );
|
reply->overlapped = (file->flags & FILE_FLAG_OVERLAPPED) != 0;
|
||||||
release_object( file );
|
release_object( file );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -674,11 +663,12 @@ DECL_HANDLER(lock_file)
|
||||||
DECL_HANDLER(unlock_file)
|
DECL_HANDLER(unlock_file)
|
||||||
{
|
{
|
||||||
struct file *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 )))
|
if ((file = get_file_obj( current->process, req->handle, 0 )))
|
||||||
{
|
{
|
||||||
file_unlock( file, req->offset_high, req->offset_low,
|
unlock_fd( file->fd, offset, count );
|
||||||
req->count_high, req->count_low );
|
|
||||||
release_object( file );
|
release_object( file );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,8 @@
|
||||||
|
|
||||||
struct fd;
|
struct fd;
|
||||||
|
|
||||||
|
typedef unsigned __int64 file_pos_t;
|
||||||
|
|
||||||
/* operations valid on file descriptor objects */
|
/* operations valid on file descriptor objects */
|
||||||
struct fd_ops
|
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 void fd_poll_event( struct fd *fd, int event );
|
||||||
extern int check_fd_events( struct fd *fd, int events );
|
extern int check_fd_events( struct fd *fd, int events );
|
||||||
extern void set_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 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 );
|
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 );
|
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 */
|
/* initialize a list */
|
||||||
inline static void list_init( struct list *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.namelen = 0;
|
||||||
process->exe.filename = NULL;
|
process->exe.filename = NULL;
|
||||||
process->group_id = 0;
|
process->group_id = 0;
|
||||||
|
list_init( &process->locks );
|
||||||
|
|
||||||
gettimeofday( &process->start_time, NULL );
|
gettimeofday( &process->start_time, NULL );
|
||||||
if ((process->next = first_process) != NULL) process->next->prev = process;
|
if ((process->next = first_process) != NULL) process->next->prev = process;
|
||||||
|
|
|
@ -67,6 +67,7 @@ struct process
|
||||||
int affinity; /* process affinity mask */
|
int affinity; /* process affinity mask */
|
||||||
int suspend; /* global process suspend count */
|
int suspend; /* global process suspend count */
|
||||||
int create_flags; /* process creation flags */
|
int create_flags; /* process creation flags */
|
||||||
|
struct list locks; /* list of file locks owned by the process */
|
||||||
struct console_input*console; /* console input */
|
struct console_input*console; /* console input */
|
||||||
enum startup_state startup_state; /* startup state */
|
enum startup_state startup_state; /* startup state */
|
||||||
struct startup_info *startup_info; /* startup info while init is in progress */
|
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 offset_high; /* offset of start of lock */
|
||||||
unsigned int count_low; /* count of bytes to lock */
|
unsigned int count_low; /* count of bytes to lock */
|
||||||
unsigned int count_high; /* 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
|
@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_low=%08x,", req->offset_low );
|
||||||
fprintf( stderr, " offset_high=%08x,", req->offset_high );
|
fprintf( stderr, " offset_high=%08x,", req->offset_high );
|
||||||
fprintf( stderr, " count_low=%08x,", req->count_low );
|
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 )
|
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)0,
|
(dump_func)0,
|
||||||
(dump_func)dump_get_file_info_reply,
|
(dump_func)dump_get_file_info_reply,
|
||||||
(dump_func)0,
|
(dump_func)dump_lock_file_reply,
|
||||||
(dump_func)0,
|
(dump_func)0,
|
||||||
(dump_func)dump_create_pipe_reply,
|
(dump_func)dump_create_pipe_reply,
|
||||||
(dump_func)dump_create_socket_reply,
|
(dump_func)dump_create_socket_reply,
|
||||||
|
|
Loading…
Reference in New Issue