1

With C++ on Linux, how does one detect block devices? Right now, I'm using this code:

for (const auto &entry : std::filesystem::directory_iterator("/dev/"))
{
    std::string name = entry.path().filename().string();
    if (name.find("sd") == 0 || name.find("nvme") == 0 || name.find("hd") == 0 || name.find("vd") == 0 || name.find("xvd") == 0)
    {
        std::cout << "Found device: " << entry.path() << std::endl;
    }
}

Which works well enough in practice, but almost certainly isn't the way it's "supposed to be done". And it isn't perfect either, as it misses losetup devices because I didn't include "loop", it also misses Network Block Devices because I didn't include "nbd".

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
hanshenrik
  • 19,904
  • 4
  • 43
  • 89
  • 1
    FYI, instead of using `name.find("sd") == 0`, etc, consider using `name.compare("sd", 2) == 0`, etc instead. Or, in C++20 and later, you can use `name.starts_with("sd")`, etc. – Remy Lebeau Jun 15 '22 at 23:45
  • Better check `/sys/block/`. And note that things like `loop` and `nbd` are created on demand. They don't exist unless in use. – Goswin von Brederlow Jun 16 '22 at 18:23
  • @GoswinvonBrederlow i need to support Cygwin and Linux, Cygwin supports /dev/sd* but it does not support /sys/block ^^ thanks for the suggestion though. (the "windows" build [here](https://github.com/divinity76/captester/commit/be027506778a5e86578a2e4603ba7fac2e58a870) is actually just a cygwin static compilation) – hanshenrik Jun 17 '22 at 00:32

2 Answers2

7

std::filesystem::directory_entry has an is_block_file() method for this exact purpose:

Checks whether the pointed-to object is a block device.

For example:

for (const auto &entry : std::filesystem::directory_iterator("/dev/"))
{
    if (entry.is_block_file())
    {
        std::cout << "Found device: " << entry.path() << std::endl;
    }
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
3

Something like this should do it (untested, no error checking for brevity):

const char *maybe_block_device = ...;
struct stat st;
stat (maybe_block_device, &st);
bool is_block_device = S_ISBLK (st.st_mode);

Where S_ISBLK is a 'helper' macro.

Documentation here and here.

Paul Sanders
  • 24,133
  • 4
  • 26
  • 48
  • 2
    Note that [`directory_entry::status()`](https://en.cppreference.com/w/cpp/filesystem/directory_entry/status) is a wrapper for `stat()`, so you don't have to call `stat()` directly, eg: `if (entry.status().type() == file_type::block)`, which can then be simplified using [`directory_entry::is_block_file()`](https://en.cppreference.com/w/cpp/filesystem/directory_entry/is_block_file), eg: `if (entry.is_block_file())` – Remy Lebeau Jun 15 '22 at 23:50
  • @RemyLebeau OK, since C++17. – Paul Sanders Jun 15 '22 at 23:52
  • Yes, which the OP is clearly using – Remy Lebeau Jun 15 '22 at 23:53
  • this is probably what std::filesystem::directory_entry::is_block_file() does under-the-hood :) interesting, thanks – hanshenrik Jun 16 '22 at 09:04
  • @hans Has to be. Maybe the `std::filesystem` variant is portable though, I'm not sure how things stand with MSVC on this. – Paul Sanders Jun 16 '22 at 10:07