Increase version to 2.9
[libi2ncommon] / src / tmpfstream.hpp
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::open_mode 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::open_mode 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::open_mode 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::open_mode 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::open_mode 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::open_mode 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::open_mode 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::open_mode 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::open_mode 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::open_mode 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::open_mode 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