712 lines
18 KiB
C
712 lines
18 KiB
C
/*++
|
|
|
|
Copyright (c) 1998 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
dir.c
|
|
|
|
Abstract:
|
|
|
|
This module implements the dir commands.
|
|
|
|
Author:
|
|
|
|
Wesley Witt (wesw) 21-Oct-1998
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "cmdcons.h"
|
|
#pragma hdrstop
|
|
|
|
//
|
|
// global external variables
|
|
//
|
|
extern LARGE_INTEGER glBias;
|
|
|
|
typedef struct _DIR_STATS {
|
|
unsigned FileCount;
|
|
LONGLONG TotalSize;
|
|
RcFileSystemType fsType;
|
|
} DIR_STATS, *PDIR_STATS;
|
|
|
|
BOOLEAN
|
|
pRcDirEnumProc(
|
|
IN LPCWSTR Directory,
|
|
IN PFILE_BOTH_DIR_INFORMATION FileInfo,
|
|
OUT NTSTATUS *Status,
|
|
IN PDIR_STATS DirStats
|
|
);
|
|
|
|
NTSTATUS
|
|
SpSystemTimeToLocalTime (
|
|
IN PLARGE_INTEGER SystemTime,
|
|
OUT PLARGE_INTEGER LocalTime
|
|
);
|
|
|
|
NTSTATUS
|
|
SpLocalTimeToSystemTime (
|
|
IN PLARGE_INTEGER LocalTime,
|
|
OUT PLARGE_INTEGER SystemTime
|
|
);
|
|
|
|
|
|
|
|
|
|
ULONG
|
|
RcCmdDir(
|
|
IN PTOKENIZED_LINE TokenizedLine
|
|
)
|
|
{
|
|
LPCWSTR Dir;
|
|
LPWSTR Path;
|
|
LPWSTR DosPath;
|
|
LPWSTR p;
|
|
NTSTATUS Status;
|
|
WCHAR Drive[4];
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
UNICODE_STRING UnicodeString;
|
|
HANDLE Handle;
|
|
OBJECT_ATTRIBUTES Obja;
|
|
DIR_STATS DirStats;
|
|
ULONG u;
|
|
ULONG rc;
|
|
PFILE_FS_VOLUME_INFORMATION VolumeInfo;
|
|
FILE_FS_SIZE_INFORMATION SizeInfo;
|
|
BYTE bfFSInfo[sizeof(FILE_FS_ATTRIBUTE_INFORMATION) +
|
|
(MAX_PATH*2)];
|
|
PFILE_FS_ATTRIBUTE_INFORMATION pFSInfo = 0;
|
|
|
|
|
|
if (RcCmdParseHelp( TokenizedLine, MSG_DIR_HELP )) {
|
|
return 1;
|
|
}
|
|
|
|
//
|
|
// If there's no argument, then we want the current directory.
|
|
//
|
|
Dir = (TokenizedLine->TokenCount == 2)
|
|
? TokenizedLine->Tokens->Next->String
|
|
: L".";
|
|
|
|
//
|
|
// Canonicalize the name once to get a full DOS-style path
|
|
// we can print out, and another time to get the NT-style path
|
|
// we'll use to actually do the work.
|
|
//
|
|
if (!RcFormFullPath(Dir,_CmdConsBlock->TemporaryBuffer,FALSE)) {
|
|
RcMessageOut(MSG_INVALID_PATH);
|
|
return 1;
|
|
}
|
|
DosPath = SpDupStringW(_CmdConsBlock->TemporaryBuffer);
|
|
|
|
if (!RcFormFullPath(Dir,_CmdConsBlock->TemporaryBuffer,TRUE)) {
|
|
RcMessageOut(MSG_INVALID_PATH);
|
|
return 1;
|
|
}
|
|
Path = SpDupStringW(_CmdConsBlock->TemporaryBuffer);
|
|
|
|
//
|
|
// Open up the root directory of the drive so we can query
|
|
// the volume label, serial number, and free space.
|
|
//
|
|
Drive[0] = DosPath[0];
|
|
Drive[1] = L':';
|
|
Drive[2] = L'\\';
|
|
Drive[3] = 0;
|
|
if (!RcFormFullPath(Drive,_CmdConsBlock->TemporaryBuffer,TRUE)) {
|
|
DEBUG_PRINTF(( "couldn't open root of drive!" ));
|
|
RcNtError( STATUS_NO_MEDIA_IN_DEVICE, MSG_NO_MEDIA_IN_DEVICE );
|
|
goto c2;
|
|
}
|
|
|
|
INIT_OBJA(&Obja,&UnicodeString,_CmdConsBlock->TemporaryBuffer);
|
|
|
|
Status = ZwOpenFile(
|
|
&Handle,
|
|
FILE_READ_ATTRIBUTES,
|
|
&Obja,
|
|
&IoStatusBlock,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
FILE_DIRECTORY_FILE
|
|
);
|
|
|
|
pRcEnableMoreMode();
|
|
|
|
if(NT_SUCCESS(Status)) {
|
|
//
|
|
// Get the volume label and serial number.
|
|
//
|
|
VolumeInfo = _CmdConsBlock->TemporaryBuffer;
|
|
|
|
Status = ZwQueryVolumeInformationFile(
|
|
Handle,
|
|
&IoStatusBlock,
|
|
VolumeInfo,
|
|
_CmdConsBlock->TemporaryBufferSize,
|
|
FileFsVolumeInformation
|
|
);
|
|
|
|
if(NT_SUCCESS(Status)) {
|
|
//
|
|
// We can tell the user the volume label and serial number.
|
|
//
|
|
VolumeInfo->VolumeLabel[VolumeInfo->VolumeLabelLength/sizeof(WCHAR)] = 0;
|
|
p = SpDupStringW(VolumeInfo->VolumeLabel);
|
|
u = VolumeInfo->VolumeSerialNumber;
|
|
|
|
RcMessageOut(
|
|
*p ? MSG_DIR_BANNER1a : MSG_DIR_BANNER1b,
|
|
RcToUpper(DosPath[0]),
|
|
p
|
|
);
|
|
|
|
SpMemFree(p);
|
|
|
|
RcMessageOut(MSG_DIR_BANNER2,u >> 16,u & 0xffff);
|
|
}
|
|
|
|
//
|
|
// Get free space value for drive.
|
|
//
|
|
Status = ZwQueryVolumeInformationFile(
|
|
Handle,
|
|
&IoStatusBlock,
|
|
&SizeInfo,
|
|
sizeof(FILE_FS_SIZE_INFORMATION),
|
|
FileFsSizeInformation
|
|
);
|
|
|
|
if(!NT_SUCCESS(Status)) {
|
|
SizeInfo.BytesPerSector = 0;
|
|
}
|
|
|
|
//
|
|
// Get the type of the file system so that we can handle
|
|
// the file times properly (NT stores the date in UTC).
|
|
//
|
|
RtlZeroMemory(bfFSInfo, sizeof(bfFSInfo));
|
|
pFSInfo = (PFILE_FS_ATTRIBUTE_INFORMATION) bfFSInfo;
|
|
|
|
Status = ZwQueryVolumeInformationFile(
|
|
Handle,
|
|
&IoStatusBlock,
|
|
pFSInfo,
|
|
sizeof(bfFSInfo),
|
|
FileFsAttributeInformation);
|
|
|
|
ZwClose(Handle);
|
|
}
|
|
|
|
//
|
|
// Tell the user the full DOS path of the directory.
|
|
//
|
|
RcMessageOut(MSG_DIR_BANNER3,DosPath);
|
|
|
|
//
|
|
// Now go enumerate the directory.
|
|
//
|
|
RtlZeroMemory(&DirStats,sizeof(DIR_STATS));
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
KdPrint(("SPCMDCON:Could not get volume information, Error Code:%lx\n", Status));
|
|
DirStats.fsType = RcUnknown; // assume FAT file system (by default)
|
|
} else {
|
|
if (!wcscmp(pFSInfo->FileSystemName, L"NTFS"))
|
|
DirStats.fsType = RcNTFS;
|
|
else if (!wcscmp(pFSInfo->FileSystemName, L"FAT"))
|
|
DirStats.fsType = RcFAT;
|
|
else if (!wcscmp(pFSInfo->FileSystemName, L"FAT32"))
|
|
DirStats.fsType = RcFAT32;
|
|
else if (!wcscmp(pFSInfo->FileSystemName, L"CDFS"))
|
|
DirStats.fsType = RcCDFS;
|
|
else
|
|
DirStats.fsType = RcUnknown;
|
|
}
|
|
|
|
KdPrint(("SPCMDCON: RcCmdDir detected file system type (%lx)-%ws\n",
|
|
DirStats.fsType, pFSInfo ? pFSInfo->FileSystemName : L"None"));
|
|
|
|
Status = RcEnumerateFiles(Dir,Path,pRcDirEnumProc,&DirStats);
|
|
|
|
pRcDisableMoreMode();
|
|
|
|
if(NT_SUCCESS(Status)) {
|
|
|
|
RcFormat64BitIntForOutput(DirStats.TotalSize,_CmdConsBlock->TemporaryBuffer,FALSE);
|
|
p = SpDupStringW(_CmdConsBlock->TemporaryBuffer);
|
|
RcMessageOut(MSG_DIR_BANNER4,DirStats.FileCount,p);
|
|
SpMemFree(p);
|
|
if(SizeInfo.BytesPerSector) {
|
|
RcFormat64BitIntForOutput(
|
|
SizeInfo.AvailableAllocationUnits.QuadPart * (LONGLONG)SizeInfo.SectorsPerAllocationUnit * (LONGLONG)SizeInfo.BytesPerSector,
|
|
_CmdConsBlock->TemporaryBuffer,
|
|
FALSE
|
|
);
|
|
p = SpDupStringW(_CmdConsBlock->TemporaryBuffer);
|
|
RcMessageOut(MSG_DIR_BANNER5,p);
|
|
SpMemFree(p);
|
|
}
|
|
} else {
|
|
RcNtError(Status,MSG_FILE_ENUM_ERROR);
|
|
}
|
|
|
|
c2:
|
|
SpMemFree(Path);
|
|
SpMemFree(DosPath);
|
|
return 1;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
pRcDirEnumProc(
|
|
IN LPCWSTR Directory,
|
|
IN PFILE_BOTH_DIR_INFORMATION FileInfo,
|
|
OUT NTSTATUS *Status,
|
|
IN PDIR_STATS DirStats
|
|
)
|
|
{
|
|
WCHAR LineOut[50];
|
|
WCHAR *p;
|
|
NTSTATUS timeStatus;
|
|
LARGE_INTEGER *pLastWriteTime = 0;
|
|
LARGE_INTEGER lastWriteTime;
|
|
LARGE_INTEGER timeBias;
|
|
TIME_FIELDS timeFields;
|
|
TIME_ZONE_INFORMATION timeZone;
|
|
|
|
UNREFERENCED_PARAMETER(Directory);
|
|
|
|
DirStats->FileCount++;
|
|
DirStats->TotalSize += FileInfo->EndOfFile.QuadPart;
|
|
lastWriteTime = FileInfo->LastWriteTime;
|
|
|
|
//
|
|
// Convert the time into local time from UTC if the file
|
|
// system is NTFS
|
|
//
|
|
switch(DirStats->fsType) {
|
|
case RcNTFS:
|
|
case RcCDFS:
|
|
// localtime = UTC - bias
|
|
lastWriteTime.QuadPart -= glBias.QuadPart;
|
|
break;
|
|
|
|
case RcFAT:
|
|
case RcFAT32:
|
|
default:
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Format the date and time, which go first.
|
|
//
|
|
RcFormatDateTime(&lastWriteTime,LineOut);
|
|
RcTextOut(LineOut);
|
|
|
|
//
|
|
// 2 spaces for separation
|
|
//
|
|
RcTextOut(L" ");
|
|
|
|
//
|
|
// File attributes.
|
|
//
|
|
p = LineOut;
|
|
if(FileInfo->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
|
*p++ = L'd';
|
|
} else {
|
|
*p++ = L'-';
|
|
}
|
|
if(FileInfo->FileAttributes & FILE_ATTRIBUTE_ARCHIVE) {
|
|
*p++ = L'a';
|
|
} else {
|
|
*p++ = L'-';
|
|
}
|
|
if(FileInfo->FileAttributes & FILE_ATTRIBUTE_READONLY) {
|
|
*p++ = L'r';
|
|
} else {
|
|
*p++ = L'-';
|
|
}
|
|
if(FileInfo->FileAttributes & FILE_ATTRIBUTE_HIDDEN) {
|
|
*p++ = L'h';
|
|
} else {
|
|
*p++ = L'-';
|
|
}
|
|
if(FileInfo->FileAttributes & FILE_ATTRIBUTE_SYSTEM) {
|
|
*p++ = L's';
|
|
} else {
|
|
*p++ = L'-';
|
|
}
|
|
if(FileInfo->FileAttributes & FILE_ATTRIBUTE_COMPRESSED) {
|
|
*p++ = L'c';
|
|
} else {
|
|
*p++ = L'-';
|
|
}
|
|
if(FileInfo->FileAttributes & FILE_ATTRIBUTE_ENCRYPTED) {
|
|
*p++ = L'e';
|
|
} else {
|
|
*p++ = L'-';
|
|
}
|
|
if(FileInfo->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
|
|
*p++ = L'p';
|
|
} else {
|
|
*p++ = L'-';
|
|
}
|
|
|
|
*p = 0;
|
|
|
|
RcTextOut(LineOut);
|
|
|
|
//
|
|
// 2 spaces for separation
|
|
//
|
|
RcTextOut(L" ");
|
|
|
|
//
|
|
// Now, put the size in there. Right justified and space padded
|
|
// up to 8 chars. Oterwise unjustified or padded.
|
|
//
|
|
RcFormat64BitIntForOutput(FileInfo->EndOfFile.QuadPart,LineOut,TRUE);
|
|
if(FileInfo->EndOfFile.QuadPart > 99999999i64) {
|
|
RcTextOut(LineOut);
|
|
} else {
|
|
RcTextOut(LineOut+11); // outputs 8 chars
|
|
}
|
|
|
|
RcTextOut(L" ");
|
|
|
|
//
|
|
// Finally, put the filename on the line. Need to 0-terminate it first.
|
|
//
|
|
wcsncpy(_CmdConsBlock->TemporaryBuffer,FileInfo->FileName,FileInfo->FileNameLength);
|
|
((WCHAR *)_CmdConsBlock->TemporaryBuffer)[FileInfo->FileNameLength] = 0;
|
|
|
|
*Status = STATUS_SUCCESS;
|
|
return((BOOLEAN)(RcTextOut(_CmdConsBlock->TemporaryBuffer) && RcTextOut(L"\r\n")));
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
RcEnumerateFiles(
|
|
IN LPCWSTR OriginalPathSpec,
|
|
IN LPCWSTR FullyQualifiedPathSpec,
|
|
IN PENUMFILESCB Callback,
|
|
IN PVOID CallerData
|
|
)
|
|
{
|
|
OBJECT_ATTRIBUTES Obja;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
HANDLE Handle;
|
|
UNICODE_STRING UnicodeString;
|
|
NTSTATUS Status;
|
|
BOOLEAN b;
|
|
WCHAR *p;
|
|
WCHAR *LastComponent = NULL;
|
|
PFILE_BOTH_DIR_INFORMATION DirectoryInfo;
|
|
unsigned u;
|
|
WCHAR *NameChar;
|
|
BOOLEAN EndsInDot;
|
|
WCHAR *DirectoryPart;
|
|
|
|
//
|
|
// Determine whether the original path spec ends with a .
|
|
// This is used below to get around a problem with specifying
|
|
// *. as a search specifier.
|
|
//
|
|
u = wcslen(OriginalPathSpec);
|
|
if(u && (OriginalPathSpec[u-1] == L'.')) {
|
|
EndsInDot = TRUE;
|
|
} else {
|
|
EndsInDot = FALSE;
|
|
}
|
|
|
|
//
|
|
// Determine whether the given path points at a directory.
|
|
// If so, we'll concatenate \* on the end and fall through
|
|
// to the common case.
|
|
//
|
|
b = FALSE;
|
|
|
|
INIT_OBJA(&Obja,&UnicodeString,FullyQualifiedPathSpec);
|
|
|
|
Status = ZwOpenFile(
|
|
&Handle,
|
|
FILE_READ_ATTRIBUTES,
|
|
&Obja,
|
|
&IoStatusBlock,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
FILE_DIRECTORY_FILE
|
|
);
|
|
|
|
if(NT_SUCCESS(Status)) {
|
|
ZwClose(Handle);
|
|
b = TRUE;
|
|
}
|
|
|
|
if(b) {
|
|
//
|
|
// Directory, append \*.
|
|
//
|
|
p = SpMemAlloc((wcslen(FullyQualifiedPathSpec)+3)*sizeof(WCHAR));
|
|
|
|
if (p) {
|
|
wcscpy(p,FullyQualifiedPathSpec);
|
|
SpConcatenatePaths(p,L"*");
|
|
EndsInDot = FALSE;
|
|
}
|
|
} else {
|
|
//
|
|
// Not directory, pass as-is. Note that this could be an actual
|
|
// file, or a wild-card spec.
|
|
//
|
|
p = SpDupStringW((PVOID)FullyQualifiedPathSpec);
|
|
}
|
|
|
|
//
|
|
// Now trim back the path/file specification so we can open the containing
|
|
// directory for enumeration.
|
|
//
|
|
if (p) {
|
|
LastComponent = wcsrchr(p,L'\\');
|
|
} else {
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
if (LastComponent) {
|
|
*LastComponent++ = 0;
|
|
}
|
|
|
|
DirectoryPart = SpMemAlloc((wcslen(p)+2)*sizeof(WCHAR));
|
|
wcscpy(DirectoryPart,p);
|
|
wcscat(DirectoryPart,L"\\");
|
|
INIT_OBJA(&Obja,&UnicodeString,p);
|
|
|
|
if (LastComponent) {
|
|
LastComponent[-1] = L'\\';
|
|
}
|
|
|
|
UnicodeString.Length += sizeof(WCHAR);
|
|
|
|
Status = ZwOpenFile(
|
|
&Handle,
|
|
FILE_LIST_DIRECTORY | SYNCHRONIZE,
|
|
&Obja,
|
|
&IoStatusBlock,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT
|
|
);
|
|
|
|
if(!NT_SUCCESS(Status)) {
|
|
SpMemFree(p);
|
|
SpMemFree(DirectoryPart);
|
|
return(Status);
|
|
}
|
|
|
|
RtlInitUnicodeString(&UnicodeString,LastComponent);
|
|
|
|
//
|
|
// The following code is adapted from the implementation for
|
|
// the FindFirstFile Win32 API, and provides additional DOS-like
|
|
// wildcard matching semantics.
|
|
//
|
|
// Special case *.* to * since it is so common. Otherwise transmogrify
|
|
// the input name according to the following rules:
|
|
//
|
|
// - Change all ? to DOS_QM
|
|
// - Change all . followed by ? or * to DOS_DOT
|
|
// - Change all * followed by a . into DOS_STAR
|
|
//
|
|
// These transmogrifications are all done in place.
|
|
//
|
|
if(!wcscmp(LastComponent,L"*.*")) {
|
|
|
|
UnicodeString.Length = sizeof(WCHAR); // trim down to just *
|
|
|
|
} else {
|
|
|
|
for(u=0, NameChar=UnicodeString.Buffer;
|
|
u < (UnicodeString.Length/sizeof(WCHAR));
|
|
u++, NameChar++) {
|
|
|
|
if(u && (*NameChar == L'.') && (*(NameChar - 1) == L'*')) {
|
|
|
|
*(NameChar-1) = DOS_STAR;
|
|
}
|
|
|
|
if((*NameChar == L'?') || (*NameChar == L'*')) {
|
|
|
|
if(*NameChar == L'?') {
|
|
*NameChar = DOS_QM;
|
|
}
|
|
|
|
if(u && (*(NameChar-1) == L'.')) {
|
|
*(NameChar-1) = DOS_DOT;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(EndsInDot && (*(NameChar - 1) == L'*')) {
|
|
*(NameChar-1) = DOS_STAR;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Finally, iterate the directory.
|
|
//
|
|
|
|
#define DIRINFO_BUFFER_SIZE ((2*MAX_PATH) + sizeof(FILE_BOTH_DIR_INFORMATION))
|
|
DirectoryInfo = SpMemAlloc(DIRINFO_BUFFER_SIZE);
|
|
|
|
b = TRUE;
|
|
|
|
while(TRUE) {
|
|
Status = ZwQueryDirectoryFile(
|
|
Handle,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&IoStatusBlock,
|
|
DirectoryInfo,
|
|
DIRINFO_BUFFER_SIZE,
|
|
FileBothDirectoryInformation,
|
|
TRUE,
|
|
&UnicodeString,
|
|
b
|
|
);
|
|
|
|
b = FALSE;
|
|
|
|
//
|
|
// Check termination condition
|
|
//
|
|
if(Status == STATUS_NO_MORE_FILES) {
|
|
Status = STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
|
|
if(!NT_SUCCESS(Status)) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// OK, nul-terminate filename and pass info to callback.
|
|
//
|
|
DirectoryInfo->FileName[DirectoryInfo->FileNameLength/sizeof(WCHAR)] = 0;
|
|
if(!Callback(DirectoryPart,DirectoryInfo,&Status,CallerData)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
ZwClose(Handle);
|
|
SpMemFree(DirectoryPart);
|
|
SpMemFree(DirectoryInfo);
|
|
SpMemFree(p);
|
|
return(Status);
|
|
}
|
|
|
|
|
|
VOID
|
|
RcFormat64BitIntForOutput(
|
|
IN LONGLONG n,
|
|
OUT LPWSTR Output,
|
|
IN BOOLEAN RightJustify
|
|
)
|
|
{
|
|
WCHAR *p;
|
|
LONGLONG d;
|
|
BOOLEAN b;
|
|
WCHAR c;
|
|
|
|
//
|
|
// Max signed 64-bit integer is 9223372036854775807 (19 digits).
|
|
// The result will be space padded to the left so it's right-justified
|
|
// if that flag is set. Otherwise it's just a plain 0-terminated string.
|
|
//
|
|
p = Output;
|
|
d = 1000000000000000000i64;
|
|
b = FALSE;
|
|
do {
|
|
c = (WCHAR)((n / d) % 10) + L'0';
|
|
if(c == L'0') {
|
|
if(!b && (d != 1)) {
|
|
c = RightJustify ? L' ' : 0;
|
|
}
|
|
} else {
|
|
b = TRUE;
|
|
}
|
|
if(c) {
|
|
*p++ = c;
|
|
}
|
|
} while(d /= 10);
|
|
*p = 0;
|
|
}
|
|
|
|
//
|
|
// This time conversion APIs should be moved to setupdd.sys
|
|
// if more modules need this
|
|
//
|
|
NTSTATUS
|
|
SpSystemTimeToLocalTime (
|
|
IN PLARGE_INTEGER SystemTime,
|
|
OUT PLARGE_INTEGER LocalTime
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
SYSTEM_TIMEOFDAY_INFORMATION TimeOfDay;
|
|
|
|
Status = ZwQuerySystemInformation(
|
|
SystemTimeOfDayInformation,
|
|
&TimeOfDay,
|
|
sizeof(TimeOfDay),
|
|
NULL
|
|
);
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// LocalTime = SystemTime - TimeZoneBias
|
|
//
|
|
LocalTime->QuadPart = SystemTime->QuadPart -
|
|
TimeOfDay.TimeZoneBias.QuadPart;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// This time conversion APIs should be moved to setupdd.sys
|
|
// if more modules need this
|
|
//
|
|
NTSTATUS
|
|
SpLocalTimeToSystemTime (
|
|
IN PLARGE_INTEGER LocalTime,
|
|
OUT PLARGE_INTEGER SystemTime
|
|
)
|
|
{
|
|
|
|
NTSTATUS Status;
|
|
SYSTEM_TIMEOFDAY_INFORMATION TimeOfDay;
|
|
|
|
Status = ZwQuerySystemInformation(
|
|
SystemTimeOfDayInformation,
|
|
&TimeOfDay,
|
|
sizeof(TimeOfDay),
|
|
NULL
|
|
);
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// SystemTime = LocalTime + TimeZoneBias
|
|
//
|
|
SystemTime->QuadPart = LocalTime->QuadPart +
|
|
TimeOfDay.TimeZoneBias.QuadPart;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|