"Fossies" - the Fresh Open Source Software Archive

Member "encfs-1.9.5/encfs/DirNode.cpp" (27 Apr 2018, 22215 Bytes) of package /linux/misc/encfs-1.9.5.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "DirNode.cpp" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.9.4_vs_1.9.5.

    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:
   51   void operator()(DIR *d) { ::closedir(d); }
   52 };
   53 
   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 
   60 DirTraverse::~DirTraverse() {
   61   dir.reset();
   62   iv = 0;
   63   naming.reset();
   64   root = false;
   65 }
   66 
   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 
   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 
  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:
  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 
  162   explicit operator bool() const { return renameList != nullptr; }
  163 
  164   bool apply();
  165   void undo();
  166 };
  167 
  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 
  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 
  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 
  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 
  264 bool DirNode::hasDirectoryNameDependency() const {
  265   return naming ? naming->getChainedNameIV() : false;
  266 }
  267 
  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 
  275 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  */
  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  */
  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  */
  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 
  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 
  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 
  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 */
  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 
  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 
  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 
  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 */
  666 std::shared_ptr<FileNode> DirNode::renameNode(const char *from,
  667                                               const char *to) {
  668   return renameNode(from, to, true);
  669 }
  670 
  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.
  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 
  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 */
  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 
  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