"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