Do you know you to unmount a drive without ejecting it. NSWorkspace has some methods to unmount drives but it also eject them.
Any idea ?
I am doing it as follows and it un-mounts the drive but doesn't eject it.
(Actually I want to eject the disk, I can only un-mount the disk. :P Please share how to eject a disk.)
DASessionRef session = DASessionCreate(kCFAllocatorDefault);
CFURLRef path = CFURLCreateWithString(NULL, CFSTR("<path_to_your_volume_here>"), NULL);
DADiskRef disk = DADiskCreateFromVolumePath(kCFAllocatorDefault, session, path);
DADiskUnmount(disk, kDADiskUnmountOptionDefault, __unmountCallback, NULL);
This is the code I am still working on and is under development and testing.
I am creating the "path" manually. You can use (and share) a better method to get the path of volume in a generic way. Perhaps this answer has hints of doing it the right way.I'll update when my development is refined and complete.
To do this, use DADiskUnmount in DiskArbitration framework.
http://developer.apple.com/library/mac/#documentation/Darwin/Reference/DiscArbitrationFramework/DiskArbitration_h/index.html#//apple_ref/c/func/DADiskUnmount
To eject the disk, unmount the disk as you stated, and then in your __unmountCallback do the following:
DADiskRef disk2 = DADiskCopyWholeDisk(disk);
DADiskEject(disk2,
kDADiskEjectOptionDefault,
NULL,
NULL);
You can pass any object as context to the DADiskUnmount() and then, for example, use it to determine if the respective disk should be ejected in the __unmountCallback.
Related
How can I set in Windows 'read-only' mode for specific logical disk (like F:), which formatted by FAT32? I know about access permissions politic for removable storage in gpedit.msc, but they works for all logical disk together. I want setting this mode via disk names (F: H: etc). How can I do it? I had tried to write simple C# code, which change access permissions for disk, but it does't work for FAT disks:
System.Security.AccessControl.DirectorySecurity d = System.IO.Directory.GetAccessControl("F:\\");
SecurityIdentifier everyone = new SecurityIdentifier(WellKnownSidType.WorldSid, null);
FileSystemAccessRule accRule = new FileSystemAccessRule(everyone, FileSystemRights.Write, AccessControlType.Deny);
d.AddAccessRule(accRule);
System.IO.Directory.SetAccessControl("F:\\", d);
I am interested in iterating all mounted file systems on OSX (currently running 10.9 Mavericks). I am looking for something similar to getmntent() or the output of the mount shell command (although I want to do it from objective C, so parsing the output of a shell command is obviously not optimal).
I have been looking a bit at the disk arbitration framework, and it appears that I could be notified about mount and unmount events using this framework. I may be missing something there, but it isn't clear to me if there is a way to iterate existing mounted file systems using Disk Arbitration.
I have explored using getfsent() which seemed like it would provide a solution, but after testing I discovered that I am not getting more than one entry from iterating getfsent(). See the following code:
struct fstab* fsentry;
setfsent();
fsentry = getfsent();
while(fsentry)
{
//do something with fsentry
fsentry = getfsent();
}
endfsent();
The only entry I am getting here is for the / file system. The second time I call getfsent() it returns NULL, as if there are no more entries. The mount command shows me several others including a mounted cifs/smb file system:
/dev/disk0s2 on / (hfs, local, journaled)
devfs on /dev (devfs, local, nobrowse)
map -hosts on /net (autofs, nosuid, automounted, nobrowse)
map auto_home on /home (autofs, automounted, nobrowse)
//user#<ip address>/public on /Volumes/public (smbfs, nodev, nosuid, mounted by user)
So it seems like getfsent() starts doing what I expect, but for some reason stops?
My question in summary is: What is the best way to iterate file systems on OSX?
If anyone has an answer to why I am only getting one result from getfsent() I would also be interested in that.
There are a couple of different ways to enumerate mounted volumes on OS X, each a using different set of APIs. At the highest (and easiest) level, you can use NSFileManager's mountedVolumeURLsIncludingResourceValuesForKeys:options:. Here's an abbreviated example:
NSArray *urls = [[NSFileManager defaultManager] mountedVolumeURLsIncludingResourceValuesForKeys:#[NSURLVolumeNameKey] options:0];
for (NSURL *url in urls) {
NSLog(#"Volume mounted at: %#", [url path]);
}
The next option takes us back to C territory - and you were so close with your original approach. On OS X (and BSD), there isn't getmntent(); instead, there is getmntinfo(), which is strikingly similar. To list mounted volumes via getmntinfo(), you can do the following:
struct statfs* mounts;
int num_mounts = getmntinfo(&mounts, MNT_WAIT);
if (num_mounts < 0) {
// do something with the error
}
for (int i = 0; i < num_mounts; i++) {
NSLog(#"Disk type '%s' mounted at: %s", mounts[i].f_fstypename, mounts[i].f_mntonname);
}
I've used both of these APIs side-by-side since the release of 10.6. getmntinfo() is always more complete than [NSFileManager mountedVolumeURLsIncludingResourceValuesForKeys:options:]: the latter will filter the /dev and other filesystems that you may or may not want to know about. It is generally reliable, however, for the disks you plug into your system.
The purpose behind the DiskArbitration framework is different, as you noticed. DiskArbitration is about monitoring and managing disk assets. With DA, you can get called whenever a new disk is mounted or unmounted. You can also manage those disks by renaming, mounting, unmounting, or ejecting them, as well as inserting yourself in the mount/unmount process - and potentially suspending requests to do the same. But, as you pointed out, it does not provide an interface for listing existing disks. Once you do get your list of mounted volumes, DA is an excellent next stop (depending, of course, on your reason for getting that list!).
Given a volume, how do I determine whether it is encrypted or not? I've found stuff like DADiskCopyDescription() and NSURL's getResourceValue:forKey:error: which give a wealth of information, but not whether the volume is encrypted.
Even if there isn't a public API for this then scraping output from a command line tool which ships with the OS would be acceptable. The closest I found was 'diskutil info /dev/disk0', but again no encryption information. Annoyingly the GUI Disk Utility app does provide this information when you click on the blue info button.
You can (ab)use IOKit for this. Note that the CoreStorage Encrypted property is not officially defined anywhere, so this is decidedly not public API. Also, you'll need to inspect the whole disk object that Core Storage offers to the OS (e.g. disk1), not the partition that the Core Storage LV lives on (e.g. disk0s2).
const char *bsdDisk = "disk1";
DASessionRef session = DASessionCreate(kCFAllocatorDefault);
DADiskRef disk = DADiskCreateFromBSDName(kCFAllocatorDefault, session, "disk1");
io_service_t diskService = DADiskCopyIOMedia(disk);
CFBooleanRef isEncrypted = IORegistryEntryCreateCFProperty(diskService,
CFSTR("CoreStorage Encrypted"),
kCFAllocatorDefault,
0);
fprintf(stdout,
"%s %s encrypted\n",
bsdDisk,
(CFBooleanGetValue(isEncrypted)) ? "is" : "is not");
CFRelease(isEncrypted);
IOObjectRelease(diskService);
CFRelease(disk);
CFRelease(session);
It looks like this information is available using system_profiler -detailLevel basic.
I am trying to determine if a file is on a local drive. I found GetDriveType() WINAPI which retrieves the drive type. However reading the description of the return values it seems, and thats how I understand it, that it retrieves a flash drive as FIXED which is not what I want.
Its working fine on local drives:
bool IsDriveRemovableOrRemote(CString driveRoot)
{
UINT driveType = GetDriveType(driveRoot);
return (DRIVE_REMOVABLE == driveType || DRIVE_CDROM == driveType || DRIVE_NO_ROOT_DIR == driveType || DRIVE_REMOTE == driveType);
}
I don't have a flash/external drive to test ATM but I would like if someone can tell me if my interpretation is correct? and if so, what better alternative should I use?
Please bear in mind that I only have the path of the file.
You should read the doco more closely. While a Flash drive is considered a fixed device, there's a note in that linked page:
To determine whether a drive is a USB-type drive, call SetupDiGetDeviceRegistryProperty and specify the SPDRP_REMOVAL_POLICY property.
The process seems a little messy if all you start with is the path but you can start reading the doco here. It looks like you may need to enumerate the devices until you find one matching your drive.
To avoid doing this to all your requests, I would do a two-stage check. If your current method says it's not fixed, treat it as non-local.
If it says it is fixed, then you can enumerate the devices using my suggested method to be certain.
Alternatively, you can enumerate all fixed non-USB drives the first time you need to, and then just cache the information. I'm pretty certain that the list of these drives won't change while the system is running - drives that get added and deleted are, by definition, removable.
You can try using DeviceIoControl and query for the BusType = BusTypeUsb by passing IOCTL_STORAGE_QUERY_PROPERTY as its second parameter. Read Determining USB by Thomas Lee at the bottom of page.
I'm trying to write a test pattern to every sector of a formatted USB drive. There is one logical drive (e.g. h:). This volume is FAT-formatted and contains data to be overwritten. Also, I want to overwrite the whole physical drive. The program is running with elevated user rights.
First I did the following:
// from the drive letter "h:" I get the physical disk number using
// IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS => "\\.\PhysicalDrive2"
hDevice = ::CreateFile( "\\.\PhysicalDrive2", GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL );
// get the number of available sectors with IOCTL_DISK_GET_DRIVE_GEOMETRY_EX
// => ulNumberOfSectors
// now I try to write some sectors, e.g. 2 (I want to use a higher value):
WriteFile( hDevice, abBuffer, 2*512, &byteswritten, NULL );
The call to WriteFile fails with ERROR_ACCESS_DENIED.
If I write one sector, it works.
When I overwrite the first sector and plug the device out and in again, Windows wants to format it. In this situation my code with 2048 sectors at once works without ERROR_ACCESS_DENIED.
I also unmounted the volume as described in CodeProject: WriteFile on Physical Drives with Windows 7 but this didn't change anything. Obviously the volume is unmounted because it's no longer visible in Windows Explorer.
I want to write more than a single sector due to perfomance reasons. I'm also afraid that other problems in the field might occur because I don't fully understand ths problem.
Any suggestions?
I didn't have problems with different WriteFile() sizes, but I did solve the
WriteFile(): Access is denied <ERROR_ACCESS_DENIED/5> to
'\.\physicaldriveX
devices (usually USB HDD/SSD) in Windows 7 running as Administrator (elevated rights) as follows:
Computer Management -> Disk Management:
Volume (H: in your case) -> right-click -> Delete Volume
Disk (Disk 2 in your case) -> right-click -> Off-line
Disk (Disk 2 in your case) -> right-click -> On-line
After that, I'm able to write to '\.\physicaldriveX' with no problem.
I think the Win7 locks (unlike previous Windows releases) the physical device as long as there is any file system on the device to avoid consistency problems.
You cannot directly access sectors of a drive which are owned by a mounted filesystem.
See Changes to the file system and to the storage stack to restrict direct disk access and direct volume access
The documentation for FSCTL_DISMOUNT_VOLUME describes the following sequence for overwriting a filesystem:
Open a volume.
Lock the volume.
Format the volume.
Dismount the volume.
Unlock the volume.
Close the volume handle.
Your pattern-writing operation would be in step 3 instead of formatting.
Another method is to use clean to delete all the partitions (and ALL DATA) on the disk:
C:\> diskpart
Diskpart> list disk
Diskpart> select disk N (where N is your disk number)
Diskpart> clean
Diskpart> exit