mos

mos

OpenWrt x86 ext4 系統升級最佳實踐

OpenWrt_Logo

Caution

本文撰寫於 OpenWrt 23 release 版本發布之時,文中所述內容在未來可能會發生改變。請務必備份重要數據,仔細閱讀可能的錯誤信息並謹慎操作,作者不對本文內容做任何保證。

WHY? 為什麼會有這篇文章存在?#

因為 OpenWrt 社區擺爛 or 人手不夠沒有給 target x86 額外做一套合適的升級工具,因此 x86 設備系統升級工具跟其它 target 一樣有且僅有sysupgrade,只能簡單粗暴地刷寫磁碟映像。OpenWrt 文檔中提供了一個既不優雅也不簡單還帶有副作用的綏靖方案

Tip

還有個比較好的解決方案是利用 extroot,使用 SquashFS 映像將 OverlayFS 的 upper 與 lower 分離放到不同的磁碟上,升級版本時直接使用預編譯映像刷寫 lower 所在磁碟,隨後修改 extroot 配置重新將 OverylayFS 指向原有 upper 磁碟即可恢復原有狀態。但由於目前主線 OpenWrt 社群仍然沒有修復這個橫跨 5 年的 bug,extroot 的配置不生效故此方案不可行1

作為技術又菜懂的也不多但是喜歡探索的ProUser受害者,花點時間根據一份升級腳本 2 撰寫此篇升級指南,以方便各位 OpenWrt 新手更方便地升級追新。

最佳實踐方案#

Warning

本文所述方法僅適用於 OpenWrt 主線及擁有良好發行版本和包管理源的 ImmortalWrt 等分支變體,不適用於任何自編譯,“大神” 魔改的重發行版本。

Note

本文 target 是 x86_64,升級至 OpenWrt 23.05.0,請自行調整以符合自己所需。

OpenWrt x86 本質上是個特化版的 Linux 發行版,理論上當然也可以如一般 Linux 發行版一樣保留配置升級大版本的,只是如前所述沒有升級工具,因此需要進行一系列操作來手動實現。

獲取版本號(可選)#

oldver=$(awk -F\" 'NR==2 {print $2}' /usr/lib/os-release)
newver=23.05.0

升級所有軟體包(可選)#

opkg update
opkg upgrade netifd #單獨升級 netifd 是因為它會重啟網路介面中斷 SSH 連接
opkg list-upgradable | cut -d ' ' -f 1 | xargs opkg upgrade

替換內核#

Important

此處需要確認 boot 分區掛載位置,我的掛載位置是 /boot 且沒有二次掛載,文件樹看起來是下面這樣子,請根據自己實際內核位置修改路徑。

tree  /boot

/boot
├── boot
│   ├── grub
│   │   ├── boot.img
│   │   ├── core.img
│   │   ├── grub.cfg
│   │   └── grub.cfg.b
│   ├── vmlinuz
│   └── vmlinuz.23.05.0-rc3
└── efi
└── boot
└── bootx64.efi
  • 備份舊版本內核
    mv /boot/boot/vmlinuz /boot/boot/vmlinuz.$oldver

  • 下載新版內核
    wget -O /boot/boot/vmlinuz "https://downloads.openwrt.org/releases/23.05.0/targets/x86/64/openwrt-23.05.0-x86-64-generic-kernel.bin"

  • 校驗文件 hash 是否一致
    sha256sum /boot/boot/vmlinuz

處理軟體包#

  • 手動下載新發行版的 kernel.ipk
    注意這並不是真正的內核,而是 opkg 提供內核版本依賴的軟體包
    wget https://downloads.openwrt.org/releases/23.05.0/targets/x86/64/packages/kernel_5.15.134-1-47964456485559d992fe6f536131fc64_x86_64.ipk

  • 手動下載新發行版的 keyring.ipk(可選)
    wget https://downloads.openwrt.org/releases/packages-23.05/x86_64/base/openwrt-keyring_2022-03-25-62471e69-2_x86_64.ipk

Note

release 版本所使用的 key 與 snapshot/rc 版有所區別,故在其之間切換時需要安裝 keyring.ipk,反之則不需要。

安裝它們
opkg install kernel_*
opkg install openwrt-keyring_*

修改 opkg 源#

  • 替換軟體源發行版版本號
    sed -i.backup "s/$oldver/$newver/g" /etc/opkg/distfeeds.conf

  • 更新包列表
    opkg update

升級新發行版軟體包#

  • 使 /lib/functions.sh 失能
    (這一步忘記因為啥了,有知道的評論區補充下)
    mv /lib/functions.sh /lib/functions.sh.b
  • 升級基本文件
    opkg upgrade base-files
  • 升級並強制覆寫除 netifd 之外的所有包
    opkg list-upgradable | cut -d ' ' -f 1 | grep -v 'netifd' | xargs opkg upgrade --force-overwrite
  • 升級 netifd
    opkg upgrade netifd

reboot&done#

如果不出意外的話,重新啟動之後就是全新版本的 OpenWrt 了。
如果前面有報錯而你沒有處理的話,此次重啟可能就再也起不來了。


注釋#

Footnotes#

  1. 据信某些衍生版本已經合併修復代碼,但鑑於其可能缺失發行版和包管理源故此處不討論。

  2. 大概是 OpenWrt 21 時代網上找的,其中部分已經失效,腳本來源已不可考,如果有知道來源的可以評論留個鏈接:

    #!/bin/sh
        
    oldver=$(awk -F\" 'NR==2 {print $2}' /usr/lib/os-release)
    newver=$(echo $1| awk -F/ '{print $5}')
    if [ -z "$1" ]; then
        echo "Please input new kernel URL"
    else
        if [ $oldver == $newver ]; then
            echo "No need upgrade"
        else
        #Upgrade 
        opkg update
        opkg upgrade netifd
        opkg list-upgradable | cut -d ' ' -f 1 | xargs opkg upgrade
        #Kernel
        mount /dev/sda /boot
        mv /boot/boot/vmlinuz /boot/boot/vmlinuz.$oldver
        wget -O /boot/boot/vmlinuz https://downloads.openwrt.org/releases/$newver/targets/x86/64/generic-kernel.bin
        cd /tmp
        wget $1
        opkg install kernel_*
        #Edit /etc/opkg/distfeeds.conf
        sed -i.bak "s/$oldver/$newver/g" /etc/opkg/distfeeds.conf
        opkg update
        #Upgrade the base-files package
        chmod -x /lib/functions.sh
        opkg upgrade base-files
        #Reconfigure your DNS setting in /etc/resolv.conf
        ln -sf /tmp/resolv.conf.ppp /etc/resolv.conf 
        #Upgrade all other core packages
        opkg list-upgradable | cut -d ' ' -f 1 | xargs opkg upgrade
        fi
    fi
        
    find_pkg(){
      local name=$1
      awk '$1 ~ /^src/' /etc/opkg.conf /etc/opkg/*.conf | while read type repo url pad; do
        local f=/var/opkg-lists/$repo
    	{
          [ $type == "src/gz" ] && zcat $f || cat $f;
        } | {
          awk -vname="$name" '$1=="Package:" && $2==name {f=1;r=1} $0=="" {f=0} f {print} END{exit 1-r}'
        } | while read key val pad; do
          [ "$key" == "Version:" ] && ver=$val
          [ "$key" == "Filename:" ] && {
            echo found $ver @$repo
            echo $url/$val
          }
        done
      done
    }
    
載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。