ntdll: Use the context argument as working variable instead of making a copy in RtlUn...
[wine] / dlls / ntdll / om.c
1 /*
2  *      Object management functions
3  *
4  * Copyright 1999, 2000 Juergen Schmied
5  * Copyright 2005 Vitaliy Margolen
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21
22 #include "config.h"
23
24 #include <stdarg.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #ifdef HAVE_IO_H
28 # include <io.h>
29 #endif
30 #ifdef HAVE_UNISTD_H
31 # include <unistd.h>
32 #endif
33
34 #include "ntstatus.h"
35 #define WIN32_NO_STATUS
36 #include "wine/debug.h"
37 #include "windef.h"
38 #include "winternl.h"
39 #include "ntdll_misc.h"
40 #include "wine/server.h"
41
42 WINE_DEFAULT_DEBUG_CHANNEL(ntdll);
43
44
45 /*
46  *      Generic object functions
47  */
48
49 /******************************************************************************
50  * NtQueryObject [NTDLL.@]
51  * ZwQueryObject [NTDLL.@]
52  */
53 NTSTATUS WINAPI NtQueryObject(IN HANDLE handle,
54                               IN OBJECT_INFORMATION_CLASS info_class,
55                               OUT PVOID ptr, IN ULONG len, OUT PULONG used_len)
56 {
57     NTSTATUS status;
58
59     TRACE("(%p,0x%08x,%p,0x%08x,%p): stub\n",
60           handle, info_class, ptr, len, used_len);
61
62     if (used_len) *used_len = 0;
63
64     switch (info_class)
65     {
66     case ObjectBasicInformation:
67         {
68             POBJECT_BASIC_INFORMATION p = ptr;
69
70             if (len < sizeof(*p)) return STATUS_INVALID_BUFFER_SIZE;
71
72             SERVER_START_REQ( get_object_info )
73             {
74                 req->handle = wine_server_obj_handle( handle );
75                 status = wine_server_call( req );
76                 if (status == STATUS_SUCCESS)
77                 {
78                     memset( p, 0, sizeof(*p) );
79                     p->GrantedAccess = reply->access;
80                     p->PointerCount = reply->ref_count;
81                     p->HandleCount = 1; /* at least one */
82                     if (used_len) *used_len = sizeof(*p);
83                 }
84             }
85             SERVER_END_REQ;
86         }
87         break;
88     case ObjectNameInformation:
89         {
90             OBJECT_NAME_INFORMATION* p = ptr;
91             ANSI_STRING unix_name;
92
93             /* first try as a file object */
94
95             if (!(status = server_get_unix_name( handle, &unix_name )))
96             {
97                 UNICODE_STRING nt_name;
98
99                 if (!(status = wine_unix_to_nt_file_name( &unix_name, &nt_name )))
100                 {
101                     if (len < sizeof(*p))
102                         status = STATUS_INFO_LENGTH_MISMATCH;
103                     else if (len < sizeof(*p) + nt_name.MaximumLength)
104                         status = STATUS_BUFFER_OVERFLOW;
105                     else
106                     {
107                         p->Name.Buffer = (WCHAR *)(p + 1);
108                         p->Name.Length = nt_name.Length;
109                         p->Name.MaximumLength = nt_name.MaximumLength;
110                         memcpy( p->Name.Buffer, nt_name.Buffer, nt_name.MaximumLength );
111                     }
112                     if (used_len) *used_len = sizeof(*p) + nt_name.MaximumLength;
113                     RtlFreeUnicodeString( &nt_name );
114                 }
115                 RtlFreeAnsiString( &unix_name );
116                 break;
117             }
118             else if (status != STATUS_OBJECT_TYPE_MISMATCH) break;
119
120             /* not a file, treat as a generic object */
121
122             SERVER_START_REQ( get_object_info )
123             {
124                 req->handle = wine_server_obj_handle( handle );
125                 if (len > sizeof(*p)) wine_server_set_reply( req, p + 1, len - sizeof(*p) );
126                 status = wine_server_call( req );
127                 if (status == STATUS_SUCCESS)
128                 {
129                     if (!reply->total)  /* no name */
130                     {
131                         if (sizeof(*p) > len) status = STATUS_INFO_LENGTH_MISMATCH;
132                         else memset( p, 0, sizeof(*p) );
133                         if (used_len) *used_len = sizeof(*p);
134                     }
135                     else if (sizeof(*p) + reply->total + sizeof(WCHAR) > len)
136                     {
137                         if (used_len) *used_len = sizeof(*p) + reply->total + sizeof(WCHAR);
138                         status = STATUS_INFO_LENGTH_MISMATCH;
139                     }
140                     else
141                     {
142                         ULONG res = wine_server_reply_size( reply );
143                         p->Name.Buffer = (WCHAR *)(p + 1);
144                         p->Name.Length = res;
145                         p->Name.MaximumLength = res + sizeof(WCHAR);
146                         p->Name.Buffer[res / sizeof(WCHAR)] = 0;
147                         if (used_len) *used_len = sizeof(*p) + p->Name.MaximumLength;
148                     }
149                 }
150             }
151             SERVER_END_REQ;
152         }
153         break;
154     case ObjectDataInformation:
155         {
156             OBJECT_DATA_INFORMATION* p = ptr;
157
158             if (len < sizeof(*p)) return STATUS_INVALID_BUFFER_SIZE;
159
160             SERVER_START_REQ( set_handle_info )
161             {
162                 req->handle = wine_server_obj_handle( handle );
163                 req->flags  = 0;
164                 req->mask   = 0;
165                 status = wine_server_call( req );
166                 if (status == STATUS_SUCCESS)
167                 {
168                     p->InheritHandle = (reply->old_flags & HANDLE_FLAG_INHERIT) ? TRUE : FALSE;
169                     p->ProtectFromClose = (reply->old_flags & HANDLE_FLAG_PROTECT_FROM_CLOSE) ? TRUE : FALSE;
170                     if (used_len) *used_len = sizeof(*p);
171                 }
172             }
173             SERVER_END_REQ;
174         }
175         break;
176     default:
177         FIXME("Unsupported information class %u\n", info_class);
178         status = STATUS_NOT_IMPLEMENTED;
179         break;
180     }
181     return status;
182 }
183
184 /******************************************************************
185  *              NtSetInformationObject [NTDLL.@]
186  *              ZwSetInformationObject [NTDLL.@]
187  *
188  */
189 NTSTATUS WINAPI NtSetInformationObject(IN HANDLE handle,
190                                        IN OBJECT_INFORMATION_CLASS info_class,
191                                        IN PVOID ptr, IN ULONG len)
192 {
193     NTSTATUS status;
194
195     TRACE("(%p,0x%08x,%p,0x%08x): stub\n",
196           handle, info_class, ptr, len);
197
198     switch (info_class)
199     {
200     case ObjectDataInformation:
201         {
202             OBJECT_DATA_INFORMATION* p = ptr;
203
204             if (len < sizeof(*p)) return STATUS_INVALID_BUFFER_SIZE;
205
206             SERVER_START_REQ( set_handle_info )
207             {
208                 req->handle = wine_server_obj_handle( handle );
209                 req->flags  = 0;
210                 req->mask   = HANDLE_FLAG_INHERIT | HANDLE_FLAG_PROTECT_FROM_CLOSE;
211                 if (p->InheritHandle)    req->flags |= HANDLE_FLAG_INHERIT;
212                 if (p->ProtectFromClose) req->flags |= HANDLE_FLAG_PROTECT_FROM_CLOSE;
213                 status = wine_server_call( req );
214             }
215             SERVER_END_REQ;
216         }
217         break;
218     default:
219         FIXME("Unsupported information class %u\n", info_class);
220         status = STATUS_NOT_IMPLEMENTED;
221         break;
222     }
223     return status;
224 }
225
226 /******************************************************************************
227  *  NtQuerySecurityObject       [NTDLL.@]
228  *
229  * An ntdll analogue to GetKernelObjectSecurity().
230  *
231  */
232 NTSTATUS WINAPI
233 NtQuerySecurityObject(
234         IN HANDLE Object,
235         IN SECURITY_INFORMATION RequestedInformation,
236         OUT PSECURITY_DESCRIPTOR pSecurityDescriptor,
237         IN ULONG Length,
238         OUT PULONG ResultLength)
239 {
240     PISECURITY_DESCRIPTOR_RELATIVE psd = pSecurityDescriptor;
241     NTSTATUS status;
242     unsigned int buffer_size = 512;
243     BOOLEAN need_more_memory;
244
245     TRACE("(%p,0x%08x,%p,0x%08x,%p)\n",
246         Object, RequestedInformation, pSecurityDescriptor, Length, ResultLength);
247
248     do
249     {
250         char *buffer = RtlAllocateHeap(GetProcessHeap(), 0, buffer_size);
251         if (!buffer)
252             return STATUS_NO_MEMORY;
253
254         need_more_memory = FALSE;
255
256         SERVER_START_REQ( get_security_object )
257         {
258             req->handle = wine_server_obj_handle( Object );
259             req->security_info = RequestedInformation;
260             wine_server_set_reply( req, buffer, buffer_size );
261             status = wine_server_call( req );
262             if (status == STATUS_SUCCESS)
263             {
264                 struct security_descriptor *sd = (struct security_descriptor *)buffer;
265                 if (reply->sd_len)
266                 {
267                     *ResultLength = sizeof(SECURITY_DESCRIPTOR_RELATIVE) +
268                         sd->owner_len + sd->group_len + sd->sacl_len + sd->dacl_len;
269                     if (Length >= *ResultLength)
270                     {
271                         psd->Revision = SECURITY_DESCRIPTOR_REVISION;
272                         psd->Sbz1 = 0;
273                         psd->Control = sd->control | SE_SELF_RELATIVE;
274                         psd->Owner = sd->owner_len ? sizeof(SECURITY_DESCRIPTOR_RELATIVE) : 0;
275                         psd->Group = sd->group_len ? sizeof(SECURITY_DESCRIPTOR_RELATIVE) + sd->owner_len : 0;
276                         psd->Sacl = sd->sacl_len ? sizeof(SECURITY_DESCRIPTOR_RELATIVE) + sd->owner_len + sd->group_len : 0;
277                         psd->Dacl = sd->dacl_len ? sizeof(SECURITY_DESCRIPTOR_RELATIVE) + sd->owner_len + sd->group_len + sd->sacl_len : 0;
278                         /* owner, group, sacl and dacl are the same type as in the server
279                          * and in the same order so we copy the memory in one block */
280                         memcpy((char *)pSecurityDescriptor + sizeof(SECURITY_DESCRIPTOR_RELATIVE),
281                                buffer + sizeof(struct security_descriptor),
282                                sd->owner_len + sd->group_len + sd->sacl_len + sd->dacl_len);
283                     }
284                     else
285                         status = STATUS_BUFFER_TOO_SMALL;
286                 }
287                 else
288                 {
289                     *ResultLength = sizeof(SECURITY_DESCRIPTOR_RELATIVE);
290                     if (Length >= *ResultLength)
291                     {
292                         memset(psd, 0, sizeof(*psd));
293                         psd->Revision = SECURITY_DESCRIPTOR_REVISION;
294                         psd->Control = SE_SELF_RELATIVE;
295                     }
296                     else
297                         status = STATUS_BUFFER_TOO_SMALL;
298                 }
299             }
300             else if (status == STATUS_BUFFER_TOO_SMALL)
301             {
302                 buffer_size = reply->sd_len;
303                 need_more_memory = TRUE;
304             }
305         }
306         SERVER_END_REQ;
307         RtlFreeHeap(GetProcessHeap(), 0, buffer);
308     } while (need_more_memory);
309
310     return status;
311 }
312
313
314 /******************************************************************************
315  *  NtDuplicateObject           [NTDLL.@]
316  *  ZwDuplicateObject           [NTDLL.@]
317  */
318 NTSTATUS WINAPI NtDuplicateObject( HANDLE source_process, HANDLE source,
319                                    HANDLE dest_process, PHANDLE dest,
320                                    ACCESS_MASK access, ULONG attributes, ULONG options )
321 {
322     NTSTATUS ret;
323     SERVER_START_REQ( dup_handle )
324     {
325         req->src_process = wine_server_obj_handle( source_process );
326         req->src_handle  = wine_server_obj_handle( source );
327         req->dst_process = wine_server_obj_handle( dest_process );
328         req->access      = access;
329         req->attributes  = attributes;
330         req->options     = options;
331
332         if (!(ret = wine_server_call( req )))
333         {
334             if (dest) *dest = wine_server_ptr_handle( reply->handle );
335             if (reply->closed)
336             {
337                 if (reply->self)
338                 {
339                     int fd = server_remove_fd_from_cache( source );
340                     if (fd != -1) close( fd );
341                 }
342             }
343             else if (options & DUPLICATE_CLOSE_SOURCE)
344                 WARN( "failed to close handle %p in process %p\n", source, source_process );
345         }
346     }
347     SERVER_END_REQ;
348     return ret;
349 }
350
351 /* Everquest 2 / Pirates of the Burning Sea hooks NtClose, so we need a wrapper */
352 NTSTATUS close_handle( HANDLE handle )
353 {
354     NTSTATUS ret;
355     int fd = server_remove_fd_from_cache( handle );
356
357     SERVER_START_REQ( close_handle )
358     {
359         req->handle = wine_server_obj_handle( handle );
360         ret = wine_server_call( req );
361     }
362     SERVER_END_REQ;
363     if (fd != -1) close( fd );
364     return ret;
365 }
366
367 /**************************************************************************
368  *                 NtClose                              [NTDLL.@]
369  *
370  * Close a handle reference to an object.
371  * 
372  * PARAMS
373  *  Handle [I] handle to close
374  *
375  * RETURNS
376  *  Success: ERROR_SUCCESS.
377  *  Failure: An NTSTATUS error code.
378  */
379 NTSTATUS WINAPI NtClose( HANDLE Handle )
380 {
381     return close_handle( Handle );
382 }
383
384 /*
385  *      Directory functions
386  */
387
388 /**************************************************************************
389  * NtOpenDirectoryObject [NTDLL.@]
390  * ZwOpenDirectoryObject [NTDLL.@]
391  *
392  * Open a namespace directory object.
393  * 
394  * PARAMS
395  *  DirectoryHandle  [O] Destination for the new directory handle
396  *  DesiredAccess    [I] Desired access to the directory
397  *  ObjectAttributes [I] Structure describing the directory
398  *
399  * RETURNS
400  *  Success: ERROR_SUCCESS.
401  *  Failure: An NTSTATUS error code.
402  */
403 NTSTATUS WINAPI NtOpenDirectoryObject(PHANDLE DirectoryHandle, ACCESS_MASK DesiredAccess,
404                                       POBJECT_ATTRIBUTES ObjectAttributes)
405 {
406     NTSTATUS ret;
407
408     if (!DirectoryHandle) return STATUS_ACCESS_VIOLATION;
409     if (!ObjectAttributes) return STATUS_INVALID_PARAMETER;
410     TRACE("(%p,0x%08x,%s)\n", DirectoryHandle, DesiredAccess, debugstr_ObjectAttributes(ObjectAttributes));
411     /* Have to test it here because server won't know difference between
412      * ObjectName == NULL and ObjectName == "" */
413     if (!ObjectAttributes->ObjectName)
414     {
415         if (ObjectAttributes->RootDirectory)
416             return STATUS_OBJECT_NAME_INVALID;
417         else
418             return STATUS_OBJECT_PATH_SYNTAX_BAD;
419     }
420
421     SERVER_START_REQ(open_directory)
422     {
423         req->access = DesiredAccess;
424         req->attributes = ObjectAttributes->Attributes;
425         req->rootdir = wine_server_obj_handle( ObjectAttributes->RootDirectory );
426         if (ObjectAttributes->ObjectName)
427             wine_server_add_data(req, ObjectAttributes->ObjectName->Buffer,
428                                  ObjectAttributes->ObjectName->Length);
429         ret = wine_server_call( req );
430         *DirectoryHandle = wine_server_ptr_handle( reply->handle );
431     }
432     SERVER_END_REQ;
433     return ret;
434 }
435
436 /******************************************************************************
437  *  NtCreateDirectoryObject     [NTDLL.@]
438  *  ZwCreateDirectoryObject     [NTDLL.@]
439  *
440  * Create a namespace directory object.
441  * 
442  * PARAMS
443  *  DirectoryHandle  [O] Destination for the new directory handle
444  *  DesiredAccess    [I] Desired access to the directory
445  *  ObjectAttributes [I] Structure describing the directory
446  *
447  * RETURNS
448  *  Success: ERROR_SUCCESS.
449  *  Failure: An NTSTATUS error code.
450  */
451 NTSTATUS WINAPI NtCreateDirectoryObject(PHANDLE DirectoryHandle, ACCESS_MASK DesiredAccess,
452                                         POBJECT_ATTRIBUTES ObjectAttributes)
453 {
454     NTSTATUS ret;
455
456     if (!DirectoryHandle) return STATUS_ACCESS_VIOLATION;
457     TRACE("(%p,0x%08x,%s)\n", DirectoryHandle, DesiredAccess, debugstr_ObjectAttributes(ObjectAttributes));
458
459     SERVER_START_REQ(create_directory)
460     {
461         req->access = DesiredAccess;
462         req->attributes = ObjectAttributes ? ObjectAttributes->Attributes : 0;
463         req->rootdir = wine_server_obj_handle( ObjectAttributes ? ObjectAttributes->RootDirectory : 0 );
464         if (ObjectAttributes && ObjectAttributes->ObjectName)
465             wine_server_add_data(req, ObjectAttributes->ObjectName->Buffer,
466                                  ObjectAttributes->ObjectName->Length);
467         ret = wine_server_call( req );
468         *DirectoryHandle = wine_server_ptr_handle( reply->handle );
469     }
470     SERVER_END_REQ;
471     return ret;
472 }
473
474 /******************************************************************************
475  * NtQueryDirectoryObject [NTDLL.@]
476  * ZwQueryDirectoryObject [NTDLL.@]
477  *
478  * Read information from a namespace directory.
479  * 
480  * PARAMS
481  *  handle        [I]   Handle to a directory object
482  *  buffer        [O]   Buffer to hold the read data
483  *  size          [I]   Size of the buffer in bytes
484  *  single_entry  [I]   If TRUE, return a single entry, if FALSE, return as many as fit in the buffer
485  *  restart       [I]   If TRUE, start scanning from the start, if FALSE, scan from Context
486  *  context       [I/O] Indicates what point of the directory the scan is at
487  *  ret_size      [O]   Caller supplied storage for the number of bytes written (or NULL)
488  *
489  * RETURNS
490  *  Success: ERROR_SUCCESS.
491  *  Failure: An NTSTATUS error code.
492  */
493 NTSTATUS WINAPI NtQueryDirectoryObject(HANDLE handle, PDIRECTORY_BASIC_INFORMATION buffer,
494                                        ULONG size, BOOLEAN single_entry, BOOLEAN restart,
495                                        PULONG context, PULONG ret_size)
496 {
497     NTSTATUS ret;
498
499     if (restart) *context = 0;
500
501     if (single_entry)
502     {
503         if (size <= sizeof(*buffer) + 2*sizeof(WCHAR)) return STATUS_BUFFER_OVERFLOW;
504
505         SERVER_START_REQ( get_directory_entry )
506         {
507             req->handle = wine_server_obj_handle( handle );
508             req->index = *context;
509             wine_server_set_reply( req, buffer + 1, size - sizeof(*buffer) - 2*sizeof(WCHAR) );
510             if (!(ret = wine_server_call( req )))
511             {
512                 buffer->ObjectName.Buffer = (WCHAR *)(buffer + 1);
513                 buffer->ObjectName.Length = reply->name_len;
514                 buffer->ObjectName.MaximumLength = reply->name_len + sizeof(WCHAR);
515                 buffer->ObjectTypeName.Buffer = (WCHAR *)(buffer + 1) + reply->name_len/sizeof(WCHAR) + 1;
516                 buffer->ObjectTypeName.Length = wine_server_reply_size( reply ) - reply->name_len;
517                 buffer->ObjectTypeName.MaximumLength = buffer->ObjectTypeName.Length + sizeof(WCHAR);
518                 /* make room for the terminating null */
519                 memmove( buffer->ObjectTypeName.Buffer, buffer->ObjectTypeName.Buffer - 1,
520                          buffer->ObjectTypeName.Length );
521                 buffer->ObjectName.Buffer[buffer->ObjectName.Length/sizeof(WCHAR)] = 0;
522                 buffer->ObjectTypeName.Buffer[buffer->ObjectTypeName.Length/sizeof(WCHAR)] = 0;
523                 (*context)++;
524             }
525         }
526         SERVER_END_REQ;
527         if (ret_size)
528             *ret_size = buffer->ObjectName.MaximumLength + buffer->ObjectTypeName.MaximumLength + sizeof(*buffer);
529     }
530     else
531     {
532         FIXME("multiple entries not implemented\n");
533         ret = STATUS_NOT_IMPLEMENTED;
534     }
535
536     return ret;
537 }
538
539 /*
540  *      Link objects
541  */
542
543 /******************************************************************************
544  *  NtOpenSymbolicLinkObject    [NTDLL.@]
545  *  ZwOpenSymbolicLinkObject    [NTDLL.@]
546  *
547  * Open a namespace symbolic link object.
548  * 
549  * PARAMS
550  *  LinkHandle       [O] Destination for the new symbolic link handle
551  *  DesiredAccess    [I] Desired access to the symbolic link
552  *  ObjectAttributes [I] Structure describing the symbolic link
553  *
554  * RETURNS
555  *  Success: ERROR_SUCCESS.
556  *  Failure: An NTSTATUS error code.
557  */
558 NTSTATUS WINAPI NtOpenSymbolicLinkObject(OUT PHANDLE LinkHandle, IN ACCESS_MASK DesiredAccess,
559                                          IN POBJECT_ATTRIBUTES ObjectAttributes)
560 {
561     NTSTATUS ret;
562     TRACE("(%p,0x%08x,%s)\n",LinkHandle, DesiredAccess, debugstr_ObjectAttributes(ObjectAttributes));
563
564     if (!LinkHandle) return STATUS_ACCESS_VIOLATION;
565     if (!ObjectAttributes) return STATUS_INVALID_PARAMETER;
566     /* Have to test it here because server won't know difference between
567      * ObjectName == NULL and ObjectName == "" */
568     if (!ObjectAttributes->ObjectName)
569     {
570         if (ObjectAttributes->RootDirectory)
571             return STATUS_OBJECT_NAME_INVALID;
572         else
573             return STATUS_OBJECT_PATH_SYNTAX_BAD;
574     }
575
576     SERVER_START_REQ(open_symlink)
577     {
578         req->access = DesiredAccess;
579         req->attributes = ObjectAttributes->Attributes;
580         req->rootdir = wine_server_obj_handle( ObjectAttributes->RootDirectory );
581         if (ObjectAttributes->ObjectName)
582             wine_server_add_data(req, ObjectAttributes->ObjectName->Buffer,
583                                  ObjectAttributes->ObjectName->Length);
584         ret = wine_server_call( req );
585         *LinkHandle = wine_server_ptr_handle( reply->handle );
586     }
587     SERVER_END_REQ;
588     return ret;
589 }
590
591 /******************************************************************************
592  *  NtCreateSymbolicLinkObject  [NTDLL.@]
593  *  ZwCreateSymbolicLinkObject  [NTDLL.@]
594  *
595  * Open a namespace symbolic link object.
596  * 
597  * PARAMS
598  *  SymbolicLinkHandle [O] Destination for the new symbolic link handle
599  *  DesiredAccess      [I] Desired access to the symbolic link
600  *  ObjectAttributes   [I] Structure describing the symbolic link
601  *  TargetName         [I] Name of the target symbolic link points to
602  *
603  * RETURNS
604  *  Success: ERROR_SUCCESS.
605  *  Failure: An NTSTATUS error code.
606  */
607 NTSTATUS WINAPI NtCreateSymbolicLinkObject(OUT PHANDLE SymbolicLinkHandle,IN ACCESS_MASK DesiredAccess,
608                                            IN POBJECT_ATTRIBUTES ObjectAttributes,
609                                            IN PUNICODE_STRING TargetName)
610 {
611     NTSTATUS ret;
612
613     if (!SymbolicLinkHandle || !TargetName) return STATUS_ACCESS_VIOLATION;
614     if (!TargetName->Buffer) return STATUS_INVALID_PARAMETER;
615
616     TRACE("(%p,0x%08x,%s -> %s)\n", SymbolicLinkHandle, DesiredAccess,
617           debugstr_ObjectAttributes(ObjectAttributes), debugstr_us(TargetName));
618
619     SERVER_START_REQ(create_symlink)
620     {
621         req->access = DesiredAccess;
622         req->attributes = ObjectAttributes ? ObjectAttributes->Attributes : 0;
623         req->rootdir = wine_server_obj_handle( ObjectAttributes ? ObjectAttributes->RootDirectory : 0 );
624         if (ObjectAttributes && ObjectAttributes->ObjectName)
625         {
626             req->name_len = ObjectAttributes->ObjectName->Length;
627             wine_server_add_data(req, ObjectAttributes->ObjectName->Buffer,
628                                  ObjectAttributes->ObjectName->Length);
629         }
630         else
631             req->name_len = 0;
632         wine_server_add_data(req, TargetName->Buffer, TargetName->Length);
633         ret = wine_server_call( req );
634         *SymbolicLinkHandle = wine_server_ptr_handle( reply->handle );
635     }
636     SERVER_END_REQ;
637     return ret;
638 }
639
640 /******************************************************************************
641  *  NtQuerySymbolicLinkObject   [NTDLL.@]
642  *  ZwQuerySymbolicLinkObject   [NTDLL.@]
643  *
644  * Query a namespace symbolic link object target name.
645  *
646  * PARAMS
647  *  handle     [I] Handle to a symbolic link object
648  *  target     [O] Destination for the symbolic link target
649  *  length     [O] Size of returned data
650  *
651  * RETURNS
652  *  Success: ERROR_SUCCESS.
653  *  Failure: An NTSTATUS error code.
654  */
655 NTSTATUS WINAPI NtQuerySymbolicLinkObject( HANDLE handle, PUNICODE_STRING target, PULONG length )
656 {
657     NTSTATUS ret;
658
659     TRACE("(%p,%p,%p)\n", handle, target, length );
660
661     if (!target) return STATUS_ACCESS_VIOLATION;
662
663     SERVER_START_REQ(query_symlink)
664     {
665         req->handle = wine_server_obj_handle( handle );
666         if (target->MaximumLength >= sizeof(WCHAR))
667             wine_server_set_reply( req, target->Buffer, target->MaximumLength - sizeof(WCHAR) );
668         if (!(ret = wine_server_call( req )))
669         {
670             target->Length = wine_server_reply_size(reply);
671             target->Buffer[target->Length / sizeof(WCHAR)] = 0;
672             if (length) *length = reply->total + sizeof(WCHAR);
673         }
674         else if (length && ret == STATUS_BUFFER_TOO_SMALL) *length = reply->total + sizeof(WCHAR);
675     }
676     SERVER_END_REQ;
677     return ret;
678 }
679
680 /******************************************************************************
681  *  NtAllocateUuids   [NTDLL.@]
682  */
683 NTSTATUS WINAPI NtAllocateUuids(
684         PULARGE_INTEGER Time,
685         PULONG Range,
686         PULONG Sequence)
687 {
688         FIXME("(%p,%p,%p), stub.\n", Time, Range, Sequence);
689         return 0;
690 }
691
692 /**************************************************************************
693  *  NtMakeTemporaryObject       [NTDLL.@]
694  *  ZwMakeTemporaryObject       [NTDLL.@]
695  *
696  * Make a permanent object temporary.
697  *
698  * PARAMS
699  *  Handle [I] handle to permanent object
700  *
701  * RETURNS
702  *  Success: STATUS_SUCCESS.
703  *  Failure: An NTSTATUS error code.
704  */
705 NTSTATUS WINAPI NtMakeTemporaryObject( HANDLE Handle )
706 {
707     FIXME("(%p), stub.\n", Handle);
708     return STATUS_SUCCESS;
709 }