ファイルシステムアプリケーション解説③(ディレクトリツリーコピー編)

つぶつぶコラム

はじめに

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

前提条件

話を簡単にするため、既にマウント済みの状態から書いていきます。
※マウントについてはこれまでの記事「解説①(基本編)」「解説②(ファイルコピー編)」とGR-FILEマニュアル(GR-FILE取扱説明書.pdf)を参照してください。
また、コピーするにはデータを入れておくバッファが必要です。基本的には大きなバッファを用意出来ればパフォーマンスが上がります。

ディレクトリツリーのコピーの流れ

ディレクトリのコピーにはいくつかの方法がありますが、今回は説明しやすい「再帰」を使用した方法を採用します。

「再帰」を使用した方法では、タスクのスタックを多く使用しますのでご注意ください。またファイルハンドルもディレクトリの階層が深いほど多く必要になりますので設定値にもご注意ください。

ディレクトリツリーのコピーでは、コピー元のディレクトリツリーを順番に検索しながら、コピー先にファイルのコピーやディレクトリの作成を行います。

図にすると以下の様になります。(下記、再帰関数はコピー元、コピー先のパス名を受け取ります。)

ディレクトリツリーのコピーの流れ

コピー元ディレクトリのオープン

ディレクトリを検索するために、ディレクトリを開きます。

ディレクトリには8.3形式のショートファイル名/ディレクトリ名を記録したエントリ、ロングファイル名を記録したエントリがあります。ファイルのサイズなどはショートファイル名のエントリにあるので、ロングファイル名を持つファイルの場合は、ロングファイル名のエントリの直後にショートファイル名のエントリが続きます。

ディレクトリのファイルを検索する場合は、ディレクトリを開いてエントリを確認していく処理が必要になります。

GR-FILEではディレクトリを開く際もgrp_fs_open() APIが使用できます。ただし、読み込み専用で開く必要があります。

コピー元ディレクトリエントリの検索

ディレクトリからエントリを1つ読み込みます。

FATのエントリでは、ショートファイル名のエントリ、ロングファイル名のエントリ、ディレクトリのエントリ、ボリュームラベルなどの種類があります。それぞれの種類ごとに必要な処理が違いますが、ここではショートファイル名、ロングファイル名、ディレクトリの3つのエントリについて処理を行います。

エントリを取得するにはgrp_fs_get_dirent() APIを使用しますが、ファイル名を受け取るバッファサイズには注意が必要です。ファイル名を受け取るバッファが小さいと、ファイル名が途中で切れた状態で返されるので、この後のコピー処理などでファイルが開けずにエラーになります。ファイル名のバッファサイズは十分な大きさを指定するようにします。

エントリがディレクトリの場合

取得したエントリがディレクトリの場合、サブディレクトリがあるということになります。ですので、サブディレクトリをコピー先に作成します。その後、サブディレクトリの中を検索しコピーを行う必要がありますので、取得したディレクトリ名をパスに繋げてサブディレクトリを検索するため本関数を再帰呼び出しします。

また。FATではディレクトリエントリの「.」、「..」は特別なエントリとなっていますので、これらは無視します。

※direntが再帰関数名です。

エントリがファイルの場合

取得したエントリがファイルの場合、ファイルのコピーを行います。ファイルのコピーについてはこれまでの記事「解説②(ファイルコピー編)」を参照ください。

エントリがロングファイル名の場合

取得したエントリがロングファイル名の場合、ロングファイル名エントリに続くショートファイル名/ディレクトリ名のエントリを取得する必要があります。ロングファイル名のエントリにはロングファイル名しか記録されていないため、ファイルサイズや各タイムスタンプなどを取得するためにショートファイル名/ディレクトリ名のエントリを取得する必要があります。

ただし、ディレクトリの作成やファイルのコピー時に使用するファイル名はロングファイル名を使用します。

ですので、取得したエントリがロングファイル名の場合、ロングファイル名を保存しておき、続くエントリの取得を行います。そのあと、ディレクトリの作成やファイルのコピーでは保存しておいたロングファイル名を使用します。

再帰呼び出しについて

再帰呼び出しを行うことでパス名の扱いが楽になります。ただし、パス名のバッファやエントリ検索時の構造体はローカル変数として定義する必要があります。

今回の方法ではサブディレクトリごとに、再帰呼び出しを行ったり戻ってきたりを繰り返し、ディレクトリツリーのコピーを行います。再帰呼び出しを行うときにサブディレクトリまでのパスを指定するため、各サブディレクトリの階層の処理では、指定されたパスに検索で取得したファイル名/ディレクトリ名を繋げれば良いことになります。(再帰処理を使わない方法ではパス名の制御が面倒ですが、タスクのスタックは少なくて済みます。)

再帰処理はうまく使えば処理を分かりやすく記述することができます。

コピー元ディレクトリのクローズ

ディレクトリエントリが無くなったら、これ以上の検索は出来ませんので開いていたディレクトリを閉じます。

開いたままにすると、ファイルハンドルも残るため、いずれファイルハンドルが枯渇してしまうため、開いたファイルハンドルは使い終わった時点で閉じるようにします。

NTフラグについて

これまでの処理で、ディレクトリツリーのコピーは可能です。

ただし、GR-FILEを使用する場合はNTフラグの注意点をお伝えしておく必要があります。WindowsNT系のOSではFAT32の仕様で予約領域となっているDIR_NTResを使用して、8.3形式のショートファイル名の大文字小文字を制御しています。

GR-FILEはDIR_NTResには対応していないため、Windowsで「src」などのファイル名を作成した場合、GR-FILEでは「SRC」となります。(今回のディレクトリツリーのコピーを行った場合でも同様になります。)

FAT32ではショート名の大文字小文字は区別が無いためファイルシステムとしては、問題は無いのですが見た目が異なるため問題になる場合があります。

DIR_NTResについては、WEB上に情報が複数ありますので興味がありましたら調べてみてください。

まとめ

いかがでしたでしょうか、今回は再帰処理を使用したディレクトリツリーのコピーについて書いてみました。

コピー元とコピー先のパスを指定することでコピーされます。また、別々のメディアに対してのコピーも可能ですし、同じメディアの異なるディレクトリへのコピーも可能です。

ディレクトリツリーのコピーは、製品出荷時の初期状態をコピーしておくなどの場面などで使用することが考えられますが、それほど使う機会はないかもしれません。

今回の処理を変更すると、ディレクトリツリー構造はそのままに、拡張子「.JPG」だけをコピーしたり、ディレクトリツリーの中のファイルやディレクトリをすべて削除するようなことも可能になります。

使いどころは難しいかもしれませんが、興味があれば調べてみてください。

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