Use free Windows DDK and compile with latest MinGW releases.
[reactos.git] / reactos / lib / ntdll / rtl / path.c
1 /* $Id: path.c,v 1.13 2002/09/07 15:12:40 chorns Exp $
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS system libraries
5 * FILE: lib/ntdll/rtl/path.c
6 * PURPOSE: Path and current directory functions
7 * UPDATE HISTORY:
8 * Created 03/02/00
9 */
10
11 /* INCLUDES ******************************************************************/
12
13 #define NTOS_USER_MODE
14 #include <ntos.h>
15 #include <string.h>
16 #include <stdio.h>
17 #include <ctype.h>
18
19 #define NDEBUG
20 #include <debug.h>
21
22 /* DEFINITONS and MACROS ******************************************************/
23
24 #define MAX_PFX_SIZE 16
25
26 #define IS_PATH_SEPARATOR(x) (((x)==L'\\')||((x)==L'/'))
27
28
29 /* FUNCTIONS *****************************************************************/
30
31 static ULONG RtlpGetDotSequence (PWSTR p)
32 {
33 ULONG Count = 0;
34
35 for (;;)
36 {
37 if (*p == '.')
38 Count++;
39 else if ((*p == '\\' || *p == '\0') && Count)
40 return Count;
41 else
42 return 0;
43 p++;
44 }
45 return 0;
46 }
47
48
49 static VOID RtlpEatPath (PWSTR Path)
50 {
51 PWSTR p, prev;
52
53 p = Path + 2;
54 prev = p;
55
56 while ((*p) != 0 || ((*p) == L'\\' && (*(p+1)) == 0))
57 {
58 ULONG DotLen;
59
60 DotLen = RtlpGetDotSequence (p+1);
61 DPRINT("DotSequenceLength %u\n", DotLen);
62 DPRINT("prev %S p %S\n",prev,p);
63
64 if (DotLen == 0)
65 {
66 prev = p;
67 do
68 {
69 p++;
70 }
71 while ((*p) != 0 && (*p) != L'\\');
72 }
73 else if (DotLen == 1)
74 {
75 wcscpy (p, p+2);
76 }
77 else
78 {
79 if (DotLen > 2)
80 {
81 int n = DotLen - 2;
82
83 while (n > 0 && prev > (Path + 2))
84 {
85 prev--;
86 if ((*prev) == L'\\')
87 n--;
88 }
89 }
90
91 if (*(p + DotLen + 1) == 0)
92 *(prev + 1) = 0;
93 else
94 wcscpy (prev, p + DotLen + 1);
95 p = prev;
96 if (prev > (Path + 2))
97 {
98 prev--;
99 while ((*prev) != L'\\')
100 {
101 prev--;
102 }
103 }
104 }
105 }
106 }
107
108
109 ULONG STDCALL RtlGetLongestNtPathLength (VOID)
110 {
111 return (MAX_PATH + 9);
112 }
113
114
115 ULONG STDCALL
116 RtlDetermineDosPathNameType_U(PWSTR Path)
117 {
118 DPRINT("RtlDetermineDosPathNameType_U %S\n", Path);
119
120 if (Path == NULL)
121 {
122 return 0;
123 }
124
125 if (IS_PATH_SEPARATOR(Path[0]))
126 {
127 if (!IS_PATH_SEPARATOR(Path[1]))
128 {
129 return 4; /* \xxx */
130 }
131
132 if (Path[2] != L'.')
133 return 1; /* \\xxx */
134
135 if (IS_PATH_SEPARATOR(Path[3]))
136 return 6; /* \\.\xxx */
137
138 if (Path[3])
139 return 1; /* \\.xxxx */
140
141 return 7; /* \\. */
142 }
143 else
144 {
145 if (Path[1] != L':')
146 return 5; /* xxx */
147
148 if (IS_PATH_SEPARATOR(Path[2]))
149 return 2; /* x:\xxx */
150
151 return 3; /* x:xxx */
152 }
153 }
154
155
156 /* returns 0 if name is not valid DOS device name, or DWORD with
157 * offset in bytes to DOS device name from beginning of buffer in high word
158 * and size in bytes of DOS device name in low word */
159
160 ULONG STDCALL
161 RtlIsDosDeviceName_U(PWSTR DeviceName)
162 {
163 ULONG Type;
164 ULONG Length = 0;
165 ULONG Offset;
166 PWCHAR wc;
167
168 if (DeviceName == NULL)
169 {
170 return 0;
171 }
172
173 while (DeviceName[Length])
174 {
175 Length++;
176 }
177
178 Type = RtlDetermineDosPathNameType_U(DeviceName);
179 if (Type <= 1)
180 {
181 return 0;
182 }
183
184 if (Type == 6)
185 {
186 if (Length == 7 &&
187 !_wcsnicmp (DeviceName, L"\\\\.\\CON", 7))
188 return 0x00080006;
189 return 0;
190 }
191
192 /* name can end with ':' */
193 if (Length && DeviceName[Length - 1 ] == L':')
194 {
195 Length--;
196 }
197
198 /* there can be spaces or points at the end of name */
199 wc = DeviceName + Length - 1;
200 while (Length && (*wc == L'.' || *wc == L' '))
201 {
202 Length--;
203 wc--;
204 }
205
206 /* let's find a beginning of name */
207 wc = DeviceName + Length - 1;
208 while (wc > DeviceName && !IS_PATH_SEPARATOR(*(wc - 1)))
209 {
210 wc--;
211 }
212 Offset = wc - DeviceName;
213 Length -= Offset;
214
215 /* check for LPTx or COMx */
216 if (Length == 4 && wc[3] >= L'0' && wc[3] <= L'9')
217 {
218 if (wc[3] == L'0')
219 {
220 return 0;
221 }
222
223 if (!_wcsnicmp (wc, L"LPT", 3) ||
224 !_wcsnicmp (wc, L"COM", 3))
225 {
226 return ((Offset * 2) << 16 ) | 8;
227 }
228 return 0;
229 }
230
231 /* check for PRN,AUX,NUL or CON */
232 if (Length == 3 &&
233 (!_wcsnicmp (wc, L"PRN", 3) ||
234 !_wcsnicmp (wc, L"AUX", 3) ||
235 !_wcsnicmp (wc, L"NUL", 3) ||
236 !_wcsnicmp (wc, L"CON", 3)))
237 {
238 return ((Offset * 2) << 16) | 6;
239 }
240
241 return 0;
242 }
243
244
245 ULONG STDCALL
246 RtlGetCurrentDirectory_U(ULONG MaximumLength,
247 PWSTR Buffer)
248 {
249 ULONG Length;
250 PCURDIR cd;
251
252 DPRINT ("RtlGetCurrentDirectory %lu %p\n", MaximumLength, Buffer);
253
254 cd = &(NtCurrentPeb ()->ProcessParameters->CurrentDirectory);
255
256 RtlAcquirePebLock();
257 Length = cd->DosPath.Length / sizeof(WCHAR);
258 if (cd->DosPath.Buffer[Length - 1] == L'\\' &&
259 cd->DosPath.Buffer[Length - 2] != L':')
260 Length--;
261
262 DPRINT ("cd->DosPath.Buffer %S Length %d\n",
263 cd->DosPath.Buffer, Length);
264
265 if (MaximumLength / sizeof(WCHAR) > Length)
266 {
267 memcpy (Buffer,
268 cd->DosPath.Buffer,
269 Length * sizeof(WCHAR));
270 Buffer[Length] = 0;
271 }
272 else
273 {
274 Length++;
275 }
276
277 RtlReleasePebLock ();
278
279 DPRINT ("CurrentDirectory %S\n", Buffer);
280
281 return (Length * sizeof(WCHAR));
282 }
283
284
285 NTSTATUS STDCALL
286 RtlSetCurrentDirectory_U(PUNICODE_STRING name)
287 {
288 UNICODE_STRING full;
289 UNICODE_STRING envvar;
290 OBJECT_ATTRIBUTES Attr;
291 IO_STATUS_BLOCK iosb;
292 PCURDIR cd;
293 NTSTATUS Status;
294 ULONG size;
295 HANDLE handle = NULL;
296 PWSTR wcs;
297 PWSTR buf = 0;
298 PFILE_NAME_INFORMATION filenameinfo;
299 ULONG backslashcount = 0;
300 PWSTR cntr;
301 WCHAR var[4];
302
303 DPRINT ("RtlSetCurrentDirectory %wZ\n", name);
304
305 RtlAcquirePebLock ();
306 cd = &NtCurrentPeb ()->ProcessParameters->CurrentDirectory;
307 size = cd->DosPath.MaximumLength;
308
309 buf = RtlAllocateHeap (RtlGetProcessHeap(),
310 0,
311 size);
312 if (buf == NULL)
313 {
314 RtlReleasePebLock ();
315 return STATUS_NO_MEMORY;
316 }
317
318 size = RtlGetFullPathName_U (name->Buffer, size, buf, 0);
319 if (!size)
320 {
321 RtlFreeHeap (RtlGetProcessHeap (),
322 0,
323 buf);
324 RtlReleasePebLock ();
325 return STATUS_OBJECT_NAME_INVALID;
326 }
327
328 if (!RtlDosPathNameToNtPathName_U (buf, &full, 0, 0))
329 {
330 RtlFreeHeap (RtlGetProcessHeap (),
331 0,
332 buf);
333 RtlFreeHeap (RtlGetProcessHeap (),
334 0,
335 full.Buffer);
336 RtlReleasePebLock ();
337 return STATUS_OBJECT_NAME_INVALID;
338 }
339
340 InitializeObjectAttributes (&Attr,
341 &full,
342 OBJ_CASE_INSENSITIVE | OBJ_INHERIT,
343 NULL,
344 NULL);
345
346 Status = NtOpenFile (&handle,
347 SYNCHRONIZE | FILE_TRAVERSE,
348 &Attr,
349 &iosb,
350 FILE_SHARE_READ | FILE_SHARE_WRITE,
351 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
352 if (!NT_SUCCESS(Status))
353 {
354 RtlFreeHeap (RtlGetProcessHeap (),
355 0,
356 buf);
357 RtlFreeHeap (RtlGetProcessHeap (),
358 0,
359 full.Buffer);
360 RtlReleasePebLock ();
361 return Status;
362 }
363
364 filenameinfo = RtlAllocateHeap(RtlGetProcessHeap(),
365 0,
366 MAX_PATH*sizeof(WCHAR)+sizeof(ULONG));
367
368 Status = NtQueryInformationFile(handle,
369 &iosb,
370 filenameinfo,
371 MAX_PATH*sizeof(WCHAR)+sizeof(ULONG),
372 FileNameInformation);
373 if (!NT_SUCCESS(Status))
374 {
375 RtlFreeHeap(RtlGetProcessHeap(),
376 0,
377 filenameinfo);
378 RtlFreeHeap(RtlGetProcessHeap(),
379 0,
380 buf);
381 RtlFreeHeap(RtlGetProcessHeap(),
382 0,
383 full.Buffer);
384 RtlReleasePebLock();
385 return(Status);
386 }
387
388 if (filenameinfo->FileName[1]) // If it's just "\", we need special handling
389 {
390 wcs = buf + size / sizeof(WCHAR) - 1;
391 if (*wcs == L'\\')
392 {
393 *(wcs) = 0;
394 wcs--;
395 size -= sizeof(WCHAR);
396 }
397
398 for (cntr=filenameinfo->FileName;*cntr!=0;cntr++)
399 {
400 if (*cntr=='\\') backslashcount++;
401 }
402
403 DPRINT("%d \n",backslashcount);
404 for (;backslashcount;wcs--)
405 {
406 if (*wcs=='\\') backslashcount--;
407 }
408 wcs++;
409
410 wcscpy(wcs,filenameinfo->FileName);
411
412 size=((wcs-buf)+wcslen(filenameinfo->FileName))*sizeof(WCHAR);
413 }
414
415 RtlFreeHeap (RtlGetProcessHeap (),
416 0,
417 filenameinfo);
418
419 /* append backslash if missing */
420 wcs = buf + size / sizeof(WCHAR) - 1;
421 if (*wcs != L'\\')
422 {
423 *(++wcs) = L'\\';
424 *(++wcs) = 0;
425 size += sizeof(WCHAR);
426 }
427
428 memmove(cd->DosPath.Buffer,
429 buf,
430 size + sizeof(WCHAR));
431 cd->DosPath.Length = size;
432
433 if (cd->Handle)
434 NtClose(cd->Handle);
435 cd->Handle = handle;
436
437 if (cd->DosPath.Buffer[1]==':')
438 {
439 envvar.Length = 2 * swprintf (var, L"=%c:", cd->DosPath.Buffer[0]);
440 envvar.MaximumLength = 8;
441 envvar.Buffer = var;
442
443 RtlSetEnvironmentVariable(NULL,
444 &envvar,
445 &cd->DosPath);
446 }
447
448 RtlFreeHeap (RtlGetProcessHeap (),
449 0,
450 buf);
451
452 RtlFreeHeap (RtlGetProcessHeap (),
453 0,
454 full.Buffer);
455
456 RtlReleasePebLock();
457
458 return STATUS_SUCCESS;
459 }
460
461
462 ULONG STDCALL
463 RtlGetFullPathName_U(PWSTR DosName,
464 ULONG size,
465 PWSTR buf,
466 PWSTR *FilePart)
467 {
468 WCHAR *wcs, var[4], drive;
469 int len;
470 int reallen;
471 DWORD offs, sz, type;
472 UNICODE_STRING usvar, pfx;
473 PCURDIR cd;
474 NTSTATUS Status;
475
476 DPRINT("RtlGetFullPathName_U %S %ld %p %p\n",
477 DosName, size, buf, FilePart);
478
479 if (!DosName || !*DosName)
480 return 0;
481
482 len = wcslen (DosName);
483
484 /* strip trailing spaces */
485 while (len && DosName[len - 1] == L' ')
486 len--;
487 if (!len)
488 return 0;
489
490 reallen=len;
491 /* strip trailing path separator (but don't change '\') */
492 if ((len > 1) &&
493 IS_PATH_SEPARATOR(DosName[len - 1]))
494 len--;
495 if (FilePart)
496 *FilePart = L'\0';
497 memset (buf, 0, size);
498
499 CHECKPOINT;
500 /* check for DOS device name */
501 sz = RtlIsDosDeviceName_U (DosName);
502 if (sz)
503 {
504 offs = sz >> 17;
505 sz &= 0x0000FFFF;
506 if (sz + 8 >= size)
507 return sz + 10;
508 wcscpy (buf, L"\\\\.\\");
509 wcsncat (buf, DosName + offs, sz / sizeof(WCHAR));
510 return sz + 8;
511 }
512
513 CHECKPOINT;
514 type = RtlDetermineDosPathNameType_U (DosName);
515
516 RtlAcquirePebLock();
517
518 cd = &(NtCurrentPeb ()->ProcessParameters->CurrentDirectory);
519 DPRINT("type %ld\n", type);
520 switch (type)
521 {
522 case 1: /* \\xxx or \\.xxx */
523 case 6: /* \\.\xxx */
524 break;
525
526 case 2: /* x:\xxx */
527 *DosName = towupper (*DosName);
528 break;
529
530 case 3: /* x:xxx */
531 drive = towupper (*DosName);
532 DosName += 2;
533 len -= 2;
534 CHECKPOINT;
535 if (drive == towupper (cd->DosPath.Buffer[0]))
536 {
537 CHECKPOINT;
538 wcscpy (buf, cd->DosPath.Buffer);
539 }
540 else
541 {
542 CHECKPOINT;
543 usvar.Length = 2 * swprintf (var, L"=%c:", drive);
544 usvar.MaximumLength = 8;
545 usvar.Buffer = var;
546 pfx.Length = 0;
547 pfx.MaximumLength = size;
548 pfx.Buffer = buf;
549 Status = RtlQueryEnvironmentVariable_U (NULL,
550 &usvar,
551 &pfx);
552 CHECKPOINT;
553 if (!NT_SUCCESS(Status))
554 {
555 CHECKPOINT;
556 if (Status == STATUS_BUFFER_TOO_SMALL)
557 return pfx.Length + len * 2 + 2;
558 swprintf (buf, L"%c:\\", drive);
559 }
560 }
561 break;
562
563 case 4: /* \xxx */
564 wcsncpy (buf, cd->DosPath.Buffer, 2);
565 break;
566
567 case 5: /* xxx */
568 wcscpy (buf, cd->DosPath.Buffer);
569 break;
570
571 case 7: /* \\. */
572 wcscpy (buf, L"\\\\.\\");
573 len = 0;
574 break;
575
576 default:
577 return 0;
578 }
579
580 DPRINT("buf \'%S\' DosName \'%S\' len %ld\n", buf, DosName, len);
581 /* add dosname to prefix */
582 wcsncat (buf, DosName, len);
583
584 CHECKPOINT;
585 /* replace slashes */
586 for (wcs = buf; *wcs; wcs++)
587 if (*wcs == L'/')
588 *wcs = L'\\';
589
590 len = wcslen (buf);
591 if (len < 3 && buf[len-1] == L':')
592 wcscat (buf, L"\\");
593
594 DPRINT("buf \'%S\'\n", buf);
595 RtlpEatPath (buf);
596 DPRINT("buf \'%S\'\n", buf);
597
598 len = wcslen (buf);
599
600 /* find file part */
601 if (FilePart)
602 {
603 for (wcs = buf + len - 1; wcs >= buf; wcs--)
604 {
605 if (*wcs == L'\\')
606 {
607 *FilePart = wcs + 1;
608 break;
609 }
610 }
611 }
612
613 RtlReleasePebLock();
614
615 return len * sizeof(WCHAR);
616 }
617
618
619 BOOLEAN STDCALL
620 RtlDosPathNameToNtPathName_U(PWSTR dosname,
621 PUNICODE_STRING ntname,
622 PWSTR *FilePart,
623 PCURDIR nah)
624 {
625 UNICODE_STRING us;
626 PCURDIR cd;
627 ULONG Type;
628 ULONG Size;
629 ULONG Length;
630 ULONG Offset;
631 WCHAR fullname[2*MAX_PATH];
632 PWSTR Buffer = NULL;
633
634 RtlAcquirePebLock ();
635
636 RtlInitUnicodeString (&us, dosname);
637 if (us.Length > 8)
638 {
639 Buffer = us.Buffer;
640 /* check for "\\?\" - allows to use very long filenames ( up to 32k ) */
641 if (Buffer[0] == L'\\' && Buffer[1] == L'\\' &&
642 Buffer[2] == L'?' && Buffer[3] == L'\\')
643 {
644 // if( f_77F68606( &us, ntname, shortname, nah ) )
645 // {
646 // RtlReleasePebLock ();
647 // return TRUE;
648 // }
649 Buffer = NULL;
650 RtlReleasePebLock ();
651 return FALSE;
652 }
653 }
654
655 Buffer = RtlAllocateHeap (RtlGetProcessHeap (),
656 0,
657 sizeof( fullname ) + MAX_PFX_SIZE);
658 if (Buffer == NULL)
659 {
660 RtlReleasePebLock ();
661 return FALSE;
662 }
663
664 Size = RtlGetFullPathName_U (dosname,
665 sizeof(fullname),
666 fullname,
667 FilePart);
668 if (Size == 0 || Size > MAX_PATH * sizeof(WCHAR))
669 {
670 RtlFreeHeap (RtlGetProcessHeap (),
671 0,
672 Buffer);
673 RtlReleasePebLock ();
674 return FALSE;
675 }
676
677 /* Set NT prefix */
678 Offset = 0;
679 wcscpy (Buffer, L"\\??\\");
680
681 Type = RtlDetermineDosPathNameType_U (fullname);
682 switch (Type)
683 {
684 case 1:
685 wcscat (Buffer, L"UNC\\");
686 Offset = 2;
687 break; /* \\xxx */
688
689 case 6:
690 Offset = 4;
691 break; /* \\.\xxx */
692 }
693 wcscat (Buffer, fullname + Offset);
694 Length = wcslen (Buffer);
695
696 /* set NT filename */
697 ntname->Length = Length * sizeof(WCHAR);
698 ntname->MaximumLength = sizeof(fullname) + MAX_PFX_SIZE;
699 ntname->Buffer = Buffer;
700
701 /* set pointer to file part if possible */
702 if (FilePart && *FilePart)
703 *FilePart = Buffer + Length - wcslen (*FilePart);
704
705 /* Set name and handle structure if possible */
706 if (nah)
707 {
708 memset (nah, 0, sizeof(CURDIR));
709 cd = &(NtCurrentPeb ()->ProcessParameters->CurrentDirectory);
710 if (Type == 5 && cd->Handle &&
711 !_wcsnicmp (cd->DosPath.Buffer, fullname, cd->DosPath.Length / 2))
712 {
713 Length = ((cd->DosPath.Length / sizeof(WCHAR)) - Offset) + ((Type == 1) ? 8 : 4);
714 nah->DosPath.Buffer = Buffer + Length;
715 nah->DosPath.Length = ntname->Length - (Length * sizeof(WCHAR));
716 nah->DosPath.MaximumLength = nah->DosPath.Length;
717 nah->Handle = cd->Handle;
718 }
719 }
720
721 RtlReleasePebLock();
722
723 return TRUE;
724 }
725
726
727 ULONG
728 STDCALL
729 RtlDosSearchPath_U (
730 WCHAR *sp,
731 WCHAR *name,
732 WCHAR *ext,
733 ULONG buf_sz,
734 WCHAR *buffer,
735 PWSTR *FilePart
736 )
737 {
738 ULONG Type;
739 ULONG Length = 0;
740 PWSTR full_name;
741 PWSTR wcs;
742 PWSTR path;
743
744 Type = RtlDetermineDosPathNameType_U (name);
745
746 if (Type == 5)
747 {
748 Length = wcslen (sp);
749 Length += wcslen (name);
750 if (wcschr (name, L'.'))
751 ext = NULL;
752 if (ext != NULL)
753 Length += wcslen (ext);
754
755 full_name = (WCHAR*)RtlAllocateHeap (RtlGetProcessHeap (),
756 0,
757 (Length + 1) * sizeof(WCHAR));
758 Length = 0;
759 if (full_name != NULL)
760 {
761 path = sp;
762 while (*path)
763 {
764 wcs = full_name;
765 while (*path && *path != L';')
766 *wcs++ = *path++;
767 if (*path)
768 path++;
769 if (wcs != full_name && *(wcs - 1) != L'\\')
770 *wcs++ = L'\\';
771 wcscpy (wcs, name);
772 if (ext)
773 wcscat (wcs, ext);
774 if (RtlDoesFileExists_U (full_name))
775 {
776 Length = RtlGetFullPathName_U (full_name,
777 buf_sz,
778 buffer,
779 FilePart);
780 break;
781 }
782 }
783
784 RtlFreeHeap (RtlGetProcessHeap (),
785 0,
786 full_name);
787 }
788 }
789 else if (RtlDoesFileExists_U (name))
790 {
791 Length = RtlGetFullPathName_U (name,
792 buf_sz,
793 buffer,
794 FilePart);
795 }
796
797 return Length;
798 }
799
800
801 BOOLEAN STDCALL
802 RtlDoesFileExists_U(IN PWSTR FileName)
803 {
804 UNICODE_STRING NtFileName;
805 OBJECT_ATTRIBUTES Attr;
806 NTSTATUS Status;
807 CURDIR CurDir;
808 PWSTR Buffer;
809
810 /* only used by replacement code */
811 HANDLE FileHandle;
812 IO_STATUS_BLOCK StatusBlock;
813
814 if (!RtlDosPathNameToNtPathName_U (FileName,
815 &NtFileName,
816 NULL,
817 &CurDir))
818 return FALSE;
819
820 /* don't forget to free it! */
821 Buffer = NtFileName.Buffer;
822
823 if (CurDir.DosPath.Length)
824 NtFileName = CurDir.DosPath;
825 else
826 CurDir.Handle = 0;
827
828 InitializeObjectAttributes (&Attr,
829 &NtFileName,
830 OBJ_CASE_INSENSITIVE,
831 CurDir.Handle,
832 NULL);
833
834 /* FIXME: not implemented yet */
835 // Status = NtQueryAttributesFile (&Attr, NULL);
836
837 /* REPLACEMENT start */
838 Status = NtOpenFile (&FileHandle,
839 0x10001,
840 &Attr,
841 &StatusBlock,
842 1,
843 FILE_SYNCHRONOUS_IO_NONALERT);
844 if (NT_SUCCESS(Status))
845 NtClose (FileHandle);
846 /* REPLACEMENT end */
847
848 RtlFreeHeap (RtlGetProcessHeap (),
849 0,
850 Buffer);
851
852 if (NT_SUCCESS(Status) ||
853 Status == STATUS_SHARING_VIOLATION ||
854 Status == STATUS_ACCESS_DENIED)
855 return TRUE;
856
857 return FALSE;
858 }
859
860 /* EOF */