System/Common

FUSE(Filesystem in USEerspace)

linuxism 2012. 10. 28. 11:17


이번에는 FUSE 파일 시스템에 대해서 살펴 보려고 한다.

아직 커널에 대해서 깊이 알고 있지 못한 상태에서 바로 커널 레벨의 파일 시스템을 만드는 것은 여러모로 무리가 있다. 따라서, 이번에는 사용자 어플리케이션 레벨에서 파일 시스템을 만들 수 있는 방법을 먼저 알아보도록 하겠다.

FUSE는 Filesystem in USEr space의 약자로서 http://fuse.sourceforge.net/ 에서 프로젝트가 진행중이다. FUSE는 리눅스 커널 2.6.15부터는 기본적으로 탑재되어 있다. 물론, FUSE가 부팅시부터 사용가능하도록 되어 있지는 않을 수 있으며, 커널 설정을 확인할 필요가 있다. 만약, 그 이전 버전에서 사용하고자 한다면 코드를 받아서 직접 컴파일해서 사용하면 된다.

FUSE의 장점은 어플리케이션 레벨에서 작업이 이루어지기 때문에 보안이나 안정성 등의 면에 있어서 좀더 나을 수 있다는 것이며, 리눅스 이외의 운영체제에서도 FUSE가 사용 가능하기 때문에 한 번 작성된 사용자 파일 시스템을 여러 운영체제에서 큰 문제 없이 돌릴 수 있다는 장점이 있다.

현재, FUSE는 리눅스를 비롯해, Mac OS X, 윈도우즈, 솔라리스 등등에서 사용 가능하다. 자세한 것은 프로젝트 사이트에서 확인 가능하다.

하지만, FUSE는 단점도 있는데, 가장 큰 단점으로는 계층이 추가됨으로 인해서 속도저하가 발생할 수 있다는 점이다.

FUSE를 사용하기 위해서는 세 가지 요소가 필요하다. 
첫째, 커널 모듈(fuse.ko)가 적재되어 있어야 한다. 현재 커널에 FUSE가 적재되어 있는지 확인하기 위해서는 /proc/filesystems를 살펴보면 된다.
둘째, 유저스페이스 라이브러들이 필요하다. 여기에는 libfuse.so, libfuse.a가 해당된다.
셋째, 사용자가 작성한 파일시스템 코드가 필요하다. 

만약, 사용하는 시스템에 FUSE가 설치되어 있지 않다면, http://fuse.sourceforge.net/ 에서 소스를 받아서 다음 과정을 거쳐 컴파일을 먼저 해야 한다.

설치 과정도 해당 사이트에 잘 나와 있지만, 간단히 보자면 다음과 같은 단계를 거치면 /usr/local/ 밑에 설치가 된다.

[work] $ ./configure
[work] $ make
[work] $ make install

만약 설치되는 경로를 바꾸고 싶다면  ./configure --prefix=/usr 등과 같이 원하는 경로를 prefix 다음에 지정하면 된다.

다음에는 FUSE를 기반으로 한 사용자 파일 시스템을 작성한 후 다음과 같은 형태로 fuse 라이브러리를 사용하도록 컴파일해야 한다.

[work] $ gcc -o my_filesystem my_fs.c -lfuse


다음에는 해당 프로그램을 컴파일하고 사용하면된다.

./my_filesystem   /마운트포인트
 
만약 컴파일이나 실행이 안된다면 라이브러리 경로가 제대로 되어 있는지 확인을 해야 한다. 만약, FUSE 라이브러리가 있는 /usr/local/lib 경로가 빠져 있다면 다음과 같이 경로를 먼저 잡아 준 후 수행해야 한다.

LD_LIBRARY_PATH=/usr/local/lib   ./my_filesystem   /마운트포인트

파일 시스템을 마운트 하는 것은 mount 명령을 사용하지 않으며, 작성한 사용자 프로그램을 올리면서 뒤에 인자로 마운트할 경로를 적어 주면 된다.

반면, 마운트를 해제할 경우에는 기존의 umount 명령을 사용해서 처리하면 된다.

umount /마운트포인트

다음 그림은 FUSE 파일 시스템의 전체적인 그림을 나타내는 것으로 fuse 프로젝트 사이트에서 가져온 것이다.
사용자 삽입 이미지
맥 버전의 FUSE 경우에는  http://code.google.com/p/macfuse/ 에서 코드를 구할 수 있다. 맥의 경우에는 다운로드후 더블클릭만으로 쉽게 설치가 되므로 더 편하게 사용할 수 있다.
MacFUSE 로고


다음 코드는 FUSE  프로젝트 사이트에서 제공하는 코드로 간단히 파일시스템을 구현하는 방법을 보여주고 있다.
/*
    FUSE: Filesystem in Userspace
    Copyright (C) 2001-2005  Miklos Szeredi <miklos@szeredi.hu>
    This program can be distributed under the terms of the GNU GPL.
    See the file COPYING.
*/
#include <fuse.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
static const char *hello_str = "Hello World!\n";
static const char *hello_path = "/hello";
static int hello_getattr(const char *path, struct stat *stbuf)
{
    int res = 0;
    memset(stbuf, 0, sizeof(struct stat));
    if(strcmp(path, "/") == 0) {
        stbuf->st_mode = S_IFDIR | 0755;
        stbuf->st_nlink = 2;
    }
    else if(strcmp(path, hello_path) == 0) {
        stbuf->st_mode = S_IFREG | 0444;
        stbuf->st_nlink = 1;
        stbuf->st_size = strlen(hello_str);
    }
    else
        res = -ENOENT;
    return res;
}
static int hello_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
                         off_t offset, struct fuse_file_info *fi)
{
    (void) offset;
    (void) fi;
    if(strcmp(path, "/") != 0)
        return -ENOENT;
    filler(buf, ".", NULL, 0);
    filler(buf, "..", NULL, 0);
    filler(buf, hello_path + 1, NULL, 0);
    return 0;
}
static int hello_open(const char *path, struct fuse_file_info *fi)
{
    if(strcmp(path, hello_path) != 0)
        return -ENOENT;
    if((fi->flags & 3) != O_RDONLY)
        return -EACCES;
    return 0;
}
static int hello_read(const char *path, char *buf, size_t size, off_t offset,
                      struct fuse_file_info *fi)
{
    size_t len;
    (void) fi;
    if(strcmp(path, hello_path) != 0)
        return -ENOENT;
    len = strlen(hello_str);
    if (offset < len) {
        if (offset + size > len)
            size = len - offset;
        memcpy(buf, hello_str + offset, size);
    } else
        size = 0;
    return size;
}
static struct fuse_operations hello_oper = {
    .getattr = hello_getattr,
    .readdir = hello_readdir,
    .open = hello_open,
    .read = hello_read,
};
int main(int argc, char *argv[])
{
    return fuse_main(argc, argv, &hello_oper);
}






출처 - http://blog.naver.com/PostView.nhn?blogId=xogml_blog&logNo=130141202446