發送端 ( btrfs send)

發送端 ( btrfs send)

我很快就會解釋。我有兩個單獨的電影和音樂子卷,我將其備份在另一個帶有發送/接收功能的磁碟上。我想將兩個子卷合併為一個(帶有cp --reflink)並將其發送到備份驅動器,從現有快照克隆重新連結的檔案。

我嘗試創建new_volume,然後cp -rx --reflink "music" "movies"進入它,然後:

btrfs send \
    -c music-snapshot \
    -c movies-snapshot \
    new_volume-snapshot | btrfs receive /path/to/backup

但它抱怨無法確定父級new_volume(由 id 標識):

ERROR: parent determination failed for <id_number>

當然music-snapshot並且movies-snapshot存在備份。我也嘗試過快照到moviesnew_volume然後cp -rx --reflink "music"進入其中,然後快照並使用 發送它btrfs send -c music-snapshot -p movies-snapshot new_volume-snapshot,但似乎-c music-snapshot沒有任何效果,無論如何都會發送資料。

我想做的事情可能嗎?

編輯:我也嘗試這樣做:

btrfs sub snap -r movies movies-A
btrfs sub snap -r music music-A 
btrfs sub snap movies new_volume
btrfs sub snap -r new_volume new_volume-A

此時我有:

movies-A
music-A
new_volume-A (exact clone of movies-A)

然後:

cp -rx --reflink music/* new_volume/
btrfs sub snap -r new_volume new_volume-B

最後,當 和 都movies-A存在music-A於目的地時,我可以這樣做:

btrfs send \
    -p movies-A \
    new_volume-A | btrfs receive /backup/

這總是完美地工作(發送的數據接近零)。但...

btrfs send \
    -p new_volume-A \
    -c music-A \
    new_volume-B | btrfs receive /backup/

我不知道如何解釋:對於它的“某些文件”,music它可以工作,而對於其他文件,它會不斷發送數據,因為它忽略了來源。我認為這種方式絕對應該有效,但我無法可靠地重現「工作」和「不工作」行為。我傾向於認為某處有錯誤。有人可以試試這個嗎?

答案1

(免責聲明:lgaudino 的評論表明,提議的合併方法不再適用於 btrfs-progs >=v5.14.2由於改變了btrfs 屬性更改快照的唯讀狀態時的行為)

你想要的可以實現,但不能直接實現,因為btrfs send( 和btrfs receive) 是相當簡單的工具,無法透過提供次要父級將快照「合併」為一個快照從工具內部。

發送端 ( btrfs send)

btrfs send輸出檔案系統層級操作(目錄/檔案建立、元資料傳輸、檔案內容傳輸等),btrfs receive「接收者」可以使用這些操作在另一個 btrfs 檔案系統上建立等效快照。這些操作可以透過執行「空運行」來查看btrfs receive --dump

一個增加的send(例如btrfs send -p <parent> <snapshot>)的工作方式與所關注的完全相同,只是僅記錄通過修改(添加/刪除目錄/文件,更新元數據)btrfs send創建所需的文件系統操作。<snapshot><parent>

也沒有進行特殊的「親子」關係檢定。假設該-p參數用於指定“父級”,btrfs send則只需產生從該快照到另一個快照(充當“子級”)所需的命令。如果兩個快照完全不相關,這甚至可以工作。

在發送端,唯一的要求btrfs send是所有快照都是只讀。只讀子卷/快照通常是透過-r選項產生的btrfs subvolume snapshot -r <subvol> <snap>, 雖然btrfs property set <snap> ro true/false也可用於在建立後變更標誌。

btrfs send對接收方是什麼或不是什麼的考慮為零,這是不可避免的,因為雙方之間沒有雙向通信,例如rsyncbtrfs 發送/接收甚至可能不會同時運行,而是在不同時間運行讀取/寫入文件。

只能有一個父母

手冊頁和(舊)維基常見問題解答不幸的是相當混亂。作者是btrfs-clone 斷言btrfs發送和接收最多只能考慮增量傳輸的父級。父級可以透過-p一個或多個-c選項直接指定或間接指定:

btrfs-send來自 btrfs-tools 4.13 選擇子磁碟區 S 的父磁碟區和給定複製來源集 C_i像這樣:

  1. 如果-p指定了選項,則使用它
  2. 如果S沒有parent_uuidset,或是找不到這個uuid,則放棄
  3. 如果存在C_iwith C_i->uuid == S->parent_uuid(S 是子卷(快照)的子卷,我們稱之為「媽媽」),則使用它
  4. 如果沒有C_i與S相同parent_uuid,則放棄
  5. 從所有「媽媽」的孩子C_i中,選擇與「媽媽」最接近的一代(實際上,ctransid,與「一代」到底有什麼區別?)。

注意維基百科有點誤導,因為它表明 -cwithout與withp不同,儘管上面的演算法通常暗示了這一點。唯一相關的例外是發送沒有父卷的子卷。-c-p-p

總之,我建議始終明確指定一個父項 via-p並忽略-c,btrfs receive根本沒有「兩個父項」這樣的概念。

接收端 ( btrfs receive)

btrfs-receive手冊頁將該程式的用途描述為:

接收更改流並複製先前由 btrfs send 產生的一個或多個子卷

在完全傳輸的情況下,將建立一個新的子卷,在增量傳輸的情況下,將<parent>建立快照(接收方等效)的新快照。無論哪種情況,新建立的子卷/快照最初都是讀寫btrfs send直到成功應用來自的「更改流」之後,才製作子卷/快照只讀

顯然,沒有辦法實現兩個父級,因為在「更改流」中首先沒有寫時複製/引用連結副本。快照<parent>只是提供了應用「變更流」的基礎。

但是接收端如何確保自己的<parent>快照與<parent>傳送端的快照相同呢?它從不檢查/比較實際內容快照/子卷,而是使用通用唯一識別符與相關的元數據不變性父快照的假設。

btrfs subvolume show <subvolume>提供如下輸出:

/mnt/btrfs/subvolume
        Name:                   subvolume
        UUID:                   5e076a14-4e42-254d-ac8e-55bebea982d1
        Parent UUID:            -
        Received UUID:          -
        Creation time:          2018-01-01 12:34:56 +0000
        Subvolume ID:           79
        Generation:             2844
        Gen at creation:        2844
        Parent ID:              5
        Top level ID:           5
        Flags:                  -
        Snapshot(s):

每個子磁碟區/快照都有三個 UUID 插槽,其中兩個可能為空。由 建立的每個快照btrfs subvolume snapshot始終有一個Parent UUID明確標識其父級的快照,並且 由 創建的每個快照btrfs receive始終都有一個Received UUID條目。如果是全量傳輸,則不會有任何Parent UUID條目,如果是增量傳輸,則會同時有條目Received UUIDParent UUID條目。使用者無法手動變更 UUID 條目。

這些 ID 足以btrfs receive確定它是否擁有「接收方」<parent>快照應該與「發送方」快照完全相同<parent>,因為它Received UUID應該等於發送方父級的 UUID,從而確保它是從它創建的,並且涉及的所有快照的只讀狀態意味著存在不應該在此期間是否有任何變化。

(附註:雖然兩個父卷中只有一側包含接收到的 UUID 條目,但 btrfs 發送/接收似乎足夠智能,可以在“更改流”中提供此元數據,因此雙方/文件系統都可以充當“發送”或“接收”端,根據用戶需求)

如何合併兩個子磁碟區/快照 (< v5.14.2)

正如我們所看到的,使用 only 進行合併是不可能的,btrfs send/receive但手動進行合併相當容易,假設有人想要將movies和合併music到一個新的子卷下unified,並假設movies和 也music完全鏡像在接收端:

  1. 在發送端建立一個新的(空)子卷:btrfs subvolume create unified
  2. 將其更改為唯讀:btrfs property set unified ro true
  3. 將其發送到備份:btrfs send /path/to/unified | btrfs receive /receiving/side/
  4. 停用唯讀兩側,例如btrfs property set /path/to/unified ro false對於發送方
  5. 手動將和的--reflink內容musicmoviesunified 兩側 cp -a --reflink /path/to/music /path/to/movies /path/to/unified/
  6. 兩側unified快照恢復為唯讀btrfs property set /path/to/unified ro true
  7. 創建一個新的讀寫快照unified進行交互,unified將作為未來增量傳輸的父級雙方

music也可以從預先存在的子磁碟區或movies快照從步驟 4 開始,而不是建立新的/空的子磁碟區並發送它。從 btrfs 的角度來看,唯一重要的是兩側(未來)的父級透過一側Received UUID指向另一側進行連接,並且兩者都僅在將來傳輸時讀取。

>= v5.14.2 做什麼

最不壞的解決方案可能是遵循上一節中的方法,但另外Received UUID透過以下方式手動重置該值python-btrfs取消設定後btrfs-properties

沒有好的僅使用標準btrfs-progs工具的解決方案。最接近“a”解決方案的可能是利用事實btrfs receive在傳輸完成之前建立的快照是可讀可寫的,並嘗試--reflink在快照設定為唯讀之前進行複製btrfs-receive。換句話說,利用競爭條件是目前僅使用 btrfs-progs >=5.14.2 標準工具來執行此操作的唯一方法。

顯然,真正需要的是有人向 btrfs 專案提出功能請求,至少允許合併,就像 5.14.2 之前的那樣,對於那些知道自己在做什麼的人來說是可能的。

無論如何,競爭條件的利用可能看起來像這樣:由於我們仍然可以更改發送端快照的唯讀狀態,因此我們可以更改所述快照,例如

使用movies並重新連結複製music到其中:

btrfs property set -f movies ro false
cp -a --reflink /path/to/music /path/to/movies/
btrfs property set -f /path/to/movies ro true   
btrfs subvolume snapshot /path/to/movies /path/to/unified_prep

如果有必要,我們現在可以移動其中的目錄和檔案unified_prep(但保留音樂目錄),然後準備好後:

btrfs subvolume snapshot -r /path/to/unified_prep /path/to/unified
btrfs send -p /path/to/movies /path/to/unified | btrfs receive /path/to/backups/

btrfs send並且btrfs receive應該不是選擇附加音樂目錄,因為它同時存在於movies(發送端)和unified(客戶端)中。現在,我們設法cp -a --reflink /bkp/to/music /bkp/to/unified/在接收端(!)同時unified傳輸新快照並因此可讀可寫,然後發送端子unified卷及其接收端快照應該相同,並且我們已成功合併子卷。

相關內容