| 1 | /* |
| 2 | The software in this package is distributed under the GNU General |
| 3 | Public License version 2 (with a special exception described below). |
| 4 | |
| 5 | A copy of GNU General Public License (GPL) is included in this distribution, |
| 6 | in the file COPYING.GPL. |
| 7 | |
| 8 | As a special exception, if other files instantiate templates or use macros |
| 9 | or inline functions from this file, or you compile this file and link it |
| 10 | with other works to produce a work based on this file, this file |
| 11 | does not by itself cause the resulting work to be covered |
| 12 | by the GNU General Public License. |
| 13 | |
| 14 | However the source code for this file must still be made available |
| 15 | in accordance with section (3) of the GNU General Public License. |
| 16 | |
| 17 | This exception does not invalidate any other reasons why a work based |
| 18 | on this file might be covered by the GNU General Public License. |
| 19 | */ |
| 20 | /** @file |
| 21 | * @brief fstream which creates files with mkstemp. |
| 22 | * |
| 23 | * @author Gerd v. Egidy |
| 24 | * |
| 25 | * @copyright © Copyright 2010 by Intra2net AG |
| 26 | */ |
| 27 | |
| 28 | #ifndef __I2N_TMPFSTREAM_HPP__ |
| 29 | #define __I2N_TMPFSTREAM_HPP__ |
| 30 | |
| 31 | #include <string> |
| 32 | |
| 33 | #include <boost/iostreams/detail/char_traits.hpp> |
| 34 | #include <boost/config.hpp> |
| 35 | #include <boost/iostreams/stream.hpp> |
| 36 | #include <boost/iostreams/device/file_descriptor.hpp> |
| 37 | |
| 38 | #include <userfunc.hpp> |
| 39 | |
| 40 | #include <sys/stat.h> |
| 41 | |
| 42 | namespace I2n |
| 43 | { |
| 44 | |
| 45 | namespace bio = boost::iostreams; |
| 46 | |
| 47 | /** |
| 48 | * @brief fstream or ofstream which creates files with mkstemp |
| 49 | * |
| 50 | * This class becomes an std::ostream or std::stream depending on the |
| 51 | * @ref Device used. |
| 52 | * For the most common cases, use the provided typedefs @ref tmpofstream |
| 53 | * and @ref tmpfstream. |
| 54 | * The temporary file is created with permissions 0600. These can be changed |
| 55 | * with @ref set_file_mode. |
| 56 | * |
| 57 | * @param Device Class which implements the Device Concept of boost::iostreams |
| 58 | * and offers a function Open(filedescriptor,close_on_exit). Usually |
| 59 | * this is boost::iostreams::file_descriptor and |
| 60 | * boost::iostreams::file_descriptor_sink |
| 61 | * @param Tr Character traits type |
| 62 | * @param Alloc Allocator for the character buffers |
| 63 | */ |
| 64 | template< typename Device, |
| 65 | typename Tr = |
| 66 | BOOST_IOSTREAMS_CHAR_TRAITS( |
| 67 | BOOST_DEDUCED_TYPENAME bio::char_type_of<Device>::type |
| 68 | ), |
| 69 | typename Alloc = |
| 70 | std::allocator< |
| 71 | BOOST_DEDUCED_TYPENAME bio::char_type_of<Device>::type |
| 72 | > > |
| 73 | class tmpfstreamTempl : public bio::stream<Device, Tr, Alloc> |
| 74 | { |
| 75 | private: |
| 76 | std::string tmpfilename; |
| 77 | bool unlinked; |
| 78 | int fd; |
| 79 | |
| 80 | public: |
| 81 | /** |
| 82 | * @brief Constructs an unopened tmpfstreamTempl. |
| 83 | */ |
| 84 | tmpfstreamTempl() |
| 85 | : bio::stream<Device, Tr, Alloc>() |
| 86 | , unlinked(false) |
| 87 | , fd(-1) |
| 88 | { } |
| 89 | |
| 90 | /** |
| 91 | * @brief Constructs and opens the tmpfstreamTempl. |
| 92 | * |
| 93 | * @param tmpnametemplate Path of the temporary file to be opened. |
| 94 | * The last 6 characters must be XXXXXX and they |
| 95 | * will be replaced with something random. |
| 96 | * @param mode std::ios_base::openmode to open the file with |
| 97 | * @param buffer_size The size of any buffers that need to be allocated |
| 98 | * @param buffer_size The size of the putback buffer, relevant only for fstream |
| 99 | */ |
| 100 | tmpfstreamTempl(const std::string& tmpnametemplate, |
| 101 | std::ios_base::openmode mode = std::ios_base::out, |
| 102 | int buffer_size = -1 , int pback_size = -1) |
| 103 | : bio::stream<Device, Tr, Alloc>() |
| 104 | , unlinked(false) |
| 105 | , fd(-1) |
| 106 | { |
| 107 | open(tmpnametemplate,mode,buffer_size,pback_size); |
| 108 | } |
| 109 | |
| 110 | bool open(const std::string& tmpnametemplate, |
| 111 | std::ios_base::openmode mode = std::ios_base::out, |
| 112 | int buffer_size = -1 , int pback_size = -1); |
| 113 | |
| 114 | /** |
| 115 | * @brief Get the filename of the temporary file. |
| 116 | * |
| 117 | * @retval returns the filename really used with the XXXXXX replaced. |
| 118 | */ |
| 119 | std::string get_tmp_filename() |
| 120 | { return tmpfilename; } |
| 121 | |
| 122 | bool set_file_mode(mode_t mode); |
| 123 | |
| 124 | bool set_owner(const I2n::User& user, const I2n::Group& group= I2n::Group()); |
| 125 | |
| 126 | bool unlink(); |
| 127 | |
| 128 | bool is_unlinked() |
| 129 | { return unlinked; } |
| 130 | |
| 131 | bool move(const std::string& targetpath, bool overwrite=false); |
| 132 | |
| 133 | bool sync(); |
| 134 | }; |
| 135 | |
| 136 | |
| 137 | /** |
| 138 | * @brief ofstream which creates files with mkstemp |
| 139 | */ |
| 140 | typedef tmpfstreamTempl<bio::file_descriptor_sink> tmpofstream; |
| 141 | /** |
| 142 | * @brief fstream which creates files with mkstemp |
| 143 | */ |
| 144 | typedef tmpfstreamTempl<bio::file_descriptor> tmpfstream; |
| 145 | |
| 146 | /** |
| 147 | * @brief fstream or ofstream which first writes to a temp file and atomically |
| 148 | * replaces an old file on close. |
| 149 | * |
| 150 | * This class becomes an std::ostream or std::stream depending on the |
| 151 | * @ref Device used. |
| 152 | * For the most common cases, use the provided typedefs @ref tmpofcopystream |
| 153 | * and @ref tmpfcopystream. |
| 154 | * The temporary file is created with permissions 0600. On @ref close the |
| 155 | * permissions of the original file will be set to 0644 by default. |
| 156 | * |
| 157 | * @param Device Class which implements the Device Concept of boost::iostreams |
| 158 | * and offers a function Open(filedescriptor,close_on_exit). Usually |
| 159 | * this is boost::iostreams::file_descriptor and |
| 160 | * boost::iostreams::file_descriptor_sink |
| 161 | * @param Tr Character traits type |
| 162 | * @param Alloc Allocator for the character buffers |
| 163 | */ |
| 164 | template< typename Device, |
| 165 | typename Tr = |
| 166 | BOOST_IOSTREAMS_CHAR_TRAITS( |
| 167 | BOOST_DEDUCED_TYPENAME bio::char_type_of<Device>::type |
| 168 | ), |
| 169 | typename Alloc = |
| 170 | std::allocator< |
| 171 | BOOST_DEDUCED_TYPENAME bio::char_type_of<Device>::type |
| 172 | > > |
| 173 | class tmpfcopystreamTempl : public tmpfstreamTempl<Device, Tr, Alloc> |
| 174 | { |
| 175 | private: |
| 176 | std::string originalfilename; |
| 177 | bool full_sync; |
| 178 | mode_t filemode_on_close; |
| 179 | I2n::User UserOnClose; |
| 180 | I2n::Group GroupOnClose; |
| 181 | |
| 182 | public: |
| 183 | static const std::string default_template_suffix; |
| 184 | static const bool default_full_sync=false; |
| 185 | static const int default_filemode_on_close=0644; |
| 186 | |
| 187 | /** |
| 188 | * @brief Constructs an unopened tmpfcopystreamTempl. |
| 189 | */ |
| 190 | tmpfcopystreamTempl() |
| 191 | : tmpfstreamTempl<Device, Tr, Alloc>() |
| 192 | , full_sync(default_full_sync) |
| 193 | , filemode_on_close(default_filemode_on_close) |
| 194 | , UserOnClose() |
| 195 | , GroupOnClose() |
| 196 | { } |
| 197 | |
| 198 | /** |
| 199 | * @brief Constructs and opens the tmpfcopystreamTempl. |
| 200 | * |
| 201 | * The temporary file will be in the same directory with .tmp.XXXXXX appended. |
| 202 | * |
| 203 | * @param filename The original filename which will be replaced on close |
| 204 | * @param mode std::ios_base::openmode to open the file with |
| 205 | * @param buffer_size The size of any buffers that need to be allocated |
| 206 | * @param buffer_size The size of the putback buffer, relevant only for fstream |
| 207 | */ |
| 208 | tmpfcopystreamTempl(const std::string& filename, |
| 209 | std::ios_base::openmode mode = std::ios_base::out, |
| 210 | int buffer_size = -1 , int pback_size = -1) |
| 211 | : tmpfstreamTempl<Device, Tr, Alloc>(filename+default_template_suffix,mode,buffer_size,pback_size) |
| 212 | , originalfilename(filename) |
| 213 | , full_sync(default_full_sync) |
| 214 | , filemode_on_close(default_filemode_on_close) |
| 215 | , UserOnClose() |
| 216 | , GroupOnClose() |
| 217 | { } |
| 218 | |
| 219 | /** |
| 220 | * @brief Constructs and opens the tmpfcopystreamTempl. |
| 221 | * |
| 222 | * filename and tmpnametemplate must be on the same filesystem. |
| 223 | * |
| 224 | * @param filename The original filename which will be replaced on close |
| 225 | * @param tmpnametemplate Path of the temporary file to be opened. |
| 226 | * The last 6 characters must be XXXXXX and they |
| 227 | * will be replaced with something random. |
| 228 | * @param mode std::ios_base::openmode to open the file with |
| 229 | * @param buffer_size The size of any buffers that need to be allocated |
| 230 | * @param buffer_size The size of the putback buffer, relevant only for fstream |
| 231 | */ |
| 232 | tmpfcopystreamTempl(const std::string& filename, |
| 233 | const std::string& tmpnametemplate, |
| 234 | std::ios_base::openmode mode = std::ios_base::out, |
| 235 | int buffer_size = -1 , int pback_size = -1) |
| 236 | : tmpfstreamTempl<Device, Tr, Alloc>(tmpnametemplate,mode,buffer_size,pback_size) |
| 237 | , originalfilename(filename) |
| 238 | , full_sync(default_full_sync) |
| 239 | , filemode_on_close(default_filemode_on_close) |
| 240 | , UserOnClose() |
| 241 | , GroupOnClose() |
| 242 | { } |
| 243 | |
| 244 | ~tmpfcopystreamTempl() |
| 245 | { close(); } |
| 246 | |
| 247 | /** |
| 248 | * @brief Opens the tmpfcopystreamTempl. |
| 249 | * |
| 250 | * The temporary file will be in the same directory with .tmp.XXXXXX appended. |
| 251 | * |
| 252 | * @param filename The original filename which will be replaced on close |
| 253 | * @param mode std::ios_base::openmode to open the file with |
| 254 | * @param buffer_size The size of any buffers that need to be allocated |
| 255 | * @param buffer_size The size of the putback buffer, relevant only for fstream |
| 256 | * @retval true if successful |
| 257 | */ |
| 258 | bool open(const std::string& filename, |
| 259 | std::ios_base::openmode mode = std::ios_base::out, |
| 260 | int buffer_size = -1 , int pback_size = -1) |
| 261 | { |
| 262 | originalfilename=filename; |
| 263 | return tmpfstreamTempl<Device, Tr, Alloc> |
| 264 | ::open(filename+default_template_suffix,mode,buffer_size,pback_size); |
| 265 | } |
| 266 | |
| 267 | /** |
| 268 | * @brief Opens the tmpfcopystreamTempl. |
| 269 | * |
| 270 | * filename and tmpnametemplate must be on the same filesystem. |
| 271 | * |
| 272 | * @param filename The original filename which will be replaced on close |
| 273 | * @param tmpnametemplate Path of the temporary file to be opened. |
| 274 | * The last 6 characters must be XXXXXX and they |
| 275 | * will be replaced with something random. |
| 276 | * @param mode std::ios_base::openmode to open the file with |
| 277 | * @param buffer_size The size of any buffers that need to be allocated |
| 278 | * @param buffer_size The size of the putback buffer, relevant only for fstream |
| 279 | * @retval true if successful |
| 280 | */ |
| 281 | bool open(const std::string& filename, |
| 282 | const std::string& tmpnametemplate, |
| 283 | std::ios_base::openmode mode = std::ios_base::out, |
| 284 | int buffer_size = -1 , int pback_size = -1) |
| 285 | { |
| 286 | originalfilename=filename; |
| 287 | return tmpfstreamTempl<Device, Tr, Alloc> |
| 288 | ::open(tmpnametemplate,mode,buffer_size,pback_size); |
| 289 | } |
| 290 | |
| 291 | void close(); |
| 292 | |
| 293 | /** |
| 294 | * @brief Returns the name of the original file that will be replaced on @ref close. |
| 295 | * @retval name of the original file |
| 296 | */ |
| 297 | std::string get_original_filename() |
| 298 | { return originalfilename; } |
| 299 | |
| 300 | /** |
| 301 | * @brief Sets the full sync mode |
| 302 | * @param do_full_sync if true the file data and metadata will be fully synced |
| 303 | * to disk on close |
| 304 | */ |
| 305 | void set_full_sync(bool do_full_sync=true) |
| 306 | { full_sync=do_full_sync; } |
| 307 | |
| 308 | /** |
| 309 | * @brief Read the current status of the full sync mode |
| 310 | * @retval true if the full sync mode is active |
| 311 | */ |
| 312 | bool get_full_sync() |
| 313 | { return full_sync; } |
| 314 | |
| 315 | /** |
| 316 | * @brief On @ref close the target file is assigned these permissions |
| 317 | */ |
| 318 | void set_filemode_on_close(mode_t mode) |
| 319 | { filemode_on_close=mode; } |
| 320 | |
| 321 | /** |
| 322 | * @brief Read the permissions that will be set on @ref close |
| 323 | * @retval permissions that will be set on @ref close |
| 324 | */ |
| 325 | mode_t get_filemode_on_close() |
| 326 | { return filemode_on_close; } |
| 327 | |
| 328 | /** |
| 329 | * @brief Change file owner (chown) that will be set on @ref close |
| 330 | * @param user the new owner |
| 331 | * @param group the new group, if left empty the main group of the user is set |
| 332 | */ |
| 333 | void set_owner_on_close(const I2n::User& user, const I2n::Group& group= I2n::Group()) |
| 334 | { |
| 335 | UserOnClose=user; |
| 336 | GroupOnClose=group; |
| 337 | } |
| 338 | |
| 339 | /** |
| 340 | * @brief Get file owner that will be set on @ref close |
| 341 | * @retval user that will be set on @ref close, an empty @ref User() means nothing will be set |
| 342 | */ |
| 343 | I2n::User get_owner_on_close() |
| 344 | { return UserOnClose; } |
| 345 | |
| 346 | /** |
| 347 | * @brief Get file owner group that will be set on @ref close |
| 348 | * @retval group that will be set on @ref close |
| 349 | */ |
| 350 | I2n::Group get_group_on_close() |
| 351 | { return GroupOnClose; } |
| 352 | |
| 353 | /** |
| 354 | * @brief Delete the file. |
| 355 | * |
| 356 | * calling unlink is a safe way to abort, |
| 357 | * the original file is not overwritten then |
| 358 | * |
| 359 | * @retval true if successful |
| 360 | */ |
| 361 | bool unlink() |
| 362 | { return tmpfstreamTempl<Device, Tr, Alloc>::unlink(); } |
| 363 | |
| 364 | private: |
| 365 | |
| 366 | /** |
| 367 | * @brief Not allowed to be called by users. |
| 368 | */ |
| 369 | bool move(const std::string& targetpath, bool overwrite=false) |
| 370 | { return tmpfstreamTempl<Device, Tr, Alloc>::move(targetpath,overwrite); } |
| 371 | }; |
| 372 | |
| 373 | /** |
| 374 | * @brief ofstream which first writes to a temp file and atomically replaces an old file on close. |
| 375 | */ |
| 376 | typedef tmpfcopystreamTempl<bio::file_descriptor_sink> tmpofcopystream; |
| 377 | /** |
| 378 | * @brief fstream which first writes to a temp file and atomically replaces an old file on close. |
| 379 | */ |
| 380 | typedef tmpfcopystreamTempl<bio::file_descriptor> tmpfcopystream; |
| 381 | |
| 382 | } |
| 383 | |
| 384 | #endif |