I've got some problems using the stat() function.
What I'm doing is very simple, and I remember it worked on another machine.
I have a directory structure like this:
/home/bernd/dir
/home/bernd/dir/file.ext
/home/bernd/dir/anotherDir
and so on...
What I want to do is to distinguish between files and directories with this source code:
DIR *dir = opendir("/home/bernd/dir");
struct dirent *pent;
while(pent = readdir(dir))
{
if((strcmp(pent->d_name,".") == 0) || (strcmp(pent->d_name,"..") == 0)
continue;
struct stat st;
string tmp = "/home/bernd/dir/" + pent->d_name;
if(stat(tmp.c_str(),&st) == -1)
cout<<strerror(errno);
else
//would be happy to get here
}
As you can see I'm simply walking through the directory and calling stat on the current element, but the stat call always returns Permission Denied. I thought I was messing with relative paths at first, or I was calling stat on the wrong path, which is kept in the string tmp, but I checked them and everything was fine.
The next thing was of course to change the permissions of the files and directories, so anyone can read and write, but the result did not change.
I really hope you guys can help me in any way and your help is highly appreciated!
Thank you in advance!
Do you have execute permission to /home/bernd/dir? Read permission only allows you to list the directory without necessarily being able to access any of its contents.
(On the other hand, execute permission without read permission lets you access the contents but makes the directory unlistable (readdir would fail).)
Related
In a game I'm making, folders with text files inside represent world saves, In the load menu of this game I want to have an option to delete a save. I'm using currently this code to try to delete the saves:
hFind = FindFirstFile((dir+"/*").c_str(), &FindFileData);
if (hFind){
do{
string s = FindFileData.cFileName;
if(s.find('.')){//prevents prossesing of "." and ".."
DeleteFile((dir+"/"+s).c_str());
}
}while(FindNextFile(hFind,&FindFileData));
CloseHandle(hFind);
}
rmdir(dir.c_str());
The only things in these folders are 3 text files so this code should be sufficient, however it isn't. What happens is all the file in the directory are deleted but not the folder, and if I try to delete this folder manually, or edit it in any way while the program is running, windows denies me access. But as soon as I close the game the folder is deleted.
I know the files inside are deleted because I tried the above code with out "rmdir(dir.c_str());" and opened the folder and all the files were gone, also with the above code if I "Delete" the save and then try to load it I there is no world and no inventory, indicating the files have been deleted.
I've tried it with removeDirectory and the same thing happens, It also says it was deleted successfully without any errors.
Why does this happen? How can I avoid this, and get it to work properly?
Any help would be greatly appreciated.
The problem was fixxed with the following code:
hFind = FindFirstFile((dir+"/*").c_str(), &FindFileData);
if (hFind){
do{
string s = FindFileData.cFileName;
if(s.find('.')){//prevents prossesing of "." and ".."
DeleteFile((dir+"/"+s).c_str());
}
}while(FindNextFile(hFind,&FindFileData));
CloseHandle(hFind);
}
findClose(hFind);
rmdir(dir.c_str());
According to the RemoveDirectory documentation:
RemoveDirectory function marks a directory for deletion on close. Therefore, the directory is not removed until the last handle to the directory is closed.
Probably your program has the directory as its current working directory, or perhaps otherwise still has a handle to it open.
In windows, rmdir is a comparability function that calls the native windows functions, so it will behave the same.
The root problem is that the code called CloseHandle instead of FindClose on the handle returned by FindFirstFile.
But the code has several more bugs. In the interest of helping future visitors here, this is the corrected code.
HANDLE hFind = FindFirstFile((dir + "\\*").c_str(), &FindFileData); // See 1 below
if (hFind != INVALID_HANDLE_VALUE) { // 2
do {
if ((FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) { // 3
const std::string s = dir + "\\" + FindFileData.cFileName;
DeleteFile(s.c_str());
}
} while (FindNextFile(hFind, &FindFileData));
// 4
FindClose(hFind); // 5
}
RemoveDirectory(dir.c_str()); // 6
Windows paths use \ rather than / as separators. Many of the APIs will accept either, but eventually you'll encounter one that doesn't, so it's best to use the correct one consistently.
FindFirstFile returns INVALID_HANDLE_VALUE (not NULL) upon failure. Since INVALID_HANDLE_VALUE is non-zero, you cannot simply test if (hFile) { ... }.
The API enumerates files and directories. The old code was trying to filter out the . and .. directories incorrectly, which could have caused it to skip some files and to attempt to use DeleteFile on other directories. It's simpler (and easier-to-understand) to skip all directories.
Don't call CloseHandle on the handle returned by FindFirstFile.
Do call FindClose on the handle returned by FindFirstFile, but do so only in the case you got a valid handle from FindFirstFile.
As long as you're using Windows-specific APIs, you may as well use them consistently and not mix with library wrappers like rmdir. The library wrappers sometimes introduce surprising limitations or behavior, though I think rmdir would work correctly in this instance.
This still leaves a significant problem: It doesn't handle Unicode in the paths (and it requires you to compile for ANSI which limits Unicode handling in other parts of the project).
I'm writing a simple C++ webserver assignment and I want to check if the file is readable by other users. If not, the server will send back 403 Forbidden.
I already have a statbuf.st_mode using stat(FILE,&statbuf) but I don't know how to retrieve the read permission for other users. I know there is "S_IROTH" but I don't how to use it. I tried to print it to terminal using cout<<S_IROTH<<endl; and it was 4 but "FILE" has a permission of 0440 so I guess I was not printing the S_IROTH of "FILE".
So my question is: How to get read permission stat for other users?
Or am I making any mistake in concept here?
Thank you.
You need to mask the mode of the file against S_IROTH. Also, you're passing statbuf incorrectly (and you should be getting a warning for it). The correct code should look like:
int result = stat(path, &statbuf);
if (result != 0) {
return NOT_FOUND;
}
if (!(statbuf.st_mode & S_IROTH)) {
return FORBIDDEN;
}
... success, continue ...
I'm trying to find out whether certain files are in a certain folder. However, even though the files exist, the way I try to find them doesn't work in certain folders.
bool FileExists(string strFilename) {
struct stat stFileInfo;
bool blnReturn;
int intStat;
intStat = stat(strFilename.c_str(),&stFileInfo);
if(intStat == 0) {
// We were able to get the file attributes
// so the file obviously exists.
blnReturn = true;
printf("Found file %s\n", strFilename.c_str());
} else {
blnReturn = false;
printf("Didn't find file %s\n", strFilename.c_str());
}
return(blnReturn);
}
When I mount a dir in /mnt/ram .. it doesn't (and sometimes does ) find the files there, however when I use another directory which is on the disk, it always finds the files.
Is there any other way to find out whether files exist in the directory?
Thanks
The result of a stat call or any other directory/file listing depends on permissions of the calling process. /mnt/ram might well be hidden for the current user.
As mentioned in the comments, opendir and readdir are the idiomatic way to get a (recursive) directory listing. Obviously, stat is part of the idiom :-).
I have written a function to test for the readability/writability of a folder.
For unit testing it, I need to produce the different cases:
a folder with readable and writable files
a folder with readable files (not writable)
a folder not writable and not readable.
Here is the code for the function I came to, up to this point:
void FileUtils::checkPath(std::string path, bool &readable, bool &writable)
{
namespace bf = boost::filesystem;
std::string filePath = path + "/test.txt";
// remove a possibly existing test file
remove(filePath.c_str());
// check that the path exists
if(!bf::is_directory(path))
{
readable = writable = false;
return;
}
// try to write in the location
std::ofstream outfile (filePath.c_str());
outfile << "I can write!" << std::endl;
outfile.close();
if(!outfile.fail() && !outfile.bad())
{
writable = true;
}
// look for a file to read
std::ifstream::pos_type size;
char * memblock;
for (bf::recursive_directory_iterator it(path); it != bf::recursive_directory_iterator(); ++it)
{
if (!is_directory(*it))
{
std::string sFilePath = it->path().string();
std::ifstream file(sFilePath.c_str(), std::ios::in|std::ios::binary|std::ios::ate);
if (file.is_open())
{
size = file.tellg();
if(size > 0)
{
memblock = new char [1];
file.seekg (0, std::ios::beg);
file.read (memblock, 1);
file.close();
delete[] memblock;
if(!file.fail() && !file.bad())
{
readable = true;
}
break;
}
}
else
{
// there is a non readable file in the folder
// readable = false;
break;
}
}
}
// delete the test file
remove(filePath.c_str());
}
Now with the tests (done with Google tests):
TEST_F(FileUtilsTest, shouldCheckPath)
{
// given an existing folder
namespace fs = boost::filesystem;
fs::create_directory("/tmp/to_be_deleted");
bool readable = false, writable = false;
FileUtils::checkPath("/tmp/to_be_deleted",readable, writable);
fs::boost::filesystem::remove_all("/tmp/to_be_deleted");
EXPECT_TRUE(readable && writable);
}
I will add more for the other cases when I will have gone further.
Now the game is open to propose a better solution :-)
The foolproof way to check permissions is to literally check the file mode. In the case of directory permissions, the meaning of "readable" and "writable" might be surprising:
Read - allows you to list the contents of the directory
Write - allows you to create, rename, delete files from the directory, essentially modifying the list of contents (also requires execute)
Execute - allows you to access (both read and write) and change properties of files within the directory
So if you have a directory with just the execute bit set, you can still read and write to the files inside. By turning the execute bit off, you can disable access to the files. As far as the contained files are concerned, the most you can figure out from the directory permissions is:
--x or r-x: existing files can be read and written to
-wx or rwx: existing files can be read and written to, files can be created, renamed and deleted
otherwise: you have no access to the files at all
To determine if a file is readable but not writeable (or vice versa) you need to check the permissions of the file itself. The directory can only tell you if the files can be accessed in general.
You can use stat() or access() (see BЈовић's comment) to find out the permissions for a file or directory. Since you're already using boost, you can also use boost::filesystem::status() which simply wraps stat().
To be portable and correct, the only way to test for readability/writability of a file/directory is to read/write from/to it. Permission models can be quite complex and non-portable (ACLs for example), so you can't simply check the permissions on the parent directory. Also, checking, and then subsequently trying to write is a race condition as the permissions could change between the check and the write.
If instead what you want is a high probability that a write will succeed, such as if you're letting the user choose a scratch folder for your application, just try writing a file and then delete it afterwords. This lets you know that at the time of user selection the directory was writable.
To be robust, always assume that filesystem operations are going to fail and design so that when they do, something logical will happen instead of a crash. In particular, design a system so that a user can figure out where the permission error is -- as there's a myriad of ways permissions can be set wrong, helpful error messages go a long way.
Iam trying to get permissions on file/folder for current user. I found nice article about it here. I have tried to run this program but I got few erros and I dont know where I can find solutions to them. I tried to make my own version. Unfortunately when i try:
LPWSTR lpszPrimaryDC = NULL;
NetGetDCName(NULL, L"A", (LPBYTE *)&lpszPrimaryDC);
I got error: NERR_DCNotFound. How I can solve this problem?
The documentation says that error is returned when it "Could not find the domain controller for the domain specified in the domainname parameter." Do you have a domain called "A"? If not, the function is right to fail (and you need to rethink why/how you are calling it).
The only way the code didn't crash and gave me correct answer to question: is file or folder readable?
`
FILE *myFile = fopen(dirPath, "r");
if (myFile == 0) {
// "File or Dir is not readable
}
`
Hope this helps. You can use the same for writing test with "w".