/*
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
BlockFuse: See 2010-12-21 entry: http://www.globallinuxsecurity.pro/blog.php
Started from Miklos's hello.c:
Copyright (C) 2001-2007 Miklos Szeredi
BlockFuse contributions:
Copyright (C) 2010-2011 Eric Wheeler
gcc -Wall `pkg-config fuse --cflags --libs` block-fuse.c -o block-fuse
*/
#define _GNU_SOURCE
#define FUSE_USE_VERSION 26
//#define DEBUG "/tmp/block-fuse.log"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
void **fd_to_mmap;
#ifdef DEBUG
FILE *log;
#endif
static char *ROOT = NULL;
time_t MOUNT_TIME;
static int blockfuse_getattr(const char *path, struct stat *stbuf)
{
int res = 0;
char spath[4096] = {0};
memset(stbuf, 0, sizeof(struct stat));
if (strcmp(path, "/") == 0) {
stbuf->st_mode = S_IFDIR | 0755;
stbuf->st_nlink = 2;
} else {
stbuf->st_mode = S_IFREG | 0400;
stbuf->st_nlink = 1;
stbuf->st_atime = MOUNT_TIME;
stbuf->st_ctime = MOUNT_TIME;
stbuf->st_mtime = MOUNT_TIME;
snprintf(spath, sizeof(spath)-1, "%s/%s", ROOT, path);
int fd = open(spath, O_RDONLY | O_NONBLOCK);
if (fd < 0)
return -errno;
if (ioctl(fd, BLKGETSIZE64, &stbuf->st_size) < 0)
return -errno;
close(fd);
}
return res;
}
static int blockfuse_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
off_t offset, struct fuse_file_info *fi)
{
(void) offset;
(void) fi;
struct dirent *de;
DIR *dp = opendir(ROOT);
if (strcmp(path, "/") != 0)
return -ENOENT;
filler(buf, ".", NULL, 0);
filler(buf, "..", NULL, 0);
while ((de = readdir(dp)) != NULL)
{
if (de->d_type == DT_BLK)
{
filler(buf, de->d_name, NULL, 0);
}
}
closedir(dp);
return 0;
}
static int blockfuse_release(const char *path, struct fuse_file_info *fi)
{
off64_t size;
if (fi->fh >= 0)
{
if (ioctl(fi->fh, BLKGETSIZE64, &size) >= 0)
{
munmap(fd_to_mmap[fi->fh], size);
fd_to_mmap[fi->fh] = NULL;
}
close(fi->fh);
}
return 0;
}
static int blockfuse_open(const char *path, struct fuse_file_info *fi)
{
char spath[4096] = {0};
off64_t size;
snprintf(spath, sizeof(spath)-1, "%s/%s", ROOT, path);
if ((fi->flags & 3) != O_RDONLY)
return -EACCES;
fi->fh = open(spath, O_RDONLY);
if (fi->fh < 0)
{
return -errno;
}
if (ioctl(fi->fh, BLKGETSIZE64, &size) < 0)
{
close(fi->fh);
return -errno;
}
fd_to_mmap[fi->fh] = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fi->fh, 0);
if (fd_to_mmap[fi->fh] == MAP_FAILED)
{
#ifdef DEBUG
fprintf(log, "mmap: %p/%d/%ld\n", fd_to_mmap[fi->fh], fi->fh, size); fflush(log);
#endif
fd_to_mmap[fi->fh] = NULL;
close(fi->fh);
return -errno;
}
return 0;
}
static int blockfuse_read(const char *path, char *buf, size_t size, off_t offset,
struct fuse_file_info *fi)
{
off64_t device_size;
off64_t len = size;
if (fi->fh >= 0)
{
if (ioctl(fi->fh, BLKGETSIZE64, &device_size) < 0)
{
return -errno;
close(fi->fh);
}
// Adjust size in case it exceeds the mmap region.
if (offset+size > device_size && offset < device_size)
len = device_size-offset;
else if (offset+size > device_size)
len = 0;
if (len)
memcpy(buf, (void*)( (fd_to_mmap[fi->fh]) + offset), len);
if (len < size)
memset(buf+len, 0, size-len);
#ifdef DEBUG
fprintf(log, "read size=%ld, len=%ld, offset=%ld, dev_size=%ld\n", size, len, offset, device_size); fflush(log);
#endif
return size;
/*
lseek(fi->fh, offset, SEEK_SET);
size_t len;
len = read(fi->fh, buf, size);
if (len < 0)
return -errno;
else if (len < size)
memset(buf+len, 0, size-len);
return len;
*/
}
else
{
return -ENOENT;
}
}
static struct fuse_operations blockfuse_oper = {
.getattr = blockfuse_getattr,
.readdir = blockfuse_readdir,
.open = blockfuse_open,
.release = blockfuse_release,
.read = blockfuse_read,
};
int main(int argc, char *argv[])
{
char *args[3] = { argv[0], argv[2], NULL };
int i;
struct rlimit rlim;
MOUNT_TIME = time(0);
if (argc < 3)
{
fprintf(stderr, "usage: %s /dev/directory /mnt/point\n", argv[0]);
exit(1);
}
if (argv[1][0] != '/' || argv[2][0] != '/')
{
fprintf(stderr, "Error: directory paths must be absolute.\n");
exit(1);
}
#ifdef DEBUG
log = fopen(DEBUG, "a");
if (!log)
{
fprintf(stderr, "/tmp/block-fuse.log\n");
exit(1);
}
else
fprintf(log, "starting.\n");
fflush(log);
#endif
// fd_to_mmap is "file descriptor" indexed. This allows us
// to keep the file descriptor for open files to do things
// like check file size, etc.
getrlimit(RLIMIT_NOFILE, &rlim);
printf("RLIMIT_NOFILE = %ld\n", rlim.rlim_cur);
fd_to_mmap = malloc(sizeof(void*) * rlim.rlim_cur);
ROOT = argv[1];
printf("%d\n", argc);
for (i = 0; i < argc; i++)
{
printf("[%d] %s\n", i, argv[i]);
}
int ret = fuse_main(2, args, &blockfuse_oper, NULL);
free(fd_to_mmap);
return ret;
}