- 论坛徽章:
- 0
|
周四,俺偶得空闲,上MSN来转了转,看看有没有MM可以套套辞什么的,唉,正郁闷于没找到目标下手欲转身溜下线时,突然,咔嚓一声炸雷,一弹出个消息窗口出现在俺眼前。原来,强大的“阿凡提”兄弟,又来发彪了:“呵呵,正巧有事想找你!”,俺暗付,要给俺介绍MM?!他自己都还没有解决战斗呢呀,嗯,正想强行输入88抓紧溜掉的时候,“你知道codepage和iocharset有什么区别么?”,K,算你快,唉,接招吧。然后,俺与之JJYY几十分钟有余,俺的功力渐已不支之际,他也被俺忽悠得是!@#$%^&*,哈哈,算是打一个平手。
回到总舵后,沉下心来反省反省:是呀,自己一直将这两个参数和utf8当作尚方宝剑用,可是并不知道它们就怎么起作用的!呵呵,以前看FAT规范时是着重看BPB部分,因为那时俺偶而把用户的硬盘分区表搞掉,所以有时被充作技术支持恢复分区表去,其实也是在救赎自己~ 这次,是得把重点放在后面的文件组织上了。不过,俺不写一大堆FAT32的规范文本了,这里是一些与下文有关的简介:
1、短文件名,使用的是单字节字符集,或者DBCS。在存储介质上保存时,文件名转换为大写形式后保存的,因为不同国家有不同的小写形式。
2、长文件名,使用的是16bit字符集,即Unicode。在存储介质上保存时,不区分大小写。
3、一个具有长文件名的文件,FAT32文件系统用一个短文件名目录项和多个长文件名目录项保存。这样,不支持长文件名的FAT实现也可以正常访问这种文件。短文件名目录项只保存11个字符,所以,为了分割基本文件名和扩展文件名,FAT实现得自己插入两者之间的“.”,实际上,在短文件名目录项的文件名字段内,不允许出现“.”。长文件名,就是长文件名啦。
OK,从入口开始。不过,我懒得一点点展开了,反正这部分也是很简单,看过一点Linux情景分析的朋友都能看明白,我就提两个关键的地方出来吧:
/*fat_fill_super() 函数:*/
sbi = kzalloc(sizeof(struct msdos_sb_info), GFP_KERNEL);
/* 这是处理codepage选项的部分 */
sprintf(buf, "cp%d", sbi->options.codepage);
sbi->nls_disk = load_nls(buf);
/* 这是处理iocharset选项的部分 */
sbi->nls_io = load_nls(sbi->options.iocharset);
OK,那么nls_io/nls_disk是个什么东东哩?嗯,一个nls_table的结构指针,nls_table的定义如下:
struct nls_table {
char *charset;
char *alias;
int (*uni2char) (wchar_t uni, unsigned char *out, int boundlen);
int (*char2uni) (const unsigned char *rawstring, int boundlen,
wchar_t *uni);
unsigned char *charset2lower;
unsigned char *charset2upper;
struct module *owner;
struct nls_table *next;
};
估计你能猜出个一二,它是干什么的了。对于nls_cp936来说,它的charset和alias成员分别等于"cp936"和"gb2312"。嗯?你对load_nls()是怎么工作的也有兴趣?恭喜你,你与俺英雄所见略同呀,下面是简化后的代码:
struct nls_table *load_nls(char *charset)
{
struct nls_table *nls;
nls = find_nls(charset);
return nls;
}
static struct nls_table *find_nls(char *charset)
{
struct nls_table *nls;
spin_lock(&nls_lock);
for (nls = tables; nls; nls = nls->next) {
if (!strcmp(nls->charset, charset))
break;
if (nls->alias && !strcmp(nls->alias, charset))
break;
}
spin_unlock(&nls_lock);
return nls;
}
呵呵呵,这下明白了吧,codepage=cp936和iocharset=gb2312,在后端上是一样一样一样地!
下一步,就是nls_io和nls_disk分别在什么时机上用呢?FAT实现里有几处使用它们的地方,但使用模式很相似,我们只看一个函数吧,解释见注释:
int fat_search_long(struct inode *inode, const unsigned char *name,
int name_len, struct fat_slot_info *sinfo)
{
struct super_block *sb = inode->i_sb;
struct msdos_sb_info *sbi = MSDOS_SB(sb);
struct buffer_head *bh = NULL;
struct msdos_dir_entry *de;
struct nls_table *nls_io = sbi->nls_io; /* 这是宝贝1,代号tom */
struct nls_table *nls_disk = sbi->nls_disk; /* 这是宝贝2,代号jerry */
wchar_t bufuname[14];
unsigned char xlate_len, nr_slots;
wchar_t *unicode = NULL;
unsigned char work[8], bufname[260]; /* 256 + 4 */
int uni_xlate = sbi->options.unicode_xlate;
int utf8 = sbi->options.utf8;
int anycase = (sbi->options.name_check != 's');
unsigned short opt_shortname = sbi->options.shortname;
loff_t cpos = 0;
int chl, i, j, last_u, err;
err = -ENOENT;
while(1) {
if (fat_get_entry(inode, &cpos, &bh, &de) == -1) /* 这应该是从硬盘上读出dir_entry之类的操作吧?!嗯,我想是! */
goto EODir;
parse_record:
nr_slots = 0;
if (de->name[0] == DELETED_FLAG)
continue;
if (de->attr != ATTR_EXT && (de->attr & ATTR_VOLUME))
continue;
if (de->attr != ATTR_EXT && IS_FREE(de->name))
continue;
if (de->attr == ATTR_EXT) { /* 这是个长文件名目录项,具体地可以参考FAT32规范26页 */
int status = fat_parse_long(inode, &cpos, &bh, &de,
&unicode, &nr_slots); /* 从磁盘上的数据结构里解析出长文件名字符串,保存在unicode变量里,nr_slots保存了这个文件有多少个长文件名目录项 */
if (status 0)
return status;
else if (status == PARSE_INVALID)
continue;
else if (status == PARSE_NOT_LONGNAME)
goto parse_record;
else if (status == PARSE_EOF)
goto EODir;
}
memcpy(work, de->name, sizeof(de->name)); /* de->name里面是11个字符的8.3文件名 */
/* see namei.c, msdos_format_name */
if (work[0] == 0x05) /* 转义字符,见FAT32规范的23页 */
work[0] = 0xE5;
for (i = 0, j = 0, last_u = 0; i 8;) {
if (!work) break;
chl = fat_shortname2uni(nls_disk, &work, 8 - i,
&bufuname[j++], opt_shortname,
de->lcase & CASE_LOWER_BASE); /* 哈哈,jerry(codepage)出现了。原来,codepage是用来将短文件名转成unicode的呀~ */
if (chl = 1) {
if (work != ' ') /* 见FAT32规范的页24 */
last_u = j;
} else {
last_u = j;
}
i += chl;
}
j = last_u;
fat_short2uni(nls_disk, ".", 1, &bufuname[j++]); /* 插入一个.字符 */
for (i = 0; i 3;) {
if (!de->ext) break;
chl = fat_shortname2uni(nls_disk, &de->ext, 3 - i, /* 一回生二回熟了哈,jerry干活真卖力气。 */
&bufuname[j++], opt_shortname,
de->lcase & CASE_LOWER_EXT);
if (chl = 1) {
if (de->ext != ' ')
last_u = j;
} else {
last_u = j;
}
i += chl;
}
if (!last_u)
continue;
bufuname[last_u] = 0x0000;
xlate_len = utf8 /* 这个utf8,其实就是指是否指定了utf8选项,也就指定了是不是先转换成UTF8再做后续处理。 */
?utf8_wcstombs(bufname, bufuname, sizeof(bufname))
:uni16_to_x8(bufname, bufuname, uni_xlate, nls_io);
if (xlate_len == name_len) /* 如果这是个短文件名文件,再由tom(iocharset)转一下bufname,再进行字符比较之 */
if ((!anycase && !memcmp(name, bufname, xlate_len)) ||
(anycase && !nls_strnicmp(nls_io, name, bufname,
xlate_len)))
goto Found;
if (nr_slots) { /* 如果这是个长文件名文件,再由tom(iocharset)转一下unicode,再进行字符比较之 */
xlate_len = utf8
?utf8_wcstombs(bufname, unicode, sizeof(bufname))
:uni16_to_x8(bufname, unicode, uni_xlate, nls_io);
if (xlate_len != name_len)
continue;
if ((!anycase && !memcmp(name, bufname, xlate_len)) ||
(anycase && !nls_strnicmp(nls_io, name, bufname,
xlate_len)))
goto Found;
}
}
Found:
nr_slots++; /* include the de */
sinfo->slot_off = cpos - nr_slots * sizeof(*de);
sinfo->nr_slots = nr_slots;
sinfo->de = de;
sinfo->bh = bh;
sinfo->i_pos = fat_make_i_pos(sb, sinfo->bh, sinfo->de);
err = 0;
EODir:
if (unicode)
free_page((unsigned long)unicode);
return err;
}
呵呵,这下,阿凡提发的这记“咔嚓炸雷彪之VFAT”让俺破了:
1、codepage,用于将短文件名转成unicode,为其后的文件名比较作辅垫的。
2、iocharset,用于正式的文件名比较之前,既用于长文件名,也用于短文件名。
我也得翻翻我那几本坏帐,找找哪卷哪页哪行上有什么套路可以还上他一“彪”,以解俺心头之怨。
哈哈,俺们老百姓今儿真高兴~下线买新手机去喽,但愿别遇到上JS。
PS:发现经常和阿凡提PK,有助于提高IC、IP和IQ,这个家伙很认真么,继续努力,吼吼吼。
本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u1/42957/showart_348708.html |
|