[Linux] 使用 diff 指令產生補丁檔,用 patch 指令幫程式上補丁
patch 這個指令在 Linux 裡面很常看到,不過自己沒有實際用過…
今天正好有個機會,要幫 p7zip 上一個 security patch,
就來試試看吧~
參考資料:
7 Patch Command Examples to Apply Diff Patch Files in Linux
1. 取得 patch 檔案
因為這次的課題是修補 p7zip,patch 檔網路上已經有了,
因此直接抓下來就好了~內容節錄如下:
--- a/CPP/7zip/UI/Agent/Agent.cpp +++ b/CPP/7zip/UI/Agent/Agent.cpp @@ -424,6 +424,8 @@ STDMETHODIMP CAgentFolder::Extract(const CMyComPtr<IArchiveExtractCallback> extractCallback = extractCallbackSpec; UStringVector pathParts; CProxyFolder *currentProxyFolder = _proxyFolderItem; + HRESULT res; + while (currentProxyFolder->Parent) { pathParts.Insert(0, currentProxyFolder->Name); @@ -445,8 +447,11 @@ STDMETHODIMP CAgentFolder::Extract(const (UInt64)(Int64)-1); CUIntVector realIndices; GetRealIndices(indices, numItems, realIndices); - return _agentSpec->GetArchive()->Extract(&realIndices.Front(), + res = _agentSpec->GetArchive()->Extract(&realIndices.Front(), realIndices.Size(), testMode, extractCallback); + if (res == S_OK && !extractCallbackSpec->CreateSymLinks()) + res = E_FAIL; + return res; COM_TRY_END }
如果是要自己產生 patch 檔案的話,可以用 diff 指令~
通常是將舊的檔案放在一個目錄 old,
整個複製成另一個目錄 new 後,再去修改 new 目錄裡的檔案,
等修改都完成後,就可以下 diff 指令:
testuser@localhost ~ $ diff -Naur old new diff -Naur old/CPP/7zip/Crypto/Sha1.cpp new/CPP/7zip/Crypto/Sha1.cpp --- old/CPP/7zip/Crypto/Sha1.cpp 2010-10-20 12:56:08.000000000 +0800 +++ new/CPP/7zip/Crypto/Sha1.cpp 2016-03-18 15:49:30.000000000 +0800 @@ -145,13 +145,13 @@ curBufferPos = 0; CContextBase::UpdateBlock(_buffer, returnRes); if (returnRes) - for (int i = 0; i < kBlockSizeInWords; i++) + for (unsigned i = 0; i < kBlockSizeInWords; i++) { UInt32 d = _buffer[i]; - data[i * 4 + 0 - kBlockSize] = (Byte)(d); - data[i * 4 + 1 - kBlockSize] = (Byte)(d >> 8); - data[i * 4 + 2 - kBlockSize] = (Byte)(d >> 16); - data[i * 4 + 3 - kBlockSize] = (Byte)(d >> 24); + data[(int)i * 4 + 0 - (int)kBlockSize] = (Byte)(d); + data[(int)i * 4 + 1 - (int)kBlockSize] = (Byte)(d >> 8); + data[(int)i * 4 + 2 - (int)kBlockSize] = (Byte)(d >> 16); + data[(int)i * 4 + 3 - (int)kBlockSize] = (Byte)(d >> 24); } returnRes = rar350Mode; }
用來產生補丁檔的 diff 指令,通常會帶 -Naur 這幾個選項,分別代表:
-a 將所有檔案都視為文字檔
-u 輸出預設三行的相同文字行
-r 遞迴比較所有的子目錄
-N 將缺少的檔案視為空檔案
將 diff 的結果用 > 轉向寫到一個 xxx.patch 檔,之後就可以用 patch 指令來下補丁了~
2. 使用 patch 來上補丁
先把要修補的原始碼準備好,在本例中是要去下載 p7zip 7.20.1 的原始碼下來解開,
再執行 patch -i patch_file 或是 patch < patch_file:
testuser@localhost ~/p7zip_9.20.1 $ patch -i ../CVE-2015-1038.patch can't find file to patch at input line 1 Perhaps you should have used the -p or --strip option? The text leading up to this was: -------------------------- |--- a/CPP/7zip/UI/Agent/Agent.cpp |+++ b/CPP/7zip/UI/Agent/Agent.cpp -------------------------- File to patch:
在上例中,因為 patch 檔是用兩個目錄比較的方式產生的,
可以看到原始碼分別放在 a 和 b 兩個目錄下做比較,
但我們的原始碼並沒有 a 和 b 目錄,所以 patch 就搞不懂狀況了…
這時要多加一個 -p1 的參數,代表要跳過一層目錄結構,
如果 patch 檔產生時是在更深層的目錄結構的話,可能就會用到 -p2, -p3, …
加上 -p1 後,patch 就成功了:
testuser@localhost ~/p7zip_9.20.1 $ patch -p1 -i ../CVE-2015-1038.patch
patching file CPP/7zip/UI/Agent/Agent.cpp
patching file CPP/7zip/UI/Agent/ArchiveFolder.cpp
patching file CPP/7zip/UI/Client7z/Client7z.cpp
patching file CPP/7zip/UI/Common/ArchiveExtractCallback.cpp
patching file CPP/7zip/UI/Common/ArchiveExtractCallback.h
patching file CPP/7zip/UI/Common/Extract.cpp
patching file CPP/Windows/FileDir.cpp
patching file CPP/Windows/FileDir.h
如果在 patch 前不放心的話,可以加上 –dry-run 參數試跑,
不會真的去上補丁,而只是給你看會修改哪些檔案,以及是否會成功~
在上面的例子裡面,我的輸入都是用 patch -i patch_file 的方式,
不過網路上似乎更多人偏愛 patch < patch_file 的方式,
但是要注意的是如果有多個 patch 檔的話,用 < 轉向的方式是不行的喔~
這時可以用 cat 再 pipe 給 patch 的方式,例如:
cat *.patch | patch -p1