读取ISO9660格式文件的过程主要分为3步:
读取PVD,地址是0x210,也可以说是2分16秒,或者LSN=16,其实都是一个地方。用dd命令把碟片从光驱里抓出来,保存成ISO文件放在电脑里,不需要把整张碟抓出,只需要抓取前面的2M字节左右,用来方便分析光盘的结构。
前面说了,第16个扇区的地址,计算起来就是16x2048 = 32k,转成16进制就是0x8000。
用UE打开刚才抓到ISO文件,转到0x8000的地方,就是下面图片的样子。首先看到CD001,就是ISO9660的格式标记。
然后后面在140 offset的地方是path table的扇区地址,156的地方是root dir record,长度是34个字节。一般Linux的标准是读取这个root record,而不是走path table的流程。
通过根记录这个节点,就可以不断遍历整个目录树,但速度会比较慢。如果想一次性读取到所有目录里的文件,就可以走path table的流程,这部分Linux里面没有实现,但普通DVD的软件里面是这样做的。
从图片里高度选中的部分是根记录的入口扇区,可以看出是0x17,也就是23扇区里面就是记录根目录节点的详细资料。
struct iso_primary_descriptor {
char type [ISODCL ( 1, 1)]; /* 711 */
char id [ISODCL ( 2, 6)];
char version [ISODCL ( 7, 7)]; /* 711 */
char unused1 [ISODCL ( 8, 8)];
char system_id [ISODCL ( 9, 40)]; /* achars */
char volume_id [ISODCL ( 41, 72)]; /* dchars */
char unused2 [ISODCL ( 73, 80)];
char volume_space_size [ISODCL ( 81, 88)]; /* 733 */
char unused3 [ISODCL ( 89, 120)];
char volume_set_size [ISODCL (121, 124)]; /* 723 */
char volume_sequence_number [ISODCL (125, 128)]; /* 723 */
char logical_block_size [ISODCL (129, 132)]; /* 723 */
char path_table_size [ISODCL (133, 140)]; /* 733 */
char type_l_path_table [ISODCL (141, 144)]; /* 731 */
char opt_type_l_path_table [ISODCL (145, 148)]; /* 731 */
char type_m_path_table [ISODCL (149, 152)]; /* 732 */
char opt_type_m_path_table [ISODCL (153, 156)]; /* 732 */
char root_directory_record [ISODCL (157, 190)]; /* 9.1 */
char volume_set_id [ISODCL (191, 318)]; /* dchars */
char publisher_id [ISODCL (319, 446)]; /* achars */
char preparer_id [ISODCL (447, 574)]; /* achars */
char application_id [ISODCL (575, 702)]; /* achars */
char copyright_file_id [ISODCL (703, 739)]; /* 7.5 dchars */
char abstract_file_id [ISODCL (740, 776)]; /* 7.5 dchars */
char bibliographic_file_id [ISODCL (777, 813)]; /* 7.5 dchars */
char creation_date [ISODCL (814, 830)]; /* 8.4.26.1 */
char modification_date [ISODCL (831, 847)]; /* 8.4.26.1 */
char expiration_date [ISODCL (848, 864)]; /* 8.4.26.1 */
char effective_date [ISODCL (865, 881)]; /* 8.4.26.1 */
char file_structure_version [ISODCL (882, 882)]; /* 711 */
char unused4 [ISODCL (883, 883)];
char application_data [ISODCL (884, 1395)];
char unused5 [ISODCL (1396, 2048)];
};
/* Almost the same as the primary descriptor but two fields are specified */
按照之前的方法计算一下,23x2048 = 46k,用16进制表示为:0xb800,查看相关的数据如下图所示:
第一个项目是当前目录,也就是./目录,然后是当前目录下的其他文件或目录,每一项里面都有extent的地址,如果是文件就指向文件的实际数据入口,如果是目录,则表示子目录的入口。一个记录是目录还是文件是由flag项里面的第2位表示的,一般flag == 0x02就是目录,如果0x00就是文件。如果一个目录的flag == 0x06,则表示它是一个associated file,意思是关联的文件。但根据ecma119的说明,一个目录不应该被定义成关联文件,所以这个地方是有问题的.在Linux下会看不到这个目录,估计是某个条件判断过滤掉了。如最后一个目录是VIDEO_TS,但标记因为是0x06,所以Linux下是看不到的。但是如果进入这个目录的实际位置,也就是0x26的地方,就可以看到里面是有内容的。
根目录信息
这里面的数据结构是dir record,结构体是这样的:
struct iso_directory_record {
char length [ISODCL (1, 1)]; /* 711 */
char ext_attr_length [ISODCL (2, 2)]; /* 711 */
char extent [ISODCL (3, 10)]; /* 733 */
char size [ISODCL (11, 18)]; /* 733 */
char date [ISODCL (19, 25)]; /* 7 by 711 */
char flags [ISODCL (26, 26)];
char file_unit_size [ISODCL (27, 27)]; /* 711 */
char interleave [ISODCL (28, 28)]; /* 711 */
char volume_sequence_number [ISODCL (29, 32)]; /* 723 */
unsigned char name_len [ISODCL (33, 33)]; /* 711 */
char name [0];
} __attribute__((packed));
进入0x26扇区,也就是0x13000的位置:
可以看到VIDEO_TS.VOB这些文件,就是刚才VIDEO_TS目录下面的文件了。
下面再写一下path table的方式,从之前PVD的地方看到,path table的入口是0x13,也就是0x9800的位置。这里只记录目录的入口,而且所有目录部分层次,都是按照平摊的方式列出,那么假如一个扇区放不完会怎么办,那么理论上应该要继续判断下一个扇区,因为Linux里面没有实现,实际上也还没有遇到过这种情况,暂时不做讨论了。
从path table上也能找到VIDEO_TS这个目录的入口地址同样为0x26.
路径表的结构比较简单:
struct iso_path_table{
unsigned char name_len[2]; /* 721 */
char extent[4]; /* 731 */
char parent[2]; /* 721 */
char name[0];
} __attribute__((packed));