1// Copyright 2010 The Kyua Authors. 2// All rights reserved. 3// 4// Redistribution and use in source and binary forms, with or without 5// modification, are permitted provided that the following conditions are 6// met: 7// 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. 16// 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. 28 29#include "utils/fs/auto_cleaners.hpp" 30 31#include "utils/format/macros.hpp" 32#include "utils/fs/exceptions.hpp" 33#include "utils/fs/operations.hpp" 34#include "utils/fs/path.hpp" 35#include "utils/logging/macros.hpp" 36#include "utils/noncopyable.hpp" 37#include "utils/sanity.hpp" 38#include "utils/signals/interrupts.hpp" 39 40namespace fs = utils::fs; 41namespace signals = utils::signals; 42 43 44/// Shared implementation of the auto_directory. 45struct utils::fs::auto_directory::impl : utils::noncopyable { 46 /// The path to the directory being managed. 47 fs::path _directory; 48 49 /// Whether cleanup() has been already executed or not. 50 bool _cleaned; 51 52 /// Constructor. 53 /// 54 /// \param directory_ The directory to grab the ownership of. 55 impl(const path& directory_) : 56 _directory(directory_), 57 _cleaned(false) 58 { 59 } 60 61 /// Destructor. 62 ~impl(void) 63 { 64 try { 65 this->cleanup(); 66 } catch (const fs::error& e) { 67 LW(F("Failed to auto-cleanup directory '%s': %s") % _directory % 68 e.what()); 69 } 70 } 71 72 /// Removes the directory. 73 /// 74 /// See the cleanup() method of the auto_directory class for details. 75 void 76 cleanup(void) 77 { 78 if (!_cleaned) { 79 // Mark this as cleaned first so that, in case of failure, we don't 80 // reraise the error from the destructor. 81 _cleaned = true; 82 83 fs::rmdir(_directory); 84 } 85 } 86}; 87 88 89/// Constructs a new auto_directory and grabs ownership of a directory. 90/// 91/// \param directory_ The directory to grab the ownership of. 92fs::auto_directory::auto_directory(const path& directory_) : 93 _pimpl(new impl(directory_)) 94{ 95} 96 97 98/// Deletes the managed directory; must be empty. 99/// 100/// This should not be relied on because it cannot provide proper error 101/// reporting. Instead, the caller should use the cleanup() method. 102fs::auto_directory::~auto_directory(void) 103{ 104} 105 106 107/// Creates a self-destructing temporary directory. 108/// 109/// See the notes for fs::mkdtemp_public() for details on the permissions 110/// given to the temporary directory, which are looser than what the standard 111/// mkdtemp would grant. 112/// 113/// \param path_template The template for the temporary path, which is a 114/// basename that is created within the TMPDIR. Must contain the XXXXXX 115/// pattern, which is atomically replaced by a random unique string. 116/// 117/// \return The self-destructing directory. 118/// 119/// \throw fs::error If the creation fails. 120fs::auto_directory 121fs::auto_directory::mkdtemp_public(const std::string& path_template) 122{ 123 signals::interrupts_inhibiter inhibiter; 124 const fs::path directory_ = fs::mkdtemp_public(path_template); 125 try { 126 return auto_directory(directory_); 127 } catch (...) { 128 fs::rmdir(directory_); 129 throw; 130 } 131} 132 133 134/// Gets the directory managed by this auto_directory. 135/// 136/// \return The path to the managed directory. 137const fs::path& 138fs::auto_directory::directory(void) const 139{ 140 return _pimpl->_directory; 141} 142 143 144/// Deletes the managed directory; must be empty. 145/// 146/// This operation is idempotent. 147/// 148/// \throw fs::error If there is a problem removing any directory or file. 149void 150fs::auto_directory::cleanup(void) 151{ 152 _pimpl->cleanup(); 153} 154 155 156/// Shared implementation of the auto_file. 157struct utils::fs::auto_file::impl : utils::noncopyable { 158 /// The path to the file being managed. 159 fs::path _file; 160 161 /// Whether removed() has been already executed or not. 162 bool _removed; 163 164 /// Constructor. 165 /// 166 /// \param file_ The file to grab the ownership of. 167 impl(const path& file_) : 168 _file(file_), 169 _removed(false) 170 { 171 } 172 173 /// Destructor. 174 ~impl(void) 175 { 176 try { 177 this->remove(); 178 } catch (const fs::error& e) { 179 LW(F("Failed to auto-cleanup file '%s': %s") % _file % 180 e.what()); 181 } 182 } 183 184 /// Removes the file. 185 /// 186 /// See the remove() method of the auto_file class for details. 187 void 188 remove(void) 189 { 190 if (!_removed) { 191 // Mark this as cleaned first so that, in case of failure, we don't 192 // reraise the error from the destructor. 193 _removed = true; 194 195 fs::unlink(_file); 196 } 197 } 198}; 199 200 201/// Constructs a new auto_file and grabs ownership of a file. 202/// 203/// \param file_ The file to grab the ownership of. 204fs::auto_file::auto_file(const path& file_) : 205 _pimpl(new impl(file_)) 206{ 207} 208 209 210/// Deletes the managed file. 211/// 212/// This should not be relied on because it cannot provide proper error 213/// reporting. Instead, the caller should use the remove() method. 214fs::auto_file::~auto_file(void) 215{ 216} 217 218 219/// Creates a self-destructing temporary file. 220/// 221/// \param path_template The template for the temporary path, which is a 222/// basename that is created within the TMPDIR. Must contain the XXXXXX 223/// pattern, which is atomically replaced by a random unique string. 224/// 225/// \return The self-destructing file. 226/// 227/// \throw fs::error If the creation fails. 228fs::auto_file 229fs::auto_file::mkstemp(const std::string& path_template) 230{ 231 signals::interrupts_inhibiter inhibiter; 232 const fs::path file_ = fs::mkstemp(path_template); 233 try { 234 return auto_file(file_); 235 } catch (...) { 236 fs::unlink(file_); 237 throw; 238 } 239} 240 241 242/// Gets the file managed by this auto_file. 243/// 244/// \return The path to the managed file. 245const fs::path& 246fs::auto_file::file(void) const 247{ 248 return _pimpl->_file; 249} 250 251 252/// Deletes the managed file. 253/// 254/// This operation is idempotent. 255/// 256/// \throw fs::error If there is a problem removing the file. 257void 258fs::auto_file::remove(void) 259{ 260 _pimpl->remove(); 261} 262