Switch license from Intranator license to GPLv2 + linking exception (ACKed by Steffen)
[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         : unlinked(false), fd(-1), bio::stream<Device, Tr, Alloc>()
86         { }
87
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     */
98     tmpfstreamTempl(const std::string& tmpnametemplate, 
99                std::ios_base::open_mode mode = std::ios_base::out,
100                int buffer_size = -1 , int pback_size = -1)
101         : unlinked(false), fd(-1), bio::stream<Device, Tr, Alloc>()
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,
108                int buffer_size = -1 , int pback_size = -1);
109
110     /**
111     * @brief Get the filename of the temporary file.
112     *
113     * @retval returns the filename really used with the XXXXXX replaced.
114     */
115     std::string get_tmp_filename()
116         { return tmpfilename; }
117
118     bool set_file_mode(mode_t mode);
119
120     bool set_owner(const I2n::User& user, const I2n::Group& group= I2n::Group());
121
122     bool unlink();
123
124     bool is_unlinked()
125         { return unlinked; }
126
127     bool move(const std::string& targetpath, bool overwrite=false);
128
129     bool sync();
130 };
131
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  */
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;
174     mode_t filemode_on_close;
175     I2n::User UserOnClose;
176     I2n::Group GroupOnClose;
177
178 public:
179     static const std::string default_template_suffix;
180     static const bool default_full_sync=false;
181     static const int default_filemode_on_close=0644;
182
183     /**
184     * @brief Constructs an unopened tmpfcopystreamTempl.
185     */
186     tmpfcopystreamTempl()
187         : full_sync(default_full_sync),
188           filemode_on_close(default_filemode_on_close),
189           UserOnClose(), GroupOnClose(),
190           tmpfstreamTempl<Device, Tr, Alloc>()
191         { }
192
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     */
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)
206         : full_sync(default_full_sync),
207           filemode_on_close(default_filemode_on_close),
208           UserOnClose(), GroupOnClose(),
209           originalfilename(filename),
210           tmpfstreamTempl<Device, Tr, Alloc>
211             (filename+default_template_suffix,mode,buffer_size,pback_size)
212         { }
213
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     */
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)
231         : full_sync(default_full_sync),
232           filemode_on_close(default_filemode_on_close),
233           UserOnClose(), GroupOnClose(),
234           originalfilename(filename), 
235           tmpfstreamTempl<Device, Tr, Alloc>
236             (tmpnametemplate,mode,buffer_size,pback_size)
237         { }
238
239     ~tmpfcopystreamTempl()
240         { close(); }
241
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     */
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
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     */
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
286     void close();
287
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     */
292     std::string get_original_filename()
293         { return originalfilename; }
294
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     */
300     void set_full_sync(bool do_full_sync=true)
301         { full_sync=do_full_sync; }
302
303     /**
304     * @brief Read the current status of the full sync mode
305     * @retval true if the full sync mode is active
306     */
307     bool get_full_sync()
308         { return full_sync; }
309
310     /**
311     * @brief On @ref close the target file is assigned these permissions
312     */
313     void set_filemode_on_close(mode_t mode)
314         { filemode_on_close=mode; }
315
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()
321         { return filemode_on_close; }
322
323     /**
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     /**
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(); }
358
359 private:
360
361     /**
362     * @brief Not allowed to be called by users.
363     */
364     bool move(const std::string& targetpath, bool overwrite=false)
365         { return tmpfstreamTempl<Device, Tr, Alloc>::move(targetpath,overwrite); }
366 };
367
368 /**
369  * @brief ofstream which first writes to a temp file and atomically replaces an old file on close.
370  */
371 typedef tmpfcopystreamTempl<bio::file_descriptor_sink> tmpofcopystream;
372 /**
373  * @brief fstream which first writes to a temp file and atomically replaces an old file on close.
374  */
375 typedef tmpfcopystreamTempl<bio::file_descriptor> tmpfcopystream;
376
377 }
378
379 #endif