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 40 namespace fs = utils::fs; 41 namespace signals = utils::signals; 42 43 44 /// Shared implementation of the auto_directory. 45 struct 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. 92 fs::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. 102 fs::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. 120 fs::auto_directory 121 fs::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. 137 const fs::path& 138 fs::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. 149 void 150 fs::auto_directory::cleanup(void) 151 { 152 _pimpl->cleanup(); 153 } 154 155 156 /// Shared implementation of the auto_file. 157 struct 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. 204 fs::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. 214 fs::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. 228 fs::auto_file 229 fs::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. 245 const fs::path& 246 fs::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. 257 void 258 fs::auto_file::remove(void) 259 { 260 _pimpl->remove(); 261 } 262