如何在内存中解压文件(C编程)?

5
让我解释一下我的想法:我有一个加密的tar文件。我可以在内存中解密它,但是显然我无法将解密后的数据作为真实文件写回硬盘。解密后的数据以char*缓冲区的形式结构化存储在内存中;如何在内存中untar它呢?我无法在libtar库中找到答案。我还尝试使用execlp("tar", "tar", "-xvO", (void*)0)来untar它。但结果不符合我的想法。有谁能给我提示最佳解决方案吗?谢谢!

也许更简单的方法是将id更改为特殊用户,编写一个临时文件,读取它,删除它,然后再切换回普通用户? - gnud
只是一个简单的问题:标题为什么要说“C编程”,因为标签已经表明了这一点? - Nathan Campos
gnud,我认为在每台目标机器上创建特殊用户是不可行的。 Campos,这个问题被标记为“c”和“tar”,抱歉,作为新手,我不明白你的意思。如果我错了,我会纠正它。 - solotim
1
注意,你的‘execlp()’调用应该以'(char *)0'参数结束,标记参数列表的结尾。此外,你可能需要指定“-xvf”和“ - ”作为参数,以便tar读取其标准输入。然后,你需要安排一个管道将你的应用程序连接到tar的输入。然后,你需要将填满已解密的tar文件的缓冲区写入管道。 - Jonathan Leffler
鉴于tar文件现在已被解密,为什么你不能将解密后的文件写入磁盘?它的内容即将被提取到磁盘上 - 因此tar文件的内容不再是机密。你可以给所创建的文件设置限制权限。 - Jonathan Leffler
谢谢,我只是在输入时忘记了(void*)0。我尝试使用管道来管理tar命令的stdin和stdout,但失败了。请参见我对eyalm的评论。 我无法将其写回磁盘,因为用户不允许获取任何tar文件的信息。解密和解压缩后的内容仅由我的程序读取。所有东西都在内存中,最终用户除非破解我的程序,否则永远无法知道加密的tar文件中的内容。 - solotim
5个回答

8
我怀疑 libtar 是答案。 使用 libtar,您可以指定自己的打开/关闭、读取和写入函数。 从 man 页面中可以看到:
int tar_open(TAR **t, char *pathname, tartype_t *type, int oflags,
             int mode, int options);

tar_open()函数打开一个tar归档文件,该文件名由pathname参数指定。 oflags参数必须是O_RDONLYO_WRONLY

type参数指定了给定文件类型的访问方法。 tartype_t结构具有名为openfunc()closefunc()readfunc()writefunc()的成员,它们是分别用于打开、关闭、读取和写入文件的函数的指针。 如果type为NULL,则文件类型默认为普通文件,并使用标准的open()close()read()write()函数。


作为tar_open函数读取char * pathname: int tar_open(TAR ** t,char * pathname,tartype_t * type,int oflags,int mode,int options); 路径名必须是某个真实的文件,对吗? - solotim
1
请看我的编辑 - 您可以使用自己的文件访问函数,这些函数可以直接在内存中工作。 - gnud
openfunc必须返回一个fildes,我无法弄清如何从缓冲区获取fildes。我尝试使用fileno(fmemopen(...)),但它只返回-1。我很沮丧。 - solotim
@solotim 难道 fildes 不是你自己选择的整数标识符吗?例如,它可以是指向虚拟文件数组的索引,该数组用于跟踪程序对其读取和写入的操作。 - Craig McQueen
@CraigMcQueen 谢谢。但是抱歉我不明白。文件描述符怎么可能是我可以定义的东西?您能否详细解释一下? - solotim
你可以定义文件操作函数(openfunc、closefunc、readfunc、writefunc),它们可以执行任何你想要的操作。你需要自己定义这些函数,并将它们传递给libtar。由于你决定了如何处理libtar认为是文件描述符的整数,因此它实际上可以是任何你想要的东西。只有你编写的函数和libtar才能看到它。 - gnud

7

我制作了一个示例,介绍如何从内存tar中读取文件内容。函数is_file_in_tar()会返回name所在的位置和length长度,如果它被存储在tar中:

#include <stdio.h> 
#include <fcntl.h> 
#include <string.h> 
#include <sys/mman.h> 

struct tar {
  char name[100];   char _unused[24];
  char size[12];    char _padding[376];
} *tar;

int is_file_in_tar( struct tar *tar, char *name, char **start, int *length ){
  for( ; tar->name[0]; tar+=1+(*length+511)/512 ){
    sscanf( tar->size, "%o", length);
    if( !strcmp(tar->name,name) ){ *start = (char*)(tar+1); return 1; }
  }
  return 0;
}

int main(){
  int fd=open( "libtar-1.2.11.tar", O_RDONLY );
  tar=mmap(NULL, 808960, PROT_READ, MAP_PRIVATE, fd, 0);

  char *start; int length; char name[]="libtar-1.2.11/TODO";
  if( is_file_in_tar(tar,name,&start,&length) ) printf("%.*s",length,start);
}

这也很棒。这样我就可以通过指针直接访问tar中的数据了。 但是,如果文件是tar.gz格式,该怎么办? - solotim
1
zlib可以在不改变文件的情况下透明地打开gzip压缩过的文件,即使文件没有经过gzip压缩也可以使用相同的方法。 - Martin Beckett

2

我用这段代码实现了它。试试看吧!

FILE*fp;
if( fp = popen("/bin/tar -xv -C /target/dir", "w") )
{
    fwrite(tar_buffer,1,tar_size,fp);
    pclose(fp);
    printf("Untar End %d Save file\n", tar_size);

}

2

您可以执行tar实用程序并将其重定向到标准输出。(tar --to-stdout)。为了读取输出,您应该使用forkpty()popen()来运行它。


我曾尝试使用“tar -xvO”命令,它从stdin读取数据并写入stdout。在我的程序中,我必须使用两个管道:一个是tardata的stdin,另一个是untarreddata的stdout。难点在于,当我将tardata输入到stdin管道时,由于原始tardata中包含一些'\0'字符,因此tar命令无法从stdin识别完整的tar文件。你有什么想法吗?谢谢! - solotim

0

只需使用普通的解压操作将其解压到内存tmpfs中。


我认为这是不安全的,因为tmpfs可以被任何其他进程访问。对吗? - solotim
根据用户权限而定。因此,我的建议是使用“特殊”用户来编写临时文件。 - gnud
根用户始终可以转储您进程中的内存内容。 - Martin Beckett

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接