Replace inet_aton() with inet_pton() to parse IPs correctly (#8825)
[libi2ncommon] / src / tmpfstream_impl.hpp
... / ...
CommitLineData
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*/
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
30 */
31
32#ifndef __I2N_TMPFSTREAM_IMPL_HPP__
33#define __I2N_TMPFSTREAM_IMPL_HPP__
34
35#include <string>
36#include <stdio.h>
37
38#include <sys/types.h>
39#include <sys/stat.h>
40#include <unistd.h>
41#include <errno.h>
42#include <stdlib.h>
43#include <fcntl.h>
44
45#include <tmpfstream.hpp>
46#include <filefunc.hxx>
47
48
49namespace I2n
50{
51
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::openmode 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*/
63template< typename Device, typename Tr, typename Alloc >
64bool tmpfstreamTempl<Device,Tr,Alloc>::open(const std::string& tmpnametemplate,
65 std::ios_base::openmode 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
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;
84
85 fd=mkostemp(chbuf,flags);
86 tmpfilename=chbuf;
87 delete[] chbuf;
88
89 if (fd==-1)
90 return false;
91
92 boost::iostreams::stream<Device,Tr,Alloc>::open(Device(fd, boost::iostreams::close_handle),
93 buffer_size,pback_size);
94
95 return tmpfstreamTempl<Device,Tr,Alloc>::is_open();
96}
97
98/**
99* @brief Changes permissions (chmod) of the file.
100*
101* @param mode the new mode as in chmod
102* @retval true if successful
103*/
104template< typename Device, typename Tr, typename Alloc >
105bool tmpfstreamTempl<Device,Tr,Alloc>::set_file_mode(mode_t mode)
106{
107 if (!get_tmp_filename().empty() && !is_unlinked())
108 return I2n::chmod(get_tmp_filename(),mode);
109 else
110 return false;
111}
112
113/**
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/**
130* @brief Delete the file.
131*
132* Can be called while the file is still open.
133*
134* @retval true if successful
135*/
136template< typename Device, typename Tr, typename Alloc >
137bool tmpfstreamTempl<Device,Tr,Alloc>::unlink()
138{
139 if (!get_tmp_filename().empty())
140 {
141 if (I2n::unlink(get_tmp_filename()))
142 {
143 unlinked=true;
144 return true;
145 }
146 else
147 return false;
148 }
149 else
150 return false;
151}
152
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*/
163template< typename Device, typename Tr, typename Alloc >
164bool tmpfstreamTempl<Device,Tr,Alloc>::move(const std::string& targetpath,
165 bool overwrite)
166{
167 if (get_tmp_filename().empty() || is_unlinked())
168 return false;
169
170 bool success=false;
171
172 if (overwrite)
173 {
174 // this overwrites an already existing target without further warning
175 // other errors possible, see errno
176 if (::rename( get_tmp_filename().c_str(), targetpath.c_str() ) == 0)
177 success=true;
178 }
179 else
180 {
181 // fails if the target already exists
182 // other errors possible, see errno
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;
194 }
195
196 return success;
197}
198
199/**
200* @brief Sync the data and metadata of the file to disk.
201*
202* @retval true if successful
203*/
204template< typename Device, typename Tr, typename Alloc >
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
220/**
221* @brief Close the stream and atomically overwrite an existing original file.
222*/
223template< typename Device, typename Tr, typename Alloc >
224void tmpfcopystreamTempl<Device,Tr,Alloc>::close()
225{
226 if (!tmpfstreamTempl<Device,Tr,Alloc>::is_open() ||
227 tmpfstreamTempl<Device,Tr,Alloc>::is_unlinked())
228 return;
229
230 if (get_full_sync())
231 tmpfstreamTempl<Device,Tr,Alloc>::sync();
232
233 // close the underlying filedescriptor
234 tmpfstreamTempl<Device,Tr,Alloc>::close();
235
236 tmpfstreamTempl<Device,Tr,Alloc>::set_file_mode(filemode_on_close);
237
238 if (UserOnClose != I2n::User())
239 tmpfstreamTempl<Device,Tr,Alloc>::set_owner(UserOnClose,GroupOnClose);
240
241 move(get_original_filename(),true);
242
243 if (get_full_sync())
244 dirsync(dirname(get_original_filename()));
245}
246
247}
248
249#endif