/* The software in this package is distributed under the GNU General Public License version 2 (with a special exception described below). A copy of GNU General Public License (GPL) is included in this distribution, in the file COPYING.GPL. As a special exception, if other files instantiate templates or use macros or inline functions from this file, or you compile this file and link it with other works to produce a work based on this file, this file does not by itself cause the resulting work to be covered by the GNU General Public License. However the source code for this file must still be made available in accordance with section (3) of the GNU General Public License. This exception does not invalidate any other reasons why a work based on this file might be covered by the GNU General Public License. */ /** @file * @brief fstream which creates files with mkstemp. * * @note This file contains the template implementation. You only need * to include it if you use something else than the explicitly * instantiated and typedefed versions * * @author Gerd v. Egidy * * @copyright © Copyright 2010 by Intra2net AG */ #ifndef __I2N_TMPFSTREAM_IMPL_HPP__ #define __I2N_TMPFSTREAM_IMPL_HPP__ #include #include #include #include #include #include #include #include #include #include namespace I2n { /** * @brief opens the tmpfstreamTempl. * * @param tmpnametemplate Path of the temporary file to be opened. * The last 6 characters must be XXXXXX and they * will be replaced with something random. * @param mode std::ios_base::open_mode to open the file with * @param buffer_size The size of any buffers that need to be allocated * @param buffer_size The size of the putback buffer, relevant only for fstream * @retval true if successful */ template< typename Device, typename Tr, typename Alloc > bool tmpfstreamTempl::open(const std::string& tmpnametemplate, std::ios_base::open_mode mode, int buffer_size, int pback_size) { if (tmpfstreamTempl::is_open()) tmpfstreamTempl::close(); char* chbuf=new char[tmpnametemplate.size()+1]; tmpnametemplate.copy(chbuf,tmpnametemplate.size()+1); chbuf[tmpnametemplate.size()]=0; // always assume out-mode, otherwise the tmpfstream would be useless int flags=0; if (mode & std::ios_base::in) flags |= O_RDWR; else flags |= O_WRONLY; if (mode & std::ios_base::app) flags |= O_APPEND; fd=mkostemp(chbuf,flags); tmpfilename=chbuf; delete[] chbuf; if (fd==-1) return false; boost::iostreams::stream::open(Device(fd, boost::iostreams::close_handle), buffer_size,pback_size); return tmpfstreamTempl::is_open(); } /** * @brief Changes permissions (chmod) of the file. * * @param mode the new mode as in chmod * @retval true if successful */ template< typename Device, typename Tr, typename Alloc > bool tmpfstreamTempl::set_file_mode(mode_t mode) { if (!get_tmp_filename().empty() && !is_unlinked()) return I2n::chmod(get_tmp_filename(),mode); else return false; } /** * @brief Changes the owner of the file (chown). * * @param user the new owner * @param group the new group, if left empty the main group of the user is set * @retval true if successful */ template< typename Device, typename Tr, typename Alloc > bool tmpfstreamTempl::set_owner(const I2n::User& user, const I2n::Group& group) { if (!get_tmp_filename().empty() && !is_unlinked()) return I2n::chown(get_tmp_filename(),user,group); else return false; } /** * @brief Delete the file. * * Can be called while the file is still open. * * @retval true if successful */ template< typename Device, typename Tr, typename Alloc > bool tmpfstreamTempl::unlink() { if (!get_tmp_filename().empty()) { if (I2n::unlink(get_tmp_filename())) { unlinked=true; return true; } else return false; } else return false; } /** * @brief Move the file to another name or path. * * The temporary file and the target path must be on the same filesystem. * Afterwards all operations (e.g. @ref unlink) are on the new filename. * * @param targetpath name and path of the new filename * @param overwrite overwrite an already existing targetpath or not * @retval true if successful */ template< typename Device, typename Tr, typename Alloc > bool tmpfstreamTempl::move(const std::string& targetpath, bool overwrite) { if (get_tmp_filename().empty() || is_unlinked()) return false; bool success=false; if (overwrite) { // this overwrites an already existing target without further warning // other errors possible, see errno if (::rename( get_tmp_filename().c_str(), targetpath.c_str() ) == 0) success=true; } else { // fails if the target already exists // other errors possible, see errno if (::link( get_tmp_filename().c_str(), targetpath.c_str() ) == 0) { success=true; ::unlink(get_tmp_filename().c_str()); } } if (success) { // store tmpfilename to allow double-move tmpfilename=targetpath; } return success; } /** * @brief Sync the data and metadata of the file to disk. * * @retval true if successful */ template< typename Device, typename Tr, typename Alloc > bool tmpfstreamTempl::sync() { if (!tmpfstreamTempl::is_open()) return false; tmpfstreamTempl::flush(); // sync the file itself if (fsync(fd) != 0) return false; // sync the dir: both is needed for data + metadata sync return dirsync(dirname(get_tmp_filename())); } /** * @brief Close the stream and atomically overwrite an existing original file. */ template< typename Device, typename Tr, typename Alloc > void tmpfcopystreamTempl::close() { if (!tmpfstreamTempl::is_open() || tmpfstreamTempl::is_unlinked()) return; if (get_full_sync()) tmpfstreamTempl::sync(); // close the underlying filedescriptor tmpfstreamTempl::close(); tmpfstreamTempl::set_file_mode(filemode_on_close); if (UserOnClose != I2n::User()) tmpfstreamTempl::set_owner(UserOnClose,GroupOnClose); move(get_original_filename(),true); if (get_full_sync()) dirsync(dirname(get_original_filename())); } } #endif