From decd319bc1bc7d427f92feba29d408eb2edb973d Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Mon, 31 Aug 2015 13:52:41 +0200 Subject: Add rmdir_r(), path_permissions() --- butl/filesystem | 64 +++++++++++++++++++++----- butl/filesystem.cxx | 126 +++++++++++++++++++++++++++++++++------------------- butl/filesystem.ixx | 29 ++++++++++++ 3 files changed, 162 insertions(+), 57 deletions(-) diff --git a/butl/filesystem b/butl/filesystem index 0a5d7eb..9168f19 100644 --- a/butl/filesystem +++ b/butl/filesystem @@ -12,6 +12,7 @@ #endif #include // ptrdiff_t +#include // uint16_t #include // move() #include @@ -20,14 +21,6 @@ namespace butl { - // Return timestamp_nonexistent if the entry at the specified path - // does not exist or is not a path. All other errors are reported - // by throwing std::system_error. Note that this function resolves - // symlinks. - // - timestamp - file_mtime (const path&); - // Return true if the path is to an existing directory. Note that // this function resolves symlinks. // @@ -54,13 +47,13 @@ namespace butl enum class mkdir_status {success, already_exists}; mkdir_status - try_mkdir (const path&, mode_t = 0777); + try_mkdir (const dir_path&, mode_t = 0777); // The '-p' version of the above (i.e., it creates the parent // directories if necessary). // mkdir_status - try_mkdir_p (const path&, mode_t = 0777); + try_mkdir_p (const dir_path&, mode_t = 0777); // Try to remove the directory returning not_exist if it does not // exist and not_empty if it is not empty. All other errors are @@ -69,7 +62,19 @@ namespace butl enum class rmdir_status {success, not_exist, not_empty}; rmdir_status - try_rmdir (const path&); + try_rmdir (const dir_path&); + + // The '-r' (recursive) version of the above. Note that it will + // never return not_empty. + // + rmdir_status + try_rmdir_r (const dir_path&); + + // As above but throws rather than returns not_exist if the directory + // does not exist, so check before calling. + // + void + rmdir_r (const dir_path&); // Try to remove the file (or symlinks) returning not_exist if // it does not exist. All other errors are reported by throwing @@ -80,6 +85,43 @@ namespace butl rmfile_status try_rmfile (const path&); + // Return timestamp_nonexistent if the entry at the specified path + // does not exist or is not a path. All other errors are reported + // by throwing std::system_error. Note that this function resolves + // symlinks. + // + timestamp + file_mtime (const path&); + + // Path permissions. + // + enum class permissions: std::uint16_t + { + // Note: matching POSIX values. + // + xo = 0001, + wo = 0002, + ro = 0004, + + xg = 0010, + wg = 0020, + rg = 0040, + + xu = 0100, + wu = 0200, + ru = 0400, + + none = 0 + }; + + permissions operator& (permissions, permissions); + permissions operator| (permissions, permissions); + permissions operator&= (permissions&, permissions); + permissions operator|= (permissions&, permissions); + + permissions + path_permissions (const path&); + // Directory entry iteration. // enum class entry_type diff --git a/butl/filesystem.cxx b/butl/filesystem.cxx index 2a1fad9..90e8e81 100644 --- a/butl/filesystem.cxx +++ b/butl/filesystem.cxx @@ -15,48 +15,6 @@ using namespace std; namespace butl { - // Figuring out whether we have the nanoseconds in some form. - // - template - inline constexpr auto nsec (const S* s) -> decltype(s->st_mtim.tv_nsec) - { - return s->st_mtim.tv_nsec; // POSIX (GNU/Linux, Solaris). - } - - template - inline constexpr auto nsec (const S* s) -> decltype(s->st_mtimespec.tv_nsec) - { - return s->st_mtimespec.tv_nsec; // MacOS X. - } - - template - inline constexpr auto nsec (const S* s) -> decltype(s->st_mtime_n) - { - return s->st_mtime_n; // AIX 5.2 and later. - } - - template - inline constexpr int nsec (...) {return 0;} - - timestamp - file_mtime (const path& p) - { - struct stat s; - if (::stat (p.string ().c_str (), &s) != 0) - { - if (errno == ENOENT || errno == ENOTDIR) - return timestamp_nonexistent; - else - throw system_error (errno, system_category ()); - } - - return S_ISREG (s.st_mode) - ? system_clock::from_time_t (s.st_mtime) + - chrono::duration_cast ( - chrono::nanoseconds (nsec (&s))) - : timestamp_nonexistent; - } - bool dir_exists (const path& p) { @@ -88,7 +46,7 @@ namespace butl } mkdir_status - try_mkdir (const path& p, mode_t m) + try_mkdir (const dir_path& p, mode_t m) { mkdir_status r (mkdir_status::success); @@ -109,11 +67,11 @@ namespace butl } mkdir_status - try_mkdir_p (const path& p, mode_t m) + try_mkdir_p (const dir_path& p, mode_t m) { if (!p.root ()) { - path d (p.directory ()); + dir_path d (p.directory ()); if (!dir_exists (d)) try_mkdir_p (d, m); @@ -123,7 +81,7 @@ namespace butl } rmdir_status - try_rmdir (const path& p) + try_rmdir (const dir_path& p) { rmdir_status r (rmdir_status::success); @@ -140,6 +98,29 @@ namespace butl return r; } + void + rmdir_r (const dir_path& p) + { + // An nftw()-based implementation (for platforms that support it) + // might be a faster way. + // + for (const dir_entry& de: dir_iterator (p)) + { + path ep (p / de.path ()); //@@ Would be good to reuse the buffer. + + if (de.ltype () == entry_type::directory) + rmdir_r (path_cast (ep)); + else + try_rmfile (ep); + } + + rmdir_status r (try_rmdir (p)); + + if (r != rmdir_status::success) + throw system_error (r == rmdir_status::not_empty ? ENOTEMPTY : ENOENT, + system_category ()); + } + rmfile_status try_rmfile (const path& p) { @@ -156,6 +137,59 @@ namespace butl return r; } + // Figuring out whether we have the nanoseconds in some form. + // + template + inline constexpr auto nsec (const S* s) -> decltype(s->st_mtim.tv_nsec) + { + return s->st_mtim.tv_nsec; // POSIX (GNU/Linux, Solaris). + } + + template + inline constexpr auto nsec (const S* s) -> decltype(s->st_mtimespec.tv_nsec) + { + return s->st_mtimespec.tv_nsec; // MacOS X. + } + + template + inline constexpr auto nsec (const S* s) -> decltype(s->st_mtime_n) + { + return s->st_mtime_n; // AIX 5.2 and later. + } + + template + inline constexpr int nsec (...) {return 0;} + + timestamp + file_mtime (const path& p) + { + struct stat s; + if (::stat (p.string ().c_str (), &s) != 0) + { + if (errno == ENOENT || errno == ENOTDIR) + return timestamp_nonexistent; + else + throw system_error (errno, system_category ()); + } + + return S_ISREG (s.st_mode) + ? system_clock::from_time_t (s.st_mtime) + + chrono::duration_cast ( + chrono::nanoseconds (nsec (&s))) + : timestamp_nonexistent; + } + + permissions + path_permissions (const path& p) + { + struct stat s; + if (::stat (p.string ().c_str (), &s) != 0) + throw system_error (errno, system_category ()); + + return static_cast ( + s.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO)); + } + #ifndef _WIN32 // dir_entry diff --git a/butl/filesystem.ixx b/butl/filesystem.ixx index 2d2690f..256cbba 100644 --- a/butl/filesystem.ixx +++ b/butl/filesystem.ixx @@ -4,6 +4,35 @@ namespace butl { + inline rmdir_status + try_rmdir_r (const dir_path& p) + { + bool e (dir_exists (p)); //@@ What if it exists but is not a directory? + + if (e) + rmdir_r (p); + + return e ? rmdir_status::success : rmdir_status::not_exist; + } + + // permissions + // + inline permissions operator& (permissions x, permissions y) {return x &= y;} + inline permissions operator| (permissions x, permissions y) {return x &= y;} + inline permissions operator&= (permissions& x, permissions y) + { + return x = static_cast ( + static_cast (x) & + static_cast (y)); + } + + inline permissions operator|= (permissions& x, permissions y) + { + return x = static_cast ( + static_cast (x) | + static_cast (y)); + } + // dir_entry // inline entry_type dir_entry:: -- cgit v1.1