[转]复制文件空洞问题(cat与cp)的区别

上次课上学了lseek这个函数,可以制造出“空洞”文件。演示中,我们发现,用vi或者od命令查看带空洞的文件,空洞部分填满了字符0。于是产生了:

问题一:空洞文件和用字符0写同样长度的文件一样吗?

我们来写一个2000000个’/0’的文件write0.file,再创建一个有长2000000的空洞的文件hole.file。具体方法就不用赘述了(前面直接写,后面lseek)。然后用ls –ls命令查看(当然也可以用du,不过比较麻烦),发现write0.file的大小为1960k,而hole.file只有8k。所以:

问题一的答案是:不一样。虽然我们用vi或者od读取这两个文件,内容都是显示的一样的,但是这两个文件是有本质的区别的。

问题二:用我们自己写的文件mycp(或者cat),拷贝出两个文件,是什么效果?

用./mycp<write0.file>mycp.write0.file和./mycp<hole.file>mycp.hole.file。再ls –ls一下发现:拷贝出来的两个文件都是1960k!看来我们的拷贝并没有真正复制到文件全部的信息。

问题二的答案是:结果是一样的。有空洞的文件拷贝完成后,所占磁盘空间变大了。我们写的那个拷贝有bug,所以:

问题三:用shell里面的cp拷贝的结果一样吗?

用cp write0.file cp.write0.file和cp hole.file cp.hole.file 。再ls –ls一下发现:拷贝出来的两个文件和源文件所占磁盘空间是一样的!应该说,shell里面的那个cp比咱们的要牛一些,它不会丢失文件的信息。man一下cp发现一个参- -sparse,这个参数专门用来处理文件空洞,它的值有三:auto(默认的,原来有洞,拷贝出来就有洞。原来没洞,拷贝出来也没洞);always(有没有洞都填满0),never(有没有洞都不填)。

答案三:cp比mycp功能强大。于是:

问题四:cp是怎么实现的?

这种问题有个万能解决办法——看源码。

#if HAVE_STRUCT_STAT_ST_BLOCKS
if (x->sparse_mode == SPARSE_AUTO && S_ISREG (sb.st_mode))
{
    /* Use a heuristic to determine whether SRC_PATH contains any sparse blocks. */
    if (fstat (source_desc, &sb))
    {
        error (0, errno, _("cannot fstat %s"), quote (src_path));
        return_val = -1;
        goto close_src_and_dst_desc;
    }
    /* If the file has fewer blocks than would normally be needed for a file of its size, then at least one of the blocks in the file is a hole. */
    if (S_ISREG (sb.st_mode) && sb.st_size / ST_NBLOCKSIZE > ST_NBLOCKS (sb))
         make_holes = 1;
}

关键在最后一个if判断,通过注释可以看出,它通过比较sb.st_size/ ST_NBLOCKSIZE(sb是被拷文件的stat,表示inode里面存放的文件的长度)和ST_NBLOCKS (sb)(表示所占的实际磁盘大小)。来判断有没有空洞,进行处理。如果有空洞,就跳,如果没有空洞,就写0。具体实现在这里:


if (make_holes)
{
    buf[n_read] = 1;   /* Sentinel to stop loop.  */
    /* Find first nonzero *word*, or the word with the sentinel.  */

    ip = (int *) buf;
    while (*ip++ == 0)
        ;

    /* Find the first nonzero *byte*, or the sentinel.  */
    cp = (char *) (ip - 1);
    while (*cp++ == 0)
        ;

    /* If we found the sentinel, the whole input block was zero, and we can make a hole.  */
    if (cp > buf + n_read)
    {
        /* Make a hole.  */
        if (lseek (dest_desc, (off_t) n_read, SEEK_CUR) < 0L)
        {
            error (0, errno, _("cannot lseek %s"), quote (dst_path));
            return_val = -1;
            goto close_src_and_dst_desc;
        }
        last_write_made_hole = 1;
    }
    else
        /* Clear to indicate that a normal write is needed. */
        ip = 0;
}

问题四答案:嗯!确实比咱们的mycp考虑的周全,但是有没有别的问题呢?

问题五:如果一个文件里面既有空洞,又有人故意写了许多0字符。那么cp还能正确拷贝出源文件的大小吗?

先想一想,如果要我做,我该怎么实现,想来想去发现这个问题好难实现。难处在于就算通过判断文件大小分析出确实存在空洞,但是要找到哪里有空洞,还是需要一个一个读。但是是空洞的地方读出来就是0字符,和专门写的0字符没有办法分辨。看看上面cp的源码,好像也没有考虑这个问题。于是我试验了一下,创建一个文件cp.test,先lseek了1000000然后又继续写了1000000个0字符。先用ls –ls看看结果,发现它的长度为2000000,占用空间为984k。好,分别用sparse的三个参数拷贝出三个文件:cp.auto;cp.always;cp.never。再查看大小,发现cp.auto和cp.always都是只有8k,cp.never有1960k。没有一个是源程序的大小984k。看来真的没有办法分别空洞和0字符的位置了。

问题五答案:cp也有拷贝不正确的时候。从这里看出,感觉auto和always参数都是一个意思,设计的有些重复了。

得到许多结论:

一、空洞和单纯的0字符并不完全一样,只是空洞被读时会输出0字符。

二、咱们写的那个mycp的程序并不完善,并不能实现cp空洞文件的功能。

三、shell的cp工具也不完善,处理空洞文件时也是有bug的。虽然可以判断出文件有没有空洞,但是不能知道哪里是真正的空洞,哪里是“伪装的空洞”。

四、从cp的源码里面学到sb.st_size和ST_NBLOCKS (sb),一般来说,这两个东西所代表的是不一样的。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,539评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,911评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,337评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,723评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,795评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,762评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,742评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,508评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,954评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,247评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,404评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,104评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,736评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,352评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,557评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,371评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,292评论 2 352