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