Merge branch 'daemon-ext'
[libi2ncommon] / src / tmpfstream_impl.hpp
CommitLineData
0e23f538
TJ
1/*
2The software in this package is distributed under the GNU General
3Public License version 2 (with a special exception described below).
4
5A copy of GNU General Public License (GPL) is included in this distribution,
6in the file COPYING.GPL.
7
8As a special exception, if other files instantiate templates or use macros
9or inline functions from this file, or you compile this file and link it
10with other works to produce a work based on this file, this file
11does not by itself cause the resulting work to be covered
12by the GNU General Public License.
13
14However the source code for this file must still be made available
15in accordance with section (3) of the GNU General Public License.
16
17This exception does not invalidate any other reasons why a work based
18on this file might be covered by the GNU General Public License.
19*/
82b461e4
GE
20/** @file
21 * @brief fstream which creates files with mkstemp.
22 *
23 * @note This file contains the template implementation. You only need
24 * to include it if you use something else than the explicitly
25 * instantiated and typedefed versions
26 *
27 * @author Gerd v. Egidy
28 *
29 * @copyright © Copyright 2010 by Intra2net AG
82b461e4
GE
30 */
31
32#ifndef __I2N_TMPFSTREAM_IMPL_HPP__
33#define __I2N_TMPFSTREAM_IMPL_HPP__
34
35#include <string>
36#include <stdio.h>
fb2b9fa7
GE
37
38#include <sys/types.h>
39#include <sys/stat.h>
82b461e4
GE
40#include <unistd.h>
41#include <errno.h>
42#include <stdlib.h>
fb2b9fa7 43#include <fcntl.h>
82b461e4
GE
44
45#include <tmpfstream.hpp>
46#include <filefunc.hxx>
47
48
49namespace I2n
50{
51
69d8da4a
GE
52/**
53* @brief opens the tmpfstreamTempl.
54*
55* @param tmpnametemplate Path of the temporary file to be opened.
56* The last 6 characters must be XXXXXX and they
57* will be replaced with something random.
58* @param mode std::ios_base::open_mode to open the file with
59* @param buffer_size The size of any buffers that need to be allocated
60* @param buffer_size The size of the putback buffer, relevant only for fstream
61* @retval true if successful
62*/
82b461e4
GE
63template< typename Device, typename Tr, typename Alloc >
64bool tmpfstreamTempl<Device,Tr,Alloc>::open(const std::string& tmpnametemplate,
65 std::ios_base::open_mode mode,
66 int buffer_size, int pback_size)
67{
68 if (tmpfstreamTempl<Device,Tr,Alloc>::is_open())
69 tmpfstreamTempl<Device,Tr,Alloc>::close();
70
71 char* chbuf=new char[tmpnametemplate.size()+1];
72 tmpnametemplate.copy(chbuf,tmpnametemplate.size()+1);
73 chbuf[tmpnametemplate.size()]=0;
74
fb2b9fa7
GE
75 // always assume out-mode, otherwise the tmpfstream would be useless
76 int flags=0;
77 if (mode & std::ios_base::in)
78 flags |= O_RDWR;
79 else
80 flags |= O_WRONLY;
81
82 if (mode & std::ios_base::app)
83 flags |= O_APPEND;
82b461e4 84
f002679a 85 fd=mkostemp(chbuf,flags);
ff09d317 86 tmpfilename=chbuf;
82b461e4
GE
87 delete[] chbuf;
88
89 if (fd==-1)
90 return false;
91
ee8c8e76 92 boost::iostreams::stream<Device,Tr,Alloc>::open(Device(fd, boost::iostreams::close_handle),
69d8da4a 93 buffer_size,pback_size);
82b461e4
GE
94
95 return tmpfstreamTempl<Device,Tr,Alloc>::is_open();
96}
97
69d8da4a
GE
98/**
99* @brief Changes permissions (chmod) of the file.
100*
101* @param mode the new mode as in chmod
ef2a6062 102* @retval true if successful
69d8da4a 103*/
82b461e4 104template< typename Device, typename Tr, typename Alloc >
69d8da4a 105bool tmpfstreamTempl<Device,Tr,Alloc>::set_file_mode(mode_t mode)
82b461e4 106{
0fcb8655 107 if (!get_tmp_filename().empty() && !is_unlinked())
c79b872c
GE
108 return I2n::chmod(get_tmp_filename(),mode);
109 else
110 return false;
82b461e4
GE
111}
112
69d8da4a 113/**
ef2a6062
GE
114* @brief Changes the owner of the file (chown).
115*
116* @param user the new owner
117* @param group the new group, if left empty the main group of the user is set
118* @retval true if successful
119*/
120template< typename Device, typename Tr, typename Alloc >
121bool tmpfstreamTempl<Device,Tr,Alloc>::set_owner(const I2n::User& user, const I2n::Group& group)
122{
123 if (!get_tmp_filename().empty() && !is_unlinked())
124 return I2n::chown(get_tmp_filename(),user,group);
125 else
126 return false;
127}
128
129/**
69d8da4a
GE
130* @brief Delete the file.
131*
132* Can be called while the file is still open.
133*
134* @retval true if successful
135*/
82b461e4
GE
136template< typename Device, typename Tr, typename Alloc >
137bool tmpfstreamTempl<Device,Tr,Alloc>::unlink()
138{
139 if (!get_tmp_filename().empty())
0fcb8655
GE
140 {
141 if (I2n::unlink(get_tmp_filename()))
142 {
143 unlinked=true;
144 return true;
145 }
146 else
147 return false;
148 }
82b461e4
GE
149 else
150 return false;
151}
152
69d8da4a
GE
153/**
154* @brief Move the file to another name or path.
155*
156* The temporary file and the target path must be on the same filesystem.
157* Afterwards all operations (e.g. @ref unlink) are on the new filename.
158*
159* @param targetpath name and path of the new filename
160* @param overwrite overwrite an already existing targetpath or not
161* @retval true if successful
162*/
82b461e4 163template< typename Device, typename Tr, typename Alloc >
c79b872c 164bool tmpfstreamTempl<Device,Tr,Alloc>::move(const std::string& targetpath,
82b461e4
GE
165 bool overwrite)
166{
0fcb8655 167 if (get_tmp_filename().empty() || is_unlinked())
c79b872c 168 return false;
82b461e4 169
7c4c2ece
GE
170 bool success=false;
171
c79b872c
GE
172 if (overwrite)
173 {
174 // this overwrites an already existing target without further warning
175 // other errors possible, see errno
7c4c2ece
GE
176 if (::rename( get_tmp_filename().c_str(), targetpath.c_str() ) == 0)
177 success=true;
c79b872c
GE
178 }
179 else
180 {
181 // fails if the target already exists
182 // other errors possible, see errno
7c4c2ece
GE
183 if (::link( get_tmp_filename().c_str(), targetpath.c_str() ) == 0)
184 {
185 success=true;
186 ::unlink(get_tmp_filename().c_str());
187 }
188 }
189
190 if (success)
191 {
192 // store tmpfilename to allow double-move
193 tmpfilename=targetpath;
c79b872c 194 }
4635af6b 195
7c4c2ece 196 return success;
82b461e4
GE
197}
198
69d8da4a
GE
199/**
200* @brief Sync the data and metadata of the file to disk.
201*
202* @retval true if successful
203*/
ff09d317 204template< typename Device, typename Tr, typename Alloc >
f002679a
GE
205bool tmpfstreamTempl<Device,Tr,Alloc>::sync()
206{
207 if (!tmpfstreamTempl<Device,Tr,Alloc>::is_open())
208 return false;
209
210 tmpfstreamTempl<Device,Tr,Alloc>::flush();
211
212 // sync the file itself
213 if (fsync(fd) != 0)
214 return false;
215
216 // sync the dir: both is needed for data + metadata sync
217 return dirsync(dirname(get_tmp_filename()));
218}
219
69d8da4a
GE
220/**
221* @brief Close the stream and atomically overwrite an existing original file.
222*/
f002679a 223template< typename Device, typename Tr, typename Alloc >
ff09d317
GE
224void tmpfcopystreamTempl<Device,Tr,Alloc>::close()
225{
a8cbce97
GE
226 if (!tmpfstreamTempl<Device,Tr,Alloc>::is_open() ||
227 tmpfstreamTempl<Device,Tr,Alloc>::is_unlinked())
ccee68fd
GE
228 return;
229
f002679a
GE
230 if (get_full_sync())
231 tmpfstreamTempl<Device,Tr,Alloc>::sync();
ccee68fd 232
ccee68fd
GE
233 // close the underlying filedescriptor
234 tmpfstreamTempl<Device,Tr,Alloc>::close();
235
a8cbce97
GE
236 tmpfstreamTempl<Device,Tr,Alloc>::set_file_mode(filemode_on_close);
237
ef2a6062
GE
238 if (UserOnClose != I2n::User())
239 tmpfstreamTempl<Device,Tr,Alloc>::set_owner(UserOnClose,GroupOnClose);
240
ccee68fd 241 move(get_original_filename(),true);
f002679a
GE
242
243 if (get_full_sync())
244 dirsync(dirname(get_original_filename()));
ff09d317
GE
245}
246
82b461e4
GE
247}
248
249#endif