xapian-core  1.4.27
unixcmds.cc
Go to the documentation of this file.
1 
4 /* Copyright (C) 2003,2004,2007,2012,2014,2015,2018 Olly Betts
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License as
8  * published by the Free Software Foundation; either version 2 of the
9  * License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19  */
20 
21 #include <config.h>
22 
23 #include "unixcmds.h"
24 
25 #include <string>
26 #include <cerrno>
27 #include <cstdlib>
28 #include <sys/types.h>
29 #include "safeunistd.h"
30 #include "safefcntl.h"
31 
32 #include "append_filename_arg.h"
33 #include "errno_to_string.h"
34 #include "filetests.h"
35 #include "str.h"
36 
37 // mingw-w64 added an implementation of nftw() but it seems to be buggy and
38 // garble paths, though I can't see where in the code things go wrong and
39 // their code seems to work when built on Linux.
40 //
41 // For now, we just blacklist nftw() here and on mingw32 (which doesn't
42 // currently have it, but let's be defensive in case somebody copies over the
43 // buggy version). Using nftw() is just a minor optimisation which only
44 // makes a real difference for developers running the testsuite a lot.
45 #if defined HAVE_NFTW && !defined __MINGW32__
46 # include <ftw.h>
47 # include <unistd.h>
48 #endif
49 
50 using namespace std;
51 
53 static void
54 checked_system(const string & cmd)
55 {
56  int res = system(cmd.c_str());
57  if (res) {
58  string msg = "system(\"";
59  msg += cmd;
60  msg += "\") failed, returning ";
61  msg += str(res);
62  throw msg;
63  }
64 }
65 
67 void cp_R(const std::string &src, const std::string &dest) {
68 #ifdef __WIN32__
69  // We create the target directory first to avoid being prompted as to
70  // whether we want to create a directory or a file (which makes no sense
71  // when copying a directory, but that's how xcopy seems to work!)
72  mkdir(dest.c_str());
73  string cmd("xcopy /E /q /Y");
74 #else
75  string cmd("cp -R");
76 #endif
77  if (!append_filename_argument(cmd, src)) return;
78  if (!append_filename_argument(cmd, dest)) return;
79 #ifdef __WIN32__
80  // xcopy reports how many files it copied, even with /q.
81  cmd += " >nul";
82 #endif
83  checked_system(cmd);
84 #ifndef __WIN32__
85  // Allow write access to the copy (to deal with builds where srcdir is
86  // readonly).
87  cmd = "chmod -R +w";
88  if (!append_filename_argument(cmd, dest)) return;
89  checked_system(cmd);
90 #endif
91 }
92 
93 #if defined HAVE_NFTW && !defined __MINGW32__
94 extern "C" {
95 static int
96 rm_rf_nftw_helper(const char* path,
97  const struct stat*,
98  int type,
99  struct FTW*)
100 {
101  int r = (type == FTW_DP ? rmdir(path) : unlink(path));
102  // Return the errno value if deletion fails as the nftw() function might
103  // overwrite errno during clean-up. Any non-zero return value will end
104  // the walk.
105  return r < 0 ? errno : 0;
106 }
107 }
108 #endif
109 
111 void rm_rf(const string &filename) {
112  // Check filename exists and is actually a directory
113  if (filename.empty() || !dir_exists(filename))
114  return;
115 
116 #if defined HAVE_NFTW && !defined __MINGW32__
117  auto flags = FTW_DEPTH | FTW_PHYS;
118  int retries = 5;
119  while (true) {
120  int eno = nftw(filename.c_str(), rm_rf_nftw_helper, 10, flags);
121  if (eno == 0)
122  return;
123 
124  // nftw() either returns 0 for OK, -1 for error, or the non-zero return
125  // value of the helper (which in our case is an errno value).
126  if (eno < 0)
127  eno = errno;
128  if (!(eno == EEXIST || eno == ENOTEMPTY) || --retries == 0) {
129  string msg = "recursive delete of \"";
130  msg += filename;
131  msg += "\") failed, errno = ";
132  errno_to_string(eno, msg);
133  throw msg;
134  }
135 
136  // On NFS, rmdir() can fail with EEXIST or ENOTEMPTY (POSIX allows
137  // either) due to .nfs* files which are used by NFS clients to
138  // implement the Unix semantics of a deleted but open file continuing
139  // to exist. We sleep and retry a few times in this situation to give
140  // the NFS client a chance to process the closing of the open handle.
141  sleep(5);
142  }
143 #else
144 # ifdef __WIN32__
145  string cmd("rd /s /q");
146 # else
147  string cmd("rm -rf");
148 # endif
149  if (!append_filename_argument(cmd, filename)) return;
150  checked_system(cmd);
151 #endif
152 }
153 
155 void touch(const string &filename) {
156  int fd = open(filename.c_str(), O_CREAT|O_WRONLY|O_BINARY, 0644);
157  if (fd >= 0) close(fd);
158 }
int close(FD &fd)
Definition: fd.h:63
Convert errno value to std::string, thread-safe if possible.
void sleep(double t)
Sleep until the time represented by this object.
Definition: realtime.h:127
#define O_BINARY
Definition: safefcntl.h:81
C++ function versions of useful Unix commands.
WritableDatabase open()
Construct a WritableDatabase object for a new, empty InMemory database.
Definition: dbfactory.h:104
STL namespace.
Convert types to std::string.
Utility functions for testing files.
void rm_rf(const string &filename)
Remove a directory and contents, just like the Unix "rm -rf" command.
Definition: unixcmds.cc:111
Append filename argument to a command string with suitable escaping.
void cp_R(const std::string &src, const std::string &dest)
Recursively copy a directory.
Definition: unixcmds.cc:67
void errno_to_string(int e, string &s)
string str(int value)
Convert int to std::string.
Definition: str.cc:90
bool dir_exists(const char *path)
Test if a directory exists.
Definition: filetests.h:136
<unistd.h>, but with compat.
static bool append_filename_argument(std::string &cmd, const std::string &arg, bool leading_space=true)
Append filename argument arg to command cmd with suitable escaping.
static void checked_system(const string &cmd)
Call system() and throw exception if it fails.
Definition: unixcmds.cc:54
include <fcntl.h>, but working around broken platforms.
void touch(const string &filename)
Touch a file, just like the Unix "touch" command.
Definition: unixcmds.cc:155