1 // Copyright 2010 Google Inc.
2 // All rights reserved.
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
8 // * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above copyright
11 // notice, this list of conditions and the following disclaimer in the
12 // documentation and/or other materials provided with the distribution.
13 // * Neither the name of Google Inc. nor the names of its contributors
14 // may be used to endorse or promote products derived from this software
15 // without specific prior written permission.
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #include "utils/fs/operations.hpp"
31 #if defined(HAVE_CONFIG_H)
48 #include "utils/auto_array.ipp"
49 #include "utils/defs.hpp"
50 #include "utils/env.hpp"
51 #include "utils/format/macros.hpp"
52 #include "utils/fs/exceptions.hpp"
53 #include "utils/fs/path.hpp"
54 #include "utils/logging/macros.hpp"
55 #include "utils/optional.ipp"
56 #include "utils/sanity.hpp"
58 namespace fs
= utils::fs
;
60 using utils::optional
;
66 /// Stats a file, without following links.
68 /// \param path The file to stat.
70 /// \return The stat structure on success.
72 /// \throw system_error An error on failure.
74 safe_stat(const fs::path
& path
)
77 if (::lstat(path
.c_str(), &sb
) == -1) {
78 const int original_errno
= errno
;
79 throw fs::system_error(F("Cannot get information about %s") % path
,
86 } // anonymous namespace
89 /// Queries the path to the current directory.
91 /// \return The path to the current directory.
93 /// \throw fs::error If there is a problem querying the current directory.
95 fs::current_path(void)
98 #if defined(HAVE_GETCWD_DYN)
99 cwd
= ::getcwd(NULL
, 0);
101 cwd
= ::getcwd(NULL
, MAXPATHLEN
);
104 const int original_errno
= errno
;
105 throw fs::system_error(F("Failed to get current working directory"),
110 const fs::path
result(cwd
);
120 /// Checks if a file exists.
122 /// Be aware that this is racy in the same way as access(2) is.
124 /// \param path The file to check the existance of.
126 /// \return True if the file exists; false otherwise.
128 fs::exists(const fs::path
& path
)
130 return ::access(path
.c_str(), F_OK
) == 0;
134 /// Locates a file in the PATH.
136 /// \param name The file to locate.
138 /// \return The path to the located file or none if it was not found. The
139 /// returned path is always absolute.
141 fs::find_in_path(const char* name
)
143 const optional
< std::string
> current_path
= utils::getenv("PATH");
144 if (!current_path
|| current_path
.get().empty())
147 std::istringstream
path_input(current_path
.get() + ":");
148 std::string path_component
;
149 while (std::getline(path_input
, path_component
, ':').good()) {
150 const fs::path candidate
= path_component
.empty() ?
151 fs::path(name
) : (fs::path(path_component
) / name
);
152 if (exists(candidate
)) {
153 if (candidate
.is_absolute())
154 return utils::make_optional(candidate
);
156 return utils::make_optional(candidate
.to_absolute());
163 /// Creates a directory.
165 /// \param dir The path to the directory to create.
166 /// \param mode The permissions for the new directory.
168 /// \throw system_error If the call to mkdir(2) fails.
170 fs::mkdir(const fs::path
& dir
, const int mode
)
172 if (::mkdir(dir
.c_str(), static_cast< mode_t
>(mode
)) == -1) {
173 const int original_errno
= errno
;
174 throw fs::system_error(F("Failed to create directory %s") % dir
,
180 /// Creates a directory and any missing parents.
182 /// This is separate from the fs::mkdir function to clearly differentiate the
183 /// libc wrapper from the more complex algorithm implemented here.
185 /// \param dir The path to the directory to create.
186 /// \param mode The permissions for the new directories.
188 /// \throw system_error If any call to mkdir(2) fails.
190 fs::mkdir_p(const fs::path
& dir
, const int mode
)
193 fs::mkdir(dir
, mode
);
194 } catch (const fs::system_error
& e
) {
195 if (e
.original_errno() == ENOENT
) {
196 fs::mkdir_p(dir
.branch_path(), mode
);
197 fs::mkdir(dir
, mode
);
198 } else if (e
.original_errno() != EEXIST
)
204 /// Creates a temporary directory.
206 /// The temporary directory is created using mkdtemp(3) using the provided
207 /// template. This should be most likely used in conjunction with
208 /// fs::auto_directory.
210 /// \param path_template The template for the temporary path, which is a
211 /// basename that is created within the TMPDIR. Must contain the XXXXXX
212 /// pattern, which is atomically replaced by a random unique string.
214 /// \return The generated path for the temporary directory.
216 /// \throw fs::system_error If the call to mkdtemp(3) fails.
218 fs::mkdtemp(const std::string
& path_template
)
220 PRE(path_template
.find("XXXXXX") != std::string::npos
);
222 const fs::path
tmpdir(utils::getenv_with_default("TMPDIR", "/tmp"));
223 const fs::path full_template
= tmpdir
/ path_template
;
225 utils::auto_array
< char > buf(new char[full_template
.str().length() + 1]);
226 std::strcpy(buf
.get(), full_template
.c_str());
227 if (::mkdtemp(buf
.get()) == NULL
) {
228 const int original_errno
= errno
;
229 throw fs::system_error(F("Cannot create temporary directory using "
230 "template %s") % full_template
,
233 return fs::path(buf
.get());
237 /// Creates a temporary file.
239 /// The temporary file is created using mkstemp(3) using the provided template.
240 /// This should be most likely used in conjunction with fs::auto_file.
242 /// \param path_template The template for the temporary path, which is a
243 /// basename that is created within the TMPDIR. Must contain the XXXXXX
244 /// pattern, which is atomically replaced by a random unique string.
246 /// \return The generated path for the temporary directory.
248 /// \throw fs::system_error If the call to mkstemp(3) fails.
250 fs::mkstemp(const std::string
& path_template
)
252 PRE(path_template
.find("XXXXXX") != std::string::npos
);
254 const fs::path
tmpdir(utils::getenv_with_default("TMPDIR", "/tmp"));
255 const fs::path full_template
= tmpdir
/ path_template
;
257 utils::auto_array
< char > buf(new char[full_template
.str().length() + 1]);
258 std::strcpy(buf
.get(), full_template
.c_str());
259 if (::mkstemp(buf
.get()) == -1) {
260 const int original_errno
= errno
;
261 throw fs::system_error(F("Cannot create temporary file using template "
262 "%s") % full_template
, original_errno
);
264 return fs::path(buf
.get());
268 /// Recursively removes a directory.
270 /// This operation simulates a "rm -r". No effort is made to forcibly delete
271 /// files and no attention is paid to mount points.
273 /// \param directory The directory to remove.
275 /// \throw fs::error If there is a problem removing any directory or file.
277 fs::rm_r(const fs::path
& directory
)
279 DIR* dirp
= ::opendir(directory
.c_str());
281 const int original_errno
= errno
;
282 throw fs::system_error(F("Failed to open directory %s") %
283 directory
.str(), original_errno
);
287 while ((dp
= ::readdir(dirp
)) != NULL
) {
288 const std::string name
= dp
->d_name
;
289 if (name
== "." || name
== "..")
292 const fs::path entry
= directory
/ dp
->d_name
;
294 const struct ::stat sb
= safe_stat(entry
);
295 if (S_ISDIR(sb
.st_mode
)) {
296 LD(F("Descending into %s") % entry
);
299 LD(F("Removing file %s") % entry
);
309 LD(F("Removing empty directory %s") % directory
);
310 fs::rmdir(directory
);
314 /// Removes an empty directory.
316 /// \param file The directory to remove.
318 /// \throw fs::system_error If the call to rmdir(2) fails.
320 fs::rmdir(const path
& file
)
322 if (::rmdir(file
.c_str()) == -1) {
323 const int original_errno
= errno
;
324 throw fs::system_error(F("Removal of %s failed") % file
,
332 /// \param file The file to remove.
334 /// \throw fs::system_error If the call to unlink(2) fails.
336 fs::unlink(const path
& file
)
338 if (::unlink(file
.c_str()) == -1) {
339 const int original_errno
= errno
;
340 throw fs::system_error(F("Removal of %s failed") % file
,