ntdll: Document RtlSetThreadErrorMode and RtlGetThreadErrorMode.
[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                 NTSTATUS status;
99
100                 if (!(status = wine_unix_to_nt_file_name( &unix_name, &nt_name )))
101                 {
102                     if (sizeof(*p) + nt_name.MaximumLength <= len)
103                     {
104                         p->Name.Buffer = (WCHAR *)(p + 1);
105                         p->Name.Length = nt_name.Length;
106                         p->Name.MaximumLength = nt_name.MaximumLength;
107                         memcpy( p->Name.Buffer, nt_name.Buffer, nt_name.MaximumLength );
108                     }
109                     else status = STATUS_INFO_LENGTH_MISMATCH;
110                     if (used_len) *used_len = sizeof(*p) + nt_name.MaximumLength;
111                     RtlFreeUnicodeString( &nt_name );
112                 }
113                 RtlFreeAnsiString( &unix_name );
114                 break;
115             }
116             else if (status != STATUS_OBJECT_TYPE_MISMATCH) break;
117
118             /* not a file, treat as a generic object */
119
120             SERVER_START_REQ( get_object_info )
121             {
122                 req->handle = wine_server_obj_handle( handle );
123                 if (len > sizeof(*p)) wine_server_set_reply( req, p + 1, len - sizeof(*p) );
124                 status = wine_server_call( req );
125                 if (status == STATUS_SUCCESS)
126                 {
127                     if (!reply->total)  /* no name */
128                     {
129                         if (sizeof(*p) > len) status = STATUS_INFO_LENGTH_MISMATCH;
130                         else memset( p, 0, sizeof(*p) );
131                         if (used_len) *used_len = sizeof(*p);
132                     }
133                     else if (sizeof(*p) + reply->total + sizeof(WCHAR) > len)
134                     {
135                         if (used_len) *used_len = sizeof(*p) + reply->total + sizeof(WCHAR);
136                         status = STATUS_INFO_LENGTH_MISMATCH;
137                     }
138                     else
139                     {
140                         ULONG res = wine_server_reply_size( reply );
141                         p->Name.Buffer = (WCHAR *)(p + 1);
142                         p->Name.Length = res;
143                         p->Name.MaximumLength = res + sizeof(WCHAR);
144                         p->Name.Buffer[res / sizeof(WCHAR)] = 0;
145                         if (used_len) *used_len = sizeof(*p) + p->Name.MaximumLength;
146                     }
147                 }
148             }
149             SERVER_END_REQ;
150         }
151         break;
152     case ObjectDataInformation:
153         {
154             OBJECT_DATA_INFORMATION* p = ptr;
155
156             if (len < sizeof(*p)) return STATUS_INVALID_BUFFER_SIZE;
157
158             SERVER_START_REQ( set_handle_info )
159             {
160                 req->handle = wine_server_obj_handle( handle );
161                 req->flags  = 0;
162                 req->mask   = 0;
163                 status = wine_server_call( req );
164                 if (status == STATUS_SUCCESS)
165                 {
166                     p->InheritHandle = (reply->old_flags & HANDLE_FLAG_INHERIT) ? TRUE : FALSE;
167                     p->ProtectFromClose = (reply->old_flags & HANDLE_FLAG_PROTECT_FROM_CLOSE) ? TRUE : FALSE;
168                     if (used_len) *used_len = sizeof(*p);
169                 }
170             }
171             SERVER_END_REQ;
172         }
173         break;
174     default:
175         FIXME("Unsupported information class %u\n", info_class);
176         status = STATUS_NOT_IMPLEMENTED;
177         break;
178     }
179     return status;
180 }
181
182 /******************************************************************
183  *              NtSetInformationObject [NTDLL.@]
184  *              ZwSetInformationObject [NTDLL.@]
185  *
186  */
187 NTSTATUS WINAPI NtSetInformationObject(IN HANDLE handle,
188                                        IN OBJECT_INFORMATION_CLASS info_class,
189                                        IN PVOID ptr, IN ULONG len)
190 {
191     NTSTATUS status;
192
193     TRACE("(%p,0x%08x,%p,0x%08x): stub\n",
194           handle, info_class, ptr, len);
195
196     switch (info_class)
197     {
198     case ObjectDataInformation:
199         {
200             OBJECT_DATA_INFORMATION* p = ptr;
201
202             if (len < sizeof(*p)) return STATUS_INVALID_BUFFER_SIZE;
203
204             SERVER_START_REQ( set_handle_info )
205             {
206                 req->handle = wine_server_obj_handle( handle );
207                 req->flags  = 0;
208                 req->mask   = HANDLE_FLAG_INHERIT | HANDLE_FLAG_PROTECT_FROM_CLOSE;
209                 if (p->InheritHandle)    req->flags |= HANDLE_FLAG_INHERIT;
210                 if (p->ProtectFromClose) req->flags |= HANDLE_FLAG_PROTECT_FROM_CLOSE;
211                 status = wine_server_call( req );
212             }
213             SERVER_END_REQ;
214         }
215         break;
216     default:
217         FIXME("Unsupported information class %u\n", info_class);
218         status = STATUS_NOT_IMPLEMENTED;
219         break;
220     }
221     return status;
222 }
223
224 /******************************************************************************
225  *  NtQuerySecurityObject       [NTDLL.@]
226  *
227  * An ntdll analogue to GetKernelObjectSecurity().
228  *
229  */
230 NTSTATUS WINAPI
231 NtQuerySecurityObject(
232         IN HANDLE Object,
233         IN SECURITY_INFORMATION RequestedInformation,
234         OUT PSECURITY_DESCRIPTOR pSecurityDescriptor,
235         IN ULONG Length,
236         OUT PULONG ResultLength)
237 {
238     PISECURITY_DESCRIPTOR_RELATIVE psd = pSecurityDescriptor;
239     NTSTATUS status;
240     unsigned int buffer_size = 512;
241     BOOLEAN need_more_memory;
242
243     TRACE("(%p,0x%08x,%p,0x%08x,%p)\n",
244         Object, RequestedInformation, pSecurityDescriptor, Length, ResultLength);
245
246     do
247     {
248         char *buffer = RtlAllocateHeap(GetProcessHeap(), 0, buffer_size);
249         if (!buffer)
250             return STATUS_NO_MEMORY;
251
252         need_more_memory = FALSE;
253
254         SERVER_START_REQ( get_security_object )
255         {
256             req->handle = wine_server_obj_handle( Object );
257             req->security_info = RequestedInformation;
258             wine_server_set_reply( req, buffer, buffer_size );
259             status = wine_server_call( req );
260             if (status == STATUS_SUCCESS)
261             {
262                 struct security_descriptor *sd = (struct security_descriptor *)buffer;
263                 if (reply->sd_len)
264                 {
265                     *ResultLength = sizeof(SECURITY_DESCRIPTOR_RELATIVE) +
266                         sd->owner_len + sd->group_len + sd->sacl_len + sd->dacl_len;
267                     if (Length >= *ResultLength)
268                     {
269                         psd->Revision = SECURITY_DESCRIPTOR_REVISION;
270                         psd->Sbz1 = 0;
271                         psd->Control = sd->control | SE_SELF_RELATIVE;
272                         psd->Owner = sd->owner_len ? sizeof(SECURITY_DESCRIPTOR_RELATIVE) : 0;
273                         psd->Group = sd->group_len ? sizeof(SECURITY_DESCRIPTOR_RELATIVE) + sd->owner_len : 0;
274                         psd->Sacl = sd->sacl_len ? sizeof(SECURITY_DESCRIPTOR_RELATIVE) + sd->owner_len + sd->group_len : 0;
275                         psd->Dacl = sd->dacl_len ? sizeof(SECURITY_DESCRIPTOR_RELATIVE) + sd->owner_len + sd->group_len + sd->sacl_len : 0;
276                         /* owner, group, sacl and dacl are the same type as in the server
277                          * and in the same order so we copy the memory in one block */
278                         memcpy((char *)pSecurityDescriptor + sizeof(SECURITY_DESCRIPTOR_RELATIVE),
279                                buffer + sizeof(struct security_descriptor),
280                                sd->owner_len + sd->group_len + sd->sacl_len + sd->dacl_len);
281                     }
282                     else
283                         status = STATUS_BUFFER_TOO_SMALL;
284                 }
285                 else
286                 {
287                     *ResultLength = sizeof(SECURITY_DESCRIPTOR_RELATIVE);
288                     if (Length >= *ResultLength)
289                     {
290                         memset(psd, 0, sizeof(*psd));
291                         psd->Revision = SECURITY_DESCRIPTOR_REVISION;
292                         psd->Control = SE_SELF_RELATIVE;
293                     }
294                     else
295                         status = STATUS_BUFFER_TOO_SMALL;
296                 }
297             }
298             else if (status == STATUS_BUFFER_TOO_SMALL)
299             {
300                 buffer_size = reply->sd_len;
301                 need_more_memory = TRUE;
302             }
303         }
304         SERVER_END_REQ;
305         RtlFreeHeap(GetProcessHeap(), 0, buffer);
306     } while (need_more_memory);
307
308     return status;
309 }
310
311
312 /******************************************************************************
313  *  NtDuplicateObject           [NTDLL.@]
314  *  ZwDuplicateObject           [NTDLL.@]
315  */
316 NTSTATUS WINAPI NtDuplicateObject( HANDLE source_process, HANDLE source,
317                                    HANDLE dest_process, PHANDLE dest,
318                                    ACCESS_MASK access, ULONG attributes, ULONG options )
319 {
320     NTSTATUS ret;
321     SERVER_START_REQ( dup_handle )
322     {
323         req->src_process = wine_server_obj_handle( source_process );
324         req->src_handle  = wine_server_obj_handle( source );
325         req->dst_process = wine_server_obj_handle( dest_process );
326         req->access      = access;
327         req->attributes  = attributes;
328         req->options     = options;
329
330         if (!(ret = wine_server_call( req )))
331         {
332             if (dest) *dest = wine_server_ptr_handle( reply->handle );
333             if (reply->closed)
334             {
335                 if (reply->self)
336                 {
337                     int fd = server_remove_fd_from_cache( source );
338                     if (fd != -1) close( fd );
339                 }
340             }
341             else if (options & DUPLICATE_CLOSE_SOURCE)
342                 WARN( "failed to close handle %p in process %p\n", source, source_process );
343         }
344     }
345     SERVER_END_REQ;
346     return ret;
347 }
348
349 /**************************************************************************
350  *                 NtClose                              [NTDLL.@]
351  *
352  * Close a handle reference to an object.
353  * 
354  * PARAMS
355  *  Handle [I] handle to close
356  *
357  * RETURNS
358  *  Success: ERROR_SUCCESS.
359  *  Failure: An NTSTATUS error code.
360  */
361 NTSTATUS WINAPI NtClose( HANDLE Handle )
362 {
363     NTSTATUS ret;
364     int fd = server_remove_fd_from_cache( Handle );
365
366     SERVER_START_REQ( close_handle )
367     {
368         req->handle = wine_server_obj_handle( Handle );
369         ret = wine_server_call( req );
370     }
371     SERVER_END_REQ;
372     if (fd != -1) close( fd );
373     return ret;
374 }
375
376 /*
377  *      Directory functions
378  */
379
380 /**************************************************************************
381  * NtOpenDirectoryObject [NTDLL.@]
382  * ZwOpenDirectoryObject [NTDLL.@]
383  *
384  * Open a namespace directory object.
385  * 
386  * PARAMS
387  *  DirectoryHandle  [O] Destination for the new directory handle
388  *  DesiredAccess    [I] Desired access to the directory
389  *  ObjectAttributes [I] Structure describing the directory
390  *
391  * RETURNS
392  *  Success: ERROR_SUCCESS.
393  *  Failure: An NTSTATUS error code.
394  */
395 NTSTATUS WINAPI NtOpenDirectoryObject(PHANDLE DirectoryHandle, ACCESS_MASK DesiredAccess,
396                                       POBJECT_ATTRIBUTES ObjectAttributes)
397 {
398     NTSTATUS ret;
399     TRACE("(%p,0x%08x,%s)\n", DirectoryHandle, DesiredAccess, debugstr_ObjectAttributes(ObjectAttributes));
400
401     if (!DirectoryHandle) return STATUS_ACCESS_VIOLATION;
402     if (!ObjectAttributes) return STATUS_INVALID_PARAMETER;
403     /* Have to test it here because server won't know difference between
404      * ObjectName == NULL and ObjectName == "" */
405     if (!ObjectAttributes->ObjectName)
406     {
407         if (ObjectAttributes->RootDirectory)
408             return STATUS_OBJECT_NAME_INVALID;
409         else
410             return STATUS_OBJECT_PATH_SYNTAX_BAD;
411     }
412
413     SERVER_START_REQ(open_directory)
414     {
415         req->access = DesiredAccess;
416         req->attributes = ObjectAttributes->Attributes;
417         req->rootdir = wine_server_obj_handle( ObjectAttributes->RootDirectory );
418         if (ObjectAttributes->ObjectName)
419             wine_server_add_data(req, ObjectAttributes->ObjectName->Buffer,
420                                  ObjectAttributes->ObjectName->Length);
421         ret = wine_server_call( req );
422         *DirectoryHandle = wine_server_ptr_handle( reply->handle );
423     }
424     SERVER_END_REQ;
425     return ret;
426 }
427
428 /******************************************************************************
429  *  NtCreateDirectoryObject     [NTDLL.@]
430  *  ZwCreateDirectoryObject     [NTDLL.@]
431  *
432  * Create a namespace directory object.
433  * 
434  * PARAMS
435  *  DirectoryHandle  [O] Destination for the new directory handle
436  *  DesiredAccess    [I] Desired access to the directory
437  *  ObjectAttributes [I] Structure describing the directory
438  *
439  * RETURNS
440  *  Success: ERROR_SUCCESS.
441  *  Failure: An NTSTATUS error code.
442  */
443 NTSTATUS WINAPI NtCreateDirectoryObject(PHANDLE DirectoryHandle, ACCESS_MASK DesiredAccess,
444                                         POBJECT_ATTRIBUTES ObjectAttributes)
445 {
446     NTSTATUS ret;
447     TRACE("(%p,0x%08x,%s)\n", DirectoryHandle, DesiredAccess, debugstr_ObjectAttributes(ObjectAttributes));
448
449     if (!DirectoryHandle) return STATUS_ACCESS_VIOLATION;
450
451     SERVER_START_REQ(create_directory)
452     {
453         req->access = DesiredAccess;
454         req->attributes = ObjectAttributes ? ObjectAttributes->Attributes : 0;
455         req->rootdir = wine_server_obj_handle( ObjectAttributes ? ObjectAttributes->RootDirectory : 0 );
456         if (ObjectAttributes && ObjectAttributes->ObjectName)
457             wine_server_add_data(req, ObjectAttributes->ObjectName->Buffer,
458                                  ObjectAttributes->ObjectName->Length);
459         ret = wine_server_call( req );
460         *DirectoryHandle = wine_server_ptr_handle( reply->handle );
461     }
462     SERVER_END_REQ;
463     return ret;
464 }
465
466 /******************************************************************************
467  * NtQueryDirectoryObject [NTDLL.@]
468  * ZwQueryDirectoryObject [NTDLL.@]
469  *
470  * Read information from a namespace directory.
471  * 
472  * PARAMS
473  *  handle        [I]   Handle to a directory object
474  *  buffer        [O]   Buffer to hold the read data
475  *  size          [I]   Size of the buffer in bytes
476  *  single_entry  [I]   If TRUE, return a single entry, if FALSE, return as many as fit in the buffer
477  *  restart       [I]   If TRUE, start scanning from the start, if FALSE, scan from Context
478  *  context       [I/O] Indicates what point of the directory the scan is at
479  *  ret_size      [O]   Caller supplied storage for the number of bytes written (or NULL)
480  *
481  * RETURNS
482  *  Success: ERROR_SUCCESS.
483  *  Failure: An NTSTATUS error code.
484  */
485 NTSTATUS WINAPI NtQueryDirectoryObject(HANDLE handle, PDIRECTORY_BASIC_INFORMATION buffer,
486                                        ULONG size, BOOLEAN single_entry, BOOLEAN restart,
487                                        PULONG context, PULONG ret_size)
488 {
489     NTSTATUS ret;
490
491     if (restart) *context = 0;
492
493     if (single_entry)
494     {
495         if (size <= sizeof(*buffer) + 2*sizeof(WCHAR)) return STATUS_BUFFER_OVERFLOW;
496
497         SERVER_START_REQ( get_directory_entry )
498         {
499             req->handle = wine_server_obj_handle( handle );
500             req->index = *context;
501             wine_server_set_reply( req, buffer + 1, size - sizeof(*buffer) - 2*sizeof(WCHAR) );
502             if (!(ret = wine_server_call( req )))
503             {
504                 buffer->ObjectName.Buffer = (WCHAR *)(buffer + 1);
505                 buffer->ObjectName.Length = reply->name_len;
506                 buffer->ObjectName.MaximumLength = reply->name_len + sizeof(WCHAR);
507                 buffer->ObjectTypeName.Buffer = (WCHAR *)(buffer + 1) + reply->name_len/sizeof(WCHAR) + 1;
508                 buffer->ObjectTypeName.Length = wine_server_reply_size( reply ) - reply->name_len;
509                 buffer->ObjectTypeName.MaximumLength = buffer->ObjectTypeName.Length + sizeof(WCHAR);
510                 /* make room for the terminating null */
511                 memmove( buffer->ObjectTypeName.Buffer, buffer->ObjectTypeName.Buffer - 1,
512                          buffer->ObjectTypeName.Length );
513                 buffer->ObjectName.Buffer[buffer->ObjectName.Length/sizeof(WCHAR)] = 0;
514                 buffer->ObjectTypeName.Buffer[buffer->ObjectTypeName.Length/sizeof(WCHAR)] = 0;
515                 (*context)++;
516             }
517         }
518         SERVER_END_REQ;
519         if (ret_size)
520             *ret_size = buffer->ObjectName.MaximumLength + buffer->ObjectTypeName.MaximumLength + sizeof(*buffer);
521     }
522     else
523     {
524         FIXME("multiple entries not implemented\n");
525         ret = STATUS_NOT_IMPLEMENTED;
526     }
527
528     return ret;
529 }
530
531 /*
532  *      Link objects
533  */
534
535 /******************************************************************************
536  *  NtOpenSymbolicLinkObject    [NTDLL.@]
537  *  ZwOpenSymbolicLinkObject    [NTDLL.@]
538  *
539  * Open a namespace symbolic link object.
540  * 
541  * PARAMS
542  *  LinkHandle       [O] Destination for the new symbolic link handle
543  *  DesiredAccess    [I] Desired access to the symbolic link
544  *  ObjectAttributes [I] Structure describing the symbolic link
545  *
546  * RETURNS
547  *  Success: ERROR_SUCCESS.
548  *  Failure: An NTSTATUS error code.
549  */
550 NTSTATUS WINAPI NtOpenSymbolicLinkObject(OUT PHANDLE LinkHandle, IN ACCESS_MASK DesiredAccess,
551                                          IN POBJECT_ATTRIBUTES ObjectAttributes)
552 {
553     NTSTATUS ret;
554     TRACE("(%p,0x%08x,%s)\n",LinkHandle, DesiredAccess, debugstr_ObjectAttributes(ObjectAttributes));
555
556     if (!LinkHandle) return STATUS_ACCESS_VIOLATION;
557     if (!ObjectAttributes) return STATUS_INVALID_PARAMETER;
558     /* Have to test it here because server won't know difference between
559      * ObjectName == NULL and ObjectName == "" */
560     if (!ObjectAttributes->ObjectName)
561     {
562         if (ObjectAttributes->RootDirectory)
563             return STATUS_OBJECT_NAME_INVALID;
564         else
565             return STATUS_OBJECT_PATH_SYNTAX_BAD;
566     }
567
568     SERVER_START_REQ(open_symlink)
569     {
570         req->access = DesiredAccess;
571         req->attributes = ObjectAttributes->Attributes;
572         req->rootdir = wine_server_obj_handle( ObjectAttributes->RootDirectory );
573         if (ObjectAttributes->ObjectName)
574             wine_server_add_data(req, ObjectAttributes->ObjectName->Buffer,
575                                  ObjectAttributes->ObjectName->Length);
576         ret = wine_server_call( req );
577         *LinkHandle = wine_server_ptr_handle( reply->handle );
578     }
579     SERVER_END_REQ;
580     return ret;
581 }
582
583 /******************************************************************************
584  *  NtCreateSymbolicLinkObject  [NTDLL.@]
585  *  ZwCreateSymbolicLinkObject  [NTDLL.@]
586  *
587  * Open a namespace symbolic link object.
588  * 
589  * PARAMS
590  *  SymbolicLinkHandle [O] Destination for the new symbolic link handle
591  *  DesiredAccess      [I] Desired access to the symbolic link
592  *  ObjectAttributes   [I] Structure describing the symbolic link
593  *  TargetName         [I] Name of the target symbolic link points to
594  *
595  * RETURNS
596  *  Success: ERROR_SUCCESS.
597  *  Failure: An NTSTATUS error code.
598  */
599 NTSTATUS WINAPI NtCreateSymbolicLinkObject(OUT PHANDLE SymbolicLinkHandle,IN ACCESS_MASK DesiredAccess,
600                                            IN POBJECT_ATTRIBUTES ObjectAttributes,
601                                            IN PUNICODE_STRING TargetName)
602 {
603     NTSTATUS ret;
604
605     if (!SymbolicLinkHandle || !TargetName) return STATUS_ACCESS_VIOLATION;
606     if (!TargetName->Buffer) return STATUS_INVALID_PARAMETER;
607
608     TRACE("(%p,0x%08x,%s -> %s)\n", SymbolicLinkHandle, DesiredAccess,
609           debugstr_ObjectAttributes(ObjectAttributes), debugstr_us(TargetName));
610
611     SERVER_START_REQ(create_symlink)
612     {
613         req->access = DesiredAccess;
614         req->attributes = ObjectAttributes ? ObjectAttributes->Attributes : 0;
615         req->rootdir = wine_server_obj_handle( ObjectAttributes ? ObjectAttributes->RootDirectory : 0 );
616         if (ObjectAttributes && ObjectAttributes->ObjectName)
617         {
618             req->name_len = ObjectAttributes->ObjectName->Length;
619             wine_server_add_data(req, ObjectAttributes->ObjectName->Buffer,
620                                  ObjectAttributes->ObjectName->Length);
621         }
622         else
623             req->name_len = 0;
624         wine_server_add_data(req, TargetName->Buffer, TargetName->Length);
625         ret = wine_server_call( req );
626         *SymbolicLinkHandle = wine_server_ptr_handle( reply->handle );
627     }
628     SERVER_END_REQ;
629     return ret;
630 }
631
632 /******************************************************************************
633  *  NtQuerySymbolicLinkObject   [NTDLL.@]
634  *  ZwQuerySymbolicLinkObject   [NTDLL.@]
635  *
636  * Query a namespace symbolic link object target name.
637  * 
638  * PARAMS
639  *  LinkHandle     [I] Handle to a symbolic link object
640  *  LinkTarget     [O] Destination for the symbolic link target
641  *  ReturnedLength [O] Size of returned data
642  *
643  * RETURNS
644  *  Success: ERROR_SUCCESS.
645  *  Failure: An NTSTATUS error code.
646  */
647 NTSTATUS WINAPI NtQuerySymbolicLinkObject(IN HANDLE LinkHandle, IN OUT PUNICODE_STRING LinkTarget,
648                                           OUT PULONG ReturnedLength OPTIONAL)
649 {
650     NTSTATUS ret;
651     TRACE("(%p,%p,%p)\n", LinkHandle, LinkTarget, ReturnedLength);
652
653     if (!LinkTarget) return STATUS_ACCESS_VIOLATION;
654
655     SERVER_START_REQ(query_symlink)
656     {
657         req->handle = wine_server_obj_handle( LinkHandle );
658         wine_server_set_reply( req, LinkTarget->Buffer, LinkTarget->MaximumLength );
659         if (!(ret = wine_server_call( req )))
660         {
661             LinkTarget->Length = wine_server_reply_size(reply);
662             if (ReturnedLength) *ReturnedLength = LinkTarget->Length;
663         }
664     }
665     SERVER_END_REQ;
666     return ret;
667 }
668
669 /******************************************************************************
670  *  NtAllocateUuids   [NTDLL.@]
671  */
672 NTSTATUS WINAPI NtAllocateUuids(
673         PULARGE_INTEGER Time,
674         PULONG Range,
675         PULONG Sequence)
676 {
677         FIXME("(%p,%p,%p), stub.\n", Time, Range, Sequence);
678         return 0;
679 }
680
681 /**************************************************************************
682  *  NtMakeTemporaryObject       [NTDLL.@]
683  *  ZwMakeTemporaryObject       [NTDLL.@]
684  *
685  * Make a permanent object temporary.
686  *
687  * PARAMS
688  *  Handle [I] handle to permanent object
689  *
690  * RETURNS
691  *  Success: STATUS_SUCCESS.
692  *  Failure: An NTSTATUS error code.
693  */
694 NTSTATUS WINAPI NtMakeTemporaryObject( HANDLE Handle )
695 {
696     FIXME("(%p), stub.\n", Handle);
697     return STATUS_SUCCESS;
698 }