1*b0d29bc4SBrooks Davis // Copyright 2010 The Kyua Authors. 2*b0d29bc4SBrooks Davis // All rights reserved. 3*b0d29bc4SBrooks Davis // 4*b0d29bc4SBrooks Davis // Redistribution and use in source and binary forms, with or without 5*b0d29bc4SBrooks Davis // modification, are permitted provided that the following conditions are 6*b0d29bc4SBrooks Davis // met: 7*b0d29bc4SBrooks Davis // 8*b0d29bc4SBrooks Davis // * Redistributions of source code must retain the above copyright 9*b0d29bc4SBrooks Davis // notice, this list of conditions and the following disclaimer. 10*b0d29bc4SBrooks Davis // * Redistributions in binary form must reproduce the above copyright 11*b0d29bc4SBrooks Davis // notice, this list of conditions and the following disclaimer in the 12*b0d29bc4SBrooks Davis // documentation and/or other materials provided with the distribution. 13*b0d29bc4SBrooks Davis // * Neither the name of Google Inc. nor the names of its contributors 14*b0d29bc4SBrooks Davis // may be used to endorse or promote products derived from this software 15*b0d29bc4SBrooks Davis // without specific prior written permission. 16*b0d29bc4SBrooks Davis // 17*b0d29bc4SBrooks Davis // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18*b0d29bc4SBrooks Davis // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19*b0d29bc4SBrooks Davis // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20*b0d29bc4SBrooks Davis // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21*b0d29bc4SBrooks Davis // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22*b0d29bc4SBrooks Davis // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23*b0d29bc4SBrooks Davis // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24*b0d29bc4SBrooks Davis // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25*b0d29bc4SBrooks Davis // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26*b0d29bc4SBrooks Davis // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27*b0d29bc4SBrooks Davis // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28*b0d29bc4SBrooks Davis 29*b0d29bc4SBrooks Davis #include "utils/fs/operations.hpp" 30*b0d29bc4SBrooks Davis 31*b0d29bc4SBrooks Davis #if defined(HAVE_CONFIG_H) 32*b0d29bc4SBrooks Davis # include "config.h" 33*b0d29bc4SBrooks Davis #endif 34*b0d29bc4SBrooks Davis 35*b0d29bc4SBrooks Davis extern "C" { 36*b0d29bc4SBrooks Davis #include <sys/param.h> 37*b0d29bc4SBrooks Davis #if defined(HAVE_SYS_MOUNT_H) 38*b0d29bc4SBrooks Davis # include <sys/mount.h> 39*b0d29bc4SBrooks Davis #endif 40*b0d29bc4SBrooks Davis #include <sys/stat.h> 41*b0d29bc4SBrooks Davis #if defined(HAVE_SYS_STATVFS_H) && defined(HAVE_STATVFS) 42*b0d29bc4SBrooks Davis # include <sys/statvfs.h> 43*b0d29bc4SBrooks Davis #endif 44*b0d29bc4SBrooks Davis #if defined(HAVE_SYS_VFS_H) 45*b0d29bc4SBrooks Davis # include <sys/vfs.h> 46*b0d29bc4SBrooks Davis #endif 47*b0d29bc4SBrooks Davis #include <sys/wait.h> 48*b0d29bc4SBrooks Davis 49*b0d29bc4SBrooks Davis #include <unistd.h> 50*b0d29bc4SBrooks Davis } 51*b0d29bc4SBrooks Davis 52*b0d29bc4SBrooks Davis #include <cerrno> 53*b0d29bc4SBrooks Davis #include <cstdlib> 54*b0d29bc4SBrooks Davis #include <cstring> 55*b0d29bc4SBrooks Davis #include <fstream> 56*b0d29bc4SBrooks Davis #include <iostream> 57*b0d29bc4SBrooks Davis #include <sstream> 58*b0d29bc4SBrooks Davis #include <string> 59*b0d29bc4SBrooks Davis 60*b0d29bc4SBrooks Davis #include "utils/auto_array.ipp" 61*b0d29bc4SBrooks Davis #include "utils/defs.hpp" 62*b0d29bc4SBrooks Davis #include "utils/env.hpp" 63*b0d29bc4SBrooks Davis #include "utils/format/macros.hpp" 64*b0d29bc4SBrooks Davis #include "utils/fs/directory.hpp" 65*b0d29bc4SBrooks Davis #include "utils/fs/exceptions.hpp" 66*b0d29bc4SBrooks Davis #include "utils/fs/path.hpp" 67*b0d29bc4SBrooks Davis #include "utils/logging/macros.hpp" 68*b0d29bc4SBrooks Davis #include "utils/optional.ipp" 69*b0d29bc4SBrooks Davis #include "utils/sanity.hpp" 70*b0d29bc4SBrooks Davis #include "utils/units.hpp" 71*b0d29bc4SBrooks Davis 72*b0d29bc4SBrooks Davis namespace fs = utils::fs; 73*b0d29bc4SBrooks Davis namespace units = utils::units; 74*b0d29bc4SBrooks Davis 75*b0d29bc4SBrooks Davis using utils::optional; 76*b0d29bc4SBrooks Davis 77*b0d29bc4SBrooks Davis 78*b0d29bc4SBrooks Davis namespace { 79*b0d29bc4SBrooks Davis 80*b0d29bc4SBrooks Davis 81*b0d29bc4SBrooks Davis /// Operating systems recognized by the code below. 82*b0d29bc4SBrooks Davis enum os_type { 83*b0d29bc4SBrooks Davis os_unsupported = 0, 84*b0d29bc4SBrooks Davis os_freebsd, 85*b0d29bc4SBrooks Davis os_linux, 86*b0d29bc4SBrooks Davis os_netbsd, 87*b0d29bc4SBrooks Davis os_sunos, 88*b0d29bc4SBrooks Davis }; 89*b0d29bc4SBrooks Davis 90*b0d29bc4SBrooks Davis 91*b0d29bc4SBrooks Davis /// The current operating system. 92*b0d29bc4SBrooks Davis static enum os_type current_os = 93*b0d29bc4SBrooks Davis #if defined(__FreeBSD__) 94*b0d29bc4SBrooks Davis os_freebsd 95*b0d29bc4SBrooks Davis #elif defined(__linux__) 96*b0d29bc4SBrooks Davis os_linux 97*b0d29bc4SBrooks Davis #elif defined(__NetBSD__) 98*b0d29bc4SBrooks Davis os_netbsd 99*b0d29bc4SBrooks Davis #elif defined(__SunOS__) 100*b0d29bc4SBrooks Davis os_sunos 101*b0d29bc4SBrooks Davis #else 102*b0d29bc4SBrooks Davis os_unsupported 103*b0d29bc4SBrooks Davis #endif 104*b0d29bc4SBrooks Davis ; 105*b0d29bc4SBrooks Davis 106*b0d29bc4SBrooks Davis 107*b0d29bc4SBrooks Davis /// Specifies if a real unmount(2) is available. 108*b0d29bc4SBrooks Davis /// 109*b0d29bc4SBrooks Davis /// We use this as a constant instead of a macro so that we can compile both 110*b0d29bc4SBrooks Davis /// versions of the unmount code unconditionally. This is a way to prevent 111*b0d29bc4SBrooks Davis /// compilation bugs going unnoticed for long. 112*b0d29bc4SBrooks Davis static const bool have_unmount2 = 113*b0d29bc4SBrooks Davis #if defined(HAVE_UNMOUNT) 114*b0d29bc4SBrooks Davis true; 115*b0d29bc4SBrooks Davis #else 116*b0d29bc4SBrooks Davis false; 117*b0d29bc4SBrooks Davis #endif 118*b0d29bc4SBrooks Davis 119*b0d29bc4SBrooks Davis 120*b0d29bc4SBrooks Davis #if !defined(UMOUNT) 121*b0d29bc4SBrooks Davis /// Fake replacement value to the path to umount(8). 122*b0d29bc4SBrooks Davis # define UMOUNT "do-not-use-this-value" 123*b0d29bc4SBrooks Davis #else 124*b0d29bc4SBrooks Davis # if defined(HAVE_UNMOUNT) 125*b0d29bc4SBrooks Davis # error "umount(8) detected when unmount(2) is also available" 126*b0d29bc4SBrooks Davis # endif 127*b0d29bc4SBrooks Davis #endif 128*b0d29bc4SBrooks Davis 129*b0d29bc4SBrooks Davis 130*b0d29bc4SBrooks Davis #if !defined(HAVE_UNMOUNT) 131*b0d29bc4SBrooks Davis /// Fake unmount(2) function for systems without it. 132*b0d29bc4SBrooks Davis /// 133*b0d29bc4SBrooks Davis /// This is only provided to allow our code to compile in all platforms 134*b0d29bc4SBrooks Davis /// regardless of whether they actually have an unmount(2) or not. 135*b0d29bc4SBrooks Davis /// 136*b0d29bc4SBrooks Davis /// \return -1 to indicate error, although this should never happen. 137*b0d29bc4SBrooks Davis static int 138*b0d29bc4SBrooks Davis unmount(const char* /* path */, 139*b0d29bc4SBrooks Davis const int /* flags */) 140*b0d29bc4SBrooks Davis { 141*b0d29bc4SBrooks Davis PRE(false); 142*b0d29bc4SBrooks Davis return -1; 143*b0d29bc4SBrooks Davis } 144*b0d29bc4SBrooks Davis #endif 145*b0d29bc4SBrooks Davis 146*b0d29bc4SBrooks Davis 147*b0d29bc4SBrooks Davis /// Error code returned by subprocess to indicate a controlled failure. 148*b0d29bc4SBrooks Davis const int exit_known_error = 123; 149*b0d29bc4SBrooks Davis 150*b0d29bc4SBrooks Davis 151*b0d29bc4SBrooks Davis static void run_mount_tmpfs(const fs::path&, const uint64_t) UTILS_NORETURN; 152*b0d29bc4SBrooks Davis 153*b0d29bc4SBrooks Davis 154*b0d29bc4SBrooks Davis /// Executes 'mount -t tmpfs' (or a similar variant). 155*b0d29bc4SBrooks Davis /// 156*b0d29bc4SBrooks Davis /// This function must be called from a subprocess as it never returns. 157*b0d29bc4SBrooks Davis /// 158*b0d29bc4SBrooks Davis /// \param mount_point Location on which to mount a tmpfs. 159*b0d29bc4SBrooks Davis /// \param size The size of the tmpfs to mount. If 0, use unlimited. 160*b0d29bc4SBrooks Davis static void 161*b0d29bc4SBrooks Davis run_mount_tmpfs(const fs::path& mount_point, const uint64_t size) 162*b0d29bc4SBrooks Davis { 163*b0d29bc4SBrooks Davis const char* mount_args[16]; 164*b0d29bc4SBrooks Davis std::string size_arg; 165*b0d29bc4SBrooks Davis 166*b0d29bc4SBrooks Davis std::size_t last = 0; 167*b0d29bc4SBrooks Davis switch (current_os) { 168*b0d29bc4SBrooks Davis case os_freebsd: 169*b0d29bc4SBrooks Davis mount_args[last++] = "mount"; 170*b0d29bc4SBrooks Davis mount_args[last++] = "-ttmpfs"; 171*b0d29bc4SBrooks Davis if (size > 0) { 172*b0d29bc4SBrooks Davis size_arg = F("-osize=%s") % size; 173*b0d29bc4SBrooks Davis mount_args[last++] = size_arg.c_str(); 174*b0d29bc4SBrooks Davis } 175*b0d29bc4SBrooks Davis mount_args[last++] = "tmpfs"; 176*b0d29bc4SBrooks Davis mount_args[last++] = mount_point.c_str(); 177*b0d29bc4SBrooks Davis break; 178*b0d29bc4SBrooks Davis 179*b0d29bc4SBrooks Davis case os_linux: 180*b0d29bc4SBrooks Davis mount_args[last++] = "mount"; 181*b0d29bc4SBrooks Davis mount_args[last++] = "-ttmpfs"; 182*b0d29bc4SBrooks Davis if (size > 0) { 183*b0d29bc4SBrooks Davis size_arg = F("-osize=%s") % size; 184*b0d29bc4SBrooks Davis mount_args[last++] = size_arg.c_str(); 185*b0d29bc4SBrooks Davis } 186*b0d29bc4SBrooks Davis mount_args[last++] = "tmpfs"; 187*b0d29bc4SBrooks Davis mount_args[last++] = mount_point.c_str(); 188*b0d29bc4SBrooks Davis break; 189*b0d29bc4SBrooks Davis 190*b0d29bc4SBrooks Davis case os_netbsd: 191*b0d29bc4SBrooks Davis mount_args[last++] = "mount"; 192*b0d29bc4SBrooks Davis mount_args[last++] = "-ttmpfs"; 193*b0d29bc4SBrooks Davis if (size > 0) { 194*b0d29bc4SBrooks Davis size_arg = F("-o-s%s") % size; 195*b0d29bc4SBrooks Davis mount_args[last++] = size_arg.c_str(); 196*b0d29bc4SBrooks Davis } 197*b0d29bc4SBrooks Davis mount_args[last++] = "tmpfs"; 198*b0d29bc4SBrooks Davis mount_args[last++] = mount_point.c_str(); 199*b0d29bc4SBrooks Davis break; 200*b0d29bc4SBrooks Davis 201*b0d29bc4SBrooks Davis case os_sunos: 202*b0d29bc4SBrooks Davis mount_args[last++] = "mount"; 203*b0d29bc4SBrooks Davis mount_args[last++] = "-Ftmpfs"; 204*b0d29bc4SBrooks Davis if (size > 0) { 205*b0d29bc4SBrooks Davis size_arg = F("-o-s%s") % size; 206*b0d29bc4SBrooks Davis mount_args[last++] = size_arg.c_str(); 207*b0d29bc4SBrooks Davis } 208*b0d29bc4SBrooks Davis mount_args[last++] = "tmpfs"; 209*b0d29bc4SBrooks Davis mount_args[last++] = mount_point.c_str(); 210*b0d29bc4SBrooks Davis break; 211*b0d29bc4SBrooks Davis 212*b0d29bc4SBrooks Davis default: 213*b0d29bc4SBrooks Davis std::cerr << "Don't know how to mount a temporary file system in this " 214*b0d29bc4SBrooks Davis "host operating system\n"; 215*b0d29bc4SBrooks Davis std::exit(exit_known_error); 216*b0d29bc4SBrooks Davis } 217*b0d29bc4SBrooks Davis mount_args[last] = NULL; 218*b0d29bc4SBrooks Davis 219*b0d29bc4SBrooks Davis const char** arg; 220*b0d29bc4SBrooks Davis std::cout << "Mounting tmpfs onto " << mount_point << " with:"; 221*b0d29bc4SBrooks Davis for (arg = &mount_args[0]; *arg != NULL; arg++) 222*b0d29bc4SBrooks Davis std::cout << " " << *arg; 223*b0d29bc4SBrooks Davis std::cout << "\n"; 224*b0d29bc4SBrooks Davis 225*b0d29bc4SBrooks Davis const int ret = ::execvp(mount_args[0], 226*b0d29bc4SBrooks Davis UTILS_UNCONST(char* const, mount_args)); 227*b0d29bc4SBrooks Davis INV(ret == -1); 228*b0d29bc4SBrooks Davis std::cerr << "Failed to exec " << mount_args[0] << "\n"; 229*b0d29bc4SBrooks Davis std::exit(EXIT_FAILURE); 230*b0d29bc4SBrooks Davis } 231*b0d29bc4SBrooks Davis 232*b0d29bc4SBrooks Davis 233*b0d29bc4SBrooks Davis /// Unmounts a file system using unmount(2). 234*b0d29bc4SBrooks Davis /// 235*b0d29bc4SBrooks Davis /// \pre unmount(2) must be available; i.e. have_unmount2 must be true. 236*b0d29bc4SBrooks Davis /// 237*b0d29bc4SBrooks Davis /// \param mount_point The file system to unmount. 238*b0d29bc4SBrooks Davis /// 239*b0d29bc4SBrooks Davis /// \throw fs::system_error If the call to unmount(2) fails. 240*b0d29bc4SBrooks Davis static void 241*b0d29bc4SBrooks Davis unmount_with_unmount2(const fs::path& mount_point) 242*b0d29bc4SBrooks Davis { 243*b0d29bc4SBrooks Davis PRE(have_unmount2); 244*b0d29bc4SBrooks Davis 245*b0d29bc4SBrooks Davis if (::unmount(mount_point.c_str(), 0) == -1) { 246*b0d29bc4SBrooks Davis const int original_errno = errno; 247*b0d29bc4SBrooks Davis throw fs::system_error(F("unmount(%s) failed") % mount_point, 248*b0d29bc4SBrooks Davis original_errno); 249*b0d29bc4SBrooks Davis } 250*b0d29bc4SBrooks Davis } 251*b0d29bc4SBrooks Davis 252*b0d29bc4SBrooks Davis 253*b0d29bc4SBrooks Davis /// Unmounts a file system using umount(8). 254*b0d29bc4SBrooks Davis /// 255*b0d29bc4SBrooks Davis /// \pre umount(2) must not be available; i.e. have_unmount2 must be false. 256*b0d29bc4SBrooks Davis /// 257*b0d29bc4SBrooks Davis /// \param mount_point The file system to unmount. 258*b0d29bc4SBrooks Davis /// 259*b0d29bc4SBrooks Davis /// \throw fs::error If the execution of umount(8) fails. 260*b0d29bc4SBrooks Davis static void 261*b0d29bc4SBrooks Davis unmount_with_umount8(const fs::path& mount_point) 262*b0d29bc4SBrooks Davis { 263*b0d29bc4SBrooks Davis PRE(!have_unmount2); 264*b0d29bc4SBrooks Davis 265*b0d29bc4SBrooks Davis const pid_t pid = ::fork(); 266*b0d29bc4SBrooks Davis if (pid == -1) { 267*b0d29bc4SBrooks Davis const int original_errno = errno; 268*b0d29bc4SBrooks Davis throw fs::system_error("Cannot fork to execute unmount tool", 269*b0d29bc4SBrooks Davis original_errno); 270*b0d29bc4SBrooks Davis } else if (pid == 0) { 271*b0d29bc4SBrooks Davis const int ret = ::execlp(UMOUNT, "umount", mount_point.c_str(), NULL); 272*b0d29bc4SBrooks Davis INV(ret == -1); 273*b0d29bc4SBrooks Davis std::cerr << "Failed to exec " UMOUNT "\n"; 274*b0d29bc4SBrooks Davis std::exit(EXIT_FAILURE); 275*b0d29bc4SBrooks Davis } 276*b0d29bc4SBrooks Davis 277*b0d29bc4SBrooks Davis int status; 278*b0d29bc4SBrooks Davis retry: 279*b0d29bc4SBrooks Davis if (::waitpid(pid, &status, 0) == -1) { 280*b0d29bc4SBrooks Davis const int original_errno = errno; 281*b0d29bc4SBrooks Davis if (errno == EINTR) 282*b0d29bc4SBrooks Davis goto retry; 283*b0d29bc4SBrooks Davis throw fs::system_error("Failed to wait for unmount subprocess", 284*b0d29bc4SBrooks Davis original_errno); 285*b0d29bc4SBrooks Davis } 286*b0d29bc4SBrooks Davis 287*b0d29bc4SBrooks Davis if (WIFEXITED(status)) { 288*b0d29bc4SBrooks Davis if (WEXITSTATUS(status) == EXIT_SUCCESS) 289*b0d29bc4SBrooks Davis return; 290*b0d29bc4SBrooks Davis else 291*b0d29bc4SBrooks Davis throw fs::error(F("Failed to unmount %s; returned exit code %s") 292*b0d29bc4SBrooks Davis % mount_point % WEXITSTATUS(status)); 293*b0d29bc4SBrooks Davis } else 294*b0d29bc4SBrooks Davis throw fs::error(F("Failed to unmount %s; unmount tool received signal") 295*b0d29bc4SBrooks Davis % mount_point); 296*b0d29bc4SBrooks Davis } 297*b0d29bc4SBrooks Davis 298*b0d29bc4SBrooks Davis 299*b0d29bc4SBrooks Davis /// Stats a file, without following links. 300*b0d29bc4SBrooks Davis /// 301*b0d29bc4SBrooks Davis /// \param path The file to stat. 302*b0d29bc4SBrooks Davis /// 303*b0d29bc4SBrooks Davis /// \return The stat structure on success. 304*b0d29bc4SBrooks Davis /// 305*b0d29bc4SBrooks Davis /// \throw system_error An error on failure. 306*b0d29bc4SBrooks Davis static struct ::stat 307*b0d29bc4SBrooks Davis safe_stat(const fs::path& path) 308*b0d29bc4SBrooks Davis { 309*b0d29bc4SBrooks Davis struct ::stat sb; 310*b0d29bc4SBrooks Davis if (::lstat(path.c_str(), &sb) == -1) { 311*b0d29bc4SBrooks Davis const int original_errno = errno; 312*b0d29bc4SBrooks Davis throw fs::system_error(F("Cannot get information about %s") % path, 313*b0d29bc4SBrooks Davis original_errno); 314*b0d29bc4SBrooks Davis } 315*b0d29bc4SBrooks Davis return sb; 316*b0d29bc4SBrooks Davis } 317*b0d29bc4SBrooks Davis 318*b0d29bc4SBrooks Davis 319*b0d29bc4SBrooks Davis } // anonymous namespace 320*b0d29bc4SBrooks Davis 321*b0d29bc4SBrooks Davis 322*b0d29bc4SBrooks Davis /// Copies a file. 323*b0d29bc4SBrooks Davis /// 324*b0d29bc4SBrooks Davis /// \param source The file to copy. 325*b0d29bc4SBrooks Davis /// \param target The destination of the new copy; must be a file name, not a 326*b0d29bc4SBrooks Davis /// directory. 327*b0d29bc4SBrooks Davis /// 328*b0d29bc4SBrooks Davis /// \throw error If there is a problem copying the file. 329*b0d29bc4SBrooks Davis void 330*b0d29bc4SBrooks Davis fs::copy(const fs::path& source, const fs::path& target) 331*b0d29bc4SBrooks Davis { 332*b0d29bc4SBrooks Davis std::ifstream input(source.c_str()); 333*b0d29bc4SBrooks Davis if (!input) 334*b0d29bc4SBrooks Davis throw error(F("Cannot open copy source %s") % source); 335*b0d29bc4SBrooks Davis 336*b0d29bc4SBrooks Davis std::ofstream output(target.c_str()); 337*b0d29bc4SBrooks Davis if (!output) 338*b0d29bc4SBrooks Davis throw error(F("Cannot create copy target %s") % target); 339*b0d29bc4SBrooks Davis 340*b0d29bc4SBrooks Davis char buffer[1024]; 341*b0d29bc4SBrooks Davis while (input.good()) { 342*b0d29bc4SBrooks Davis input.read(buffer, sizeof(buffer)); 343*b0d29bc4SBrooks Davis if (input.good() || input.eof()) 344*b0d29bc4SBrooks Davis output.write(buffer, input.gcount()); 345*b0d29bc4SBrooks Davis } 346*b0d29bc4SBrooks Davis if (!input.good() && !input.eof()) 347*b0d29bc4SBrooks Davis throw error(F("Error while reading input file %s") % source); 348*b0d29bc4SBrooks Davis } 349*b0d29bc4SBrooks Davis 350*b0d29bc4SBrooks Davis 351*b0d29bc4SBrooks Davis /// Queries the path to the current directory. 352*b0d29bc4SBrooks Davis /// 353*b0d29bc4SBrooks Davis /// \return The path to the current directory. 354*b0d29bc4SBrooks Davis /// 355*b0d29bc4SBrooks Davis /// \throw fs::error If there is a problem querying the current directory. 356*b0d29bc4SBrooks Davis fs::path 357*b0d29bc4SBrooks Davis fs::current_path(void) 358*b0d29bc4SBrooks Davis { 359*b0d29bc4SBrooks Davis char* cwd; 360*b0d29bc4SBrooks Davis #if defined(HAVE_GETCWD_DYN) 361*b0d29bc4SBrooks Davis cwd = ::getcwd(NULL, 0); 362*b0d29bc4SBrooks Davis #else 363*b0d29bc4SBrooks Davis cwd = ::getcwd(NULL, MAXPATHLEN); 364*b0d29bc4SBrooks Davis #endif 365*b0d29bc4SBrooks Davis if (cwd == NULL) { 366*b0d29bc4SBrooks Davis const int original_errno = errno; 367*b0d29bc4SBrooks Davis throw fs::system_error(F("Failed to get current working directory"), 368*b0d29bc4SBrooks Davis original_errno); 369*b0d29bc4SBrooks Davis } 370*b0d29bc4SBrooks Davis 371*b0d29bc4SBrooks Davis try { 372*b0d29bc4SBrooks Davis const fs::path result(cwd); 373*b0d29bc4SBrooks Davis std::free(cwd); 374*b0d29bc4SBrooks Davis return result; 375*b0d29bc4SBrooks Davis } catch (...) { 376*b0d29bc4SBrooks Davis std::free(cwd); 377*b0d29bc4SBrooks Davis throw; 378*b0d29bc4SBrooks Davis } 379*b0d29bc4SBrooks Davis } 380*b0d29bc4SBrooks Davis 381*b0d29bc4SBrooks Davis 382*b0d29bc4SBrooks Davis /// Checks if a file exists. 383*b0d29bc4SBrooks Davis /// 384*b0d29bc4SBrooks Davis /// Be aware that this is racy in the same way as access(2) is. 385*b0d29bc4SBrooks Davis /// 386*b0d29bc4SBrooks Davis /// \param path The file to check the existance of. 387*b0d29bc4SBrooks Davis /// 388*b0d29bc4SBrooks Davis /// \return True if the file exists; false otherwise. 389*b0d29bc4SBrooks Davis bool 390*b0d29bc4SBrooks Davis fs::exists(const fs::path& path) 391*b0d29bc4SBrooks Davis { 392*b0d29bc4SBrooks Davis return ::access(path.c_str(), F_OK) == 0; 393*b0d29bc4SBrooks Davis } 394*b0d29bc4SBrooks Davis 395*b0d29bc4SBrooks Davis 396*b0d29bc4SBrooks Davis /// Locates a file in the PATH. 397*b0d29bc4SBrooks Davis /// 398*b0d29bc4SBrooks Davis /// \param name The file to locate. 399*b0d29bc4SBrooks Davis /// 400*b0d29bc4SBrooks Davis /// \return The path to the located file or none if it was not found. The 401*b0d29bc4SBrooks Davis /// returned path is always absolute. 402*b0d29bc4SBrooks Davis optional< fs::path > 403*b0d29bc4SBrooks Davis fs::find_in_path(const char* name) 404*b0d29bc4SBrooks Davis { 405*b0d29bc4SBrooks Davis const optional< std::string > current_path = utils::getenv("PATH"); 406*b0d29bc4SBrooks Davis if (!current_path || current_path.get().empty()) 407*b0d29bc4SBrooks Davis return none; 408*b0d29bc4SBrooks Davis 409*b0d29bc4SBrooks Davis std::istringstream path_input(current_path.get() + ":"); 410*b0d29bc4SBrooks Davis std::string path_component; 411*b0d29bc4SBrooks Davis while (std::getline(path_input, path_component, ':').good()) { 412*b0d29bc4SBrooks Davis const fs::path candidate = path_component.empty() ? 413*b0d29bc4SBrooks Davis fs::path(name) : (fs::path(path_component) / name); 414*b0d29bc4SBrooks Davis if (exists(candidate)) { 415*b0d29bc4SBrooks Davis if (candidate.is_absolute()) 416*b0d29bc4SBrooks Davis return utils::make_optional(candidate); 417*b0d29bc4SBrooks Davis else 418*b0d29bc4SBrooks Davis return utils::make_optional(candidate.to_absolute()); 419*b0d29bc4SBrooks Davis } 420*b0d29bc4SBrooks Davis } 421*b0d29bc4SBrooks Davis return none; 422*b0d29bc4SBrooks Davis } 423*b0d29bc4SBrooks Davis 424*b0d29bc4SBrooks Davis 425*b0d29bc4SBrooks Davis /// Calculates the free space in a given file system. 426*b0d29bc4SBrooks Davis /// 427*b0d29bc4SBrooks Davis /// \param path Path to a file in the file system for which to check the free 428*b0d29bc4SBrooks Davis /// disk space. 429*b0d29bc4SBrooks Davis /// 430*b0d29bc4SBrooks Davis /// \return The amount of free space usable by a non-root user. 431*b0d29bc4SBrooks Davis /// 432*b0d29bc4SBrooks Davis /// \throw system_error If the call to statfs(2) fails. 433*b0d29bc4SBrooks Davis utils::units::bytes 434*b0d29bc4SBrooks Davis fs::free_disk_space(const fs::path& path) 435*b0d29bc4SBrooks Davis { 436*b0d29bc4SBrooks Davis #if defined(HAVE_STATVFS) 437*b0d29bc4SBrooks Davis struct ::statvfs buf; 438*b0d29bc4SBrooks Davis if (::statvfs(path.c_str(), &buf) == -1) { 439*b0d29bc4SBrooks Davis const int original_errno = errno; 440*b0d29bc4SBrooks Davis throw fs::system_error(F("Failed to stat file system for %s") % path, 441*b0d29bc4SBrooks Davis original_errno); 442*b0d29bc4SBrooks Davis } 443*b0d29bc4SBrooks Davis return units::bytes(uint64_t(buf.f_bsize) * buf.f_bavail); 444*b0d29bc4SBrooks Davis #elif defined(HAVE_STATFS) 445*b0d29bc4SBrooks Davis struct ::statfs buf; 446*b0d29bc4SBrooks Davis if (::statfs(path.c_str(), &buf) == -1) { 447*b0d29bc4SBrooks Davis const int original_errno = errno; 448*b0d29bc4SBrooks Davis throw fs::system_error(F("Failed to stat file system for %s") % path, 449*b0d29bc4SBrooks Davis original_errno); 450*b0d29bc4SBrooks Davis } 451*b0d29bc4SBrooks Davis return units::bytes(uint64_t(buf.f_bsize) * buf.f_bavail); 452*b0d29bc4SBrooks Davis #else 453*b0d29bc4SBrooks Davis # error "Don't know how to query free disk space" 454*b0d29bc4SBrooks Davis #endif 455*b0d29bc4SBrooks Davis } 456*b0d29bc4SBrooks Davis 457*b0d29bc4SBrooks Davis 458*b0d29bc4SBrooks Davis /// Checks if the given path is a directory or not. 459*b0d29bc4SBrooks Davis /// 460*b0d29bc4SBrooks Davis /// \return True if the path is a directory; false otherwise. 461*b0d29bc4SBrooks Davis bool 462*b0d29bc4SBrooks Davis fs::is_directory(const fs::path& path) 463*b0d29bc4SBrooks Davis { 464*b0d29bc4SBrooks Davis const struct ::stat sb = safe_stat(path); 465*b0d29bc4SBrooks Davis return S_ISDIR(sb.st_mode); 466*b0d29bc4SBrooks Davis } 467*b0d29bc4SBrooks Davis 468*b0d29bc4SBrooks Davis 469*b0d29bc4SBrooks Davis /// Creates a directory. 470*b0d29bc4SBrooks Davis /// 471*b0d29bc4SBrooks Davis /// \param dir The path to the directory to create. 472*b0d29bc4SBrooks Davis /// \param mode The permissions for the new directory. 473*b0d29bc4SBrooks Davis /// 474*b0d29bc4SBrooks Davis /// \throw system_error If the call to mkdir(2) fails. 475*b0d29bc4SBrooks Davis void 476*b0d29bc4SBrooks Davis fs::mkdir(const fs::path& dir, const int mode) 477*b0d29bc4SBrooks Davis { 478*b0d29bc4SBrooks Davis if (::mkdir(dir.c_str(), static_cast< mode_t >(mode)) == -1) { 479*b0d29bc4SBrooks Davis const int original_errno = errno; 480*b0d29bc4SBrooks Davis throw fs::system_error(F("Failed to create directory %s") % dir, 481*b0d29bc4SBrooks Davis original_errno); 482*b0d29bc4SBrooks Davis } 483*b0d29bc4SBrooks Davis } 484*b0d29bc4SBrooks Davis 485*b0d29bc4SBrooks Davis 486*b0d29bc4SBrooks Davis /// Creates a directory and any missing parents. 487*b0d29bc4SBrooks Davis /// 488*b0d29bc4SBrooks Davis /// This is separate from the fs::mkdir function to clearly differentiate the 489*b0d29bc4SBrooks Davis /// libc wrapper from the more complex algorithm implemented here. 490*b0d29bc4SBrooks Davis /// 491*b0d29bc4SBrooks Davis /// \param dir The path to the directory to create. 492*b0d29bc4SBrooks Davis /// \param mode The permissions for the new directories. 493*b0d29bc4SBrooks Davis /// 494*b0d29bc4SBrooks Davis /// \throw system_error If any call to mkdir(2) fails. 495*b0d29bc4SBrooks Davis void 496*b0d29bc4SBrooks Davis fs::mkdir_p(const fs::path& dir, const int mode) 497*b0d29bc4SBrooks Davis { 498*b0d29bc4SBrooks Davis try { 499*b0d29bc4SBrooks Davis fs::mkdir(dir, mode); 500*b0d29bc4SBrooks Davis } catch (const fs::system_error& e) { 501*b0d29bc4SBrooks Davis if (e.original_errno() == ENOENT) { 502*b0d29bc4SBrooks Davis fs::mkdir_p(dir.branch_path(), mode); 503*b0d29bc4SBrooks Davis fs::mkdir(dir, mode); 504*b0d29bc4SBrooks Davis } else if (e.original_errno() != EEXIST) 505*b0d29bc4SBrooks Davis throw e; 506*b0d29bc4SBrooks Davis } 507*b0d29bc4SBrooks Davis } 508*b0d29bc4SBrooks Davis 509*b0d29bc4SBrooks Davis 510*b0d29bc4SBrooks Davis /// Creates a temporary directory that is world readable/accessible. 511*b0d29bc4SBrooks Davis /// 512*b0d29bc4SBrooks Davis /// The temporary directory is created using mkdtemp(3) using the provided 513*b0d29bc4SBrooks Davis /// template. This should be most likely used in conjunction with 514*b0d29bc4SBrooks Davis /// fs::auto_directory. 515*b0d29bc4SBrooks Davis /// 516*b0d29bc4SBrooks Davis /// The temporary directory is given read and execute permissions to everyone 517*b0d29bc4SBrooks Davis /// and thus should not be used to protect data that may be subject to snooping. 518*b0d29bc4SBrooks Davis /// This goes together with the assumption that this function is used to create 519*b0d29bc4SBrooks Davis /// temporary directories for test cases, and that those test cases may 520*b0d29bc4SBrooks Davis /// sometimes be executed as an unprivileged user. In those cases, we need to 521*b0d29bc4SBrooks Davis /// support two different things: 522*b0d29bc4SBrooks Davis /// 523*b0d29bc4SBrooks Davis /// - Allow the unprivileged code to write to files in the work directory by 524*b0d29bc4SBrooks Davis /// name (e.g. to write the results file, whose name is provided by the 525*b0d29bc4SBrooks Davis /// monitor code running as root). This requires us to grant search 526*b0d29bc4SBrooks Davis /// permissions. 527*b0d29bc4SBrooks Davis /// 528*b0d29bc4SBrooks Davis /// - Allow the test cases themselves to call getcwd(3) at any point. At least 529*b0d29bc4SBrooks Davis /// on NetBSD 7.x, getcwd(3) requires both read and search permissions on all 530*b0d29bc4SBrooks Davis /// path components leading to the current directory. This requires us to 531*b0d29bc4SBrooks Davis /// grant both read and search permissions. 532*b0d29bc4SBrooks Davis /// 533*b0d29bc4SBrooks Davis /// TODO(jmmv): A cleaner way to support this would be for the test executor to 534*b0d29bc4SBrooks Davis /// create two work directory hierarchies directly rooted under TMPDIR: one for 535*b0d29bc4SBrooks Davis /// root and one for the unprivileged user. However, that requires more 536*b0d29bc4SBrooks Davis /// bookkeeping for no real gain, because we are not really trying to protect 537*b0d29bc4SBrooks Davis /// the data within our temporary directories against attacks. 538*b0d29bc4SBrooks Davis /// 539*b0d29bc4SBrooks Davis /// \param path_template The template for the temporary path, which is a 540*b0d29bc4SBrooks Davis /// basename that is created within the TMPDIR. Must contain the XXXXXX 541*b0d29bc4SBrooks Davis /// pattern, which is atomically replaced by a random unique string. 542*b0d29bc4SBrooks Davis /// 543*b0d29bc4SBrooks Davis /// \return The generated path for the temporary directory. 544*b0d29bc4SBrooks Davis /// 545*b0d29bc4SBrooks Davis /// \throw fs::system_error If the call to mkdtemp(3) fails. 546*b0d29bc4SBrooks Davis fs::path 547*b0d29bc4SBrooks Davis fs::mkdtemp_public(const std::string& path_template) 548*b0d29bc4SBrooks Davis { 549*b0d29bc4SBrooks Davis PRE(path_template.find("XXXXXX") != std::string::npos); 550*b0d29bc4SBrooks Davis 551*b0d29bc4SBrooks Davis const fs::path tmpdir(utils::getenv_with_default("TMPDIR", "/tmp")); 552*b0d29bc4SBrooks Davis const fs::path full_template = tmpdir / path_template; 553*b0d29bc4SBrooks Davis 554*b0d29bc4SBrooks Davis utils::auto_array< char > buf(new char[full_template.str().length() + 1]); 555*b0d29bc4SBrooks Davis std::strcpy(buf.get(), full_template.c_str()); 556*b0d29bc4SBrooks Davis if (::mkdtemp(buf.get()) == NULL) { 557*b0d29bc4SBrooks Davis const int original_errno = errno; 558*b0d29bc4SBrooks Davis throw fs::system_error(F("Cannot create temporary directory using " 559*b0d29bc4SBrooks Davis "template %s") % full_template, 560*b0d29bc4SBrooks Davis original_errno); 561*b0d29bc4SBrooks Davis } 562*b0d29bc4SBrooks Davis const fs::path path(buf.get()); 563*b0d29bc4SBrooks Davis 564*b0d29bc4SBrooks Davis if (::chmod(path.c_str(), 0755) == -1) { 565*b0d29bc4SBrooks Davis const int original_errno = errno; 566*b0d29bc4SBrooks Davis 567*b0d29bc4SBrooks Davis try { 568*b0d29bc4SBrooks Davis rmdir(path); 569*b0d29bc4SBrooks Davis } catch (const fs::system_error& e) { 570*b0d29bc4SBrooks Davis // This really should not fail. We just created the directory and 571*b0d29bc4SBrooks Davis // have not written anything to it so there is no reason for this to 572*b0d29bc4SBrooks Davis // fail. But better handle the failure just in case. 573*b0d29bc4SBrooks Davis LW(F("Failed to delete just-created temporary directory %s") 574*b0d29bc4SBrooks Davis % path); 575*b0d29bc4SBrooks Davis } 576*b0d29bc4SBrooks Davis 577*b0d29bc4SBrooks Davis throw fs::system_error(F("Failed to grant search permissions on " 578*b0d29bc4SBrooks Davis "temporary directory %s") % path, 579*b0d29bc4SBrooks Davis original_errno); 580*b0d29bc4SBrooks Davis } 581*b0d29bc4SBrooks Davis 582*b0d29bc4SBrooks Davis return path; 583*b0d29bc4SBrooks Davis } 584*b0d29bc4SBrooks Davis 585*b0d29bc4SBrooks Davis 586*b0d29bc4SBrooks Davis /// Creates a temporary file. 587*b0d29bc4SBrooks Davis /// 588*b0d29bc4SBrooks Davis /// The temporary file is created using mkstemp(3) using the provided template. 589*b0d29bc4SBrooks Davis /// This should be most likely used in conjunction with fs::auto_file. 590*b0d29bc4SBrooks Davis /// 591*b0d29bc4SBrooks Davis /// \param path_template The template for the temporary path, which is a 592*b0d29bc4SBrooks Davis /// basename that is created within the TMPDIR. Must contain the XXXXXX 593*b0d29bc4SBrooks Davis /// pattern, which is atomically replaced by a random unique string. 594*b0d29bc4SBrooks Davis /// 595*b0d29bc4SBrooks Davis /// \return The generated path for the temporary directory. 596*b0d29bc4SBrooks Davis /// 597*b0d29bc4SBrooks Davis /// \throw fs::system_error If the call to mkstemp(3) fails. 598*b0d29bc4SBrooks Davis fs::path 599*b0d29bc4SBrooks Davis fs::mkstemp(const std::string& path_template) 600*b0d29bc4SBrooks Davis { 601*b0d29bc4SBrooks Davis PRE(path_template.find("XXXXXX") != std::string::npos); 602*b0d29bc4SBrooks Davis 603*b0d29bc4SBrooks Davis const fs::path tmpdir(utils::getenv_with_default("TMPDIR", "/tmp")); 604*b0d29bc4SBrooks Davis const fs::path full_template = tmpdir / path_template; 605*b0d29bc4SBrooks Davis 606*b0d29bc4SBrooks Davis utils::auto_array< char > buf(new char[full_template.str().length() + 1]); 607*b0d29bc4SBrooks Davis std::strcpy(buf.get(), full_template.c_str()); 608*b0d29bc4SBrooks Davis if (::mkstemp(buf.get()) == -1) { 609*b0d29bc4SBrooks Davis const int original_errno = errno; 610*b0d29bc4SBrooks Davis throw fs::system_error(F("Cannot create temporary file using template " 611*b0d29bc4SBrooks Davis "%s") % full_template, original_errno); 612*b0d29bc4SBrooks Davis } 613*b0d29bc4SBrooks Davis return fs::path(buf.get()); 614*b0d29bc4SBrooks Davis } 615*b0d29bc4SBrooks Davis 616*b0d29bc4SBrooks Davis 617*b0d29bc4SBrooks Davis /// Mounts a temporary file system with unlimited size. 618*b0d29bc4SBrooks Davis /// 619*b0d29bc4SBrooks Davis /// \param in_mount_point The path on which the file system will be mounted. 620*b0d29bc4SBrooks Davis /// 621*b0d29bc4SBrooks Davis /// \throw fs::system_error If the attempt to mount process fails. 622*b0d29bc4SBrooks Davis /// \throw fs::unsupported_operation_error If the code does not know how to 623*b0d29bc4SBrooks Davis /// mount a temporary file system in the current operating system. 624*b0d29bc4SBrooks Davis void 625*b0d29bc4SBrooks Davis fs::mount_tmpfs(const fs::path& in_mount_point) 626*b0d29bc4SBrooks Davis { 627*b0d29bc4SBrooks Davis mount_tmpfs(in_mount_point, units::bytes()); 628*b0d29bc4SBrooks Davis } 629*b0d29bc4SBrooks Davis 630*b0d29bc4SBrooks Davis 631*b0d29bc4SBrooks Davis /// Mounts a temporary file system. 632*b0d29bc4SBrooks Davis /// 633*b0d29bc4SBrooks Davis /// \param in_mount_point The path on which the file system will be mounted. 634*b0d29bc4SBrooks Davis /// \param size The size of the tmpfs to mount. If 0, use unlimited. 635*b0d29bc4SBrooks Davis /// 636*b0d29bc4SBrooks Davis /// \throw fs::system_error If the attempt to mount process fails. 637*b0d29bc4SBrooks Davis /// \throw fs::unsupported_operation_error If the code does not know how to 638*b0d29bc4SBrooks Davis /// mount a temporary file system in the current operating system. 639*b0d29bc4SBrooks Davis void 640*b0d29bc4SBrooks Davis fs::mount_tmpfs(const fs::path& in_mount_point, const units::bytes& size) 641*b0d29bc4SBrooks Davis { 642*b0d29bc4SBrooks Davis // SunOS's mount(8) requires paths to be absolute. To err on the side of 643*b0d29bc4SBrooks Davis // caution, let's make the mount point absolute in all cases. 644*b0d29bc4SBrooks Davis const fs::path mount_point = in_mount_point.is_absolute() ? 645*b0d29bc4SBrooks Davis in_mount_point : in_mount_point.to_absolute(); 646*b0d29bc4SBrooks Davis 647*b0d29bc4SBrooks Davis const pid_t pid = ::fork(); 648*b0d29bc4SBrooks Davis if (pid == -1) { 649*b0d29bc4SBrooks Davis const int original_errno = errno; 650*b0d29bc4SBrooks Davis throw fs::system_error("Cannot fork to execute mount tool", 651*b0d29bc4SBrooks Davis original_errno); 652*b0d29bc4SBrooks Davis } 653*b0d29bc4SBrooks Davis if (pid == 0) 654*b0d29bc4SBrooks Davis run_mount_tmpfs(mount_point, size); 655*b0d29bc4SBrooks Davis 656*b0d29bc4SBrooks Davis int status; 657*b0d29bc4SBrooks Davis retry: 658*b0d29bc4SBrooks Davis if (::waitpid(pid, &status, 0) == -1) { 659*b0d29bc4SBrooks Davis const int original_errno = errno; 660*b0d29bc4SBrooks Davis if (errno == EINTR) 661*b0d29bc4SBrooks Davis goto retry; 662*b0d29bc4SBrooks Davis throw fs::system_error("Failed to wait for mount subprocess", 663*b0d29bc4SBrooks Davis original_errno); 664*b0d29bc4SBrooks Davis } 665*b0d29bc4SBrooks Davis 666*b0d29bc4SBrooks Davis if (WIFEXITED(status)) { 667*b0d29bc4SBrooks Davis if (WEXITSTATUS(status) == exit_known_error) 668*b0d29bc4SBrooks Davis throw fs::unsupported_operation_error( 669*b0d29bc4SBrooks Davis "Don't know how to mount a tmpfs on this operating system"); 670*b0d29bc4SBrooks Davis else if (WEXITSTATUS(status) == EXIT_SUCCESS) 671*b0d29bc4SBrooks Davis return; 672*b0d29bc4SBrooks Davis else 673*b0d29bc4SBrooks Davis throw fs::error(F("Failed to mount tmpfs on %s; returned exit " 674*b0d29bc4SBrooks Davis "code %s") % mount_point % WEXITSTATUS(status)); 675*b0d29bc4SBrooks Davis } else { 676*b0d29bc4SBrooks Davis throw fs::error(F("Failed to mount tmpfs on %s; mount tool " 677*b0d29bc4SBrooks Davis "received signal") % mount_point); 678*b0d29bc4SBrooks Davis } 679*b0d29bc4SBrooks Davis } 680*b0d29bc4SBrooks Davis 681*b0d29bc4SBrooks Davis 682*b0d29bc4SBrooks Davis /// Recursively removes a directory. 683*b0d29bc4SBrooks Davis /// 684*b0d29bc4SBrooks Davis /// This operation simulates a "rm -r". No effort is made to forcibly delete 685*b0d29bc4SBrooks Davis /// files and no attention is paid to mount points. 686*b0d29bc4SBrooks Davis /// 687*b0d29bc4SBrooks Davis /// \param directory The directory to remove. 688*b0d29bc4SBrooks Davis /// 689*b0d29bc4SBrooks Davis /// \throw fs::error If there is a problem removing any directory or file. 690*b0d29bc4SBrooks Davis void 691*b0d29bc4SBrooks Davis fs::rm_r(const fs::path& directory) 692*b0d29bc4SBrooks Davis { 693*b0d29bc4SBrooks Davis const fs::directory dir(directory); 694*b0d29bc4SBrooks Davis 695*b0d29bc4SBrooks Davis for (fs::directory::const_iterator iter = dir.begin(); iter != dir.end(); 696*b0d29bc4SBrooks Davis ++iter) { 697*b0d29bc4SBrooks Davis if (iter->name == "." || iter->name == "..") 698*b0d29bc4SBrooks Davis continue; 699*b0d29bc4SBrooks Davis 700*b0d29bc4SBrooks Davis const fs::path entry = directory / iter->name; 701*b0d29bc4SBrooks Davis 702*b0d29bc4SBrooks Davis if (fs::is_directory(entry)) { 703*b0d29bc4SBrooks Davis LD(F("Descending into %s") % entry); 704*b0d29bc4SBrooks Davis fs::rm_r(entry); 705*b0d29bc4SBrooks Davis } else { 706*b0d29bc4SBrooks Davis LD(F("Removing file %s") % entry); 707*b0d29bc4SBrooks Davis fs::unlink(entry); 708*b0d29bc4SBrooks Davis } 709*b0d29bc4SBrooks Davis } 710*b0d29bc4SBrooks Davis 711*b0d29bc4SBrooks Davis LD(F("Removing empty directory %s") % directory); 712*b0d29bc4SBrooks Davis fs::rmdir(directory); 713*b0d29bc4SBrooks Davis } 714*b0d29bc4SBrooks Davis 715*b0d29bc4SBrooks Davis 716*b0d29bc4SBrooks Davis /// Removes an empty directory. 717*b0d29bc4SBrooks Davis /// 718*b0d29bc4SBrooks Davis /// \param file The directory to remove. 719*b0d29bc4SBrooks Davis /// 720*b0d29bc4SBrooks Davis /// \throw fs::system_error If the call to rmdir(2) fails. 721*b0d29bc4SBrooks Davis void 722*b0d29bc4SBrooks Davis fs::rmdir(const path& file) 723*b0d29bc4SBrooks Davis { 724*b0d29bc4SBrooks Davis if (::rmdir(file.c_str()) == -1) { 725*b0d29bc4SBrooks Davis const int original_errno = errno; 726*b0d29bc4SBrooks Davis throw fs::system_error(F("Removal of %s failed") % file, 727*b0d29bc4SBrooks Davis original_errno); 728*b0d29bc4SBrooks Davis } 729*b0d29bc4SBrooks Davis } 730*b0d29bc4SBrooks Davis 731*b0d29bc4SBrooks Davis 732*b0d29bc4SBrooks Davis /// Obtains all the entries in a directory. 733*b0d29bc4SBrooks Davis /// 734*b0d29bc4SBrooks Davis /// \param path The directory to scan. 735*b0d29bc4SBrooks Davis /// 736*b0d29bc4SBrooks Davis /// \return The set of all directory entries in the given directory. 737*b0d29bc4SBrooks Davis /// 738*b0d29bc4SBrooks Davis /// \throw fs::system_error If reading the directory fails for any reason. 739*b0d29bc4SBrooks Davis std::set< fs::directory_entry > 740*b0d29bc4SBrooks Davis fs::scan_directory(const fs::path& path) 741*b0d29bc4SBrooks Davis { 742*b0d29bc4SBrooks Davis std::set< fs::directory_entry > contents; 743*b0d29bc4SBrooks Davis 744*b0d29bc4SBrooks Davis fs::directory dir(path); 745*b0d29bc4SBrooks Davis for (fs::directory::const_iterator iter = dir.begin(); iter != dir.end(); 746*b0d29bc4SBrooks Davis ++iter) { 747*b0d29bc4SBrooks Davis contents.insert(*iter); 748*b0d29bc4SBrooks Davis } 749*b0d29bc4SBrooks Davis 750*b0d29bc4SBrooks Davis return contents; 751*b0d29bc4SBrooks Davis } 752*b0d29bc4SBrooks Davis 753*b0d29bc4SBrooks Davis 754*b0d29bc4SBrooks Davis /// Removes a file. 755*b0d29bc4SBrooks Davis /// 756*b0d29bc4SBrooks Davis /// \param file The file to remove. 757*b0d29bc4SBrooks Davis /// 758*b0d29bc4SBrooks Davis /// \throw fs::system_error If the call to unlink(2) fails. 759*b0d29bc4SBrooks Davis void 760*b0d29bc4SBrooks Davis fs::unlink(const path& file) 761*b0d29bc4SBrooks Davis { 762*b0d29bc4SBrooks Davis if (::unlink(file.c_str()) == -1) { 763*b0d29bc4SBrooks Davis const int original_errno = errno; 764*b0d29bc4SBrooks Davis throw fs::system_error(F("Removal of %s failed") % file, 765*b0d29bc4SBrooks Davis original_errno); 766*b0d29bc4SBrooks Davis } 767*b0d29bc4SBrooks Davis } 768*b0d29bc4SBrooks Davis 769*b0d29bc4SBrooks Davis 770*b0d29bc4SBrooks Davis /// Unmounts a file system. 771*b0d29bc4SBrooks Davis /// 772*b0d29bc4SBrooks Davis /// \param in_mount_point The file system to unmount. 773*b0d29bc4SBrooks Davis /// 774*b0d29bc4SBrooks Davis /// \throw fs::error If the unmount fails. 775*b0d29bc4SBrooks Davis void 776*b0d29bc4SBrooks Davis fs::unmount(const fs::path& in_mount_point) 777*b0d29bc4SBrooks Davis { 778*b0d29bc4SBrooks Davis // FreeBSD's unmount(2) requires paths to be absolute. To err on the side 779*b0d29bc4SBrooks Davis // of caution, let's make it absolute in all cases. 780*b0d29bc4SBrooks Davis const fs::path mount_point = in_mount_point.is_absolute() ? 781*b0d29bc4SBrooks Davis in_mount_point : in_mount_point.to_absolute(); 782*b0d29bc4SBrooks Davis 783*b0d29bc4SBrooks Davis static const int unmount_retries = 3; 784*b0d29bc4SBrooks Davis static const int unmount_retry_delay_seconds = 1; 785*b0d29bc4SBrooks Davis 786*b0d29bc4SBrooks Davis int retries = unmount_retries; 787*b0d29bc4SBrooks Davis retry: 788*b0d29bc4SBrooks Davis try { 789*b0d29bc4SBrooks Davis if (have_unmount2) { 790*b0d29bc4SBrooks Davis unmount_with_unmount2(mount_point); 791*b0d29bc4SBrooks Davis } else { 792*b0d29bc4SBrooks Davis unmount_with_umount8(mount_point); 793*b0d29bc4SBrooks Davis } 794*b0d29bc4SBrooks Davis } catch (const fs::system_error& error) { 795*b0d29bc4SBrooks Davis if (error.original_errno() == EBUSY && retries > 0) { 796*b0d29bc4SBrooks Davis LW(F("%s busy; unmount retries left %s") % mount_point % retries); 797*b0d29bc4SBrooks Davis retries--; 798*b0d29bc4SBrooks Davis ::sleep(unmount_retry_delay_seconds); 799*b0d29bc4SBrooks Davis goto retry; 800*b0d29bc4SBrooks Davis } 801*b0d29bc4SBrooks Davis throw; 802*b0d29bc4SBrooks Davis } 803*b0d29bc4SBrooks Davis } 804