2016-07-12 46 views
0

使用golang中的归档文件/ tar包,似乎无法访问文件具有的硬链接数量。但是,我记得在某处读取tar目录或文件可以保留硬链接。保存硬链接的Tar归档文件

是否有一些包可以帮助我做到这一点?

+0

其实我已经重新阅读你的问题好几次,现在扶着想到你居然要问如何找到硬链接的文件*在源文件系统*当它们被添加到压缩文件,而不是怎么算的在tar档案中的硬连接文件的数量(我回答的问题)。你能否详细说明你想问什么? – kostix

+0

嘿kostix, 我其实的意思是问如何计算硬链接的数量,同时流式存档。尽管感谢您的输入! – steve

+0

好的,用PoC实现更新我的代码。适用于我的硬链接的测试目录(对文件/目录名的模数处理不完整);我没有时间调整它,像股票'焦油'工作,对不起。 – kostix

回答

3

tar会保留硬链接。

这里的三个硬链接的文件,一个文件,一个环节一个样品目录:

foo% vdir . 
total 16 
-rw-r--r-- 3 kostix kostix 5 Jul 12 19:37 bar.txt 
-rw-r--r-- 3 kostix kostix 5 Jul 12 19:37 foo.txt 
-rw-r--r-- 3 kostix kostix 5 Jul 12 19:37 test.txt 
-rw-r--r-- 1 kostix kostix 9 Jul 12 19:49 xyzzy.txt 

现在我们将其归档使用GNU tar并验证它确实增加了链接 (因为我们没有传递给它--hard-dereferece命令行选项):

foo% tar -cf ../foo.tar . 
foo% tar -tvf ../foo.tar 
drwxr-xr-x kostix/kostix  0 2016-07-12 19:49 ./ 
-rw-r--r-- kostix/kostix  9 2016-07-12 19:49 ./xyzzy.txt 
-rw-r--r-- kostix/kostix  5 2016-07-12 19:37 ./bar.txt 
hrw-r--r-- kostix/kostix  0 2016-07-12 19:37 ./test.txt link to ./bar.txt 
hrw-r--r-- kostix/kostix  0 2016-07-12 19:37 ./foo.txt link to ./bar.txt 

archive/tar文档是指一束限定在tar ARCHI标准文献(不幸的是,没有一个标准:例如,GNU tar不支持POSIX扩展属性,而BSD tar(依赖于libarchive),而pax也是如此。 举的硬链接其位:

LNKTYPE

此标志表示链接到另一个文件的文件任何类型的, 先前存档。这些文件在Unix中由每个具有相同设备和inode编号的文件标识。链接名称在 链接名称字段中指定,其后缀为空。

因此,一个hadrlink是指一些 前述(已归档)通过其名称的文件的特殊类型(“1”)的enrty。

所以我们来创建一个游乐场的例子。

我们的base64编码我们的档案:

foo% base64 <../foo.tar | xclip -selection clipboard 

&hellip;而写the code。 档案包含一个单一目录,一个文件(类型'0'),另一个文件(类型'0'),后面跟着两个硬链接(类型'1')。

从操场示例的输出:

Archive entry '5': ./ 
Archive entry '0': ./xyzzy.txt 
Archive entry '0': ./bar.txt 
Archive entry '1': ./test.txt link to ./bar.txt 
Archive entry '1': ./foo.txt link to ./bar.txt 

所以你的链接计数代码应该:

  1. 扫描整个档案记录,通过记录。

  2. 记住任何常规文件(类型archive/tar.TypeReg 或键入archive/tar.TypeRegA)已经处理过的,并具有与其相关联的计数器,从1开始。

    那么,在现实中,你最好是独家和记录条目各类 除了符号链接和目录—因为焦油 档案文件可以包含字符和块设备节点和FIFO(命名管道)。

  3. 当你遇到一个硬链接(类型archive/tar.TypeReg),

    1. 阅读它的头的Linkname领域。
    2. 查看“已见”文件的列表,并增加与该名称匹配的条目的计数器 。在2016年7月13日

更新作为OP其实是想知道如何在 源文件系统管理的硬链接,这里的更新。

的主要想法是,与POSIX语义文件系统:

  • 目录项指定一个文件实际上指向称为“索引节点”的特殊 文件系统元数据块。 inode包含指向它的目录条目数 。

    创建硬链接实际上只是:

    1. 创建新目录条目指向的inode原始(源)的 在ln条款文件—“链接目标”。
    2. 递增该inode中的链接计数器。
  • 因此,任何文件被唯一地标识由两个整数: “设备号”标识主机文件系统 在其上的文件的位置,并且节点号码识别该文件的数据的物理设备。

    接下来,如果两个文件具有相同的(设备,inode)对,则它们表示相同的内容。或者,如果我们换一种说法,其中一个 是另一个的硬链接。

因此,将文件添加至tar存档,同时保留硬链接的工作原理是这样的:

  1. 已经添加了一个文件,它的(设备,索引节点)对保存一些查找表。

  2. 当添加另一个文件时,找出它的(设备,inode)对,然后在该表中查找它。

    如果找到匹配项,则表明文件的数据已经传输,我们应该添加一个硬链接。

    否则,表现如步骤(1)。

所以这里的代码:

package main 

import (
    "archive/tar" 
    "io" 
    "log" 
    "os" 
    "path/filepath" 
    "syscall" 
) 

type devino struct { 
    Dev uint64 
    Ino uint64 
} 

func main() { 
    log.SetFlags(0) 

    if len(os.Args) != 2 { 
     log.Fatalf("Usage: %s DIR\n", os.Args[0]) 
    } 

    seen := make(map[devino]string) 

    tw := tar.NewWriter(os.Stdout) 

    err := filepath.Walk(os.Args[1], 
     func(fn string, fi os.FileInfo, we error) (err error) { 
      if we != nil { 
       log.Fatal("Error processing directory", we) 
      } 

      hdr, err := tar.FileInfoHeader(fi, "") 
      if err != nil { 
       return 
      } 

      if fi.IsDir() { 
       err = tw.WriteHeader(hdr) 
       return 
      } 

      st := fi.Sys().(*syscall.Stat_t) 
      di := devino{ 
       Dev: st.Dev, 
       Ino: st.Ino, 
      } 

      orig, ok := seen[di] 
      if ok { 
       hdr.Typeflag = tar.TypeLink 
       hdr.Linkname = orig 
       hdr.Size = 0 

       err = tw.WriteHeader(hdr) 
       return 
      } 

      fd, err := os.Open(fn) 
      if err != nil { 
       return 
      } 
      err = tw.WriteHeader(hdr) 
      if err != nil { 
       return 
      } 
      _, err = io.Copy(tw, fd) 
      fd.Close() // Ignoring error for a file opened R/O 
      if err == nil { 
       seen[di] = fi.Name() 
      } 
      return err 
     }) 

    if err != nil { 
     log.Fatal(err) 
    } 

    err = tw.Close() 
    if err != nil { 
     log.Fatal(err) 
    } 

    return 
} 

请注意,这是相当跛:

  • 它不适当地文件和目录名的交易。

  • 它并不试图用正确的符号链接和FIFO, 工作,并跳过Unix域套接字等

  • 它假定它工作在一个POSIX环境。

    在非POSIX系统中,Sys()方法呼吁 os.FileInfo类型的值可能会返回别的东西,而不是POSIX'y syscall.Stat_t

    假设在Windows上有多个由不同的 “磁盘”或“驱动器”托管的文件系统。我不知道Go如何处理它。 也许在这种情况下,必须以某种方式模拟“设备号”。

在另一方面,它显示了如何处理硬链接:

  • 将头结构的“链接名称”字段。
  • 将标题的“大小”字段重置为0(因为没有数据会跟随)。

您可能还需要使用其他方法来维持查找表:如果你的大多数文件都预计将位于同一个物理文件系统,每个条目为每个条目的设备编号浪费的uint64。因此,地图层次结构可能是一个明智的做法:首先将设备编号映射到将inode编号映射到文件名的另一个映射。

希望这会有所帮助。

+0

哇我不期待这么多的反馈!这些是一些真正有用的观点,谢谢分享:) – steve