Catalog Racing Tour (C ++)

I need to go through the directory tree and get the stat values ​​for each file. I want to do this safely until the file system is modified.

In Python, the best option is os.fwalkone that gives access to fd for a passing directory; I can then os.statwith dir_fd ( fstatat) and get the current stat values. It’s like no race, as it can be done on Linux (if the contents of this directory change, I may have to re-examine it). In C there is nftwone that is implemented similarly, and ftswhich in glibc uses a simple (l) stat and therefore is racy (it reduces the race window by changing directories, which is inconvenient).

C ++ has a new file system API that has finished raising , which caches values statbut does not disclose them (and I need access to st_dev). It is not just a header library, so I cannot get around this.

Am I missing a suitable C ++ option that uses fstatatand is not bound by the idea of ​​Boost without exposing calls to the platform? Or is my best option for transferring nftw(or even find)?

+4
source share
1 answer

It turns out that it was quite simple to implement.

I used libposix from dryproject.

#include <posix++.h>

class Walker {
public:
    void walk(posix::directory dir) {
        dir.for_each([this, dir](auto& dirent) {
            if (dirent.name == "." or dirent.name == "..")
                    return;
            if (!handle_dirent(dirent))
                return;
            struct stat stat;
            if (dirent.type == DT_DIR || dirent.type == DT_UNKNOWN) {
                int fd = openat(
                    dir.fd(), dirent.name.c_str(), O_DIRECTORY|O_NOFOLLOW|O_NOATIME);
                if (fd < 0) {
                    // ELOOP when O_NOFOLLOW is used on a symlink
                    if (errno == ENOTDIR || errno == ELOOP)
                        goto enotdir;
                    if (errno == ENOENT)
                        goto enoent;
                    posix::throw_error(
                        "openat", "%d, \"%s\"", dir.fd(), dirent.name);
                }
                posix::directory dir1(fd);
                fstat(fd, &stat);
                if (handle_directory(dirent, fd, stat))
                    walk(dir1);
                close(fd);
                return;
            }
enotdir:
            try {
                dir.stat(dirent.name.c_str(), stat, AT_SYMLINK_NOFOLLOW);
            } catch (const posix::runtime_error &error) {
                if (error.number() == ENOENT)
                    goto enoent;
                throw;
            }
            handle_file(dirent, stat);
            return;
enoent:
            handle_missing(dirent);
        });
    }
protected:
    /* return value: whether to stat */
    virtual bool handle_dirent(const posix::directory::entry&) { return true; }
    /* return value: whether to recurse
     * stat will refer to a directory, dirent info may be obsolete */
    virtual bool handle_directory(
            const posix::directory::entry &dirent,
            const int fd, const struct stat&) { return true; }
    /* stat might refer to a directory in case of a race;
     * it still won't be recursed into.  dirent may be obsolete. */
    virtual void handle_file(
            const posix::directory::entry &dirent,
            const struct stat&) {}
    /* in case of a race */
    virtual void handle_missing(
            const posix::directory::entry &dirent) {}
};

GNU find ( , -size $RANDOM find stat , DT_DIR ).

+1

Source: https://habr.com/ru/post/1609289/


All Articles