How to get drive name from drive letter? - c++

I have searched internet but couldn't find answer,how can I get drive name from drive letter on c++ for windows?I mean if I say G:\ it has to give me the name of pen drive.Ex:Removable Disk.

It is as simple as calling the GetVolumeInformation API function. You pass in the drive letter as the path name (e.g., G:\), and the function fills a buffer containing the volume name (along with other information, if you are interested in any of that).
Here is the code required to retrieve the volume name for the G:\ drive. The volume name is placed into the szVolumeName buffer:
WCHAR szVolumeName[MAX_PATH];
BOOL bSucceeded = GetVolumeInformationW(L"G:\\",
szVolumeName,
MAX_PATH,
NULL,
NULL,
NULL,
NULL,
0);
If you want any of the other information while you're calling the function, like the volume's DOS serial number, the file system name, etc., then you can change the parameters from NULL to the appropriate buffers.

Related

How to open a partition handle to get its information on a dynamic disk?

For the purpose of logging in my application I need to get information about physical partitions on the disk, such as the info provided in PARTITION_INFORMATION_EX. I have no problem doing it for "Basic disks" using the following method:
First let's assume the following drive configuration:
Then the following code works fine:
//Say, if I want to get info for partion 1 on disk 0 (or volume C:)
HANDLE hPart = ::CreateFile(L"\\\\?\\GLOBALROOT\\Device\\Harddisk0\\Partition1",
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
OPEN_EXISTING, 0, NULL);
if(hPart != INVALID_HANDLE_VALUE)
{
PARTITION_INFORMATION_EX pix = {0};
DWORD bytesReturned = 0;
if(::DeviceIoControl(hPart, IOCTL_DISK_GET_PARTITION_INFO_EX, NULL, 0, &pix, sizeof(pix), &bytesReturned, NULL))
{
//Got info OK
}
::CloseHandle(hPart);
}
But if I try to retrieve the info on a "dynamic partition", say, disk 2, partition 1 (or 2nd half of spanned volume F:) and I try to use "\\\\?\\GLOBALROOT\\Device\\Harddisk2\\Partition1" in a call to CreateFile, it fails with error code 2, or ERROR_FILE_NOT_FOUND.
So the question is, how do I retrieve partition info on a dynamic disk?
Read the documentation:
Basic and Dynamic Disks
In particular, this section provides a hint:
Detecting the Type of Disk
There is no specific function to programmatically detect the type of disk a particular file or directory is located on. There is an indirect method.
First, call GetVolumePathName. Then, call CreateFile to open the volume using the path. Next, use IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS with the volume handle to obtain the disk number and use the disk number to construct the disk path, such as "\\?\PhysicalDriveX". Finally, use IOCTL_DISK_GET_DRIVE_LAYOUT_EX to obtain the partition list, and check the PartitionType for each entry in the partition list.

Converting from SECURITY_DESCRIPTOR to string and back again doesn't work

Simple test program to illustrate idea:
'MyBuildSecurityDescriptor' is a function copied from here to create a basic security descriptor.
PSECURITY_DESCRIPTOR pSecDesc = MyBuildSecurityDescriptor();
// Convert to a PISECURITY_DESCRIPTOR to view the contents.
PISECURITY_DESCRIPTOR piSecDesc = (PISECURITY_DESCRIPTOR)pSecDesc;
LPTSTR szSecDesc;
ConvertSecurityDescriptorToStringSecurityDescriptor(pSecDesc, SECURITY_DESCRIPTOR_REVISION,
DACL_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION,
&szSecDesc,
NULL);
// At this point szSecDesc has the correct security descriptor in it.
// "D:(A;;KR;;;WD)(A;;KA;;;BA)" for those interested.
// Now to convert back from string version.
PSECURITY_DESCRIPTOR pSecurityDescriptor;
ConvertStringSecurityDescriptorToSecurityDescriptor(szSecDesc, SDDL_REVISION_1, &pSecurityDescriptor, NULL);
// Convert to a PISECURITY_DESCRIPTOR to view the contents.
PISECURITY_DESCRIPTOR piNewSecDesc = (PISECURITY_DESCRIPTOR)pSecurityDescriptor;
Here are the debug views in visual studio for piSecDesc and piNewSecDesc for the above program;
So my question is why does the SECURITY_DESCRIPTOR after the conversions not have the correct data?
Given that this states (about ConverStringSecurityDescriptorToSecurityDescriptor) "This function retrieves a security descriptor that the ConvertSecurityDescriptorToStringSecurityDescriptor function converted to string format." I would assume this simple program would work.
Some context
My work requires me to retrieve a SECURITY_DESCRIPTOR then pass this, via a pipe, to another program. The easiest way it seemed was to convert to a string, pass it across, then convert back on the other side.
I strongly suspect that the differences you're seeing are because MyBuildSecurityDescriptor builds an absolute security descriptor, while ConvertStringSecurityDescriptorToSecurityDescriptor builds a self-relative security descriptor. You can convert self-relative security descriptors to absolute security descriptors using MakeAbsoluteSD, but quite honestly, there's no real point to it; all Windows APIs accept absolute or self-relative security descriptors.
MSDN explicitly notes in the description for the SECURITY_DESCRIPTOR structure:
Because the internal format of a security descriptor can vary, we
recommend that applications not modify the SECURITY_DESCRIPTOR
structure directly. For creating and manipulating a security
descriptor, use the functions listed in See Also.
I would use the IsValidSecurityDescriptor function (as well as checking return values) to verify the consistency of your descriptors.

GetDiskFreeSpaceEx with NULL Directory Name failing

I'm trying to use GetDiskFreeSpaceEx in my C++ win32 application to get the total available bytes on the 'current' drive. I'm on Windows 7.
I'm using this sample code: http://support.microsoft.com/kb/231497
And it works! Well, almost. It works if I provide a drive, such as:
...
szDrive[0] = 'C'; // <-- specifying drive
szDrive[1] = ':';
szDrive[2] = '\\';
szDrive[3] = '\0';
pszDrive = szDrive;
...
fResult = pGetDiskFreeSpaceEx ((LPCTSTR)pszDrive,
    (PULARGE_INTEGER)&i64FreeBytesToCaller,
    (PULARGE_INTEGER)&i64TotalBytes,
(PULARGE_INTEGER)&i64FreeBytes);
fResult becomes true and i can go on to accurately calculate the number of free bytes available.
The problem, however, is that I was hoping to not have to specify the drive, but instead just use the 'current' one. The docs I found online (Here) state:
lpDirectoryName [in, optional]
A directory on the disk. If this parameter is NULL, the function uses the root of the current disk.
But if I pass in NULL for the Directory Name then GetDiskFreeSpaceEx ends up returning false and the data remains as garbage.
fResult = pGetDiskFreeSpaceEx (NULL,
    (PULARGE_INTEGER)&i64FreeBytesToCaller,
    (PULARGE_INTEGER)&i64TotalBytes,
(PULARGE_INTEGER)&i64FreeBytes);
//fResult == false
Is this odd? Surely I'm missing something? Any help is appreciated!
EDIT
As per JosephH's comment, I did a GetLastError() call. It returned the DWORD for:
ERROR_INVALID_NAME 123 (0x7B)
The filename, directory name, or volume label syntax is incorrect.
2nd EDIT
Buried down in the comments I mentioned:
I tried GetCurrentDirectory and it returns the correct absolute path, except it prefixes it with \\?\
it returns the correct absolute path, except it prefixes it with \\?\
That's the key to this mystery. What you got back is the name of the directory with the native api path name. Windows is an operating system that internally looks very different from what you are familiar with winapi programming. The Windows kernel has a completely different api, it resembles the DEC VMS operating system a lot. No coincidence, David Cutler used to work for DEC. On top of that native OS were originally three api layers, Win32, POSIX and OS/2. They made it easy to port programs from other operating systems to Windows NT. Nobody cared much for the POSIX and OS/2 layers, they were dropped at XP time.
One infamous restriction in Win32 is the value of MAX_PATH, 260. It sets the largest permitted size of a C string that stores a file path name. The native api permits much larger names, 32000 characters. You can bypass the Win32 restriction by using the path name using the native api format. Which is simply the same path name as you are familiar with, but prefixed with \\?\.
So surely the reason that you got such a string back from GetCurrentDirectory() is because your current directory name is longer than 259 characters. Extrapolating further, GetDiskFreeSpaceEx() failed because it has a bug, it rejects the long name it sees when you pass NULL. Somewhat understandable, it isn't normally asked to deal with long names. Everybody just passes the drive name.
This is fairly typical for what happens when you create directories with such long names. Stuff just starts falling over randomly. In general there is a lot of C code around that uses MAX_PATH and that code will fail miserably when it has to deal with path names that are longer than that. This is a pretty exploitable problem too for its ability to create stack buffer overflow in a C program, technically a carefully crafted file name could be used to manipulate programs and inject malware.
There is no real cure for this problem, that bug in GetDiskFreeSpaceEx() isn't going to be fixed any time soon. Delete that directory, it can cause lots more trouble, and write this off as a learning experience.
I am pretty sure you will have to retrieve the current drive and directory and pass that to the function. I remember attempting to use GetDiskFreeSpaceEx() with the directory name as ".", but that did not work.

GetLogicalDrives plus additional information C++

I need help with one of my programs that pulls out available drives on a system and prints various information about the drives. I am using VC++ and am fairly new to C++ and need some high level inputs or example code from experienced programmers.
Here is my current source code:
#include "stdafx.h"
#include Windows.h
#include stdio.h
#include iostream
using namespace std;
int main()
{
// Initial Dummy drive
WCHAR myDrives[] = L" A";
// Get the logical drive bitmask (1st drive at bit position 0, 2nd drive at bit position 1... so on)
DWORD myDrivesBitMask = GetLogicalDrives();
// Verifying the returned drive mask
if(myDrivesBitMask == 0)
wprintf(L"GetLogicalDrives() failed with error code: %d\n", GetLastError());
else {
wprintf(L"This machine has the following logical drives:\n");
while(myDrivesBitMask) {
// Use the bitwise AND with 1 to identify
// whether there is a drive present or not.
if(myDrivesBitMask & 1) {
// Printing out the available drives
wprintf(L"drive %s\n", myDrives);
}
// increment counter for the next available drive.
myDrives[1]++;
// shift the bitmask binary right
myDrivesBitMask >>= 1;
}
wprintf(L"\n");
}
system("pause");
}
`
-Here is the output-
This machine has the following logical drives:
drive C
drive D
drive E
drive F
drive G
drive H
drive I
I need to output additional information about each drive (perhaps an example will tell the story in a shorter amount of time):
Drive – C:\
Drive Type: Fixed
Drive Ready Status: True
Volume Label: Boot Drive
File System Type : NTFS
Free Space: 30021926912
Total Drive Size: 240055742464
Drive – D:\
Drive Type: Fixed
Drive Ready Status: True
Volume Label: Application Data
File System Type : NTFS
Free Space: 42462507008
Total Drive Size: 240054693888
Which methods, libs api, etc. can I use to pull out drive type, drive status, volume label, file system type, free space, and total drive size?
*Side note, I noticed a defect with my pre-processor directives, specifically within the standard I/O header files. I know that is not the recommended way using printf and cout is type safe and the proper route to go but I couldn't figure out how to format output in cout as you would do in wprintf(L"drive %s\n", myDrives);.... so how would you do this with cout??
Thanks in advance.
You want to look at functions such as GetVolumeInformation to retrieve file system information such as free space and volume name.
GetDriveType will give you some basic information about the drive type, but USB thumb sticks and flash readers can give surprising results.
I'm not sure what you mean by "ready status". If you mean whether there is a valid volume in the drive, then you can try CreateFile with a path of "\\.\C:" to try and open the volume. If it fails then there is no volume (disk) present. This will be of use for SD card readers. To do this without an error dialog appearing you will need to call SetErrorMode(SEM_NOOPENFILEERRORBOX) first.
To check whether a drive is ready you may also use GetDiskFreeSpaceEx. If this fails, the drive is not ready/usable.
Here is some example code: http://pinvoke.net/default.aspx/coredll/GetDiskFreeSpaceEx.html

Why is CreateFile failing to open a file across a network share?

I wrote a small program which frequently opens small, user text files and until now haven't encountered any problems with read/write access or any sorts of conflict. The files are selected in another piece of software which I have no control over, and are passed to me as a string.
When attempting to open a file from a mapped network drive I am getting a "The system cannot find the path specified" error (GetLastError() = 3).
The call is shown below, *iNCfileName = "z:\\Validation\\Sample Files\\1_1-4 120MM.CC", where Z: is a mapped folder on our domain.
iNCfile = CreateFile( iNCfileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
if ( iNCfile == INVALID_HANDLE_VALUE )
{
string msg; // lots of better ways to get this printed ... but ...
dw = GetLastError();
msg = iNCfileName;
msg += ": ";
msg += _com_error(dw).ErrorMessage();
print_error(dw , (char*)msg.c_str() );
return 102;
}
The file opens from my program if I copy it to the local hard drive. It also opens in notepad from the mapped drive.
Could this be a problem between the "Z:\whatever.txt" mapped representation and the true file name (\mydomain\Validation\S....??)?
If so, how can I convert from one to the other in a programmatic way (assume I won't know the domain/share names ahead of time)?
If it makes any difference I use VS2010 and the application executes on a Win XP machine.
Related: my follow up question
I've encountered this before. When using a path like \\DOMAIN\PATH\FILE.TXT I had to first call WNetAddConnection2().
Here is my code (of course you can exclude the NULL members):
NETRESOURCE nr = {0}; //new structure for network resource
nr.dwType = RESOURCETYPE_ANY; //generic resource (any type allowed)
nr.lpLocalName = NULL; //does not use a device
nr.lpRemoteName = "\\\\DOMAIN\\PATH\\FOLDER"; //For me, this pointed to an account's documents folder, and from there I could use a subfolder
nr.lpProvider = NULL; //no provider
DWORD ret = WNetAddConnection2 (&nr, NULL, NULL, CONNECT_TEMPORARY); //add connection
Don't forget the header and library.
I just had the same issue; trying to create a file using API CreateFileW under a mapped drive ( Z:\folder ) did not worked; howerver, after researching this subject i tried to create the file using the real path ( \\Shared_computer_name\folder\ ) immediately worked successfully.
Now I have to work a function to retrieve the real name of a mapped drive, to use it when necessary... just found WNetGetUniversalName, have to make it to work.