a better way than stat() to look for files in dir? - c++

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 :-).

Related

Best way to check if file matching abc* exists in a particular directory

I want to know how to check if a file matching core* exists in /tmp/ path. The actual file can be anything like below.
corexyz.txt
coreabc.tar.gz
core*
If the complete file name was known, I could have used stat or access to know if the file existed. But here, I want to know if there is any file matching core*.
bool checkFile (std::string ABC)
{
return true; /* return true if file ABC* exists */
}
If you can use c++17 the filesystem library will be interesting for you.
You can use std::filesystem::directory_iterator to iterate over all the files in a directory and check if they start with the given prefix using path.filename().

Avoid TOCTOU (time-of-check, time-of-use) race condition between stat and rename

How to avoid TOCTOU(time-of-check, time-of-use) race condition for race condition between stat and rename for LOGFILE ?
Required to move the log file after its size value exceeds the max size.
result = stat(LOGFILE, & data);
if (result != 0) {
// stat failed
// file probably does not exist
} else if (data.st_size > MAX_LOGSIZE) {
unlink(PREV_LOGFILE);
(void) rename(LOGFILE, PREV_LOGFILE);
}
The standard way to avoid TOCTTOU on file operations is to open the file once and then do everything that you need through the file descriptor rather than the file name.
However, both renaming and unlinking a file require its path (because they need to know what link to rename or remove), so you can't use that approach here. An alternative might be to copy the file's contents elsewhere and then truncate it to zero bytes, although your scenario with log files probably requires that the operation be atomic, which may be difficult to achieve. Another approach is to require tight access controls on the directory: if an attacker cannot write to the directory, then it cannot play TOCTTOU games with your process. You can use unlinkat and renameat to restrict your paths to a specific directory's file descriptor so that you don't need to worry about the directory itself changing.
Something like this untested code might do the job, assuming a POSIX-like platform:
dirfd = open(LOGDIR, O_DIRECTORY);
// check for failure
res = fstatat(dirfd, LOGFILE, statbuf, AT_SYMLINK_NOFOLLOW);
if ((0 == res) && (S_ISREG(statbuf) && (data.st_size > MAX_LOGSIZE)) {
unlinkat(dirfd, PREV_LOGFILE, 0);
renameat(dirfd, LOGFILE, dirfd, PREV_LOGFILE);
}
close(dirfd);

C++ - What permissions does stat() need?

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).)

Finding the file path of all files in a folder

I'm trying to convert a bunch of images to textures using SDL. So far, I know its possible to do everything manually:
//Load front alpha texture
if (!gModulatedTexture.loadFromFile("14_animated_sprites_and_vsync/text2.png"))
{
printf("Failed to load front texture!\n");
success = false;
}
else
.....
However, I have quite a few images I want to load so what I'm looking for is a way to automate the process. I want to put all my images into a single folder, and then do something like this:
i=0
while (there are still images to load) {
textureBank[i] = current image
i++
}
I wast thinking there might be some easy way to just read in the file path of all the files in a directory, but I haven't been able to find a way to do that.
Any suggestions?
You don't need to use any 3rd-party library like boost, just call the following function (for Windows OS). After this, you will get all file paths within given folder in vector<string>.
#include <Windows.h>
// folder must end with "/", e.g. "D:/images/"
vector<string> get_all_files_full_path_within_folder(string folder)
{
vector<string> names;
char search_path[200];
sprintf(search_path, "%s*.*", folder.c_str());
WIN32_FIND_DATA fd;
HANDLE hFind = ::FindFirstFile(search_path, &fd);
if(hFind != INVALID_HANDLE_VALUE)
{
do
{
// read all (real) files in current folder, delete '!' read other 2 default folder . and ..
if(! (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) )
{
names.push_back(folder+fd.cFileName);
}
}while(::FindNextFile(hFind, &fd));
::FindClose(hFind);
}
return names;
}
Since you are using SDL, I’ll assume you want to be cross-platform. The boost::filesystem library can do this.
Take a look at their directory iteration example.
Although it’s part of a 3rd-party library, boost::filesystem is proposed for inclusion in a future C++ standard, TR2, so it’s worth the effort to learn. It should eventually be the standard C++ way to work with files and directories.

Proper way to test for readability/writability of a folder

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.