mirror of
https://github.com/vgough/encfs.git
synced 2024-11-21 23:43:26 +01:00
b606ef122b
git-svn-id: http://encfs.googlecode.com/svn/trunk@124 db9cf616-1c43-0410-9cb8-a902689de0d6
260 lines
6.2 KiB
C++
260 lines
6.2 KiB
C++
/*****************************************************************************
|
|
* Author: Valient Gough <vgough@pobox.com>
|
|
*
|
|
*****************************************************************************
|
|
* Copyright (c) 2003-2004, Valient Gough
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify it
|
|
* under the terms of the GNU Lesser General Public License as published by the
|
|
* Free Software Foundation, either version 3 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 Lesser General Public License
|
|
* for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
// Include encfs first, because we need to include fuse.h before any inclusion
|
|
// of sys/stat.h or other system headers (to be safe)
|
|
#include "fs/encfs.h"
|
|
|
|
#include <errno.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#ifdef linux
|
|
#include <sys/fsuid.h>
|
|
#endif
|
|
|
|
#include <cstring>
|
|
|
|
#include "base/config.h"
|
|
#include "base/Error.h"
|
|
#include "base/Mutex.h"
|
|
#include "cipher/MemoryPool.h"
|
|
|
|
#include "fs/CipherFileIO.h"
|
|
#include "fs/DirNode.h"
|
|
#include "fs/FileIO.h"
|
|
#include "fs/FileNode.h"
|
|
#include "fs/FileUtils.h"
|
|
#include "fs/MACFileIO.h"
|
|
#include "fs/RawFileIO.h"
|
|
#include "fs/fsconfig.pb.h"
|
|
|
|
#include <glog/logging.h>
|
|
|
|
using std::string;
|
|
|
|
namespace encfs {
|
|
|
|
/*
|
|
TODO: locking at the FileNode level is inefficient, since this precludes
|
|
multiple concurrent IO operations within the same file.
|
|
|
|
There is no reason why simultainous reads cannot be satisfied, or why one
|
|
read has to wait for the decoding of the previous read before it can be
|
|
sent to the IO subsystem!
|
|
*/
|
|
|
|
FileNode::FileNode(DirNode *parent_, const FSConfigPtr &cfg,
|
|
const char *plaintextName_, const char *cipherName_) {
|
|
Lock _lock(mutex);
|
|
|
|
this->_pname = plaintextName_;
|
|
this->_cname = cipherName_;
|
|
this->parent = parent_;
|
|
|
|
this->fsConfig = cfg;
|
|
|
|
// chain RawFileIO & CipherFileIO
|
|
shared_ptr<FileIO> rawIO(new RawFileIO(_cname));
|
|
io = shared_ptr<FileIO>(new CipherFileIO(rawIO, fsConfig));
|
|
|
|
if (cfg->config->block_mac_bytes() || cfg->config->block_mac_rand_bytes())
|
|
io = shared_ptr<FileIO>(new MACFileIO(io, fsConfig));
|
|
}
|
|
|
|
FileNode::~FileNode() {
|
|
// FileNode mutex should be locked before the destructor is called
|
|
|
|
_pname.assign(_pname.length(), '\0');
|
|
_cname.assign(_cname.length(), '\0');
|
|
io.reset();
|
|
}
|
|
|
|
const char *FileNode::cipherName() const { return _cname.c_str(); }
|
|
|
|
const char *FileNode::plaintextName() const { return _pname.c_str(); }
|
|
|
|
string FileNode::plaintextParent() const { return parentDirectory(_pname); }
|
|
|
|
static bool setIV(const shared_ptr<FileIO> &io, uint64_t iv) {
|
|
struct stat stbuf;
|
|
if ((io->getAttr(&stbuf) < 0) || S_ISREG(stbuf.st_mode))
|
|
return io->setIV(iv);
|
|
else
|
|
return true;
|
|
}
|
|
|
|
bool FileNode::setName(const char *plaintextName_, const char *cipherName_,
|
|
uint64_t iv, bool setIVFirst) {
|
|
// Lock _lock( mutex );
|
|
VLOG(1) << "calling setIV on " << cipherName_;
|
|
if (setIVFirst) {
|
|
if (fsConfig->config->external_iv() && !setIV(io, iv)) return false;
|
|
|
|
// now change the name..
|
|
if (plaintextName_) this->_pname = plaintextName_;
|
|
if (cipherName_) {
|
|
this->_cname = cipherName_;
|
|
io->setFileName(cipherName_);
|
|
}
|
|
} else {
|
|
std::string oldPName = _pname;
|
|
std::string oldCName = _cname;
|
|
|
|
if (plaintextName_) this->_pname = plaintextName_;
|
|
if (cipherName_) {
|
|
this->_cname = cipherName_;
|
|
io->setFileName(cipherName_);
|
|
}
|
|
|
|
if (fsConfig->config->external_iv() && !setIV(io, iv)) {
|
|
_pname = oldPName;
|
|
_cname = oldCName;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
int FileNode::mknod(mode_t mode, dev_t rdev, uid_t uid, gid_t gid) {
|
|
Lock _lock(mutex);
|
|
|
|
int res;
|
|
int olduid = -1;
|
|
int oldgid = -1;
|
|
if (uid != 0) {
|
|
olduid = setfsuid(uid);
|
|
if (olduid == -1) {
|
|
LOG(INFO) << "setfsuid error: " << strerror(errno);
|
|
return -EPERM;
|
|
}
|
|
}
|
|
if (gid != 0) {
|
|
oldgid = setfsgid(gid);
|
|
if (oldgid == -1) {
|
|
LOG(INFO) << "setfsgid error: " << strerror(errno);
|
|
return -EPERM;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* cf. xmp_mknod() in fusexmp.c
|
|
* The regular file stuff could be stripped off if there
|
|
* were a create method (advised to have)
|
|
*/
|
|
if (S_ISREG(mode)) {
|
|
res = ::open(_cname.c_str(), O_CREAT | O_EXCL | O_WRONLY, mode);
|
|
if (res >= 0) res = ::close(res);
|
|
} else if (S_ISFIFO(mode))
|
|
res = ::mkfifo(_cname.c_str(), mode);
|
|
else
|
|
res = ::mknod(_cname.c_str(), mode, rdev);
|
|
|
|
if (olduid >= 0) setfsuid(olduid);
|
|
if (oldgid >= 0) setfsgid(oldgid);
|
|
|
|
if (res == -1) {
|
|
int eno = errno;
|
|
VLOG(1) << "mknod error: " << strerror(eno);
|
|
res = -eno;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
int FileNode::open(int flags) const {
|
|
Lock _lock(mutex);
|
|
|
|
int res = io->open(flags);
|
|
return res;
|
|
}
|
|
|
|
int FileNode::getAttr(struct stat *stbuf) const {
|
|
Lock _lock(mutex);
|
|
|
|
int res = io->getAttr(stbuf);
|
|
return res;
|
|
}
|
|
|
|
off_t FileNode::getSize() const {
|
|
Lock _lock(mutex);
|
|
|
|
int res = io->getSize();
|
|
return res;
|
|
}
|
|
|
|
ssize_t FileNode::read(off_t offset, unsigned char *data, ssize_t size) const {
|
|
IORequest req;
|
|
req.offset = offset;
|
|
req.dataLen = size;
|
|
req.data = data;
|
|
|
|
Lock _lock(mutex);
|
|
|
|
return io->read(req);
|
|
}
|
|
|
|
bool FileNode::write(off_t offset, unsigned char *data, ssize_t size) {
|
|
VLOG(1) << "FileNode::write offset " << offset << ", data size " << size;
|
|
|
|
IORequest req;
|
|
req.offset = offset;
|
|
req.dataLen = size;
|
|
req.data = data;
|
|
|
|
Lock _lock(mutex);
|
|
|
|
return io->write(req);
|
|
}
|
|
|
|
int FileNode::truncate(off_t size) {
|
|
Lock _lock(mutex);
|
|
|
|
return io->truncate(size);
|
|
}
|
|
|
|
int FileNode::sync(bool datasync) {
|
|
Lock _lock(mutex);
|
|
|
|
int fh = io->open(O_RDONLY);
|
|
if (fh >= 0) {
|
|
int res;
|
|
#ifdef linux
|
|
if (datasync)
|
|
res = fdatasync(fh);
|
|
else
|
|
res = fsync(fh);
|
|
#else
|
|
// no fdatasync support
|
|
// TODO: use autoconfig to check for it..
|
|
res = fsync(fh);
|
|
#endif
|
|
|
|
if (res == -1) res = -errno;
|
|
|
|
return res;
|
|
} else
|
|
return fh;
|
|
}
|
|
|
|
} // namespace encfs
|