エンディアン
Rev.3を表示中。最新版はこちら。
エンディアンとは、CPUに依存する複数バイトデータによるメモリー上の配置の事をいい、ビッグエンディアンとリトルエンディアン(ミドルエンディアンというのもあるらしいです。)とがあります。X86はリトルエンディアンです。例えば0x1234は、アドレス0からメモリ上に配置すると、ビッグエンディアンではアドレス0に0x12,アドレス1には0x34となりますが、リトルエンディアンでは、アドレス0に 0x34、アドレス1に0x12と逆転します。どっちがどっちとよくこんがらがるので、私自身の解釈で、アドレスの大きい方で終わる方をビッグエンディアンで、アドレスの小さい方で終わるのをリトルビッグエンディアンと理解しています。(命名そのまままですが。)
この違いは、同じシステム下で動作するなら問題ないのですが、異種間に渡って共通するデータなら問題となります。そこでは個々にエンディアンを統一することで、OSレベルでその違いを吸収する事で解決しちます。
ext2/3ではリトルエンディアンとしているようです。(通信ではビッグエンディアンのようです。ネットからの情報ですが。)
ext2_next_entry関数は、ext2ファイルシステムのディレクトリー内の、引数で指定するエントリーext2_dirent *pの、次のエントリーを求めるものです。エントリーはstruct ext2_dir_entry_2のフォーマットで順に配置されています。すなわち次のエントリーは、そのエントリーにそのサイズrec_lenを加算したものが次のエントリーになるわけです。
static inline ext2_dirent *ext2_next_entry(ext2_dirent *p) { return (ext2_dirent *)((char *)p + ext2_rec_len_from_disk(p->rec_len)); } typedef struct ext2_dir_entry_2 ext2_dirent; struct ext2_dir_entry_2 { __le32 inode; /* Inode number */ __le16 rec_len; /* Directory entry length */ __u8 name_len; /* Name length */ __u8 file_type; char name[EXT2_NAME_LEN]; /* File name */ };ここで問題となるのは、struct ext2_dir_entry_2は、実デバイスから取得するものです。これはリトルエンディアンです。ビッグエンディアンの処理系では問題です。そこでp->rec_lenを直接加算するのでなく、ext2_rec_len_from_disk関数を呼ぶことでその違いを吸収しています。
static inline unsigned ext2_rec_len_from_disk(__le16 dlen) { unsigned len = le16_to_cpu(dlen); if (len == EXT2_MAX_REC_LEN) return 1 << 16; return len; }ext2_rec_len_from_disk関数ではle16_to_cpuマクロをコールします。le16_to_cpuマクロは、リトルエンディアンなら、何にもせず引数の値を返し、ビッグエンディアンなら、bswap_16/32をコールする事で、バイト並びを変換して返す事で、エンディアンの違いを吸収しています。
#if BYTE_ORDER == LITTLE_ENDIAN #define le16_to_cpu(val) (val) #define le32_to_cpu(val) (val) #endif #if BYTE_ORDER == BIG_ENDIAN #define le16_to_cpu(val) bswap_16(val) #define le32_to_cpu(val) bswap_32(val) #endifここでは、ファイルシステムのメタ情報について見ましたが、ext2/3ではデータそのものもエンディアンの処理をする必要があるわけです。