2016-07-12 7 views
0

golangのarchive/tarパッケージを使用すると、ファイルに含まれるハードリンクの数にアクセスできないようです。しかし、ディレクトリやファイルをtar'ingするとハードリンクを保存できるということをどこかで読んでいることを覚えています。ハードリンクを保存するTarアーカイブ

私はこれを行うのに役立ついくつかのパッケージがありますか?

+0

私は実際にあなたの質問を何度か読んだことがあり、ソースファイルシステム上でハードリンクされたファイル*を見つける方法を尋ねたかったと思っています。 tarアーカイブにあるハードリンクされたファイルの数(私が答えた質問)。あなたは本当にあなたが尋ねたいと思っていたことを詳しく教えてください。 – kostix

+0

Hey kostix、 実際には、tarアーカイブをストリーミングしている間にハードリンクの数を数える方法を尋ねることを意味しました。しかし、あなたの入力をありがとう! – steve

+0

さて、私のコードをPoCの実装で更新しました。ハードリンク(ファイル/ディレクトリ名のモジュロ不完全な扱い)で私のテストディレクトリのために働きます。私は株式「タール」のように働くようにそれを調整する時間がありません、申し訳ありません。 – kostix

答えて

3

tarハードリンクは保持されます。

ここではサンプルの3つのハード・リンクされたファイルとディレクトリおよび単一のリンクを持つ一つのファイルです:

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の上の基準を定める書類の束を指し、 BSD tar(これはlibarchiveに依存していますが、paxなど)は、POSIX拡張属性をサポートしていません。例えば、GNU tarはPOSIX拡張属性をサポートしていません。ハードリンク上のビットを引用する :

LNKTYPE

このフラグは は以前にアーカイブ、任意のタイプの別のファイルにリンクされたファイルを表します。そのようなファイルは、Unixでは、同じファイルと同じ番号の を持つ各ファイルによって識別されます。リンク先の名前は、リンク名フィールドの末尾にnullを指定して に指定されています。

そこで、hadrlinkは、その名前によって(既にアーカイブ)ファイルに先行するいくつか を指す特別なタイプ(「1」)のenrtyあります。

遊び場の例を作成しましょう。

私たちは、アーカイブエンコードBASE64:

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

&hellip;及びthe codeを書きます。 アーカイブには、1つのディレクトリ、1つのファイル(タイプ '0')、別のファイル(タイプ '0')、2つのハードリンク(タイプ '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. あなたは、そのヘッダのLinknameフィールドを読むハードリンク(タイプarchive/tar.TypeReg)、

    1. が発生しました。
    2. "見た"ファイルの一覧を見て、その名前に一致するエントリのカウンタ を増やしてください。

2016年7月13日

の更新OPが実際に ソースファイルシステム上にハードリンクを管理する方法を知りたいと思ったとして、ここでの更新です。

  • 実際にファイルを指定したディレクトリエントリは、「iノード」と呼ばれる特殊な ファイルシステムのメタデータブロックを指す:

    チーフアイデアは、POSIXのセマンティクスを持つファイルシステム上にあることです。 inodeには、それを指し示すディレクトリエントリの番号 が含まれています。ハードリンクを作成する

    は、実際には次のとおりです。

    1. は、iノード元(ソース)の lnの用語で—「リンク先」をファイルに新しいディレクトリエントリのポインティングを作成します。
    2. そのinodeのリンクカウンターをインクリメントします。
  • したがって、任意のファイルを一意に2つの整数の番号により識別される: ファイルが配置されているファイルシステム をホスト物理的装置を識別する「デバイス番号」、及びファイルのデータを識別するinode番号。

    2つのファイルが同じ(デバイス、iノード)のペアを持つ場合、 は同じ内容を表します。あるいは、別の言葉で言えば、 はもう一方へのハードリンクです。

ので、ハードリンクを維持しながらtarアーカイブにファイルを追加すると、このように動作します:

  1. は、いくつかのルックアップテーブルにその(デバイス、iノード)のペアを保存し、ファイルを追加しました。

  2. 別のファイルを追加するときは、(デバイス、iノード)のペアとそのテーブル内の を調べてください。

    一致するエントリが見つかった場合、そのファイルのデータはすでにストリームされています。 とハードリンクを追加する必要があります。

    それ以外の場合は、手順(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システムで

    、タイプ os.FileInfoの値で呼び出さSys()方法は何か他のものではなく、POSIX'y syscall.Stat_tを返すことがあります。

    Windowsでは、「ディスク」または「ドライブ」の異なる でホストされている複数のファイルシステムがあるとします。私はGoがそれをどのように処理するのか分かりません。 この場合、何らかの理由で「デバイス番号」をエミュレートする必要があったかもしれません。一方

、それはハードリンクを処理する方法を示しています。

  • ヘッダ構造体の「リンク名」フィールドを設定します。
  • ヘッダーの「サイズ」フィールドを0にリセットします(データがないため)。あなたのファイルのほとんどが同じ物理ファイルシステム上に配置することが期待されている場合は、各エントリは、各エントリのデバイス番号をuint64を浪費:

はまた、ルックアップテーブルを維持するために別のアプローチを使用する場合があります。したがって、地図の階層構造は分かりやすいことです。まず、デバイス番号を別のマップにマップし、inode番号をファイル名にマッピングします。

これが役に立ちます。

+0

うわー私はこの多くのフィードバックを期待していませんでした!これらは本当に有用な点です。共有していただきありがとうございます:) – steve

関連する問題