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 | */ | |
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 | ||
49 | namespace 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 |
63 | template< typename Device, typename Tr, typename Alloc > |
64 | bool 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 | 104 | template< typename Device, typename Tr, typename Alloc > |
69d8da4a | 105 | bool 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 | */ | |
120 | template< typename Device, typename Tr, typename Alloc > | |
121 | bool 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 |
136 | template< typename Device, typename Tr, typename Alloc > |
137 | bool 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 | 163 | template< typename Device, typename Tr, typename Alloc > |
c79b872c | 164 | bool 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 | 204 | template< typename Device, typename Tr, typename Alloc > |
f002679a GE |
205 | bool 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 | 223 | template< typename Device, typename Tr, typename Alloc > |
ff09d317 GE |
224 | void 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 |