What's the way to get default volume on Mac 64-bit?
I have a code like that:
GetVolParmsInfoBuffer buf_64 = { 0 };
status = FSGetVolumeParms(vol_ref, // use default volume
&buf_64, // write
req_count);
The problem is that I can't pass 0 in vol_ref. On Mac 32-bit I could write:
GetVolParmsInfoBuffer buf_32 = { 0 };
HParamBlockRec pb;
pb.ioParam.ioCompletion = NULL; // not doing async I/O
pb.ioParam.ioNamePtr = NULL; // we don't use path name
pb.ioParam.ioVRefNum = 0; // use default volume
pb.ioParam.ioBuffer = reinterpret_cast(&buf_32); // write data here
pb.ioParam.ioReqCount = req_count;
OSErr err = PBHGetVolParmsSync(&pb);
ASSERT_EQ(err, noErr);
Thanks in advance,
- Oleksii
In the File Manager docs, you'll notice a function group titled “Manipulating the Default Volume”. All of those functions are deprecated.
If you search Google for the functions therein, particularly HSetVol, you'll find this mailing list post by Eric Schlegel, which says HSetVol had the effect of setting the current working directory (expressed as a volume/directory pair) on Mac OS. He also says that it doesn't work on Mac OS X: It should work on File Manager functions, but does not set the working directory used for resolving relative paths in other APIs (e.g., open and fopen) like it did on Mac OS.
Moreover, those functions are not available in 64-bit Mac OS X. So the answer is: You don't, because there is no default volume.
The old meaning of it was analogous to the current working directory, so you can do the same thing by getting the CWD and resolving that path to an FSRef. However, for a Mac OS X application (particularly one that doesn't set the CWD by any means, as most don't), this is not terribly useful: The default CWD for an application is /, the root directory of the boot volume. On the other hand, if you run your executable directly or under Xcode's Debugger, its CWD will not be /, which means it could be some other volume—most probably, the one with your Home folder on it.
You should refer to the boot volume (or whatever volume you're interested in) specifically, not attempt to get or simulate getting the default (current working) directory.
For the boot volume, you might try kOnSystemDisk, which is one of the constants in the Folder Manager. If that doesn't work, use Folder Manager's FSFindFolder function to retrieve the System folder, then use File Manager's FSGetVolumeInfo function to get what volume it's on.
Well. I don't really know what "default volume" is. All I know is that Carbon manual (File Manager) says:
ioVRefNum
A volume reference number, 0 for the default volume, or a drive number.
Well, I seem to find the answer for my question.
FSVolumeInfoParam vol_info = { 0 };
vol_info.ioVRefNum = kFSInvalidVolumeRefNum; // will obtain it
vol_info.volumeIndex = 1; // XXX: is it the default volume as well?
vol_info.whichInfo = kFSVolInfoNone; // don't pass volume info
err = PBGetVolumeInfoSync(&vol_info);
The only thing I'm not sure of is if the 1st volume is the default one...
P.S. I guess the problem is that I don't quite understand what "default volume" really is ;-)
Related
I need to call ZwCreateFile with a relative path, with the path being relative to the root of a \Device\HarddiskVolumeSomeNumber using InitializeObjectAttributes.
For example, I would like to use the string "path.txt" as ObjectName and a handle to "\Device\HarddiskVolume5" as RootDirectory.
My problem is obtaining a HANDLE object for the device directory. It seems ZwOpenFile and ZwOpenDirectory are not able to open these paths, even though I am able to open a file such as \Device\HarddiskVolume5\hello.txt
How must I obtain a HANDLE object for use as RootDirectory in OBJECT_ATTRIBUTES?
Edit: Changed title to say 5 instead of 1. I am aware that some numbered Harddisks such as 1 are not normal filesystems, such as partition data.
I am using wxWidgets in Mac with C++ (obviously) and Xcode.
This line returns false, when it should be true.
` if(!wxFileExists(filePicker->GetPath())
{
wxMessageDialog(this, "error").ShowModal();
return;
} `
where filePicker is a wxFilePickerCtrl. It's value is :
filepath std::__1::string "/Users/swtsvn/Main/Proj1/Mac/binaries/osx/Debug/wxSampleApp.app"
The path has upper case, and no spaces. The File is located in that path, since I picked that file path using file picker wxWidget tool.
I searched for a reason wxFileExists might return false on Mac but not on Windows, but could not find one in Google.
If anyone knows the answer, kindly let me know.
According to the docs, wxFileExists():
Returns true if the file exists and is a plain file.
So, it returns false for directories. Mac app bundles, such as your wxSampleApp.app, are directories. It is the intentional behavior of the function that it return false in this case.
You could presumably use wxFileExists(path) || wxDirExists(path) instead, although that will be somewhat inefficient.
I'm using the following function to delete a file to the recycle bin: (C++, MFC, Unicode)
bool DeleteFileToPaperbasket (CString filename)
{
TCHAR Buffer[2048+4];
_tcsncpy_s (Buffer, 2048+4, filename, 2048);
Buffer[_tcslen(Buffer)+1]=0; //Double-Null-Termination
SHFILEOPSTRUCT s;
s.hwnd = NULL;
s.wFunc = FO_DELETE;
s.pFrom = Buffer;
s.pTo = NULL;
s.fFlags = FOF_ALLOWUNDO | FOF_SILENT | FOF_NOERRORUI;
s.fAnyOperationsAborted = false;
s.hNameMappings = NULL;
s.lpszProgressTitle = NULL;
int rc = SHFileOperation(&s);
return (rc==0);
}
This works nicely for most files. But if path+filename exceed 255 characters (and still much shorter that 2048 characters), SHFileOperation returns 124. Which is DE_INVALIDFILES.
But what's wrong? I checked everything a million times. The path is double-null terminated, I'm not using \\?\ and it works for short filenames.
I'm totally out of ideas...
I think backwards comparability is biting you in the --- in several ways, and I'd need to actually see the paths your using and implement some error checking code to help. But here are some hints.
You would not get a DE_INVALIDFILES 0x7C "The path in the source or destination or both was invalid." for a max path violation, you'd get a DE_PATHTOODEEP 0x79 "The source or destination path exceeded or would exceed MAX_PATH."
These error codes(return value) do, can, and have changed over time, to be sure what your specific error code means, you need to check it with GetLastError function(msdn)
Also, taken from the SHFileOperation function documentation: "If you do not check fAnyOperationsAborted as well as the return value, you cannot know that the function accomplished the full task you asked of it and you might proceed under incorrect assumptions."
You should not be using this API for extremely long path names, it has been replaced in vista+ by IFileOperation interface
The explanation for why it may work in explorer and not thru this LEGACY api is - Taken from the msdn page on Naming Files, Paths, and Namespaces
The shell and the file system have different requirements. It is
possible to create a path with the Windows API that the shell user
interface is not able to interpret properly.
Hope this was helpful
The recycle bin doesn't support files whose paths exceed MAX_PATH in length. You can verify this for yourself by trying to recycle such a file in Explorer - you will get an error message about the path being too long.
I've written a duplicate finder in Java, but I need to include hard link support for it. Unfortunately, there seems to be no way to dig out a file's MFT entry in Java.
Although there is a method called fileKey() in the BasicFileAttributeView class, it won't work on the NTFS file system (I haven't tested it on ext yet).
I also found the method isSameFile() (in java.nio.file.Path). Does anyone know how this method works? It seems to be doing the right thing, but it returns a Boolean value, so it is worthless for me (I wish to put the results into a map and group them by their MFT entries).
I can always compare the creation times, modification times, etc. for each file, but this is just giving up.
Is there any way to accomplish what I am trying to do in either C++ or Java? I care more about making it work on NTFS than ext.
You would need to use the FILE_ID_FULL_DIRECTORY_INFORMATION structure along with the NtQueryDirectoryFile function (or the FILE_INTERNAL_INFORMATION structure along with the NtQueryInformationFile, if you already have a handle) inside ntdll.dll (available since Windows XP, if not earlier) to get the 8-byte file IDs and check if they are the same.
This will tell you if they are the same file, but not if they are the same stream of the same file.
I'm not sure how to detect if two files are the same stream from user-mode -- there is a structure named FILE_STREAM_INFORMATION which can return all the streams associated with a file, but it doesn't tell you which stream you have currently opened.
Detecting hard links is usually accomplished by calling FindFirstFileNameW. But there is a lower level way.
To get the NTFS equivalent to inodes, try the FSCTL_GET_OBJECT_ID ioctl code.
There's a unique (until the file is deleted) identifier in the BY_HANDLE_FILE_INFORMATION structure as well.
If the volume has an enabled USN Change Journal, you can issue the FSCTL_READ_FILE_USN_DATA ioctl code. Check the FileReferenceNumber member in the USN_RECORD structure
In Java you can use sun.nio.ch.FileKey which is a non-transparent enclosure for NTFS Inode. All the hard links share the same Inode.
Therefore, if you need to collect hard links, you can create FileKey from each suspect and compare them (e.g. by putting pairs of FileKey -> File into a Multimap)
I find fileKey is always null. Here is some code that can actually read the NTFS inode number. There remain many aspects I'm not happy with, not least, it relies on reflection.
import sun.nio.ch.FileKey;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Path;
class NTFS {
static long inodeFromPath(Path path) throws IOException, NoSuchFieldException, IllegalAccessException {
try (FileInputStream fi = new FileInputStream(path.toFile())) {
FileDescriptor fd = fi.getFD();
FileKey fk = FileKey.create(fd);
Field privateField = FileKey.class.getDeclaredField("nFileIndexHigh");
privateField.setAccessible(true);
long high = (long) privateField.get(fk);
privateField = FileKey.class.getDeclaredField("nFileIndexLow");
privateField.setAccessible(true);
long low = (long) privateField.get(fk);
long power = (long) 1 << 32;
long inode = high * power + low;
return inode;
}
}
}
Is there a way to get the SearchPath API to not search in c:\windows when using the default search path (passing NULL as the first param)? I can't modify the caller to send in a specific path.
I have a system with an application ini file in c:\windows (which I don't want it to use, but for legacy reasons has to remain there). I put my copy of the same ini file in c:\users\public, and put c:\users\public at the front of my system path environment variable, but a call to SearchPath still finds the c:\windows version. If I delete that version, it then finds the c:\users\public version, so I know the path was set correctly.
I know this is very late, but having just run into this problem myself, I would propose a better solution.
The first argument to SearchPath, as you have found, can be used to specify the directories you want it to search, instead of the default order. You can retrieve and use the current user's PATH with GetEnvironmentVariable, and then search within that:
DWORD err = GetEnvironmentVariable("PATH", NULL, 0);
char* path = new char[err+1]; path[err] = 0;
GetEnvironmentVariable("PATH", path, err);
err = SearchPath(path, "application", ".ini", 0, NULL, NULL);
char* searchResult = new char[err+1]; searchResult[err] = 0;
err = SearchPath(path, "application", ".ini", err, searchResult, NULL);
According to MSDN, there's nothing you can do about this bar changing a system level (HKLM) registry entry (Which is a "bad thing"). The registry change would cause the search order to start with the current working directory, which you could set to the desired folder in a shortcut. (Again, I'm going to say; changing a Machine Level registry entry to do this - is potentially dangerous!)
Have you looked into application shims? This may be something that could work for you.
Try SetCurrentDirectory("c:\users\public") and then SearchPath(...).