fs-win.cpp revision 922fb31280a8b013eca26bc49b3750979b3db6f0
/* $Id$ */
/** @file
* IPRT - File System, Win32.
*/
/*
* Copyright (C) 2006-2007 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* you can redistribute it and/or modify it under the terms of the GNU
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*
* The contents of this file may alternatively be used under the terms
* of the Common Development and Distribution License Version 1.0
* (CDDL) only, as it comes in the "COPYING.CDDL" file of the
* VirtualBox OSE distribution, in which case the provisions of the
* CDDL are applicable instead of those of the GPL.
*
* You may elect to license modified versions of this file under the
* terms and conditions of either the GPL or the CDDL or both.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#define LOG_GROUP RTLOGGROUP_FS
#include <windows.h>
#include <iprt/fs.h>
#include <iprt/path.h>
#include <iprt/string.h>
#include <iprt/param.h>
#include <iprt/err.h>
#include <iprt/log.h>
#include <iprt/assert.h>
#include "internal/fs.h"
#include <iprt/stream.h>
/* from ntdef.h */
typedef LONG NTSTATUS;
/* from ntddk.h */
typedef struct _IO_STATUS_BLOCK {
union {
NTSTATUS Status;
PVOID Pointer;
};
ULONG_PTR Information;
} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;
typedef enum _FSINFOCLASS {
FileFsAttributeInformation = 5,
} FS_INFORMATION_CLASS, *PFS_INFORMATION_CLASS;
/* from ntifs.h */
typedef struct _FILE_FS_ATTRIBUTE_INFORMATION {
ULONG FileSystemAttributes;
LONG MaximumComponentNameLength;
ULONG FIleSystemNameLength;
WCHAR FileSystemName[1];
} FILE_FS_ATTRIBUTE_INFORMATION, *PFILE_FS_ATTRIBUTE_INFORMATION;
extern "C" NTSTATUS NTAPI NtQueryVolumeInformationFile(HANDLE, PIO_STATUS_BLOCK, PVOID, ULONG, FS_INFORMATION_CLASS);
/**
* Checks quickly if this is an correct root specification.
* Root specs ends with a slash of some kind.
*
* @returns indicator.
* @param pszFsPath Path to check.
*/
static bool rtFsIsRoot(const char *pszFsPath)
{
/*
* UNC has exactly two slashes..
*
* Anything else starting with slashe(s) requires
* expansion and will have to take the long road.
*/
if (RTPATH_IS_SLASH(pszFsPath[0]))
{
if ( !RTPATH_IS_SLASH(pszFsPath[1])
|| RTPATH_IS_SLASH(pszFsPath[2]))
return false;
/* end of machine name */
const char *pszSlash = strpbrk(pszFsPath + 2, "\\/");
if (!pszSlash)
return false;
/* end of service name. */
pszSlash = strpbrk(pszSlash + 1, "\\/");
if (!pszSlash)
return false;
return pszSlash[1] == '\0';
}
/*
* Ok the other alternative is driver letter.
*/
return pszFsPath[0] >= 'A' && pszFsPath[0] <= 'Z'
&& pszFsPath[1] == ':'
&& RTPATH_IS_SLASH(pszFsPath[2])
&& !pszFsPath[3];
}
/**
* Finds the root of the specified volume.
*
* @returns iprt status code.
* @param pszFsPath Path within the filesystem. Verified as one byte or more.
* @param ppwszFsRoot Where to store the returned string. Free with rtFsFreeRoot(),
*/
static int rtFsGetRoot(const char *pszFsPath, PRTUTF16 *ppwszFsRoot)
{
/*
* Do straight forward stuff first,
*/
if (rtFsIsRoot(pszFsPath))
return RTStrToUtf16(pszFsPath, ppwszFsRoot);
/*
* Expand and add slash (if required).
*/
char szFullPath[RTPATH_MAX];
int rc = RTPathAbs(pszFsPath, szFullPath, sizeof(szFullPath));
if (RT_FAILURE(rc))
return rc;
size_t cb = strlen(szFullPath);
if (!RTPATH_IS_SLASH(szFullPath[cb - 1]))
{
AssertReturn(cb + 1 < RTPATH_MAX, VERR_FILENAME_TOO_LONG);
szFullPath[cb] = '\\';
szFullPath[++cb] = '\0';
}
/*
* Convert the path.
*/
rc = RTStrToUtf16(szFullPath, ppwszFsRoot);
if (RT_FAILURE(rc))
return rc == VERR_BUFFER_OVERFLOW ? VERR_FILENAME_TOO_LONG : rc;
/*
* Walk the path until our proper API is happy or there is no more path left.
*/
PRTUTF16 pwszStart = *ppwszFsRoot;
if (!GetVolumeInformationW(pwszStart, NULL, 0, NULL, NULL, 0, NULL, 0))
{
PRTUTF16 pwszEnd = pwszStart + RTUtf16Len(pwszStart);
PRTUTF16 pwszMin = pwszStart + 2;
do
{
/* Strip off the last path component. */
while (pwszEnd-- > pwszMin)
if (RTPATH_IS_SLASH(*pwszEnd))
break;
AssertReturn(pwszEnd >= pwszMin, VERR_INTERNAL_ERROR); /* leaks, but that's irrelevant for an internal error. */
pwszEnd[1] = '\0';
} while (!GetVolumeInformationW(pwszStart, NULL, 0, NULL, NULL, 0, NULL, 0));
}
return VINF_SUCCESS;
}
/**
* Frees string returned by rtFsGetRoot().
*/
static void rtFsFreeRoot(PRTUTF16 pwszFsRoot)
{
RTUtf16Free(pwszFsRoot);
}
RTR3DECL(int) RTFsQuerySizes(const char *pszFsPath, RTFOFF *pcbTotal, RTFOFF *pcbFree,
uint32_t *pcbBlock, uint32_t *pcbSector)
{
/*
* Validate & get valid root path.
*/
AssertMsgReturn(VALID_PTR(pszFsPath) && *pszFsPath, ("%p", pszFsPath), VERR_INVALID_PARAMETER);
PRTUTF16 pwszFsRoot;
int rc = rtFsGetRoot(pszFsPath, &pwszFsRoot);
if (RT_FAILURE(rc))
return rc;
/*
* Free and total.
*/
if (pcbTotal || pcbFree)
{
ULARGE_INTEGER cbTotal;
ULARGE_INTEGER cbFree;
if (GetDiskFreeSpaceExW(pwszFsRoot, &cbFree, &cbTotal, NULL))
{
if (pcbTotal)
*pcbTotal = cbTotal.QuadPart;
if (pcbFree)
*pcbFree = cbFree.QuadPart;
}
else
{
DWORD Err = GetLastError();
rc = RTErrConvertFromWin32(Err);
Log(("RTFsQuerySizes(%s,): GetDiskFreeSpaceEx failed with lasterr %d (%Rrc)\n",
pszFsPath, Err, rc));
}
}
/*
* Block and sector size.
*/
if ( RT_SUCCESS(rc)
&& (pcbBlock || pcbSector))
{
DWORD dwDummy1, dwDummy2;
DWORD cbSector;
DWORD cSectorsPerCluster;
if (GetDiskFreeSpaceW(pwszFsRoot, &cSectorsPerCluster, &cbSector, &dwDummy1, &dwDummy2))
{
if (pcbBlock)
*pcbBlock = cbSector * cSectorsPerCluster;
if (pcbSector)
*pcbSector = cbSector;
}
else
{
DWORD Err = GetLastError();
rc = RTErrConvertFromWin32(Err);
Log(("RTFsQuerySizes(%s,): GetDiskFreeSpace failed with lasterr %d (%Rrc)\n",
pszFsPath, Err, rc));
}
}
rtFsFreeRoot(pwszFsRoot);
return rc;
}
/**
* Query the serial number of a filesystem.
*
* @returns iprt status code.
* @param pszFsPath Path within the mounted filesystem.
* @param pu32Serial Where to store the serial number.
*/
RTR3DECL(int) RTFsQuerySerial(const char *pszFsPath, uint32_t *pu32Serial)
{
/*
* Validate & get valid root path.
*/
AssertMsgReturn(VALID_PTR(pszFsPath) && *pszFsPath, ("%p", pszFsPath), VERR_INVALID_PARAMETER);
AssertMsgReturn(VALID_PTR(pu32Serial), ("%p", pu32Serial), VERR_INVALID_PARAMETER);
PRTUTF16 pwszFsRoot;
int rc = rtFsGetRoot(pszFsPath, &pwszFsRoot);
if (RT_FAILURE(rc))
return rc;
/*
* Do work.
*/
DWORD dwMaxName;
DWORD dwFlags;
DWORD dwSerial;
if (GetVolumeInformationW(pwszFsRoot, NULL, 0, &dwSerial, &dwMaxName, &dwFlags, NULL, 0))
*pu32Serial = dwSerial;
else
{
DWORD Err = GetLastError();
rc = RTErrConvertFromWin32(Err);
Log(("RTFsQuerySizes(%s,): GetDiskFreeSpaceEx failed with lasterr %d (%Rrc)\n",
pszFsPath, Err, rc));
}
RTUtf16Free(pwszFsRoot);
return rc;
}
/**
* Query the properties of a mounted filesystem.
*
* @returns iprt status code.
* @param pszFsPath Path within the mounted filesystem.
* @param pProperties Where to store the properties.
*/
RTR3DECL(int) RTFsQueryProperties(const char *pszFsPath, PRTFSPROPERTIES pProperties)
{
/*
* Validate & get valid root path.
*/
AssertMsgReturn(VALID_PTR(pszFsPath) && *pszFsPath, ("%p", pszFsPath), VERR_INVALID_PARAMETER);
AssertMsgReturn(VALID_PTR(pProperties), ("%p", pProperties), VERR_INVALID_PARAMETER);
PRTUTF16 pwszFsRoot;
int rc = rtFsGetRoot(pszFsPath, &pwszFsRoot);
if (RT_FAILURE(rc))
return rc;
/*
* Do work.
*/
DWORD dwMaxName;
DWORD dwFlags;
DWORD dwSerial;
if (GetVolumeInformationW(pwszFsRoot, NULL, 0, &dwSerial, &dwMaxName, &dwFlags, NULL, 0))
{
memset(pProperties, 0, sizeof(*pProperties));
pProperties->cbMaxComponent = dwMaxName;
pProperties->fFileCompression = !!(dwFlags & FILE_FILE_COMPRESSION);
pProperties->fCompressed = !!(dwFlags & FILE_VOLUME_IS_COMPRESSED);
pProperties->fReadOnly = !!(dwFlags & FILE_READ_ONLY_VOLUME);
pProperties->fSupportsUnicode = !!(dwFlags & FILE_UNICODE_ON_DISK);
pProperties->fCaseSensitive = false; /* win32 is case preserving only */
pProperties->fRemote = false; /* no idea yet */
}
else
{
DWORD Err = GetLastError();
rc = RTErrConvertFromWin32(Err);
Log(("RTFsQuerySizes(%s,): GetVolumeInformation failed with lasterr %d (%Rrc)\n",
pszFsPath, Err, rc));
}
RTUtf16Free(pwszFsRoot);
return rc;
}
RTR3DECL(int) RTFsQueryType(const char *pszFsPath, uint32_t *pu32Type)
{
int rc = VINF_SUCCESS;
*pu32Type = RTFS_FS_TYPE_UNKNOWN;
HANDLE hFile = CreateFile(pszFsPath,
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS,
NULL);
if (hFile != INVALID_HANDLE_VALUE)
{
char abBuf[8192];
IO_STATUS_BLOCK Ios;
NTSTATUS rcNt = NtQueryVolumeInformationFile(hFile, &Ios,
abBuf, sizeof(abBuf),
FileFsAttributeInformation);
if (rcNt >= 0)
{
PFILE_FS_ATTRIBUTE_INFORMATION pFsAttrInfo = (PFILE_FS_ATTRIBUTE_INFORMATION)abBuf;
if ( pFsAttrInfo->FileSystemName[0] == 'N'
&& pFsAttrInfo->FileSystemName[1] == 'T'
&& pFsAttrInfo->FileSystemName[2] == 'F'
&& pFsAttrInfo->FileSystemName[3] == 'S'
&& pFsAttrInfo->FileSystemName[4] == '\0')
*pu32Type = RTFS_FS_TYPE_NTFS;
else if ( pFsAttrInfo->FileSystemName[0] == 'F'
&& pFsAttrInfo->FileSystemName[1] == 'A'
&& pFsAttrInfo->FileSystemName[2] == 'T'
&& ( ( pFsAttrInfo->FileSystemName[3] == '3'
&& pFsAttrInfo->FileSystemName[4] == '2'
&& pFsAttrInfo->FileSystemName[5] == '\0')
|| pFsAttrInfo->FileSystemName[3] == '\0'))
/* This is either FAT32 or FAT12/16. IMO it doesn't make
* sense to distinguish more detailed because we cannot
* easily distinguish between these FAT types on Linux
* and users who put some image file on an FAT16 partition
* should know what they are doing. */
*pu32Type = RTFS_FS_TYPE_FAT;
}
/* else: Is RTFS_FS_UNKNOWN suffient or should we return an error? */
CloseHandle(hFile);
}
return rc;
}