'2007/11/12'에 해당되는 글 2건

  1. 2007/11/12 FUSE 파일 시스템 Overview
  2. 2007/11/12 파일 시스템의 주요 구조체들
사용자 삽입 이미지

이번에는 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);
}


크리에이티브 커먼즈 라이선스
Creative Commons License
이올린에 북마크하기(0) 이올린에 추천하기(0)
Posted by Daniel Kwon
register_filesystem을 통해서 전달되는 구조체의 내용 중 가장 중요한 부분은 바로 super block에 대한 정보를 반환하는 get_sb 함수이다. 이 함수를 통해서 파일시스템의 가장 중요하며 시작점에 해당하는 역할을 하는 슈퍼블럭에 대한 구조체인 struct super_block을 얻을 수 있다.

슈퍼블럭은 파일시스템마다 불리는 이름이 다르며, MS-DOS의 경우에는 FAT(File Allocation Table)이라고 부른다. 여기에는 파일을 찾기 위한 기본적인 정보들이 들어 있다. 물론, 유닉스 계열의 파일 시스템에서는 이에 대한 내용이 i-node 테이블에서 주로 관리된다. 일반적으로 슈퍼블럭은 해당 파일시스템에 대한 기본적인 정보들을 가지고 있다고 보면 된다.

다음 그림은 ext2 파일 시스템의 전체 레이아웃으로서 슈퍼블럭이 차지하는 위치를 알 수 있다.


사용자 삽입 이미지
 
908 struct super_block {
909         struct list_head        s_list;         /* Keep this first */
910         dev_t                   s_dev;          /* search index; _not_ kdev_t */
911         unsigned long           s_blocksize;
912         unsigned char           s_blocksize_bits;
913         unsigned char           s_dirt;
914         unsigned long long      s_maxbytes;     /* Max file size */
915         struct file_system_type *s_type;
916         const struct super_operations   *s_op;
917         struct dquot_operations *dq_op;
918         struct quotactl_ops     *s_qcop;
919         struct export_operations *s_export_op;
920         unsigned long           s_flags;
921         unsigned long           s_magic;
922         struct dentry           *s_root;
923         struct rw_semaphore     s_umount;
924         struct mutex            s_lock;
925         int                     s_count;
926         int                     s_syncing;
927         int                     s_need_sync_fs;
928         atomic_t                s_active;
929 #ifdef CONFIG_SECURITY
930         void                    *s_security;
931 #endif
932         struct xattr_handler    **s_xattr;
933
934         struct list_head        s_inodes;       /* all inodes */
935         struct list_head        s_dirty;        /* dirty inodes */
936         struct list_head        s_io;           /* parked for writeback */
937         struct hlist_head       s_anon;         /* anonymous dentries for (nfs) exporting */
938         struct list_head        s_files;
939
940         struct block_device     *s_bdev;
941         struct mtd_info         *s_mtd;
942         struct list_head        s_instances;
943         struct quota_info       s_dquot;        /* Diskquota specific options */
944
945         int                     s_frozen;
946         wait_queue_head_t       s_wait_unfrozen;
947
948         char s_id[32];                          /* Informational name */
949
950         void                    *s_fs_info;     /* Filesystem private info */
951
952         /*
953          * The next field is for VFS *only*. No filesystems have any business
954          * even looking at it. You had been warned.
955          */
956         struct mutex s_vfs_rename_mutex;        /* Kludge */
957
958         /* Granularity of c/m/atime in ns.
959            Cannot be worse than a second */
960         u32                s_time_gran;
961
962         /*
963          * Filesystem subtype.  If non-empty the filesystem type field
964          * in /proc/mounts will be "type.subtype"
965          */
966         char *s_subtype;
967 };
<위치 : http://lxr.linux.no/source/include/linux/fs.h#L908>


파일 시스템을 등록할 때 전달되는 struct file_system_type의 get_sb() 함수를 통해 반환되도록 되어 있는 struct super_block 구조체는 슈퍼 블럭에서 관리하는 정보들을 모두 얻을 수 있도록 되어 있다. 물론, 파일 시스템에 다라서는 일부 내용이 무의미할 수 도 있다. 어쨌든, 파티션상에 존재하는 해당 파일 시스템에 대해서 데이타를 다룰 때 사용하게 되는 여러가지 정보들이 여기에 존재하게 된다.

이 중에서 슈퍼블럭을 다루기 위해서 필요한 연산들이 916번째 줄에 있는 struct super_operations *s_op 필드이다. struct super_operations는 다음과 같이 정의되어 있다.

1161 struct super_operations {
1162         struct inode *(*alloc_inode)(struct super_block *sb);
1163         void (*destroy_inode)(struct inode *);
1164
1165         void (*read_inode) (struct inode *);
1166  
1167         void (*dirty_inode) (struct inode *);
1168         int (*write_inode) (struct inode *, int);
1169         void (*put_inode) (struct inode *);
1170         void (*drop_inode) (struct inode *);
1171 void (*delete_inode) (struct inode *);
1172         void (*put_super) (struct super_block *);
1173         void (*write_super) (struct super_block *);
1174         int (*sync_fs)(struct super_block *sb, int wait);
1175         void (*write_super_lockfs) (struct super_block *);
1176         void (*unlockfs) (struct super_block *);
1177         int (*statfs) (struct dentry *, struct kstatfs *);
1178         int (*remount_fs) (struct super_block *, int *, char *);
1179         void (*clear_inode) (struct inode *);
1180         void (*umount_begin) (struct vfsmount *, int);
1181
1182         int (*show_options)(struct seq_file *, struct vfsmount *);
1183         int (*show_stats)(struct seq_file *, struct vfsmount *);
1184 #ifdef CONFIG_QUOTA
1185         ssize_t (*quota_read)(struct super_block *, int, char *, size_t, loff_t);
1186         ssize_t (*quota_write)(struct super_block *, int, const char *, size_t, loff_t);
1187 #endif
1188 };
<위치 : http://lxr.linux.no/source/include/linux/fs.h#L1161>

super_operation 구조체에서 하는 일은 super_block을 다루는 코드와 함께 super_block을 기반으로 실제 파일에 해당하는 정보를 찾을 수 있는 inode를 다루는 연산들로 구성되어 있다. 새로운 파일이 생성될 경우에는 alloc_inode를 사용하게 되며, 파일 정보와 관련해서 read_inode(), write_inode() 함수들도 사용하게 된다. mount 관련된 구조체인 vfsmount는 이 전번 글에서 간단히 살펴보았으므로 여기에서는 그냥 넘어간다.

inode는 실제로 파일을 만들기 위한 기본이 되는 정보로서 이를 통해 어떤 파일이 어디에 있으며, 어떠한 속성으로 만들어 졌는지, 크기는 얼마인지, 소유자는 누구인지, 접근 권한은 어떻게 되는지 등의 정보를 볼 수 있다. 즉, 일반적으로 ls -l 명령을 사용했을 때 나오는 결과에 해당하는 정보를 가지고 있다고 보면 된다.

530 struct inode {
531         struct hlist_node       i_hash;
532         struct list_head        i_list;
533         struct list_head        i_sb_list;
534         struct list_head        i_dentry;
535         unsigned long           i_ino;
536         atomic_t                i_count;
537         unsigned int            i_nlink;
538         uid_t                   i_uid;
539         gid_t                   i_gid;
540         dev_t                   i_rdev;
541         unsigned long           i_version;
542         loff_t                  i_size;
543 #ifdef __NEED_I_SIZE_ORDERED
544         seqcount_t              i_size_seqcount;
545 #endif
546         struct timespec         i_atime;
547         struct timespec         i_mtime;
548         struct timespec         i_ctime;
549         unsigned int            i_blkbits;
550         blkcnt_t                i_blocks;
551         unsigned short          i_bytes;
552         umode_t                 i_mode;
553         spinlock_t              i_lock; /* i_blocks, i_bytes, maybe i_size */
554         struct mutex            i_mutex;
555         struct rw_semaphore     i_alloc_sem;
556         const struct inode_operations   *i_op;
557         const struct file_operations    *i_fop; /* former ->i_op->default_file_ops */
558         struct super_block      *i_sb;
559         struct file_lock        *i_flock;
560         struct address_space    *i_mapping;
561         struct address_space    i_data;
562 #ifdef CONFIG_QUOTA
563         struct dquot            *i_dquot[MAXQUOTAS];
564 #endif
565         struct list_head        i_devices;
566         union {
567                 struct pipe_inode_info  *i_pipe;
568                 struct block_device     *i_bdev;
569                 struct cdev             *i_cdev;
570         };
571         int                     i_cindex;
572
573         __u32                   i_generation;
574
575 #ifdef CONFIG_DNOTIFY
576         unsigned long           i_dnotify_mask; /* Directory notify events */
577         struct dnotify_struct   *i_dnotify; /* for directory notifications */
578 #endif
579
580 #ifdef CONFIG_INOTIFY
581         struct list_head        inotify_watches; /* watches on this inode */
582         struct mutex            inotify_mutex;  /* protects the watches list */
583 #endif
584
585         unsigned long           i_state;
586         unsigned long           dirtied_when;   /* jiffies of first dirtying */
587
588         unsigned int            i_flags;
589
590         atomic_t                i_writecount;
591 #ifdef CONFIG_SECURITY
592         void                    *i_security;
593 #endif
594         void                    *i_private; /* fs or device private pointer */
595 };
<위치 : http://lxr.linux.no/source/include/linux/fs.h#L530>

i-node를 통해 파일의 실제 데이타를 제외한 모든 정보를 얻을 수 있다. 일부 파일시스템(FAT, FAT16, FAT32 등등)에 대해서는 struct inode 구조체의 일부 필드가 의미없을 수 도 있다. inode 구조체에서는 실제 inode를 다루는 방법과 파일을 다루는 방법을 위해서 두 가지 구조체를 제공하고 있다.

먼저, inode를 다루는 함수들을 위해서는 struct inode_operations *i_op 필드가 제공된다. 이 구조체는 다음과 같은 형태를 가지고 있다.

1118 struct inode_operations {
1119         int (*create) (struct inode *,struct dentry *,int, struct nameidata *);
1120         struct dentry * (*lookup) (struct inode *,struct dentry *, struct nameidata *);
1121         int (*link) (struct dentry *,struct inode *,struct dentry *);
1122         int (*unlink) (struct inode *,struct dentry *);
1123         int (*symlink) (struct inode *,struct dentry *,const char *);
1124         int (*mkdir) (struct inode *,struct dentry *,int);
1125         int (*rmdir) (struct inode *,struct dentry *);
1126         int (*mknod) (struct inode *,struct dentry *,int,dev_t);
1127 int (*rename) (struct inode *, struct dentry *,
1128 struct inode *, struct dentry *);
1129 int (*readlink) (struct dentry *, char __user *,int);
1130         void * (*follow_link) (struct dentry *, struct nameidata *);
1131 void (*put_link) (struct dentry *, struct nameidata *, void *);
1132         void (*truncate) (struct inode *);
1133         int (*permission) (struct inode *, int, struct nameidata *);
1134         int (*setattr) (struct dentry *, struct iattr *);
1135         int (*getattr) (struct vfsmount *mnt, struct dentry *, struct kstat *);
1136         int (*setxattr) (struct dentry *, const char *,const void *,size_t,int);
1137         ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t);
1138         ssize_t (*listxattr) (struct dentry *, char *, size_t);
1139         int (*removexattr) (struct dentry *, const char *);
1140         void (*truncate_range)(struct inode *, loff_t, loff_t);
1141 };
<위치 : http://lxr.linux.no/source/include/linux/fs.h#L1118>


inode_operations에서는 실제 파일 정보를 만들고 제거하는 다양한 방법을 제공한다.

파일 내용을 다루는 연산을 위해서는 struct file_operations *i_fop 필드가 제공된다. 이 구조체는 다음과 같은 형태를 가지고 있다.

1088 struct file_operations {
1089 struct module *owner;
1090         loff_t (*llseek) (struct file *, loff_t, int);
1091         ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
1092         ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
1093         ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
1094         ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
1095         int (*readdir) (struct file *, void *, filldir_t);
1096         unsigned int (*poll) (struct file *, struct poll_table_struct *);
1097         int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
1098         long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
1099         long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
1100         int (*mmap) (struct file *, struct vm_area_struct *);
1101         int (*open) (struct inode *, struct file *);
1102         int (*flush) (struct file *, fl_owner_t id);
1103         int (*release) (struct inode *, struct file *);
1104         int (*fsync) (struct file *, struct dentry *, int datasync);
1105         int (*aio_fsync) (struct kiocb *, int datasync);
1106         int (*fasync) (int, struct file *, int);
1107         int (*lock) (struct file *, int, struct file_lock *);
1108         ssize_t (*sendfile) (struct file *, loff_t *, size_t, read_actor_t, void *);
1109         ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
1110         unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
1111         int (*check_flags)(int);
1112         int (*dir_notify)(struct file *filp, unsigned long arg);
1113         int (*flock) (struct file *, int, struct file_lock *);
1114         ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
1115         ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
1116 };
<위치 : http://lxr.linux.no/source/include/linux/fs.h#L1088>

파일 시스템을 만드는 것은 결국 이러한 연산자들을 잘 구성하는 것으로 시작한다고 볼 수 있다. 물론, 각각은 실제 물리적인 장치에 어떻게 배치되느냐도 중요하지만, 일단은 이러한 함수들에 내용을 넣는 것부터 시작해 보는 것도 괜찮을 것이다.

아래 코드는 이러한 내용들을 간단히 채워 넣은 파일 시스템 예제 코드로서 아래 주소에서 코드 및 설명을 볼 수 있다.
<참조 위치 : http://www.geocities.com/ravikiran_uvs/articles/rkfs.html>

/**
 * Notes:
 * Implementing a small filesystem having one file
 *
 * -> What happens when we mount a file system?
 * -> What do we need to provide to the kernel so that we are mountable?
 * -> What inode, dentry and file operations do we have to support?
 */
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/fs.h>
#include <linux/sched.h>
#include <linux/statfs.h>
#include <linux/mm.h>
#include <asm/uaccess.h>
#include <asm/errno.h>
#include <linux/buffer_head.h>
#include <linux/pagemap.h> /* unlock_page */
#define RKFS_MAGIC 0xabcd
#define FILE_INODE_NUMBER 2
/* file_system_type */
/* get_sb */
static struct super_block *
rkfs_get_sb(struct file_system_type *, int, const char *, void *);
/* kill_sb */
static void
rkfs_kill_sb(struct super_block *);
/* super_operations */
/* read_inode */
static void
rkfs_super_read_inode(struct inode *inode);
/* write_inode */
static int
rkfs_super_write_inode(struct inode *inode, int sync);
/* inode_operations */
/* lookup */
static struct dentry *
rkfs_inode_lookup(struct inode *parent_inode, struct dentry *dentry, struct nameidata *);
/* file_operations */
static int
rkfs_file_open (struct inode *, struct file *);
/* readdir */
static int
rkfs_file_readdir(struct file *file, void *dirent, filldir_t filldir);
/* release */
static int
rkfs_file_release (struct inode *, struct file *);
/* Address Space Operations */
/* writepage */
static int
rkfs_writepage(struct page *page, struct writeback_control *wbc);
/* readpage */
static int
rkfs_readpage(struct file *file, struct page *page);
/* prepare_write */
static int
rkfs_prepare_write(struct file *file, struct page *page,
     unsigned from, unsigned to);
/* commit_write */
static int
rkfs_commit_write(struct file *file, struct page *page,
    unsigned from, unsigned to);

/* readpage */

/*
 * Data declarations
 */
static struct super_operations rkfs_sops = {
  read_inode: rkfs_super_read_inode,
  statfs: simple_statfs, /* handler from libfs */
  write_inode: &rkfs_super_write_inode
};
static struct inode_operations rkfs_iops = {
  lookup: rkfs_inode_lookup
};
static struct file_operations rkfs_fops = {
  open: rkfs_file_open,
  read: &generic_file_read,
  readdir: &rkfs_file_readdir,
  write: &generic_file_write,
  release: &rkfs_file_release,
  fsync: simple_sync_file
};
static struct file_system_type rkfs = {
  name:        "rkfs",
  get_sb:    rkfs_get_sb,
  kill_sb: rkfs_kill_sb,
  owner:        THIS_MODULE
};
static struct address_space_operations rkfs_aops = {
  .readpage = rkfs_readpage,
  .writepage = rkfs_writepage,
  .prepare_write = rkfs_prepare_write,
  .commit_write = rkfs_commit_write
};
static struct inode *rkfs_root_inode;
static char file_buf[PAGE_SIZE] = "Hello World\n";
static int file_size = 12;
/*
 * File-System Operations
 */
static int
rkfs_fill_super(struct super_block *sb, void *data, int silent)
{
  printk("RKFS: rkfs_fill_super\n" );
  sb->s_blocksize = 1024;
  sb->s_blocksize_bits = 10;
  sb->s_magic = RKFS_MAGIC;
  sb->s_op = &rkfs_sops; // super block operations
  sb->s_type = &rkfs; // file_system_type
  rkfs_root_inode = iget(sb, 1); // allocate an inode
  rkfs_root_inode->i_op = &rkfs_iops; // set the inode ops
  rkfs_root_inode->i_mode = S_IFDIR|S_IRWXU;
  rkfs_root_inode->i_fop = &rkfs_fops;
  if(!(sb->s_root = d_alloc_root(rkfs_root_inode))) {
    iput(rkfs_root_inode);
    return -ENOMEM;
  }
  return 0;
}
static struct super_block *
rkfs_get_sb(struct file_system_type *fs_type, int flags, const char *devname, void *data) {
  /* rkfs_fill_super this will be called to fill the superblock */
  return get_sb_single(
         fs_type,
         flags,
         data,
         &rkfs_fill_super);
}
static void
rkfs_kill_sb(struct super_block *super) {
  kill_anon_super(super);
}
/*
 * Super-Block Operations
 */
static void
rkfs_super_read_inode(struct inode *inode) {
  printk("RKFS: rkfs_super_read_inode\n");
  inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
  inode->i_mapping->a_ops = &rkfs_aops;
}
static int
rkfs_super_write_inode(struct inode *inode, int wait) {
  printk("RKFS: rkfs_super_write_inode (i_ino = %d) = %d\n",
  (int)inode->i_ino,
  (int)i_size_read(inode));
  if(inode->i_ino == FILE_INODE_NUMBER) {
    file_size = i_size_read(inode);
  }
  return 0;
}
/*
 * Inode Operations
 */
static char filename[] = "hello.txt";
static int filename_len = sizeof(filename) - 1;
static struct dentry *
rkfs_inode_lookup(struct inode *parent_inode, struct dentry *dentry, struct nameidata *nameidata) {
  struct inode *file_inode;
  printk("RKFS: rkfs_inode_lookup\n");
  if(parent_inode->i_ino != rkfs_root_inode->i_ino ||
     dentry->d_name.len != filename_len ||
     strncmp(dentry->d_name.name, filename, dentry->d_name.len)) {
    d_add(dentry, NULL);
    goto out;
  }
  file_inode = iget(parent_inode->i_sb, FILE_INODE_NUMBER);
  if(!file_inode)
    return ERR_PTR(-EACCES);
  file_inode->i_size = file_size;
  file_inode->i_mode = S_IFREG|S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH;
  file_inode->i_fop = &rkfs_fops;
  //  file_inode->i_fop
  d_add(dentry, file_inode);
 out:
  return NULL;
}
/*
 * File Operations
 */
static int
rkfs_file_open(struct inode *inode, struct file *file) {
  printk("RKFS: @rkfs_file_open max_readahead = %d (size = %d)\n", (int)file->f_ra.ra_pages, file_size);
  file->f_ra.ra_pages = 0; /* No read-ahead */
  return generic_file_open(inode, file);
}
static int
rkfs_file_release (struct inode *ino, struct file *file) {
  struct dentry *dentry;
  dentry = file->f_dentry;
  return 0;
}
static int
rkfs_file_readdir(struct file *file, void *dirent, filldir_t filldir) {
  struct dentry *de = file->f_dentry;
  if(file->f_pos > 2)
    return 1;
  if(filldir(dirent, ".", 1, file->f_pos++, de->d_inode->i_ino, DT_DIR))
    return 0;
  if(filldir(dirent, "..", 2, file->f_pos++, de->d_parent->d_inode->i_ino, DT_DIR))
    return 0;
  if(filldir(dirent, filename, filename_len, file->f_pos++, FILE_INODE_NUMBER, DT_REG))
    return 0;
  return 1;
}
/* address_space_operations */
static int
rkfs_writepage(struct page *page, struct writeback_control *wbc) {
  void *page_addr = kmap(page);
  printk("[RKFS] offset = %d\n", (int)page->index);
  printk("RKFS: WritePage: [%s] [%s] [%s] [%s]\n",
  PageUptodate(page) ? "Uptodate" : "Not Uptodate",
  PageDirty(page) ? "Dirty" : "Not Dirty",
  PageWriteback(page) ? "PageWriteback Set" : "PageWriteback Cleared",
  PageLocked(page) ? "Locked" : "Unlocked");
  memcpy(file_buf, page_addr, PAGE_SIZE);
  ClearPageDirty(page);
  if(PageLocked(page))
    unlock_page(page);
  kunmap(page);
  return 0;
}
static int
rkfs_readpage(struct file *file, struct page *page) {
  void *page_addr;
  printk("RKFS: readpage called for page index=[%d]\n", (int)page->index);
  if(page->index > 0) {
    return -ENOSPC;
  }
  printk("RKFS: Page: [%s] [%s] [%s] [%s]\n",
  PageUptodate(page) ? "Uptodate" : "Not Uptodate",
  PageDirty(page) ? "Dirty" : "Not Dirty",
  PageWriteback(page) ? "PageWriteback Set" : "PageWriteback Cleared",
  PageLocked(page) ? "Locked" : "Unlocked");
  SetPageUptodate(page);
  page_addr = kmap(page);
  if(page_addr)
   memcpy(page_addr, file_buf, PAGE_SIZE);
  if(PageLocked(page))
    unlock_page(page);
  kunmap(page);
  return 0;
}
static int
rkfs_prepare_write(struct file *file, struct page *page,
     unsigned from, unsigned to) {
  return 0;
}
/* modified from generic_commit_write. generic_commit_write calls the
 * block device layer to write set up buffer heads for I/O.
 */
static int
rkfs_commit_write(struct file *file, struct page *page,
    unsigned from, unsigned to) {
  struct inode *inode = page->mapping->host;
  void *page_addr = kmap(page);
  loff_t pos = ((loff_t)page->index << PAGE_CACHE_SHIFT) + to;
  printk("RKFS: commit_write: [%s] [%s] [%s] \n",
  PageUptodate(page) ? "Uptodate" : "Not Uptodate",
  PageDirty(page) ? "Dirty" : "Not Dirty",
  PageLocked(page) ? "Locked" : "Unlocked");
  if(page->index == 0) {
    memcpy(file_buf, page_addr, PAGE_SIZE);
    ClearPageDirty(page);
  }
  SetPageUptodate(page);
  kunmap(page);
  
  /*
   * No need to use i_size_read() here, the i_size
   * cannot change under us because we hold i_sem.
   */
  if (pos > inode->i_size) {
    i_size_write(inode, pos);
    mark_inode_dirty(inode);
  }
  
  return 0;
}

static int
rkfs_init_module(void) {
  int err;
  err = register_filesystem(&rkfs);
  return err;
}
static void
rkfs_cleanup_module(void) {
  unregister_filesystem(&rkfs);
}
module_init(rkfs_init_module);
module_exit(rkfs_cleanup_module);
MODULE_LICENSE("GPL");

다음번부터는 실제 파일 시스템들을 하나씩 들쳐볼까 한다. 여러모로 어려운 부분이 있겠지만, 실제 파일 시스템을 살펴보는 것이 제일 좋은 방법이라 생각된다.
크리에이티브 커먼즈 라이선스
Creative Commons License
이올린에 북마크하기(0) 이올린에 추천하기(0)
Posted by Daniel Kwon