From a345a24df9e40e0ba2bf1b35a5a98420b4cc255d Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Mon, 21 Nov 2016 15:15:18 +0200 Subject: Make process_path effective path always absolute --- butl/path | 10 ++++++-- butl/process | 16 ++++++------ butl/process.cxx | 75 ++++++++++++++++++++++++++++++++++++++++++-------------- 3 files changed, 72 insertions(+), 29 deletions(-) (limited to 'butl') diff --git a/butl/path b/butl/path index e6c9166..d60938f 100644 --- a/butl/path +++ b/butl/path @@ -108,10 +108,16 @@ namespace butl static bool absolute (const string_type& s) { + return absolute (s.c_str (), s.size ()); + } + + static bool + absolute (const C* s, size_type n) + { #ifdef _WIN32 - return s.size () > 1 && s[1] == ':'; + return n > 1 && s[1] == ':'; #else - return s.size () != 0 && is_separator (s[0]); + return n != 0 && is_separator (s[0]); #endif } diff --git a/butl/process b/butl/process index e98e367..ff22577 100644 --- a/butl/process +++ b/butl/process @@ -50,21 +50,21 @@ namespace butl // directory and if found then that's what should end up in child's argv[0]. // So this is the recall path. It is called recall because this is what the // caller of the parent process will be able to execute if you printed the - // command line. Finally, effective is the actual path to the executable - // that will include the directory part if found in PATH, the .exe extension - // if one is missing, etc. + // command line (provided you haven't changed the CWD). Finally, effective + // is the absolute path to the executable that will include the directory + // part if found in PATH, the .exe extension if one is missing, etc. // // As an example, let's say we run foo\foo.exe that itself spawns bar which // is found as foo\bar.exe. The paths will then be: // // initial: bar // recall: foo\bar - // effective: foo\bar.exe + // effective: c:\...\foo\bar.exe // - // In most cases, at least on POSIX, all three paths will be the same. As an - // optimization, if the recall path is empty, then it means it is the same - // as initial. Similarly, if the effective path is empty then, it is the - // same as recall (and if that is empty, as initial). + // In most cases, at least on POSIX, the first two paths will be the same. + // As an optimization, if the recall path is empty, then it means it is the + // same as initial. Similarly, if the effective path is empty then, it is + // the same as recall (and if that is empty, as initial). // // Note that the call to path_search() below adjust args[0] to point to the // recall path which brings up lifetime issues. To address this this class diff --git a/butl/process.cxx b/butl/process.cxx index 41645e4..1a83481 100644 --- a/butl/process.cxx +++ b/butl/process.cxx @@ -153,7 +153,9 @@ namespace butl (si.st_mode & (S_IEXEC | S_IXGRP | S_IXOTH)) != 0); }; - auto search = [&ep, f, fn, &exists] (const char* d, size_t dn) -> bool + auto search = [&ep, f, fn, &exists] (const char* d, + size_t dn, + bool norm = false) -> bool { string s (move (ep).string ()); // Reuse buffer. @@ -167,18 +169,33 @@ namespace butl s.append (f, fn); ep = path (move (s)); // Move back into result. + + if (norm) + ep.normalize (); + return exists (ep.string ().c_str ()); }; - // If there is a directory component in the file, then search does not - // apply. But make sure the file actually exists. + // If there is a directory component in the file, then the PATH search + // does not apply. If the path is relative, then prepend CWD. In both + // cases make sure the file actually exists. // if (traits::find_separator (f, fn) != nullptr) { - if (exists (f)) // ?: calls deleted copy ctor. - return r; + if (traits::absolute (f, fn)) + { + if (exists (f)) + return r; + } else - return process_path (); + { + const string& d (traits::current ()); + + if (search (d.c_str (), d.size (), true)) + return r; + } + + return process_path (); } // The search order is documented in exec(3). Some of the differences @@ -435,7 +452,9 @@ namespace butl return _stat (f, &si) == 0 && S_ISREG (si.st_mode); }; - auto search = [&ep, f, fn, ext, &exists] (const char* d, size_t dn) -> bool + auto search = [&ep, f, fn, ext, &exists] (const char* d, + size_t dn, + bool norm = false) -> bool { string s (move (ep).string ()); // Reuse buffer. @@ -450,6 +469,9 @@ namespace butl s.append (f, fn); ep = path (move (s)); // Move back into result. + if (norm) + ep.normalize (); + // Add the .exe extension if necessary. // if (ext) @@ -458,22 +480,33 @@ namespace butl return exists (ep.string ().c_str ()); }; - // If there is a directory component in the file, then search does not - // apply. But we may still need to append the extension and make sure the - // file actually exists. + // If there is a directory component in the file, then the PATH search + // does not apply. If the path is relative, then prepend CWD. In both + // cases we may still need to append the extension and make sure the file + // actually exists. // if (traits::find_separator (f, fn) != nullptr) { - if (ext) + if (traits::absolute (f, fn)) { - ep = path (f, fn); - ep += ".exe"; - } + if (ext) + { + ep = path (f, fn); + ep += ".exe"; + } - if (exists (r.effect_string ())) // ?: calls deleted copy ctor. - return r; + if (exists (r.effect_string ())) + return r; + } else - return process_path (); + { + const string& d (traits::current ()); + + if (search (d.c_str (), d.size (), true)) // Appends extension. + return r; + } + + return process_path (); } // The search order is documented in CreateProcess(). First we look in the @@ -526,8 +559,12 @@ namespace butl // The recall path is the same as initial, though it might not be a bad // idea to prepend .\ for clarity. // - if (search ("", 0)) - return r; + { + const string& d (traits::current ()); + + if (search (d.c_str (), d.size ())) + return r; + } // Now search in PATH. Recall is unchanged. // -- cgit v1.1