LCOV - code coverage report
Current view: top level - src/esys/repo - filesystem.cpp (source / functions) Hit Total Coverage
Test: esysrepo_coverage.info Lines: 65 117 55.6 %
Date: 2026-08-12 14:26:50 Functions: 3 8 37.5 %

          Line data    Source code
       1             : /*!
       2             :  * \file esys/repo/filesystem.cpp
       3             :  * \brief
       4             :  *
       5             :  * \cond
       6             :  * __legal_b__
       7             :  *
       8             :  * Copyright (c) 2020-2026 Michel Gillet
       9             :  * Distributed under the MIT License.
      10             :  * (See accompanying file LICENSE.txt or
      11             :  * copy at https://opensource.org/licenses/MIT)
      12             :  *
      13             :  * __legal_e__
      14             :  * \endcond
      15             :  *
      16             :  */
      17             : 
      18             : #include "esys/repo/esysrepo_prec.h"
      19             : #include "esys/repo/filesystem.h"
      20             : 
      21             : #if ESYSREPO_HAS_STD_FILESYSTEM
      22             : #include <filesystem>
      23             : #endif
      24             : 
      25             : #include <boost/filesystem.hpp>
      26             : #include <boost/version.hpp>
      27             : 
      28             : #if BOOST_VERSION <= 107100
      29             : #define ESYSREPO_BOOST_FILESYSTEM_HAS_RECUR 0
      30             : #else
      31             : #define ESYSREPO_BOOST_FILESYSTEM_HAS_RECUR 1
      32             : #endif
      33             : 
      34             : namespace esys::repo
      35             : {
      36             : 
      37             : namespace stdcpp
      38             : {
      39             : 
      40           0 : ESYSREPO_API int copy(const std::string &src, const std::string &dst, bool recursive)
      41             : {
      42             : #if ESYSREPO_HAS_STD_FILESYSTEM
      43             :     try
      44             :     {
      45             :         auto options = std::filesystem::copy_options::copy_symlinks;
      46             :         if (recursive) options |= std::filesystem::copy_options::recursive;
      47             :         std::filesystem::copy(src, dst, options);
      48             :     }
      49             :     catch (std::exception &)
      50             :     {
      51             :         return -1;
      52             :     }
      53             :     return 0;
      54             : #else
      55           0 :     (void)src;
      56           0 :     (void)dst;
      57           0 :     (void)recursive;
      58           0 :     return -1;
      59             : #endif
      60             : }
      61             : 
      62           0 : ESYSREPO_API int remove_all(const std::string &path)
      63             : {
      64             : #if ESYSREPO_HAS_STD_FILESYSTEM
      65             :     try
      66             :     {
      67             :         std::filesystem::remove_all(path);
      68             :     }
      69             :     catch (std::exception &)
      70             :     {
      71             :         return -1;
      72             :     }
      73             :     return 0;
      74             : #else
      75           0 :     (void)path;
      76           0 :     return -1;
      77             : #endif
      78             : }
      79             : 
      80             : } // namespace stdcpp
      81             : 
      82             : namespace boost_no_recur
      83             : {
      84             : 
      85         262 : ESYSREPO_API int copy(const std::string &src, const std::string &dst, bool recursive)
      86             : {
      87         262 :     boost::system::error_code ec;
      88             : 
      89         262 :     if (!recursive)
      90             :     {
      91           0 :         const auto sym_stat = boost::filesystem::symlink_status(src, ec);
      92           0 :         if (ec) return -1;
      93           0 :         if (boost::filesystem::is_symlink(sym_stat))
      94             :         {
      95           0 :             boost::filesystem::copy_symlink(src, dst, ec);
      96           0 :             return ec ? -1 : 0;
      97             :         }
      98           0 :         try
      99             :         {
     100           0 :             boost::filesystem::copy(src, dst);
     101             :         }
     102           0 :         catch (std::exception &)
     103             :         {
     104           0 :             return -1;
     105           0 :         }
     106           0 :         return 0;
     107             :     }
     108             : 
     109         262 :     boost::filesystem::directory_iterator it(src, ec);
     110         524 :     if (ec) return -1;
     111             : 
     112             :     try
     113             :     {
     114         911 :         while (it != boost::filesystem::directory_iterator{})
     115             :         {
     116         649 :             boost::filesystem::path copy_to = dst;
     117        1298 :             copy_to /= it->path().filename();
     118             : 
     119         649 :             const auto sym_stat = boost::filesystem::symlink_status(*it, ec);
     120         649 :             if (ec) return -1;
     121             : 
     122         649 :             if (boost::filesystem::is_symlink(sym_stat))
     123             :             {
     124             :                 // Preserve Git mode-120000 links (e.g. ChromiumOS default.xml -> snapshot.xml).
     125           1 :                 boost::filesystem::copy_symlink(*it, copy_to, ec);
     126           1 :                 if (ec) return -1;
     127             :             }
     128         648 :             else if (boost::filesystem::is_directory(sym_stat))
     129             :             {
     130         247 :                 if (!boost::filesystem::create_directory(copy_to)) return -1;
     131         247 :                 int result = copy(it->path().string(), copy_to.string(), recursive);
     132         247 :                 if (result < 0) return result;
     133             :             }
     134             :             else
     135             :             {
     136         401 :                 boost::filesystem::copy(*it, copy_to, ec);
     137         401 :                 if (ec) return -1;
     138             :             }
     139             : 
     140         649 :             it.increment(ec);
     141         649 :             if (ec) return -1;
     142         649 :         }
     143             :     }
     144           0 :     catch (std::exception &)
     145             :     {
     146           0 :         return -1;
     147           0 :     }
     148             :     return 0;
     149         262 : }
     150             : 
     151             : } // namespace boost_no_recur
     152             : 
     153             : namespace boost_recur
     154             : {
     155             : 
     156           0 : ESYSREPO_API int copy(const std::string &src, const std::string &dst, bool recursive)
     157             : {
     158             : #if ESYSREPO_BOOST_FILESYSTEM_HAS_RECUR
     159           0 :     try
     160             :     {
     161           0 :         auto options = boost::filesystem::copy_options::copy_symlinks;
     162           0 :         if (recursive) options |= boost::filesystem::copy_options::recursive;
     163           0 :         boost::filesystem::copy(src, dst, options);
     164             :     }
     165           0 :     catch (std::exception &)
     166             :     {
     167           0 :         return -1;
     168           0 :     }
     169           0 :     return 0;
     170             : #else
     171             :     (void)src;
     172             :     (void)dst;
     173             :     (void)recursive;
     174             :     return -1;
     175             : #endif
     176             : }
     177             : 
     178             : } // namespace boost_recur
     179             : 
     180             : namespace boost_all
     181             : {
     182             : 
     183           0 : ESYSREPO_API int remove_all(const std::string &path)
     184             : {
     185           0 :     try
     186             :     {
     187           0 :         boost::filesystem::remove_all(path);
     188             :     }
     189           0 :     catch (std::exception &)
     190             :     {
     191           0 :         return -1;
     192           0 :     }
     193           0 :     return 0;
     194             : }
     195             : 
     196             : } // namespace boost_all
     197             : 
     198             : namespace boost_no_all
     199             : {
     200             : 
     201         264 : ESYSREPO_API int remove_all(const std::string &path)
     202             : {
     203         264 :     boost::system::error_code ec;
     204         264 :     boost::filesystem::directory_iterator it(path, ec);
     205         264 :     if (ec) return -1;
     206             : 
     207             :     try
     208             :     {
     209         913 :         while (it != boost::filesystem::directory_iterator{})
     210             :         {
     211         649 :             const auto sym_stat = boost::filesystem::symlink_status(*it, ec);
     212         649 :             if (ec) return -1;
     213             : 
     214         649 :             if (boost::filesystem::is_symlink(sym_stat))
     215             :             {
     216             :                 // Remove the link itself; do not follow into the target.
     217           0 :                 boost::filesystem::remove(*it, ec);
     218           0 :                 if (ec) return -1;
     219             :             }
     220         649 :             else if (boost::filesystem::is_directory(sym_stat))
     221             :             {
     222         249 :                 int result = boost_no_all::remove_all(it->path().string());
     223         249 :                 if (result < 0) return result;
     224             :             }
     225             :             else
     226             :             {
     227         400 :                 auto stat = boost::filesystem::status(it->path(), ec);
     228             : 
     229         400 :                 if (ec == boost::system::error_code())
     230             :                 {
     231         400 :                     if ((stat.permissions() & boost::filesystem::perms::others_write)
     232             :                         != boost::filesystem::perms::others_write)
     233             :                     {
     234         400 :                         boost::filesystem::permissions(*it,
     235             :                                                        stat.permissions() | boost::filesystem::perms::others_write);
     236             :                     }
     237             :                 }
     238             : 
     239         400 :                 try
     240             :                 {
     241         400 :                     boost::filesystem::remove(*it);
     242             :                 }
     243           0 :                 catch (std::exception &)
     244             :                 {
     245           0 :                     return -1;
     246           0 :                 }
     247             :             }
     248         649 :             it.increment(ec);
     249         649 :             if (ec) return -1;
     250             :         }
     251         264 :         boost::filesystem::remove(path, ec);
     252         264 :         if (ec) return -1;
     253             :     }
     254           0 :     catch (std::exception &)
     255             :     {
     256           0 :         return -1;
     257           0 :     }
     258             :     return 0;
     259         264 : }
     260             : 
     261          35 : ESYSREPO_API int move(const std::string &src, const std::string &dst, bool recursive)
     262             : {
     263          35 :     boost::system::error_code ec;
     264          35 :     const boost::filesystem::path src_p(src);
     265          35 :     const boost::filesystem::path dst_p(dst);
     266             : 
     267          35 :     const bool dst_exists = boost::filesystem::exists(dst_p);
     268          35 :     const bool dst_empty_dir =
     269         105 :         dst_exists && boost::filesystem::is_directory(dst_p) && boost::filesystem::is_empty(dst_p);
     270             : 
     271             :     // Prefer rename so Git symlinks (mode 120000) stay links instead of followed copies.
     272          35 :     if (!dst_exists || dst_empty_dir)
     273             :     {
     274          21 :         if (dst_empty_dir)
     275             :         {
     276          21 :             boost::filesystem::remove(dst_p, ec);
     277          21 :             if (ec) return -1;
     278             :         }
     279             : 
     280          21 :         boost::filesystem::rename(src_p, dst_p, ec);
     281          21 :         if (!ec) return 0;
     282             : 
     283             :         // Cross-device rename failure: restore an empty destination when we removed one.
     284           0 :         if (dst_empty_dir)
     285             :         {
     286           0 :             boost::filesystem::create_directory(dst_p, ec);
     287           0 :             if (ec) return -1;
     288             :         }
     289             :     }
     290             : 
     291             :     // Recursive copy merges children into an existing destination directory.
     292          14 :     if (!boost::filesystem::exists(dst_p))
     293             :     {
     294           0 :         boost::filesystem::create_directories(dst_p, ec);
     295          35 :         if (ec) return -1;
     296             :     }
     297             : 
     298          14 :     int result = copy(src, dst, recursive);
     299          14 :     if (result < 0) return -1;
     300             : 
     301          14 :     result = remove_all(src);
     302          14 :     if (result < 0) return -2;
     303             :     return 0;
     304          35 : }
     305             : 
     306             : } // namespace boost_no_all
     307             : 
     308           0 : ESYSREPO_API int move(const std::string &src, const std::string &dst, bool recursive)
     309             : {
     310           0 :     return boost_no_all::move(src, dst, recursive);
     311             : }
     312             : 
     313             : } // namespace esys::repo

Generated by: LCOV version 1.14