Implemented file locking functions (partly based on my old Corel

patch). Added a few regression tests.
This commit is contained in:
Alexandre Julliard 2003-03-18 05:04:33 +00:00
parent 6fb0277699
commit ce61349334
12 changed files with 558 additions and 268 deletions

View File

@ -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();
}

View File

@ -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;
}
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,221 +3110,17 @@ 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
{
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;
}
if (reserved)
{
SetLastError( ERROR_INVALID_PARAMETER );
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;
return UnlockFile( hFile, overlapped->Offset, overlapped->OffsetHigh, count_low, count_high );
}
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.@)

View File

@ -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)

View File

@ -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 */

View File

@ -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 )
{
fd->next = inode->closed;
inode->closed = 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)
{

View File

@ -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 );
}
}

View 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 );

View File

@ -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 )
{

View File

@ -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;

View File

@ -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 */

View File

@ -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

View File

@ -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,