Linux Kernel(4.19) Hacks

rousalome.egloos.com

포토로그 Kernel Crash


통계 위젯 (화이트)

130199
1107
135858


[리눅스커널] 가상파일시스템: struct inode 구조체 분석 13. 가상 파일 시스템

아이노드에 대한 세부 속성은 struct inode 구조체에서 확인할 수 있습니다. 먼저 struct inode 구조체 선언부를 볼까요?
[https://elixir.bootlin.com/linux/v4.19.30/source/source/include/linux/fs.h]
struct inode {
umode_t i_mode;
unsigned short i_opflags;
kuid_t i_uid;
kgid_t i_gid;
unsigned int i_flags;
...
const struct inode_operations *i_op;
struct super_block *i_sb;
struct address_space *i_mapping;
...
}

struct inode 구조체 각각 필드에 대한 설명은 다음 테이블에서 확인할 수 있습니다.
타입 필드 설명
const struct inode_operations *i_op inode 연산
struct super_block *i_sb 수퍼 블록 객체를 가리키는 포인터
struct address_space *i_mapping address_space를 가리키는 포인터
unsigned long i_ino inode 번호
const unsigned int i_nlink 하드 링크의 갯수
dev_t i_rdev 실제 장치 식별자
loff_t i_size 바이트 단위의 파일 크기
struct timespec i_atime 마지막으로 파일에 접근한 시간
i_mtime 마지막으로 파일에 쓴 시간
i_ctime 마지막으로 inode를 변경한 시간 
unsigned short i_bytes 파일의 마지막 블록에 있는 바이트 갯수
unsigned int i_blkbits 비트 단위의 블록 사이즈
blkcnt_t i_blocks 파일의 블록 갯수
unsigned long i_state inode 상태 플래그
unsigned long dirtied_when inode가 dirty 상태가 된 시간
unsigned long dirtied_time_when inode가 dirty 상태에서 변경된 시간
struct hlist_node i_hash 해쉬 리스트를 가리키는 포인터
struct list_head i_sb_list 수퍼 블록의 inode 리스트를 가리키는 포인터
atomic_t i_count 사용 카운터
atomic_t i_writecount 쓰기 프로세스를 위한 사용 카운터

struct inode 구조체 필드 중 필요가 있는 필드를 살펴보겠습니다.

umode_t i_mode;

파일 종류와 동작 권한과 같은 아이노드 상태를 저장합니다. 아래 정의한 플래그를 OR 비트 연산한 결과를 저장합니다.
[https://elixir.bootlin.com/linux/v4.19.30/source/source/include/uapi/linux/stat.h]
#define S_IFMT  00170000
#define S_IFSOCK 0140000
#define S_IFLNK 0120000
#define S_IFREG  0100000
#define S_IFBLK  0060000
#define S_IFDIR  0040000
#define S_IFCHR  0020000
#define S_IFIFO  0010000
#define S_ISUID  0004000
#define S_ISGID  0002000
#define S_ISVTX  0001000

각 매크로에 대한 설명은 다음 테이블을 참고하세요.
매크로 설명
S_IFMT  파일 종류 식별하기 위한 마스크 비트
S_IFLNK 링크 파일
S_IFREG 일반 파일
S_IFCHR 캐릭터 다바이스용 파일
S_IFBLK 블록 다바이스용 파일
S_IFDIR 디렉토리 파일
S_IFLNK 심볼릭 파일
S_IFIFO FIFO 용 파일

i_mode 필드를 사용하는 코드는 어디일까요? 파일을 오픈할 때 호출하는 may_open() 함수를 살펴봅시다.
[https://elixir.bootlin.com/linux/v4.19.30/source/source/fs/namei.c]
1 static int may_open(const struct path *path, int acc_mode, int flag)
2 {
3 struct dentry *dentry = path->dentry;
4 struct inode *inode = dentry->d_inode;
5 int error;
6
7 if (!inode)
8 return -ENOENT;
9
10 switch (inode->i_mode & S_IFMT) {
11 case S_IFLNK:
12 return -ELOOP;
13 case S_IFDIR:
14 if (acc_mode & MAY_WRITE)
15 return -EISDIR;
16 break;
17 case S_IFBLK:
18 case S_IFCHR:
19 if (!may_open_dev(path))
20 return -EACCES;
21 /*FALLTHRU*/
22 case S_IFIFO:
23 case S_IFSOCK:
24 flag &= ~O_TRUNC;
25 break;
26 }

may_open() 함수는 함수 이름과 같이 파일을 오픈 조건을 점검하는 역할을 수행합니다.

위 코드는 2단계 동작으로 분류할 수 있습니다.
7~8번째 코드와 같이 아이노드가 유효한지 체크하는 코드와 아이노드 모드에 따라 파일을 제어하는 10~26번째 줄 코드입니다.

10번째 줄 코드를 봅시다.
10 switch (inode->i_mode & S_IFMT) {

아이노드 객체에서 i_mode 란 필드를 S_IFMT 플래그와 AND 비트 연산을 수행합니다.
앞에서 설명했듯이 S_IFMT 플래그는 i_mode 필드에서 아이노드 파일 종류 비트를 추출하는 마스크입니다.

11~16번째 줄 코드와 22~25번째 줄 코드와 같이 파일 종류가 심볼릭 링크, 디덱토리, 소켓이나 FIFO 이면 에러 코드를 반환합니다. 파일 종류가 캐릭터 디바이스나 블록 디바이스인 경우 may_open_dev() 함수를 호출합니다.

i_mode 필드는 아래에 정의된 매크로를 써서 파일 형태를 점검합니다.
[https://elixir.bootlin.com/linux/v4.19.30/source/source/include/uapi/linux/stat.h]
#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
#define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
#define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK)
#define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO)
#define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK)

m은 i_mode 필드며 S_IFMT 비트 마스크와 AND 비트 연산한 결과를 반환합니다.


파일 종류를 점검하는 매크로 중 S_ISDIR()와 S_ISREG() 매크로 함수 사용 예를 소개합니다.
다음은 vfs_truncate() 함수인데 파일을 지정된 크기로 자를 때 호출됩니다.
[https://elixir.bootlin.com/linux/v4.19.30/source/source/fs/open.c]
1 long vfs_truncate(const struct path *path, loff_t length)
2 {
3    struct inode *inode;
...
4    inode = path->dentry->d_inode;
5    mnt = path->mnt;
6
7    /* For directories it's -EISDIR, for other non-regulars - -EINVAL */
8    if (S_ISDIR(inode->i_mode))
9        return -EISDIR;
10    if (!S_ISREG(inode->i_mode))
11        return -EINVAL;

다음 8~9번째 줄 코드를 봅시다.
8    if (S_ISDIR(inode->i_mode))
9        return -EISDIR;

아이노드 객체 i_mode 필드를 S_ISDIR() 매크로에 전달해서 파일이 디렉토리 타입인지 점검합니다. 만약 파일 종류가 디렉토리이면 9번째 줄 코드와 같이 EISDIR 매크로 정수를 마이너스로 반환합니다.

입력이 디렉토리라는 에러 메시지입니다.

이번에 볼 코드는 10~11번째 줄입니다.
10    if (!S_ISREG(inode->i_mode))
11        return -EINVAL;

아이노드 객체 i_mode 필드를 S_ISREG() 매크로에 전달해서 파일이 일반 파일 타입인지 점검합니다. 만약 파일 종류가 일반 파일이 아니면 11번째 줄 코드와 같이 EINVAL 매크로 정수를 마이너스로 반환합니다. 유효하지 않은 입력이라는 에러 메크로입니다.


unsigned long i_state;

아이노드 파일의 세부 동작을 저장하는 플래그입니다. 다음 매크로를 AND 비트 연산한 결과를 저장합니다.
[https://elixir.bootlin.com/linux/v4.19.30/source/source/include/linux/fs.h]
#define I_DIRTY_SYNC (1 << 0)
#define I_DIRTY_DATASYNC (1 << 1)
#define I_DIRTY_PAGES (1 << 2)
#define __I_NEW 3
#define I_NEW (1 << __I_NEW)
#define I_WILL_FREE (1 << 4)
#define I_FREEING (1 << 5)
#define I_CLEAR (1 << 6)
#define __I_SYNC 7
#define I_SYNC (1 << __I_SYNC)
#define I_REFERENCED (1 << 8)
#define __I_DIO_WAKEUP 9
#define I_DIO_WAKEUP (1 << __I_DIO_WAKEUP)
#define I_LINKABLE (1 << 10)
#define I_DIRTY_TIME (1 << 11)
#define __I_DIRTY_TIME_EXPIRED 12
#define I_DIRTY_TIME_EXPIRED (1 << __I_DIRTY_TIME_EXPIRED)
#define I_WB_SWITCH (1 << 13)
#define I_OVL_INUSE (1 << 14)

이번 소절에서는 아이노드 객체 자료구조인 struct inode 구조체를 알아봤습니다. 다음 소절에서 아이노드 함수 오퍼레이션에 대해 살펴보겠습니다.


"혹시 궁금한 점이 있으면 댓글로 질문 남겨주세요. 아는 한 성실히 답변 올려드리겠습니다!" 

Thanks,
Austin Kim(austindh.kim@gmail.com)


Reference(가상 파일시스템)

가상 파일시스템 소개
파일 객체
파일 객체 함수 오퍼레이션 동작
프로세스는 파일객체 자료구조를 어떻게 관리할까?
슈퍼블록 객체
아이노드 객체
덴트리 객체
가상 파일시스템 디버깅

핑백

덧글

댓글 입력 영역