diff options
-rw-r--r-- | libbutl/filesystem.cxx | 56 | ||||
-rw-r--r-- | libbutl/filesystem.mxx | 21 |
2 files changed, 75 insertions, 2 deletions
diff --git a/libbutl/filesystem.cxx b/libbutl/filesystem.cxx index df738ac..b310dd8 100644 --- a/libbutl/filesystem.cxx +++ b/libbutl/filesystem.cxx @@ -671,6 +671,62 @@ namespace butl } #endif + entry_type + mkanylink (const path& target, const path& link, bool copy, bool rel) + { + using error = pair<entry_type, system_error>; + + try + { + mksymlink (rel ? target.relative (link.directory ()) : target, link); + return entry_type::symlink; + } + catch (system_error& e) + { + // Note that we are not guaranteed (here and below) that the + // system_error exception is of the generic category. + // + if (e.code ().category () == generic_category ()) + { + int c (e.code ().value ()); + if (c == ENOSYS || // Not implemented. + c == EPERM) // Not supported by the filesystem(s). + { + try + { + mkhardlink (target, link); + return entry_type::other; + } + catch (system_error& e) + { + if (copy && e.code ().category () == generic_category ()) + { + int c (e.code ().value ()); + if (c == ENOSYS || // Not implemented. + c == EPERM || // Not supported by the filesystem(s). + c == EXDEV) // On different filesystems. + { + try + { + cpfile (target, link); + return entry_type::regular; + } + catch (system_error& e) + { + throw error (entry_type::regular, move (e)); + } + } + } + + throw error (entry_type::other, move (e)); + } + } + } + + throw error (entry_type::symlink, move (e)); + } + } + // For I/O operations cpfile() can throw ios_base::failure exception that is // not derived from system_error for old versions of g++ (as of 4.9). From // the other hand cpfile() must throw system_error only. Let's catch diff --git a/libbutl/filesystem.mxx b/libbutl/filesystem.mxx index 2e3a0d9..e028975 100644 --- a/libbutl/filesystem.mxx +++ b/libbutl/filesystem.mxx @@ -289,8 +289,8 @@ LIBBUTL_MODEXPORT namespace butl // Create a hard link to a file (default) or directory (third argument is // true). Throw std::system_error on failures. // - // Note that on Linix, FreeBSD, Windows and some other platforms the target - // can not be a directory. + // Note that on Linux, FreeBSD, Windows and some other platforms the target + // cannot be a directory. // LIBBUTL_SYMEXPORT void mkhardlink (const path& target, const path& link, bool dir = false); @@ -303,6 +303,23 @@ LIBBUTL_MODEXPORT namespace butl mkhardlink (target, link, true /* dir */); } + // Make a symlink, hardlink, or, if `copy` is true, a copy of a file (note: + // no directories, only files), whichever is possible in that order. If + // `relative` is true, then make the symlink target relative to the link + // directory (note: it is the caller's responsibility to make sure this is + // possible). + // + // On success, return the type of entry created: `regular` for copy, + // `symlink` for symlink, and `other` for hardlink. On failure, throw a + // `pair<entry_type, system_error>` with the first half indicating the part + // of the logic that caused the error. + // + LIBBUTL_SYMEXPORT entry_type + mkanylink (const path& target, + const path& link, + bool copy, + bool relative = false); + // File copy flags. // enum class cpflags: std::uint16_t |