* Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
#include "compatibility.h"
#include "partition_support.h"
#include <errno.h>
#include <unistd.h>
#include <list>
#include "fssh_errno.h"
#include "fssh_stat.h"
#include "fssh_unistd.h"
#include "stat_priv.h"
using namespace FSShell;
namespace FSShell {
struct FileRestriction {
FileRestriction(fssh_dev_t device, fssh_ino_t node, fssh_off_t startOffset,
fssh_off_t endOffset)
:
device(device),
node(node),
startOffset(startOffset),
endOffset(endOffset)
{
}
fssh_dev_t device;
fssh_ino_t node;
fssh_off_t startOffset;
fssh_off_t endOffset;
};
typedef std::list<FileRestriction*> FileRestrictionList;
static FileRestrictionList sFileRestrictions;
static FileRestriction*
find_file_restriction(fssh_dev_t device, fssh_ino_t node)
{
for (FileRestrictionList::iterator it = sFileRestrictions.begin();
it != sFileRestrictions.end(); ++it) {
FileRestriction* restriction = *it;
if (restriction->device == device && restriction->node == node)
return restriction;
}
return NULL;
}
static FileRestriction*
find_file_restriction(int fd)
{
struct fssh_stat st;
if (unrestricted_fstat(fd, &st) < 0)
return NULL;
return find_file_restriction(st.fssh_st_dev, st.fssh_st_ino);
}
void
add_file_restriction(const char* fileName, fssh_off_t startOffset,
fssh_off_t endOffset)
{
struct fssh_stat st;
if (unrestricted_stat(fileName, &st) < 0)
return;
fssh_dev_t device = st.fssh_st_dev;
fssh_ino_t node = st.fssh_st_ino;
FileRestriction* restriction = find_file_restriction(device, node);
if (restriction)
return;
if (endOffset < 0)
endOffset = st.fssh_st_size;
restriction = new FileRestriction(device, node, startOffset, endOffset);
sFileRestrictions.push_back(restriction);
}
void
restricted_file_opened(int fd)
{
FileRestriction* restriction = find_file_restriction(fd);
if (!restriction)
return;
lseek(fd, restriction->startOffset, SEEK_SET);
}
void
restricted_file_duped(int oldFD, int newFD)
{
}
void
restricted_file_closed(int fd)
{
}
int
restricted_file_restrict_io(int fd, fssh_off_t& pos, fssh_off_t size)
{
FileRestriction* restriction = find_file_restriction(fd);
if (!restriction)
return 0;
if (pos < 0) {
pos = lseek(fd, 0, SEEK_CUR);
if (pos < 0)
return -1;
} else
pos += restriction->startOffset;
if (pos < restriction->startOffset || pos > restriction->endOffset) {
fssh_set_errno(B_BAD_VALUE);
return -1;
}
fssh_off_t maxSize = restriction->endOffset - pos;
if (size > maxSize)
size = maxSize;
return 0;
}
void
restricted_file_restrict_stat(struct fssh_stat* st)
{
FileRestriction* restriction = find_file_restriction(st->fssh_st_dev,
st->fssh_st_ino);
if (!restriction)
return;
st->fssh_st_size = restriction->endOffset - restriction->startOffset;
}
static int
to_platform_seek_mode(int fsshWhence)
{
switch (fsshWhence) {
case FSSH_SEEK_CUR:
return SEEK_CUR;
case FSSH_SEEK_END:
return SEEK_END;
case FSSH_SEEK_SET:
default:
return SEEK_SET;
}
}
}
fssh_off_t
fssh_lseek(int fd, fssh_off_t offset, int whence)
{
FileRestriction* restriction = find_file_restriction(fd);
if (!restriction)
return lseek(fd, offset, to_platform_seek_mode(whence));
fssh_off_t pos;
switch (whence) {
case FSSH_SEEK_CUR:
{
pos = lseek(fd, 0, SEEK_CUR);
if (pos < 0)
return pos;
pos += offset;
break;
}
case FSSH_SEEK_END:
pos = restriction->endOffset + offset;
break;
case FSSH_SEEK_SET:
default:
pos = restriction->startOffset + offset;
break;
}
if (pos < restriction->startOffset) {
fssh_set_errno(B_BAD_VALUE);
return -1;
}
pos = lseek(fd, pos, SEEK_SET);
if (pos >= 0)
pos -= restriction->startOffset;
return pos;
}