// OpenVPN -- An application to securely tunnel IP networks // over a single port, with support for SSL/TLS-based // session authentication and key exchange, // packet encryption, packet authentication, and // packet compression. // // Copyright (C) 2012-2017 OpenVPN Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License Version 3 // as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program in the COPYING file. // If not, see . #ifndef OPENVPN_COMMON_TEMPFILE_H #define OPENVPN_COMMON_TEMPFILE_H #include #include #include // for memcpy #include // for write, unlink, lseek #include // for lseek #include #include #include #include #include #include #include namespace openvpn { class TempFile { public: OPENVPN_EXCEPTION(tempfile_exception); TempFile(const std::string& fn_template, const bool fn_delete) : fn(new char[fn_template.length()+1]), del(fn_delete) { std::memcpy(fn.get(), fn_template.c_str(), fn_template.length()+1); const size_t pos = fn_template.find("XXXXXX"); if (pos != std::string::npos) { const int suffixlen = fn_template.length() - pos - 6; if (suffixlen > 0) fd.reset(::mkstemps(fn.get(), suffixlen)); else fd.reset(::mkstemp(fn.get())); if (!fd.defined()) { const int eno = errno; OPENVPN_THROW(tempfile_exception, "error creating temporary file from template: " << fn_template << " : " << strerror_str(eno)); } } else OPENVPN_THROW(tempfile_exception, "badly formed temporary file template: " << fn_template); } ~TempFile() { fd.close(); delete_file(); } void reset() { const off_t off = ::lseek(fd(), 0, SEEK_SET); if (off < 0) { const int eno = errno; OPENVPN_THROW(tempfile_exception, "seek error on temporary file: " << filename() << " : " << strerror_str(eno)); } if (off) OPENVPN_THROW(tempfile_exception, "unexpected seek on temporary file: " << filename()); } void truncate() { reset(); if (::ftruncate(fd(), 0) < 0) { const int eno = errno; OPENVPN_THROW(tempfile_exception, "ftruncate error on temporary file: " << filename() << " : " << strerror_str(eno)); } } void write(const std::string& content) { const ssize_t size = write_retry(fd(), content.c_str(), content.length()); if (size < 0) { const int eno = errno; OPENVPN_THROW(tempfile_exception, "error writing to temporary file: " << filename() << " : " << strerror_str(eno)); } else if (size != content.length()) { OPENVPN_THROW(tempfile_exception, "incomplete write to temporary file: " << filename()); } } std::string read() { BufferList buflist = buf_read(fd(), filename()); return buflist.to_string(); } std::string filename() const { if (fn) return fn.get(); else return ""; } void close_file() { if (!fd.close()) { const int eno = errno; OPENVPN_THROW(tempfile_exception, "error closing temporary file: " << filename() << " : " << strerror_str(eno)); } } void set_delete(const bool del_flag) { del = del_flag; } void delete_file() { if (fn && del) { ::unlink(fn.get()); del = false; } } ScopedFD fd; private: std::unique_ptr fn; bool del; }; } #endif