Subtitle: Learning std::filesystem through file_size routines.

Last week I wrote a short post that explained how to use std::filesystem::file_size . Today I’d like to continue and show some significant differences that this new functionality has over the “older” techniques (like reading a file and getting its file position).

We’ll also learn something about permissions and how to manage them in std::filesystem .

Recap

STL before C++17 didn’t contain any direct facilities to work with a filesystem. We could only use third party libraries (like Boost), or system APIs.

With C++17 we have two methods:

std::uintmax_t std::filesystem::file_size(const std::filesystem::path& p);

std::uintmax_t std::filesystem::directory_entry::file_size() const;

For example, here’s a code that returns the file size:

try { const auto fsize = std ::filesystem::file_size( "test.file" ); } catch (fs::filesystem_error& ex) { std :: cout << ex.what() << '

' ; }

What are the advantages (besides shorter code) over the existing C++ methods? Is this method faster?

The Series

This article is part of my series about C++17 Library Utilities. Here’s the list of the topics in the series:

Resources about C++17 STL:

File Permissions

The other, popular technique that is available in C++ (apart from using third-party API) is to open a file and then read its file position (with tellg() ). The first question we may ask is - how about file permission? What if you cannot open a file?

The C++17’s way doesn’t have to open a file, as it reads only file attributes:

From cppreference:

For a regular file p, returns the size determined as if by reading the st_size member of the structure obtained by POSIX stat (symlinks are followed)

We can check this with a simple code:

Let’s create a simple file:

std ::ofstream sample( "hello.txt" ); sample << "Hello World!

" ;

We can read the current file permissions and show them.

void outputPerms(fs::perms p, std ::string_view title) { if (!title.empty()) std :: cout << title << ": " ; std :: cout << "owner: " << ((p & fs::perms::owner_read) != fs::perms::none ? "r" : "-" ) << ((p & fs::perms::owner_write) != fs::perms::none ? "w" : "-" ) << ((p & fs::perms::owner_exec) != fs::perms::none ? "x" : "-" ); std :: cout << " group: " << ((p & fs::perms::group_read) != fs::perms::none ? "r" : "-" ) << ((p & fs::perms::group_write) != fs::perms::none ? "w" : "-" ) << ((p & fs::perms::group_exec) != fs::perms::none ? "x" : "-" ); std :: cout << " others: " << ((p & fs::perms::others_read) != fs::perms::none ? "r" : "-" ) << ((p & fs::perms::others_write) != fs::perms::none ? "w" : "-" ) << ((p & fs::perms::others_exec) != fs::perms::none ? "x" : "-" ) << '

' ; }

For our file we can see:

outputPerms(fs::status( "hello.txt" ).permissions());

And we’ll get (On Linux at Coliru):

- - - - -

We have the right, so tellg() will work as expected:

std ::ifstream testFile( std :: string ( "hello.txt" ), std ::ios::binary | std ::ios::ate); if (testFile.good()) std :: cout << "tellgSize: " << testFile.tellg() << '

' ; else throw std ::runtime_error( "cannot read file..." );

But how about changing permissions so that we cannot open the file for reading, writing or executing?

fs::permissions(sTempName, fs::perms::owner_all, fs::perm_options::remove); outputPerms(fs::status(sTempName).permissions());

it shows:

- - - - - - -

fs::permissions is a method that allows us to set permissions - we pass a flag that we’d like to change (it’s a bitmask) and then “operation” - remove , replace or add .

In our case, I’m removing all owner permissions from the file.

perms::owner_all is composed of owner_read | owner_write | owner_exec .

Now… let’s try to execute the same code that uses tellg() … and we’ll get:

general exception: cannot read file ...

But how about fs::file_size ?:

auto fsize = fs::file_size(sTempName); std :: cout << "fsize: " << fsize << '

' ;

We’ll get the expected output:

fsize : 13

No matter the permissions of the file (common permissions like read/write/exec), we can read its size.

Demo here @Coliru

Parent Directory Access

While there’s no need to have read/write/exec rights for the file, we need a parent directory rights to be correct.

I did one more test and I removed all rights from "." directory (the place were the file was created):

fs::permissions( "." , fs::perms::owner_all, fs::perm_options::remove); auto fsize = fs::file_size(sTempName); std :: cout << "fsize: " << fsize << '

' ;

But I only got:

filesystem error ! filesystem error : cannot get file size : Permission denied [hello.txt]

You can play with the code here @Coliru

Note For Windows

Windows is not a POSIX system, so it also doesn’t map POSIX file permissions to its scheme. For std::filesystem it only supports two modes:

(read/write) - read, write, and execute - all modes

(read-only) - read, execute - all modes

That’s why our demo code won’t work. Disabling read access for a file does not affect.

Performance

Getting a file size is maybe not the crucial hot-spot in your application… but since we’re C++ programmers, we’d like to know what’s faster… right? :)

Since there’s no need to read the file… then std::filesystem methods should be faster… isn’t it?

What’s more, directory_entry method might be even faster as it should be able to cache the results for a single path - so if you want to access that information many times, it’s wiser to use directory_entry .

Here’s a simple test (thanks to Patrice Roy for the initial test example)

you can play with a demo code here @Coliru.

The test is run N = 10'000 times.

On Coliru (Linux):

filesystem: :file_size : 2543920 in 21 ms. homemade file_size : 2543920 in 66 ms. directory_entry file_size : 2543920 in 13 ms.

On Windows:

PS .\Test .exe filesystem: :file_size : 1200128 in 81 ms. homemade file_size : 1200128 in 395 ms. directory_entry file_size : 1200128 in 0 ms. PS .\Test .exe filesystem: :file_size : 1200128 in 81 ms. homemade file_size : 1200128 in 390 ms. directory_entry file_size : 1200128 in 0 ms.

It’s interesting to see that the directory_entry method is almost no-op in comparison to other techniques. But I haven’t measured the first time access.

Summary

In this blog post, we’ve shed some light over the file_size function. We covered permissions that are required to get the file status and also compared the performance.