Linux isofs rock ridge扩展与CE签名处理
ISO 9660是光盘文件系统的基础标准,但它不支持Unix风格的长文件名、符号链接、权限和所有权信息。Rock Ridge扩展通过System Use Sharing Protocol(SUSP)在ISO 9660目录记录的System Use字段中嵌入额外的属性记录,使光盘文件系统能够表达完整的Unix语义。
ISO 9660的每个目录记录有一个固定长度的系统使用区域(System Use Area),起始于目录记录偏移量X(由Directory Record Length字段决定)。Rock Ridge扩展的System Use字段在isofs中通过以下结构解析:
struct iso_directory_record {
char length;
char ext_attr_length;
char extent[8];
char size[8];
char date[7];
char flags;
char file_unit_size;
char interleave;
char volume_sequence_number[4];
unsigned char name_len;
char name[0];
} __attribute__((packed));
在name之后紧跟着System Use字段。Rock Ridge核心的入口解析函数是parse_rock_ridge_inode_internal(),它遍历系统使用区域并处理每个SUSP条目:
static int parse_rock_ridge_inode_internal(struct iso_inode_info *inode,
struct inode *iget_inode,
int regard_as_data)
{
int sig;
unsigned int len;
unsigned char *rr = inode->i_sb->s_rock_ridge;
unsigned char *ptr, *end;
struct rock_ridge *ri;
struct ce_handle ce;
int ret = 0;
ptr = inode->i_sb->s_rock_ridge;
end = ptr + inode->i_sb->s_rock_ridge_size;
while (ptr < end) {
struct rock_ridge *rr = (struct rock_ridge *)ptr;
sig = (rr->signature[0] << 8) | rr->signature[1];
len = rr->len;
if (len < 4 || ptr + len > end)
break;
根据SP(System Use Sharing Protocol)规范,每个SUSP条目的格式为2字节签名、1字节长度、1字节版本。不同的签名对应的处理分支如下:
switch (sig) {
case SIG_RR:
inode->i_rock_ridge = rr->u.RR.flags;
break;
case SIG_PX:
inode->i_mode = isonum_733(rr->u.PX.mode);
inode->i_uid = isonum_733(rr->u.PX.uid);
inode->i_gid = isonum_733(rr->u.PX.gid);
break;
case SIG_PN:
inode->i_rdev = old_decode_dev(isonum_733(rr->u.PN.dev));
break;
case SIG_SL:
parse_rock_ridge_symlink(rr, inode);
break;
case SIG_NM:
parse_rock_ridge_filename(rr, inode);
break;
case SIG_CL:
inode->i_ino = isonum_733(rr->u.CL.location);
break;
case SIG_PL:
inode->i_ino = isonum_733(rr->u.PL.location);
break;
case SIG_CE:
ce.extent = isonum_733(rr->u.CE.extent);
ce.offset = isonum_733(rr->u.CE.offset);
ce.size = isonum_733(rr->u.CE.size);
ret = rock_ridge_continue(inode, &ce);
if (ret)
return ret;
break;
}
ptr += len;
}
return ret;
}
CE签名(SIG_CE, 0x4345)的处理最为特殊。CE条目的作用是提供Continuation Area(延续区域),当系统使用区域不足以容纳所有Rock Ridge条目时,CE通过一个独立的extent扩展存储空间。CE条目包含三个32位字段:extent(逻辑块号)、offset(块内偏移)、size(数据大小)。
rock_ridge_continue()处理CE扩展区的读取:
static int rock_ridge_continue(struct iso_inode_info *inode,
struct ce_handle *ce)
{
int blocksize = inode->i_sb->s_blocksize;
struct buffer_head *bh;
unsigned char *ptr;
int ret;
bh = sb_bread(inode->i_sb, ce->extent);
if (!bh)
return -EIO;
ptr = bh->b_data + ce->offset;
inode->i_sb->s_rock_ridge = kmalloc(ce->size, GFP_KERNEL);
if (!inode->i_sb->s_rock_ridge) {
brelse(bh);
return -ENOMEM;
}
memcpy(inode->i_sb->s_rock_ridge, ptr, ce->size);
inode->i_sb->s_rock_ridge_size = ce->size;
brelse(bh);
return 0;
}
该函数通过sb_bread()读取CE扩展区的数据块,将其中offset偏移处开始、长度为size的数据复制到s_rock_ridge缓冲区中。之后主解析循环继续从这个新缓冲区读取剩余的Rock Ridge条目。
Rock Ridge条目的验证涉及checksum校验。每个条目的第4字节起如果version字段匹配,则使用SP签名中的checksum字段验证数据完整性。SP条目是SUSP协议中的第一个条目,位于系统使用区域的起始处:
case SIG_SP:
if (rr->u.SP.len_skp < 4)
break;
inode->i_sb->s_rock_offset = rr->u.SP.len_skp;
break;
SP条目的len_skp字段指定了系统使用区域的起始偏移(通常为4),所有后续条目从该偏移开始解析。
ER(Extensions Reference)条目声明了使用的扩展协议ID,包括Rock Ridge的版本信息和POSIX属性声明。当isofs检测到Rock Ridge的ER签名时,会设置inode->i_rock_ridge标志位,启用Unix语义的完整处理。
RR(Rock Ridge)条目本身是一个位掩码,其中RR_PX位表示存在POSIX属性,RR_PN表示存在设备号,RR_SL表示符号链接,RR_NM表示备用文件名,RR_CL/RR_PL表示子目录/父目录链接(用于处理深度超过8层的目录层次结构)。
Rock Ridge通过CL(Child Link)和PL(Parent Link)机制解决ISO 9660目录深度限制问题。CL条目将一个目录记录标记为指向另一个位置的符号链接,而PL条目提供父目录的逆向链接。这种机制使得ISO 9660原本受限的8层目录可以扩展到任意深度。
Linux isofs rock ridge扩展与CE签名处理