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