1 /*****************************************************************************
2 * Author: Valient Gough <vgough@pobox.com>
3 *
4 *****************************************************************************
5 * Copyright (c) 2004, Valient Gough
6 *
7 * This program is free software: you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as published by the
9 * Free Software Foundation, either version 3 of the License, or (at your
10 * option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
15 * for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #ifdef __linux__
22 #define _XOPEN_SOURCE 500 // pick up pread , pwrite
23 #endif
24 #include "easylogging++.h"
25 #include <cerrno>
26 #include <cinttypes>
27 #include <cstring>
28 #include <fcntl.h>
29 #include <sys/stat.h>
30 #include <unistd.h>
31 #include <utility>
32
33 #include "Error.h"
34 #include "FileIO.h"
35 #include "RawFileIO.h"
36
37 using namespace std;
38
39 namespace encfs {
40
41 static Interface RawFileIO_iface("FileIO/Raw", 1, 0, 0);
42
NewRawFileIO(const Interface & iface)43 FileIO *NewRawFileIO(const Interface &iface) {
44 (void)iface;
45 return new RawFileIO();
46 }
47
swap(int & x,int & y)48 inline void swap(int &x, int &y) {
49 int tmp = x;
50 x = y;
51 y = tmp;
52 }
53
RawFileIO()54 RawFileIO::RawFileIO()
55 : knownSize(false), fileSize(0), fd(-1), oldfd(-1), canWrite(false) {}
56
RawFileIO(std::string fileName)57 RawFileIO::RawFileIO(std::string fileName)
58 : name(std::move(fileName)),
59 knownSize(false),
60 fileSize(0),
61 fd(-1),
62 oldfd(-1),
63 canWrite(false) {}
64
~RawFileIO()65 RawFileIO::~RawFileIO() {
66 int _fd = -1;
67 int _oldfd = -1;
68
69 swap(_fd, fd);
70 swap(_oldfd, oldfd);
71
72 if (_oldfd != -1) {
73 close(_oldfd);
74 }
75
76 if (_fd != -1) {
77 close(_fd);
78 }
79 }
80
interface() const81 Interface RawFileIO::interface() const { return RawFileIO_iface; }
82
83 /*
84 Workaround for opening a file for write when permissions don't allow.
85 Since the kernel has already checked permissions, we can assume it is ok to
86 provide access. So force it by changing permissions temporarily. Should
87 be called with a lock around it so that there won't be a race condition
88 with calls to lstat picking up the wrong permissions.
89
90 This works around the problem described in
91 https://github.com/vgough/encfs/issues/181
92 Without this, "umask 0777 ; echo foo > bar" fails.
93
94 Sets errno when -1 is returned.
95 */
open_readonly_workaround(const char * path,int flags)96 static int open_readonly_workaround(const char *path, int flags) {
97 int fd = -1;
98 struct stat stbuf;
99 memset(&stbuf, 0, sizeof(struct stat));
100 if (lstat(path, &stbuf) != -1) {
101 // make sure user has read/write permission..
102 if (chmod(path, stbuf.st_mode | 0600) != -1) {
103 fd = ::open(path, flags);
104 chmod(path, stbuf.st_mode);
105 }
106 }
107 return fd;
108 }
109
110 /*
111 We shouldn't have to support all possible open flags, so untaint the flags
112 argument by only taking ones we understand and accept.
113 - Since the kernel has already done permission tests before calling us, we
114 shouldn't have to worry about access control.
115 - Basically we just need to distinguish between read and write flags
116 - Also keep the O_LARGEFILE flag, in case the underlying filesystem needs
117 it..
118 */
open(int flags)119 int RawFileIO::open(int flags) {
120 bool requestWrite = (((flags & O_RDWR) != 0) || ((flags & O_WRONLY) != 0));
121 VLOG(1) << "open call, requestWrite = " << requestWrite;
122
123 // if we have a descriptor and it is writable, or we don't need writable..
124 if ((fd >= 0) && (canWrite || !requestWrite)) {
125 VLOG(1) << "using existing file descriptor";
126 return fd; // success
127 }
128
129 int finalFlags = requestWrite ? O_RDWR : O_RDONLY;
130
131 #if defined(O_LARGEFILE)
132 if ((flags & O_LARGEFILE) != 0) {
133 finalFlags |= O_LARGEFILE;
134 }
135 #else
136 #warning O_LARGEFILE not supported
137 #endif
138
139 int eno = 0;
140 int newFd = ::open(name.c_str(), finalFlags);
141 if (newFd < 0) {
142 eno = errno;
143 }
144
145 VLOG(1) << "open file with flags " << finalFlags << ", result = " << newFd;
146
147 if ((newFd == -1) && (eno == EACCES)) {
148 VLOG(1) << "using readonly workaround for open";
149 newFd = open_readonly_workaround(name.c_str(), finalFlags);
150 eno = errno;
151 }
152
153 if (newFd < 0) {
154 RLOG(DEBUG) << "::open error: " << strerror(eno);
155 return -eno;
156 }
157
158 if (oldfd >= 0) {
159 RLOG(ERROR) << "leaking FD?: oldfd = " << oldfd << ", fd = " << fd
160 << ", newfd = " << newFd;
161 }
162
163 // the old fd might still be in use, so just keep it around for
164 // now.
165 canWrite = requestWrite;
166 oldfd = fd;
167 fd = newFd;
168
169 return fd;
170 }
171
getAttr(struct stat * stbuf) const172 int RawFileIO::getAttr(struct stat *stbuf) const {
173 int res = lstat(name.c_str(), stbuf);
174 int eno = errno;
175
176 if (res < 0) {
177 RLOG(DEBUG) << "getAttr error on " << name << ": " << strerror(eno);
178 }
179
180 return (res < 0) ? -eno : 0;
181 }
182
setFileName(const char * fileName)183 void RawFileIO::setFileName(const char *fileName) { name = fileName; }
184
getFileName() const185 const char *RawFileIO::getFileName() const { return name.c_str(); }
186
getSize() const187 off_t RawFileIO::getSize() const {
188 if (!knownSize) {
189 struct stat stbuf;
190 memset(&stbuf, 0, sizeof(struct stat));
191 int res = lstat(name.c_str(), &stbuf);
192
193 if (res == 0) {
194 const_cast<RawFileIO *>(this)->fileSize = stbuf.st_size;
195 const_cast<RawFileIO *>(this)->knownSize = true;
196 return fileSize;
197 }
198 int eno = errno;
199 RLOG(ERROR) << "getSize on " << name << " failed: " << strerror(eno);
200 return -eno;
201 }
202 return fileSize;
203 }
204
read(const IORequest & req) const205 ssize_t RawFileIO::read(const IORequest &req) const {
206 rAssert(fd >= 0);
207
208 ssize_t readSize = pread(fd, req.data, req.dataLen, req.offset);
209
210 if (readSize < 0) {
211 int eno = errno;
212 RLOG(WARNING) << "read failed at offset " << req.offset << " for "
213 << req.dataLen << " bytes: " << strerror(eno);
214 return -eno;
215 }
216
217 return readSize;
218 }
219
write(const IORequest & req)220 ssize_t RawFileIO::write(const IORequest &req) {
221 rAssert(fd >= 0);
222 rAssert(canWrite);
223
224 // int retrys = 10;
225 void *buf = req.data;
226 ssize_t bytes = req.dataLen;
227 off_t offset = req.offset;
228
229 /*
230 * Let's write while pwrite() writes, to avoid writing only a part of the
231 * request,
232 * whereas it could have been fully written. This to avoid inconsistencies /
233 * corruption.
234 */
235 // while ((bytes != 0) && retrys > 0) {
236 while (bytes != 0) {
237 ssize_t writeSize = ::pwrite(fd, buf, bytes, offset);
238
239 if (writeSize < 0) {
240 int eno = errno;
241 knownSize = false;
242 RLOG(WARNING) << "write failed at offset " << offset << " for " << bytes
243 << " bytes: " << strerror(eno);
244 // pwrite is not expected to return 0, so eno should always be set, but we
245 // never know...
246 return -eno;
247 }
248 if (writeSize == 0) {
249 return -EIO;
250 }
251
252 bytes -= writeSize;
253 offset += writeSize;
254 buf = (void *)((char *)buf + writeSize);
255 }
256
257 // if (bytes != 0) {
258 // RLOG(ERROR) << "Write error: wrote " << req.dataLen - bytes << " bytes of
259 // "
260 // << req.dataLen << ", max retries reached";
261 // knownSize = false;
262 // return (eno) ? -eno : -EIO;
263 // }
264 if (knownSize) {
265 off_t last = req.offset + req.dataLen;
266 if (last > fileSize) {
267 fileSize = last;
268 }
269 }
270
271 return req.dataLen;
272 }
273
truncate(off_t size)274 int RawFileIO::truncate(off_t size) {
275 int res;
276
277 if (fd >= 0 && canWrite) {
278 res = ::ftruncate(fd, size);
279 } else {
280 res = ::truncate(name.c_str(), size);
281 }
282
283 if (res < 0) {
284 int eno = errno;
285 RLOG(WARNING) << "truncate failed for " << name << " (" << fd << ") size "
286 << size << ", error " << strerror(eno);
287 res = -eno;
288 knownSize = false;
289 } else {
290 res = 0;
291 fileSize = size;
292 knownSize = true;
293 }
294
295 if (fd >= 0 && canWrite) {
296 #if defined(HAVE_FDATASYNC)
297 ::fdatasync(fd);
298 #else
299 ::fsync(fd);
300 #endif
301 }
302
303 return res;
304 }
305
isWritable() const306 bool RawFileIO::isWritable() const { return canWrite; }
307
308 } // namespace encfs
309