oleaut32: Fix OLEPicture rendering vector metafiles without setting up the source...
[wine] / server / object.c
index cda7bfe..f08548c 100644 (file)
 /*
  * Server-side objects
- * These are the server equivalent of K32OBJ
  *
  * Copyright (C) 1998 Alexandre Julliard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
+#include "config.h"
+#include "wine/port.h"
+
 #include <assert.h>
 #include <limits.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
 #include <unistd.h>
+#include <stdarg.h>
 
+#include "ntstatus.h"
+#define WIN32_NO_STATUS
+#include "winternl.h"
+
+#include "file.h"
+#include "process.h"
 #include "thread.h"
 #include "unicode.h"
+#include "security.h"
 
 
 struct object_name
 {
-    struct object_name *next;
-    struct object_name *prev;
-    struct object      *obj;
-    size_t              len;
+    struct list         entry;           /* entry in the hash list */
+    struct object      *obj;             /* object owning this name */
+    struct object      *parent;          /* parent object */
+    data_size_t         len;             /* name length in bytes */
     WCHAR               name[1];
 };
 
-#define NAME_HASH_SIZE 37
+struct namespace
+{
+    unsigned int        hash_size;       /* size of hash table */
+    struct list         names[1];        /* array of hash entry lists */
+};
 
-static struct object_name *names[NAME_HASH_SIZE];
 
 #ifdef DEBUG_OBJECTS
-static struct object *first;
+static struct list object_list = LIST_INIT(object_list);
+static struct list static_object_list = LIST_INIT(static_object_list);
 
 void dump_objects(void)
 {
-    struct object *ptr = first;
-    while (ptr)
+    struct list *p;
+
+    LIST_FOR_EACH( p, &static_object_list )
     {
+        struct object *ptr = LIST_ENTRY( p, struct object, obj_list );
+        fprintf( stderr, "%p:%d: ", ptr, ptr->refcount );
+        ptr->ops->dump( ptr, 1 );
+    }
+    LIST_FOR_EACH( p, &object_list )
+    {
+        struct object *ptr = LIST_ENTRY( p, struct object, obj_list );
         fprintf( stderr, "%p:%d: ", ptr, ptr->refcount );
         ptr->ops->dump( ptr, 1 );
-        ptr = ptr->next;
     }
 }
-#endif
+
+void close_objects(void)
+{
+    struct list *ptr;
+
+    /* release the static objects */
+    while ((ptr = list_head( &static_object_list )))
+    {
+        struct object *obj = LIST_ENTRY( ptr, struct object, obj_list );
+        /* move it back to the standard list before freeing */
+        list_remove( &obj->obj_list );
+        list_add_head( &object_list, &obj->obj_list );
+        release_object( obj );
+    }
+
+    dump_objects();  /* dump any remaining objects */
+}
+
+#endif  /* DEBUG_OBJECTS */
 
 /*****************************************************************/
 
@@ -67,23 +120,24 @@ void *memdup( const void *data, size_t len )
 
 /*****************************************************************/
 
-static int get_name_hash( const WCHAR *name, size_t len )
+static int get_name_hash( const struct namespace *namespace, const WCHAR *name, data_size_t len )
 {
     WCHAR hash = 0;
-    while (len--) hash ^= *name++;
-    return hash % NAME_HASH_SIZE;
+    len /= sizeof(WCHAR);
+    while (len--) hash ^= tolowerW(*name++);
+    return hash % namespace->hash_size;
 }
 
 /* allocate a name for an object */
-static struct object_name *alloc_name( const WCHAR *name, size_t len )
+static struct object_name *alloc_name( const struct unicode_str *name )
 {
     struct object_name *ptr;
 
-    if ((ptr = mem_alloc( sizeof(*ptr) + len * sizeof(ptr->name[0]) )))
+    if ((ptr = mem_alloc( sizeof(*ptr) + name->len - sizeof(ptr->name) )))
     {
-        ptr->len = len;
-        memcpy( ptr->name, name, len * sizeof(ptr->name[0]) );
-        ptr->name[len] = 0;
+        ptr->len = name->len;
+        ptr->parent = NULL;
+        memcpy( ptr->name, name->str, name->len );
     }
     return ptr;
 }
@@ -92,90 +146,90 @@ static struct object_name *alloc_name( const WCHAR *name, size_t len )
 static void free_name( struct object *obj )
 {
     struct object_name *ptr = obj->name;
-    if (ptr->next) ptr->next->prev = ptr->prev;
-    if (ptr->prev) ptr->prev->next = ptr->next;
-    else
-    {
-        int hash;
-        for (hash = 0; hash < NAME_HASH_SIZE; hash++)
-            if (names[hash] == ptr)
-            {
-                names[hash] = ptr->next;
-                break;
-            }
-    }
+    list_remove( &ptr->entry );
+    if (ptr->parent) release_object( ptr->parent );
     free( ptr );
 }
 
 /* set the name of an existing object */
-static void set_object_name( struct object *obj, struct object_name *ptr )
+static void set_object_name( struct namespace *namespace,
+                             struct object *obj, struct object_name *ptr )
 {
-    int hash = get_name_hash( ptr->name, ptr->len );
+    int hash = get_name_hash( namespace, ptr->name, ptr->len );
 
-    if ((ptr->next = names[hash]) != NULL) ptr->next->prev = ptr;
+    list_add_head( &namespace->names[hash], &ptr->entry );
     ptr->obj = obj;
-    ptr->prev = NULL;
-    names[hash] = ptr;
-    assert( !obj->name );
     obj->name = ptr;
 }
 
+/* get the name of an existing object */
+const WCHAR *get_object_name( struct object *obj, data_size_t *len )
+{
+    struct object_name *ptr = obj->name;
+    if (!ptr) return NULL;
+    *len = ptr->len;
+    return ptr->name;
+}
+
 /* allocate and initialize an object */
-/* if the function fails the fd is closed */
-void *alloc_object( const struct object_ops *ops, int fd )
+void *alloc_object( const struct object_ops *ops )
 {
     struct object *obj = mem_alloc( ops->size );
     if (obj)
     {
         obj->refcount = 1;
-        obj->fd       = fd;
-        obj->select   = -1;
         obj->ops      = ops;
-        obj->head     = NULL;
-        obj->tail     = NULL;
         obj->name     = NULL;
-        if ((fd != -1) && (add_select_user( obj ) == -1))
-        {
-            close( fd );
-            free( obj );
-            return NULL;
-        }
+        obj->sd       = NULL;
+        list_init( &obj->wait_queue );
 #ifdef DEBUG_OBJECTS
-        obj->prev = NULL;
-        if ((obj->next = first) != NULL) obj->next->prev = obj;
-        first = obj;
+        list_add_head( &object_list, &obj->obj_list );
 #endif
         return obj;
     }
-    if (fd != -1) close( fd );
     return NULL;
 }
 
-void *create_named_object( const struct object_ops *ops, const WCHAR *name, size_t len )
+void *create_object( struct namespace *namespace, const struct object_ops *ops,
+                     const struct unicode_str *name, struct object *parent )
 {
     struct object *obj;
     struct object_name *name_ptr;
 
-    if (!name || !len) return alloc_object( ops, -1 );
-    if (!(name_ptr = alloc_name( name, len ))) return NULL;
+    if (!(name_ptr = alloc_name( name ))) return NULL;
+    if ((obj = alloc_object( ops )))
+    {
+        set_object_name( namespace, obj, name_ptr );
+        if (parent) name_ptr->parent = grab_object( parent );
+    }
+    else
+        free( name_ptr );
+    return obj;
+}
+
+void *create_named_object( struct namespace *namespace, const struct object_ops *ops,
+                           const struct unicode_str *name, unsigned int attributes )
+{
+    struct object *obj;
 
-    if ((obj = find_object( name_ptr->name, name_ptr->len )))
+    if (!name || !name->len) return alloc_object( ops );
+
+    if ((obj = find_object( namespace, name, attributes )))
     {
-        free( name_ptr );  /* we no longer need it */
-        if (obj->ops == ops)
+        if (attributes & OBJ_OPENIF && obj->ops == ops)
+            set_error( STATUS_OBJECT_NAME_EXISTS );
+        else
         {
-            set_error( STATUS_OBJECT_NAME_COLLISION );
-            return obj;
+            release_object( obj );
+            obj = NULL;
+            if (attributes & OBJ_OPENIF)
+                set_error( STATUS_OBJECT_TYPE_MISMATCH );
+            else
+                set_error( STATUS_OBJECT_NAME_COLLISION );
         }
-        set_error( STATUS_OBJECT_TYPE_MISMATCH );
-        return NULL;
-    }
-    if ((obj = alloc_object( ops, -1 )))
-    {
-        set_object_name( obj, name_ptr );
-        clear_error();
+        return obj;
     }
-    else free( name_ptr );
+    if ((obj = create_object( namespace, ops, name, NULL ))) clear_error();
     return obj;
 }
 
@@ -186,11 +240,27 @@ void dump_object_name( struct object *obj )
     else
     {
         fprintf( stderr, "name=L\"" );
-        dump_strW( obj->name->name, strlenW(obj->name->name), stderr, "\"\"" );
+        dump_strW( obj->name->name, obj->name->len/sizeof(WCHAR), stderr, "\"\"" );
         fputc( '\"', stderr );
     }
 }
 
+/* unlink a named object from its namespace, without freeing the object itself */
+void unlink_named_object( struct object *obj )
+{
+    if (obj->name) free_name( obj );
+    obj->name = NULL;
+}
+
+/* mark an object as being stored statically, i.e. only released at shutdown */
+void make_object_static( struct object *obj )
+{
+#ifdef DEBUG_OBJECTS
+    list_remove( &obj->obj_list );
+    list_add_head( &static_object_list, &obj->obj_list );
+#endif
+}
+
 /* grab an object (i.e. increment its refcount) and return the object */
 struct object *grab_object( void *ptr )
 {
@@ -208,16 +278,12 @@ void release_object( void *ptr )
     if (!--obj->refcount)
     {
         /* if the refcount is 0, nobody can be in the wait queue */
-        assert( !obj->head );
-        assert( !obj->tail );
+        assert( list_empty( &obj->wait_queue ));
         obj->ops->destroy( obj );
         if (obj->name) free_name( obj );
-        if (obj->select != -1) remove_select_user( obj );
-        if (obj->fd != -1) close( obj->fd );
+        free( obj->sd );
 #ifdef DEBUG_OBJECTS
-        if (obj->next) obj->next->prev = obj->prev;
-        if (obj->prev) obj->prev->next = obj->next;
-        else first = obj->next;
+        list_remove( &obj->obj_list );
         memset( obj, 0xaa, obj->ops->size );
 #endif
         free( obj );
@@ -225,20 +291,73 @@ void release_object( void *ptr )
 }
 
 /* find an object by its name; the refcount is incremented */
-struct object *find_object( const WCHAR *name, size_t len )
+struct object *find_object( const struct namespace *namespace, const struct unicode_str *name,
+                            unsigned int attributes )
 {
-    struct object_name *ptr;
-    if (!name || !len) return NULL;
-    for (ptr = names[ get_name_hash( name, len ) ]; ptr; ptr = ptr->next)
+    const struct list *list;
+    struct list *p;
+
+    if (!name || !name->len) return NULL;
+
+    list = &namespace->names[ get_name_hash( namespace, name->str, name->len ) ];
+    LIST_FOR_EACH( p, list )
+    {
+        const struct object_name *ptr = LIST_ENTRY( p, struct object_name, entry );
+        if (ptr->len != name->len) continue;
+        if (attributes & OBJ_CASE_INSENSITIVE)
+        {
+            if (!strncmpiW( ptr->name, name->str, name->len/sizeof(WCHAR) ))
+                return grab_object( ptr->obj );
+        }
+        else
+        {
+            if (!memcmp( ptr->name, name->str, name->len ))
+                return grab_object( ptr->obj );
+        }
+    }
+    return NULL;
+}
+
+/* find an object by its index; the refcount is incremented */
+struct object *find_object_index( const struct namespace *namespace, unsigned int index )
+{
+    unsigned int i;
+
+    /* FIXME: not efficient at all */
+    for (i = 0; i < namespace->hash_size; i++)
     {
-        if (ptr->len != len) continue;
-        if (!memcmp( ptr->name, name, len*sizeof(WCHAR) )) return grab_object( ptr->obj );
+        const struct object_name *ptr;
+        LIST_FOR_EACH_ENTRY( ptr, &namespace->names[i], const struct object_name, entry )
+        {
+            if (!index--) return grab_object( ptr->obj );
+        }
     }
+    set_error( STATUS_NO_MORE_ENTRIES );
     return NULL;
 }
 
+/* allocate a namespace */
+struct namespace *create_namespace( unsigned int hash_size )
+{
+    struct namespace *namespace;
+    unsigned int i;
+
+    namespace = mem_alloc( sizeof(*namespace) + (hash_size - 1) * sizeof(namespace->names[0]) );
+    if (namespace)
+    {
+        namespace->hash_size      = hash_size;
+        for (i = 0; i < hash_size; i++) list_init( &namespace->names[i] );
+    }
+    return namespace;
+}
+
 /* functions for unimplemented/default object operations */
 
+struct object_type *no_get_type( struct object *obj )
+{
+    return NULL;
+}
+
 int no_add_queue( struct object *obj, struct wait_queue_entry *entry )
 {
     set_error( STATUS_OBJECT_TYPE_MISMATCH );
@@ -250,73 +369,138 @@ int no_satisfied( struct object *obj, struct thread *thread )
     return 0;  /* not abandoned */
 }
 
-int no_read_fd( struct object *obj )
+int no_signal( struct object *obj, unsigned int access )
 {
     set_error( STATUS_OBJECT_TYPE_MISMATCH );
-    return -1;
+    return 0;
 }
 
-int no_write_fd( struct object *obj )
+struct fd *no_get_fd( struct object *obj )
 {
     set_error( STATUS_OBJECT_TYPE_MISMATCH );
-    return -1;
+    return NULL;
 }
 
-int no_flush( struct object *obj )
+unsigned int no_map_access( struct object *obj, unsigned int access )
 {
-    set_error( STATUS_OBJECT_TYPE_MISMATCH );
-    return 0;
+    if (access & GENERIC_READ)    access |= STANDARD_RIGHTS_READ;
+    if (access & GENERIC_WRITE)   access |= STANDARD_RIGHTS_WRITE;
+    if (access & GENERIC_EXECUTE) access |= STANDARD_RIGHTS_EXECUTE;
+    if (access & GENERIC_ALL)     access |= STANDARD_RIGHTS_ALL;
+    return access & ~(GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL);
 }
 
-int no_get_file_info( struct object *obj, struct get_file_info_request *info )
+struct security_descriptor *default_get_sd( struct object *obj )
 {
-    set_error( STATUS_OBJECT_TYPE_MISMATCH );
-    return 0;
+    return obj->sd;
 }
 
-void no_destroy( struct object *obj )
+int default_set_sd( struct object *obj, const struct security_descriptor *sd,
+                    unsigned int set_info )
 {
-}
+    struct security_descriptor new_sd, *new_sd_ptr;
+    int present;
+    const SID *owner, *group;
+    const ACL *sacl, *dacl;
+    char *ptr;
 
-/* default add_queue() routine for objects that poll() on an fd */
-int default_poll_add_queue( struct object *obj, struct wait_queue_entry *entry )
-{
-    if (!obj->head)  /* first on the queue */
-        set_select_events( obj, obj->ops->get_poll_events( obj ) );
-    add_queue( obj, entry );
+    if (!set_info) return 1;
+
+    new_sd.control = sd->control & ~SE_SELF_RELATIVE;
+
+    owner = sd_get_owner( sd );
+    if (set_info & OWNER_SECURITY_INFORMATION && owner)
+        new_sd.owner_len = sd->owner_len;
+    else
+    {
+        owner = token_get_user( current->process->token );
+        new_sd.owner_len = FIELD_OFFSET(SID, SubAuthority[owner->SubAuthorityCount]);
+        new_sd.control |= SE_OWNER_DEFAULTED;
+    }
+
+    group = sd_get_group( sd );
+    if (set_info & GROUP_SECURITY_INFORMATION && group)
+        new_sd.group_len = sd->group_len;
+    else
+    {
+        group = token_get_primary_group( current->process->token );
+        new_sd.group_len = FIELD_OFFSET(SID, SubAuthority[group->SubAuthorityCount]);
+        new_sd.control |= SE_GROUP_DEFAULTED;
+    }
+
+    new_sd.control |= SE_SACL_PRESENT;
+    sacl = sd_get_sacl( sd, &present );
+    if (set_info & SACL_SECURITY_INFORMATION && present)
+        new_sd.sacl_len = sd->sacl_len;
+    else
+    {
+        if (obj->sd) sacl = sd_get_sacl( obj->sd, &present );
+
+        if (obj->sd && present)
+            new_sd.sacl_len = obj->sd->sacl_len;
+        else
+        {
+            new_sd.sacl_len = 0;
+            new_sd.control |= SE_SACL_DEFAULTED;
+        }
+    }
+
+    new_sd.control |= SE_DACL_PRESENT;
+    dacl = sd_get_dacl( sd, &present );
+    if (set_info & DACL_SECURITY_INFORMATION && present)
+        new_sd.dacl_len = sd->dacl_len;
+    else
+    {
+        if (obj->sd) dacl = sd_get_dacl( obj->sd, &present );
+
+        if (obj->sd && present)
+            new_sd.dacl_len = obj->sd->dacl_len;
+        else
+        {
+            dacl = token_get_default_dacl( current->process->token );
+            new_sd.dacl_len = dacl->AclSize;
+            new_sd.control |= SE_DACL_DEFAULTED;
+        }
+    }
+
+    ptr = mem_alloc( sizeof(new_sd) + new_sd.owner_len + new_sd.group_len +
+                     new_sd.sacl_len + new_sd.dacl_len );
+    if (!ptr) return 0;
+    new_sd_ptr = (struct security_descriptor*)ptr;
+
+    memcpy( ptr, &new_sd, sizeof(new_sd) );
+    ptr += sizeof(new_sd);
+    memcpy( ptr, owner, new_sd.owner_len );
+    ptr += new_sd.owner_len;
+    memcpy( ptr, group, new_sd.group_len );
+    ptr += new_sd.group_len;
+    memcpy( ptr, sacl, new_sd.sacl_len );
+    ptr += new_sd.sacl_len;
+    memcpy( ptr, dacl, new_sd.dacl_len );
+
+    free( obj->sd );
+    obj->sd = new_sd_ptr;
     return 1;
 }
 
-/* default remove_queue() routine for objects that poll() on an fd */
-void default_poll_remove_queue( struct object *obj, struct wait_queue_entry *entry )
+struct object *no_lookup_name( struct object *obj, struct unicode_str *name,
+                               unsigned int attr )
 {
-    grab_object(obj);
-    remove_queue( obj, entry );
-    if (!obj->head)  /* last on the queue is gone */
-        set_select_events( obj, 0 );
-    release_object( obj );
+    return NULL;
 }
 
-/* default signaled() routine for objects that poll() on an fd */
-int default_poll_signaled( struct object *obj, struct thread *thread )
+struct object *no_open_file( struct object *obj, unsigned int access, unsigned int sharing,
+                             unsigned int options )
 {
-    int events = obj->ops->get_poll_events( obj );
+    set_error( STATUS_OBJECT_TYPE_MISMATCH );
+    return NULL;
+}
 
-    if (check_select_events( obj->fd, events ))
-    {
-        /* stop waiting on select() if we are signaled */
-        set_select_events( obj, 0 );
-        return 1;
-    }
-    /* restart waiting on select() if we are no longer signaled */
-    if (obj->head) set_select_events( obj, events );
-    return 0;
+int no_close_handle( struct object *obj, struct process *process, obj_handle_t handle )
+{
+    return 1;  /* ok to close */
 }
 
-/* default handler for poll() events */
-void default_poll_event( struct object *obj, int event )
+void no_destroy( struct object *obj )
 {
-    /* an error occurred, stop polling this fd to avoid busy-looping */
-    if (event & (POLLERR | POLLHUP)) set_select_events( obj, -1 );
-    wake_up( obj, 0 );
 }