c60dfbced7fcf7406aa5df50cac1e2a9d6f8964d
[folly.git] / folly / File.cpp
1 /*
2  * Copyright 2014 Facebook, Inc.
3  *
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
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 #include "folly/File.h"
18
19 #include <fcntl.h>
20 #include <unistd.h>
21
22 #include "folly/Exception.h"
23 #include "folly/FileUtil.h"
24 #include "folly/Format.h"
25 #include "folly/ScopeGuard.h"
26
27 #include <system_error>
28
29 #include <glog/logging.h>
30
31 namespace folly {
32
33 File::File()
34   : fd_(-1)
35   , ownsFd_(false)
36 {}
37
38 File::File(int fd, bool ownsFd)
39   : fd_(fd)
40   , ownsFd_(ownsFd) {
41   CHECK_GE(fd, -1) << "fd must be -1 or non-negative";
42   CHECK(fd != -1 || !ownsFd) << "cannot own -1";
43 }
44
45 File::File(const char* name, int flags, mode_t mode)
46   : fd_(::open(name, flags, mode))
47   , ownsFd_(false) {
48   if (fd_ == -1) {
49     throwSystemError(folly::format("open(\"{}\", {:#o}, 0{:#o}) failed",
50                                    name, flags, mode).fbstr());
51   }
52   ownsFd_ = true;
53 }
54
55 File::File(File&& other)
56   : fd_(other.fd_)
57   , ownsFd_(other.ownsFd_) {
58   other.release();
59 }
60
61 File& File::operator=(File&& other) {
62   closeNoThrow();
63   swap(other);
64   return *this;
65 }
66
67 File::~File() {
68   closeNoThrow();  // ignore error
69 }
70
71 /* static */ File File::temporary() {
72   // make a temp file with tmpfile(), dup the fd, then return it in a File.
73   FILE* tmpFile = tmpfile();
74   checkFopenError(tmpFile, "tmpfile() failed");
75   SCOPE_EXIT { fclose(tmpFile); };
76
77   int fd = ::dup(fileno(tmpFile));
78   checkUnixError(fd, "dup() failed");
79
80   return File(fd, true);
81 }
82
83 int File::release() {
84   int released = fd_;
85   fd_ = -1;
86   ownsFd_ = false;
87   return released;
88 }
89
90 void File::swap(File& other) {
91   using std::swap;
92   swap(fd_, other.fd_);
93   swap(ownsFd_, other.ownsFd_);
94 }
95
96 void swap(File& a, File& b) {
97   a.swap(b);
98 }
99
100 File File::dup() const {
101   if (fd_ != -1) {
102     int fd = ::dup(fd_);
103     checkUnixError(fd, "dup() failed");
104
105     return File(fd, true);
106   }
107
108   return File();
109 }
110
111 void File::close() {
112   if (!closeNoThrow()) {
113     throwSystemError("close() failed");
114   }
115 }
116
117 bool File::closeNoThrow() {
118   int r = ownsFd_ ? ::close(fd_) : 0;
119   release();
120   return r == 0;
121 }
122
123 void File::lock() { doLock(LOCK_EX); }
124 bool File::try_lock() { return doTryLock(LOCK_EX); }
125 void File::lock_shared() { doLock(LOCK_SH); }
126 bool File::try_lock_shared() { return doTryLock(LOCK_SH); }
127
128 void File::doLock(int op) {
129   checkUnixError(flockNoInt(fd_, op), "flock() failed (lock)");
130 }
131
132 bool File::doTryLock(int op) {
133   int r = flockNoInt(fd_, op | LOCK_NB);
134   // flock returns EWOULDBLOCK if already locked
135   if (r == -1 && errno == EWOULDBLOCK) return false;
136   checkUnixError(r, "flock() failed (try_lock)");
137   return true;
138 }
139
140 void File::unlock() {
141   checkUnixError(flockNoInt(fd_, LOCK_UN), "flock() failed (unlock)");
142 }
143 void File::unlock_shared() { unlock(); }
144
145 }  // namespace folly