Skip to content

Instantly share code, notes, and snippets.

@umedaikiti
Last active December 17, 2016 11:15
Show Gist options
  • Save umedaikiti/6375148 to your computer and use it in GitHub Desktop.
Save umedaikiti/6375148 to your computer and use it in GitHub Desktop.
fuseのサンプルプログラム
/*
* fuseのサンプルプログラム
* リンクは実装しない
* 起動は sudo ./fusetest <mountpoint>
* 終了は sudo umount <mountpoint>
*/
#define FUSE_USE_VERSION 28
#include <string.h>
#include <libgen.h>
#include <errno.h>
#include <fuse.h>
#include <time.h>
#include <syslog.h>
#define ENTRY_MAX_DATA 128
#define ENTRY_MAX_NAME 16
//struct entryのフラグ
#define FLAG_TYPE_MASK 0x1
#define FLAG_TYPE_DIR 0x0
#define FLAG_TYPE_FILE 0x1
#define FLAG_ENTRY_USED 0x100
#define NUM_OF_ENTRIES 100
#define for_each_entry(i) for(i=0;i<NUM_OF_ENTRIES;i++) if(entries[i].flags & FLAG_ENTRY_USED)
//ファイルまたはディレクトリを表す
struct entry{
int flags, size; //sizeはdataのサイズ
char data[ENTRY_MAX_DATA]; //ファイルを表すときのみ使う
char name[ENTRY_MAX_NAME];
struct entry *parent;
mode_t mode; //パーミッションとか
};
//flagsとmodeの役割分担がやや曖昧
struct entry entries[NUM_OF_ENTRIES];
static void entry_init(struct entry *e,struct entry *parent, char *name, int flags, mode_t mode)
{
e->flags = flags | FLAG_ENTRY_USED;
e->size = 0;
if(strlen(name) >= ENTRY_MAX_NAME){
//ENAMETOOLONGだが、面倒なので無視
}
strncpy(e->name, name, ENTRY_MAX_NAME);
e->parent = parent;
e->mode = mode;
}
static int entry_is_dir(struct entry *e)
{
return (e->flags & FLAG_TYPE_MASK) == FLAG_TYPE_DIR;
}
static int entry_is_file(struct entry *e)
{
return (e->flags & FLAG_TYPE_MASK) == FLAG_TYPE_FILE;
}
static struct entry *get_unused_entry()
{
int i;
for(i=0;i<NUM_OF_ENTRIES;i++){
if((entries[i].flags & FLAG_ENTRY_USED) == 0){
return entries + i;
}
}
return NULL;
}
//名前の重複は確認しない
static int entry_mkdir(struct entry *parent, char *name, mode_t mode)
{
struct entry *e = get_unused_entry();
if(e){
entry_init(e, parent, name, FLAG_TYPE_DIR, mode);
return 0;
}
else return -ENOSPC;
}
static int create_file(struct entry *parent, char *name, mode_t mode)
{
struct entry *e = get_unused_entry();
if(e){
entry_init(e, parent, name, FLAG_TYPE_FILE, mode);
return 0;
}
else return -ENOSPC;
}
static int entry_is_root(struct entry *e)
{
return e->parent == NULL;
}
/*
* 引数のnはバッファのサイズ(sizeof(buf))
* 成功: strlen(buf)を返す
* エラー: -1を返す
*/
static int entry_get_path(struct entry *e, char *buf, int n)
{
int i, len;
if(entry_is_root(e) && n >= 2){
strcpy(buf, "/");
return 1;
}
i = entry_get_path(e->parent, buf, n);
//buf == "/" のとき、"//abc"のようにならないよう気をつける
if(i == 1){
i = 0;
}
len = strlen(e->name);
if(i != -1 && i + len + 2 <= n){//+2は '/' '\0'の分
buf[i++] = '/';
strncpy(buf + i, e->name, n - i);
return i + len;
}
else return -1;
}
//パスの文字列を比較して探す
//検索にハッシュを使ったほうがいいかも
static struct entry *get_entry(const char *path)
{
char buf[512];//十分大きく
int i, err;
for_each_entry(i){
err = entry_get_path(entries+i, buf, sizeof(buf));
if(err != -1 && strcmp(buf, path) == 0){
return entries + i;
}
}
return NULL;
}
static int fusetest_getattr(const char *path, struct stat *stbuf)
{
struct entry *e = get_entry(path);
syslog(LOG_INFO, "getattr: %s\n", path);
if(!e) return -ENOENT;
memset(stbuf, 0, sizeof(struct stat));
switch(e->flags & FLAG_TYPE_MASK){
case FLAG_TYPE_DIR:
stbuf->st_mode = S_IFDIR | e->mode;
stbuf->st_nlink = 2;
break;
case FLAG_TYPE_FILE:
stbuf->st_mode = S_IFREG | e->mode;
stbuf->st_nlink = 1;
stbuf->st_size = e->size;
break;
default:
return -ENOENT;
}
//タイムスタンプ
//面倒なので、現在時刻を用いる
stbuf->st_atime = stbuf->st_mtime = stbuf->st_ctime = time(NULL);
return 0;
}
static int fusetest_readdir(const char *path,
void *buf,
fuse_fill_dir_t filler,
off_t offset,
struct fuse_file_info *fi)
{
struct entry *e = get_entry(path);
int i;
if(!e || !entry_is_dir(e)) return -ENOENT;
filler(buf, ".", NULL, 0);
filler(buf, "..", NULL, 0);
for_each_entry(i){
if(entries[i].parent == e){
filler(buf, entries[i].name, NULL, 0);
}
}
return 0;
}
static int fusetest_open(const char *path, struct fuse_file_info *fi)
{
struct entry *e = get_entry(path);
syslog(LOG_INFO, "open: flags = %x\n", fi->flags);
if(!e || !entry_is_file(e)) return -ENOENT;
return 0;
}
static int fusetest_read(const char *path,
char *buf,
size_t size,
off_t offset,
struct fuse_file_info *fi)
{
struct entry *e = get_entry(path);
if(!e || !entry_is_file(e)){
return -ENOENT;
}
if(offset > e->size) return 0;
if(offset + size > e->size)
size = e->size - offset;
memcpy(buf, e->data + offset, size);
return size;
}
static int fusetest_write(const char *path,
const char *buf,
size_t size,
off_t offset,
struct fuse_file_info *fi)
{
struct entry *e = get_entry(path);
if(!e || !entry_is_file(e)){
return -ENOENT;
}
if(offset >= ENTRY_MAX_DATA)
return -ENOSPC;
if(offset + size > ENTRY_MAX_DATA)
size = ENTRY_MAX_DATA - offset;
memcpy(e->data + offset, buf, size);
e->size = offset + size;
return size;
}
static int fusetest_mkdir(const char *path, mode_t mode)
{
char tmp[512];
struct entry *parent;
syslog(LOG_INFO, "mkdir: %o\n", mode);
if(strlen(path) >= sizeof(tmp)) return -ENAMETOOLONG;
strncpy(tmp, path, sizeof(tmp));
parent = get_entry(dirname(tmp));
if(get_entry(path)) return -EEXIST;
if(!parent) return -ENOENT;
if(!entry_is_dir(parent)) return -ENOTDIR;
strncpy(tmp, path, sizeof(tmp));
return entry_mkdir(parent, basename(tmp), mode);
}
static int fusetest_rmdir(const char *path)
{
struct entry *e = get_entry(path);
if(!e) return -ENOENT;
if(!entry_is_dir(e)) return -ENOTDIR;
int i;
for_each_entry(i){
if(entries[i].parent == e) return -ENOTEMPTY;
}
e->flags = 0;
return 0;
}
static int fusetest_create(const char *path, mode_t mode, struct fuse_file_info *fi)
{
char tmp[512];
struct entry *parent;
syslog(LOG_INFO, "create: %o\n", mode);
if(strlen(path) >= sizeof(tmp)) return -ENAMETOOLONG;
strncpy(tmp, path, sizeof(tmp));
parent = get_entry(dirname(tmp));
if(get_entry(path)) return -EEXIST;
if(!parent) return -ENOENT;
strncpy(tmp, path, sizeof(tmp));
return create_file(parent, basename(tmp), mode);
}
static int fusetest_unlink(const char *path)
{
struct entry *e = get_entry(path);
if(!e) return -ENOENT;
if(entry_is_dir(e)) return -EISDIR;
e->flags = 0; //e->flags & FLAG_ENTRY_USED == 0 にする
return 0;
}
static int fusetest_truncate(const char *path, off_t size)
{
struct entry *e = get_entry(path);
if(!e) return -ENOENT;
if(entry_is_dir(e)) return -EISDIR;
if(size > ENTRY_MAX_DATA || size < 0) return -EINVAL;
e->size = size;
return 0;
}
static int fusetest_chmod(const char *path, mode_t mode)
{
struct entry *e = get_entry(path);
if(!e) return -ENOENT;
syslog(LOG_INFO, "chmod: %o\n", mode);
e->mode = mode;
return 0;
}
static int fusetest_chown(const char *path, uid_t uid, gid_t gid)
{
syslog(LOG_INFO, "chown: uid = %d, gid = %d\n", uid, gid);
return -ENOSYS; //未実装としておく
}
//touchに必要
static int fusetest_utimens(const char *path, const struct timespec tv[2])
{
syslog(LOG_INFO, "utimens: path = %s\n", path);
syslog(LOG_INFO, "atime: %s .%09d\n", ctime(&tv[0].tv_sec), (int)tv[0].tv_nsec);
syslog(LOG_INFO, "mtime: %s .%09d\n", ctime(&tv[1].tv_sec), (int)tv[1].tv_nsec);
// return -ENOSYS;
return 0;
}
//適当にファイルやディレクトリを作成しておく
static void *fusetest_init(struct fuse_conn_info *conn)
{
entry_init(entries, NULL, "", FLAG_TYPE_DIR, 0755);
entry_mkdir(entries, "dir1", 0755);
create_file(entries, "hello.txt", 0644);
struct entry *e = get_entry("/dir1");
create_file(e, "aaa", 0644);
e = get_entry("/hello.txt");
e->size = 7;
memcpy(e->data, "abcdefg", e->size);
return NULL;
}
//http://fuse.sourceforge.net/doxygen/index.html 参照
//エラーコードはman 2 open, man 3 errnoなどで調べる
static struct fuse_operations fusetest_op = {
.getattr = fusetest_getattr,
.readdir = fusetest_readdir,
.open = fusetest_open,
.read = fusetest_read,
.write = fusetest_write,
.mkdir = fusetest_mkdir,
.rmdir = fusetest_rmdir,
.create = fusetest_create,
.unlink = fusetest_unlink,
.truncate = fusetest_truncate,
.chmod = fusetest_chmod,
.chown = fusetest_chown,
.utimens = fusetest_utimens,
.init = fusetest_init,
};
//destroyでファイルに保存、とかもできると思う
int main(int argc, char *argv[])
{
openlog("fusetest", LOG_PID, LOG_USER);
return fuse_main(argc, argv, &fusetest_op, NULL);
}
all: fusetest
fusetest: fusetest.c
gcc -Wall fusetest.c `PKG_CONFIG_PATH=/usr/lib/i386-linux-gnu/pkgconfig/ pkg-config fuse --cflags --libs` -o fusetest
.PHONY: clean
clean:
rm -f fusetest
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment