311 lines
8 KiB
C
311 lines
8 KiB
C
|
#include "psxdll.h"
|
||
|
|
||
|
#if DBG
|
||
|
VOID DumpNames(PSZ, PSTRING );
|
||
|
#endif //DBG
|
||
|
|
||
|
PSTRING
|
||
|
SetPrefix(
|
||
|
char **Source
|
||
|
)
|
||
|
{
|
||
|
static STRING SparePrefix; // pointer to this may be returned
|
||
|
static char PrefixBuf[512];
|
||
|
|
||
|
//
|
||
|
// Test for leading slash. This tells us whether to start at the root
|
||
|
// or at the current working directory.
|
||
|
//
|
||
|
|
||
|
if (!IS_POSIX_PATH_SEPARATOR(*Source)) {
|
||
|
// relative pathname
|
||
|
return &PdxDirectoryPrefix.NtCurrentWorkingDirectory;
|
||
|
}
|
||
|
|
||
|
if (!IS_POSIX_PATH_SEPARATOR(&(*Source)[1])) {
|
||
|
// first char is slash, but second is not. Start at root.
|
||
|
return &PdxDirectoryPrefix.PsxRoot;
|
||
|
}
|
||
|
if (IS_POSIX_PATH_SEPARATOR(&(*Source)[2])) {
|
||
|
// first three chars are slashes; interpreted as single slash.
|
||
|
return &PdxDirectoryPrefix.PsxRoot;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// The path starts with "//something":
|
||
|
// //X/ is \DosDevices\X:\
|
||
|
//
|
||
|
|
||
|
memset(PrefixBuf, 0, sizeof(PrefixBuf));
|
||
|
PSX_GET_SESSION_NAME_A(PrefixBuf, DOSDEVICE_A);
|
||
|
|
||
|
strncat(PrefixBuf, &(*Source)[2], 1); // get "X"
|
||
|
strcat(PrefixBuf, ":"); // make "X:"
|
||
|
*Source += 3;
|
||
|
|
||
|
SparePrefix.Buffer = PrefixBuf;
|
||
|
SparePrefix.Length = (USHORT)strlen(PrefixBuf);
|
||
|
SparePrefix.MaximumLength = sizeof(PrefixBuf);
|
||
|
|
||
|
return &SparePrefix;
|
||
|
}
|
||
|
|
||
|
BOOLEAN
|
||
|
PdxCanonicalize(
|
||
|
IN PCHAR PathName,
|
||
|
OUT PUNICODE_STRING CanonPath_U,
|
||
|
IN PVOID Heap
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This function accepts a POSIX pathname and converts it into a
|
||
|
Unicode NT pathname.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
PathName - Supplies the POSIX pathname to be translated.
|
||
|
|
||
|
CanonPath_U - Returns the canonicalized Unicode NT pathname. On a
|
||
|
successful call, storage is allocated and the CannonPath_U->Buffer
|
||
|
points to the allocated storage.
|
||
|
|
||
|
Heap - Supplies the heap that should be used to allocate storage from
|
||
|
to store the canonicalized pathname.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
TRUE - The pathname was successfully canonicalized. Storage was
|
||
|
allocated, and the CanonPath_U string is initialized.
|
||
|
|
||
|
FALSE - The pathname was not canonicalized. No Storage was
|
||
|
allocated, and the CanonPath_U string is not initialized. The
|
||
|
'errno' variable is set appropriately in this case.
|
||
|
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
ANSI_STRING AnsiCanonPath;
|
||
|
PANSI_STRING CanonPath_A;
|
||
|
LONG PathNameLength;
|
||
|
char *Source, *Dest, *pch;
|
||
|
PSTRING Prefix;
|
||
|
ULONG UnicodeLength;
|
||
|
NTSTATUS Status;
|
||
|
|
||
|
CanonPath_A = &AnsiCanonPath;
|
||
|
CanonPath_A->Buffer = NULL;
|
||
|
|
||
|
try {
|
||
|
PathNameLength = strlen(PathName);
|
||
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
|
PathNameLength = -1;
|
||
|
}
|
||
|
if (PathNameLength == -1) {
|
||
|
errno = EFAULT;
|
||
|
return FALSE;
|
||
|
}
|
||
|
if (PathNameLength == 0) {
|
||
|
errno = ENOENT;
|
||
|
return FALSE;
|
||
|
}
|
||
|
if (PathNameLength > PATH_MAX) {
|
||
|
errno = ENAMETOOLONG;
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
Source = PathName;
|
||
|
|
||
|
Prefix = SetPrefix(&Source);
|
||
|
|
||
|
CanonPath_A->MaximumLength = (USHORT)(PathNameLength + Prefix->Length + 1);
|
||
|
CanonPath_A->Buffer = RtlAllocateHeap(Heap, 0, CanonPath_A->MaximumLength);
|
||
|
if (NULL == CanonPath_A->Buffer) {
|
||
|
errno = ENOMEM;
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Copy the prefix
|
||
|
//
|
||
|
|
||
|
RtlCopyString(CanonPath_A, Prefix);
|
||
|
|
||
|
Dest = CanonPath_A->Buffer + CanonPath_A->Length;
|
||
|
|
||
|
while ('\0' != *Source) switch (*Source) {
|
||
|
case '/':
|
||
|
// Skip adjacent /'s
|
||
|
|
||
|
if (Dest[-1] != '\\') {
|
||
|
*Dest++ = '\\';
|
||
|
}
|
||
|
|
||
|
while (IS_POSIX_PATH_SEPARATOR(Source)) {
|
||
|
Source++;
|
||
|
}
|
||
|
break;
|
||
|
case '.':
|
||
|
//
|
||
|
// Eat single dots as in "/./". For dot-dot back up one level.
|
||
|
// Any other dot is just a filename character.
|
||
|
//
|
||
|
|
||
|
if (IS_POSIX_DOT(Source)) {
|
||
|
Source++;
|
||
|
break;
|
||
|
}
|
||
|
if (IS_POSIX_DOT_DOT(Source)) {
|
||
|
UNICODE_STRING U;
|
||
|
OBJECT_ATTRIBUTES Obj;
|
||
|
HANDLE FileHandle;
|
||
|
IO_STATUS_BLOCK Iosb;
|
||
|
|
||
|
// back up destination string looking for a \.
|
||
|
|
||
|
do {
|
||
|
Dest--;
|
||
|
} while (*Dest != '\\');
|
||
|
|
||
|
//
|
||
|
// Make sure the directory that we're using dot-dot
|
||
|
// in actually exists.
|
||
|
//
|
||
|
|
||
|
if (Dest == CanonPath_A->Buffer +
|
||
|
PdxDirectoryPrefix.PsxRoot.Length) {
|
||
|
*(Dest + 1) = '\000';
|
||
|
} else {
|
||
|
*Dest = '\000';
|
||
|
}
|
||
|
|
||
|
CanonPath_A->Length = (USHORT)strlen(CanonPath_A->Buffer);
|
||
|
Status = RtlAnsiStringToUnicodeString(&U, CanonPath_A,
|
||
|
TRUE);
|
||
|
if (!NT_SUCCESS(Status)) {
|
||
|
RtlFreeHeap(Heap, 0, CanonPath_A->Buffer);
|
||
|
errno = ENOMEM;
|
||
|
return FALSE;
|
||
|
}
|
||
|
InitializeObjectAttributes(&Obj, &U, 0, NULL, 0);
|
||
|
Status = NtOpenFile(&FileHandle, SYNCHRONIZE, &Obj,
|
||
|
&Iosb,
|
||
|
FILE_SHARE_READ|FILE_SHARE_WRITE|
|
||
|
FILE_SHARE_DELETE,
|
||
|
FILE_SYNCHRONOUS_IO_NONALERT |
|
||
|
FILE_DIRECTORY_FILE);
|
||
|
RtlFreeUnicodeString(&U);
|
||
|
if (!NT_SUCCESS(Status)) {
|
||
|
RtlFreeHeap(Heap, 0, CanonPath_A->Buffer);
|
||
|
errno = PdxStatusToErrno(Status);
|
||
|
return FALSE;
|
||
|
}
|
||
|
NtClose(FileHandle);
|
||
|
|
||
|
//
|
||
|
// Back up to previous component: "\a\b\c\" to "\a\b\".
|
||
|
// But if we come to the root, we stay there.
|
||
|
//
|
||
|
|
||
|
do {
|
||
|
if (Dest == CanonPath_A->Buffer +
|
||
|
PdxDirectoryPrefix.PsxRoot.Length) {
|
||
|
*Dest++ = '\\';
|
||
|
break;
|
||
|
}
|
||
|
Dest--;
|
||
|
} while (*Dest != '\\');
|
||
|
|
||
|
// Advance source past the dot-dot
|
||
|
|
||
|
Source += 2;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// This dot is just a filename character.
|
||
|
//FALLTHROUGH
|
||
|
|
||
|
default:
|
||
|
//
|
||
|
// Copy a pathname component. If the pathname component
|
||
|
// is too long, return ENAMETOOLONG. Note that using a
|
||
|
// constant NAME_MAX is bogus, since it could be different
|
||
|
// for different filesystems.
|
||
|
//
|
||
|
|
||
|
pch = strchr(Source, '/');
|
||
|
if (NULL == pch) {
|
||
|
// this is the last component in the path.
|
||
|
|
||
|
if (strlen(Source) > NAME_MAX) {
|
||
|
errno = ENAMETOOLONG;
|
||
|
RtlFreeHeap(Heap, 0, CanonPath_A->Buffer);
|
||
|
return FALSE;
|
||
|
}
|
||
|
} else {
|
||
|
if (pch - Source > NAME_MAX) {
|
||
|
errno = ENAMETOOLONG;
|
||
|
RtlFreeHeap(Heap, 0, CanonPath_A->Buffer);
|
||
|
return FALSE;
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
while (*Source != '\0' && *Source != '/') {
|
||
|
*Dest++ = *Source++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Make sure that we never give back "/DosDevices/C:" ... the
|
||
|
// Object Manager doesn't deal with that, we need a trailing
|
||
|
// slash.
|
||
|
//
|
||
|
|
||
|
if (Dest == CanonPath_A->Buffer + PdxDirectoryPrefix.PsxRoot.Length) {
|
||
|
if (Dest[-1] != '\\') {
|
||
|
*Dest++ = '\\';
|
||
|
}
|
||
|
}
|
||
|
|
||
|
CanonPath_A->Length = (USHORT)((ULONG_PTR)Dest - (ULONG_PTR)CanonPath_A->Buffer);
|
||
|
CanonPath_A->Buffer[CanonPath_A->Length] = '\0';
|
||
|
|
||
|
// Convert ansi pathname to unicode - use internal heap for Buffer
|
||
|
|
||
|
UnicodeLength = RtlAnsiStringToUnicodeSize(CanonPath_A);
|
||
|
CanonPath_U->MaximumLength = (USHORT)UnicodeLength;
|
||
|
CanonPath_U->Buffer = RtlAllocateHeap(Heap, 0, UnicodeLength);
|
||
|
|
||
|
if (NULL == CanonPath_U->Buffer) {
|
||
|
RtlFreeHeap(Heap, 0, CanonPath_A->Buffer);
|
||
|
errno = ENOMEM;
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
Status = RtlAnsiStringToUnicodeString(CanonPath_U, CanonPath_A, FALSE);
|
||
|
ASSERT(NT_SUCCESS(Status));
|
||
|
|
||
|
RtlFreeHeap(Heap, 0, CanonPath_A->Buffer);
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
#if DBG
|
||
|
VOID
|
||
|
DumpNames(
|
||
|
IN PSZ PathName,
|
||
|
IN PSTRING CanonPath_A
|
||
|
)
|
||
|
{
|
||
|
USHORT i;
|
||
|
PSZ p;
|
||
|
|
||
|
KdPrint(("Input Path: \"%s\"\n",PathName));
|
||
|
KdPrint(("Output Path: \"%Z\"\n", CanonPath_A));
|
||
|
}
|
||
|
|
||
|
#endif //DBG
|