"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