"Fossies" - the Fresh Open Source Software Archive 
Member "encfs-1.9.5/encfs/encfs.cpp" (27 Apr 2018, 24438 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 "encfs.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-2007, Valient Gough
6 *
7 * This program is free software; you can distribute it and/or modify it under
8 * the terms of the GNU General Public License (GPL), as published by the Free
9 * Software Foundation; either version 2 of the License, or (at your option)
10 * 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 General Public License for
15 * more details.
16 */
17
18 #include "encfs.h"
19
20 #include <cerrno>
21 #include <cinttypes>
22 #include <cstddef>
23 #include <cstdint>
24 #include <cstdio>
25 #include <cstring>
26 #include <ctime>
27 #include <fcntl.h>
28 #include <limits>
29 #include <memory>
30 #include <sys/stat.h>
31 #include <sys/statvfs.h>
32 #include <sys/time.h>
33 #include <unistd.h>
34 #include <utime.h>
35 #ifdef __linux__
36 #include <sys/fsuid.h>
37 #endif
38
39 #if defined(HAVE_SYS_XATTR_H)
40 #include <sys/xattr.h>
41 #elif defined(HAVE_ATTR_XATTR_H)
42 #include <attr/xattr.h>
43 #endif
44
45 #include "easylogging++.h"
46 #include <functional>
47 #include <string>
48 #include <vector>
49
50 #include "Context.h"
51 #include "DirNode.h"
52 #include "Error.h"
53 #include "FileNode.h"
54 #include "FileUtils.h"
55 #include "fuse.h"
56
57 #ifndef MIN
58 #define MIN(a, b) (((a) < (b)) ? (a) : (b))
59 #endif
60
61 #define ESUCCESS 0
62
63 using namespace std;
64 using namespace std::placeholders;
65
66 namespace encfs {
67
68 #define GET_FN(ctx, finfo) (ctx)->getNode((void *)(uintptr_t)(finfo)->fh)
69
70 static EncFS_Context *context() {
71 return (EncFS_Context *)fuse_get_context()->private_data;
72 }
73
74 /**
75 * Helper function - determine if the filesystem is read-only
76 * Optionally takes a pointer to the EncFS_Context, will get it from FUSE
77 * if the argument is NULL.
78 */
79 static bool isReadOnly(EncFS_Context *ctx) { return ctx->opts->readOnly; }
80
81 // helper function -- apply a functor to a cipher path, given the plain path
82 static int withCipherPath(
83 const char *opName, const char *path,
84 const function<int(EncFS_Context *, const string &)> &op,
85 bool passReturnCode = false) {
86 EncFS_Context *ctx = context();
87
88 int res = -EIO;
89 std::shared_ptr<DirNode> FSRoot = ctx->getRoot(&res);
90 if (!FSRoot) {
91 return res;
92 }
93
94 try {
95 string cyName = FSRoot->cipherPath(path);
96 VLOG(1) << "op: " << opName << " : " << cyName;
97
98 res = op(ctx, cyName);
99
100 if (res == -1) {
101 int eno = errno;
102 VLOG(1) << "op: " << opName << " error: " << strerror(eno);
103 res = -eno;
104 } else if (!passReturnCode) {
105 res = ESUCCESS;
106 }
107 } catch (encfs::Error &err) {
108 RLOG(ERROR) << "withCipherPath: error caught in " << opName << ": "
109 << err.what();
110 }
111 return res;
112 }
113
114 static void checkCanary(const std::shared_ptr<FileNode> &fnode) {
115 if (fnode->canary == CANARY_OK) {
116 return;
117 }
118 if (fnode->canary == CANARY_RELEASED) {
119 // "fnode" may have been released after it was retrieved by
120 // lookupFuseFh. This is not an error. std::shared_ptr will release
121 // the memory only when all operations on the FileNode have been
122 // completed.
123 return;
124 }
125 if (fnode->canary == CANARY_DESTROYED) {
126 RLOG(ERROR)
127 << "canary=CANARY_DESTROYED. FileNode accessed after it was destroyed.";
128 } else {
129 RLOG(ERROR) << "canary=0x" << std::hex << fnode->canary
130 << ". Memory corruption?";
131 }
132 throw Error("dead canary");
133 }
134
135 // helper function -- apply a functor to a node
136 static int withFileNode(const char *opName, const char *path,
137 struct fuse_file_info *fi,
138 const function<int(FileNode *)> &op) {
139 EncFS_Context *ctx = context();
140
141 int res = -EIO;
142 bool skipUsageCount = false;
143 if (strlen(path) == 1) {
144 skipUsageCount = true;
145 }
146 std::shared_ptr<DirNode> FSRoot = ctx->getRoot(&res, skipUsageCount);
147 if (!FSRoot) {
148 return res;
149 }
150
151 try {
152
153 auto do_op = [&FSRoot, opName, &op](std::shared_ptr<FileNode> fnode) {
154 rAssert(fnode != nullptr);
155 checkCanary(fnode);
156 VLOG(1) << "op: " << opName << " : " << fnode->cipherName();
157
158 // check that we're not recursing into the mount point itself
159 if (FSRoot->touchesMountpoint(fnode->cipherName())) {
160 VLOG(1) << "op: " << opName << " error: Tried to touch mountpoint: '"
161 << fnode->cipherName() << "'";
162 return -EIO;
163 }
164 return op(fnode.get());
165 };
166
167 if (fi != nullptr && fi->fh != 0) {
168 auto node = ctx->lookupFuseFh(fi->fh);
169 if (node == nullptr) {
170 #ifdef __CYGWIN__
171 if (strcmp(opName, "flush") == 0) {
172 RLOG(WARNING) << "Filenode to flush not found, file has certainly be renamed: "
173 << path;
174 return 0;
175 }
176 #endif
177 auto msg = "fh=" + std::to_string(fi->fh) + " not found in fuseFhMap";
178 throw Error(msg.c_str());
179 }
180 res = do_op(node);
181 } else {
182 res = do_op(FSRoot->lookupNode(path, opName));
183 }
184
185 if (res < 0) {
186 RLOG(DEBUG) << "op: " << opName << " error: " << strerror(-res);
187 }
188 } catch (encfs::Error &err) {
189 RLOG(ERROR) << "withFileNode: error caught in " << opName << ": "
190 << err.what();
191 }
192 return res;
193 }
194
195 /*
196 The log messages below always print encrypted filenames, not
197 plaintext. This avoids possibly leaking information to log files.
198
199 The purpose of this layer of code is to take the FUSE request and dispatch
200 to the internal interfaces. Any marshaling of arguments and return types
201 can be done here.
202 */
203
204 int _do_getattr(FileNode *fnode, struct stat *stbuf) {
205 int res = fnode->getAttr(stbuf);
206 if (res == ESUCCESS && S_ISLNK(stbuf->st_mode)) {
207 EncFS_Context *ctx = context();
208 std::shared_ptr<DirNode> FSRoot = ctx->getRoot(&res);
209 if (FSRoot) {
210 // determine plaintext link size.. Easiest to read and decrypt..
211 std::vector<char> buf(stbuf->st_size + 1, '\0');
212
213 res = ::readlink(fnode->cipherName(), buf.data(), stbuf->st_size);
214 if (res >= 0) {
215 // other functions expect c-strings to be null-terminated, which
216 // readlink doesn't provide
217 buf[res] = '\0';
218
219 stbuf->st_size = FSRoot->plainPath(buf.data()).length();
220
221 res = ESUCCESS;
222 } else {
223 res = -errno;
224 }
225 }
226 }
227
228 return res;
229 }
230
231 int encfs_getattr(const char *path, struct stat *stbuf) {
232 return withFileNode("getattr", path, nullptr, bind(_do_getattr, _1, stbuf));
233 }
234
235 int encfs_fgetattr(const char *path, struct stat *stbuf,
236 struct fuse_file_info *fi) {
237 return withFileNode("fgetattr", path, fi, bind(_do_getattr, _1, stbuf));
238 }
239
240 int encfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
241 off_t offset, struct fuse_file_info *finfo) {
242 EncFS_Context *ctx = context();
243
244 //unused parameters
245 (void)offset;
246 (void)finfo;
247
248 int res = ESUCCESS;
249 std::shared_ptr<DirNode> FSRoot = ctx->getRoot(&res);
250 if (!FSRoot) {
251 return res;
252 }
253
254 try {
255
256 DirTraverse dt = FSRoot->openDir(path);
257
258 VLOG(1) << "readdir on " << FSRoot->cipherPath(path);
259
260 if (dt.valid()) {
261 int fileType = 0;
262 ino_t inode = 0;
263
264 std::string name = dt.nextPlaintextName(&fileType, &inode);
265 while (!name.empty()) {
266 struct stat st;
267 st.st_ino = inode;
268 st.st_mode = fileType << 12;
269
270 // TODO: add offset support.
271 #if defined(fuse_fill_dir_flags)
272 if (filler(buf, name.c_str(), &st, 0, 0)) break;
273 #else
274 if (filler(buf, name.c_str(), &st, 0) != 0) {
275 break;
276 }
277 #endif
278
279 name = dt.nextPlaintextName(&fileType, &inode);
280 }
281 } else {
282 VLOG(1) << "readdir request invalid, path: '" << path << "'";
283 }
284
285 return res;
286 } catch (encfs::Error &err) {
287 RLOG(ERROR) << "Error caught in readdir";
288 return -EIO;
289 }
290 }
291
292 int encfs_mknod(const char *path, mode_t mode, dev_t rdev) {
293 EncFS_Context *ctx = context();
294
295 if (isReadOnly(ctx)) {
296 return -EROFS;
297 }
298
299 int res = -EIO;
300 std::shared_ptr<DirNode> FSRoot = ctx->getRoot(&res);
301 if (!FSRoot) {
302 return res;
303 }
304
305 try {
306 std::shared_ptr<FileNode> fnode = FSRoot->lookupNode(path, "mknod");
307
308 VLOG(1) << "mknod on " << fnode->cipherName() << ", mode " << mode
309 << ", dev " << rdev;
310
311 uid_t uid = 0;
312 gid_t gid = 0;
313 if (ctx->publicFilesystem) {
314 fuse_context *context = fuse_get_context();
315 uid = context->uid;
316 gid = context->gid;
317 }
318 res = fnode->mknod(mode, rdev, uid, gid);
319 // Is this error due to access problems?
320 if (ctx->publicFilesystem && -res == EACCES) {
321 // try again using the parent dir's group
322 string parent = fnode->plaintextParent();
323 VLOG(1) << "trying public filesystem workaround for " << parent;
324 std::shared_ptr<FileNode> dnode =
325 FSRoot->lookupNode(parent.c_str(), "mknod");
326
327 struct stat st;
328 if (dnode->getAttr(&st) == 0) {
329 res = fnode->mknod(mode, rdev, uid, st.st_gid);
330 }
331 }
332 } catch (encfs::Error &err) {
333 RLOG(ERROR) << "error caught in mknod: " << err.what();
334 }
335 return res;
336 }
337
338 int encfs_mkdir(const char *path, mode_t mode) {
339 fuse_context *fctx = fuse_get_context();
340 EncFS_Context *ctx = context();
341
342 if (isReadOnly(ctx)) {
343 return -EROFS;
344 }
345
346 int res = -EIO;
347 std::shared_ptr<DirNode> FSRoot = ctx->getRoot(&res);
348 if (!FSRoot) {
349 return res;
350 }
351
352 try {
353 uid_t uid = 0;
354 gid_t gid = 0;
355 if (ctx->publicFilesystem) {
356 uid = fctx->uid;
357 gid = fctx->gid;
358 }
359 res = FSRoot->mkdir(path, mode, uid, gid);
360 // Is this error due to access problems?
361 if (ctx->publicFilesystem && -res == EACCES) {
362 // try again using the parent dir's group
363 string parent = parentDirectory(path);
364 std::shared_ptr<FileNode> dnode =
365 FSRoot->lookupNode(parent.c_str(), "mkdir");
366
367 struct stat st;
368 if (dnode->getAttr(&st) == 0) {
369 res = FSRoot->mkdir(path, mode, uid, st.st_gid);
370 }
371 }
372 } catch (encfs::Error &err) {
373 RLOG(ERROR) << "error caught in mkdir: " << err.what();
374 }
375 return res;
376 }
377
378 int encfs_unlink(const char *path) {
379 EncFS_Context *ctx = context();
380 if (isReadOnly(ctx)) {
381 return -EROFS;
382 }
383
384 int res = -EIO;
385 std::shared_ptr<DirNode> FSRoot = ctx->getRoot(&res);
386 if (!FSRoot) {
387 return res;
388 }
389
390 try {
391 // let DirNode handle it atomically so that it can handle race
392 // conditions
393 res = FSRoot->unlink(path);
394 } catch (encfs::Error &err) {
395 RLOG(ERROR) << "error caught in unlink: " << err.what();
396 }
397 return res;
398 }
399
400 int _do_rmdir(EncFS_Context *, const string &cipherPath) {
401 return rmdir(cipherPath.c_str());
402 }
403
404 int encfs_rmdir(const char *path) {
405 EncFS_Context *ctx = context();
406 if (isReadOnly(ctx)) {
407 return -EROFS;
408 }
409 return withCipherPath("rmdir", path, bind(_do_rmdir, _1, _2));
410 }
411
412 int _do_readlink(EncFS_Context *ctx, const string &cyName, char *buf,
413 size_t size) {
414 int res = ESUCCESS;
415 std::shared_ptr<DirNode> FSRoot = ctx->getRoot(&res);
416 if (!FSRoot) {
417 return res;
418 }
419
420 res = ::readlink(cyName.c_str(), buf, size - 1);
421
422 if (res == -1) {
423 return -errno;
424 }
425
426 buf[res] = '\0'; // ensure null termination
427 string decodedName;
428 decodedName = FSRoot->plainPath(buf);
429
430 if (!decodedName.empty()) {
431 strncpy(buf, decodedName.c_str(), size - 1);
432 buf[size - 1] = '\0';
433
434 return ESUCCESS;
435 }
436 RLOG(WARNING) << "Error decoding link";
437 return -1;
438 }
439
440 int encfs_readlink(const char *path, char *buf, size_t size) {
441 return withCipherPath("readlink", path,
442 bind(_do_readlink, _1, _2, buf, size));
443 }
444
445 /**
446 * Create a symbolic link pointing to "to" named "from"
447 */
448 int encfs_symlink(const char *to, const char *from) {
449 EncFS_Context *ctx = context();
450
451 if (isReadOnly(ctx)) {
452 return -EROFS;
453 }
454
455 int res = -EIO;
456 std::shared_ptr<DirNode> FSRoot = ctx->getRoot(&res);
457 if (!FSRoot) {
458 return res;
459 }
460
461 try {
462 string fromCName = FSRoot->cipherPath(from);
463 // allow fully qualified names in symbolic links.
464 string toCName = FSRoot->relativeCipherPath(to);
465
466 VLOG(1) << "symlink " << fromCName << " -> " << toCName;
467
468 // use setfsuid / setfsgid so that the new link will be owned by the
469 // uid/gid provided by the fuse_context.
470 int olduid = -1;
471 int oldgid = -1;
472 if (ctx->publicFilesystem) {
473 fuse_context *context = fuse_get_context();
474 oldgid = setfsgid(context->gid);
475 if (oldgid == -1) {
476 int eno = errno;
477 RLOG(DEBUG) << "setfsgid error: " << strerror(eno);
478 return -EPERM;
479 }
480 olduid = setfsuid(context->uid);
481 if (olduid == -1) {
482 int eno = errno;
483 RLOG(DEBUG) << "setfsuid error: " << strerror(eno);
484 return -EPERM;
485 }
486 }
487 res = ::symlink(toCName.c_str(), fromCName.c_str());
488 if (olduid >= 0) {
489 if(setfsuid(olduid) == -1) {
490 int eno = errno;
491 RLOG(DEBUG) << "setfsuid back error: " << strerror(eno);
492 // does not return error here as initial setfsuid worked
493 }
494 }
495 if (oldgid >= 0) {
496 if(setfsgid(oldgid) == -1) {
497 int eno = errno;
498 RLOG(DEBUG) << "setfsgid back error: " << strerror(eno);
499 // does not return error here as initial setfsgid worked
500 }
501 }
502
503 if (res == -1) {
504 res = -errno;
505 } else {
506 res = ESUCCESS;
507 }
508 } catch (encfs::Error &err) {
509 RLOG(ERROR) << "error caught in symlink: " << err.what();
510 }
511 return res;
512 }
513
514 int encfs_link(const char *to, const char *from) {
515 EncFS_Context *ctx = context();
516
517 if (isReadOnly(ctx)) {
518 return -EROFS;
519 }
520
521 int res = -EIO;
522 std::shared_ptr<DirNode> FSRoot = ctx->getRoot(&res);
523 if (!FSRoot) {
524 return res;
525 }
526
527 try {
528 res = FSRoot->link(to, from);
529 } catch (encfs::Error &err) {
530 RLOG(ERROR) << "error caught in link: " << err.what();
531 }
532 return res;
533 }
534
535 int encfs_rename(const char *from, const char *to) {
536 EncFS_Context *ctx = context();
537
538 if (isReadOnly(ctx)) {
539 return -EROFS;
540 }
541
542 int res = -EIO;
543 std::shared_ptr<DirNode> FSRoot = ctx->getRoot(&res);
544 if (!FSRoot) {
545 return res;
546 }
547
548 try {
549 res = FSRoot->rename(from, to);
550 } catch (encfs::Error &err) {
551 RLOG(ERROR) << "error caught in rename: " << err.what();
552 }
553 return res;
554 }
555
556 int _do_chmod(EncFS_Context *, const string &cipherPath, mode_t mode) {
557 return chmod(cipherPath.c_str(), mode);
558 }
559
560 int encfs_chmod(const char *path, mode_t mode) {
561 EncFS_Context *ctx = context();
562 if (isReadOnly(ctx)) {
563 return -EROFS;
564 }
565 return withCipherPath("chmod", path, bind(_do_chmod, _1, _2, mode));
566 }
567
568 int _do_chown(EncFS_Context *, const string &cyName, uid_t u, gid_t g) {
569 int res = lchown(cyName.c_str(), u, g);
570 return (res == -1) ? -errno : ESUCCESS;
571 }
572
573 int encfs_chown(const char *path, uid_t uid, gid_t gid) {
574 EncFS_Context *ctx = context();
575 if (isReadOnly(ctx)) {
576 return -EROFS;
577 }
578 return withCipherPath("chown", path, bind(_do_chown, _1, _2, uid, gid));
579 }
580
581 int _do_truncate(FileNode *fnode, off_t size) { return fnode->truncate(size); }
582
583 int encfs_truncate(const char *path, off_t size) {
584 EncFS_Context *ctx = context();
585 if (isReadOnly(ctx)) {
586 return -EROFS;
587 }
588 return withFileNode("truncate", path, nullptr, bind(_do_truncate, _1, size));
589 }
590
591 int encfs_ftruncate(const char *path, off_t size, struct fuse_file_info *fi) {
592 EncFS_Context *ctx = context();
593 if (isReadOnly(ctx)) {
594 return -EROFS;
595 }
596 return withFileNode("ftruncate", path, fi, bind(_do_truncate, _1, size));
597 }
598
599 int _do_utime(EncFS_Context *, const string &cyName, struct utimbuf *buf) {
600 int res = utime(cyName.c_str(), buf);
601 return (res == -1) ? -errno : ESUCCESS;
602 }
603
604 int encfs_utime(const char *path, struct utimbuf *buf) {
605 EncFS_Context *ctx = context();
606 if (isReadOnly(ctx)) {
607 return -EROFS;
608 }
609 return withCipherPath("utime", path, bind(_do_utime, _1, _2, buf));
610 }
611
612 int _do_utimens(EncFS_Context *, const string &cyName,
613 const struct timespec ts[2]) {
614 #ifdef HAVE_UTIMENSAT
615 int res = utimensat(AT_FDCWD, cyName.c_str(), ts, AT_SYMLINK_NOFOLLOW);
616 #else
617 struct timeval tv[2];
618 tv[0].tv_sec = ts[0].tv_sec;
619 tv[0].tv_usec = ts[0].tv_nsec / 1000;
620 tv[1].tv_sec = ts[1].tv_sec;
621 tv[1].tv_usec = ts[1].tv_nsec / 1000;
622
623 int res = lutimes(cyName.c_str(), tv);
624 #endif
625 return (res == -1) ? -errno : ESUCCESS;
626 }
627
628 int encfs_utimens(const char *path, const struct timespec ts[2]) {
629 EncFS_Context *ctx = context();
630 if (isReadOnly(ctx)) {
631 return -EROFS;
632 }
633 return withCipherPath("utimens", path, bind(_do_utimens, _1, _2, ts));
634 }
635
636 int encfs_open(const char *path, struct fuse_file_info *file) {
637 EncFS_Context *ctx = context();
638
639 if (isReadOnly(ctx) &&
640 (((file->flags & O_WRONLY) != 0) || ((file->flags & O_RDWR) != 0))) {
641 return -EROFS;
642 }
643
644 int res = -EIO;
645 std::shared_ptr<DirNode> FSRoot = ctx->getRoot(&res);
646 if (!FSRoot) {
647 return res;
648 }
649
650 try {
651 std::shared_ptr<FileNode> fnode =
652 FSRoot->openNode(path, "open", file->flags, &res);
653
654 if (fnode) {
655 VLOG(1) << "encfs_open for " << fnode->cipherName() << ", flags "
656 << file->flags;
657
658 if (res >= 0) {
659 ctx->putNode(path, fnode);
660 file->fh = fnode->fuseFh;
661 res = ESUCCESS;
662 }
663 }
664 } catch (encfs::Error &err) {
665 RLOG(ERROR) << "error caught in open: " << err.what();
666 }
667
668 return res;
669 }
670
671 int encfs_create(const char *path, mode_t mode, struct fuse_file_info *file) {
672 int res = encfs_mknod(path, mode, 0);
673 if (res != 0) {
674 return res;
675 }
676
677 return encfs_open(path, file);
678 }
679
680 int _do_flush(FileNode *fnode) {
681 /* Flush can be called multiple times for an open file, so it doesn't
682 close the file. However it is important to call close() for some
683 underlying filesystems (like NFS).
684 */
685 int res = fnode->open(O_RDONLY);
686 if (res >= 0) {
687 int fh = res;
688 int nfh = dup(fh);
689 if (nfh == -1) {
690 return -errno;
691 }
692 res = close(nfh);
693 if (res == -1) {
694 return -errno;
695 }
696 }
697
698 return res;
699 }
700
701 // Called on each close() of a file descriptor
702 int encfs_flush(const char *path, struct fuse_file_info *fi) {
703 return withFileNode("flush", path, fi, bind(_do_flush, _1));
704 }
705
706 /*
707 Note: This is advisory -- it might benefit us to keep file nodes around for a
708 bit after they are released just in case they are reopened soon. But that
709 requires a cache layer.
710 */
711 int encfs_release(const char *path, struct fuse_file_info *finfo) {
712 EncFS_Context *ctx = context();
713
714 try {
715 auto fnode = ctx->lookupFuseFh(finfo->fh);
716 ctx->eraseNode(path, fnode);
717 return ESUCCESS;
718 } catch (encfs::Error &err) {
719 RLOG(ERROR) << "error caught in release: " << err.what();
720 return -EIO;
721 }
722 }
723
724 ssize_t _do_read(FileNode *fnode, unsigned char *ptr, size_t size, off_t off) {
725 return fnode->read(off, ptr, size);
726 }
727
728 int encfs_read(const char *path, char *buf, size_t size, off_t offset,
729 struct fuse_file_info *file) {
730 // Unfortunately we have to convert from ssize_t (pread) to int (fuse), so
731 // let's check this will be OK
732 if (size > std::numeric_limits<int>::max()) {
733 size = std::numeric_limits<int>::max();
734 }
735 return withFileNode("read", path, file,
736 bind(_do_read, _1, (unsigned char *)buf, size, offset));
737 }
738
739 int _do_fsync(FileNode *fnode, int dataSync) {
740 return fnode->sync(dataSync != 0);
741 }
742
743 int encfs_fsync(const char *path, int dataSync, struct fuse_file_info *file) {
744 EncFS_Context *ctx = context();
745 if (isReadOnly(ctx)) {
746 return -EROFS;
747 }
748 return withFileNode("fsync", path, file, bind(_do_fsync, _1, dataSync));
749 }
750
751 ssize_t _do_write(FileNode *fnode, unsigned char *ptr, size_t size,
752 off_t offset) {
753 return fnode->write(offset, ptr, size);
754 }
755
756 int encfs_write(const char *path, const char *buf, size_t size, off_t offset,
757 struct fuse_file_info *file) {
758 // Unfortunately we have to convert from ssize_t (pwrite) to int (fuse), so
759 // let's check this will be OK
760 if (size > std::numeric_limits<int>::max()) {
761 size = std::numeric_limits<int>::max();
762 }
763 EncFS_Context *ctx = context();
764 if (isReadOnly(ctx)) {
765 return -EROFS;
766 }
767 return withFileNode("write", path, file,
768 bind(_do_write, _1, (unsigned char *)buf, size, offset));
769 }
770
771 // statfs works even if encfs is detached..
772 int encfs_statfs(const char *path, struct statvfs *st) {
773 EncFS_Context *ctx = context();
774
775 int res = -EIO;
776 try {
777 (void)path; // path should always be '/' for now..
778 rAssert(st != nullptr);
779 string cyName = ctx->rootCipherDir;
780
781 VLOG(1) << "doing statfs of " << cyName;
782 res = statvfs(cyName.c_str(), st);
783 if (res == 0) {
784 // adjust maximum name length..
785 st->f_namemax = 6 * (st->f_namemax - 2) / 8; // approx..
786 }
787 if (res == -1) {
788 res = -errno;
789 }
790 } catch (encfs::Error &err) {
791 RLOG(ERROR) << "error caught in statfs: " << err.what();
792 }
793 return res;
794 }
795
796 #ifdef HAVE_XATTR
797
798 #ifdef XATTR_ADD_OPT
799 int _do_setxattr(EncFS_Context *, const string &cyName, const char *name,
800 const char *value, size_t size, uint32_t pos) {
801 int options = XATTR_NOFOLLOW;
802 return ::setxattr(cyName.c_str(), name, value, size, pos, options);
803 }
804 int encfs_setxattr(const char *path, const char *name, const char *value,
805 size_t size, int flags, uint32_t position) {
806 EncFS_Context *ctx = context();
807 if (isReadOnly(ctx)) {
808 return -EROFS;
809 }
810 (void)flags;
811 return withCipherPath("setxattr", path, bind(_do_setxattr, _1, _2, name,
812 value, size, position));
813 }
814 #else
815 int _do_setxattr(EncFS_Context *, const string &cyName, const char *name,
816 const char *value, size_t size, int flags) {
817 return ::lsetxattr(cyName.c_str(), name, value, size, flags);
818 }
819 int encfs_setxattr(const char *path, const char *name, const char *value,
820 size_t size, int flags) {
821 EncFS_Context *ctx = context();
822 if (isReadOnly(ctx)) {
823 return -EROFS;
824 }
825 return withCipherPath("setxattr", path,
826 bind(_do_setxattr, _1, _2, name, value, size, flags));
827 }
828 #endif
829
830 #ifdef XATTR_ADD_OPT
831 int _do_getxattr(EncFS_Context *, const string &cyName, const char *name,
832 void *value, size_t size, uint32_t pos) {
833 int options = XATTR_NOFOLLOW;
834 return ::getxattr(cyName.c_str(), name, value, size, pos, options);
835 }
836 int encfs_getxattr(const char *path, const char *name, char *value, size_t size,
837 uint32_t position) {
838 return withCipherPath(
839 "getxattr", path,
840 bind(_do_getxattr, _1, _2, name, (void *)value, size, position), true);
841 }
842 #else
843 int _do_getxattr(EncFS_Context *, const string &cyName, const char *name,
844 void *value, size_t size) {
845 return ::lgetxattr(cyName.c_str(), name, value, size);
846 }
847 int encfs_getxattr(const char *path, const char *name, char *value,
848 size_t size) {
849 return withCipherPath("getxattr", path,
850 bind(_do_getxattr, _1, _2, name, (void *)value, size),
851 true);
852 }
853 #endif
854
855 int _do_listxattr(EncFS_Context *, const string &cyName, char *list,
856 size_t size) {
857 #ifdef XATTR_ADD_OPT
858 int options = XATTR_NOFOLLOW;
859 int res = ::listxattr(cyName.c_str(), list, size, options);
860 #else
861 int res = ::llistxattr(cyName.c_str(), list, size);
862 #endif
863 return (res == -1) ? -errno : res;
864 }
865
866 int encfs_listxattr(const char *path, char *list, size_t size) {
867 return withCipherPath("listxattr", path,
868 bind(_do_listxattr, _1, _2, list, size), true);
869 }
870
871 int _do_removexattr(EncFS_Context *, const string &cyName, const char *name) {
872 #ifdef XATTR_ADD_OPT
873 int options = XATTR_NOFOLLOW;
874 int res = ::removexattr(cyName.c_str(), name, options);
875 #else
876 int res = ::lremovexattr(cyName.c_str(), name);
877 #endif
878 return (res == -1) ? -errno : res;
879 }
880
881 int encfs_removexattr(const char *path, const char *name) {
882 EncFS_Context *ctx = context();
883 if (isReadOnly(ctx)) {
884 return -EROFS;
885 }
886
887 return withCipherPath("removexattr", path,
888 bind(_do_removexattr, _1, _2, name));
889 }
890
891 #endif // HAVE_XATTR
892
893 } // namespace encfs