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