You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
602 lines
17 KiB
602 lines
17 KiB
|
|
/*++
|
|
|
|
Copyright (c) 1992-2001 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
diskspace.cpp
|
|
|
|
Abstract:
|
|
|
|
WinDbg Extension Api
|
|
|
|
Environment:
|
|
|
|
Kernel Mode.
|
|
|
|
Revision History:
|
|
|
|
Mike McCracken (mmccr) 09/17/2001
|
|
|
|
Prints out free space for a specifed volume
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
|
|
ULONG GetUNICODE_STRING(ULONG64 ul64Address, CHAR *pszBuffer, ULONG ulSize)
|
|
{
|
|
ULONG64 ul64Buffer = 0;
|
|
ULONG ulLength = 0;
|
|
ULONG ulBytesRead = 0;
|
|
WCHAR wszLocalBuffer[MAX_PATH + 1] = {0};
|
|
|
|
if ((!pszBuffer) || (!ulSize))
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
if (GetFieldValue(ul64Address, "nt!_UNICODE_STRING",
|
|
"Length", ulLength))
|
|
{
|
|
dprintf("Cannot get UNICODE_STRING length!\n");
|
|
return E_FAIL;
|
|
}
|
|
|
|
if (GetFieldValue(ul64Address, "nt!_UNICODE_STRING",
|
|
"Buffer", ul64Buffer))
|
|
{
|
|
dprintf("Cannot get UNICODE_STRING buffer!\n");
|
|
return E_FAIL;
|
|
}
|
|
|
|
if (!ReadMemory(ul64Buffer,
|
|
wszLocalBuffer,
|
|
(ulLength < (MAX_PATH * 2)) ? ulLength : (MAX_PATH * 2),
|
|
&ulBytesRead))
|
|
{
|
|
dprintf("Failed ReadMemory at 0x%I64x!\n", ul64Buffer);
|
|
return E_FAIL;
|
|
}
|
|
|
|
wcstombs(pszBuffer, wszLocalBuffer, ulSize);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
ULONG64 GetObpRootDirectoryObjectAddress()
|
|
{
|
|
ULONG64 ul64Temp = 0;
|
|
ULONG64 ul64ObjRoot = 0;
|
|
|
|
//Get the address of the pointer
|
|
GetExpressionEx("nt!ObpRootDirectoryObject", &ul64Temp, NULL);
|
|
|
|
//Get the value in the pointer
|
|
ReadPtr(ul64Temp, &ul64ObjRoot);
|
|
|
|
return ul64ObjRoot;
|
|
}
|
|
|
|
ULONG GetNumberOfHashBuckets()
|
|
{
|
|
FIELD_INFO Field = {(PUCHAR)"HashBuckets", NULL, 0, DBG_DUMP_FIELD_NO_CALLBACK_REQ , 0, NULL};
|
|
SYM_DUMP_PARAM Sym = {sizeof(SYM_DUMP_PARAM), (PUCHAR)"nt!_OBJECT_DIRECTORY", DBG_DUMP_NO_PRINT, 0,
|
|
NULL, NULL, NULL, sizeof (Field) / sizeof(FIELD_INFO), &Field};
|
|
|
|
// Get the size of the HashBuckets member
|
|
if (Ioctl(IG_DUMP_SYMBOL_INFO,
|
|
&Sym,
|
|
sizeof(Sym)))
|
|
{
|
|
dprintf("Failed to get size of HashBuckets!\n");
|
|
return 0;
|
|
}
|
|
|
|
return (Field.size / (IsPtr64() ? 8 : 4));
|
|
}
|
|
|
|
ULONG GetObjectTypeName(ULONG64 ul64Object, CHAR *pszTypeName, ULONG ulSize)
|
|
{
|
|
|
|
ULONG ulTypeNameOffset = 0;
|
|
ULONG ulObjectBodyOffset = 0;
|
|
ULONG64 ul64Type = 0;
|
|
|
|
// Get the offset of the object body
|
|
if (GetFieldOffset("nt!_OBJECT_HEADER", "Body", &ulObjectBodyOffset))
|
|
{
|
|
dprintf("Cannot get ObjectBody offset!\n");
|
|
return E_FAIL;
|
|
}
|
|
|
|
if (GetFieldValue(ul64Object - ulObjectBodyOffset, "nt!_OBJECT_HEADER", "Type", ul64Type))
|
|
{
|
|
dprintf("Failed to get Type value at 0x%I64x!\n", ul64Object);
|
|
return E_FAIL;
|
|
}
|
|
|
|
if (GetFieldOffset("nt!_OBJECT_TYPE", "Name", &ulTypeNameOffset))
|
|
{
|
|
dprintf("Cannot get TypeName offset!\n");
|
|
return E_FAIL;
|
|
}
|
|
|
|
return GetUNICODE_STRING(ul64Type + ulTypeNameOffset, pszTypeName, ulSize);
|
|
}
|
|
|
|
ULONG64 GetObjectChildDirectory(ULONG64 ul64Object)
|
|
{
|
|
CHAR szTypeName[MAX_PATH + 1] = {0};
|
|
|
|
if (GetObjectTypeName(ul64Object, szTypeName, sizeof(szTypeName)))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (!_stricmp(szTypeName, "Directory"))
|
|
{
|
|
return ul64Object;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
ULONG GetRealDeviceForSymbolicLink(ULONG64 ul64Object, CHAR *pszDevicePath, ULONG ulSize)
|
|
{
|
|
CHAR szTypeName[MAX_PATH + 1] = {0};
|
|
ULONG ulLinkTargetOffset = 0;
|
|
|
|
if (GetObjectTypeName(ul64Object, szTypeName, sizeof(szTypeName)))
|
|
{
|
|
dprintf("Could not get TypeName for object in GetRealDeviceForSymbolicLink!\n");
|
|
return E_FAIL;
|
|
}
|
|
|
|
if (_stricmp(szTypeName, "SymbolicLink"))
|
|
{
|
|
dprintf("Object in GetRealDeviceForSymbolicLink is a %s\n", szTypeName);
|
|
return E_FAIL;
|
|
}
|
|
|
|
// Get the offset of the object body
|
|
if (GetFieldOffset("nt!_OBJECT_SYMBOLIC_LINK", "LinkTarget", &ulLinkTargetOffset))
|
|
{
|
|
dprintf("Cannot get LinkTarget offset!\n");
|
|
return E_FAIL;
|
|
}
|
|
|
|
return GetUNICODE_STRING(ul64Object + ulLinkTargetOffset, pszDevicePath, ulSize);
|
|
}
|
|
|
|
ULONG64 FindObjectByName(CHAR *ObjectPath, ULONG64 ul64StartPoint)
|
|
{
|
|
ULONG64 ul64ObjRoot = ul64StartPoint;
|
|
ULONG64 ul64DirEntry = 0;
|
|
|
|
ULONG ulNumberOfBuckets = 0;
|
|
ULONG ulPointerSize = 4;
|
|
ULONG ulHashOffset = 0;
|
|
ULONG ulObjectBodyOffset = 0;
|
|
ULONG ulNameInfoNameOffset = 0;
|
|
|
|
ULONG i = 0;
|
|
CHAR PathCopy[MAX_PATH + 1] = {0};
|
|
CHAR *PathPtr = ObjectPath;
|
|
|
|
if (!PathPtr)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
while (PathPtr[0] == '\\')
|
|
{
|
|
PathPtr++;
|
|
}
|
|
|
|
//Copy the Path String
|
|
strncpy(PathCopy, PathPtr, min(sizeof(PathCopy)-1, strlen(PathPtr)));
|
|
|
|
if (ul64ObjRoot == 0)
|
|
{
|
|
// Get the address of the Root Directory Object
|
|
ul64ObjRoot = GetObpRootDirectoryObjectAddress();
|
|
if (!ul64ObjRoot)
|
|
{
|
|
dprintf("Could not get the address of the ObpRootDirectoryObject!\n");
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
if (ObjectPath[0] == '\0')
|
|
{
|
|
return ul64ObjRoot;
|
|
}
|
|
|
|
PathPtr = PathCopy;
|
|
while ((PathPtr[0] != '\\') && (PathPtr[0] != '\0'))
|
|
{
|
|
PathPtr++;
|
|
}
|
|
|
|
if (PathPtr[0] == '\\')
|
|
{
|
|
PathPtr[0] = '\0';
|
|
PathPtr++;
|
|
}
|
|
|
|
//Get the offset of the hashbuckets field in the _OBJECT_DIRECTORY structure
|
|
if (GetFieldOffset("nt!_OBJECT_DIRECTORY", "HashBuckets", &ulHashOffset))
|
|
{
|
|
dprintf("Cannot get HashBuckets offset!\n");
|
|
return NULL;
|
|
}
|
|
|
|
// Get the pointer size for our architecture
|
|
ulPointerSize = (IsPtr64() ? 8 : 4);
|
|
|
|
// Try to dynamically determine the number of HashBuckets in the _OBJECT_DIRECTORY structure
|
|
ulNumberOfBuckets = GetNumberOfHashBuckets();
|
|
if (!ulNumberOfBuckets)
|
|
{
|
|
ulNumberOfBuckets = 37; // From ob.h #define NUMBER_HASH_BUCKETS 37
|
|
}
|
|
|
|
// Get the offset of the object body
|
|
if (GetFieldOffset("nt!_OBJECT_HEADER", "Body", &ulObjectBodyOffset))
|
|
{
|
|
dprintf("Cannot get ObjectBody offset!\n");
|
|
return NULL;
|
|
}
|
|
|
|
|
|
// Get the offset of the object body
|
|
if (GetFieldOffset("nt!_OBJECT_HEADER_NAME_INFO", "Name", &ulNameInfoNameOffset))
|
|
{
|
|
dprintf("Cannot get NameInfo Name Offset!\n");
|
|
return NULL;
|
|
}
|
|
|
|
// Iterate through each bucket
|
|
for (i=0; i < ulNumberOfBuckets; i++)
|
|
{
|
|
// Get the address of each _OBJECT_DIRECTORY_ENTRY in the HashBucket array
|
|
if (ReadPointer(ul64ObjRoot + ulHashOffset + (i * ulPointerSize), &ul64DirEntry))
|
|
{
|
|
while (ul64DirEntry)
|
|
{
|
|
ULONG64 ul64Object = 0;
|
|
ULONG64 ul64Header = 0;
|
|
ULONG64 ul64NameInfo = 0;
|
|
ULONG ulNameInfoOffset = 0;
|
|
CHAR szObjName[MAX_PATH + 1] = {0};
|
|
|
|
// Setup to point at our current object \
|
|
// - this is actually a pointer to the body field of the _OBJECT_HEADER Structure
|
|
if (GetFieldValue(ul64DirEntry, "nt!_OBJECT_DIRECTORY_ENTRY", "Object", ul64Object))
|
|
{
|
|
dprintf("Failed to get object value at 0x%I64x!\n", ul64DirEntry);
|
|
break;
|
|
}
|
|
|
|
// Find the header for this object by subtracting the body (current) offset
|
|
ul64Header = ul64Object - ulObjectBodyOffset;
|
|
|
|
// Get the offset from the top of the header of the NameInfoObject
|
|
if (GetFieldValue(ul64Header, "nt!_OBJECT_HEADER", "NameInfoOffset", ulNameInfoOffset))
|
|
{
|
|
dprintf("Failed to get NameInfoOffset pointer from objectheader at 0x%I64x!\n", ul64Header);
|
|
break;
|
|
}
|
|
|
|
// If zero the object does not have one
|
|
if (ulNameInfoOffset == 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
// Set our pointer to point to the _OBJECT_HEADER_NAME_INFO structure
|
|
ul64NameInfo = ul64Header - ulNameInfoOffset;
|
|
|
|
// Get the objects name
|
|
if (GetUNICODE_STRING(ul64NameInfo + ulNameInfoNameOffset, szObjName, sizeof(szObjName)))
|
|
{
|
|
dprintf("Could Not Get Name\n");
|
|
break;
|
|
}
|
|
|
|
if (!_stricmp(PathCopy, szObjName))
|
|
{
|
|
ULONG64 ul64NextDirectory = 0;
|
|
|
|
if (PathPtr[0] == '\0')
|
|
{
|
|
return ul64Object;
|
|
}
|
|
|
|
ul64NextDirectory = GetObjectChildDirectory(ul64Object);
|
|
return FindObjectByName(PathPtr, ul64NextDirectory);
|
|
}
|
|
|
|
//Get the next _OBJECT_DIRECTORY_ENTRY
|
|
if (GetFieldValue(ul64DirEntry, "nt!_OBJECT_DIRECTORY_ENTRY", "ChainLink", ul64DirEntry))
|
|
{
|
|
dprintf("Failed to get next object value at 0x%I64x!\n", ul64Object);
|
|
break;
|
|
}
|
|
|
|
} // while loop
|
|
} // if statement
|
|
} // for loop
|
|
|
|
return NULL;
|
|
}
|
|
|
|
ULONG64 GetVPBPtrFromDeviceObject(ULONG64 ul64DeviceObject)
|
|
{
|
|
ULONG64 ul64VpbPtr = 0;
|
|
|
|
// Get the offset from the top of the header of the NameInfoObject
|
|
if (GetFieldValue(ul64DeviceObject, "nt!_DEVICE_OBJECT", "Vpb", ul64VpbPtr))
|
|
{
|
|
dprintf("Failed to get Vbp pointer from DeviceObject at 0x%I64x!\n", ul64DeviceObject);
|
|
return NULL;
|
|
}
|
|
|
|
return ul64VpbPtr;
|
|
}
|
|
|
|
|
|
ULONG GetDeviceDriverString(ULONG64 ul64Device, CHAR *pszString, ULONG ulSize)
|
|
{
|
|
ULONG ulNameOffset = 0;
|
|
ULONG64 ul64Driver = 0;
|
|
|
|
if (GetFieldValue(ul64Device, "nt!_DEVICE_OBJECT", "DriverObject", ul64Driver))
|
|
{
|
|
dprintf("Failed to get DriverObject from Device pointer at 0x%I64x!\n", ul64Device);
|
|
return E_FAIL;
|
|
}
|
|
|
|
// Get the offset of the DriverName in the _DRIVER_OBJECT
|
|
if (GetFieldOffset("nt!_DRIVER_OBJECT", "DriverName", &ulNameOffset))
|
|
{
|
|
dprintf("Cannot get DriverName offset!\n");
|
|
return E_FAIL;
|
|
}
|
|
|
|
return GetUNICODE_STRING(ul64Driver + ulNameOffset, pszString, ulSize);
|
|
}
|
|
|
|
|
|
VOID OutputData(ULONG ulBytesPerCluster,
|
|
ULONG64 ul64TotalClusters,
|
|
ULONG64 ul64FreeClusters)
|
|
{
|
|
ULONG64 ul64TotalBytes = ul64TotalClusters * ulBytesPerCluster;
|
|
ULONG64 ul64FreeBytes = ul64FreeClusters * ulBytesPerCluster;
|
|
|
|
dprintf(" Cluster Size %u KB\n", ulBytesPerCluster / 1024);
|
|
dprintf(" Total Clusters %I64u KB\n", ul64TotalClusters);
|
|
dprintf(" Free Clusters %I64u KB\n", ul64FreeClusters);
|
|
dprintf(" Total Space %I64u GB (%I64u KB)\n",
|
|
(ul64TotalBytes) / (0x40000000),
|
|
(ul64TotalBytes) / (0x400));
|
|
|
|
if (ul64FreeBytes > 0x40000000)
|
|
{
|
|
dprintf(" Free Space %I64f GB (%.2I64u MB)\n",
|
|
(float)(ul64FreeBytes) / (0x40000000),
|
|
(ul64FreeBytes) / (0x100000));
|
|
}
|
|
else if (ul64FreeBytes > 0x100000)
|
|
{
|
|
dprintf(" Free Space %I64f MB (%.2I64u KB)\n",
|
|
(float)(ul64FreeBytes) / (0x100000),
|
|
(ul64FreeBytes) / (0x400));
|
|
}
|
|
else
|
|
{
|
|
dprintf(" Free Space %I64u Bytes\n", ul64FreeBytes);
|
|
}
|
|
}
|
|
|
|
ULONG GetAndOutputNTFSData(CHAR cDriveletter, ULONG64 ul64DevObj)
|
|
{
|
|
ULONG ulVCBOffset = 0;
|
|
ULONG ulBytesPerCluster = 0;
|
|
ULONG64 ul64TotalClusters = 0;
|
|
ULONG64 ul64FreeClusters = 0;
|
|
|
|
if (GetFieldOffset("ntfs!_VOLUME_DEVICE_OBJECT", "Vcb", &ulVCBOffset))
|
|
{
|
|
dprintf("Cannot get Vcb offset for NTFS Device!\n");
|
|
return E_FAIL;
|
|
}
|
|
|
|
if (GetFieldValue(ul64DevObj + ulVCBOffset, "ntfs!_VCB", "BytesPerCluster", ulBytesPerCluster))
|
|
{
|
|
dprintf("Failed to get BytesPerCluster from VCB at 0x%I64x!\n", ul64DevObj + ulVCBOffset);
|
|
return E_FAIL;
|
|
}
|
|
|
|
if (GetFieldValue(ul64DevObj + ulVCBOffset, "ntfs!_VCB", "TotalClusters", ul64TotalClusters))
|
|
{
|
|
dprintf("Failed to get TotalClusters from VCB at 0x%I64x!\n", ul64DevObj + ulVCBOffset);
|
|
return E_FAIL;
|
|
}
|
|
|
|
if (GetFieldValue(ul64DevObj + ulVCBOffset, "ntfs!_VCB", "FreeClusters", ul64FreeClusters))
|
|
{
|
|
dprintf("Failed to get FreeClusters from VCB at 0x%I64x!\n", ul64DevObj + ulVCBOffset);
|
|
return E_FAIL;
|
|
}
|
|
|
|
OutputData(ulBytesPerCluster, ul64TotalClusters, ul64FreeClusters);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
ULONG GetAndOutputFatData(CHAR cDriveletter, ULONG64 ul64DevObj)
|
|
{
|
|
ULONG ulVCBOffset = 0;
|
|
ULONG ulBytesPerSector = 0;
|
|
ULONG ulSectorsPerCluster = 0;
|
|
ULONG64 ul64TotalClusters = 0;
|
|
ULONG64 ul64FreeClusters = 0;
|
|
|
|
if (GetFieldOffset("fastfat!_VOLUME_DEVICE_OBJECT", "Vcb", &ulVCBOffset))
|
|
{
|
|
dprintf("Cannot get Vcb offset for FastFat Device!\n");
|
|
return E_FAIL;
|
|
}
|
|
|
|
if (GetFieldValue(ul64DevObj + ulVCBOffset, "fastfat!_VCB", "Bpb.BytesPerSector", ulBytesPerSector))
|
|
{
|
|
dprintf("Failed to get BytesPerSector from VCB at 0x%I64x!\n", ul64DevObj + ulVCBOffset);
|
|
return E_FAIL;
|
|
}
|
|
|
|
if (GetFieldValue(ul64DevObj + ulVCBOffset, "fastfat!_VCB", "Bpb.SectorsPerCluster", ulSectorsPerCluster))
|
|
{
|
|
dprintf("Failed to get SectorsPerCluster from VCB at 0x%I64x!\n", ul64DevObj + ulVCBOffset);
|
|
return E_FAIL;
|
|
}
|
|
|
|
if (GetFieldValue(ul64DevObj + ulVCBOffset, "fastfat!_VCB", "AllocationSupport.NumberOfClusters", ul64TotalClusters))
|
|
{
|
|
dprintf("Failed to get TotalClusters from VCB at 0x%I64x!\n", ul64DevObj + ulVCBOffset);
|
|
return E_FAIL;
|
|
}
|
|
|
|
if (GetFieldValue(ul64DevObj + ulVCBOffset, "fastfat!_VCB", "AllocationSupport.NumberOfFreeClusters", ul64FreeClusters))
|
|
{
|
|
dprintf("Failed to get FreeClusters from VCB at 0x%I64x!\n", ul64DevObj + ulVCBOffset);
|
|
return E_FAIL;
|
|
}
|
|
|
|
OutputData(ulBytesPerSector * ulSectorsPerCluster, ul64TotalClusters, ul64FreeClusters);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
DECLARE_API( diskspace )
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Dumps free disk space for the specified volume
|
|
|
|
Arguments:
|
|
|
|
args - The volume letter of the drive on which you want the info
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
ULONG ulReturn = S_OK;
|
|
CHAR cVolume = args[0];
|
|
CHAR szRootPath[MAX_PATH + 1] = {0};
|
|
ULONG64 ul64Drive = 0;
|
|
ULONG64 ul64Vpb = 0;
|
|
|
|
INIT_API();
|
|
|
|
// Make sure we have a valid drive letter as the first character.
|
|
if (((cVolume < 'A') || (cVolume > 'z')) ||
|
|
((cVolume > 'Z') && (cVolume < 'a')))
|
|
{
|
|
dprintf("'%s' is not a valid drive specification!\n", args);
|
|
ulReturn = E_FAIL;
|
|
goto exit;
|
|
}
|
|
|
|
// Make sure this is likely to be a valid param in that it is
|
|
// followed by a space of a colon.
|
|
if ((args[1] != ' ') && (args[1] != ':') && (args[1] != '\0'))
|
|
{
|
|
dprintf("'%s' is not a valid drive specification!\n", args);
|
|
ulReturn = E_FAIL;
|
|
goto exit;
|
|
}
|
|
|
|
sprintf(szRootPath, "\\GLOBAL??\\%c:", cVolume);
|
|
|
|
dprintf("Checking Free Space for %c: ", cVolume);
|
|
|
|
ul64Drive = FindObjectByName(szRootPath, 0);
|
|
if (!ul64Drive)
|
|
{
|
|
dprintf("\nFailed to find volume %c:!\n", cVolume);
|
|
ulReturn = E_FAIL;
|
|
goto exit;
|
|
}
|
|
|
|
GetRealDeviceForSymbolicLink(ul64Drive, szRootPath, sizeof(szRootPath));
|
|
if (strstr(_strlwr(szRootPath), "cdrom"))
|
|
{
|
|
dprintf("\n%c: is a CDROM drive. This function is not supported!\n", cVolume);
|
|
ulReturn = E_FAIL;
|
|
goto exit;
|
|
}
|
|
dprintf("..");
|
|
|
|
ul64Drive = FindObjectByName(szRootPath, 0);
|
|
dprintf("..");
|
|
|
|
if (GetFieldValue(ul64Drive, "nt!_DEVICE_OBJECT", "Vpb", ul64Vpb))
|
|
{
|
|
dprintf("Failed to get Vbp pointer from DeviceObject at 0x%I64x!\n", ul64Drive);
|
|
ulReturn = E_FAIL;
|
|
goto exit;
|
|
}
|
|
dprintf("..");
|
|
|
|
if (GetFieldValue(ul64Vpb, "nt!_VPB", "DeviceObject", ul64Drive))
|
|
{
|
|
dprintf("Failed to get DeviceObject from VBP pointer at 0x%I64x!\n", ul64Vpb);
|
|
ulReturn = E_FAIL;
|
|
goto exit;
|
|
}
|
|
dprintf("..");
|
|
|
|
|
|
if (GetDeviceDriverString(ul64Drive, szRootPath, sizeof(szRootPath)))
|
|
{
|
|
dprintf("Failed to Get Driver String From Device at 0x%I64x!\n", ul64Drive);
|
|
ulReturn = E_FAIL;
|
|
goto exit;
|
|
}
|
|
dprintf("..\n");
|
|
|
|
if (strstr(_strlwr(szRootPath), "ntfs"))
|
|
{
|
|
GetAndOutputNTFSData(cVolume, ul64Drive);
|
|
}
|
|
else if (strstr(_strlwr(szRootPath), "fastfat"))
|
|
{
|
|
GetAndOutputFatData(cVolume, ul64Drive);
|
|
}
|
|
else if (strstr(_strlwr(szRootPath), "cdfs"))
|
|
{
|
|
dprintf("This extension does not support the cdfs filesystem!\n");
|
|
}
|
|
else
|
|
{
|
|
dprintf("Unable to determine Volume Type for %s!\n", szRootPath);
|
|
}
|
|
|
|
exit:
|
|
|
|
EXIT_API();
|
|
|
|
return ulReturn;
|
|
}
|