Check file sharing permissions based on the file inode instead of the
file name.
Added regression test for sharing permissions.

diff --git a/server/fd.c b/server/fd.c
index c41c4ed..f9491c3 100644
--- a/server/fd.c
+++ b/server/fd.c
@@ -66,6 +66,8 @@
     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 */
+    unsigned int         access;      /* file access (GENERIC_READ/WRITE) */
+    unsigned int         sharing;     /* file sharing mode */
     int                  unix_fd;     /* unix file descriptor */
     int                  fs_locks;    /* can we use filesystem locks for this fd? */
     int                  poll_index;  /* index of fd in poll array */
@@ -817,6 +819,8 @@
     fd->user       = user;
     fd->inode      = NULL;
     fd->closed     = NULL;
+    fd->access     = 0;
+    fd->sharing    = 0;
     fd->unix_fd    = -1;
     fd->fs_locks   = 1;
     fd->poll_index = -1;
@@ -831,10 +835,41 @@
     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 */
 /* the fd must have been created with alloc_fd */
 /* 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 closed_fd *closed_fd;
@@ -873,6 +908,12 @@
         fd->inode = inode;
         fd->closed = closed_fd;
         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
     {
@@ -908,6 +949,12 @@
     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 */
 void fd_poll_event( struct fd *fd, int event )
 {
diff --git a/server/file.c b/server/file.c
index 6b803f4..1b21930 100644
--- a/server/file.c
+++ b/server/file.c
@@ -56,20 +56,14 @@
 {
     struct object       obj;        /* object header */
     struct fd          *fd;         /* file descriptor for this file */
-    struct file        *next;       /* next file in hashing list */
     char               *name;       /* file name */
     unsigned int        access;     /* file access (GENERIC_READ/WRITE) */
     unsigned int        options;    /* file options (FILE_DELETE_ON_CLOSE, FILE_SYNCHRONOUS...) */
-    unsigned int        sharing;    /* file sharing mode */
     int                 removable;  /* is file on removable media? */
     struct async_queue  read_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 struct fd *file_get_fd( struct object *obj );
 static void file_destroy( struct object *obj );
@@ -106,38 +100,6 @@
     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 */
 /* if the function fails the fd is closed */
 static struct file *create_file_for_fd( int fd, unsigned int access, unsigned int sharing )
@@ -147,10 +109,8 @@
     if ((file = alloc_object( &file_ops )))
     {
         file->name       = NULL;
-        file->next       = NULL;
         file->access     = access;
         file->options    = FILE_SYNCHRONOUS_IO_NONALERT;
-        file->sharing    = sharing;
         file->removable  = 0;
         if (!(file->fd = create_anonymous_fd( &file_fd_ops, fd, &file->obj )))
         {
@@ -167,7 +127,7 @@
                                    unsigned int attrs, int removable )
 {
     struct file *file;
-    int hash, flags;
+    int flags;
     char *name;
     mode_t mode;
 
@@ -175,10 +135,6 @@
     memcpy( name, nameptr, len );
     name[len] = 0;
 
-    /* check sharing mode */
-    hash = get_name_hash( name );
-    if (!check_sharing( name, hash, access, sharing )) goto error;
-
     switch(create)
     {
     case FILE_CREATE:       flags = O_CREAT | O_EXCL; break;
@@ -206,11 +162,8 @@
 
     file->access     = access;
     file->options    = options;
-    file->sharing    = sharing;
     file->removable  = removable;
     file->name       = name;
-    file->next       = file_hash[hash];
-    file_hash[hash]  = file;
     if (is_overlapped( file ))
     {
         init_async_queue (&file->read_q);
@@ -219,7 +172,8 @@
 
     /* FIXME: should set error to STATUS_OBJECT_NAME_COLLISION if file existed before */
     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 );
         return NULL;
@@ -249,7 +203,7 @@
 /* check if two file objects point to the same file */
 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 */
@@ -434,11 +388,6 @@
 
     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 );
         free( file->name );
     }
diff --git a/server/file.h b/server/file.h
index 85b47ae..8376e1b 100644
--- a/server/file.h
+++ b/server/file.h
@@ -45,11 +45,13 @@
 /* file descriptor functions */
 
 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,
                                        int unix_fd, struct object *user );
 extern void *get_fd_user( 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 int check_fd_events( struct fd *fd, int events );
 extern void set_fd_events( struct fd *fd, int events );