Last active
December 17, 2016 11:15
-
-
Save umedaikiti/6375148 to your computer and use it in GitHub Desktop.
fuseのサンプルプログラム
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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