1 /*****************************************************************************
2  * Author:   Valient Gough <vgough@pobox.com>
3  *
4  *****************************************************************************
5  * Copyright (c) 2003-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 #include "FileNode.h"
22 
23 #include <cerrno>
24 #include <cinttypes>
25 #include <cstring>
26 #include <fcntl.h>
27 #ifdef __linux__
28 #include <sys/fsuid.h>
29 #endif
30 #include <sys/stat.h>
31 #include <sys/types.h>
32 #include <unistd.h>
33 
34 #include "CipherFileIO.h"
35 #include "Error.h"
36 #include "FileIO.h"
37 #include "FileUtils.h"
38 #include "MACFileIO.h"
39 #include "Mutex.h"
40 #include "RawFileIO.h"
41 
42 using namespace std;
43 
44 namespace encfs {
45 
46 /*
47    TODO: locking at the FileNode level is inefficient, since this precludes
48    multiple IO operations from going on concurrently within the same file.
49 
50    There is no reason why simultainous reads cannot be satisfied, or why one
51    read has to wait for the decoding of the previous read before it can be
52    sent to the IO subsystem!
53 */
54 
FileNode(DirNode * parent_,const FSConfigPtr & cfg,const char * plaintextName_,const char * cipherName_,uint64_t fuseFh)55 FileNode::FileNode(DirNode *parent_, const FSConfigPtr &cfg,
56                    const char *plaintextName_, const char *cipherName_,
57                    uint64_t fuseFh) {
58 
59   pthread_mutex_init(&mutex, nullptr);
60 
61   Lock _lock(mutex);
62 
63   this->canary = CANARY_OK;
64 
65   this->_pname = plaintextName_;
66   this->_cname = cipherName_;
67   this->parent = parent_;
68 
69   this->fsConfig = cfg;
70 
71   this->fuseFh = fuseFh;
72 
73   // chain RawFileIO & CipherFileIO
74   std::shared_ptr<FileIO> rawIO(new RawFileIO(_cname));
75   io = std::shared_ptr<FileIO>(new CipherFileIO(rawIO, fsConfig));
76 
77   if ((cfg->config->blockMACBytes != 0) ||
78       (cfg->config->blockMACRandBytes != 0)) {
79     io = std::shared_ptr<FileIO>(new MACFileIO(io, fsConfig));
80   }
81 }
82 
~FileNode()83 FileNode::~FileNode() {
84   // FileNode mutex should be locked before the destructor is called
85   // pthread_mutex_lock( &mutex );
86 
87   canary = CANARY_DESTROYED;
88   _pname.assign(_pname.length(), '\0');
89   _cname.assign(_cname.length(), '\0');
90   io.reset();
91 
92   pthread_mutex_destroy(&mutex);
93 }
94 
cipherName() const95 const char *FileNode::cipherName() const { return _cname.c_str(); }
96 
plaintextName() const97 const char *FileNode::plaintextName() const { return _pname.c_str(); }
98 
plaintextParent() const99 string FileNode::plaintextParent() const { return parentDirectory(_pname); }
100 
setIV(const std::shared_ptr<FileIO> & io,uint64_t iv)101 static bool setIV(const std::shared_ptr<FileIO> &io, uint64_t iv) {
102   struct stat stbuf;
103   if ((io->getAttr(&stbuf) < 0) || S_ISREG(stbuf.st_mode)) {
104     return io->setIV(iv);
105   }
106   return true;
107 }
108 
setName(const char * plaintextName_,const char * cipherName_,uint64_t iv,bool setIVFirst)109 bool FileNode::setName(const char *plaintextName_, const char *cipherName_,
110                        uint64_t iv, bool setIVFirst) {
111   // Lock _lock( mutex );
112   if (cipherName_ != nullptr) {
113     VLOG(1) << "calling setIV on " << cipherName_;
114   }
115 
116   if (setIVFirst) {
117     if (fsConfig->config->externalIVChaining && !setIV(io, iv)) {
118       return false;
119     }
120 
121     // now change the name..
122     if (plaintextName_ != nullptr) {
123       this->_pname = plaintextName_;
124     }
125     if (cipherName_ != nullptr) {
126       this->_cname = cipherName_;
127       io->setFileName(cipherName_);
128     }
129   } else {
130     std::string oldPName = _pname;
131     std::string oldCName = _cname;
132 
133     if (plaintextName_ != nullptr) {
134       this->_pname = plaintextName_;
135     }
136     if (cipherName_ != nullptr) {
137       this->_cname = cipherName_;
138       io->setFileName(cipherName_);
139     }
140 
141     if (fsConfig->config->externalIVChaining && !setIV(io, iv)) {
142       _pname = oldPName;
143       _cname = oldCName;
144       return false;
145     }
146   }
147 
148   return true;
149 }
150 
mknod(mode_t mode,dev_t rdev,uid_t uid,gid_t gid)151 int FileNode::mknod(mode_t mode, dev_t rdev, uid_t uid, gid_t gid) {
152   Lock _lock(mutex);
153 
154   int res;
155   int olduid = -1;
156   int oldgid = -1;
157   if (gid != 0) {
158     oldgid = setfsgid(gid);
159     if (oldgid == -1) {
160       int eno = errno;
161       RLOG(DEBUG) << "setfsgid error: " << strerror(eno);
162       return -EPERM;
163     }
164   }
165   if (uid != 0) {
166     olduid = setfsuid(uid);
167     if (olduid == -1) {
168       int eno = errno;
169       RLOG(DEBUG) << "setfsuid error: " << strerror(eno);
170       return -EPERM;
171     }
172   }
173 
174   /*
175    * cf. xmp_mknod() in fusexmp.c
176    * The regular file stuff could be stripped off if there
177    * were a create method (advised to have)
178    */
179   if (S_ISREG(mode)) {
180     res = ::open(_cname.c_str(), O_CREAT | O_EXCL | O_WRONLY, mode);
181     if (res >= 0) {
182       res = ::close(res);
183     }
184   } else if (S_ISFIFO(mode)) {
185     res = ::mkfifo(_cname.c_str(), mode);
186   } else {
187     res = ::mknod(_cname.c_str(), mode, rdev);
188   }
189 
190   if (res == -1) {
191     int eno = errno;
192     VLOG(1) << "mknod error: " << strerror(eno);
193     res = -eno;
194   }
195 
196   if (olduid >= 0) {
197     if(setfsuid(olduid) == -1) {
198       int eno = errno;
199       RLOG(DEBUG) << "setfsuid back error: " << strerror(eno);
200       // does not return error here as initial setfsuid worked
201     }
202   }
203   if (oldgid >= 0) {
204     if(setfsgid(oldgid) == -1) {
205       int eno = errno;
206       RLOG(DEBUG) << "setfsgid back error: " << strerror(eno);
207       // does not return error here as initial setfsgid worked
208     }
209   }
210 
211   return res;
212 }
213 
open(int flags) const214 int FileNode::open(int flags) const {
215   Lock _lock(mutex);
216 
217   int res = io->open(flags);
218   return res;
219 }
220 
getAttr(struct stat * stbuf) const221 int FileNode::getAttr(struct stat *stbuf) const {
222   Lock _lock(mutex);
223 
224   int res = io->getAttr(stbuf);
225   return res;
226 }
227 
getSize() const228 off_t FileNode::getSize() const {
229   Lock _lock(mutex);
230 
231   off_t res = io->getSize();
232   return res;
233 }
234 
read(off_t offset,unsigned char * data,size_t size) const235 ssize_t FileNode::read(off_t offset, unsigned char *data, size_t size) const {
236   IORequest req;
237   req.offset = offset;
238   req.dataLen = size;
239   req.data = data;
240 
241   Lock _lock(mutex);
242 
243   return io->read(req);
244 }
245 
write(off_t offset,unsigned char * data,size_t size)246 ssize_t FileNode::write(off_t offset, unsigned char *data, size_t size) {
247   VLOG(1) << "FileNode::write offset " << offset << ", data size " << size;
248 
249   IORequest req;
250   req.offset = offset;
251   req.dataLen = size;
252   req.data = data;
253 
254   Lock _lock(mutex);
255 
256   ssize_t res = io->write(req);
257   // Of course due to encryption we genrally write more than requested
258   if (res < 0) {
259     return res;
260   }
261   return size;
262 }
263 
truncate(off_t size)264 int FileNode::truncate(off_t size) {
265   Lock _lock(mutex);
266 
267   return io->truncate(size);
268 }
269 
sync(bool datasync)270 int FileNode::sync(bool datasync) {
271   Lock _lock(mutex);
272 
273   int fh = io->open(O_RDONLY);
274   if (fh >= 0) {
275     int res = -EIO;
276 #if defined(HAVE_FDATASYNC)
277     if (datasync) {
278       res = fdatasync(fh);
279     } else {
280       res = fsync(fh);
281     }
282 #else
283     (void)datasync;
284     res = fsync(fh);
285 #endif
286 
287     if (res == -1) {
288       res = -errno;
289     }
290 
291     return res;
292   }
293   return fh;
294 }
295 
296 }  // namespace encfs
297