2013-01-29 04:07:54 +01:00
|
|
|
/*****************************************************************************
|
|
|
|
* Author: Valient Gough <vgough@pobox.com>
|
|
|
|
*
|
|
|
|
*****************************************************************************
|
|
|
|
* Copyright (c) 2003-2007, Valient Gough
|
2013-10-20 00:35:26 +02:00
|
|
|
*
|
|
|
|
* This program is free software; you can distribute it and/or modify it under
|
2013-01-29 04:07:54 +01:00
|
|
|
* the terms of the GNU General Public License (GPL), as published by the Free
|
|
|
|
* Software Foundation; either version 2 of the License, or (at your option)
|
|
|
|
* any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
|
|
* more details.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "fs/encfs.h"
|
|
|
|
|
|
|
|
#include <cstdio>
|
|
|
|
#include <cstring>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <dirent.h>
|
|
|
|
#include <cerrno>
|
|
|
|
#include <sys/statvfs.h>
|
|
|
|
#include <sys/time.h>
|
|
|
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
#ifdef linux
|
|
|
|
#include <sys/fsuid.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef HAVE_ATTR_XATTR_H
|
|
|
|
#include <attr/xattr.h>
|
|
|
|
#elif defined(HAVE_SYS_XATTR_H)
|
|
|
|
#include <sys/xattr.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
#include <string>
|
|
|
|
#include <map>
|
|
|
|
|
|
|
|
#ifdef HAVE_TR1_TUPLE
|
|
|
|
#include <tr1/tuple>
|
2013-03-05 07:39:51 +01:00
|
|
|
using std::tr1::get;
|
|
|
|
using std::tr1::make_tuple;
|
|
|
|
using std::tr1::tuple;
|
2013-01-29 04:07:54 +01:00
|
|
|
#else
|
|
|
|
#include <tuple>
|
2013-03-05 07:39:51 +01:00
|
|
|
using std::get;
|
|
|
|
using std::make_tuple;
|
|
|
|
using std::tuple;
|
2013-01-29 04:07:54 +01:00
|
|
|
#endif
|
|
|
|
|
2013-03-05 07:38:00 +01:00
|
|
|
#include "base/config.h"
|
2013-01-29 04:07:54 +01:00
|
|
|
#include "base/shared_ptr.h"
|
|
|
|
#include "base/Mutex.h"
|
|
|
|
#include "base/Error.h"
|
|
|
|
#include "cipher/MemoryPool.h"
|
|
|
|
#include "fs/DirNode.h"
|
|
|
|
#include "fs/FileUtils.h"
|
|
|
|
#include "fs/Context.h"
|
|
|
|
|
|
|
|
#include <glog/logging.h>
|
|
|
|
|
2013-03-05 07:39:51 +01:00
|
|
|
using std::map;
|
|
|
|
using std::string;
|
|
|
|
using std::vector;
|
|
|
|
|
2013-03-05 07:29:58 +01:00
|
|
|
namespace encfs {
|
|
|
|
|
2013-01-29 04:07:54 +01:00
|
|
|
#ifndef MIN
|
2013-10-20 00:35:26 +02:00
|
|
|
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
|
2013-01-29 04:07:54 +01:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#define ESUCCESS 0
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
#define GET_FN(ctx, finfo) ctx->getNode((void *)(uintptr_t)finfo->fh)
|
2013-01-29 04:07:54 +01:00
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
static EncFS_Context *context() {
|
2013-10-21 07:38:54 +02:00
|
|
|
return static_cast<EncFS_Context *>(fuse_get_context()->private_data);
|
2013-01-29 04:07:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
The log messages below always print out encrypted filenames, not
|
|
|
|
plaintext. The reason is so that it isn't possible to leak information
|
|
|
|
about the encrypted data through logging interfaces.
|
|
|
|
|
|
|
|
|
|
|
|
The purpose of this layer of code is to take the FUSE request and dispatch
|
|
|
|
to the internal interfaces. Any marshaling of arguments and return types
|
|
|
|
can be done here.
|
|
|
|
*/
|
|
|
|
|
|
|
|
// helper function -- apply a functor to a cipher path, given the plain path
|
2013-10-20 00:35:26 +02:00
|
|
|
template <typename T>
|
|
|
|
static int withCipherPath(const char *opName, const char *path,
|
|
|
|
int (*op)(EncFS_Context *, const string &name,
|
|
|
|
T data),
|
|
|
|
T data, bool passReturnCode = false) {
|
2013-01-29 04:07:54 +01:00
|
|
|
EncFS_Context *ctx = context();
|
|
|
|
|
|
|
|
int res = -EIO;
|
|
|
|
shared_ptr<DirNode> FSRoot = ctx->getRoot(&res);
|
2013-10-20 00:35:26 +02:00
|
|
|
if (!FSRoot) return res;
|
2013-01-29 04:07:54 +01:00
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
try {
|
|
|
|
string cyName = FSRoot->cipherPath(path);
|
2013-01-29 04:07:54 +01:00
|
|
|
VLOG(1) << opName << " " << cyName.c_str();
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
res = op(ctx, cyName, data);
|
2013-01-29 04:07:54 +01:00
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
if (res == -1) {
|
2013-01-29 04:07:54 +01:00
|
|
|
res = -errno;
|
|
|
|
LOG(INFO) << opName << " error: " << strerror(-res);
|
2013-10-20 00:35:26 +02:00
|
|
|
} else if (!passReturnCode)
|
2013-01-29 04:07:54 +01:00
|
|
|
res = ESUCCESS;
|
2013-10-20 00:35:26 +02:00
|
|
|
}
|
|
|
|
catch (Error &err) {
|
|
|
|
LOG(ERROR) << "error caught in " << opName << ":" << err.what();
|
2013-01-29 04:07:54 +01:00
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
// helper function -- apply a functor to a node
|
2013-10-20 00:35:26 +02:00
|
|
|
template <typename T>
|
|
|
|
static int withFileNode(const char *opName, const char *path,
|
|
|
|
struct fuse_file_info *fi,
|
|
|
|
int (*op)(FileNode *, T data), T data) {
|
2013-01-29 04:07:54 +01:00
|
|
|
EncFS_Context *ctx = context();
|
|
|
|
|
|
|
|
int res = -EIO;
|
|
|
|
shared_ptr<DirNode> FSRoot = ctx->getRoot(&res);
|
2013-10-20 00:35:26 +02:00
|
|
|
if (!FSRoot) return res;
|
2013-01-29 04:07:54 +01:00
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
try {
|
2013-01-29 04:07:54 +01:00
|
|
|
shared_ptr<FileNode> fnode;
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
if (fi != NULL)
|
2013-01-29 04:07:54 +01:00
|
|
|
fnode = GET_FN(ctx, fi);
|
|
|
|
else
|
2013-10-20 00:35:26 +02:00
|
|
|
fnode = FSRoot->lookupNode(path, opName);
|
2013-01-29 04:07:54 +01:00
|
|
|
|
|
|
|
rAssert(fnode != NULL);
|
|
|
|
VLOG(1) << opName << " " << fnode->cipherName();
|
2013-10-20 00:35:26 +02:00
|
|
|
res = op(fnode.get(), data);
|
2013-01-29 04:07:54 +01:00
|
|
|
|
|
|
|
LOG_IF(INFO, res < 0) << opName << " error: " << strerror(-res);
|
2013-10-20 00:35:26 +02:00
|
|
|
}
|
|
|
|
catch (Error &err) {
|
|
|
|
LOG(ERROR) << "error caught in " << opName << ":" << err.what();
|
2013-01-29 04:07:54 +01:00
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
int _do_getattr(FileNode *fnode, struct stat *stbuf) {
|
2013-01-29 04:07:54 +01:00
|
|
|
int res = fnode->getAttr(stbuf);
|
2013-10-20 00:35:26 +02:00
|
|
|
if (res == ESUCCESS && S_ISLNK(stbuf->st_mode)) {
|
2013-01-29 04:07:54 +01:00
|
|
|
EncFS_Context *ctx = context();
|
|
|
|
shared_ptr<DirNode> FSRoot = ctx->getRoot(&res);
|
2013-10-20 00:35:26 +02:00
|
|
|
if (FSRoot) {
|
2013-01-29 04:07:54 +01:00
|
|
|
// determine plaintext link size.. Easiest to read and decrypt..
|
2013-10-20 00:35:26 +02:00
|
|
|
vector<char> buf(stbuf->st_size + 1, 0);
|
2013-01-29 04:07:54 +01:00
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
res = ::readlink(fnode->cipherName(), &buf[0], stbuf->st_size);
|
|
|
|
if (res >= 0) {
|
2013-01-29 04:07:54 +01:00
|
|
|
// other functions expect c-strings to be null-terminated, which
|
|
|
|
// readlink doesn't provide
|
|
|
|
buf[res] = '\0';
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
stbuf->st_size = FSRoot->plainPath(&buf[0]).length();
|
2013-01-29 04:07:54 +01:00
|
|
|
|
|
|
|
res = ESUCCESS;
|
|
|
|
} else
|
|
|
|
res = -errno;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
int encfs_getattr(const char *path, struct stat *stbuf) {
|
|
|
|
return withFileNode("getattr", path, NULL, _do_getattr, stbuf);
|
2013-01-29 04:07:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
int encfs_fgetattr(const char *path, struct stat *stbuf,
|
2013-10-20 00:35:26 +02:00
|
|
|
struct fuse_file_info *fi) {
|
|
|
|
return withFileNode("fgetattr", path, fi, _do_getattr, stbuf);
|
2013-01-29 04:07:54 +01:00
|
|
|
}
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
int encfs_getdir(const char *path, fuse_dirh_t h, fuse_dirfil_t filler) {
|
2013-01-29 04:07:54 +01:00
|
|
|
EncFS_Context *ctx = context();
|
|
|
|
|
|
|
|
int res = ESUCCESS;
|
|
|
|
shared_ptr<DirNode> FSRoot = ctx->getRoot(&res);
|
2013-10-20 00:35:26 +02:00
|
|
|
if (!FSRoot) return res;
|
2013-01-29 04:07:54 +01:00
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
try {
|
2013-01-29 04:07:54 +01:00
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
DirTraverse dt = FSRoot->openDir(path);
|
2013-01-29 04:07:54 +01:00
|
|
|
|
|
|
|
VLOG(1) << "getdir on " << FSRoot->cipherPath(path);
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
if (dt.valid()) {
|
2013-01-29 04:07:54 +01:00
|
|
|
int fileType = 0;
|
|
|
|
ino_t inode = 0;
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
std::string name = dt.nextPlaintextName(&fileType, &inode);
|
|
|
|
while (!name.empty()) {
|
|
|
|
res = filler(h, name.c_str(), fileType, inode);
|
2013-01-29 04:07:54 +01:00
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
if (res != ESUCCESS) break;
|
2013-01-29 04:07:54 +01:00
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
name = dt.nextPlaintextName(&fileType, &inode);
|
2013-01-29 04:07:54 +01:00
|
|
|
}
|
2013-10-20 00:35:26 +02:00
|
|
|
} else {
|
2013-01-29 04:07:54 +01:00
|
|
|
LOG(INFO) << "getdir request invalid, path: '" << path << "'";
|
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
2013-10-20 00:35:26 +02:00
|
|
|
}
|
|
|
|
catch (Error &err) {
|
2013-01-29 04:07:54 +01:00
|
|
|
LOG(ERROR) << "error caught in getdir: " << err.what();
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
int encfs_mknod(const char *path, mode_t mode, dev_t rdev) {
|
2013-01-29 04:07:54 +01:00
|
|
|
EncFS_Context *ctx = context();
|
|
|
|
|
|
|
|
int res = -EIO;
|
|
|
|
shared_ptr<DirNode> FSRoot = ctx->getRoot(&res);
|
2013-10-20 00:35:26 +02:00
|
|
|
if (!FSRoot) return res;
|
2013-01-29 04:07:54 +01:00
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
try {
|
|
|
|
shared_ptr<FileNode> fnode = FSRoot->lookupNode(path, "mknod");
|
2013-01-29 04:07:54 +01:00
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
VLOG(1) << "mknod on " << fnode->cipherName() << ", mode " << mode
|
|
|
|
<< ", dev " << rdev;
|
2013-01-29 04:07:54 +01:00
|
|
|
|
|
|
|
uid_t uid = 0;
|
|
|
|
gid_t gid = 0;
|
2013-10-20 00:35:26 +02:00
|
|
|
if (ctx->publicFilesystem) {
|
2013-01-29 04:07:54 +01:00
|
|
|
fuse_context *context = fuse_get_context();
|
|
|
|
uid = context->uid;
|
|
|
|
gid = context->gid;
|
|
|
|
}
|
2013-10-20 00:35:26 +02:00
|
|
|
res = fnode->mknod(mode, rdev, uid, gid);
|
2013-01-29 04:07:54 +01:00
|
|
|
// Is this error due to access problems?
|
2013-10-20 00:35:26 +02:00
|
|
|
if (ctx->publicFilesystem && -res == EACCES) {
|
2013-01-29 04:07:54 +01:00
|
|
|
// try again using the parent dir's group
|
|
|
|
string parent = fnode->plaintextParent();
|
2013-10-20 00:35:26 +02:00
|
|
|
LOG(INFO) << "attempting public filesystem workaround for "
|
2013-01-29 04:07:54 +01:00
|
|
|
<< parent.c_str();
|
2013-10-20 00:35:26 +02:00
|
|
|
shared_ptr<FileNode> dnode = FSRoot->lookupNode(parent.c_str(), "mknod");
|
2013-01-29 04:07:54 +01:00
|
|
|
|
|
|
|
struct stat st;
|
2013-10-20 00:35:26 +02:00
|
|
|
if (dnode->getAttr(&st) == 0)
|
|
|
|
res = fnode->mknod(mode, rdev, uid, st.st_gid);
|
2013-01-29 04:07:54 +01:00
|
|
|
}
|
2013-10-20 00:35:26 +02:00
|
|
|
}
|
|
|
|
catch (Error &err) {
|
2013-01-29 04:07:54 +01:00
|
|
|
LOG(ERROR) << "error caught in mknod: " << err.what();
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
int encfs_mkdir(const char *path, mode_t mode) {
|
2013-01-29 04:07:54 +01:00
|
|
|
fuse_context *fctx = fuse_get_context();
|
|
|
|
EncFS_Context *ctx = context();
|
|
|
|
|
|
|
|
int res = -EIO;
|
|
|
|
shared_ptr<DirNode> FSRoot = ctx->getRoot(&res);
|
2013-10-20 00:35:26 +02:00
|
|
|
if (!FSRoot) return res;
|
2013-01-29 04:07:54 +01:00
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
try {
|
2013-01-29 04:07:54 +01:00
|
|
|
uid_t uid = 0;
|
|
|
|
gid_t gid = 0;
|
2013-10-20 00:35:26 +02:00
|
|
|
if (ctx->publicFilesystem) {
|
2013-01-29 04:07:54 +01:00
|
|
|
uid = fctx->uid;
|
|
|
|
gid = fctx->gid;
|
|
|
|
}
|
2013-10-20 00:35:26 +02:00
|
|
|
res = FSRoot->mkdir(path, mode, uid, gid);
|
2013-01-29 04:07:54 +01:00
|
|
|
// Is this error due to access problems?
|
2013-10-20 00:35:26 +02:00
|
|
|
if (ctx->publicFilesystem && -res == EACCES) {
|
2013-01-29 04:07:54 +01:00
|
|
|
// try again using the parent dir's group
|
2013-10-20 00:35:26 +02:00
|
|
|
string parent = parentDirectory(path);
|
|
|
|
shared_ptr<FileNode> dnode = FSRoot->lookupNode(parent.c_str(), "mkdir");
|
2013-01-29 04:07:54 +01:00
|
|
|
|
|
|
|
struct stat st;
|
2013-10-20 00:35:26 +02:00
|
|
|
if (dnode->getAttr(&st) == 0)
|
|
|
|
res = FSRoot->mkdir(path, mode, uid, st.st_gid);
|
2013-01-29 04:07:54 +01:00
|
|
|
}
|
2013-10-20 00:35:26 +02:00
|
|
|
}
|
|
|
|
catch (Error &err) {
|
2013-01-29 04:07:54 +01:00
|
|
|
LOG(ERROR) << "error caught in mkdir: " << err.what();
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
int encfs_unlink(const char *path) {
|
2013-01-29 04:07:54 +01:00
|
|
|
EncFS_Context *ctx = context();
|
|
|
|
|
|
|
|
int res = -EIO;
|
|
|
|
shared_ptr<DirNode> FSRoot = ctx->getRoot(&res);
|
2013-10-20 00:35:26 +02:00
|
|
|
if (!FSRoot) return res;
|
2013-01-29 04:07:54 +01:00
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
try {
|
2013-01-29 04:07:54 +01:00
|
|
|
// let DirNode handle it atomically so that it can handle race
|
|
|
|
// conditions
|
2013-10-20 00:35:26 +02:00
|
|
|
res = FSRoot->unlink(path);
|
|
|
|
}
|
|
|
|
catch (Error &err) {
|
2013-01-29 04:07:54 +01:00
|
|
|
LOG(ERROR) << "error caught in unlink: " << err.what();
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
int _do_rmdir(EncFS_Context *, const string &cipherPath, int) {
|
|
|
|
return rmdir(cipherPath.c_str());
|
2013-01-29 04:07:54 +01:00
|
|
|
}
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
int encfs_rmdir(const char *path) {
|
|
|
|
return withCipherPath("rmdir", path, _do_rmdir, 0);
|
2013-01-29 04:07:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
int _do_readlink(EncFS_Context *ctx, const string &cyName,
|
2013-10-20 00:35:26 +02:00
|
|
|
tuple<char *, size_t> data) {
|
2013-01-29 04:07:54 +01:00
|
|
|
char *buf = get<0>(data);
|
|
|
|
size_t size = get<1>(data);
|
|
|
|
|
|
|
|
int res = ESUCCESS;
|
|
|
|
shared_ptr<DirNode> FSRoot = ctx->getRoot(&res);
|
2013-10-20 00:35:26 +02:00
|
|
|
if (!FSRoot) return res;
|
2013-01-29 04:07:54 +01:00
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
res = ::readlink(cyName.c_str(), buf, size - 1);
|
2013-01-29 04:07:54 +01:00
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
if (res == -1) return -errno;
|
2013-01-29 04:07:54 +01:00
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
buf[res] = '\0'; // ensure null termination
|
2013-01-29 04:07:54 +01:00
|
|
|
string decodedName;
|
2013-10-20 00:35:26 +02:00
|
|
|
try {
|
|
|
|
decodedName = FSRoot->plainPath(buf);
|
|
|
|
}
|
|
|
|
catch (...) {
|
|
|
|
}
|
2013-01-29 04:07:54 +01:00
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
if (!decodedName.empty()) {
|
|
|
|
strncpy(buf, decodedName.c_str(), size - 1);
|
|
|
|
buf[size - 1] = '\0';
|
2013-01-29 04:07:54 +01:00
|
|
|
|
|
|
|
return ESUCCESS;
|
2013-10-20 00:35:26 +02:00
|
|
|
} else {
|
2013-01-29 04:07:54 +01:00
|
|
|
LOG(WARNING) << "Error decoding link";
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
int encfs_readlink(const char *path, char *buf, size_t size) {
|
|
|
|
return withCipherPath("readlink", path, _do_readlink, make_tuple(buf, size));
|
2013-01-29 04:07:54 +01:00
|
|
|
}
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
int encfs_symlink(const char *from, const char *to) {
|
2013-01-29 04:07:54 +01:00
|
|
|
EncFS_Context *ctx = context();
|
|
|
|
|
|
|
|
int res = -EIO;
|
|
|
|
shared_ptr<DirNode> FSRoot = ctx->getRoot(&res);
|
2013-10-20 00:35:26 +02:00
|
|
|
if (!FSRoot) return res;
|
2013-01-29 04:07:54 +01:00
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
try {
|
2013-01-29 04:07:54 +01:00
|
|
|
// allow fully qualified names in symbolic links.
|
2013-10-20 00:35:26 +02:00
|
|
|
string fromCName = FSRoot->relativeCipherPath(from);
|
|
|
|
string toCName = FSRoot->cipherPath(to);
|
2013-01-29 04:07:54 +01:00
|
|
|
|
|
|
|
VLOG(1) << "symlink " << fromCName << " -> " << toCName;
|
|
|
|
|
|
|
|
// use setfsuid / setfsgid so that the new link will be owned by the
|
|
|
|
// uid/gid provided by the fuse_context.
|
|
|
|
int olduid = -1;
|
|
|
|
int oldgid = -1;
|
2013-10-20 00:35:26 +02:00
|
|
|
if (ctx->publicFilesystem) {
|
2013-01-29 04:07:54 +01:00
|
|
|
fuse_context *context = fuse_get_context();
|
2013-10-20 00:35:26 +02:00
|
|
|
olduid = setfsuid(context->uid);
|
|
|
|
oldgid = setfsgid(context->gid);
|
2013-01-29 04:07:54 +01:00
|
|
|
}
|
2013-10-20 00:35:26 +02:00
|
|
|
res = ::symlink(fromCName.c_str(), toCName.c_str());
|
|
|
|
if (olduid >= 0) setfsuid(olduid);
|
|
|
|
if (oldgid >= 0) setfsgid(oldgid);
|
2013-01-29 04:07:54 +01:00
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
if (res == -1)
|
2013-01-29 04:07:54 +01:00
|
|
|
res = -errno;
|
|
|
|
else
|
|
|
|
res = ESUCCESS;
|
2013-10-20 00:35:26 +02:00
|
|
|
}
|
|
|
|
catch (Error &err) {
|
2013-01-29 04:07:54 +01:00
|
|
|
LOG(ERROR) << "error caught in symlink: " << err.what();
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
int encfs_link(const char *from, const char *to) {
|
2013-01-29 04:07:54 +01:00
|
|
|
EncFS_Context *ctx = context();
|
|
|
|
|
|
|
|
int res = -EIO;
|
|
|
|
shared_ptr<DirNode> FSRoot = ctx->getRoot(&res);
|
2013-10-20 00:35:26 +02:00
|
|
|
if (!FSRoot) return res;
|
2013-01-29 04:07:54 +01:00
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
try {
|
|
|
|
res = FSRoot->link(from, to);
|
|
|
|
}
|
|
|
|
catch (Error &err) {
|
2013-01-29 04:07:54 +01:00
|
|
|
LOG(ERROR) << "error caught in link: " << err.what();
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
int encfs_rename(const char *from, const char *to) {
|
2013-01-29 04:07:54 +01:00
|
|
|
EncFS_Context *ctx = context();
|
|
|
|
|
|
|
|
int res = -EIO;
|
|
|
|
shared_ptr<DirNode> FSRoot = ctx->getRoot(&res);
|
2013-10-20 00:35:26 +02:00
|
|
|
if (!FSRoot) return res;
|
2013-01-29 04:07:54 +01:00
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
try {
|
|
|
|
res = FSRoot->rename(from, to);
|
|
|
|
}
|
|
|
|
catch (Error &err) {
|
2013-01-29 04:07:54 +01:00
|
|
|
LOG(ERROR) << "error caught in rename: " << err.what();
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
int _do_chmod(EncFS_Context *, const string &cipherPath, mode_t mode) {
|
2013-03-05 07:38:00 +01:00
|
|
|
#ifdef HAVE_LCHMOD
|
2013-10-20 00:35:26 +02:00
|
|
|
return lchmod(cipherPath.c_str(), mode);
|
2013-03-05 07:38:00 +01:00
|
|
|
#else
|
2013-10-20 00:35:26 +02:00
|
|
|
return chmod(cipherPath.c_str(), mode);
|
2013-03-05 07:38:00 +01:00
|
|
|
#endif
|
2013-01-29 04:07:54 +01:00
|
|
|
}
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
int encfs_chmod(const char *path, mode_t mode) {
|
|
|
|
return withCipherPath("chmod", path, _do_chmod, mode);
|
2013-01-29 04:07:54 +01:00
|
|
|
}
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
int _do_chown(EncFS_Context *, const string &cyName, tuple<uid_t, gid_t> data) {
|
|
|
|
int res = lchown(cyName.c_str(), get<0>(data), get<1>(data));
|
2013-01-29 04:07:54 +01:00
|
|
|
return (res == -1) ? -errno : ESUCCESS;
|
|
|
|
}
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
int encfs_chown(const char *path, uid_t uid, gid_t gid) {
|
|
|
|
return withCipherPath("chown", path, _do_chown, make_tuple(uid, gid));
|
2013-01-29 04:07:54 +01:00
|
|
|
}
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
int _do_truncate(FileNode *fnode, off_t size) { return fnode->truncate(size); }
|
2013-01-29 04:07:54 +01:00
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
int encfs_truncate(const char *path, off_t size) {
|
|
|
|
return withFileNode("truncate", path, NULL, _do_truncate, size);
|
2013-01-29 04:07:54 +01:00
|
|
|
}
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
int encfs_ftruncate(const char *path, off_t size, struct fuse_file_info *fi) {
|
|
|
|
return withFileNode("ftruncate", path, fi, _do_truncate, size);
|
2013-01-29 04:07:54 +01:00
|
|
|
}
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
int _do_utime(EncFS_Context *, const string &cyName, struct utimbuf *buf) {
|
|
|
|
int res = utime(cyName.c_str(), buf);
|
2013-01-29 04:07:54 +01:00
|
|
|
return (res == -1) ? -errno : ESUCCESS;
|
|
|
|
}
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
int encfs_utime(const char *path, struct utimbuf *buf) {
|
|
|
|
return withCipherPath("utime", path, _do_utime, buf);
|
2013-01-29 04:07:54 +01:00
|
|
|
}
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
int _do_utimens(EncFS_Context *, const string &cyName,
|
|
|
|
const struct timespec ts[2]) {
|
2013-01-29 04:07:54 +01:00
|
|
|
struct timeval tv[2];
|
|
|
|
tv[0].tv_sec = ts[0].tv_sec;
|
|
|
|
tv[0].tv_usec = ts[0].tv_nsec / 1000;
|
|
|
|
tv[1].tv_sec = ts[1].tv_sec;
|
|
|
|
tv[1].tv_usec = ts[1].tv_nsec / 1000;
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
int res = lutimes(cyName.c_str(), tv);
|
2013-01-29 04:07:54 +01:00
|
|
|
return (res == -1) ? -errno : ESUCCESS;
|
|
|
|
}
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
int encfs_utimens(const char *path, const struct timespec ts[2]) {
|
|
|
|
return withCipherPath("utimens", path, _do_utimens, ts);
|
2013-01-29 04:07:54 +01:00
|
|
|
}
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
int encfs_open(const char *path, struct fuse_file_info *file) {
|
2013-01-29 04:07:54 +01:00
|
|
|
EncFS_Context *ctx = context();
|
|
|
|
|
|
|
|
int res = -EIO;
|
|
|
|
shared_ptr<DirNode> FSRoot = ctx->getRoot(&res);
|
2013-10-20 00:35:26 +02:00
|
|
|
if (!FSRoot) return res;
|
2013-01-29 04:07:54 +01:00
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
try {
|
|
|
|
shared_ptr<FileNode> fnode =
|
|
|
|
FSRoot->openNode(path, "open", file->flags, &res);
|
2013-01-29 04:07:54 +01:00
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
if (fnode) {
|
|
|
|
VLOG(1) << "encfs_open for " << fnode->cipherName() << ", flags "
|
|
|
|
<< file->flags;
|
2013-01-29 04:07:54 +01:00
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
if (res >= 0) {
|
2013-01-29 04:07:54 +01:00
|
|
|
file->fh = (uintptr_t)ctx->putNode(path, fnode);
|
|
|
|
res = ESUCCESS;
|
|
|
|
}
|
|
|
|
}
|
2013-10-20 00:35:26 +02:00
|
|
|
}
|
|
|
|
catch (Error &err) {
|
2013-01-29 04:07:54 +01:00
|
|
|
LOG(ERROR) << "error caught in open: " << err.what();
|
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
int _do_flush(FileNode *fnode, int) {
|
2013-01-29 04:07:54 +01:00
|
|
|
/* Flush can be called multiple times for an open file, so it doesn't
|
|
|
|
close the file. However it is important to call close() for some
|
|
|
|
underlying filesystems (like NFS).
|
|
|
|
*/
|
2013-10-20 00:35:26 +02:00
|
|
|
int res = fnode->open(O_RDONLY);
|
|
|
|
if (res >= 0) {
|
2013-01-29 04:07:54 +01:00
|
|
|
int fh = res;
|
|
|
|
res = close(dup(fh));
|
2013-10-20 00:35:26 +02:00
|
|
|
if (res == -1) res = -errno;
|
2013-01-29 04:07:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
int encfs_flush(const char *path, struct fuse_file_info *fi) {
|
|
|
|
return withFileNode("flush", path, fi, _do_flush, 0);
|
2013-01-29 04:07:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Note: This is advisory -- it might benefit us to keep file nodes around for a
|
|
|
|
bit after they are released just in case they are reopened soon. But that
|
|
|
|
requires a cache layer.
|
|
|
|
*/
|
2013-10-20 00:35:26 +02:00
|
|
|
int encfs_release(const char *path, struct fuse_file_info *finfo) {
|
2013-01-29 04:07:54 +01:00
|
|
|
EncFS_Context *ctx = context();
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
try {
|
|
|
|
ctx->eraseNode(path, (void *)(uintptr_t)finfo->fh);
|
2013-01-29 04:07:54 +01:00
|
|
|
return ESUCCESS;
|
2013-10-20 00:35:26 +02:00
|
|
|
}
|
|
|
|
catch (Error &err) {
|
2013-01-29 04:07:54 +01:00
|
|
|
LOG(ERROR) << "error caught in release: " << err.what();
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
int _do_read(FileNode *fnode, tuple<unsigned char *, size_t, off_t> data) {
|
|
|
|
return fnode->read(get<2>(data), get<0>(data), get<1>(data));
|
2013-01-29 04:07:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
int encfs_read(const char *path, char *buf, size_t size, off_t offset,
|
2013-10-20 00:35:26 +02:00
|
|
|
struct fuse_file_info *file) {
|
|
|
|
return withFileNode("read", path, file, _do_read,
|
|
|
|
make_tuple((unsigned char *)buf, size, offset));
|
2013-01-29 04:07:54 +01:00
|
|
|
}
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
int _do_fsync(FileNode *fnode, int dataSync) {
|
|
|
|
return fnode->sync(dataSync != 0);
|
2013-01-29 04:07:54 +01:00
|
|
|
}
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
int encfs_fsync(const char *path, int dataSync, struct fuse_file_info *file) {
|
|
|
|
return withFileNode("fsync", path, file, _do_fsync, dataSync);
|
2013-01-29 04:07:54 +01:00
|
|
|
}
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
int _do_write(FileNode *fnode, tuple<const char *, size_t, off_t> data) {
|
2013-01-29 04:07:54 +01:00
|
|
|
size_t size = get<1>(data);
|
2013-10-20 00:35:26 +02:00
|
|
|
if (fnode->write(get<2>(data), (unsigned char *)get<0>(data), size))
|
2013-01-29 04:07:54 +01:00
|
|
|
return size;
|
|
|
|
else
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
int encfs_write(const char *path, const char *buf, size_t size, off_t offset,
|
|
|
|
struct fuse_file_info *file) {
|
2013-01-29 04:07:54 +01:00
|
|
|
return withFileNode("write", path, file, _do_write,
|
2013-10-20 00:35:26 +02:00
|
|
|
make_tuple(buf, size, offset));
|
2013-01-29 04:07:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// statfs works even if encfs is detached..
|
2013-10-20 00:35:26 +02:00
|
|
|
int encfs_statfs(const char *path, struct statvfs *st) {
|
2013-01-29 04:07:54 +01:00
|
|
|
EncFS_Context *ctx = context();
|
|
|
|
|
|
|
|
int res = -EIO;
|
2013-10-20 00:35:26 +02:00
|
|
|
try {
|
|
|
|
(void)path; // path should always be '/' for now..
|
|
|
|
rAssert(st != NULL);
|
2013-01-29 04:07:54 +01:00
|
|
|
string cyName = ctx->rootCipherDir;
|
|
|
|
|
|
|
|
VLOG(1) << "doing statfs of " << cyName;
|
2013-10-20 00:35:26 +02:00
|
|
|
res = statvfs(cyName.c_str(), st);
|
|
|
|
if (!res) {
|
2013-01-29 04:07:54 +01:00
|
|
|
// adjust maximum name length..
|
2013-10-20 00:35:26 +02:00
|
|
|
st->f_namemax = 6 * (st->f_namemax - 2) / 8; // approx..
|
2013-01-29 04:07:54 +01:00
|
|
|
}
|
2013-10-20 00:35:26 +02:00
|
|
|
if (res == -1) res = -errno;
|
|
|
|
}
|
|
|
|
catch (Error &err) {
|
2013-01-29 04:07:54 +01:00
|
|
|
LOG(ERROR) << "error caught in statfs: " << err.what();
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef HAVE_XATTR
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef XATTR_ADD_OPT
|
2013-10-20 00:35:26 +02:00
|
|
|
int _do_setxattr(EncFS_Context *, const string &cyName,
|
|
|
|
tuple<const char *, const char *, size_t, uint32_t> data) {
|
2013-03-05 07:38:00 +01:00
|
|
|
int options = XATTR_NOFOLLOW;
|
2013-10-20 00:35:26 +02:00
|
|
|
return ::setxattr(cyName.c_str(), get<0>(data), get<1>(data), get<2>(data),
|
|
|
|
get<3>(data), options);
|
2013-01-29 04:07:54 +01:00
|
|
|
}
|
2013-10-20 00:35:26 +02:00
|
|
|
int encfs_setxattr(const char *path, const char *name, const char *value,
|
|
|
|
size_t size, int flags, uint32_t position) {
|
2013-01-29 04:07:54 +01:00
|
|
|
(void)flags;
|
2013-10-20 00:35:26 +02:00
|
|
|
return withCipherPath("setxattr", path, _do_setxattr,
|
|
|
|
make_tuple(name, value, size, position));
|
2013-01-29 04:07:54 +01:00
|
|
|
}
|
|
|
|
#else
|
2013-10-20 00:35:26 +02:00
|
|
|
int _do_setxattr(EncFS_Context *, const string &cyName,
|
|
|
|
tuple<const char *, const char *, size_t, int> data) {
|
|
|
|
return ::setxattr(cyName.c_str(), get<0>(data), get<1>(data), get<2>(data),
|
|
|
|
get<3>(data));
|
|
|
|
}
|
|
|
|
int encfs_setxattr(const char *path, const char *name, const char *value,
|
|
|
|
size_t size, int flags) {
|
|
|
|
return withCipherPath("setxattr", path, _do_setxattr,
|
|
|
|
make_tuple(name, value, size, flags));
|
2013-01-29 04:07:54 +01:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef XATTR_ADD_OPT
|
|
|
|
int _do_getxattr(EncFS_Context *, const string &cyName,
|
2013-10-20 00:35:26 +02:00
|
|
|
tuple<const char *, void *, size_t, uint32_t> data) {
|
2013-01-29 04:07:54 +01:00
|
|
|
int options = 0;
|
2013-10-20 00:35:26 +02:00
|
|
|
return ::getxattr(cyName.c_str(), get<0>(data), get<1>(data), get<2>(data),
|
|
|
|
get<3>(data), options);
|
2013-01-29 04:07:54 +01:00
|
|
|
}
|
2013-10-20 00:35:26 +02:00
|
|
|
int encfs_getxattr(const char *path, const char *name, char *value, size_t size,
|
|
|
|
uint32_t position) {
|
|
|
|
return withCipherPath("getxattr", path, _do_getxattr,
|
|
|
|
make_tuple(name, (void *)value, size, position), true);
|
2013-01-29 04:07:54 +01:00
|
|
|
}
|
|
|
|
#else
|
|
|
|
int _do_getxattr(EncFS_Context *, const string &cyName,
|
2013-10-20 00:35:26 +02:00
|
|
|
tuple<const char *, void *, size_t> data) {
|
|
|
|
return ::getxattr(cyName.c_str(), get<0>(data), get<1>(data), get<2>(data));
|
2013-01-29 04:07:54 +01:00
|
|
|
}
|
2013-10-20 00:35:26 +02:00
|
|
|
int encfs_getxattr(const char *path, const char *name, char *value,
|
|
|
|
size_t size) {
|
|
|
|
return withCipherPath("getxattr", path, _do_getxattr,
|
|
|
|
make_tuple(name, (void *)value, size), true);
|
2013-01-29 04:07:54 +01:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
int _do_listxattr(EncFS_Context *, const string &cyName,
|
2013-10-20 00:35:26 +02:00
|
|
|
tuple<char *, size_t> data) {
|
2013-01-29 04:07:54 +01:00
|
|
|
#ifdef XATTR_ADD_OPT
|
|
|
|
int options = 0;
|
2013-10-20 00:35:26 +02:00
|
|
|
int res = ::listxattr(cyName.c_str(), get<0>(data), get<1>(data), options);
|
2013-01-29 04:07:54 +01:00
|
|
|
#else
|
2013-10-20 00:35:26 +02:00
|
|
|
int res = ::listxattr(cyName.c_str(), get<0>(data), get<1>(data));
|
2013-01-29 04:07:54 +01:00
|
|
|
#endif
|
|
|
|
return (res == -1) ? -errno : res;
|
|
|
|
}
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
int encfs_listxattr(const char *path, char *list, size_t size) {
|
|
|
|
return withCipherPath("listxattr", path, _do_listxattr,
|
|
|
|
make_tuple(list, size), true);
|
2013-01-29 04:07:54 +01:00
|
|
|
}
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
int _do_removexattr(EncFS_Context *, const string &cyName, const char *name) {
|
2013-01-29 04:07:54 +01:00
|
|
|
#ifdef XATTR_ADD_OPT
|
|
|
|
int options = 0;
|
2013-10-20 00:35:26 +02:00
|
|
|
int res = ::removexattr(cyName.c_str(), name, options);
|
2013-01-29 04:07:54 +01:00
|
|
|
#else
|
2013-10-20 00:35:26 +02:00
|
|
|
int res = ::removexattr(cyName.c_str(), name);
|
2013-01-29 04:07:54 +01:00
|
|
|
#endif
|
|
|
|
return (res == -1) ? -errno : res;
|
|
|
|
}
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
int encfs_removexattr(const char *path, const char *name) {
|
|
|
|
return withCipherPath("removexattr", path, _do_removexattr, name);
|
2013-01-29 04:07:54 +01:00
|
|
|
}
|
|
|
|
|
2013-03-05 07:29:58 +01:00
|
|
|
} // namespace encfs
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
#endif // HAVE_XATTR
|