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. 引数からファイル名を取得.
  2. 読みこむファイルのオープン.
  3. 書き出すファイルのオープン.
  4. コピー元のファイルから 1 バイトを読み込み.
  5. コピー先のファイルに 1 バイト書き出し.
  6. ファイル末でなければ 4. に戻る.
  7. オープンしたファイルのクローズ.
; --------------------------------------------------------------------------
; ファイルの読み込みと書きこみ : ファイルコピー
; 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 になります.