/* 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. * * @author Gerd v. Egidy * * @copyright © Copyright 2010 by Intra2net AG */ #ifndef __I2N_TMPFSTREAM_HPP__ #define __I2N_TMPFSTREAM_HPP__ #include #include #include #include #include #include #include namespace I2n { namespace bio = boost::iostreams; /** * @brief fstream or ofstream which creates files with mkstemp * * This class becomes an std::ostream or std::stream depending on the * @ref Device used. * For the most common cases, use the provided typedefs @ref tmpofstream * and @ref tmpfstream. * The temporary file is created with permissions 0600. These can be changed * with @ref set_file_mode. * * @param Device Class which implements the Device Concept of boost::iostreams * and offers a function Open(filedescriptor,close_on_exit). Usually * this is boost::iostreams::file_descriptor and * boost::iostreams::file_descriptor_sink * @param Tr Character traits type * @param Alloc Allocator for the character buffers */ template< typename Device, typename Tr = BOOST_IOSTREAMS_CHAR_TRAITS( BOOST_DEDUCED_TYPENAME bio::char_type_of::type ), typename Alloc = std::allocator< BOOST_DEDUCED_TYPENAME bio::char_type_of::type > > class tmpfstreamTempl : public bio::stream { private: std::string tmpfilename; bool unlinked; int fd; public: /** * @brief Constructs an unopened tmpfstreamTempl. */ tmpfstreamTempl() : bio::stream() , unlinked(false) , fd(-1) { } /** * @brief Constructs and 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 */ tmpfstreamTempl(const std::string& tmpnametemplate, std::ios_base::open_mode mode = std::ios_base::out, int buffer_size = -1 , int pback_size = -1) : bio::stream() , unlinked(false) , fd(-1) { open(tmpnametemplate,mode,buffer_size,pback_size); } bool open(const std::string& tmpnametemplate, std::ios_base::open_mode mode = std::ios_base::out, int buffer_size = -1 , int pback_size = -1); /** * @brief Get the filename of the temporary file. * * @retval returns the filename really used with the XXXXXX replaced. */ std::string get_tmp_filename() { return tmpfilename; } bool set_file_mode(mode_t mode); bool set_owner(const I2n::User& user, const I2n::Group& group= I2n::Group()); bool unlink(); bool is_unlinked() { return unlinked; } bool move(const std::string& targetpath, bool overwrite=false); bool sync(); }; /** * @brief ofstream which creates files with mkstemp */ typedef tmpfstreamTempl tmpofstream; /** * @brief fstream which creates files with mkstemp */ typedef tmpfstreamTempl tmpfstream; /** * @brief fstream or ofstream which first writes to a temp file and atomically * replaces an old file on close. * * This class becomes an std::ostream or std::stream depending on the * @ref Device used. * For the most common cases, use the provided typedefs @ref tmpofcopystream * and @ref tmpfcopystream. * The temporary file is created with permissions 0600. On @ref close the * permissions of the original file will be set to 0644 by default. * * @param Device Class which implements the Device Concept of boost::iostreams * and offers a function Open(filedescriptor,close_on_exit). Usually * this is boost::iostreams::file_descriptor and * boost::iostreams::file_descriptor_sink * @param Tr Character traits type * @param Alloc Allocator for the character buffers */ template< typename Device, typename Tr = BOOST_IOSTREAMS_CHAR_TRAITS( BOOST_DEDUCED_TYPENAME bio::char_type_of::type ), typename Alloc = std::allocator< BOOST_DEDUCED_TYPENAME bio::char_type_of::type > > class tmpfcopystreamTempl : public tmpfstreamTempl { private: std::string originalfilename; bool full_sync; mode_t filemode_on_close; I2n::User UserOnClose; I2n::Group GroupOnClose; public: static const std::string default_template_suffix; static const bool default_full_sync=false; static const int default_filemode_on_close=0644; /** * @brief Constructs an unopened tmpfcopystreamTempl. */ tmpfcopystreamTempl() : tmpfstreamTempl() , full_sync(default_full_sync) , filemode_on_close(default_filemode_on_close) , UserOnClose() , GroupOnClose() { } /** * @brief Constructs and opens the tmpfcopystreamTempl. * * The temporary file will be in the same directory with .tmp.XXXXXX appended. * * @param filename The original filename which will be replaced on close * @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 */ tmpfcopystreamTempl(const std::string& filename, std::ios_base::open_mode mode = std::ios_base::out, int buffer_size = -1 , int pback_size = -1) : tmpfstreamTempl(filename+default_template_suffix,mode,buffer_size,pback_size) , originalfilename(filename) , full_sync(default_full_sync) , filemode_on_close(default_filemode_on_close) , UserOnClose() , GroupOnClose() { } /** * @brief Constructs and opens the tmpfcopystreamTempl. * * filename and tmpnametemplate must be on the same filesystem. * * @param filename The original filename which will be replaced on close * @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 */ tmpfcopystreamTempl(const std::string& filename, const std::string& tmpnametemplate, std::ios_base::open_mode mode = std::ios_base::out, int buffer_size = -1 , int pback_size = -1) : tmpfstreamTempl(tmpnametemplate,mode,buffer_size,pback_size) , originalfilename(filename) , full_sync(default_full_sync) , filemode_on_close(default_filemode_on_close) , UserOnClose() , GroupOnClose() { } ~tmpfcopystreamTempl() { close(); } /** * @brief Opens the tmpfcopystreamTempl. * * The temporary file will be in the same directory with .tmp.XXXXXX appended. * * @param filename The original filename which will be replaced on close * @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 */ bool open(const std::string& filename, std::ios_base::open_mode mode = std::ios_base::out, int buffer_size = -1 , int pback_size = -1) { originalfilename=filename; return tmpfstreamTempl ::open(filename+default_template_suffix,mode,buffer_size,pback_size); } /** * @brief Opens the tmpfcopystreamTempl. * * filename and tmpnametemplate must be on the same filesystem. * * @param filename The original filename which will be replaced on close * @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 */ bool open(const std::string& filename, const std::string& tmpnametemplate, std::ios_base::open_mode mode = std::ios_base::out, int buffer_size = -1 , int pback_size = -1) { originalfilename=filename; return tmpfstreamTempl ::open(tmpnametemplate,mode,buffer_size,pback_size); } void close(); /** * @brief Returns the name of the original file that will be replaced on @ref close. * @retval name of the original file */ std::string get_original_filename() { return originalfilename; } /** * @brief Sets the full sync mode * @param do_full_sync if true the file data and metadata will be fully synced * to disk on close */ void set_full_sync(bool do_full_sync=true) { full_sync=do_full_sync; } /** * @brief Read the current status of the full sync mode * @retval true if the full sync mode is active */ bool get_full_sync() { return full_sync; } /** * @brief On @ref close the target file is assigned these permissions */ void set_filemode_on_close(mode_t mode) { filemode_on_close=mode; } /** * @brief Read the permissions that will be set on @ref close * @retval permissions that will be set on @ref close */ mode_t get_filemode_on_close() { return filemode_on_close; } /** * @brief Change file owner (chown) that will be set on @ref close * @param user the new owner * @param group the new group, if left empty the main group of the user is set */ void set_owner_on_close(const I2n::User& user, const I2n::Group& group= I2n::Group()) { UserOnClose=user; GroupOnClose=group; } /** * @brief Get file owner that will be set on @ref close * @retval user that will be set on @ref close, an empty @ref User() means nothing will be set */ I2n::User get_owner_on_close() { return UserOnClose; } /** * @brief Get file owner group that will be set on @ref close * @retval group that will be set on @ref close */ I2n::Group get_group_on_close() { return GroupOnClose; } /** * @brief Delete the file. * * calling unlink is a safe way to abort, * the original file is not overwritten then * * @retval true if successful */ bool unlink() { return tmpfstreamTempl::unlink(); } private: /** * @brief Not allowed to be called by users. */ bool move(const std::string& targetpath, bool overwrite=false) { return tmpfstreamTempl::move(targetpath,overwrite); } }; /** * @brief ofstream which first writes to a temp file and atomically replaces an old file on close. */ typedef tmpfcopystreamTempl tmpofcopystream; /** * @brief fstream which first writes to a temp file and atomically replaces an old file on close. */ typedef tmpfcopystreamTempl tmpfcopystream; } #endif