695 lines
17 KiB
C
695 lines
17 KiB
C
/*++
|
||
|
||
Copyright (c) 1998 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
util.c
|
||
|
||
Abstract:
|
||
|
||
This module implements all utility functions.
|
||
|
||
Author:
|
||
|
||
Wesley Witt (wesw) 21-Oct-1998
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "cmdcons.h"
|
||
#pragma hdrstop
|
||
|
||
|
||
#include "remboot.h"
|
||
|
||
HANDLE NetUseHandles[26] = { NULL };
|
||
BOOLEAN RdrIsInKernelMode = FALSE;
|
||
|
||
RC_ALLOWED_DIRECTORY AllowedDirs[] = {
|
||
{ FALSE, L"$WIN_NT$.~BT" },
|
||
{ FALSE, L"$WIN_NT$.~LS" },
|
||
{ FALSE, L"CMDCONS" },
|
||
{ TRUE, L"SYSTEM VOLUME INFORMATION" }
|
||
};
|
||
|
||
BOOLEAN
|
||
RcIsPathNameAllowed(
|
||
IN LPCWSTR FullPath,
|
||
IN BOOLEAN RemovableMediaOk,
|
||
IN BOOLEAN Mkdir
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine verifies that the specified path name is
|
||
allowed based on the security context that the console
|
||
user is logged into.
|
||
|
||
Arguments:
|
||
|
||
FullPath - specifies the full path to be verified.
|
||
|
||
Return Value:
|
||
|
||
FALSE if failure, indicating the path is not allowed.
|
||
TRUE otherwise.
|
||
|
||
--*/
|
||
|
||
{
|
||
WCHAR TempBuf[MAX_PATH*2];
|
||
UNICODE_STRING UnicodeString;
|
||
OBJECT_ATTRIBUTES Obja;
|
||
HANDLE Handle;
|
||
IO_STATUS_BLOCK IoStatusBlock;
|
||
NTSTATUS Status;
|
||
BOOL isDirectory = TRUE;
|
||
|
||
//
|
||
// should we bypass security?
|
||
//
|
||
|
||
if (AllowAllPaths) {
|
||
return TRUE;
|
||
}
|
||
|
||
//
|
||
// some special processing for dos paths
|
||
// we must make sure that only the root and %systemdir% are allowed.
|
||
//
|
||
|
||
if (FullPath[1] == L':' && FullPath[2] == L'\\' && FullPath[3] == 0) {
|
||
//
|
||
// root directory is ok.
|
||
//
|
||
return TRUE;
|
||
}
|
||
|
||
SpStringToUpper((PWSTR)FullPath);
|
||
|
||
if (!RcGetNTFileName((PWSTR)FullPath,TempBuf))
|
||
return FALSE;
|
||
|
||
INIT_OBJA(&Obja,&UnicodeString,TempBuf);
|
||
|
||
Status = ZwOpenFile(
|
||
&Handle,
|
||
FILE_READ_ATTRIBUTES,
|
||
&Obja,
|
||
&IoStatusBlock,
|
||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||
FILE_DIRECTORY_FILE
|
||
);
|
||
if( !NT_SUCCESS(Status) ) {
|
||
isDirectory = FALSE;
|
||
|
||
} else {
|
||
ZwClose( Handle );
|
||
}
|
||
|
||
if (isDirectory == FALSE && wcsrchr( FullPath, L'\\' ) == ( &FullPath[2] )) {
|
||
//
|
||
// if the cannonicalized path has only one slash the user is trying to do something
|
||
// to the files in the root, which we allow.
|
||
//
|
||
// however we do not allow users to mess with directories at the root
|
||
//
|
||
if (Mkdir) {
|
||
return FALSE;
|
||
} else {
|
||
return TRUE;
|
||
}
|
||
}
|
||
|
||
ASSERT(SelectedInstall != NULL);
|
||
|
||
if(SelectedInstall != NULL) {
|
||
//
|
||
// Get the length of the first element in the path
|
||
//
|
||
PCWSTR sz;
|
||
DWORD Len;
|
||
DWORD i;
|
||
WCHAR SelectedInstallDrive;
|
||
|
||
sz = wcschr(FullPath + 3, L'\\');
|
||
|
||
if(NULL == sz) {
|
||
sz = FullPath + wcslen(FullPath);
|
||
}
|
||
|
||
Len = (DWORD) ((sz - FullPath) - 3);
|
||
SelectedInstallDrive = RcToUpper(SelectedInstall->DriveLetter);
|
||
|
||
//
|
||
// See if path begins with the install path
|
||
//
|
||
if(FullPath[0] == SelectedInstallDrive && 0 == _wcsnicmp(FullPath + 3, SelectedInstall->Path, Len)) {
|
||
return TRUE;
|
||
}
|
||
|
||
//
|
||
// See if the path begins with an allowed dir
|
||
//
|
||
for(i = 0; i < sizeof(AllowedDirs) / sizeof(AllowedDirs[0]); ++i) {
|
||
if((!AllowedDirs[i].MustBeOnInstallDrive || FullPath[0] == SelectedInstallDrive) &&
|
||
0 == _wcsnicmp(FullPath + 3, AllowedDirs[i].Directory, Len)) {
|
||
return TRUE;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (RcIsFileOnRemovableMedia(TempBuf) == STATUS_SUCCESS) {
|
||
if (RemovableMediaOk) {
|
||
return TRUE;
|
||
}
|
||
}
|
||
|
||
if (RcIsNetworkDrive(TempBuf) == STATUS_SUCCESS) {
|
||
//
|
||
// Context that was used for connection will do appropriate security checking.
|
||
//
|
||
return TRUE;
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
RcDoesPathHaveWildCards(
|
||
IN LPCWSTR FullPath
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine verifies that the specified path name is
|
||
allowed based on the security context that the console
|
||
user is logged into.
|
||
|
||
Arguments:
|
||
|
||
FullPath - specifies the full path to be verified.
|
||
|
||
Return Value:
|
||
|
||
FALSE if failure, indicating the path is not allowed.
|
||
TRUE otherwise.
|
||
|
||
--*/
|
||
|
||
{
|
||
if (wcsrchr( FullPath, L'*' )) {
|
||
return TRUE;
|
||
}
|
||
|
||
if (wcsrchr( FullPath, L'?' )) {
|
||
return TRUE;
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
NTSTATUS
|
||
RcIsNetworkDrive(
|
||
IN PWSTR FileName
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine returns if the FileName given is a network path.
|
||
|
||
Arguments:
|
||
|
||
FileName - specifies the full path to be checked.
|
||
|
||
Return Value:
|
||
|
||
Any other than STATUS_SUCCESS if failure, indicating the path is not on the network,
|
||
STATUS_SUCCESS otherwise.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
FILE_FS_DEVICE_INFORMATION DeviceInfo;
|
||
PWSTR BaseNtName;
|
||
|
||
if (wcsncmp(FileName, L"\\DosDevice", wcslen(L"\\DosDevice")) == 0) {
|
||
Status = GetDriveLetterLinkTarget( FileName, &BaseNtName );
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
return Status;
|
||
}
|
||
} else {
|
||
BaseNtName = FileName;
|
||
}
|
||
|
||
Status = pRcGetDeviceInfo( BaseNtName, &DeviceInfo );
|
||
if(NT_SUCCESS(Status)) {
|
||
if (DeviceInfo.DeviceType != FILE_DEVICE_NETWORK_FILE_SYSTEM) {
|
||
Status = STATUS_NO_MEDIA;
|
||
}
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
RcDoNetUse(
|
||
PWSTR Share,
|
||
PWSTR User,
|
||
PWSTR Password,
|
||
PWSTR Drive
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine attempts to make a connection using the redirector to the remote server.
|
||
|
||
Arguments:
|
||
|
||
Share - A string of the form "\\server\share"
|
||
|
||
User - A string of the form "domain\user"
|
||
|
||
Password - A string containing the password information.
|
||
|
||
Drive - Filled in with a string of the form "X", where X is the drive letter the share
|
||
has been mapped to.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS if successful, indicating Drive contains the mapped drive letter,
|
||
otherwise the appropriate error code.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
PWSTR NtDeviceName;
|
||
ULONG ShareLength;
|
||
WCHAR DriveLetter;
|
||
WCHAR temporaryBuffer[128];
|
||
PWCHAR Temp, Temp2;
|
||
HANDLE Handle;
|
||
ULONG EaBufferLength;
|
||
PWSTR UserName;
|
||
PWSTR DomainName;
|
||
PVOID EaBuffer;
|
||
UNICODE_STRING UnicodeString;
|
||
UNICODE_STRING UnicodeString2;
|
||
OBJECT_ATTRIBUTES ObjectAttributes;
|
||
IO_STATUS_BLOCK IoStatusBlock;
|
||
PFILE_FULL_EA_INFORMATION FullEaInfo;
|
||
|
||
//
|
||
// Switch the redirector to kernel-mode security if it is not.
|
||
//
|
||
if (!RdrIsInKernelMode) {
|
||
Status = PutRdrInKernelMode();
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
return Status;
|
||
}
|
||
|
||
RdrIsInKernelMode = TRUE;
|
||
}
|
||
|
||
//
|
||
// Search for an open drive letter, starting at D: and working up.
|
||
//
|
||
wcscpy(temporaryBuffer, L"\\DosDevices\\D:");
|
||
Temp = wcsstr(temporaryBuffer, L"D:");
|
||
|
||
for (DriveLetter = L'D'; (Temp && (DriveLetter <= L'Z')); DriveLetter++) {
|
||
*Temp = DriveLetter;
|
||
|
||
Status = GetDriveLetterLinkTarget( temporaryBuffer, &Temp2 );
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (DriveLetter > L'Z') {
|
||
return STATUS_OBJECT_NAME_INVALID;
|
||
}
|
||
|
||
//
|
||
// Build the NT device name.
|
||
//
|
||
ShareLength = wcslen(Share);
|
||
NtDeviceName = SpMemAlloc(ShareLength * sizeof(WCHAR) + sizeof(L"\\Device\\LanmanRedirector\\;X:0"));
|
||
if (NtDeviceName == NULL) {
|
||
return STATUS_NO_MEMORY;
|
||
}
|
||
wcscpy(NtDeviceName, L"\\Device\\LanmanRedirector\\;");
|
||
temporaryBuffer[0] = DriveLetter;
|
||
temporaryBuffer[1] = UNICODE_NULL;
|
||
wcscat(NtDeviceName, temporaryBuffer);
|
||
wcscat(NtDeviceName, L":0");
|
||
wcscat(NtDeviceName, Share + 1);
|
||
|
||
//
|
||
// Chop the username and domainname into individual values.
|
||
//
|
||
wcscpy(temporaryBuffer, User);
|
||
DomainName = temporaryBuffer;
|
||
UserName = wcsstr(temporaryBuffer, L"\\");
|
||
|
||
if (UserName == NULL) {
|
||
SpMemFree(NtDeviceName);
|
||
return STATUS_OBJECT_NAME_INVALID;
|
||
}
|
||
*UserName = UNICODE_NULL;
|
||
UserName++;
|
||
|
||
//
|
||
// Create buffer with user credentials
|
||
//
|
||
|
||
EaBufferLength = FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName[0]);
|
||
EaBufferLength += sizeof(EA_NAME_DOMAIN);
|
||
EaBufferLength += (wcslen(DomainName) * sizeof(WCHAR));
|
||
if (EaBufferLength & (sizeof(ULONG) - 1)) {
|
||
//
|
||
// Long align the next entry
|
||
//
|
||
EaBufferLength += (sizeof(ULONG) - (EaBufferLength & (sizeof(ULONG) - 1)));
|
||
}
|
||
|
||
EaBufferLength += FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName[0]);
|
||
EaBufferLength += sizeof(EA_NAME_USERNAME);
|
||
EaBufferLength += (wcslen(UserName) * sizeof(WCHAR));
|
||
if (EaBufferLength & (sizeof(ULONG) - 1)) {
|
||
//
|
||
// Long align the next entry
|
||
//
|
||
EaBufferLength += (sizeof(ULONG) - (EaBufferLength & (sizeof(ULONG) - 1)));
|
||
}
|
||
|
||
EaBufferLength += FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName[0]);
|
||
EaBufferLength += sizeof(EA_NAME_PASSWORD);
|
||
EaBufferLength += (wcslen(Password) * sizeof(WCHAR));
|
||
|
||
EaBuffer = SpMemAlloc(EaBufferLength);
|
||
if (EaBuffer == NULL) {
|
||
SpMemFree(NtDeviceName);
|
||
return STATUS_NO_MEMORY;
|
||
}
|
||
|
||
FullEaInfo = (PFILE_FULL_EA_INFORMATION)EaBuffer;
|
||
|
||
FullEaInfo->Flags = 0;
|
||
FullEaInfo->EaNameLength = sizeof(EA_NAME_DOMAIN) - 1;
|
||
FullEaInfo->EaValueLength = (wcslen(DomainName)) * sizeof(WCHAR);
|
||
strcpy(&(FullEaInfo->EaName[0]), EA_NAME_DOMAIN);
|
||
memcpy(&(FullEaInfo->EaName[FullEaInfo->EaNameLength + 1]), DomainName, FullEaInfo->EaValueLength);
|
||
FullEaInfo->NextEntryOffset = FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName[0]) +
|
||
FullEaInfo->EaNameLength + 1 +
|
||
FullEaInfo->EaValueLength;
|
||
if (FullEaInfo->NextEntryOffset & (sizeof(ULONG) - 1)) {
|
||
FullEaInfo->NextEntryOffset += (sizeof(ULONG) -
|
||
(FullEaInfo->NextEntryOffset &
|
||
(sizeof(ULONG) - 1)));
|
||
}
|
||
|
||
|
||
FullEaInfo = (PFILE_FULL_EA_INFORMATION)(((char *)FullEaInfo) + FullEaInfo->NextEntryOffset);
|
||
|
||
FullEaInfo->Flags = 0;
|
||
FullEaInfo->EaNameLength = sizeof(EA_NAME_USERNAME) - 1;
|
||
FullEaInfo->EaValueLength = (wcslen(UserName)) * sizeof(WCHAR);
|
||
strcpy(&(FullEaInfo->EaName[0]), EA_NAME_USERNAME);
|
||
memcpy(&(FullEaInfo->EaName[FullEaInfo->EaNameLength + 1]), UserName, FullEaInfo->EaValueLength);
|
||
FullEaInfo->NextEntryOffset = FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName[0]) +
|
||
FullEaInfo->EaNameLength + 1 +
|
||
FullEaInfo->EaValueLength;
|
||
if (FullEaInfo->NextEntryOffset & (sizeof(ULONG) - 1)) {
|
||
FullEaInfo->NextEntryOffset += (sizeof(ULONG) -
|
||
(FullEaInfo->NextEntryOffset &
|
||
(sizeof(ULONG) - 1)));
|
||
}
|
||
|
||
|
||
FullEaInfo = (PFILE_FULL_EA_INFORMATION)(((char *)FullEaInfo) + FullEaInfo->NextEntryOffset);
|
||
|
||
FullEaInfo->Flags = 0;
|
||
FullEaInfo->EaNameLength = sizeof(EA_NAME_PASSWORD) - 1;
|
||
FullEaInfo->EaValueLength = (wcslen(Password)) * sizeof(WCHAR);
|
||
strcpy(&(FullEaInfo->EaName[0]), EA_NAME_PASSWORD);
|
||
memcpy(&(FullEaInfo->EaName[FullEaInfo->EaNameLength + 1]), Password, FullEaInfo->EaValueLength);
|
||
FullEaInfo->NextEntryOffset = 0;
|
||
|
||
//
|
||
// Now make the connection
|
||
//
|
||
RtlInitUnicodeString(&UnicodeString, NtDeviceName);
|
||
InitializeObjectAttributes(&ObjectAttributes,
|
||
&UnicodeString,
|
||
OBJ_CASE_INSENSITIVE,
|
||
NULL,
|
||
NULL
|
||
);
|
||
|
||
Status = ZwCreateFile(&Handle,
|
||
SYNCHRONIZE,
|
||
&ObjectAttributes,
|
||
&IoStatusBlock,
|
||
NULL,
|
||
FILE_ATTRIBUTE_NORMAL,
|
||
FILE_SHARE_READ,
|
||
FILE_OPEN_IF,
|
||
(FILE_CREATE_TREE_CONNECTION | FILE_SYNCHRONOUS_IO_NONALERT),
|
||
EaBuffer,
|
||
EaBufferLength
|
||
);
|
||
|
||
if (NT_SUCCESS(Status) && NT_SUCCESS(IoStatusBlock.Status)) {
|
||
//
|
||
// Save off the handle so we can close it later if need be
|
||
//
|
||
NetUseHandles[DriveLetter - L'A'] = Handle;
|
||
Drive[0] = DriveLetter;
|
||
Drive[1] = L':';
|
||
Drive[2] = UNICODE_NULL;
|
||
|
||
//
|
||
// Now create a symbolic link from the dos drive letter to the redirector
|
||
//
|
||
wcscpy(temporaryBuffer, L"\\DosDevices\\");
|
||
wcscat(temporaryBuffer, Drive);
|
||
RtlInitUnicodeString(&UnicodeString2, temporaryBuffer);
|
||
|
||
Status = IoCreateSymbolicLink(&UnicodeString2, &UnicodeString);
|
||
if (!NT_SUCCESS(Status)) {
|
||
ZwClose(Handle);
|
||
NetUseHandles[DriveLetter - L'A'] = NULL;
|
||
} else {
|
||
RcAddDrive(DriveLetter);
|
||
}
|
||
|
||
}
|
||
|
||
SpMemFree(NtDeviceName);
|
||
return Status;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
RcNetUnuse(
|
||
PWSTR Drive
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine closes a network connection.
|
||
|
||
Arguments:
|
||
|
||
Drive - A string of the form "X:", where X is the drive letter returned by a previous call to
|
||
NetDoNetUse().
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS if successful, indicating the drive letter has been unmapped,
|
||
otherwise the appropriate error code.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
WCHAR DriveLetter;
|
||
WCHAR temporaryBuffer[128];
|
||
UNICODE_STRING UnicodeString;
|
||
|
||
DriveLetter = *Drive;
|
||
if ((DriveLetter >= L'a') && (DriveLetter <= L'z')) {
|
||
DriveLetter = L'A' + (DriveLetter - L'a');
|
||
}
|
||
|
||
if ((DriveLetter < L'A') | (DriveLetter > L'Z')) {
|
||
return STATUS_OBJECT_NAME_INVALID;
|
||
}
|
||
|
||
if (NetUseHandles[DriveLetter - L'A'] == NULL) {
|
||
return STATUS_OBJECT_NAME_INVALID;
|
||
}
|
||
|
||
if (RcGetCurrentDriveLetter() == DriveLetter) {
|
||
return STATUS_CONNECTION_IN_USE;
|
||
}
|
||
|
||
wcscpy(temporaryBuffer, L"\\DosDevices\\");
|
||
wcscat(temporaryBuffer, Drive);
|
||
RtlInitUnicodeString(&UnicodeString, temporaryBuffer);
|
||
|
||
Status = IoDeleteSymbolicLink(&UnicodeString);
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
ZwClose(NetUseHandles[DriveLetter - L'A']);
|
||
NetUseHandles[DriveLetter - L'A'] = NULL;
|
||
RcRemoveDrive(DriveLetter);
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
PutRdrInKernelMode(
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine IOCTLs down to the rdr to force it to use kernel-mode security.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS if successful, otherwise the appropriate error code.
|
||
|
||
--*/
|
||
|
||
{
|
||
OBJECT_ATTRIBUTES ObjectAttributes;
|
||
UNICODE_STRING UnicodeString;
|
||
IO_STATUS_BLOCK IoStatusBlock;
|
||
NTSTATUS Status;
|
||
HANDLE Handle;
|
||
|
||
RtlInitUnicodeString(&UnicodeString, DD_NFS_DEVICE_NAME_U);
|
||
|
||
InitializeObjectAttributes(
|
||
&ObjectAttributes,
|
||
&UnicodeString,
|
||
OBJ_CASE_INSENSITIVE,
|
||
NULL,
|
||
NULL
|
||
);
|
||
|
||
Status = ZwCreateFile(
|
||
&Handle,
|
||
GENERIC_READ | GENERIC_WRITE,
|
||
&ObjectAttributes,
|
||
&IoStatusBlock,
|
||
NULL,
|
||
0,
|
||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||
FILE_OPEN,
|
||
FILE_SYNCHRONOUS_IO_NONALERT,
|
||
NULL,
|
||
0
|
||
);
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
KdPrint(("SPCMDCON: Unable to open redirector. %x\n", Status));
|
||
return Status;
|
||
}
|
||
|
||
Status = ZwDeviceIoControlFile(Handle,
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
&IoStatusBlock,
|
||
IOCTL_LMMR_USEKERNELSEC,
|
||
NULL,
|
||
0,
|
||
NULL,
|
||
0
|
||
);
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
Status = IoStatusBlock.Status;
|
||
}
|
||
|
||
ZwClose(Handle);
|
||
|
||
return Status;
|
||
}
|
||
|
||
BOOLEAN
|
||
RcIsArc(
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Run time check to determine if this is an Arc system. We attempt to read an
|
||
Arc variable using the Hal. This will fail for Bios based systems.
|
||
|
||
Arguments:
|
||
|
||
None
|
||
|
||
Return Value:
|
||
|
||
True = This is an Arc system.
|
||
|
||
--*/
|
||
|
||
{
|
||
#ifdef _X86_
|
||
ARC_STATUS ArcStatus = EBADF;
|
||
//
|
||
// Get the env var into the temp buffer.
|
||
//
|
||
UCHAR wbuff[130];
|
||
//
|
||
// Get the env var into the temp buffer.
|
||
//
|
||
ArcStatus = HalGetEnvironmentVariable(
|
||
"OsLoader",
|
||
sizeof(wbuff),
|
||
wbuff
|
||
);
|
||
|
||
return((ArcStatus == ESUCCESS) ? TRUE: FALSE);
|
||
#else
|
||
return TRUE;
|
||
#endif
|
||
}
|