| /* | |
| * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA | |
| * | |
| */ | |
| #include "config.h" | |
| #include "wine/port.h" | |
| #include <assert.h> | |
| #include <stdarg.h> | |
| #include <stdlib.h> | |
| #include <stdio.h> | |
| #include <sys/types.h> | |
| #include "ntstatus.h" | |
| #define WIN32_NO_STATUS | |
| #include "winternl.h" | |
| #include "ddk/wdm.h" | |
| #include "handle.h" | |
| #include "request.h" | |
| #include "process.h" | |
| #include "object.h" | |
| #include "unicode.h" | |
| #define HASH_SIZE 7 /* default hash size */ | |
| struct directory | |
| { | |
| struct object obj; /* object header */ | |
| struct namespace *entries; /* directory's name space */ | |
| }; | |
| static void directory_dump( struct object *obj, int verbose ); | |
| static unsigned int directory_map_access( struct object *obj, unsigned int access ); | |
| static struct object *directory_lookup_name( struct object *obj, struct unicode_str *name, | |
| unsigned int attr ); | |
| static void directory_destroy( struct object *obj ); | |
| 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_map_access, /* map_access */ | |
| directory_lookup_name, /* lookup_name */ | |
| no_open_file, /* open_file */ | |
| no_close_handle, /* close_handle */ | |
| directory_destroy /* destroy */ | |
| }; | |
| static struct directory *root_directory; | |
| static void directory_dump( struct object *obj, int verbose ) | |
| { | |
| assert( obj->ops == &directory_ops ); | |
| fputs( "Directory ", stderr ); | |
| dump_object_name( obj ); | |
| fputc( '\n', stderr ); | |
| } | |
| static struct object *directory_lookup_name( struct object *obj, struct unicode_str *name, | |
| unsigned int attr ) | |
| { | |
| struct directory *dir = (struct directory *)obj; | |
| struct object *found; | |
| struct unicode_str tmp; | |
| const WCHAR *p; | |
| assert( obj->ops == &directory_ops ); | |
| if (!(p = memchrW( name->str, '\\', name->len / sizeof(WCHAR) ))) | |
| /* Last element in the path name */ | |
| tmp.len = name->len; | |
| else | |
| tmp.len = (p - name->str) * sizeof(WCHAR); | |
| tmp.str = name->str; | |
| if ((found = find_object( dir->entries, &tmp, attr ))) | |
| { | |
| /* Skip trailing \\ */ | |
| if (p) | |
| { | |
| p++; | |
| tmp.len += sizeof(WCHAR); | |
| } | |
| /* Move to the next element*/ | |
| name->str = p; | |
| name->len -= tmp.len; | |
| return found; | |
| } | |
| if (name->str) | |
| { | |
| if (tmp.len == 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; | |
| } | |
| static unsigned int directory_map_access( struct object *obj, unsigned int access ) | |
| { | |
| if (access & GENERIC_READ) access |= FILE_GENERIC_READ; | |
| if (access & GENERIC_WRITE) access |= FILE_GENERIC_WRITE; | |
| if (access & GENERIC_EXECUTE) access |= FILE_GENERIC_EXECUTE; | |
| if (access & GENERIC_ALL) access |= FILE_ALL_ACCESS; | |
| return access & ~(GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL); | |
| } | |
| static void directory_destroy( struct object *obj ) | |
| { | |
| struct directory *dir = (struct directory *)obj; | |
| assert( obj->ops == &directory_ops ); | |
| free( dir->entries ); | |
| } | |
| static struct directory *create_directory( struct directory *root, const struct unicode_str *name, | |
| unsigned int attr, unsigned int hash_size ) | |
| { | |
| struct directory *dir; | |
| if ((dir = create_named_object_dir( root, name, attr, &directory_ops )) && | |
| get_error() != STATUS_OBJECT_NAME_EXISTS) | |
| { | |
| if (!(dir->entries = create_namespace( hash_size ))) | |
| { | |
| release_object( dir ); | |
| dir = NULL; | |
| } | |
| } | |
| return dir; | |
| } | |
| struct directory *get_directory_obj( struct process *process, obj_handle_t handle, unsigned int access ) | |
| { | |
| return (struct directory *)get_handle_obj( process, handle, access, &directory_ops ); | |
| } | |
| /****************************************************************************** | |
| * Find an object by its name in a given root object | |
| * | |
| * PARAMS | |
| * root [I] directory to start search from or NULL to start from \\ | |
| * name [I] object name to search for | |
| * attr [I] OBJECT_ATTRIBUTES.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_dir( struct directory *root, const struct unicode_str *name, | |
| unsigned int attr, struct unicode_str *name_left ) | |
| { | |
| struct object *obj, *parent; | |
| struct unicode_str name_tmp; | |
| if (name) name_tmp = *name; | |
| else name_tmp.len = 0; | |
| /* Arguments check: | |
| * - Either rootdir or name have to be specified | |
| * - If root is specified path shouldn't start with backslash */ | |
| if (root) | |
| { | |
| if (name_tmp.len && name_tmp.str[0] == '\\') | |
| { | |
| set_error( STATUS_OBJECT_PATH_SYNTAX_BAD ); | |
| return NULL; | |
| } | |
| parent = grab_object( root ); | |
| } | |
| else | |
| { | |
| if (!name_tmp.len || name_tmp.str[0] != '\\') | |
| { | |
| set_error( STATUS_OBJECT_PATH_SYNTAX_BAD ); | |
| return NULL; | |
| } | |
| parent = grab_object( &root_directory->obj ); | |
| /* skip leading backslash */ | |
| name_tmp.str++; | |
| name_tmp.len -= sizeof(WCHAR); | |
| } | |
| /* Special case for opening RootDirectory */ | |
| if (!name_tmp.len) goto done; | |
| while ((obj = parent->ops->lookup_name( parent, &name_tmp, attr ))) | |
| { | |
| /* move to the next element */ | |
| release_object ( parent ); | |
| parent = obj; | |
| } | |
| if (get_error()) | |
| { | |
| release_object( parent ); | |
| return NULL; | |
| } | |
| done: | |
| if (name_left) *name_left = name_tmp; | |
| return parent; | |
| } | |
| /* create a named (if name is present) or unnamed object. */ | |
| void *create_named_object_dir( struct directory *root, const struct unicode_str *name, | |
| unsigned int attributes, const struct object_ops *ops ) | |
| { | |
| struct object *obj, *new_obj = NULL; | |
| struct unicode_str new_name; | |
| if (!name || !name->len) return alloc_object( ops ); | |
| if (!(obj = find_object_dir( root, name, attributes, &new_name ))) return NULL; | |
| if (!new_name.len) | |
| { | |
| if (attributes & OBJ_OPENIF && obj->ops == ops) | |
| set_error( STATUS_OBJECT_NAME_EXISTS ); | |
| else | |
| { | |
| release_object( obj ); | |
| obj = NULL; | |
| if (attributes & OBJ_OPENIF) | |
| set_error( STATUS_OBJECT_TYPE_MISMATCH ); | |
| else | |
| set_error( STATUS_OBJECT_NAME_COLLISION ); | |
| } | |
| return obj; | |
| } | |
| /* ATM we can't insert objects into anything else but directories */ | |
| if (obj->ops != &directory_ops) | |
| set_error( STATUS_OBJECT_TYPE_MISMATCH ); | |
| else | |
| { | |
| struct directory *dir = (struct directory *)obj; | |
| if ((new_obj = create_object( dir->entries, ops, &new_name, &dir->obj ))) | |
| clear_error(); | |
| } | |
| release_object( obj ); | |
| return new_obj; | |
| } | |
| /* open a new handle to an existing object */ | |
| void *open_object_dir( struct directory *root, const struct unicode_str *name, | |
| unsigned int attr, const struct object_ops *ops ) | |
| { | |
| struct unicode_str name_left; | |
| struct object *obj; | |
| if ((obj = find_object_dir( root, name, attr, &name_left ))) | |
| { | |
| if (name_left.len) /* not fully parsed */ | |
| set_error( STATUS_OBJECT_NAME_NOT_FOUND ); | |
| else if (ops && obj->ops != ops) | |
| set_error( STATUS_OBJECT_TYPE_MISMATCH ); | |
| else | |
| return obj; | |
| release_object( obj ); | |
| } | |
| return NULL; | |
| } | |
| /* Global initialization */ | |
| void init_directories(void) | |
| { | |
| /* Directories */ | |
| static const WCHAR dir_globalW[] = {'\\','?','?'}; | |
| static const WCHAR dir_driverW[] = {'D','r','i','v','e','r'}; | |
| static const WCHAR dir_deviceW[] = {'D','e','v','i','c','e'}; | |
| static const WCHAR dir_basenamedW[] = {'\\','B','a','s','e','N','a','m','e','d','O','b','j','e','c','t','s'}; | |
| static const WCHAR dir_named_pipeW[] = {'\\','D','e','v','i','c','e','\\','N','a','m','e','d','P','i','p','e'}; | |
| static const WCHAR dir_mailslotW[] = {'\\','D','e','v','i','c','e','\\','M','a','i','l','S','l','o','t'}; | |
| static const struct unicode_str dir_global_str = {dir_globalW, sizeof(dir_globalW)}; | |
| static const struct unicode_str dir_driver_str = {dir_driverW, sizeof(dir_driverW)}; | |
| static const struct unicode_str dir_device_str = {dir_deviceW, sizeof(dir_deviceW)}; | |
| static const struct unicode_str dir_basenamed_str = {dir_basenamedW, sizeof(dir_basenamedW)}; | |
| static const struct unicode_str dir_named_pipe_str = {dir_named_pipeW, sizeof(dir_named_pipeW)}; | |
| static const struct unicode_str dir_mailslot_str = {dir_mailslotW, sizeof(dir_mailslotW)}; | |
| /* symlinks */ | |
| static const WCHAR link_dosdevW[] = {'D','o','s','D','e','v','i','c','e','s'}; | |
| static const WCHAR link_globalW[] = {'G','l','o','b','a','l'}; | |
| static const WCHAR link_localW[] = {'L','o','c','a','l'}; | |
| static const WCHAR link_pipeW[] = {'P','I','P','E'}; | |
| static const WCHAR link_mailslotW[] = {'M','A','I','L','S','L','O','T'}; | |
| static const struct unicode_str link_dosdev_str = {link_dosdevW, sizeof(link_dosdevW)}; | |
| static const struct unicode_str link_global_str = {link_globalW, sizeof(link_globalW)}; | |
| static const struct unicode_str link_local_str = {link_localW, sizeof(link_localW)}; | |
| static const struct unicode_str link_pipe_str = {link_pipeW, sizeof(link_pipeW)}; | |
| static const struct unicode_str link_mailslot_str = {link_mailslotW, sizeof(link_mailslotW)}; | |
| /* devices */ | |
| static const WCHAR named_pipeW[] = {'N','a','m','e','d','P','i','p','e'}; | |
| static const WCHAR mailslotW[] = {'M','a','i','l','S','l','o','t'}; | |
| static const struct unicode_str named_pipe_str = {named_pipeW, sizeof(named_pipeW)}; | |
| static const struct unicode_str mailslot_str = {mailslotW, sizeof(mailslotW)}; | |
| struct directory *dir_driver, *dir_device, *dir_global, *dir_basenamed; | |
| struct symlink *link_dosdev, *link_global1, *link_global2, *link_local, *link_pipe, *link_mailslot; | |
| root_directory = create_directory( NULL, NULL, 0, HASH_SIZE ); | |
| dir_driver = create_directory( root_directory, &dir_driver_str, 0, HASH_SIZE ); | |
| dir_device = create_directory( root_directory, &dir_device_str, 0, HASH_SIZE ); | |
| make_object_static( &root_directory->obj ); | |
| make_object_static( &dir_driver->obj ); | |
| dir_global = create_directory( NULL, &dir_global_str, 0, HASH_SIZE ); | |
| /* use a larger hash table for this one since it can contain a lot of objects */ | |
| dir_basenamed = create_directory( NULL, &dir_basenamed_str, 0, 37 ); | |
| /* devices */ | |
| create_named_pipe_device( dir_device, &named_pipe_str ); | |
| create_mailslot_device( dir_device, &mailslot_str ); | |
| /* symlinks */ | |
| link_dosdev = create_symlink( root_directory, &link_dosdev_str, 0, &dir_global_str ); | |
| link_global1 = create_symlink( dir_global, &link_global_str, 0, &dir_global_str ); | |
| link_global2 = create_symlink( dir_basenamed, &link_global_str, 0, &dir_basenamed_str ); | |
| link_local = create_symlink( dir_basenamed, &link_local_str, 0, &dir_basenamed_str ); | |
| link_pipe = create_symlink( dir_global, &link_pipe_str, 0, &dir_named_pipe_str ); | |
| link_mailslot = create_symlink( dir_global, &link_mailslot_str, 0, &dir_mailslot_str ); | |
| make_object_static( (struct object *)link_dosdev ); | |
| make_object_static( (struct object *)link_global1 ); | |
| make_object_static( (struct object *)link_global2 ); | |
| make_object_static( (struct object *)link_local ); | |
| make_object_static( (struct object *)link_pipe ); | |
| make_object_static( (struct object *)link_mailslot ); | |
| /* the symlinks or devices hold references so we can release these */ | |
| release_object( dir_global ); | |
| release_object( dir_device ); | |
| release_object( dir_basenamed ); | |
| } | |
| /* create a directory object */ | |
| DECL_HANDLER(create_directory) | |
| { | |
| struct unicode_str name; | |
| struct directory *dir, *root = NULL; | |
| reply->handle = 0; | |
| get_req_unicode_str( &name ); | |
| if (req->rootdir && !(root = get_directory_obj( current->process, req->rootdir, 0 ))) | |
| return; | |
| if ((dir = create_directory( root, &name, req->attributes, HASH_SIZE ))) | |
| { | |
| reply->handle = alloc_handle( current->process, dir, req->access, req->attributes ); | |
| release_object( dir ); | |
| } | |
| if (root) release_object( root ); | |
| } | |
| /* open a directory object */ | |
| DECL_HANDLER(open_directory) | |
| { | |
| struct unicode_str name; | |
| struct directory *dir, *root = NULL; | |
| get_req_unicode_str( &name ); | |
| if (req->rootdir && !(root = get_directory_obj( current->process, req->rootdir, 0 ))) | |
| return; | |
| if ((dir = open_object_dir( root, &name, req->attributes, &directory_ops ))) | |
| { | |
| reply->handle = alloc_handle( current->process, &dir->obj, req->access, req->attributes ); | |
| release_object( dir ); | |
| } | |
| if (root) release_object( root ); | |
| } |