2 * Copyright 2013 Facebook, Inc.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 #include "folly/experimental/TestUtil.h"
22 #include <system_error>
24 #include "folly/Format.h"
29 TemporaryFile::TemporaryFile(const char* prefix, Scope scope,
30 bool closeOnDestruction)
32 closeOnDestruction_(closeOnDestruction) {
33 static const char* suffix = ".XXXXXX"; // per mkstemp(3)
34 if (!prefix || prefix[0] == '\0') {
37 const char* dir = nullptr;
38 if (!strchr(prefix, '/')) {
39 // Not a full path, try getenv("TMPDIR") or "/tmp"
40 dir = getenv("TMPDIR");
44 // The "!" is a placeholder to ensure that &(path[0]) is null-terminated.
45 // This is the only standard-compliant way to get at a null-terminated
46 // non-const char string inside a std::string: put the null-terminator
48 path_ = format("{}/{}{}!", dir, prefix, suffix).str();
50 path_ = format("{}{}!", prefix, suffix).str();
53 // Replace the '!' with a null terminator, we'll get rid of it later
54 path_[path_.size() - 1] = '\0';
56 fd_ = mkstemp(&(path_[0]));
58 throw std::system_error(errno, std::system_category(),
59 format("mkstemp failed: {}", path_).str().c_str());
62 DCHECK_EQ(path_[path_.size() - 1], '\0');
63 path_.erase(path_.size() - 1);
65 if (scope_ == Scope::UNLINK_IMMEDIATELY) {
66 if (unlink(path_.c_str()) == -1) {
67 throw std::system_error(errno, std::system_category(),
68 format("unlink failed: {}", path_).str().c_str());
70 path_.clear(); // path no longer available or meaningful
74 const std::string& TemporaryFile::path() const {
75 CHECK(scope_ != Scope::UNLINK_IMMEDIATELY);
76 DCHECK(!path_.empty());
80 TemporaryFile::~TemporaryFile() {
81 if (fd_ != -1 && closeOnDestruction_) {
82 if (close(fd_) == -1) {
83 PLOG(ERROR) << "close failed";
87 // If we previously failed to unlink() (UNLINK_IMMEDIATELY), we'll
89 if (scope_ != Scope::PERMANENT && !path_.empty()) {
90 if (unlink(path_.c_str()) == -1) {
91 PLOG(ERROR) << "unlink(" << path_ << ") failed";