2016-07-12 43 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