免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1273 | 回复: 0
打印 上一主题 下一主题

FAT32文件系统的小“秘秘” [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2007-07-28 08:41 |只看该作者 |倒序浏览

                                                    周四,俺偶得空闲,上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
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP