wine/server/symlink.c
Alexandre Julliard 0f273c17ff server: Added data_size_t type to represent sizes in the server protocol.
Make it an unsigned int to save some space on Win64, if we need to
transfer more than 4Gb over the server pipe something is seriously
wrong.
2006-07-26 11:11:03 +02:00

211 lines
6.6 KiB
C

/*
* Server-side symbolic link 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 "object.h"
#include "unicode.h"
struct symlink
{
struct object obj; /* object header */
WCHAR *target; /* target of the symlink */
data_size_t len; /* target len in bytes */
};
static void symlink_dump( struct object *obj, int verbose );
static unsigned int symlink_map_access( struct object *obj, unsigned int access );
static struct object *symlink_lookup_name( struct object *obj, struct unicode_str *name,
unsigned int attr );
static void symlink_destroy( struct object *obj );
static const struct object_ops symlink_ops =
{
sizeof(struct symlink), /* size */
symlink_dump, /* dump */
no_add_queue, /* add_queue */
NULL, /* remove_queue */
NULL, /* signaled */
NULL, /* satisfied */
no_signal, /* signal */
no_get_fd, /* get_fd */
symlink_map_access, /* map_access */
symlink_lookup_name, /* lookup_name */
no_close_handle, /* close_handle */
symlink_destroy /* destroy */
};
static void symlink_dump( struct object *obj, int verbose )
{
struct symlink *symlink = (struct symlink *)obj;
assert( obj->ops == &symlink_ops );
fprintf( stderr, "Symlink " );
dump_object_name( obj );
fprintf( stderr, " -> L\"" );
dump_strW( symlink->target, symlink->len / sizeof(WCHAR), stderr, "\"\"" );
fprintf( stderr, "\"\n" );
}
static struct object *symlink_lookup_name( struct object *obj, struct unicode_str *name,
unsigned int attr )
{
struct symlink *symlink = (struct symlink *)obj;
struct unicode_str target_str, name_left;
struct object *target;
assert( obj->ops == &symlink_ops );
if (attr & OBJ_OPENLINK) return NULL;
target_str.str = symlink->target;
target_str.len = symlink->len;
if ((target = find_object_dir( NULL, &target_str, attr, &name_left )))
{
if (name_left.len)
{
release_object( target );
target = NULL;
set_error( STATUS_OBJECT_PATH_NOT_FOUND );
}
}
return target;
}
static unsigned int symlink_map_access( struct object *obj, unsigned int access )
{
if (access & GENERIC_READ) access |= STANDARD_RIGHTS_READ | SYMBOLIC_LINK_QUERY;
if (access & GENERIC_WRITE) access |= STANDARD_RIGHTS_WRITE;
if (access & GENERIC_EXECUTE) access |= STANDARD_RIGHTS_EXECUTE;
if (access & GENERIC_ALL) access |= SYMBOLIC_LINK_ALL_ACCESS;
return access & ~(GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL);
}
static void symlink_destroy( struct object *obj )
{
struct symlink *symlink = (struct symlink *)obj;
assert( obj->ops == &symlink_ops );
free( symlink->target );
}
struct symlink *create_symlink( struct directory *root, const struct unicode_str *name,
unsigned int attr, const struct unicode_str *target )
{
struct symlink *symlink;
if (!target->len)
{
set_error( STATUS_INVALID_PARAMETER );
return NULL;
}
if ((symlink = create_named_object_dir( root, name, attr, &symlink_ops )) &&
(get_error() != STATUS_OBJECT_NAME_EXISTS))
{
if ((symlink->target = memdup( target->str, target->len )))
symlink->len = target->len;
else
{
release_object( symlink );
symlink = NULL;
}
}
return symlink;
}
/* create a symbolic link object */
DECL_HANDLER(create_symlink)
{
struct symlink *symlink;
struct unicode_str name, target;
struct directory *root = NULL;
if (req->name_len > get_req_data_size())
{
set_error( STATUS_INVALID_PARAMETER );
return;
}
name.str = get_req_data();
target.str = name.str + req->name_len / sizeof(WCHAR);
name.len = (target.str - name.str) * sizeof(WCHAR);
target.len = ((get_req_data_size() - name.len) / sizeof(WCHAR)) * sizeof(WCHAR);
if (req->rootdir && !(root = get_directory_obj( current->process, req->rootdir, 0 )))
return;
if ((symlink = create_symlink( root, &name, req->attributes, &target )))
{
reply->handle = alloc_handle( current->process, symlink, req->access, req->attributes );
release_object( symlink );
}
if (root) release_object( root );
}
/* open a symbolic link object */
DECL_HANDLER(open_symlink)
{
struct unicode_str name;
struct directory *root = NULL;
struct symlink *symlink;
get_req_unicode_str( &name );
if (req->rootdir && !(root = get_directory_obj( current->process, req->rootdir, 0 )))
return;
if ((symlink = open_object_dir( root, &name, req->attributes | OBJ_OPENLINK, &symlink_ops )))
{
reply->handle = alloc_handle( current->process, &symlink->obj, req->access, req->attributes );
release_object( symlink );
}
if (root) release_object( root );
}
/* query a symbolic link object */
DECL_HANDLER(query_symlink)
{
struct symlink *symlink;
symlink = (struct symlink *)get_handle_obj( current->process, req->handle,
SYMBOLIC_LINK_QUERY, &symlink_ops );
if (!symlink) return;
if (get_reply_max_size() < symlink->len)
set_error( STATUS_BUFFER_TOO_SMALL );
else
set_reply_data( symlink->target, symlink->len );
release_object( symlink );
}