aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libbutl/filesystem.cxx56
-rw-r--r--libbutl/filesystem.mxx21
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