ファイルシステムアプリケーション解説②(ファイルコピー編)

つぶつぶコラム

はじめに

今回はファイルシステムでの、ファイルのコピーについて書いていきます。
当社製品であるGR-FILEのAPIを使用しますが、処理の流れは他のファイルシステムでも使えると思います。

前提条件

話を簡単にするため、既にマウント済みの状態から書いていきます。
マウントについては前回の記事とGR-FILE取扱説明書.pdf を参照してください。
また、コピーするにはデータを入れて置くバッファも必要です。基本的には大きなバッファを用意出来ればパフォーマンスが上がります。

コピーの流れ

ファイルのコピーにはいくつかの方法があります。
今回は一つのタスクだけで行う簡単な方法を採用します。
ファイルのコピーは、コピー元ファイルのオープン、コピー先ファイルのオープン(クリエイト)、ファイルの読み込み、ファイルの書き込み、コピー元ファイルのクローズ、コピー先ファイルのクローズと言った処理を行います。
図にすると以下の様になります。

コピーの流れ

コピー元ファイルのオープン

コピーするファイルを開きます。
コピー元なので、既に存在している必要があります。基本的には読み込みのみを行うのでリード指定で開いても構いません。
DirectIOを指定することで、条件が合えばパフォーマンスが向上します。
下記ソースコードは、GR-FILEに付属するサンプルアプリケーションの抜粋です。

※DirectIOとは、アプリケーションの指定したバッファアドレスを下位ドライバに渡し、極力データのコピーが発生しないように制御する方法です。DirectIOを使うとパフォーマンスが向上する可能性はありますが、GR-FILEのキャッシュバッファを使用しなくなるため、頻繁にアクセスするファイルでは逆にパフォーマンスが低下する可能性もあります。このあたりに関してはGR-FILEのアプリケーションノートに記載がありますので参照いただければと思います。DirectIOはGR-FILE特有の機能です。

コピー先ファイルのオープン

コピーする先のファイルをオープンします。
既存ファイルをオープンした場合は上書きになるので注意が必要です。
存在しないファイルの場合は、grp_fs_open()にGRP_FS_O_CREATモードを指定しても良いですし、grp_fs_create()でファイルを作成した後にgrp_fs_open()で開いても構いません。今回はファイルの作成とオープンを同時に行える、grp_fs_open()にGRP_FS_O_CREATを指定する方法を取りたいと思います。
コピー先のファイルは書き込みを行うので書き込めるモードを指定します。
コピー先のファイルを開くモードの指定の仕方はいくつかあるので、目的にあったモードを指定します。
書き込み時もDirectIOを指定することで、条件が合えばパフォーマンスが向上します。
今回の方法では、コピー元ファイル、コピー先ファイルを同時にオープンするので、最低でもファイルハンドルに2つ以上の空きが必要です。

コピー元ファイルのリード

コピーを行うファイルからファイルの中身を用意したバッファに読み出します。バッファはアプリケーション側で用意します。
grp_fs_read()は指定されたファイルハンドルから、指定されたバッファに、指定されたバイト数を読み込みます。もちろん用意したバッファサイズ以上を指定してはいけません。
grp_fs_read()の実行が終わるとAPIから制御が返ってきます。その際に戻り値として読み込んだバイト数、もしくはエラー番号が返されます。
エラー番号が返された場合は適切なエラー処理を行います。
読み込んだ値が0の場合は、エラーではないが1バイトも読めなかったことになるので、ファイルの終わりと判断します。(サンプルアプリケーションでは1バイトも読めなかった場合にファイルの終わりと判断しますが、他の方法での判断(例えばEOFを確認するなど)もありますのでやり易い方法でよいと思います。)
読み込んだ値が1以上の場合は、ファイルのデータがバッファにあるので、コピー先のファイルへ書き込みを行います。

コピー先ファイルのライト

バッファのデータをコピー先のファイルに書き込みます。
grp_fs_write()は指定したファイルハンドルに、指定したバッファから、指定したバイト数分だけ書き込みを行います。読み込んだデータサイズ以上のバイト数を指定してはいけません。(読めたメモリの状態が書き込まれるのでファイルコピーとしてはバグになります。)
grp_fs_write()の実行が終わるとAPIから制御が返ってきます。その際に戻り値として書き込めたバイト数、もしくはエラー番号が返されます。
エラー番号が返された場合は適切なエラー処理を行います。
筆者はめんどくさがりなので、戻り値が指定したバイト数と同じかどうかで判断してしまいます。(同じでなければエラーとしています。)
この辺りは異常系の処理なので、処理方法は千差万別かと思います。
書き込みが出来た場合は、ファイルのリードに戻り、繰り返します。

コピー元ファイルのクローズ、コピー先ファイルのクローズ

コピーが出来た場合、エラーが発生した場合など、ファイルオープンが成功した場合は使用後にファイルのクローズが必要です。
ファイルクローズを行う事でファイルハンドルを開放し、次のファイルオープンに備えることが出来ます。

ループの終了判断

バッファサイズは有限なので、通常はループを行いながら、ファイルリードとファイルライトを行います。
この時のループの条件ですが、いくつかの判断方法があります。
ファイルリードで読み込んだデータが0バイトなら終了と判断する方法や、あらかじめgrp_fs_stat()などでファイルサイズを取得しておいて終了条件とする方法、などありますがこの辺りはやり易い方法で良いと思います。

まとめ

いかがでしたでしょうか、今回は非常に簡単なファイルコピーの方法を書いてみました。
この方法を複数のファイルに対して行うことで、ファイルの連続コピーや、ディレクトリの作成などを組み合わせるとディレクトリごとのコピーなどにも使えます。
今回の方法以外にもコピーのやり方はありますので、ご興味があれば調べてみてください。

GR-File関連製品のページはこちら