/* * Server-side directory object management * * Copyright (C) 2005 Vitaliy Margolen * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include "config.h" #include "wine/port.h" #include #include #include #include #include "windef.h" #include "winternl.h" #include "handle.h" #include "request.h" #include "process.h" #include "unicode.h" #include "wine/list.h" #include "object.h" #define HASH_SIZE 37 #define DIRECTORY_QUERY (0x0001) #define DIRECTORY_TRAVERSE (0x0002) #define DIRECTORY_CREATE_OBJECT (0x0004) #define DIRECTORY_CREATE_SUBDIRECTORY (0x0008) #define DIRECTORY_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | 0xF) struct directory { struct object obj; /* object header */ struct list entries[HASH_SIZE]; /* array of hash entry lists */ }; static void directory_dump( struct object *obj, int verbose ); static struct object *directory_lookup_name( struct object *obj, struct unicode_str *name, unsigned int attr ); static const struct object_ops directory_ops = { sizeof(struct directory), /* size */ directory_dump, /* dump */ no_add_queue, /* add_queue */ NULL, /* remove_queue */ NULL, /* signaled */ NULL, /* satisfied */ no_signal, /* signal */ no_get_fd, /* get_fd */ directory_lookup_name, /* lookup_name */ no_close_handle, /* close_handle */ no_destroy /* destroy */ }; static void directory_dump( struct object *obj, int verbose ) { struct directory *dir = (struct directory *)obj; assert( obj->ops == &directory_ops ); fputs( "Directory ", stderr ); dump_object_name( obj ); if (verbose) { unsigned int i; fputs( " entries:\n", stderr ); for (i = 0; i < HASH_SIZE; i++) { struct list *p = &dir->entries[i]; struct object_name *ptr; LIST_FOR_EACH_ENTRY(ptr, p, struct object_name, entry) { fputs( " ", stderr ); dump_object_name( ptr->obj ); fputc( '\n', stderr ); } } } else fputc( '\n', stderr ); } /* find an object by its name in a given directory; the refcount is incremented * name - is a relative name */ static struct object *find_object_in_dir( struct directory *dir, const WCHAR *name, size_t len, unsigned int attr ) { const struct list *list, *p; if (!name || !len) return NULL; list = &dir->entries[ get_name_hash( name, len ) % HASH_SIZE ]; LIST_FOR_EACH( p, list ) { const struct object_name *ptr = LIST_ENTRY( p, const struct object_name, entry ); if (!ptr || ptr->len != len) continue; if (attr & OBJ_CASE_INSENSITIVE) { if (!strncmpiW( ptr->name, name, len/sizeof(WCHAR) )) return grab_object( ptr->obj ); } else { if (!memcmp( ptr->name, name, len )) return grab_object( ptr->obj ); } } return NULL; } static struct object *directory_lookup_name( struct object *obj, struct unicode_str *name, unsigned int attr ) { struct object *found; WCHAR *p; size_t sz; assert( obj->ops == &directory_ops ); if (!(p = memchrW( name->str, '\\', name->len / sizeof(WCHAR) ))) /* Last element in the path name */ sz = name->len; else sz = (p - name->str) * sizeof(WCHAR); if ((found = find_object_in_dir( (struct directory *)obj, name->str, sz, attr ))) { /* Skip trailing \\ */ if (p) { p++; sz += sizeof(WCHAR); } /* Move to the next element*/ name->str = p; name->len -= sz; return found; } if (name->str) { if (sz == 0) /* Double backslash */ set_error( STATUS_OBJECT_NAME_INVALID ); else if (p) /* Path still has backslashes */ set_error( STATUS_OBJECT_PATH_NOT_FOUND ); } return NULL; } /******************************************************************************/ /* Create root directory - it's a special case when no namespace exists yet.*/ struct directory *create_root( void ) { static const WCHAR rootW[]={'\\'}; static const struct unicode_str root = {rootW, sizeof(rootW)}; struct directory *dir; struct object_name *name_ptr; if (!(name_ptr = alloc_name( &root ))) return NULL; if ((dir = alloc_object( &directory_ops ))) { unsigned int i; /* root has no parents */ list_init( &name_ptr->entry ); name_ptr->obj = &dir->obj; dir->obj.name = name_ptr; for (i = 0; i < HASH_SIZE; i++) list_init( &dir->entries[i] ); } else free( name_ptr ); return dir; } struct directory *create_directory( const struct object_attr *attr ) { struct directory *dir; if ((dir = create_named_object( attr, &directory_ops ))) { if (get_error() != STATUS_OBJECT_NAME_EXISTS) { unsigned int i; for (i = 0; i < HASH_SIZE; i++) list_init( &dir->entries[i] ); } } return dir; } static void insert_into_dir( struct directory* dir, struct object *obj ) { int hash = get_name_hash( obj->name->name, obj->name->len) % HASH_SIZE; grab_object( dir ); obj->name->parent = &dir->obj; list_add_head( &dir->entries[hash], &obj->name->entry ); } /******************************************************************************/ /* * Find an object by it's name in a given root object * * PARAMS * attr [I] rootdir, name and attributes * name_left [O] [optional] leftover name if object is not found * * RETURNS * NULL: If params are invalid * Found: If object with exact name is found returns that object. (name_left->len == 0) * Object's refcount is incremented * Not found: The last matched parent. (name_left->len > 0) * Parent's refcount is incremented. */ struct object *find_object( const struct object_attr *attr, struct unicode_str *name_left ) { struct object *obj, *parent; struct unicode_str name_l = *attr->name; /* Arguments check: * - Either name or root have to be specified * - If root is specified path shouldn't start with backslash */ if (!attr || (!attr->rootdir && (!name_l.str || !name_l.len)) || (name_l.len && ((*name_l.str == '\\' && attr->rootdir) || (*name_l.str != '\\' && !attr->rootdir)))) { set_error( STATUS_OBJECT_PATH_SYNTAX_BAD ); return NULL; } if (attr->rootdir) parent = get_handle_obj( current->process, attr->rootdir, 0, &directory_ops ); else parent = grab_object( root_directory ); if (!parent) return NULL; /* Skip leading backslash */ if (name_l.len && *name_l.str == '\\') { name_l.str++; name_l.len -= sizeof(WCHAR); } /* Special case for opening RootDirectory */ if (!name_l.len) goto done; while ((obj = parent->ops->lookup_name( parent, &name_l, attr->attributes ))) { /* move to the next element */ release_object ( parent ); parent = obj; } if (get_error()) { release_object ( parent ); return NULL; } done: if (name_left) dup_unicode_str(&name_l, name_left); return parent; } void *create_named_object( const struct object_attr *attr, const struct object_ops *ops ) { struct object *obj, *new_obj = NULL; struct unicode_str new_name; struct object_name *name_ptr; if (!attr || !attr->name || !attr->name->len) { if((new_obj = alloc_object( ops ))) return new_obj; } if (!(obj = find_object( attr, &new_name ))) return NULL; if (!new_name.len) { if (attr->attributes & OBJ_OPENIF && obj->ops == ops) set_error( STATUS_OBJECT_NAME_EXISTS ); else { release_object( obj ); obj = NULL; if (attr->attributes & OBJ_OPENIF) set_error( STATUS_OBJECT_TYPE_MISMATCH ); else set_error( STATUS_OBJECT_NAME_COLLISION ); } return obj; } /* We can't insert objects into anything else but directories */ if (obj->ops != &directory_ops) { release_object( obj ); set_error( STATUS_OBJECT_TYPE_MISMATCH ); return NULL; } if (!(name_ptr = alloc_name( &new_name ))) goto done; if ((new_obj = alloc_object( ops ))) { name_ptr->obj = new_obj; new_obj->name = name_ptr; insert_into_dir( (struct directory *)obj, new_obj ); clear_error(); } else free( name_ptr ); done: release_object( obj ); free_unicode_str( &new_name ); if (new_obj){fprintf(stderr, "create_named_object:%d: mew_obj: ", __LINE__);dump_object_name( new_obj );fprintf(stderr, " in: "); dump_object_name( obj ); fprintf(stderr, " refcount=%d \n", obj->refcount);} return new_obj; } /* open a new handle to an existing object */ obj_handle_t open_object( const struct object_attr *attr, const struct object_ops *ops, unsigned int access ) { obj_handle_t handle = 0; struct unicode_str name_left; struct object *obj; if ((obj = find_object( attr, &name_left ))) { if (name_left.len) /* not fully parsed */ set_error( STATUS_OBJECT_PATH_NOT_FOUND ); else if (ops && obj->ops != ops) set_error( STATUS_OBJECT_TYPE_MISMATCH ); else handle = alloc_handle( current->process, obj, access, attr->attributes & OBJ_INHERIT ); release_object( obj ); free_unicode_str( &name_left ); } return handle; } /******************************************************************************/ DECL_HANDLER(create_directory) { struct directory *dir; struct object_attr attr; struct unicode_str name; reply->handle = 0; GET_OBJECT_ATTR(&attr, req, &name) if ((dir = create_directory( &attr ))) { reply->handle = alloc_handle( current->process, dir, req->access, attr.attributes & OBJ_INHERIT ); release_object( dir ); } } DECL_HANDLER(open_directory) { struct object_attr attr; struct unicode_str name; GET_OBJECT_ATTR(&attr, req, &name) reply->handle = open_object( &attr, &directory_ops, req->access ); }