File::dup
[folly.git] / folly / File.cpp
1 /*
2  * Copyright 2013 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 {}
42
43 File::File(const char* name, int flags, mode_t mode)
44   : fd_(::open(name, flags, mode))
45   , ownsFd_(false) {
46   if (fd_ == -1) {
47     throwSystemError(folly::format("open(\"{}\", {:#o}, 0{:#o}) failed",
48                                    name, flags, mode).fbstr());
49   }
50   ownsFd_ = true;
51 }
52
53 File::File(File&& other)
54   : fd_(other.fd_)
55   , ownsFd_(other.ownsFd_) {
56
57   other.release();
58 }
59
60 File& File::operator=(File&& other) {
61   closeNoThrow();
62   swap(other);
63   return *this;
64 }
65
66 File::~File() {
67   closeNoThrow();  // ignore error
68 }
69
70 /* static */ File File::temporary() {
71   // make a temp file with tmpfile(), dup the fd, then return it in a File.
72   FILE* tmpFile = tmpfile();
73   checkFopenError(tmpFile, "tmpfile() failed");
74   SCOPE_EXIT { fclose(tmpFile); };
75
76   int fd = ::dup(fileno(tmpFile));
77   checkUnixError(fd, "dup() failed");
78
79   return File(fd, true);
80 }
81
82 void File::release() {
83   fd_ = -1;
84   ownsFd_ = false;
85 }
86
87 void File::swap(File& other) {
88   using std::swap;
89   swap(fd_, other.fd_);
90   swap(ownsFd_, other.ownsFd_);
91 }
92
93 void swap(File& a, File& b) {
94   a.swap(b);
95 }
96
97 File File::dup() const {
98   if (fd_ >= 0) {
99     int fd = ::dup(fd_);
100     checkUnixError(fd, "dup() failed");
101
102     return File(fd, true);
103   }
104
105   return File();
106 }
107
108 void File::close() {
109   if (!closeNoThrow()) {
110     throwSystemError("close() failed");
111   }
112 }
113
114 bool File::closeNoThrow() {
115   int r = ownsFd_ ? ::close(fd_) : 0;
116   release();
117   return r == 0;
118 }
119
120 void File::lock() { doLock(LOCK_EX); }
121 bool File::try_lock() { return doTryLock(LOCK_EX); }
122 void File::lock_shared() { doLock(LOCK_SH); }
123 bool File::try_lock_shared() { return doTryLock(LOCK_SH); }
124
125 void File::doLock(int op) {
126   checkUnixError(flockNoInt(fd_, op), "flock() failed (lock)");
127 }
128
129 bool File::doTryLock(int op) {
130   int r = flockNoInt(fd_, op | LOCK_NB);
131   // flock returns EWOULDBLOCK if already locked
132   if (r == -1 && errno == EWOULDBLOCK) return false;
133   checkUnixError(r, "flock() failed (try_lock)");
134   return true;
135 }
136
137 void File::unlock() {
138   checkUnixError(flockNoInt(fd_, LOCK_UN), "flock() failed (unlock)");
139 }
140 void File::unlock_shared() { unlock(); }
141
142 }  // namespace folly