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