mirror of
https://github.com/vgough/encfs.git
synced 2024-11-29 03:15:07 +01:00
b606ef122b
git-svn-id: http://encfs.googlecode.com/svn/trunk@124 db9cf616-1c43-0410-9cb8-a902689de0d6
272 lines
7.1 KiB
C++
272 lines
7.1 KiB
C++
/*****************************************************************************
|
|
* Author: Valient Gough <vgough@pobox.com>
|
|
*
|
|
*****************************************************************************
|
|
* Copyright (c) 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/>.
|
|
*/
|
|
|
|
#ifdef linux
|
|
#define _XOPEN_SOURCE 500 // pick up pread , pwrite
|
|
#endif
|
|
#include <unistd.h>
|
|
|
|
#include "base/Error.h"
|
|
#include "fs/RawFileIO.h"
|
|
|
|
#include <glog/logging.h>
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <cstring>
|
|
|
|
#include <cerrno>
|
|
|
|
namespace encfs {
|
|
|
|
static Interface RawFileIO_iface = makeInterface("FileIO/Raw", 1, 0, 0);
|
|
|
|
inline void swap(int &x, int &y) {
|
|
int tmp = x;
|
|
x = y;
|
|
y = tmp;
|
|
}
|
|
|
|
RawFileIO::RawFileIO()
|
|
: knownSize(false), fileSize(0), fd(-1), oldfd(-1), canWrite(false) {}
|
|
|
|
RawFileIO::RawFileIO(const std::string &fileName)
|
|
: name(fileName),
|
|
knownSize(false),
|
|
fileSize(0),
|
|
fd(-1),
|
|
oldfd(-1),
|
|
canWrite(false) {}
|
|
|
|
RawFileIO::~RawFileIO() {
|
|
int _fd = -1;
|
|
int _oldfd = -1;
|
|
|
|
swap(_fd, fd);
|
|
swap(_oldfd, oldfd);
|
|
|
|
if (_oldfd != -1) close(_oldfd);
|
|
|
|
if (_fd != -1) close(_fd);
|
|
}
|
|
|
|
Interface RawFileIO::interface() const { return RawFileIO_iface; }
|
|
|
|
/*
|
|
Workaround for opening a file for write when permissions don't allow.
|
|
Since the kernel has already checked permissions, we can assume it is ok to
|
|
provide access. So force it by changing permissions temporarily. Should
|
|
be called with a lock around it so that there won't be a race condition
|
|
with calls to lstat picking up the wrong permissions.
|
|
*/
|
|
static int open_readonly_workaround(const char *path, int flags) {
|
|
int fd = -1;
|
|
struct stat stbuf;
|
|
memset(&stbuf, 0, sizeof(struct stat));
|
|
if (lstat(path, &stbuf) != -1) {
|
|
// make sure user has read/write permission..
|
|
chmod(path, stbuf.st_mode | 0600);
|
|
fd = ::open(path, flags);
|
|
chmod(path, stbuf.st_mode);
|
|
} else {
|
|
LOG(INFO) << "can't stat file " << path;
|
|
}
|
|
|
|
return fd;
|
|
}
|
|
|
|
/*
|
|
We shouldn't have to support all possible open flags, so untaint the flags
|
|
argument by only taking ones we understand and accept.
|
|
- Since the kernel has already done permission tests before calling us, we
|
|
shouldn't have to worry about access control.
|
|
- Basically we just need to distinguish between read and write flags
|
|
- Also keep the O_LARGEFILE flag, in case the underlying filesystem needs
|
|
it..
|
|
*/
|
|
int RawFileIO::open(int flags) {
|
|
bool requestWrite = ((flags & O_RDWR) || (flags & O_WRONLY));
|
|
|
|
VLOG(1) << "open call for " << (requestWrite ? "writable" : "read only")
|
|
<< " file";
|
|
|
|
int result = 0;
|
|
|
|
// if we have a descriptor and it is writable, or we don't need writable..
|
|
if ((fd >= 0) && (canWrite || !requestWrite)) {
|
|
VLOG(1) << "using existing file descriptor";
|
|
result = fd; // success
|
|
} else {
|
|
int finalFlags = requestWrite ? O_RDWR : O_RDONLY;
|
|
|
|
#if defined(O_LARGEFILE)
|
|
if (flags & O_LARGEFILE) finalFlags |= O_LARGEFILE;
|
|
#else
|
|
#warning O_LARGEFILE not supported
|
|
#endif
|
|
|
|
int newFd = ::open(name.c_str(), finalFlags);
|
|
|
|
VLOG(1) << "open file with flags " << finalFlags << ", result = " << newFd;
|
|
|
|
if ((newFd == -1) && (errno == EACCES)) {
|
|
VLOG(1) << "using readonly workaround for open";
|
|
newFd = open_readonly_workaround(name.c_str(), finalFlags);
|
|
}
|
|
|
|
if (newFd >= 0) {
|
|
if (oldfd >= 0) {
|
|
LOG(ERROR) << "leaking FD?: oldfd = " << oldfd << ", fd = " << fd
|
|
<< ", newfd = " << newFd;
|
|
}
|
|
|
|
// the old fd might still be in use, so just keep it around for
|
|
// now.
|
|
canWrite = requestWrite;
|
|
oldfd = fd;
|
|
result = fd = newFd;
|
|
} else {
|
|
result = -errno;
|
|
LOG(INFO) << "::open error: " << strerror(errno);
|
|
}
|
|
}
|
|
|
|
LOG_IF(INFO, result < 0) << "file " << name << " open failure: " << -result;
|
|
|
|
return result;
|
|
}
|
|
|
|
int RawFileIO::getAttr(struct stat *stbuf) const {
|
|
int res = lstat(name.c_str(), stbuf);
|
|
int eno = errno;
|
|
|
|
LOG_IF(INFO, res < 0) << "getAttr error on " << name << ": " << strerror(eno);
|
|
|
|
return (res < 0) ? -eno : 0;
|
|
}
|
|
|
|
void RawFileIO::setFileName(const char *fileName) { name = fileName; }
|
|
|
|
const char *RawFileIO::getFileName() const { return name.c_str(); }
|
|
|
|
off_t RawFileIO::getSize() const {
|
|
if (!knownSize) {
|
|
struct stat stbuf;
|
|
memset(&stbuf, 0, sizeof(struct stat));
|
|
int res = lstat(name.c_str(), &stbuf);
|
|
|
|
if (res == 0) {
|
|
fileSize = stbuf.st_size;
|
|
knownSize = true;
|
|
return fileSize;
|
|
} else
|
|
return -1;
|
|
} else {
|
|
return fileSize;
|
|
}
|
|
}
|
|
|
|
ssize_t RawFileIO::read(const IORequest &req) const {
|
|
rAssert(fd >= 0);
|
|
|
|
VLOG(2) << "Read " << req.dataLen << " bytes from offset " << req.offset;
|
|
ssize_t readSize = pread(fd, req.data, req.dataLen, req.offset);
|
|
|
|
if (readSize < 0) {
|
|
LOG(INFO) << "read failed at offset " << req.offset << " for "
|
|
<< req.dataLen << " bytes: " << strerror(errno);
|
|
}
|
|
|
|
return readSize;
|
|
}
|
|
|
|
bool RawFileIO::write(const IORequest &req) {
|
|
rAssert(fd >= 0);
|
|
rAssert(true == canWrite);
|
|
|
|
VLOG(2) << "Write " << req.dataLen << " bytes to offset " << req.offset;
|
|
|
|
int retrys = 10;
|
|
void *buf = req.data;
|
|
ssize_t bytes = req.dataLen;
|
|
off_t offset = req.offset;
|
|
|
|
while (bytes && retrys > 0) {
|
|
ssize_t writeSize = ::pwrite(fd, buf, bytes, offset);
|
|
|
|
if (writeSize < 0) {
|
|
knownSize = false;
|
|
LOG(INFO) << "write failed at offset " << offset << " for " << bytes
|
|
<< " bytes: " << strerror(errno);
|
|
return false;
|
|
}
|
|
|
|
bytes -= writeSize;
|
|
offset += writeSize;
|
|
buf = (void *)((char *)buf + writeSize);
|
|
--retrys;
|
|
}
|
|
|
|
if (bytes != 0) {
|
|
LOG(ERROR) << "Write error: wrote " << (req.dataLen - bytes) << " bytes of "
|
|
<< req.dataLen << ", max retries reached";
|
|
knownSize = false;
|
|
return false;
|
|
} else {
|
|
if (knownSize) {
|
|
off_t last = req.offset + req.dataLen;
|
|
if (last > fileSize) fileSize = last;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
int RawFileIO::truncate(off_t size) {
|
|
int res;
|
|
|
|
if (fd >= 0 && canWrite) {
|
|
res = ::ftruncate(fd, size);
|
|
#ifndef __FreeBSD__
|
|
::fdatasync(fd);
|
|
#endif
|
|
} else
|
|
res = ::truncate(name.c_str(), size);
|
|
|
|
if (res < 0) {
|
|
int eno = errno;
|
|
LOG(INFO) << "truncate failed for " << name << " (" << fd << ") size "
|
|
<< size << ", error " << strerror(eno);
|
|
res = -eno;
|
|
knownSize = false;
|
|
} else {
|
|
res = 0;
|
|
fileSize = size;
|
|
knownSize = true;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
bool RawFileIO::isWritable() const { return canWrite; }
|
|
|
|
} // namespace encfs
|