This question already has answers here:
Checking if a directory exists in Unix (system call)
(5 answers)
Closed 4 years ago.
How would I determine if a directory (not a file) existed using C++ in Linux? I tried using the stat() function but it returned positive when a file was found. I only want to find if the inputted string is a directory, not something else.
According to man(2) stat you can use the S_ISDIR macro on the st_mode field:
bool isdir = S_ISDIR(st.st_mode);
Side note, I would recommend using Boost and/or Qt4 to make cross-platform support easier if your software can be viable on other OSs.
how about something i found here
#include <dirent.h>
bool DirectoryExists( const char* pzPath )
{
if ( pzPath == NULL) return false;
DIR *pDir;
bool bExists = false;
pDir = opendir (pzPath);
if (pDir != NULL)
{
bExists = true;
(void) closedir (pDir);
}
return bExists;
}
Or using stat
struct stat st;
if(stat("/tmp",&st) == 0)
if(st.st_mode & S_IFDIR != 0)
printf(" /tmp is present\n");
If you can check out the boost filesystem library. It's a great way to deal with this kind of problems in a generic and portable manner.
In this case it would suffice to use:
#include "boost/filesystem.hpp"
using namespace boost::filesystem;
...
if ( !exists( "test/mydir" ) ) {bla bla}
The way I understand your question is this: you have a path, say, /foo/bar/baz (baz is a file) and you want to know whether /foo/bar exists. If so, the solution looks something like this (untested):
char *myDir = dirname(myPath);
struct stat myStat;
if ((stat(myDir, &myStat) == 0) && (((myStat.st_mode) & S_IFMT) == S_IFDIR)) {
// myDir exists and is a directory.
}
In C++17**, std::filesystem provides two variants to determine the existence of a path:
is_directory() determines, if a path is a directory and does exist in the actual filesystem
exists() just determines, if the path exists in the actual filesystem (not checking, if it is a directory)
Example (without error handling):
#include <iostream>
#include <filesystem> // C++17
//#include <experimental/filesystem> // C++14
namespace fs = std::filesystem;
//namespace fs = std::experimental::filesystem; // C++14
int main()
{
// Prepare.
const auto processWorkingDir = fs::current_path();
const auto existingDir = processWorkingDir / "existing/directory"; // Should exist in file system.
const auto notExistingDir = processWorkingDir / "fake/path";
const auto file = processWorkingDir / "file.ext"; // Should exist in file system.
// Test.
std::cout
<< "existing dir:\t" << fs::is_directory(existingDir) << "\n"
<< "fake dir:\t" << fs::is_directory(notExistingDir) << "\n"
<< "existing file:\t" << fs::is_directory(file) << "\n\n";
std::cout
<< "existing dir:\t" << fs::exists(existingDir) << "\n"
<< "fake dir:\t" << fs::exists(notExistingDir) << "\n"
<< "existing file:\t" << fs::exists(file);
}
Possible output:
existing dir: 1
fake dir: 0
existing file: 0
existing dir: 1
fake dir: 0
existing file: 1
**in C++14 std::experimental::filesystem is available
Both functions throw filesystem_error in case of errors. If you want to avoid catching exceptions, use the overloaded variants with std::error_code as second parameter.
#include <filesystem>
#include <iostream>
namespace fs = std::filesystem;
bool isExistingDir(const fs::path& p) noexcept
{
try
{
return fs::is_directory(p);
}
catch (std::exception& e)
{
// Output the error message.
const auto theError = std::string{ e.what() };
std::cerr << theError;
return false;
}
}
bool isExistingDirEC(const fs::path& p) noexcept
{
std::error_code ec;
const auto isDir = fs::is_directory(p, ec);
if (ec)
{
// Output the error message.
const auto theError = ec.message();
std::cerr << theError;
return false;
}
else
{
return isDir;
}
}
int main()
{
const auto notExistingPath = fs::path{ "\xa0\xa1" };
isExistingDir(notExistingPath);
isExistingDirEC(notExistingPath);
}
If you want to find out whether a directory exists because you want to do something with it if it does (create a file/directory inside, scan its contents, etc) you should just go ahead and do whatever you want to do, then check whether it failed, and if so, report strerror(errno) to the user. This is a general principle of programming under Unix: don't try to figure out whether the thing you want to do will work. Attempt it, then see if it failed.
If you want to behave specially if whatever-it-was failed because a directory didn't exist (for instance, if you want to create a file and all necessary containing directories) you check for errno == ENOENT after open fails.
I see that one responder has recommended the use of boost::filesystem. I would like to endorse this recommendation, but sadly I cannot, because boost::filesystem is not header-only, and all of Boost's non-header-only modules have a horrible track record of causing mysterious breakage if you upgrade the shared library without recompiling the app, or even if you just didn't manage to compile your app with exactly the same flags used to compile the shared library. The maintenance grief is just not worth it.
Related
I am coding a simple replacement for std::filesystem::exists() function using Windows API. Surprisingly, it turned out to be pretty hard. I want to keep my code simple, so I am using minimum functions. My function of choice is GetFileAttributesW(). Code is tested with fs::recursive_directory_iterator() function. My function thinks that all files in “C:\Windows\servicing\LCU*” don’t exist (ERROR_PATH_NOT_FOUND). This directory is responsible for storing Windows Update Caches and is famous for having extremely long file names. I couldn’t find anything else about this directory. Example of filenames and my code are included below. Hope this helps!
Edited:
The solution to this problem is to prepend absolute file path with “\\?\” char sequence. It makes Windows handle short files correctly!
C:\Windows\servicing\LCU\Package_for_RollupFix~31bf3856ad364e35~amd64~~19041.2006.1.7\amd64_microsoft-windows-a..g-whatsnew.appxmain_31bf3856ad364e35_10.0.19041.1741_none_ee5d4a8d060d7653\f\new360videossquare44x44logo.targetsize-16_altform-unplated_contrast-black.png
C:\Windows\servicing\LCU\Package_for_RollupFix~31bf3856ad364e35~amd64~~19041.2006.1.7\amd64_microsoft-windows-a..g-whatsnew.appxmain_31bf3856ad364e35_10.0.19041.1741_none_ee5d4a8d060d7653\f\new360videossquare44x44logo.targetsize-16_altform-unplated_contrast-white.png
C:\Windows\servicing\LCU\Package_for_RollupFix~31bf3856ad364e35~amd64~~19041.2006.1.7\amd64_microsoft-windows-a..g-whatsnew.appxmain_31bf3856ad364e35_10.0.19041.1741_none_ee5d4a8d060d7653\f\new360videossquare44x44logo.targetsize-20_altform-unplated_contrast-black.png
C:\Windows\servicing\LCU\Package_for_RollupFix~31bf3856ad364e35~amd64~~19041.2006.1.7\amd64_microsoft-windows-a..g-whatsnew.appxmain_31bf3856ad364e35_10.0.19041.1741_none_ee5d4a8d060d7653\f\new360videossquare44x44logo.targetsize-20_altform-unplated_contrast-white.png
#include <windows.h>
#include <filesystem>
#include <iostream>
#include <string>
using namespace std;
namespace fs = std::filesystem;
int FileExists(wstring file_path) {
/* TODO:
1. Doesn't work with "C:\\Windows\\servicing\\LCU\\*".
2. Improve error system.
*/
DWORD attributes = GetFileAttributesW(file_path.c_str());
// Valid attributes => File exists
if (attributes != INVALID_FILE_ATTRIBUTES) {
return true;
}
DWORD error_code = GetLastError();
wcout << error_code << ' ' << file_path << '\n';
// Path related error => File doesn't exist
if (error_code == ERROR_PATH_NOT_FOUND || error_code == ERROR_INVALID_NAME ||
error_code == ERROR_FILE_NOT_FOUND || error_code == ERROR_BAD_NETPATH)
{
return false;
}
// Other errors are logged before if statement
// File is busy with IO operations, etc.
return error_code;
}
int main() {
for (fs::path path : fs::recursive_directory_iterator("C:\\", fs::directory_options::skip_permission_denied)) {
FileExists(path);
}
return 0;
}
The solution that worked for me is to prepend absolute file path with “\\?\” char sequence. Somehow, it makes Windows handle shortened file paths correctly!
Check out MSDN Article "Maximum File Path Limitation" for more info.
Assumption
According to the documentation, calling rename on Linux performs an atomic replace:
If newpath already exists, it will be atomically replaced, so that there is no point at which another process attempting to access newpath will find it missing.
Contradiction
However, if I run a simple parallel test, with each thread running the following operations:
create a file foo<thread_id>
rename foo<thread_id> to cache_file (cache_file is the same for every thread)
hard link cache_file to bar<thread_id>
it will eventually fail to create the hard link with the following error:
filesystem error: cannot create hard link: No such file or directory [/app/cache_file] [/app/bar1]. So it seems that the replacement of cache_file is not atomic, as concurrently creating a hard link causes an error. (Note that cache_file is actually stored in a content addressable storage, so the overwrite shouldn't do any harm, as the content of the replaced file and the replacement file is exactly the same.)
Question
Shouldn't the hard link creation always succeed if the replacement operation is atomic, so that the created hard link refers to either the replaced file or the replacement file?
See the minimal working example on godbolt or here:
#include <thread>
#include <vector>
#include <string>
#include <algorithm>
#include <iostream>
#include <fstream>
#include <filesystem>
#include <cstdio>
#include <fcntl.h>
#include <unistd.h>
auto myrename(std::filesystem::path const& from,
std::filesystem::path const& to, int variant) -> bool {
switch (variant) {
case 0: // c++ rename
std::filesystem::rename(from, to);
return true;
case 1: // c rename
return std::rename(from.c_str(), to.c_str()) == 0;
case 2: // linux rename (same as std::rename?)
return rename(from.c_str(), to.c_str()) == 0;
case 3: // linux link and unlink (no overwrite)
return (link(from.c_str(), to.c_str()) == 0 or errno == EEXIST)
and unlink(from.c_str()) == 0;
case 4: // linux renameat2 without overwrite
return renameat2(0, from.c_str(), 0, to.c_str(), RENAME_NOREPLACE) == 0
or (errno == EEXIST and unlink(from.c_str()) == 0);
default:
return false;
}
}
auto mylink(std::filesystem::path const& from, std::filesystem::path const& to,
int variant) -> bool {
if (std::filesystem::exists(to)) std::filesystem::remove(to);
switch (variant) {
case 0: // c++ hard link
std::filesystem::create_hard_link(from, to);
return true;
case 1: // linux link
return link(from.c_str(), to.c_str()) == 0;
default:
return false;
}
}
auto create_store_stage(std::string const& id) noexcept -> bool {
try {
auto cwd = std::filesystem::current_path();
auto cache = cwd / "cache_file"; // common
auto ifile = cwd / ("foo" + id); // thread local
auto ofile = cwd / ("bar" + id); // thread local
return std::ofstream{ifile}.put('x') // 1. create input file
and myrename(ifile, cache, 0) // 2. store in cache
and mylink(cache, ofile, 0); // 3. hard link to output file
} catch (std::exception const& e) {
std::cout << "caught exception: " << e.what() << std::endl;
return false;
}
}
int main(int argc, const char *argv[]) {
bool fail{};
std::vector<std::thread> threads{};
for (int i{}; i < std::thread::hardware_concurrency(); ++i) {
threads.emplace_back([id = std::to_string(i), &fail]{
while (not fail and create_store_stage(id)) {}
if (errno) perror(("thread " + id + " failed with error").c_str());
fail = true;
});
}
std::for_each(threads.begin(), threads.end(), [](auto& t) { t.join(); });
return 0;
}
Additional Notes
tested on Debian 11 (Kernel 5.10.0) and Ubuntu 20.04 (Kernel 5.8.0)
tested with GCC 9.3/10.2 and Clang 10.0.0/11.0.0 (although I don't expect the compiler to be the issue)
myrename() variants 3 and 4 work correctly (both do not overwrite, which is fine for a content addressable storage)
as expected, neither variant 0 nor 1 of mylink() does make any difference (both use link(), according to strace)
interesting: on WSL2 with Ubuntu 20.04 (Kernel 4.4.0) the myrename() variants 0, 1, and 2 work correctly, but 3 and 4 fail with filesystem error: cannot create hard link: Invalid argument [/app/cache_file] [/app/bar3] and Invalid argument, respectively
*Update
as pointed out by the busybee, link() should be atomic as well. The Linux man pages do not mention any atomic properties, while the POSIX specification explicitly does:
The link() function shall atomically create a new link for the existing file and the link count of the file shall be incremented by one.
as mentioned by numzero, this could be an unintended side-effect. But I did some testing and this behavior dates back to at least Kernel version 2.6.32.
I used following code.
dp = opendir( dir.c_str() );
while ((dirp = readdir( dp )))
{
filepath = dir + "/" + dirp->d_name;
}
But dirp->d_name value is as follows.
.\000\000\000\004\324E\020\000\000\000\000\000\324!+^S\361Tf\030\000\004..\000\000\004\237X\n\000\000\000\000\000fJ\035\224\321M\264l(\000\bFontTest1.pdf\000\000\000\000\000\000\000\b\236X\n\000\000\000\000\000\377\377\377\377\377\377\377\177(\000\bproject_report.pdf\000\000\b
Are you perhaps mis-handling the "." and ".." dirs? (every dir has them)
I excluded them with:
std::string fn(ent->dname);
if(fn == ".") { if(dbg2) { std::cout << "S_DOT" << std::endl; } continue;}
if(fn == "..") { if(dbg2) { std::cout << "D_DOT" << std::endl; } continue;}
... prior to handling the various d_types
switch(ent->d_type)
{
case DT_UNKNOWN: {...}
case DT_DIR: {...}
// ... etc
}
The "continue" jumped to the beginning of the loop processing dirent entries.
The best cross platform way to do this is if you have the filesystem library. Unfortunately that's still a Technical Specification right now, so you'll need to either use Boost's version of this library or: experimental/filesystem.
Once you have filesystem though you can simply use a directory_iterator:
copy(directory_iterator(dir), directory_iterator(), ostream_iterator<path>(cout, "\n"))
That example may have been a bit complex. If I can clarify something for you let me know.
In Visual Studio 2015 this code runs if you simply #include <filesystem> and do using namespace tr2::sys. Unfortunately gcc 5.3 hasn't implemented directory_iterator.operator++() yet, so you'll need Boost there or you'll get an error along the lines of:
Undefined reference to std::experimental::filesystem::v1::__cxx11::directory_iterator::operator++()
Thanks all. I am able to sort it out by adding following code.
unsigned char isFile =0x8;
if ( dirp->d_type == isFile)
//process it
I'm trying to code a program where it opens and reads a file automatically. But the problem is the file is stored in a folder which name is unknown. I only know where the folder is located and the file's name. How to get to that file's path in char* ?
Edit: example: d:\files\<random folder>\data.txt
I don't know the name of random folder but I know that it exists in d:\files
Since this is tagged windows, you might as well use the Windows API functions:
FindFirstFile()
FindNextFile()
to enumerate and loop through all the files in a given directory.
To check for a directory, look at dwFileAttributes contained in the WIN32_FIND_DATA structure (filled by the calls to Find...File()). But make sure to skip . and .. directories. If needed, this can be done recursively.
You can check the links for some examples, or see Listing the Files in a Directory.
In case you are using MFC, you can use CFileFind (which is a wrapper around the API functions):
CFileFind finder;
BOOL bWorking = finder.FindFile(_T("*.*"));
while (bWorking)
{
bWorking = finder.FindNextFile();
TRACE(_T("%s\n"), (LPCTSTR)finder.GetFileName());
}
Just for fun, I implemented this using the new, experimental <filesystem> FS Technical Specification supported by GCC 5.
#include <iostream>
#include <experimental/filesystem>
// for readability
namespace fs = std::experimental::filesystem;
int main(int, char* argv[])
{
if(!argv[1])
{
std::cerr << "require 2 parameters, search directory and filename\n";
return EXIT_FAILURE;
}
fs::path search_dir = argv[1];
if(!fs::is_directory(search_dir))
{
std::cerr << "First parameter must be a directory: " << search_dir << '\n';
return EXIT_FAILURE;
}
if(!argv[2])
{
std::cerr << "Expected filename to search for\n";
return EXIT_FAILURE;
}
// file to search for
fs::path file_name = argv[2];
const fs::directory_iterator dir_end; // directory end sentinel
// used to iterate through each subdirectory of search_dir
fs::directory_iterator dir_iter(search_dir);
for(; dir_iter != dir_end; ++dir_iter)
{
// skip non directories
if(!fs::is_directory(dir_iter->path()))
continue;
// check directory for file
// iterate through files in this subdirectory dir_iter->path()
auto file_iter = fs::directory_iterator(dir_iter->path());
for(; file_iter != dir_end; ++file_iter)
{
// ignore directories and wrong filenames
if(fs::is_directory(file_iter->path())
|| file_iter->path().filename() != file_name)
continue;
// Ok we found it (the first one)
std::cout << "path: " << file_iter->path().string() << '\n';
return EXIT_SUCCESS;
}
}
// Not found
std::cout << file_name << " was not found in " << search_dir.string() << '\n';
return EXIT_FAILURE;
}
The idea is: list the directories under d:\files and try to open
the file in each directory.
There isn't (yet) a standard C++ way of getting all the existing files/directories. A crude but easy way of doing this would be
system("dir d:\\files /b /ad > tmpfile");
This lists all directories (/ad), redirected to a temporary file. Then open the file:
std::ifstream list("tmpfile");
And read it:
std::string dirname;
std::string filename;
while (std::getline(list, dirname))
{
filename = "d:\\files\\" + dirname + "\\data.txt";
if ( ... file exists ... )
break;
}
I call this method crude because it has problems that are hard/impossible to fix:
It overwrites a potentially useful file
It doesn't work if current directory is read-only
It will only work in Windows
It might be possible to use _popen and fgets instead of redirecting to file.
This question already has answers here:
Checking if a dir. entry returned by readdir is a directory, link or file. dent->d_type isn't showing the type
(4 answers)
Closed 3 years ago.
I am trying to list all the files in a certain directory on a shared drive using the following code:
#include <iostream>
#include <string>
#include "dirent.h"
int main ()
{
DIR *directoryHandle = opendir("./temp/");
if (NULL != directoryHandle)
{
dirent *entry = readdir(directoryHandle);
while (NULL != entry)
{
//skip directories and select only files (hopefully)
if ((DT_DIR != entry->d_type) && (DT_REG == entry->d_type || DT_UNKNOWN == entry->d_type))
{
std::cout << "Name: " << entry->d_name << " Type:" << std::to_string(entry->d_type) << std::endl;
}
//go to next entry
entry = readdir(directoryHandle);
}
closedir(directoryHandle);
}
return 0;
}
The problem is that entry->d_type contains DT_UNKNOWN for directories as well as for files in the ./temp/ directory.
Is there any (reliable) linux-specific way to try and read each entry and determine if it's a file or directory?
The output of cat /etc/SuSE-release is:
SUSE Linux Enterprise Desktop 11 (x86_64) VERSION = 11 PATCHLEVEL = 1
The Linux version is: 2.6.32.59-0.7-default
Still, I expect this code to work on other platforms as well.
Use stat. You'll get a struct stat with an st_mode field, and macros S_ISDIR/S_ISREG:
isDirectory = S_ISDIR(statBuf.st_mode);
isFile = S_ISREG(statBuf.st_mode);
See man 2 stat. Include sys/stat.h.
If you are getting DT_UNKNOWN you're going to have to go ahead and call lstat() and inspect the st_mode field to determine if this is a file, directory, or symlink. If you don't care about symlinks, use stat() instead.
There's the boost filesystem library that has a command is_directory.
Using such a library would certainly make the code work on other platforms as well but I'm not sure if it will work for your specific problem.
Give this a try. It list files in a directory minus the folders :
#include <dirent.h>
#include <stdio.h>
# include <sys/types.h>
# include <sys/mode.h>
# include <stat.h>
DIR *d;
struct dirent *dir;
struct stat s;
d = opendir("./temp/");
if (d)
{
while ((dir = readdir(d)))
{
if (stat(dir->d_name,&s) != 0) {
/* is this a regular file? */
if ((s.st_mode & S_IFMT) == S_IFREG)
printf("%s\n", dir->d_name);
}
}
closedir(d);
}
unsigned char isFolder =0x4;
DIR Dir;
struct dirent *DirEntry;
Dir = opendir("c:/test/")
while(Dir=readdir(Dir))
{
cout << DirEntry->d_name;
if ( DirEntry->d_type == isFolder)
{
cout <<"Found a Directory : " << DirEntry->d_name << endl;
}
}