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