Check file sharing permissions based on the file inode instead of the
file name. Added regression test for sharing permissions.
This commit is contained in:
parent
ae72222741
commit
74bd1e47ed
|
@ -914,6 +914,66 @@ static void test_LockFile(void)
|
||||||
DeleteFileA( filename );
|
DeleteFileA( filename );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int is_sharing_compatible( DWORD access1, DWORD sharing1, DWORD access2, DWORD sharing2 )
|
||||||
|
{
|
||||||
|
if (!access1 || !access2) return 1;
|
||||||
|
if ((access1 & GENERIC_READ) && !(sharing2 & FILE_SHARE_READ)) return 0;
|
||||||
|
if ((access1 & GENERIC_WRITE) && !(sharing2 & FILE_SHARE_WRITE)) return 0;
|
||||||
|
if ((access2 & GENERIC_READ) && !(sharing1 & FILE_SHARE_READ)) return 0;
|
||||||
|
if ((access2 & GENERIC_WRITE) && !(sharing1 & FILE_SHARE_WRITE)) return 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_file_sharing(void)
|
||||||
|
{
|
||||||
|
static const DWORD access_modes[4] = { 0, GENERIC_READ, GENERIC_WRITE, GENERIC_READ|GENERIC_WRITE };
|
||||||
|
static const DWORD sharing_modes[4] = { 0, FILE_SHARE_READ, FILE_SHARE_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE };
|
||||||
|
int a1, s1, a2, s2;
|
||||||
|
|
||||||
|
for (a1 = 0; a1 < 4; a1++)
|
||||||
|
{
|
||||||
|
for (s1 = 0; s1 < 4; s1++)
|
||||||
|
{
|
||||||
|
HANDLE h = CreateFileA( filename, access_modes[a1], sharing_modes[s1],
|
||||||
|
NULL, CREATE_ALWAYS, 0, 0 );
|
||||||
|
if (h == INVALID_HANDLE_VALUE)
|
||||||
|
{
|
||||||
|
ok(0,"couldn't create file \"%s\" (err=%ld)\n",filename,GetLastError());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (a2 = 0; a2 < 4; a2++)
|
||||||
|
{
|
||||||
|
for (s2 = 0; s2 < 4; s2++)
|
||||||
|
{
|
||||||
|
HANDLE h2 = CreateFileA( filename, access_modes[a2], sharing_modes[s2],
|
||||||
|
NULL, CREATE_ALWAYS, 0, 0 );
|
||||||
|
if (is_sharing_compatible( access_modes[a1], sharing_modes[s1],
|
||||||
|
access_modes[a2], sharing_modes[s2] ))
|
||||||
|
{
|
||||||
|
ok( h2 != INVALID_HANDLE_VALUE,
|
||||||
|
"open failed for modes %lx/%lx/%lx/%lx err %ld\n",
|
||||||
|
access_modes[a1], sharing_modes[s1],
|
||||||
|
access_modes[a2], sharing_modes[s2], GetLastError() );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ok( h2 == INVALID_HANDLE_VALUE,
|
||||||
|
"open succeeded for modes %lx/%lx/%lx/%lx\n",
|
||||||
|
access_modes[a1], sharing_modes[s1],
|
||||||
|
access_modes[a2], sharing_modes[s2] );
|
||||||
|
if (h2 == INVALID_HANDLE_VALUE)
|
||||||
|
ok( GetLastError() == ERROR_SHARING_VIOLATION,
|
||||||
|
"wrong error code %ld\n", GetLastError() );
|
||||||
|
}
|
||||||
|
if (h2 != INVALID_HANDLE_VALUE) CloseHandle( h2 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CloseHandle( h );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DeleteFileA( filename );
|
||||||
|
}
|
||||||
|
|
||||||
static void test_FindFirstFileA()
|
static void test_FindFirstFileA()
|
||||||
{
|
{
|
||||||
HANDLE handle;
|
HANDLE handle;
|
||||||
|
@ -1000,6 +1060,7 @@ START_TEST(file)
|
||||||
test_FindFirstFileA();
|
test_FindFirstFileA();
|
||||||
test_FindNextFileA();
|
test_FindNextFileA();
|
||||||
test_LockFile();
|
test_LockFile();
|
||||||
|
test_file_sharing();
|
||||||
test_offset_in_overlapped_structure();
|
test_offset_in_overlapped_structure();
|
||||||
test_MapFile();
|
test_MapFile();
|
||||||
}
|
}
|
||||||
|
|
49
server/fd.c
49
server/fd.c
|
@ -66,6 +66,8 @@ struct fd
|
||||||
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 */
|
struct list locks; /* list of locks on this fd */
|
||||||
|
unsigned int access; /* file access (GENERIC_READ/WRITE) */
|
||||||
|
unsigned int sharing; /* file sharing mode */
|
||||||
int unix_fd; /* unix file descriptor */
|
int unix_fd; /* unix file descriptor */
|
||||||
int fs_locks; /* can we use filesystem locks for this fd? */
|
int fs_locks; /* can we use filesystem locks for this fd? */
|
||||||
int poll_index; /* index of fd in poll array */
|
int poll_index; /* index of fd in poll array */
|
||||||
|
@ -817,6 +819,8 @@ struct fd *alloc_fd( const struct fd_ops *fd_user_ops, struct object *user )
|
||||||
fd->user = user;
|
fd->user = user;
|
||||||
fd->inode = NULL;
|
fd->inode = NULL;
|
||||||
fd->closed = NULL;
|
fd->closed = NULL;
|
||||||
|
fd->access = 0;
|
||||||
|
fd->sharing = 0;
|
||||||
fd->unix_fd = -1;
|
fd->unix_fd = -1;
|
||||||
fd->fs_locks = 1;
|
fd->fs_locks = 1;
|
||||||
fd->poll_index = -1;
|
fd->poll_index = -1;
|
||||||
|
@ -831,10 +835,41 @@ struct fd *alloc_fd( const struct fd_ops *fd_user_ops, struct object *user )
|
||||||
return fd;
|
return fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* check if the desired access is possible without violating */
|
||||||
|
/* the sharing mode of other opens of the same file */
|
||||||
|
static int check_sharing( struct fd *fd, unsigned int access, unsigned int sharing )
|
||||||
|
{
|
||||||
|
unsigned int existing_sharing = FILE_SHARE_READ | FILE_SHARE_WRITE;
|
||||||
|
unsigned int existing_access = 0;
|
||||||
|
struct list *ptr;
|
||||||
|
|
||||||
|
/* if access mode is 0, sharing mode is ignored */
|
||||||
|
if (!access) sharing = FILE_SHARE_READ|FILE_SHARE_WRITE;
|
||||||
|
fd->access = access;
|
||||||
|
fd->sharing = sharing;
|
||||||
|
|
||||||
|
LIST_FOR_EACH( ptr, &fd->inode->open )
|
||||||
|
{
|
||||||
|
struct fd *fd_ptr = LIST_ENTRY( ptr, struct fd, inode_entry );
|
||||||
|
if (fd_ptr != fd)
|
||||||
|
{
|
||||||
|
existing_sharing &= fd_ptr->sharing;
|
||||||
|
existing_access |= fd_ptr->access;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((access & GENERIC_READ) && !(existing_sharing & FILE_SHARE_READ)) return 0;
|
||||||
|
if ((access & GENERIC_WRITE) && !(existing_sharing & FILE_SHARE_WRITE)) return 0;
|
||||||
|
if ((existing_access & GENERIC_READ) && !(sharing & FILE_SHARE_READ)) return 0;
|
||||||
|
if ((existing_access & GENERIC_WRITE) && !(sharing & FILE_SHARE_WRITE)) return 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
/* open() wrapper using a struct fd */
|
/* open() wrapper using a struct fd */
|
||||||
/* the fd must have been created with alloc_fd */
|
/* the fd must have been created with alloc_fd */
|
||||||
/* on error the fd object is released */
|
/* on error the fd object is released */
|
||||||
struct fd *open_fd( struct fd *fd, const char *name, int flags, mode_t *mode )
|
struct fd *open_fd( struct fd *fd, const char *name, int flags, mode_t *mode,
|
||||||
|
unsigned int access, unsigned int sharing )
|
||||||
{
|
{
|
||||||
struct stat st;
|
struct stat st;
|
||||||
struct closed_fd *closed_fd;
|
struct closed_fd *closed_fd;
|
||||||
|
@ -873,6 +908,12 @@ struct fd *open_fd( struct fd *fd, const char *name, int flags, mode_t *mode )
|
||||||
fd->inode = inode;
|
fd->inode = inode;
|
||||||
fd->closed = closed_fd;
|
fd->closed = closed_fd;
|
||||||
list_add_head( &inode->open, &fd->inode_entry );
|
list_add_head( &inode->open, &fd->inode_entry );
|
||||||
|
if (!check_sharing( fd, access, sharing ))
|
||||||
|
{
|
||||||
|
release_object( fd );
|
||||||
|
set_error( STATUS_SHARING_VIOLATION );
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -908,6 +949,12 @@ int get_unix_fd( struct fd *fd )
|
||||||
return fd->unix_fd;
|
return fd->unix_fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* check if two file descriptors point to the same file */
|
||||||
|
int is_same_file_fd( struct fd *fd1, struct fd *fd2 )
|
||||||
|
{
|
||||||
|
return fd1->inode == fd2->inode;
|
||||||
|
}
|
||||||
|
|
||||||
/* callback for event happening in the main poll() loop */
|
/* callback for event happening in the main poll() loop */
|
||||||
void fd_poll_event( struct fd *fd, int event )
|
void fd_poll_event( struct fd *fd, int event )
|
||||||
{
|
{
|
||||||
|
|
|
@ -56,20 +56,14 @@ struct file
|
||||||
{
|
{
|
||||||
struct object obj; /* object header */
|
struct object obj; /* object header */
|
||||||
struct fd *fd; /* file descriptor for this file */
|
struct fd *fd; /* file descriptor for this file */
|
||||||
struct file *next; /* next file in hashing list */
|
|
||||||
char *name; /* file name */
|
char *name; /* file name */
|
||||||
unsigned int access; /* file access (GENERIC_READ/WRITE) */
|
unsigned int access; /* file access (GENERIC_READ/WRITE) */
|
||||||
unsigned int options; /* file options (FILE_DELETE_ON_CLOSE, FILE_SYNCHRONOUS...) */
|
unsigned int options; /* file options (FILE_DELETE_ON_CLOSE, FILE_SYNCHRONOUS...) */
|
||||||
unsigned int sharing; /* file sharing mode */
|
|
||||||
int removable; /* is file on removable media? */
|
int removable; /* is file on removable media? */
|
||||||
struct async_queue read_q;
|
struct async_queue read_q;
|
||||||
struct async_queue write_q;
|
struct async_queue write_q;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define NAME_HASH_SIZE 37
|
|
||||||
|
|
||||||
static struct file *file_hash[NAME_HASH_SIZE];
|
|
||||||
|
|
||||||
static void file_dump( struct object *obj, int verbose );
|
static void file_dump( struct object *obj, int verbose );
|
||||||
static struct fd *file_get_fd( struct object *obj );
|
static struct fd *file_get_fd( struct object *obj );
|
||||||
static void file_destroy( struct object *obj );
|
static void file_destroy( struct object *obj );
|
||||||
|
@ -106,38 +100,6 @@ static inline int is_overlapped( const struct file *file )
|
||||||
return !(file->options & (FILE_SYNCHRONOUS_IO_ALERT | FILE_SYNCHRONOUS_IO_NONALERT));
|
return !(file->options & (FILE_SYNCHRONOUS_IO_ALERT | FILE_SYNCHRONOUS_IO_NONALERT));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int get_name_hash( const char *name )
|
|
||||||
{
|
|
||||||
int hash = 0;
|
|
||||||
while (*name) hash ^= (unsigned char)*name++;
|
|
||||||
return hash % NAME_HASH_SIZE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* check if the desired access is possible without violating */
|
|
||||||
/* the sharing mode of other opens of the same file */
|
|
||||||
static int check_sharing( const char *name, int hash, unsigned int access,
|
|
||||||
unsigned int sharing )
|
|
||||||
{
|
|
||||||
struct file *file;
|
|
||||||
unsigned int existing_sharing = FILE_SHARE_READ | FILE_SHARE_WRITE;
|
|
||||||
unsigned int existing_access = 0;
|
|
||||||
|
|
||||||
for (file = file_hash[hash]; file; file = file->next)
|
|
||||||
{
|
|
||||||
if (strcmp( file->name, name )) continue;
|
|
||||||
existing_sharing &= file->sharing;
|
|
||||||
existing_access |= file->access;
|
|
||||||
}
|
|
||||||
if ((access & GENERIC_READ) && !(existing_sharing & FILE_SHARE_READ)) goto error;
|
|
||||||
if ((access & GENERIC_WRITE) && !(existing_sharing & FILE_SHARE_WRITE)) goto error;
|
|
||||||
if ((existing_access & GENERIC_READ) && !(sharing & FILE_SHARE_READ)) goto error;
|
|
||||||
if ((existing_access & GENERIC_WRITE) && !(sharing & FILE_SHARE_WRITE)) goto error;
|
|
||||||
return 1;
|
|
||||||
error:
|
|
||||||
set_error( STATUS_SHARING_VIOLATION );
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* create a file from a file descriptor */
|
/* create a file from a file descriptor */
|
||||||
/* if the function fails the fd is closed */
|
/* if the function fails the fd is closed */
|
||||||
static struct file *create_file_for_fd( int fd, unsigned int access, unsigned int sharing )
|
static struct file *create_file_for_fd( int fd, unsigned int access, unsigned int sharing )
|
||||||
|
@ -147,10 +109,8 @@ static struct file *create_file_for_fd( int fd, unsigned int access, unsigned in
|
||||||
if ((file = alloc_object( &file_ops )))
|
if ((file = alloc_object( &file_ops )))
|
||||||
{
|
{
|
||||||
file->name = NULL;
|
file->name = NULL;
|
||||||
file->next = NULL;
|
|
||||||
file->access = access;
|
file->access = access;
|
||||||
file->options = FILE_SYNCHRONOUS_IO_NONALERT;
|
file->options = FILE_SYNCHRONOUS_IO_NONALERT;
|
||||||
file->sharing = sharing;
|
|
||||||
file->removable = 0;
|
file->removable = 0;
|
||||||
if (!(file->fd = create_anonymous_fd( &file_fd_ops, fd, &file->obj )))
|
if (!(file->fd = create_anonymous_fd( &file_fd_ops, fd, &file->obj )))
|
||||||
{
|
{
|
||||||
|
@ -167,7 +127,7 @@ static struct object *create_file( const char *nameptr, size_t len, unsigned int
|
||||||
unsigned int attrs, int removable )
|
unsigned int attrs, int removable )
|
||||||
{
|
{
|
||||||
struct file *file;
|
struct file *file;
|
||||||
int hash, flags;
|
int flags;
|
||||||
char *name;
|
char *name;
|
||||||
mode_t mode;
|
mode_t mode;
|
||||||
|
|
||||||
|
@ -175,10 +135,6 @@ static struct object *create_file( const char *nameptr, size_t len, unsigned int
|
||||||
memcpy( name, nameptr, len );
|
memcpy( name, nameptr, len );
|
||||||
name[len] = 0;
|
name[len] = 0;
|
||||||
|
|
||||||
/* check sharing mode */
|
|
||||||
hash = get_name_hash( name );
|
|
||||||
if (!check_sharing( name, hash, access, sharing )) goto error;
|
|
||||||
|
|
||||||
switch(create)
|
switch(create)
|
||||||
{
|
{
|
||||||
case FILE_CREATE: flags = O_CREAT | O_EXCL; break;
|
case FILE_CREATE: flags = O_CREAT | O_EXCL; break;
|
||||||
|
@ -206,11 +162,8 @@ static struct object *create_file( const char *nameptr, size_t len, unsigned int
|
||||||
|
|
||||||
file->access = access;
|
file->access = access;
|
||||||
file->options = options;
|
file->options = options;
|
||||||
file->sharing = sharing;
|
|
||||||
file->removable = removable;
|
file->removable = removable;
|
||||||
file->name = name;
|
file->name = name;
|
||||||
file->next = file_hash[hash];
|
|
||||||
file_hash[hash] = file;
|
|
||||||
if (is_overlapped( file ))
|
if (is_overlapped( file ))
|
||||||
{
|
{
|
||||||
init_async_queue (&file->read_q);
|
init_async_queue (&file->read_q);
|
||||||
|
@ -219,7 +172,8 @@ static struct object *create_file( const char *nameptr, size_t len, unsigned int
|
||||||
|
|
||||||
/* FIXME: should set error to STATUS_OBJECT_NAME_COLLISION if file existed before */
|
/* FIXME: should set error to STATUS_OBJECT_NAME_COLLISION if file existed before */
|
||||||
if (!(file->fd = alloc_fd( &file_fd_ops, &file->obj )) ||
|
if (!(file->fd = alloc_fd( &file_fd_ops, &file->obj )) ||
|
||||||
!(file->fd = open_fd( file->fd, name, flags | O_NONBLOCK | O_LARGEFILE, &mode )))
|
!(file->fd = open_fd( file->fd, name, flags | O_NONBLOCK | O_LARGEFILE,
|
||||||
|
&mode, access, sharing)))
|
||||||
{
|
{
|
||||||
release_object( file );
|
release_object( file );
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -249,7 +203,7 @@ static struct object *create_file( const char *nameptr, size_t len, unsigned int
|
||||||
/* check if two file objects point to the same file */
|
/* check if two file objects point to the same file */
|
||||||
int is_same_file( struct file *file1, struct file *file2 )
|
int is_same_file( struct file *file1, struct file *file2 )
|
||||||
{
|
{
|
||||||
return !strcmp( file1->name, file2->name );
|
return is_same_file_fd( file1->fd, file2->fd );
|
||||||
}
|
}
|
||||||
|
|
||||||
/* check if the file is on removable media */
|
/* check if the file is on removable media */
|
||||||
|
@ -434,11 +388,6 @@ static void file_destroy( struct object *obj )
|
||||||
|
|
||||||
if (file->name)
|
if (file->name)
|
||||||
{
|
{
|
||||||
/* remove it from the hashing list */
|
|
||||||
struct file **pptr = &file_hash[get_name_hash( file->name )];
|
|
||||||
while (*pptr && *pptr != file) pptr = &(*pptr)->next;
|
|
||||||
assert( *pptr );
|
|
||||||
*pptr = (*pptr)->next;
|
|
||||||
if (file->options & FILE_DELETE_ON_CLOSE) unlink( file->name );
|
if (file->options & FILE_DELETE_ON_CLOSE) unlink( file->name );
|
||||||
free( file->name );
|
free( file->name );
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,11 +45,13 @@ struct fd_ops
|
||||||
/* file descriptor functions */
|
/* file descriptor functions */
|
||||||
|
|
||||||
extern struct fd *alloc_fd( const struct fd_ops *fd_user_ops, struct object *user );
|
extern struct fd *alloc_fd( const struct fd_ops *fd_user_ops, struct object *user );
|
||||||
extern struct fd *open_fd( struct fd *fd, const char *name, int flags, mode_t *mode );
|
extern struct fd *open_fd( struct fd *fd, const char *name, int flags, mode_t *mode,
|
||||||
|
unsigned int access, unsigned int sharing );
|
||||||
extern struct fd *create_anonymous_fd( const struct fd_ops *fd_user_ops,
|
extern struct fd *create_anonymous_fd( const struct fd_ops *fd_user_ops,
|
||||||
int unix_fd, struct object *user );
|
int unix_fd, struct object *user );
|
||||||
extern void *get_fd_user( struct fd *fd );
|
extern void *get_fd_user( struct fd *fd );
|
||||||
extern int get_unix_fd( struct fd *fd );
|
extern int get_unix_fd( struct fd *fd );
|
||||||
|
extern int is_same_file_fd( struct fd *fd1, struct fd *fd2 );
|
||||||
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 );
|
||||||
|
|
Loading…
Reference in New Issue