2015-09-28 18 views
4

我需要遍歷目錄樹並獲取每個文件的統計值。我想在文件系統被修改時安全地做到這一點。無競爭目錄散步(C++)

在Python,最好的選擇是os.fwalk,它可以訪問到FD的目錄遍歷被;然後我可以用dir_fd(fstatatos.stat獲得當前的統計值。這是可以在Linux上進行的免競賽(如果該目錄的內容正在被修改,我可能需要重新掃描它)。在C中,有nftw,這是類似實現的,而fts,它在glibc中使用普通(l)stat,因此是racy(它通過改變目錄來減少比賽窗口,這是不方便的)。

C++有一個新的filesystem APIgraduated from boost,其緩存stat值但doesn't expose them(我需要訪問st_dev)。這不是純粹的頭文件庫,所以我無法解決這個問題。

我缺少一個像樣的C++選項,使用fstatat,而不是通過不暴露特定於平臺的調用Boost的理想約束?或者是我最好的選擇包裝nftw(或甚至find)?

回答

1

事實證明,這是很簡單的實現。

我用dryproject的libposix

#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找到(與基類比較時,使用-size $RANDOM壓制輸出力findstat所有文件,而不僅僅是DT_DIR候選人)。