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