Unreadable file attributes on windows - c++

Consider a following snippet of code
#include <iostream>
#include <windows.h>
int main()
{
WIN32_FILE_ATTRIBUTE_DATA wfad;
GetFileAttributesEx(("C:\\TEMP\\noreadfile"), GetFileExInfoStandard, &wfad); //"noreadfile" is unreadable file
std::cout << wfad.dwFileAttributes; // 128
return 0;
}
For an unreadable file (File that does not have read permissions or file that has its read permissions set as "Deny" in its properties -> security tab) on Windows, GetFileAttributesEx returns FILE_ATTRIBUTE_NORMAL, which means that no other attribute is set for that file.
This attribute is also returned for a writeable as well as non-readonly files.
We use this information to set the permissions for files in our product code.
We concluded that GetFileAttributesEx might be returning incorrect attribute in case of unreadable files. We wonder if our conclusion is right or not.
If yes, then is this a known issue with GetFileAttributesEx ?
If not then
What is the correct way to get the file attributes (file permissions perhaps ?) for an unreadable file using Windows API or if possible using Boost or standard C++ filesystem libraries ?

It's probably not succeeding at all. If you look at the documentation for GetFileAttributesEx, it actually returns a BOOL.
Return value If the function succeeds, the return value is a nonzero
value.
If the function fails, the return value is zero (0). To get extended
error information, call GetLastError.
My guess is that "fwad" is undefined if the call fails. Try checking the return value for a failure indication. My guess is that GetLastError will return something like ERROR_ACCESS_DENIED.
The Windows API doesn't throw exceptions, so unfortunately you'll have to check just about every return value.

Related

Can GetFileAttributesW handle url-encoded spaces (%20) in file paths?

Function call:
OS_WRAPI::GetFileAttributesW(file_name_str); // file_name_str value is L"C:\\Test%20Tool\\test.exe"
returns INVALID_FILE_ATTRIBUTES.
Function call:
OS_WRAPI::GetFileAttributesW(file_name_str); // file_name_str value is L"C:\\TestTool\\test.exe"
returns valid attributes.
Both paths exist.
How can I get the file attributes in the case of url-encoded spaces (%20) in the file path?
Can GetFileAttributesW handle spaces in filepaths?
Yes.
Note that L"C:\\Test%20Tool\\test.exe" does not contain a space. Probably you should be passing L"C:\\Test Tool\\test.exe".
The documentation says:
If the function fails, the return value is INVALID_FILE_ATTRIBUTES. To get extended error information, call GetLastError.
So you should, in case INVALID_FILE_ATTRIBUTES is returned, call GetLastError. I'd expect that to return ERROR_PATH_NOT_FOUND.
If the question is actually
Can GetFileAttributesW handle L"%20" in filepaths?
The answer is still yes. If that path really exists, and GetFileAttributesW is returning INVALID_FILE_ATTRIBUTES, then there must be some other problem, but the presence of L"%20" in a file name presents no problems for the Windows API. Again start by calling GetLastError.
Perhaps what's really at issue here, is that you think that Windows uses L"%20" to encode a string in the file system. It does not. On the file system, L"%20" and L" " are two distinct names.

YAML::LoadFile(std::string const&) does not find file [yaml-cpp in ROS]

I'm trying to use data from a yaml file in a ROS(kinetic)/ c++ code so yaml-cpp seems like a good option for me. My code yields no errors but does not work properly:
It seems like the YAML::LoadFile function is not able to find my file since the following lines go to exception:
YAML::Node yamlnode_;
try{
yamlnode_= YAML::LoadFile("../yaml_file.yaml");
}
catch(std::exception &e){
ROS_ERROR("Failed to load yaml file");
}
Including yaml-cpp via
#include <yaml-cpp/yaml.h>
seems to work since YAML:: functions are recognized afterwards.
The path ../yaml_file.yaml is set up correctly which I also checked in program via
#include "../yaml_file.yaml"
which yields parsing errors (as expected) which show me that the correct file was found (but obviously cannot be included).
The yaml_file.yaml is used successfully in multiple .xacro files.
Keep in mind that I am somewhat new to ROS and yaml-cpp; I'm looking forward to see your questions and answers
That including the YAML file using #include "../yaml_file.yaml" throws errors only indicates that that file resides somewhere in the parent directory of either the current directory or one of the directories in the include path the compiler uses.
When you use "../yaml_file.yaml", for loading, then the file needs to be in the parent directory of the current directory at that point of execution. That is normally the parent directory of the directory from which you launch the program (which doesn't have to be the directory in which your program resides). But if your program changes the current directory (using chdir()), and you have no idea where it might "be", you can just want to print the result of getcwd() to get your bearings.
You should use #include <unistd.h> for getcwd() , and it takes a pointer to a character buffer large enough to hold the path and as second parameter the (size_t) that buffer can hold. (With some implementations, you can pass NULL, and a buffer that is large enough is allocated, in that case use the return value)

Could DropBox interfere with DeleteFile()/rename()

I had the following code which got executed every two
minutes all day long:
int sucessfully_deleted = DeleteFile(dest_filename);
if (!sucessfully_deleted)
{
// this never happens
}
rename(source_filename,dest_filename);
Once every several hours the rename() would fail with errno=13 (EACCES). The files involved were all sitting on a DropBox directory and I had a hunch that DropBox could be the cause. I figured that it might just be possible that the DeleteFile() function may return with a non-zero successfully_deleted but actually DropBox could still be busy doing some stuff in relation to the deletion that prevented rename() from succeeding. What I did next was to change rename() to my_rename() which would attempt a rename() and upon any failure would Sleep() for one second and try a second time. Sure enough that has worked perfectly ever since. What's more, I get a diagnostic message displaying first-attempt-failures every several hours. It has never failed on the second attempt.
So you could say that the problem is entirely solved... but I would like to understand what might be going on so as to better defend myself against any related DropBox issues in the future...
Really I would like to have a new super_delete() function which does not return until the file is properly deleted and finished with in all respects.
under windows request to delete file really never delete file just. it mark it FCB (File Control Block) with special flag (FCB_STATE_DELETE_ON_CLOSE). real deletion will be only when the last file handle will be closed.
The DeleteFile function marks a file for deletion on close. Therefore,
the file deletion does not occur until the last handle to the file is
closed. Subsequent calls to CreateFile to open the file fail with
ERROR_ACCESS_DENIED.
also if exist section ( memory-mapped file ) open on file - file even can not be marked for delete. api call fail with STATUS_CANNOT_DELETE. so in general impossible always delete file.
in case exist another open handles for file (but not section !) begin from windows 10 rs1 exist new functional for delete - FileDispositionInformationEx with FILE_DISPOSITION_POSIX_SEMANTICS. in this case:
Normally a file marked for deletion is not actually deleted until all
open handles for the file have been closed and the link count for the
file is zero. When marking a file for deletion using
FILE_DISPOSITION_POSIX_SEMANTICS, the link gets removed from the visible namespace as soon as the POSIX delete handle has been closed,
but the file’s data streams remain accessible by other existing
handles until the last handle has been closed.
ULONG DeletePosix(PCWSTR lpFileName)
{
HANDLE hFile = CreateFileW(lpFileName, DELETE, FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_OPEN_REPARSE_POINT, 0);
if (hFile == INVALID_HANDLE_VALUE)
{
return GetLastError();
}
static FILE_DISPOSITION_INFO_EX fdi = { FILE_DISPOSITION_DELETE| FILE_DISPOSITION_POSIX_SEMANTICS };
ULONG dwError = SetFileInformationByHandle(hFile, FileDispositionInfoEx, &fdi, sizeof(fdi))
? NOERROR : GetLastError();
// win10 rs1: file removed from parent folder here
CloseHandle(hFile);
return dwError;
}
Update
Sorry i didn't get the question correctly the first time. I thought DeleteFile returned error 13.
Now I understand that DeleteFile succeeds but rename fails immediatlely after.
It could be just a sync issue with the filesystem. After calling DeleteFile the file will be deleted when the OS commits the changes to the filesystem. That may not appen immediately.
If you need to perform multiple operations to the same path, you should have a look at transactions https://learn.microsoft.com/it-it/windows/desktop/api/winbase/nf-winbase-deletefiletransacteda.
-- OLD ANSWER --
That is correct. If the another application handles to that file, DeleteFile will fail.
Citing MSDN docs https://learn.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-deletefile :
The DeleteFile function fails if an application attempts to delete a file that has other handles open for normal I/O or as a memory-mapped file (FILE_SHARE_DELETE must have been specified when other handles were opened).
This applies to dropbox, the antivirus, or in general, any other application that may open those files.
Dropbox may open the file to compute its hash (to look for changes) at any moment. Same goes with the antivirus.

How to know why GetPrivateProfileInt/String fail

I am working on a piece of code in the old application, using C++ in Visual Studio 2008. It attempts to read some values from an INI file, and I'm trying to do some error handling in the case of the section or key I am trying to read does not exist in the file.
Here is the code snippet:
int nValue = GetPrivateProfileInt (SECTION, KEY, -1, sINIFile);
if(nValue == -1) // default value
{
int nLastError = GetLastError();
if(nLastError != 0)
{
// do something
}
}
sINIFile contains the full path to my required INI file.
After some testing, I've found that GetLastError() returns 2 (ERROR_FILE_NOT_FOUND) if:
the file doesn't exist OR,
the section doesn't exist OR,
the key doesn't exist.
I would like to know the specific reason the GetPrivateProfileInt returned the default value - is this possible?
Note: I've looked into checking the value of 'errno' (or errorno, as it says on the MSDN page) but this is always 0 for any of the above cases.
If GetLastError() won't help, then you need to do some detective work. You should check yourself if the registry key or file exists (you know that most ini files are mapped to the registry, right?), or if the section doesn't exist (GetPrivateProfileSection), and if neither of those apply, then the key doesn't exist.

Getting the file-mode from the FILE struct?

I have a piece of C code, a function to be specific, which operates on a FILE*.
Depending on which mode the FILE* was opened with there are certain things I can and cannot do.
Is there any way I can obtain the mode the FILE* was opened with?
That FILE* is all the info I can rely on, because it is created somewhere else in the program and the actual file-name is long lost before it reaches my function, and this I cannot influence.
I would prefer a portable solution.
Edit: I'm not interested in file-restrictions specifying which users can do what with the file. That is mostly irrelevant as it is dealt with upon file-opening. For this bit of code I only care about the open-mode.
On POSIX (and sufficiently similar) systems, fcntl(fileno(f), F_GETFL) will return the mode/flags for the open file in the form that would be passed to open (not fopen). To check whether it was opened read-only, read-write, or write-only, you can do something like:
int mode = fcntl(fileno(f), F_GETFL);
switch (mode & O_ACCMODE) {
case O_RDONLY: ...
case O_WRONLY: ...
case O_RDWR: ...
}
You can also check for flags like O_APPEND, etc.
Assuming Linux/Unix:
See fstat(), to get the details of file permissions.
To get the file descriptor us fileno() for that function