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 "DirNode.h"
22
23 #include <cerrno>
24 #include <cstdio>
25 #include <cstring>
26 #ifdef __linux__
27 #include <sys/fsuid.h>
28 #endif
29 #include <pthread.h>
30 #include <sys/stat.h>
31 #include <sys/types.h>
32 #include <unistd.h>
33 #include <utility>
34 #include <utime.h>
35
36 #include "Context.h"
37 #include "Error.h"
38 #include "FSConfig.h"
39 #include "FileNode.h"
40 #include "FileUtils.h"
41 #include "Mutex.h"
42 #include "NameIO.h"
43 #include "easylogging++.h"
44
45 using namespace std;
46
47 namespace encfs {
48
49 class DirDeleter {
50 public:
operator ()(DIR * d)51 void operator()(DIR *d) { ::closedir(d); }
52 };
53
DirTraverse(std::shared_ptr<DIR> _dirPtr,uint64_t _iv,std::shared_ptr<NameIO> _naming,bool _root)54 DirTraverse::DirTraverse(std::shared_ptr<DIR> _dirPtr, uint64_t _iv,
55 std::shared_ptr<NameIO> _naming, bool _root)
56 : dir(std::move(_dirPtr)), iv(_iv), naming(std::move(_naming)), root(_root) {}
57
58 DirTraverse &DirTraverse::operator=(const DirTraverse &src) = default;
59
~DirTraverse()60 DirTraverse::~DirTraverse() {
61 dir.reset();
62 iv = 0;
63 naming.reset();
64 root = false;
65 }
66
_nextName(struct dirent * & de,const std::shared_ptr<DIR> & dir,int * fileType,ino_t * inode)67 static bool _nextName(struct dirent *&de, const std::shared_ptr<DIR> &dir,
68 int *fileType, ino_t *inode) {
69 de = ::readdir(dir.get());
70
71 if (de != nullptr) {
72 if (fileType != nullptr) {
73 #if defined(HAVE_DIRENT_D_TYPE)
74 *fileType = de->d_type;
75 #else
76 #warning "struct dirent.d_type not supported"
77 *fileType = 0;
78 #endif
79 }
80 if (inode != nullptr) {
81 *inode = de->d_ino;
82 }
83 return true;
84 }
85 if (fileType != nullptr) {
86 *fileType = 0;
87 }
88 return false;
89 }
90
nextPlaintextName(int * fileType,ino_t * inode)91 std::string DirTraverse::nextPlaintextName(int *fileType, ino_t *inode) {
92 struct dirent *de = nullptr;
93 while (_nextName(de, dir, fileType, inode)) {
94 if (root && (strcmp(".encfs6.xml", de->d_name) == 0)) {
95 VLOG(1) << "skipping filename: " << de->d_name;
96 continue;
97 }
98 try {
99 uint64_t localIv = iv;
100 return naming->decodePath(de->d_name, &localIv);
101 } catch (encfs::Error &ex) {
102 // .. .problem decoding, ignore it and continue on to next name..
103 VLOG(1) << "error decoding filename: " << de->d_name;
104 }
105 }
106
107 return string();
108 }
109
nextInvalid()110 std::string DirTraverse::nextInvalid() {
111 struct dirent *de = nullptr;
112 // find the first name which produces a decoding error...
113 while (_nextName(de, dir, (int *)nullptr, (ino_t *)nullptr)) {
114 if (root && (strcmp(".encfs6.xml", de->d_name) == 0)) {
115 VLOG(1) << "skipping filename: " << de->d_name;
116 continue;
117 }
118 try {
119 uint64_t localIv = iv;
120 naming->decodePath(de->d_name, &localIv);
121 continue;
122 } catch (encfs::Error &ex) {
123 return string(de->d_name);
124 }
125 }
126
127 return string();
128 }
129
130 struct RenameEl {
131 // ciphertext names
132 string oldCName;
133 string newCName; // intermediate name (not final cname)
134
135 // plaintext names
136 string oldPName;
137 string newPName;
138
139 bool isDirectory;
140 };
141
142 class RenameOp {
143 private:
144 DirNode *dn;
145 std::shared_ptr<list<RenameEl> > renameList;
146 list<RenameEl>::const_iterator last;
147
148 public:
RenameOp(DirNode * _dn,std::shared_ptr<list<RenameEl>> _renameList)149 RenameOp(DirNode *_dn, std::shared_ptr<list<RenameEl> > _renameList)
150 : dn(_dn), renameList(std::move(_renameList)) {
151 last = renameList->begin();
152 }
153
154 // destructor
155 ~RenameOp();
156
157 RenameOp(const RenameOp &src) = delete; // copy contructor
158 RenameOp(RenameOp&& other) = delete; // move constructor
159 RenameOp& operator=(const RenameOp& other) = delete; // copy assignment
160 RenameOp& operator=(RenameOp&& other) = delete; // move assignment
161
operator bool() const162 explicit operator bool() const { return renameList != nullptr; }
163
164 bool apply();
165 void undo();
166 };
167
~RenameOp()168 RenameOp::~RenameOp() {
169 if (renameList) {
170 // got a bunch of decoded filenames sitting in memory.. do a little
171 // cleanup before leaving..
172 list<RenameEl>::iterator it;
173 for (it = renameList->begin(); it != renameList->end(); ++it) {
174 it->oldPName.assign(it->oldPName.size(), ' ');
175 it->newPName.assign(it->newPName.size(), ' ');
176 }
177 }
178 }
179
apply()180 bool RenameOp::apply() {
181 try {
182 while (last != renameList->end()) {
183 // backing store rename.
184 VLOG(1) << "renaming " << last->oldCName << " -> " << last->newCName;
185
186 struct stat st;
187 bool preserve_mtime = ::stat(last->oldCName.c_str(), &st) == 0;
188
189 // internal node rename..
190 dn->renameNode(last->oldPName.c_str(), last->newPName.c_str());
191
192 // rename on disk..
193 if (::rename(last->oldCName.c_str(), last->newCName.c_str()) == -1) {
194 int eno = errno;
195 RLOG(WARNING) << "Error renaming " << last->oldCName << ": "
196 << strerror(eno);
197 dn->renameNode(last->newPName.c_str(), last->oldPName.c_str(), false);
198 return false;
199 }
200
201 if (preserve_mtime) {
202 struct utimbuf ut;
203 ut.actime = st.st_atime;
204 ut.modtime = st.st_mtime;
205 ::utime(last->newCName.c_str(), &ut);
206 }
207
208 ++last;
209 }
210
211 return true;
212 } catch (encfs::Error &err) {
213 RLOG(WARNING) << err.what();
214 return false;
215 }
216 }
217
undo()218 void RenameOp::undo() {
219 VLOG(1) << "in undoRename";
220
221 if (last == renameList->begin()) {
222 VLOG(1) << "nothing to undo";
223 return; // nothing to undo
224 }
225
226 // list has to be processed backwards, otherwise we may rename
227 // directories and directory contents in the wrong order!
228 int undoCount = 0;
229 auto it = last;
230
231 while (it != renameList->begin()) {
232 --it;
233
234 VLOG(1) << "undo: renaming " << it->newCName << " -> " << it->oldCName;
235
236 ::rename(it->newCName.c_str(), it->oldCName.c_str());
237 try {
238 dn->renameNode(it->newPName.c_str(), it->oldPName.c_str(), false);
239 } catch (encfs::Error &err) {
240 RLOG(WARNING) << err.what();
241 // continue on anyway...
242 }
243 ++undoCount;
244 };
245
246 RLOG(WARNING) << "Undo rename count: " << undoCount;
247 }
248
DirNode(EncFS_Context * _ctx,const string & sourceDir,const FSConfigPtr & _config)249 DirNode::DirNode(EncFS_Context *_ctx, const string &sourceDir,
250 const FSConfigPtr &_config) {
251 pthread_mutex_init(&mutex, nullptr);
252
253 Lock _lock(mutex);
254
255 ctx = _ctx;
256 rootDir = sourceDir; // .. and fsConfig->opts->mountPoint have trailing slash
257 fsConfig = _config;
258
259 naming = fsConfig->nameCoding;
260 }
261
262 DirNode::~DirNode() = default;
263
hasDirectoryNameDependency() const264 bool DirNode::hasDirectoryNameDependency() const {
265 return naming ? naming->getChainedNameIV() : false;
266 }
267
rootDirectory()268 string DirNode::rootDirectory() {
269 // don't update last access here, otherwise 'du' would cause lastAccess to
270 // be reset.
271 // chop off '/' terminator from root dir.
272 return string(rootDir, 0, rootDir.length() - 1);
273 }
274
touchesMountpoint(const char * realPath) const275 bool DirNode::touchesMountpoint(const char *realPath) const {
276 const string &mountPoint = fsConfig->opts->mountPoint;
277 // compare mountPoint up to the leading slash.
278 // examples:
279 // mountPoint = /home/user/Junk/experiment/
280 // realPath = /home/user/Junk/experiment
281 // realPath = /home/user/Junk/experiment/abc
282 const ssize_t len = mountPoint.length() - 1;
283
284 if (mountPoint.compare(0, len, realPath, len) == 0) {
285 // if next character is a NUL or a slash, then we're referencing our
286 // mount point:
287 // .../experiment => true
288 // .../experiment/... => true
289 // .../experiment2/abc => false
290 return realPath[len] == '\0' || realPath[len] == '/';
291 }
292
293 return false;
294 }
295
296 /**
297 * Encrypt a plain-text file path to the ciphertext path with the
298 * ciphertext root directory name prefixed.
299 *
300 * Example:
301 * $ encfs -f -v cipher plain
302 * $ cd plain
303 * $ touch foobar
304 * cipherPath: /foobar encoded to cipher/NKAKsn2APtmquuKPoF4QRPxS
305 */
cipherPath(const char * plaintextPath)306 string DirNode::cipherPath(const char *plaintextPath) {
307 return rootDir + naming->encodePath(plaintextPath);
308 }
309
310 /**
311 * Same as cipherPath(), but does not prefix the ciphertext root directory
312 */
cipherPathWithoutRoot(const char * plaintextPath)313 string DirNode::cipherPathWithoutRoot(const char *plaintextPath) {
314 return naming->encodePath(plaintextPath);
315 }
316
317 /**
318 * Return the decrypted version of cipherPath
319 *
320 * In reverse mode, returns the encrypted version of cipherPath
321 */
plainPath(const char * cipherPath_)322 string DirNode::plainPath(const char *cipherPath_) {
323 try {
324 // Handle special absolute path encodings.
325 char mark = '+';
326 string prefix = "/";
327 if (fsConfig->reverseEncryption) {
328 mark = '/';
329 prefix = "+";
330 }
331 if (cipherPath_[0] == mark) {
332 return prefix +
333 naming->decodeName(cipherPath_ + 1, strlen(cipherPath_ + 1));
334 }
335
336 // Default.
337 return naming->decodePath(cipherPath_);
338 } catch (encfs::Error &err) {
339 RLOG(ERROR) << "decode err: " << err.what();
340 return string();
341 }
342 }
343
relativeCipherPath(const char * plaintextPath)344 string DirNode::relativeCipherPath(const char *plaintextPath) {
345 try {
346 // use '+' prefix to indicate special decoding.
347 char mark = fsConfig->reverseEncryption ? '+' : '/';
348 if (plaintextPath[0] == mark) {
349 return string(fsConfig->reverseEncryption ? "/" : "+") +
350 naming->encodeName(plaintextPath + 1, strlen(plaintextPath + 1));
351 }
352
353 return naming->encodePath(plaintextPath);
354 } catch (encfs::Error &err) {
355 RLOG(ERROR) << "encode err: " << err.what();
356 return string();
357 }
358 }
359
openDir(const char * plaintextPath)360 DirTraverse DirNode::openDir(const char *plaintextPath) {
361 string cyName = rootDir + naming->encodePath(plaintextPath);
362
363 DIR *dir = ::opendir(cyName.c_str());
364 if (dir == nullptr) {
365 int eno = errno;
366 VLOG(1) << "opendir error " << strerror(eno);
367 return DirTraverse(shared_ptr<DIR>(), 0, std::shared_ptr<NameIO>(), false);
368 }
369 std::shared_ptr<DIR> dp(dir, DirDeleter());
370
371 uint64_t iv = 0;
372 // if we're using chained IV mode, then compute the IV at this
373 // directory level..
374 try {
375 if (naming->getChainedNameIV()) {
376 naming->encodePath(plaintextPath, &iv);
377 }
378 } catch (encfs::Error &err) {
379 RLOG(ERROR) << "encode err: " << err.what();
380 }
381 return DirTraverse(dp, iv, naming, (strlen(plaintextPath) == 1));
382 }
383
genRenameList(list<RenameEl> & renameList,const char * fromP,const char * toP)384 bool DirNode::genRenameList(list<RenameEl> &renameList, const char *fromP,
385 const char *toP) {
386 uint64_t fromIV = 0, toIV = 0;
387
388 // compute the IV for both paths
389 string fromCPart = naming->encodePath(fromP, &fromIV);
390 string toCPart = naming->encodePath(toP, &toIV);
391
392 // where the files live before the rename..
393 string sourcePath = rootDir + fromCPart;
394
395 // ok..... we wish it was so simple.. should almost never happen
396 if (fromIV == toIV) {
397 return true;
398 }
399
400 // generate the real destination path, where we expect to find the files..
401 VLOG(1) << "opendir " << sourcePath;
402 std::shared_ptr<DIR> dir =
403 std::shared_ptr<DIR>(opendir(sourcePath.c_str()), DirDeleter());
404 if (!dir) {
405 return false;
406 }
407
408 struct dirent *de = nullptr;
409 while ((de = ::readdir(dir.get())) != nullptr) {
410 // decode the name using the oldIV
411 uint64_t localIV = fromIV;
412 string plainName;
413
414 if ((de->d_name[0] == '.') &&
415 ((de->d_name[1] == '\0') ||
416 ((de->d_name[1] == '.') && (de->d_name[2] == '\0')))) {
417 // skip "." and ".."
418 continue;
419 }
420
421 try {
422 plainName = naming->decodePath(de->d_name, &localIV);
423 } catch (encfs::Error &ex) {
424 // if filename can't be decoded, then ignore it..
425 continue;
426 }
427
428 // any error in the following will trigger a rename failure.
429 try {
430 // re-encode using the new IV..
431 localIV = toIV;
432 string newName = naming->encodePath(plainName.c_str(), &localIV);
433
434 // store rename information..
435 string oldFull = sourcePath + '/' + de->d_name;
436 string newFull = sourcePath + '/' + newName;
437
438 RenameEl ren;
439 ren.oldCName = oldFull;
440 ren.newCName = newFull;
441 ren.oldPName = string(fromP) + '/' + plainName;
442 ren.newPName = string(toP) + '/' + plainName;
443
444 bool isDir;
445 #if defined(HAVE_DIRENT_D_TYPE)
446 if (de->d_type != DT_UNKNOWN) {
447 isDir = (de->d_type == DT_DIR);
448 } else
449 #endif
450 {
451 isDir = isDirectory(oldFull.c_str());
452 }
453
454 ren.isDirectory = isDir;
455
456 if (isDir) {
457 // recurse.. We want to add subdirectory elements before the
458 // parent, as that is the logical rename order..
459 if (!genRenameList(renameList, ren.oldPName.c_str(),
460 ren.newPName.c_str())) {
461 return false;
462 }
463 }
464
465 VLOG(1) << "adding file " << oldFull << " to rename list";
466
467 renameList.push_back(ren);
468 } catch (encfs::Error &err) {
469 // We can't convert this name, because we don't have a valid IV for
470 // it (or perhaps a valid key).. It will be inaccessible..
471 RLOG(WARNING) << "Aborting rename: error on file: "
472 << fromCPart.append(1, '/').append(de->d_name);
473 RLOG(WARNING) << err.what();
474
475 // abort.. Err on the side of safety and disallow rename, rather
476 // then loosing files..
477 return false;
478 }
479 }
480
481 return true;
482 }
483
484 /*
485 A bit of a pain.. If a directory is renamed in a filesystem with
486 directory initialization vector chaining, then we have to recursively
487 rename every descendent of this directory, as all initialization vectors
488 will have changed..
489
490 Returns a list of renamed items on success, a null list on failure.
491 */
newRenameOp(const char * fromP,const char * toP)492 std::shared_ptr<RenameOp> DirNode::newRenameOp(const char *fromP,
493 const char *toP) {
494 // Do the rename in two stages to avoid chasing our tail
495 // Undo everything if we encounter an error!
496 std::shared_ptr<list<RenameEl> > renameList(new list<RenameEl>);
497 if (!genRenameList(*renameList.get(), fromP, toP)) {
498 RLOG(WARNING) << "Error during generation of recursive rename list";
499 return std::shared_ptr<RenameOp>();
500 }
501 return std::make_shared<RenameOp>(this, renameList);
502 }
503
mkdir(const char * plaintextPath,mode_t mode,uid_t uid,gid_t gid)504 int DirNode::mkdir(const char *plaintextPath, mode_t mode, uid_t uid,
505 gid_t gid) {
506 string cyName = rootDir + naming->encodePath(plaintextPath);
507 rAssert(!cyName.empty());
508
509 VLOG(1) << "mkdir on " << cyName;
510
511 // if uid or gid are set, then that should be the directory owner
512 int olduid = -1;
513 int oldgid = -1;
514 if (gid != 0) {
515 oldgid = setfsgid(gid);
516 if (oldgid == -1) {
517 int eno = errno;
518 RLOG(DEBUG) << "setfsgid error: " << strerror(eno);
519 return -EPERM;
520 }
521 }
522 if (uid != 0) {
523 olduid = setfsuid(uid);
524 if (olduid == -1) {
525 int eno = errno;
526 RLOG(DEBUG) << "setfsuid error: " << strerror(eno);
527 return -EPERM;
528 }
529 }
530
531 int res = ::mkdir(cyName.c_str(), mode);
532
533 if (res == -1) {
534 int eno = errno;
535 RLOG(WARNING) << "mkdir error on " << cyName << " mode " << mode << ": "
536 << strerror(eno);
537 res = -eno;
538 }
539
540 if (olduid >= 0) {
541 if(setfsuid(olduid) == -1) {
542 int eno = errno;
543 RLOG(DEBUG) << "setfsuid back error: " << strerror(eno);
544 // does not return error here as initial setfsuid worked
545 }
546 }
547 if (oldgid >= 0) {
548 if(setfsgid(oldgid) == -1) {
549 int eno = errno;
550 RLOG(DEBUG) << "setfsgid back error: " << strerror(eno);
551 // does not return error here as initial setfsgid worked
552 }
553 }
554
555 return res;
556 }
557
rename(const char * fromPlaintext,const char * toPlaintext)558 int DirNode::rename(const char *fromPlaintext, const char *toPlaintext) {
559 Lock _lock(mutex);
560
561 string fromCName = rootDir + naming->encodePath(fromPlaintext);
562 string toCName = rootDir + naming->encodePath(toPlaintext);
563 rAssert(!fromCName.empty());
564 rAssert(!toCName.empty());
565
566 VLOG(1) << "rename " << fromCName << " -> " << toCName;
567
568 std::shared_ptr<FileNode> toNode = findOrCreate(toPlaintext);
569
570 std::shared_ptr<RenameOp> renameOp;
571 if (hasDirectoryNameDependency() && isDirectory(fromCName.c_str())) {
572 VLOG(1) << "recursive rename begin";
573 renameOp = newRenameOp(fromPlaintext, toPlaintext);
574
575 if (!renameOp || !renameOp->apply()) {
576 if (renameOp) {
577 renameOp->undo();
578 }
579
580 RLOG(WARNING) << "rename aborted";
581 return -EACCES;
582 }
583 VLOG(1) << "recursive rename end";
584 }
585
586 int res = 0;
587 try {
588 struct stat st;
589 bool preserve_mtime = ::stat(fromCName.c_str(), &st) == 0;
590
591 renameNode(fromPlaintext, toPlaintext);
592 res = ::rename(fromCName.c_str(), toCName.c_str());
593
594 if (res == -1) {
595 // undo
596 res = -errno;
597 renameNode(toPlaintext, fromPlaintext, false);
598
599 if (renameOp) {
600 renameOp->undo();
601 }
602 }
603 else {
604 #ifdef __CYGWIN__
605 // When renaming a file, Windows first opens it, renames it and then closes it
606 // We then must decrease the target openFiles count
607 // We could recreate the source so that close will not (silently) fails,
608 // however it will update modification time of the file, so break what we do below.
609 // Let's simply warn in eraseNode().
610 if (!isDirectory(toCName.c_str())) {
611 std::shared_ptr<FileNode> toNode = findOrCreate(toPlaintext);
612 ctx->eraseNode(toPlaintext, toNode);
613 //ctx->putNode(fromPlaintext, toNode);
614 }
615 #endif
616 if (preserve_mtime) {
617 struct utimbuf ut;
618 ut.actime = st.st_atime;
619 ut.modtime = st.st_mtime;
620 ::utime(toCName.c_str(), &ut);
621 }
622 }
623 } catch (encfs::Error &err) {
624 // exception from renameNode, just show the error and continue..
625 RLOG(WARNING) << err.what();
626 res = -EIO;
627 }
628
629 if (res != 0) {
630 VLOG(1) << "rename failed: " << strerror(-res);
631 }
632
633 return res;
634 }
635
link(const char * to,const char * from)636 int DirNode::link(const char *to, const char *from) {
637 Lock _lock(mutex);
638
639 string toCName = rootDir + naming->encodePath(to);
640 string fromCName = rootDir + naming->encodePath(from);
641
642 rAssert(!toCName.empty());
643 rAssert(!fromCName.empty());
644
645 VLOG(1) << "link " << fromCName << " -> " << toCName;
646
647 int res = -EPERM;
648 if (fsConfig->config->externalIVChaining) {
649 VLOG(1) << "hard links not supported with external IV chaining!";
650 } else {
651 res = ::link(toCName.c_str(), fromCName.c_str());
652 if (res == -1) {
653 res = -errno;
654 } else {
655 res = 0;
656 }
657 }
658
659 return res;
660 }
661
662 /*
663 The node is keyed by filename, so a rename means the internal node names
664 must be changed.
665 */
renameNode(const char * from,const char * to)666 std::shared_ptr<FileNode> DirNode::renameNode(const char *from,
667 const char *to) {
668 return renameNode(from, to, true);
669 }
670
renameNode(const char * from,const char * to,bool forwardMode)671 std::shared_ptr<FileNode> DirNode::renameNode(const char *from, const char *to,
672 bool forwardMode) {
673 std::shared_ptr<FileNode> node = findOrCreate(from);
674
675 if (node) {
676 uint64_t newIV = 0;
677 string cname = rootDir + naming->encodePath(to, &newIV);
678
679 VLOG(1) << "renaming internal node " << node->cipherName() << " -> "
680 << cname;
681
682 if (node->setName(to, cname.c_str(), newIV, forwardMode)) {
683 if (ctx != nullptr) {
684 ctx->renameNode(from, to);
685 }
686 } else {
687 // rename error! - put it back
688 RLOG(ERROR) << "renameNode failed";
689 throw Error("Internal node name change failed!");
690 }
691 }
692
693 return node;
694 }
695
696 // findOrCreate checks if we already have a FileNode for "plainName" and
697 // creates a new one if we don't. Returns the FileNode.
findOrCreate(const char * plainName)698 std::shared_ptr<FileNode> DirNode::findOrCreate(const char *plainName) {
699 std::shared_ptr<FileNode> node;
700
701 // See if we already have a FileNode for this path.
702 if (ctx != nullptr) {
703 node = ctx->lookupNode(plainName);
704
705 // If we don't, create a new one.
706 if (!node) {
707 uint64_t iv = 0;
708 string cipherName = naming->encodePath(plainName, &iv);
709 uint64_t fuseFh = ctx->nextFuseFh();
710 node.reset(new FileNode(this, fsConfig, plainName,
711 (rootDir + cipherName).c_str(), fuseFh));
712
713 if (fsConfig->config->externalIVChaining) {
714 node->setName(nullptr, nullptr, iv);
715 }
716
717 VLOG(1) << "created FileNode for " << node->cipherName();
718 }
719 }
720
721 return node;
722 }
723
lookupNode(const char * plainName,const char *)724 shared_ptr<FileNode> DirNode::lookupNode(const char *plainName,
725 const char * /* requestor */) {
726 Lock _lock(mutex);
727 return findOrCreate(plainName);
728 }
729
730 /*
731 Similar to lookupNode, except that we also call open() and only return a
732 node on sucess. This is done in one step to avoid any race conditions
733 with the stored state of the file.
734 "result" is set to -1 on failure, a value >= 0 on success.
735 */
openNode(const char * plainName,const char * requestor,int flags,int * result)736 std::shared_ptr<FileNode> DirNode::openNode(const char *plainName,
737 const char *requestor, int flags,
738 int *result) {
739 (void)requestor;
740 rAssert(result != nullptr);
741 Lock _lock(mutex);
742
743 std::shared_ptr<FileNode> node = findOrCreate(plainName);
744
745 if (node && (*result = node->open(flags)) >= 0) {
746 return node;
747 }
748 return std::shared_ptr<FileNode>();
749 }
750
unlink(const char * plaintextName)751 int DirNode::unlink(const char *plaintextName) {
752 string cyName = naming->encodePath(plaintextName);
753 VLOG(1) << "unlink " << cyName;
754
755 Lock _lock(mutex);
756
757 // Windows does not allow deleting opened files, so no need to check
758 // There is this "issue" however : https://github.com/billziss-gh/winfsp/issues/157
759 #ifndef __CYGWIN__
760 if ((ctx != nullptr) && ctx->lookupNode(plaintextName)) {
761 // If FUSE is running with "hard_remove" option where it doesn't
762 // hide open files for us, then we can't allow an unlink of an open
763 // file..
764 RLOG(WARNING) << "Refusing to unlink open file: " << cyName
765 << ", hard_remove option "
766 "is probably in effect";
767 return -EBUSY;
768 }
769 #endif
770
771 int res = 0;
772 string fullName = rootDir + cyName;
773 res = ::unlink(fullName.c_str());
774 if (res == -1) {
775 res = -errno;
776 VLOG(1) << "unlink error: " << strerror(-res);
777 }
778
779 return res;
780 }
781
782 } // namespace encfs
783