基于dokan的内存文件系统实现

dokan是windows系统上的FUSE实现,基于它提供的接口,可以很容易的实现一个用户态文件系统,挂载到系统成为一个分区,使用上就和本地硬盘一样。
本文基于dokan实现了一个内存型文件系统,使用内存存储数据,读写速度极快,当然缺点也很多,最重要的就是数据不能持久化,卸载之后数据全部消失,另外简化了实现,没有目录相关操作,也就是说只支持一级目录及多个文件的模式,文件系统大小受限于本机内存大小。

1.安装dokan

本文使用的系统是win10家庭中文版1809,dokan版本1.3.0,下载DokanSetup.exe安装dokan。
安装时可能遇到驱动无法注册的问题,需要使用管理员打开powershell,进入dokany目录下执行命令:

powershell.exe -executionpolicy remotesigned -File .\cert\dokan-import.ps1

完成后继续安装驱动:

dokanctl.exe /i d

2.编译环境

本文采用cygwin编译,编译工具g++。

3.内存文件系统代码

本文基于dokan提供的fuse_mirror框架实现文件系统。dokan提供windows平台的接口,为了简化开发难度和照顾linux的编程习惯,dokan又提供了fuse_mirror框架,用户只需要实现少部分文件接口,即可运行一个文件系统了。
简单解释一下本文实现的内存文件系统实现原理,本文件系统仅需考虑多个文件即可,实现创建、删除、读写、重命名功能。每个文件由多个1M的数据块组成,在实现上数据块用string结构,文件采用map结构组织多个数据块,键值是数据块编号。文件系统采用map结构的files存储多个文件,files作用是将文件名映射到文件结构。创建文件主要实现是检查文件是否已存在,不存在则往files里插入新的文件结构。删除实现则从files里删除文件,同时扣除文件系统空间使用量。重命名主要是采用move语义将文件内容转移,从files删除原有文件名,然后插入新的文件。读写实现比较复杂,读文件时根据指定的偏移计算对应数据块编号,然后从数据块从复制数据到用户缓冲区,难点是需要考虑读取范围超出文件大小、跨多个数据块读取、读取不足一个数据块等多种情形,为了简化难度,这里没有考虑稀疏文件的支持。写文件过程类似,当超出文件大小范围时则创建新的数据块,新建的数据块预申请1M内存,写完之后更新文件系统空间使用量。

废话不多说,来看代码,本代码基于fusexmp.cpp修改,文件名fusexmp-mem.cpp。

#define FUSE_USE_VERSION 30

#ifdef HAVE_CONFIG_H
//#include <config.h>
#endif

#ifdef linux
/* For pread()/pwrite()/utimensat() */
#define _XOPEN_SOURCE 700
#endif

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <dirent.h>
#include <errno.h>
#include <sys/time.h>
#ifdef HAVE_SETXATTR
#include <sys/xattr.h>
#endif
#include <iostream>
#include <map>
#include <vector>
#include <string>
#include <mutex>
#include <thread>
#include <assert.h>

#include "fuse.h"

using namespace std;
#define LOG debug && printf
#define BLK_SIZE (1<<20)
#define blk_at(off) ((int)((off) / BLK_SIZE))
#define blk_off(off) ((int)((off) & (BLK_SIZE - 1)))

const static int MAX_FS_KB = 8*1024*1024;
const static int MAX_FILE_NUM = 100;
const static int MAX_FILE_NAME_LEN = 100;
static bool debug = false;

class Log{
private:
    const char* func;
    const char* path;
    int& res;
public:
    Log(const char*f, const char*p, int& r): res(r){
        func = f;
        path = p;
        LOG("%s: %s\n", func, path) && fflush(stderr);
    }
    ~Log(){
        if(!debug)
            return;
        if(res>=0)
            cout << func << ": " << path << " ok " << res << endl;
        else
            cout << func << ": " << path << " errno " << -res << " " << strerror(-res) << endl;
    }
};

template<class X>
class Log2{
private:
    const char* func;
    const char* path;
    const X& x;
    int& res;
public:
    Log2(const char*f, const char*p, const X& x_, int& r): x(x_), res(r){
        func = f;
        path = p;
        if(!debug)
            return;
        cout << func << ": " << path << " " << x<< endl;
        //fflush(stdout);
    }
    ~Log2(){
        if(!debug)
            return;
        if(res>=0)
            cout << func << ": " << path << " " << x << " ok " << res << endl;
        else
            cout << func << ": " << path << " " << x << " errno " << -res << " " << strerror(-res) << endl;
        //fflush(stdout);
    }
};

struct myfile{
    int ino;
    off_t size;
    map<int, string> content;
    mutex mtx;
    
    myfile(){
        ino = 0;
        size = 0;
    }
    
    myfile(myfile&& m):ino(m.ino), size(m.size), 
    content(move(m.content)){}
    
    int do_read(const char *path, char *buf, size_t size, off_t offset);
    int do_write(const char *path, const char *buf, size_t size, off_t offset);
};


struct myfs{
    // filename -> file
    // test.txt -> file
    map<string, myfile> files;
    volatile size_t used_kb;
    volatile size_t total_kb;
    int max_files;
};

static mutex mtx;
static myfs fs;


int myfile::do_read(const char *path, char *buf, size_t size, off_t offset){
    int res = 0;
    if(offset < 0)
        offset = 0;
    if(offset >= this->size){
        LOG("%s: %s off %lld >= size %lld\n", __func__, path, offset, this->size);
        return res;
    }
    if(offset + size > this->size){
        size = this->size - offset;
        LOG("%s: %s off %lld size %lld max %lld\n", __func__, path, offset, this->size, size);
    }
    res = size;
    unique_lock<mutex> lck(this->mtx);
    while(size > 0){
        int b = blk_at(offset);
        int s = blk_off(offset);
        const string& ct = this->content[b];
        LOG("read blk %d off %d blk size %lld\n", b, s, ct.size());
        if(s+size <= ct.size()){
            memcpy(buf, &ct[s], size);
            offset += size;
            LOG("read %lld bytes from blk %d off %d\n", size, b, s);
            break;
        } else {
            size_t r = ct.size() - s;
            memcpy(buf, ct.c_str() + s, r);
            LOG("read %d bytes from blk %d off %d\n", r, b, s);
            buf += r;
            size -= r;
            offset += r;
        }
    }
    return res;
}
int myfile::do_write(const char *path, const char *buf, size_t size, off_t offset){
    int res = 0;
    {
        if(offset < 0)
            offset = 0;
        unique_lock<mutex> lck(mtx);
        size_t old_size_kb = (1023 + this->size) >> 10;
        size_t new_size_kb = (1023 + offset + size) >> 10;
        if(new_size_kb > old_size_kb){
            if(fs.total_kb <= fs.used_kb){
                fprintf(stderr, "error: no space used %lld/%lld\n", fs.used_kb, fs.total_kb);
                res = -EACCES;
                return res;
            }
            if(new_size_kb - old_size_kb + fs.used_kb > fs.total_kb){
                size = 1024*(fs.total_kb - fs.used_kb);
                new_size_kb = (1023 + offset + size) >> 10;
                if(new_size_kb > old_size_kb)
                    fs.used_kb += new_size_kb - old_size_kb;
            } else {
                fs.used_kb += new_size_kb - old_size_kb;
            }
        }
    }
    res = size;
    unique_lock<mutex> lck(this->mtx);
    while(size > 0){
        int b = blk_at(offset);
        int s = blk_off(offset);
        string& ct = this->content[b];
        if(ct.empty()){
            LOG("new block at %d\n", b);
            ct.reserve(BLK_SIZE);
        }
        size_t r = s + size <= BLK_SIZE ? size : BLK_SIZE - s;
        const char* t = buf + r;
        size -= r;
        offset += r;
        assert(s <= ct.size());
        if(s + r <= ct.size()){
            memcpy(&ct[s], buf, r);
        } else {
            size_t cpy = ct.size() - s;
            r -= cpy;
            memcpy(&ct[s], buf, cpy);
            buf += cpy;
            ct.append(buf, r);
        }
        buf = t;
    }
    if(offset > this->size)
        this->size = offset;
    return res;
}


myfile* findfile(const char*name){
    auto ptr = fs.files.find(name);
    if(ptr == fs.files.end())
        return NULL;
    return &(ptr->second);
}

static int xmp_getattr(const char *path, struct stat *stbuf)
{
    unique_lock<mutex> lck(mtx);
    int res = 0;

    Log log(__func__, path, res);
    if(path[1] == '\0'){
        memset(stbuf, 0, sizeof *stbuf);
        stbuf->st_ino = 1;
        stbuf->st_mode = S_IFDIR|0777;
        stbuf->st_nlink = 1;
        stbuf->st_uid = 0;
        stbuf->st_size = fs.used_kb * 1024;
        stbuf->st_blksize = 512;
        stbuf->st_blocks = (stbuf->st_size + 511) & ~511;
        return 0;
    }
    string name(path+1);
    auto ptr = fs.files.find(name);
    if(fs.files.end() == ptr){
        res = -ENOENT;
        return res;
    }
    myfile& mf = ptr->second;
    stbuf->st_ino = mf.ino;
    stbuf->st_mode = 0666;
    stbuf->st_nlink = 1;
    stbuf->st_uid = 0;
    stbuf->st_size = mf.size;
    stbuf->st_blksize = 512;
    stbuf->st_blocks = (mf.size + 511) & ~511;
    
    return 0;
}

static int xmp_create(const char *path, mode_t mode, struct fuse_file_info *fi){
    int res = 0;
    Log log(__func__, path, res);
    unique_lock<mutex> lck(mtx);
    if(!path[1]){
        res = -EACCES ;
        return res;
    }
    if(fs.files.size() == MAX_FILE_NUM){
        LOG("%s: error no space\n", __func__);
        res = -EACCES ;
        return res;
    }
    ++path;
    if(NULL!=findfile(path)){
        res = -EEXIST;
        return res;
    }
    if(strlen(path) > MAX_FILE_NAME_LEN - 1){
        LOG("%s: error name too long\n", __func__);
        res = -EACCES ;
        return res;
    }
    myfile& m = fs.files[string(path)];
    m.ino = fs.max_files + 1;
    m.size = 0;
    fi->fh = (off_t)(&m);
    return res;
}

static int xmp_open(const char *path, struct fuse_file_info *fi)
{
    int res=0;
    Log2 log(__func__, path, fi->flags, res);
    if(!path[1]){
        res = -EACCES ;
        return res;
    }
    ++path;
    myfile* pf;
    unique_lock<mutex> lck(mtx);
    if(!(pf=findfile(path))){
        if((fi->flags & (O_WRONLY|O_CREAT)) == (O_WRONLY|O_CREAT)){
            if(fs.files.size() == MAX_FILE_NUM){
                LOG("%s: error no space\n", __func__);
                res = -EACCES;
                return res;
            }
            if(strlen(path) > MAX_FILE_NAME_LEN - 1){
                LOG("%s: error name too long\n", __func__);
                res = -EACCES ;
                return res;
            }
            myfile& m = fs.files[string(path)];
            m.ino = fs.max_files + 1;
            m.size = 0;
            fi->fh = (off_t)(&m);
            return res;
        }
        res = -ENOENT;
        return res;
    }
    fi->fh = (off_t)(pf);
    return 0;
}

static int xmp_read(const char *path, char *buf, size_t size, off_t offset,
            struct fuse_file_info *fi)
{
    int res = 0;
    Log2 log(__func__, path, offset , res);
    myfile* pf = (myfile*)fi->fh;
    if(!pf){
        res = -ENOENT;
        return res;
    }
    res = pf->do_read(path, buf, size, offset);
    return res;
}

static int xmp_write(const char *path, const char *buf, size_t size,
             off_t offset, struct fuse_file_info *fi)
{
    int res = 0;
    Log2 log(__func__, path, size, res);
    myfile* pf = (myfile*)fi->fh;
    if(!pf){
        res = -ENOENT;
        return res;
    }
    res = pf->do_write(path, buf, size, offset);
    return res;
}

static int xmp_rename(const char *from, const char *to)
{
    int res = 0;
    Log2 log(__func__, from, to, res);
    if(!from[1] || !to[1]){
        res = -EACCES ;
        return res;
    }
    ++from;
    ++to;
    myfile* pf;
    unique_lock<mutex> lck(mtx);
    if(!(pf=findfile(from))){
        res = -ENOENT;
        return res;
    }
    if(NULL!=findfile(to)){
        res = -EEXIST;
        return res;
    }
    fs.files.emplace(to, myfile(move(*(&fs.files[from]))));
    fs.files.erase(from);
    return res;
}


static int xmp_readdir(const char * path, void * buf,
                       fuse_fill_dir_t fill, FUSE_OFF_T off,
                       struct fuse_file_info *fi){
    int res = 0;
    Log log(__func__, path, res);
    map<string, size_t> tmp_files;
    size_t used_kb=0;
    {
        unique_lock<mutex> lck(mtx);
        if(path[1]){
            res = -ENOENT;
            return res;
        }
        used_kb = fs.used_kb;
        for(auto& ptr: fs.files){
            tmp_files[ptr.first] = ptr.second.size;
        }
    }
    struct stat stbuf={0};
    //stbuf.st_ino = 1;
    stbuf.st_mode = S_IFDIR|0777;
    stbuf.st_nlink = 1;
    stbuf.st_uid = 0;
    stbuf.st_size = used_kb * 1024;
    stbuf.st_blksize = 512;
    stbuf.st_blocks = (stbuf.st_size + 511) & ~511;
    char m[2] = ".";
    char f[3] = "..";
   if(fill(buf, m, &stbuf, 0))
       return 0;
   if(fill(buf, f, &stbuf, 0))
       return 0;
    stbuf.st_mode = 0666;
    stbuf.st_nlink = 1;
    stbuf.st_uid = 0;
    stbuf.st_blksize = 512;
    char name[MAX_FILE_NAME_LEN];
    for(auto& ptr: tmp_files){
        const string& name_s = ptr.first;
        strcpy(name, name_s.c_str());
        stbuf.st_size = ptr.second;
        stbuf.st_blocks = (stbuf.st_size + 511) & ~511;
        LOG("%s: name %s size %lld\n", __func__, name, stbuf.st_size);
       if(fill(buf, name, &stbuf, 0))
           break;
    }
    
   return 0;
}


static int xmp_opendir(const char * path, struct fuse_file_info * info){
    if(path[1])
        return  -ENOENT;
    return 0;
}

static int xmp_unlink(const char *path)
{
    int res = 0;
    Log log(__func__, path, res);
    if(!path[1]){
        res = -EACCES;
        return res;
    }
    ++path;
    unique_lock<mutex> lck(mtx);
    myfile* pf;
    if(!(pf=findfile(path))){
        res = -ENOENT;
        return res;
    }
    size_t size_kb = (1023 + pf->size) >> 10;
    fs.used_kb -= size_kb;
    fs.files.erase(path);
    return res;
}


static int xmp_statfs(const char *path, struct statvfs *stbuf)
{
    int res = 0;
    Log log(__func__, path, res);
    unique_lock<mutex> lck(mtx);
    stbuf->f_bsize = 512;
    stbuf->f_bavail = (fs.total_kb - fs.used_kb) * 2;
    stbuf->f_bfree = stbuf->f_bavail;
    stbuf->f_blocks = fs.total_kb * 2;
    return 0;
}

static int xmp_access(const char *path, int mask)
{
    int res;
    return 0;
}

static int xmp_readlink(const char *path, char *buf, size_t size)
{
    int res = -EINVAL;
    Log log(__func__, path, res);
    return res;
}

static int xmp_mknod(const char *path, mode_t mode, dev_t rdev)
{
    int res = -EINVAL;
    Log log(__func__, path, res);
    return res;
}

static int xmp_mkdir(const char *path, mode_t mode)
{
    return -EACCES ;
}

static int xmp_rmdir(const char *path)
{
    return -EACCES ;
}

static int xmp_symlink(const char *from, const char *to)
{
    return -EINVAL;
}

static int xmp_link(const char *from, const char *to)
{
    return -EINVAL;
}

static int xmp_chmod(const char *path, mode_t mode)
{
    return -EINVAL;
}

static int xmp_chown(const char *path, uid_t uid, gid_t gid)
{
    return -EINVAL;
}

static int xmp_truncate(const char *path, off_t size)
{
    return -EINVAL;
}


static int xmp_release(const char *path, struct fuse_file_info *fi)
{
    return 0;
}

static int xmp_fsync(const char *path, int isdatasync,
             struct fuse_file_info *fi)
{
    /* Just a stub.  This method is optional and can safely be left
       unimplemented */

    (void) path;
    (void) isdatasync;
    (void) fi;
    return 0;
}

static int xmp_fallocate(const char *path, int mode,
            off_t offset, off_t length, struct fuse_file_info *fi)
{
    int fd;
    int res;

    (void) fi;

    return -EOPNOTSUPP;
}

/* xattr operations are optional and can safely be left unimplemented */
static int xmp_setxattr(const char *path, const char *name, const char *value,
            size_t size, int flags)
{
    return 0;
}

static int xmp_getxattr(const char *path, const char *name, char *value,
            size_t size)
{
    return 0;
}

static int xmp_listxattr(const char *path, char *list, size_t size)
{
    return 0;
}

static int xmp_removexattr(const char *path, const char *name)
{
    return 0;
}

static  int xmp_releasedir(const char *, struct fuse_file_info *){
    return 0;
}

inline double getnow(){
    struct timeval tv;
    gettimeofday(&tv, NULL);
    return tv.tv_sec + 0.000001 * tv.tv_usec;
}

// for *pret
#define RET_NONE 0
#define RET_STARTED 1
#define RET_OK 2
#define RET_ERROR 3


struct BenchArgs{
// for op_file
    const static int READ_TEST = 1;
    const static int WRITE_TEST = 2;
    const static int WRITE_READ_TEST = READ_TEST | WRITE_TEST;
    int num;
    int size_gb;
    int run_num;
    
    BenchArgs(int num_, int size_gb_, int run_num_){
        num = num_;
        size_gb = size_gb_;
        run_num = run_num_;
    }
    // read or write file thread
    static void _test_read_write_thread(BenchArgs* pbench, int id, int op_file, int64_t* completed, int* pret);
    
    // multiple files test
    void _cmd_testreadwrite(int op_file);
    void do_bench();
};

void BenchArgs::_test_read_write_thread(BenchArgs* pbench, int id, 
    int op_file, int64_t* completed, int* pret)
{
    *pret = RET_STARTED;
    char name[MAX_FILE_NAME_LEN];
    sprintf(name, "/pbench_%d", id);
    *completed = 0;
    int fd, ret;
    size_t buf_len = 1024*1024;
    char *buf = new char[buf_len];
    size_t total_bytes = 1024LL * 1024 * 1024 * pbench->size_gb;
    size_t op_num = total_bytes/buf_len;
    int64_t wr = 0;
    int read_file = op_file == READ_TEST;
    struct fuse_file_info fi = {0};

    if(read_file)
        op_num = 1024*1024;
    if(op_file == READ_TEST)
        fd = xmp_open(name, &fi);
    else {
        fi.flags = O_WRONLY|O_CREAT;
        fd = xmp_open(name, &fi);
    }
    if(fd < 0){
        fprintf(stderr, "open [%s] failed ret [%d]\n", name, fd);
        *pret = RET_ERROR;
        return;
    }
    memset(buf, '\0', buf_len);
    fprintf(stderr, "%d test on [%s]\n", id, name);
    for(int i=0; i < op_num; ++i){
        int r;
        if(read_file)
            r = xmp_read(name, buf, buf_len, wr, &fi);
        else
            r = xmp_write(name, buf, buf_len, wr, &fi);
        if(buf_len != r){
            if(read_file && r < buf_len){
                break;
            }
            fprintf(stderr, "read or write on remote file [%s] failed ret [%d]\n", name, r);
            *pret = RET_ERROR;
            break;
        }
        wr += r;
        *completed = wr;
        LOG("process %d wrote %lldB\n", id, wr);
    }
    LOG("try to close file\n");
    xmp_release(name, &fi);
    fprintf(stderr, "%d ok to close file %s\n", id, name);
    if(*pret == RET_STARTED)
        *pret = RET_OK;
    delete[] buf;
}

void BenchArgs::_cmd_testreadwrite(int op_file)
{
    std::thread workers[num];
    int64_t completed[num];
    int rets[num];
    for(int k=0;k<num; ++k){
        rets[k] = RET_NONE;
        workers[k]= std::thread(_test_read_write_thread, this, k, op_file, &completed[k], &rets[k]);
    }
    fprintf(stderr, "start to %s %d files with size %dGB\n", 
        op_file&READ_TEST? "read" : "write", num, size_gb);
    // test
    int64_t lastwr = 0;
    int k=0;
    double st = getnow();
    for(k=1; ;++k){
        sleep(1);
        int dead = 0;
        int64_t wr = 0;
        for(int j=0; j< num; ++j){
            int r = rets[j];
            wr += completed[j];
            if(r > RET_STARTED){
                ++dead;
                continue;
            }
        }
        wr  = wr >> 20;
        int avg = (int)(wr/(getnow() - st));
        fprintf(stderr, "%d: %lldMB %lldMB/s %dMB/s\n", k, wr, wr - lastwr, avg);
        fflush(stderr);
        lastwr = wr;
        if(dead == num){
            break;
        }
    }
    fprintf(stderr, "Avg: %ds, %lldMB, %lldMB/s\n", k, lastwr, (int)(lastwr/(getnow() - st)));
    // wait for workers
    for(int k=0;k<num; ++k){
        workers[k].join();
    }
}

void BenchArgs::do_bench()
{
    for(int k=0; k < run_num; ++k){
        if(k > 0){
            fprintf(stderr, "\nrun %d test\n", k+1);
        }
        _cmd_testreadwrite(WRITE_TEST);
        _cmd_testreadwrite(READ_TEST);
    }
}

static char usage[] = "usage: ./xmp-mem mountpoint [options]\n"
               "\n"
               "general options:\n"
               "    -o opt,[opt...]        mount options\n"
               "    -h   --help            print help\n"
               "    -V   --version         print version\n"
               "    -g                     debug xmp-mem\n"
               "    -m {memory_gb}         max memory in gb, default is 8, min is 1\n"
               "    --bench [num] [size_gb] [run_num]\n"
               "                           num: files num, default is 4\n"
               "                           size_gb: every file size in GB, default is 1\n"
               "                           run_num: test num\n"
               "\n"
               "    -o umask=M             set file and directory permissions (octal)\n"
               "    -o fileumask=M         set file permissions (octal)\n"
               "    -o dirumask=M          set directory permissions (octal)\n"
               "    -o fsname=M            set filesystem name\n"
               "    -o volname=M           set volume name\n"
               "    -o uncname=M           set UNC name\n"
               "    -o setsignals=M        set signal usage (1 to use)\n"
               "    -o daemon_timeout=M    set timeout in seconds\n"
               "    -o alloc_unit_size=M   set allocation unit size\n"
               "    -o sector_size=M       set sector size\n"
               "    -n                     use network drive\n"
;

/*
 * prog
usage: ./xmp-mem mountpoint [options]

*/
int main(int argc, char *argv[])
{
    fs.max_files = MAX_FILE_NUM;
    fs.used_kb = 0;
    fs.total_kb = MAX_FS_KB;
    char* newargv[100];
    int n = 0;
    BenchArgs* pbench=nullptr;
    
    newargv[n++] = argv[0];
    newargv[n++] = "-f";
    for(int k=1; k < argc; ++k){
        if(0==strcmp("-f", argv[k])){
            continue;
        } else if(0==strcmp("--bench", argv[k])){
            int num = 1;
            int gb = 1;
            int run_num = 1;
            if(++k < argc){
                if(1!=sscanf(argv[k], "%d", &num) || num < 1){
                    printf("error: invalid bench file num\n%s", usage);
                    return 0;
                }
            }
            if(++k < argc){
                if(1!=sscanf(argv[k], "%d", &gb) || gb < 1){
                    printf("error: invalid bench file size\n%s", usage);
                    return 0;
                }
            }
            if(++k < argc){
                if(1!=sscanf(argv[k], "%d", &run_num) || run_num < 1){
                    printf("error: invalid bench test num\n%s", usage);
                    return 0;
                }
            }
            pbench = new BenchArgs(num, gb, run_num);
        } else if(0==strcmp("-g", argv[k])){
            debug = true;
        } else if(0==strcmp("-m", argv[k])){
            int gb = 1;
            if(1==sscanf(argv[k+1], "%d", &gb))
                fs.total_kb = gb << 20;
            ++k;
        } else if(0==strcmp("-h", argv[k])){
            printf("sizeof size_t %d\n", sizeof(size_t));
            printf("%s", usage);
            return 0;
        } else {
            newargv[n++] = argv[k];
        }
    }
    for(int k=1;k<n;++k)
        LOG("arg[%d]=%s\n", k, newargv[k]);
#define DEFINE_OP(op) xmp_oper.op = xmp_##op
    printf("mem-xmpfs inited\n");
    umask(0);
    struct fuse_operations xmp_oper = {0};
    DEFINE_OP(getattr);
    DEFINE_OP(readlink);
    DEFINE_OP(mknod);
    DEFINE_OP(mkdir);
    DEFINE_OP(unlink);
    DEFINE_OP(rmdir);
    DEFINE_OP(symlink);
    DEFINE_OP(rename);
    DEFINE_OP(link);
    DEFINE_OP(chmod);
    DEFINE_OP(chown);
    DEFINE_OP(truncate);
    DEFINE_OP(open);
    DEFINE_OP(read);
    DEFINE_OP(write);
    DEFINE_OP(statfs);
    DEFINE_OP(release);
    DEFINE_OP(fsync);
    DEFINE_OP(setxattr);
    DEFINE_OP(getxattr);
    DEFINE_OP(listxattr);
    DEFINE_OP(removexattr);
    DEFINE_OP(opendir);
    DEFINE_OP(readdir);
    DEFINE_OP(releasedir);
    DEFINE_OP(create);
    if(pbench){
        pbench->do_bench();
        return 0;
    } else {
        int ret = fuse_main(n, newargv, &xmp_oper, NULL);
        return ret;
    }
}

4. 编译

使用cygwin编译的Makefile如下。

#Makefile by fwt
CPP       = g++ -DWIDE_OFF_T -D__USE_FILE_OFFSET64 \
 -I. -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE\
-D_REENTRANT -D_THREAD_SAFE -D__STDC_FORMAT_MACROS \
-fno-strict-aliasing -fsigned-char -Wno-invalid-offsetof \
-fno-builtin-malloc -fno-builtin-calloc \
-fno-builtin-realloc -fno-builtin-free -DPIC -std=c++17 -O1  

%.o: %.cpp
    $(CPP) -w -c $^ -o $@

XMP_MEM_OBJECTS=dokanfuse.o \
    fuse_helpers.o \
    fuse_opt.o \
    fusemain.o \
    fusexmp-mem.o \
    utils.o

ALL: xmp-mem.exe

xmp-mem.exe: $(XMP_MEM_OBJECTS) dokan1.lib  
    $(CPP) $(CFLAGS) $(CLIBS) -o $@ $^  -unicode
    @echo "**************************************************************"
    @echo "MAKE "$@" FINISH"
    @echo "**************************************************************"

clean:
    rm -f  $(XMP_MEM_OBJECTS)   xmp-mem.exe

执行make -j4即可编译。

5.运行测试

5.1 运行挂载

采用默认参数挂载,挂载盘符M。

$ ./xmp-mem.exe M:
mem-xmpfs inited

默认使用8GB内存,增加-m可以指定内存大小,例如使用16GB内存,则执行命令./xmp-mem.exe -m 16 M:即可。
打开资源管理器,检验挂载成果。

挂载出来的M盘

如上图,我们成功的挂载出了M盘,注意查看M盘的使用空间,正是8GB。

5.2 基本功能测试

进入M盘,我们可以创建文件。


创建文本文件

打开这个文本写一点数据进去。


写入文本

还可以复制一个大文件进去。


从本地硬盘复制文件到M盘

复制完成后如下。


完成复制大文件

当前已经有两个文件,总量约4.2GB,再检查空间的可用量,已经减少到3G多。


使用量统计

删除文件,可用量又恢复了。


删除文件,可用量恢复

5.3 卸载

最后是卸载M盘,打开任务管理器,找到进程然后杀死。


任务管理器
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343

推荐阅读更多精彩内容

  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,085评论 1 32
  • ORA-00001: 违反唯一约束条件 (.) 错误说明:当在唯一索引所对应的列上键入重复值时,会触发此异常。 O...
    我想起个好名字阅读 5,169评论 0 9
  • 当数据量增大到超出了单个物理计算机存储容量时,有必要把它分开存储在多个不同的计算机中。那些管理存储在多个网络互连的...
    单行线的旋律阅读 1,907评论 0 7
  • 最近在Android上测试64GB的TF卡支持情况,发现在Windows平台上默认会把卡格式化为exFAT,而不是...
    网路元素阅读 2,468评论 0 1
  • feisky云计算、虚拟化与Linux技术笔记posts - 1014, comments - 298, trac...
    不排版阅读 3,813评论 0 5