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