Slide 1

Slide 1 text

open()の実装を 読んでみる Kernel/VM探検隊@北陸 part4 @happynote3966

Slide 2

Slide 2 text

$ whoami はっぴーのーと(@happynote3966) どこかの学生 happynote3966.hatenadiary.com 興味のあること:OSとか仮想化とかセキュリティとか SecHack365というイベントでQEMUをイジイジ 2

Slide 3

Slide 3 text

$ whoami はっぴーのーと(@happynote3966) どこかの学生 happynote3966.hatenadiary.com 興味のあること:OSとか仮想化とかセキュリティとか SecHack365というイベントでQEMUをイジイジ 3

Slide 4

Slide 4 text

動機 OSの勉強のためにLinuxのソースコードを読んでみようとは誰もが考える →何度か挑んでみてもワケがわからなくなり終了する 「Linuxのコア部分の入り口になるシステムコールなら読みやすいんじゃないか?」 →「じゃあやってみよう!とりあえず open()からやってみるか!」 ユーザーモードアプリケーション ライブラリ システムコール(open) い ろ い ろ

Slide 5

Slide 5 text

Linuxのバージョン commit 31130a16d459de809cd1c03eabc9567d094aae6a 4.19-rc1(だと思う)

Slide 6

Slide 6 text

どこにopen()があるのか?

Slide 7

Slide 7 text

システムコールの呼び出し場所 fs/open.c内の1074行目

Slide 8

Slide 8 text

do_sys_open

Slide 9

Slide 9 text

do_sys_open

Slide 10

Slide 10 text

呼び出しの前提 int fd = open(“file.txt”,O_RDONLY)

Slide 11

Slide 11 text

読む時に意識すること likelyマクロとunlikelyマクロを利用する(※) likelyマクロは条件が真になりやすく、unlikelyマクロは条件が偽になりやすい →likelyの部分はなるべく読んで、unlikelyの部分はなるべく読まない errnoの存在を利用する 今回はopen()システムコールが無事に成功することを前提にする →errnoが代入されたりするようなところはなるべく読まない (※)https://qiita.com/kaityo256/items/8a0c5376fad17907e1f6

Slide 12

Slide 12 text

build_open_flags()周辺を読む

Slide 13

Slide 13 text

ソースコード

Slide 14

Slide 14 text

やっていること 開きたいファイルのopen_flags構造体を初期化している 関数自体は0を返すので、呼び出し元の次のif文は実行されない struct open_flags{ int open_flag; umode_t mode; int acc_mode; int intent; int lookup_flags; }

Slide 15

Slide 15 text

open_flags open_flag = 0 mode = 0 acc_mode = 004 intent = 0x100 (LOOKUP_OPEN) lookup_flags = 0x001 (LOOKUP_FOLLOW)

Slide 16

Slide 16 text

tmp = getname(filename)周辺を読む (getname_flags(filename,0,NULL))

Slide 17

Slide 17 text

ソースコード

Slide 18

Slide 18 text

やっていること 開きたいファイルの名前に関するfilename構造体を スラブアロケータから取得して初期化している その後、初期化したfilename構造体へのポインタを返す struct filename{ const char *name; const __user char *uptr int refcnt; struct audit_names *aname; const char iname[]; }

Slide 19

Slide 19 text

open_flags open_flag = 0 mode = 0 acc_mode = 004 intent = 0x100 (LOOKUP_OPEN) lookup_flags = 0x001 (LOOKUP_FOLLOW) filename name = iname uptr = “file.txt”(userland) refcnt = 1 aname = NULL iname[] = “file.txt”

Slide 20

Slide 20 text

fd = get_unused_fd_flags(flags)周辺を読む (__alloc_fd(current->files,0,rlimit(RLIMIT_NOFILE),flags))

Slide 21

Slide 21 text

ソースコード

Slide 22

Slide 22 text

やっていること 開きたいファイルのファイルディスクリプタを割り当ててもらう 今割り当てられているファイルディスクリプタの中から空いているものを選ぶ (今回だと3になる) struct fdtable{ unsigned int max_fds; struct file __rcu **fd; unsigned long *close_on_exec; unsigned long *open_fds; unsigned long *full_fds_bits; struct rcu_head rcu; }

Slide 23

Slide 23 text

イメージ struct fdtable{ unsigned int max_fds; struct file __rcu **fd; unsigned long *close_on_exec; unsigned long *open_fds; unsigned long *full_fds_bits; struct rcu_head rcu; } kvmallocで割り当てられた領域 open_fdsの領域 11100000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 close_on_execの領域 full_fds_bitsの領域

Slide 24

Slide 24 text

do_filp_openを読む

Slide 25

Slide 25 text

ソースコード

Slide 26

Slide 26 text

set_nameidata()での処理 パス名の検索処理に関連する構造体であるnameidata構造体を初期化する struct nameidata{ struct path path; struct qstr last; struct path root; struct inode *inode; unsigned int flags; unsigned seq, m_seq; int last_type; unsigned depth; int total_link_count; struct saved { struct path link; struct delayed_call done; const char *name; unsigned seq; } *stack, internal[EMBEDDED_LEVELS]; struct filename *name; struct nameidata *saved; struct inode *link_inode; unsigned root_seq; int dtf; }

Slide 27

Slide 27 text

open_flags open_flag = 0 mode = 0 acc_mode = 004 intent = 0x100 (LOOKUP_OPEN) lookup_flags = 0x001 (LOOKUP_FOLLOW) filename name = iname uptr = “file.txt”(userland) refcnt = 1 aname = NULL iname[] = “file.txt” nameidata path last root inode flags seq m_seq last_type depth total_link_count = old->total_link_count; stack internal[EMBEDDED_LEVELS] name saved = current->nameidata link_inode root_seq dfd = AT_FDCWD

Slide 28

Slide 28 text

path_openat()

Slide 29

Slide 29 text

alloc_empty_file()での処理 オープンされているファイルとプロセスとのやり取りに関する file構造体を作成している struct file{ union { struct llist_node fu_llist; struct rcu_head fu_rcuhead; }f_u; struct path fpath; struct inode *f_inode; const struct file_operations *f_op; spinlock_t f_lock; enum rw_hint f_write_hint; atomic_long_t f_count; unsigned int f_flags; fmode_t f_mode; struct mutex f_pos_lock; loff_t f_pos; struct fown_struct f_owner; const struct cred *f_cred; struct file_ra_state f_ra; u64 f_verson; void *f_security; void *private_data; struct list_head f_ep_links; struct list_head f_tfile_lliink; struct address_space *f_mapping; errseq_t f_wb_err; }

Slide 30

Slide 30 text

open_flags open_flag = 0 mode = 0 acc_mode = 004 intent = 0x100 (LOOKUP_OPEN) lookup_flags = 0x001 (LOOKUP_FOLLOW) filename name = iname uptr = “file.txt”(userland) refcnt = 1 aname = NULL iname[] = “file.txt” nameidata path last root inode flags seq m_seq last_type depth total_link_count = old->total_link_count; stack internal[EMBEDDED_LEVELS] name saved = current->nameidata link_inode root_seq dfd = AT_FDCWD file f_u; fpath; f_inode; f_op; f_lock; f_write_hint; f_count = 1 f_flags = 0 f_mode = 1(FMODE_READ) f_pos_lock; f_pos; f_owner; f_cred = current->cred f_ra; f_verson; f_security; private_data; f_ep_links; f_tfile_lliink; f_mapping; f_wb_err;

Slide 31

Slide 31 text

path_init(一部省略)

Slide 32

Slide 32 text

open_flags open_flag = 0 mode = 0 acc_mode = 004 intent = 0x100 (LOOKUP_OPEN) lookup_flags = 0x001 (LOOKUP_FOLLOW) filename name = iname uptr = “file.txt”(userland) refcnt = 1 aname = NULL iname[] = “file.txt” nameidata path = current->fs->pwd last = (.hash_len = hash_len,.name = ) root inode = flags = LOOKUP_FOLLOW | LOOKUP_RCU | LOOKUP_PARENT seq m_seq last_type = LAST_NORM depth = 0 total_link_count = old->total_link_count; stack internal[EMBEDDED_LEVELS] name saved = current->nameidata link_inode root_seq dfd = AT_FDCWD file f_u; fpath; f_inode; f_op; f_lock; f_write_hint; f_count = 1 f_flags = 0 f_mode = 1(FMODE_READ) f_pos_lock; f_pos; f_owner; f_cred = current->cred f_ra; f_verson; f_security; private_data; f_ep_links; f_tfile_lliink; f_mapping; f_wb_err; dentry struct inode *d_inode; inode const struct file_operations i_fop; file_operations (ext4_file_operations)

Slide 33

Slide 33 text

link_path_walk()の処理 dエントリキャッシュのハッシュテーブルを探すために ハッシュ値を計算するらしい (詳しいハッシュの計算などは分からなかった)

Slide 34

Slide 34 text

do_last()(※抜粋)

Slide 35

Slide 35 text

vfs_open()

Slide 36

Slide 36 text

do_dentry_open()(※抜粋)

Slide 37

Slide 37 text

open_flags open_flag = 0 mode = 0 acc_mode = 004 intent = 0x100 (LOOKUP_OPEN) lookup_flags = 0x001 (LOOKUP_FOLLOW) filename name = iname uptr = “file.txt”(userland) refcnt = 1 aname = NULL iname[] = “file.txt” nameidata path = current->fs->pwd last = (.hash_len = hash_len,.name = ) root inode = flags = LOOKUP_FOLLOW | LOOKUP_RCU | LOOKUP_PARENT seq m_seq last_type = LAST_NORM depth = 0 total_link_count = old->total_link_count; stack internal[EMBEDDED_LEVELS] name saved = current->nameidata link_inode root_seq dfd = AT_FDCWD file f_u; fpath; f_inode; f_op; f_lock; f_write_hint; f_count = 1 f_flags = 0 f_mode = 1(FMODE_READ) f_pos_lock; f_pos; f_owner; f_cred = current->cred f_ra; f_verson; f_security; private_data; f_ep_links; f_tfile_lliink; f_mapping; f_wb_err; dentry struct inode *d_inode; inode const struct file_operations i_fop; file_operations (ext4_file_operations)

Slide 38

Slide 38 text

file_operations構造体(※抜粋) struct file_operations{ struct module *owner; loff_t (*llseek) (struct file *,loff_t, int); ssize_t (*read) (struct file *, char __user *,size_t,loff_t *); ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); ssize_t (*read_iter) (struct kiocb *, struct iov_iter *); ssize_t (*write_iter) (struct kiocb *, struct iov_iter *); int (*iterate) (struct file *, struct dir_context *); int (*iterate_shared) (struct file *, struct dir_context *); __poll_t (*poll) (struct file *, struct poll_table_struct *); long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); long (*compat_ioctl) (struct file *, unsigned int, unsigned long); int (*mmap) (struct file*, struct vm_area_struct *); unsigned long mmap_supported_flags; int (*open) (struct inode *, struct file *); int (*flush) (struct file *, fl_owner_t id); int (*release) (struct inode *, struct file *); int (*fsync) (struct file *, loff_t, loff_t, int datasync);

Slide 39

Slide 39 text

ファイルシステムの確認 ファイルシステムの種類を確認するにはdf -Tコマンドを使う

Slide 40

Slide 40 text

ext4のfile_operations fs/ext4/inode.cにある

Slide 41

Slide 41 text

まとめ ①ファイルの権限やモードに関するopen_flags構造体を作成し

Slide 42

Slide 42 text

まとめ ①ファイルの権限やモードに関するopen_flags構造体を作成し ②ファイルの名前に関するfilename構造体を作成して

Slide 43

Slide 43 text

まとめ ①ファイルの権限やモードに関するopen_flags構造体を作成し ②ファイルの名前に関するfilename構造体を作成して ③空いているファイルディスクリプタを探して

Slide 44

Slide 44 text

まとめ ①ファイルの権限やモードに関するopen_flags構造体を作成し ②ファイルの名前に関するfilename構造体を作成して ③空いているファイルディスクリプタを探して ④指定されたファイル名が存在する ファイルシステムに沿ったfile_operationsのopenを実行する

Slide 45

Slide 45 text

今後の課題や感想 [課題]もっと色んな引数のパターンを想定したソースコードリーディングを行う すごく単純な場合で読んだために「特殊な場合」を読めていない [課題]カーネルデバッグをしながら読むべきだった 実際の値を確認していないからどこか間違っている可能性大

Slide 46

Slide 46 text

今後の課題や感想 [課題]もっと色んな引数のパターンを想定したソースコードリーディングを行う すごく単純な場合で読んだために「特殊な場合」を読めていない [課題]カーネルデバッグをしながら読むべきだった 実際の値を確認していないからどこか間違っている可能性大 [感想]Linuxの一部を理解するだけでも難しかったのに Linux & 人の気持ちを完全に理解しているLinusはスゴイ

Slide 47

Slide 47 text

Thank you for your attention!