7. 任意のファイルの読み書き
シェルのリダイレクト機能を使用すれば標準入出力だけを使ってファイル を扱うことができますが,エディタのようにアプリケーションから直接 ファイルを読み書きしたい場合もあります.
この章では簡単な例としてファイルをコピーするプログラムを作成します. ファイルをコピーするには,ファイルを読みながら別ファイルに書き込めば 実現できます.1文字読む毎に書き出せば最も原始的なコピーコマンドとな ります.
結局, cat <コピー元ファイル >コピー先ファイル と同じです.
必要な操作はコピー元のファイルのオープン,コピー先のファイル のオープン, コピー元のファイルを読み込ながら,コピー先のファイルに 書き出す,オープンしたファイルのクローズです. さらにコピー元の ファイルを読み込んで「何か処理した後」,コピー先のファイルに書き出せば フィルタ系のコマンドが作成できます.
簡単ですね.
実装する前に少し準備をしておきます.
最初は C のソースやカーネルで使われている変数の型をアセンブラで使い やすいように次のファイルを用意します.C の構造体の宣言をアセンブラ用 に変換する場合に便利です.
;Copyright (C) 2000 Jun Mizutani <mizutani.jun@nifty.ne.jp> ; file : vartype.inc ; created : 2000/05/21 %ifndef __VARTYPE_INC %define __VARTYPE_INC %define INT resd %define UINT resd %define LONG resd %define ULONG resd %define SHORT resw %define USHORT resw %define CHAR resb %define UCHAR resb %define BYTE resb %define _INT dd %define _UINT dd %define _LONG dd %define _ULONG dd %define _SHORT dw %define _USHORT dw %define _CHAR db %define _UCHAR db %define _BYTE db %endif
もうひとつだけ用意しておきます.ファイルの操作には色々な定数を指定する 必要があります.それらの定数宣言のためのファイルをカーネルのヘッダファイル include/asm-i386/fcntl.h から作成しておきましょう.早速 vartype.inc を使って (インクルードして) います.
;Copyright (C) 2000 Jun Mizutani <mizutani.jun@nifty.ne.jp>
;
; file : fcntl.inc
; created : 2000/05/21
; derived from : linux-2.2.14/include/asm-i386/fcntl.h
%ifndef __FCNTL_INC
%define __FCNTL_INC
%include "vartype.inc"
; open/fcntl - O_SYNC is only implemented on blocks devices and on files
; located on an ext2 file system
%assign O_ACCMODE 0003q
%assign O_RDONLY 00q
%assign O_WRONLY 01q
%assign O_RDWR 02q
%assign O_CREAT 0100q ; not fcntl
%assign O_EXCL 0200q ; not fcntl
%assign O_NOCTTY 0400q ; not fcntl
%assign O_TRUNC 01000q ; not fcntl
%assign O_APPEND 02000q
%assign O_NONBLOCK 04000q
%assign O_NDELAY O_NONBLOCK
%assign O_SYNC 010000q
%assign FASYNC 020000q ; fcntl, for BSD compatibility
%assign O_DIRECT 040000q ; direct disk access hint - currently ignored
%assign O_LARGEFILE 0100000q
%assign O_DIRECTORY 0200000q ; must be a directory
%assign O_NOFOLLOW 0400000q ; don't follow links
%assign F_DUPFD 0 ; dup
%assign F_GETFD 1 ; get f_flags
%assign F_SETFD 2 ; set f_flags
%assign F_GETFL 3 ; more flags (cloexec)
%assign F_SETFL 4
%assign F_GETLK 5
%assign F_SETLK 6
%assign F_SETLKW 7
%assign F_SETOWN 8 ; for sockets.
%assign F_GETOWN 9 ; for sockets.
%assign F_SETSIG 10 ; for sockets.
%assign F_GETSIG 11 ; for sockets.
; for F_[GET|SET]FL
%assign FD_CLOEXEC 1 ; actually anything with low bit set goes
; for posix fcntl() and lockf()
%assign F_RDLCK 0
%assign F_WRLCK 1
%assign F_UNLCK 2
; for old implementation of bsd flock ()
%assign F_EXLCK 4 ; or 3
%assign F_SHLCK 8 ; or 4
; operations for bsd flock(), also used by the kernel implementation
%assign LOCK_SH 1 ; shared lock
%assign LOCK_EX 2 ; exclusive lock
%assign LOCK_NB 4 ; or'd with one of the above to prevent
blocking
%assign LOCK_UN 8 ; remove lock
struc flock
.l_type USHORT 1;
.l_whence USHORT 1;
.l_start LONG 1;
.l_len LONG 1;
.l_pid LONG 1;
endstruc
%endif
ファイルをオープンする場合のモード指定のために O_RDONLY, O_WRONLY, O_RDWR, O_CREAT ,O_EXCL, O_TRUNC, O_APPEND などを使用します. 8進定数の場合は数値の後ろに「q」を追加している事に注意してください.
さて,本題です.
ファイルの読み書きは,すでに出てきた sys_read と sys_write システム コールを使います.ただし今回はファイルディスクリプタを指定します. ファイルディスクリプタは sys_open システムコールで eax に返された値を 使用します.使い終わったファイルは sys_close でクローズします.
プログラムの流れは次のようになります.
- 引数からファイル名を取得.
- 読みこむファイルのオープン.
- 書き出すファイルのオープン.
- コピー元のファイルから 1 バイトを読み込み.
- コピー先のファイルに 1 バイト書き出し.
- ファイル末でなければ 4. に戻る.
- オープンしたファイルのクローズ.
; --------------------------------------------------------------------------
; ファイルの読み込みと書きこみ : ファイルコピー
; files.asm
; Jun Mizutani 5/21/2000
; --------------------------------------------------------------------------
section .bss
buf resd 1 ; バッファ
rfn resd 1 ; read file name
wfn resd 1 ; write file name
rfd resd 1 ; fd
wfd resd 1 ; fd
section .text
global _start
%include "fcntl.inc"
%include "stdio.inc"
_start:
pop eax ; 引数の数 argc
pop ebx ; コマンド名取得
dec eax ; 実際の引数の数
cmp eax, 2 ; 引数は2個のみ
jz .nx
jmp arg_err ; if no args jmp noarg_err
; ファイル名引数の処理
.nx
pop ebx ; pop filename pointer
mov [rfn], ebx
pop ebx ; pop filename pointer
mov [wfn], ebx
mov eax, [rfn]
call OutAsciiZ
mov eax, to_msg
call OutAsciiZ
mov ebx, [rfn]
call fropen
js open_err
mov [rfd], eax
mov eax, [wfn]
call OutAsciiZ
call NewLine
mov ebx, [wfn]
call fwopen
js open_err
mov [wfd], eax
mov ecx, buf
.loop
mov ebx, [rfd]
call fread1
test eax, eax
js read_err
jz done
mov ebx, [wfd]
call fwrite1
test eax, eax
js write_err
jmp short .loop
done:
mov eax, done_msg
exit:
push eax
mov ebx, [rfd]
call fclose
mov ebx, [wfd]
call fclose
pop eax
call OutAsciiZ
call NewLine
call Exit
open_err:
mov eax, e_open_msg
jmp exit
read_err:
mov eax, e_read_msg
jmp exit
write_err:
mov eax, e_write_msg
jmp exit
arg_err:
mov eax, e_arg_msg
jmp exit
; ファイルをオープン
; enter ebx: 第1引数 filename
; return eax: fd, if error then eax will be negative.
; int sys_open(const char * filename, int flags, int mode)
fropen:
mov ecx, O_RDONLY ; 第2引数 flag
jmp short fopen
fwopen:
mov ecx, O_CREAT | O_WRONLY | O_TRUNC
fopen: mov eax, SYS_open ; システムコール番号
mov edx, 0644q ; 第3引数 mode
int 0x80
test eax,eax ; eax <- fd
ret
; ファイルをクローズ
; enter ebx: 第1引数 ファイルディスクリプタ
; int sys_close(unsigned int fd)
fclose: mov eax, SYS_close ; システムコール番号
int 0x80
ret
; ファイルから1バイト読みこみ ebx : fd
; enter ebx : 第1引数 ファイルディスクリプタ
; ecx : 第2引数 バッファアドレス
; ssize_t sys_read(unsigned int fd, char * buf, size_t count)
fread1: mov eax, SYS_read ; システムコール番号
mov edx, 1 ; 第3引数 読みこみバイト数
int 0x80
ret
; ファイルへ1バイト書きこみ
; enter ebx : 第1引数 ファイルディスクリプタ
; ecx : 第2引数 バッファアドレス
; ssize_t sys_write(unsigned int fd, const char * buf, size_t count)
fwrite1: mov eax, SYS_write ; システムコール番号
mov edx, 1 ; 第3引数 書きこみバイト数
int 0x80
ret
to_msg db " --> ", 0
e_open_msg db "Can't open such file.", 10, 0
e_read_msg db "Can't read such file.", 10, 0
e_write_msg db "Can't write such file.", 10, 0
e_arg_msg db "Two filenames required.", 10, 0
done_msg db "completed.", 10, 0
; --------------------------------------------------------------------------
コマンドライン引数は,コピー元のファイル名とコピー先のファイル名の 2つです.引数の個数が2以外ではエラーとなります.* などをファイル名に 指定しないで下さい.シェルによって展開された場合に既存のファイルを 上書きしてしまう場合があります.
コピー先のファイルは O_CREAT | O_WRONLY | O_TRUNC でオープンして いるため,存在しなければ作成,あれば上書きとなります.パーミッションは 644 になります.