/* 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; }