在 C 中创建我自己的存档工具 [关闭]

Posted

技术标签:

【中文标题】在 C 中创建我自己的存档工具 [关闭]【英文标题】:Creating my own archive tool in C [closed] 【发布时间】:2015-04-15 05:06:03 【问题描述】:

我刚刚被分配了一个项目来为 unix 创建一个归档工具。所以在创建程序后,我会做类似的事情

"./bar -c test_archive.bar file.1"

它将创建一个 test_archive.bar,其中包含 file.1。然后我可以执行一些命令来列出里面的文件等等。但是我无法理解制作 test_archive.bar 的概念,我意识到它本质上只是一个文件,但如果你说打开一个.tgz "vi file.tgz" 它会给出一个目录/文件列表,

那么,有什么好的方法可以创建一个存档/目录,我可以在其中推断一些文件并列出它们的名称等。

注意:我查看了 tar.c 以及其中包含的所有文件,但每个文件都非常抽象,很难理解。

注意:我知道如何读取命令行标志等。

【问题讨论】:

gnu.org/software/tar/manual/html_node/Standard.html 有帮助吗? 将档案视为一本书。它有按顺序排列的章节(文件),以及一个说明每章开始位置和名称的目录。 感谢@mikyra 链接帮助很大 您引用的vi 示例只是vi 应用程序的一个功能。它恰好知道如何解压缩和读取 tar 存档。在制作自己的存档格式时,您首先不需要关心vi 或任何其他特定应用程序,尽管您可能会发现扩展vi 以便它可以理解您的文件格式是值得的。 @n.m.对,我理解它背后的理论,我只是不明白如何实现它。就像它只是一个文件中的一系列数据块?有一个标题描述下面的数据,就像一个数据字符串是标题“然后是一个字节字符串是文件 【参考方案1】:

使用旧的(但仍然有效的)tar 格式实际上很容易做到。 Wikipedia has a nice explanation of the format here.你只需要这样做:

对于每个文件:

填写并将标头发送到 tar 文件 发出文件内容 将文件大小填充为 512 字节的倍数

tar 文件最基本的有效标头是:(基本上是从 Wikipedia 复制的)

100 字节:文件名 8 字节:文件模式 8 字节:所有者的数字 ID 8 字节:组的数字 ID 12 字节:文件大小 12字节:上次修改时间的时间戳 8 字节:校验和 1 字节:文件类型 100 字节:链接文件的名称

文件类型可以是 0(普通文件)、1(硬链接)或 2(符号链接)。链接文件的名称是链接指向的文件的名称。如果我没记错的话,如果你有硬链接或符号链接,文件内容应该是空的。

引用***:

“数值使用 ASCII 数字以八进制数编码,前导零。出于历史原因,应使用最终的 NUL 或空格字符。”

"校验和的计算方法是将标头记录的无符号字节值与作为 ascii 空格的八个校验和字节(十进制值 32)相加。它存储为六位八进制数,后跟前导零由一个 NUL 然后一个空格。”

这是一个简单的 tarball 生成器。创建一个提取器,处理自动文件馈送等,留给读者练习。

#include<stdio.h>
#include<string.h>


struct tar_header
    char name[100];
    char mode[8];
    char owner[8];
    char group[8];
    char size[12];
    char modified[12];
    char checksum[8];
    char type[1];
    char link[100];
    char padding[255];
;

void fexpand(FILE* f, size_t amount, int value)
    while( amount-- )
        fputc( value, f );
    


void tar_add(FILE* tar_file, const char* file, const char* internal_name)
    //Get current position; round to a multiple of 512 if we aren't there already
    size_t index = ftell( tar_file );
    size_t offset = index % 512;
    if( offset != 0 )
        fexpand( tar_file, 512 - offset, 0);
    
    //Store the index for the header to return to later
    index = ftell( tar_file );
    //Write some space for our header
    fexpand( tar_file, sizeof(struct tar_header), 0 );
    //Write the input file to the tar file
    FILE* input = fopen( file, "rb" );
    if( input == NULL )
        fprintf( stderr, "Failed to open %s for reading\n", file);
        return;
    
    //Copy the file content to the tar file
    while( !feof(input) )
        char buffer[2000];
        size_t read = fread( buffer, 1, 2000, input );
        fwrite( buffer, 1, read, tar_file);
    
    //Get the end to calculate the size of the file
    size_t end = ftell( tar_file );
    //Round the file size to a multiple of 512 bytes
    offset = end % 512;
    if( end != 0 )
        fexpand( tar_file, 512 - offset, 0);
    
    //Fill out a new tar header
    struct tar_header header;
    memset( &header, 0, sizeof( struct tar_header ) );
    snprintf( header.name, 100, "%s", internal_name  );
    snprintf( header.mode, 8, "%06o ", 0777 ); //You should probably query the input file for this info
    snprintf( header.owner, 8, "%06o ", 0 ); //^
    snprintf( header.group, 8, "%06o ", 0 ); //^
    snprintf( header.size, 12, "%011o", end - 512 - index );
    snprintf( header.modified, 12, "%011o ", time(0) ); //Again, get this from the filesystem
    memset( header.checksum, ' ', 8);
    header.type[0] = '0';

    //Calculate the checksum
    size_t checksum = 0;
    int i;
    const unsigned char* bytes = &header;
    for( i = 0; i < sizeof( struct tar_header ); ++i )
        checksum += bytes[i];
    

    snprintf( header.checksum, 8, "%06o ", checksum );

    //Save the new end to return to after writing the header
    end = ftell(tar_file);

    //Write the header
    fseek( tar_file, index, SEEK_SET );
    fwrite( bytes, 1, sizeof( struct tar_header ), tar_file );

    //Return to the end
    fseek( tar_file, end, SEEK_SET );
    fclose( input );


int main( int argc, char* argv[] )
    if( argc > 1 )
        FILE* tar = fopen( argv[1], "wb" );
        if( !tar )
            fprintf( stderr, "Failed to open %s for writing\n", argv[1] );
            return 1;
        
        int i;
        for( i = 2; i < argc; ++i )
            tar_add( tar, argv[i], argv[i] );
        
        //Pad out the end of the tar file
        fexpand( tar, 1024, 0);
        fclose( tar );
        return 0;
    
    fprintf( stderr, "Please specify some file names!\n" );
    return 0;

【讨论】:

【参考方案2】:

那么,有什么好的方法可以创建存档/目录 我可以在其中推断一些文件并列出它们的名称 等等。

基本上有两种方法:

    一个接一个地复制文件内容,每个都以“header”块为前缀,包含有关文件名、大小和(可选)其他属性的信息。焦油就是一个例子。示例:

    一个接一个地复制文件内容并放在某个地方(在末尾的开头)“索引”,其中包含文件名列表及其大小和(可选)其他属性。查看文件大小时,您可以计算出各个文件的开始/结束位置。

大多数现实世界的存档器使用这些组合,并添加其他功能,例如校验和、压缩和加密。

例子

假设我们有两个名为hello.txt 的文件包含Hello, World!(12 个字节)和bar.txt,包含foobar(6 个字节)。

在第一种方法中,存档看起来像这样

[hello.txt,12][Hello, World!][bar.txt,6][foobar]
 ^- fixed size ^- 12 bytes    ^- fixed size ^- 6 bytes

标头块的长度必须是恒定的,或者您必须在某处对其长度进行编码。

第二次:

[Hello, World!foobar][hello.txt,12,bar.txt,6]
 ^- 12+6 bytes

【讨论】:

你的好例子与上面的例子结合起来真的帮助了我。非常感谢。

以上是关于在 C 中创建我自己的存档工具 [关闭]的主要内容,如果未能解决你的问题,请参考以下文章

在 R 中创建我自己的蜘蛛图而不使用任何库

编辑:在Node-Red上导入C项目[关闭]

如何在 php 中创建存档列表?

如何使用 Python zipfile 将文件放入 zip 存档中

如何在 NetBeans 中创建我的项目的 war 文件?

为啥我应该在构造函数而不是 ngOnInit 中创建我的 Angular2 响应式表单?