-
Notifications
You must be signed in to change notification settings - Fork 529
Expand file tree
/
Copy pathrekernel
More file actions
executable file
·501 lines (427 loc) · 19 KB
/
rekernel
File metadata and controls
executable file
·501 lines (427 loc) · 19 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
#!/bin/bash
#================================================================================================
#
# This file is licensed under the terms of the GNU General Public
# License version 2. This program is licensed "as is" without any
# warranty of any kind, whether express or implied.
#
# This file is a part of the remake fnnas
# https://github.com/ophub/fnnas
#
# Description: Automatically extract FnNAS image, generate kernel artifacts, and repackage output files.
# Copyright (C) 2025~ https://fnnas.com
# Copyright (C) 2026~ https://github.com/ophub/fnnas
#
# Command: sudo ./rekernel
#
#======================================== Functions list ========================================
#
# error_msg : Output error message and abort
# mount_try : Attempt to mount a partition with retries
# init_var : Initialize variables and parse command-line arguments
# download_debs : Download kernel deb packages
# download_dtbs : Download kernel DTB files
# find_fnnas : Locate the source FnNAS image file
# extract_fnnas : Attach loop device and mount image partitions
# unmount_fnnas : Unmount partitions and detach loop device
# chroot_fnnas : Enter chroot to generate kernel artifacts
# clean_tmp : Clean up temporary files
#
#================================ Set make environment variables ================================
# Related file storage paths
current_path="${PWD}"
fnnas_path="${current_path}/fnnas-arm64"
fnnas_renas_file="*.img"
fnnas_debs="${current_path}/fnnas-debs"
fnnas_dtbs="${current_path}/fnnas-dtbs"
script_path="${current_path}/make-fnnas/scripts"
make_path="${current_path}/fnnas"
out_path="${make_path}/out"
tmp_path="${make_path}/tmp"
tmp_outpath="${tmp_path}/tmp_out"
tmp_fnnas="${tmp_path}/tmp_fnnas"
tmp_image="${tmp_path}/tmp_image"
# Create required directories
[[ -d "${make_path}" ]] || mkdir -p "${make_path}"
[[ -d "${fnnas_debs}" ]] || mkdir -p "${fnnas_debs}"
[[ -d "${fnnas_dtbs}" ]] || mkdir -p "${fnnas_dtbs}"
# Set the kernel deb packages download repository
debs_repo="https://github.com/ophub/fnnas"
# Platform for additional deb installation (amlogic/allwinner/rockchip/none)
debs_install="none"
# Whether to download and use DTB files (true/yes)
dtbs_install="true"
# Kernel DTB version to download (e.g., 6.12.y)
dtbs_version="6.12.y"
# Server information
arch_info="$(uname -m)"
# QEMU static binary for ARM64 emulation
qemu_binary_arm64="qemu-aarch64-static"
# Kernel version output file (generated by chroot, read by host)
kernel_version_output="var/tmp/kernel_version_output"
# Set font colors for output
STEPS="[\033[95m STEPS \033[0m]"
INFO="[\033[94m INFO \033[0m]"
NOTE="[\033[93m NOTE \033[0m]"
WARNING="[\033[93m WARNING \033[0m]"
SUCCESS="[\033[92m SUCCESS \033[0m]"
ERROR="[\033[91m ERROR \033[0m]"
#================================================================================================
# Output error message and abort
error_msg() {
echo -e " [💔] ${ERROR} ${1}"
exit 1
}
# Attempt to mount a device with retries
mount_try() {
local m_dev="${1}"
local m_target="${2}"
[[ -n "${m_dev}" && -n "${m_target}" ]] || {
error_msg "Missing mount parameters: [ ${m_dev}, ${m_target} ]"
}
local t="1"
local max_try="10"
while [[ "${t}" -le "${max_try}" ]]; do
mount "${m_dev}" "${m_target}"
if [[ "${?}" -eq "0" ]]; then
break
else
sync && sleep 3
umount "${m_target}" 2>/dev/null
((t++))
fi
done
[[ "${t}" -gt "${max_try}" ]] && error_msg "Failed to mount after [ ${t} ] attempts."
}
init_var() {
echo -e "${STEPS} Initializing variables..."
# If followed by [ : ], the option requires a parameter value
local options="r:k:e:t:"
local long_options="debs-repository:,kernel:,debs-install:,dtbs-install:"
parsed_args=$(getopt -o "${options}" --long "${long_options}" -- "${@}")
[[ ${?} -ne 0 ]] && error_msg "Failed to parse command-line parameters."
eval set -- "${parsed_args}"
while true; do
case "${1}" in
-r | --debs-repository)
if [[ -n "${2}" ]]; then
debs_repo="${2}"
shift 2
else
error_msg "Invalid -r parameter [ ${2} ]!"
fi
;;
-k | --kernel)
if [[ -n "${2}" ]]; then
dtbs_version="${2}"
shift 2
else
error_msg "Invalid -k parameter [ ${2} ]!"
fi
;;
-e | --debs-install)
debs_install="${2}"
shift 2
;;
-t | --dtbs-install)
dtbs_install="${2}"
shift 2
;;
--)
shift
break
;;
*)
[[ -n "${1}" ]] && error_msg "Invalid option [ ${1} ]!"
break
;;
esac
done
# Normalize the kernel repository URL format
[[ -z "${debs_repo}" ]] && debs_repo="ophub/fnnas"
[[ "${debs_repo}" =~ ^https: ]] && debs_repo="$(echo ${debs_repo} | awk -F'/' '{print $4"/"$5}')"
kernel_api="https://github.com/${debs_repo}"
# Identify the kernel <VERSION> and <PATCHLEVEL>, such as [ 6.12 ]
kernel_verpatch="$(echo ${dtbs_version} | awk -F '.' '{print $1"."$2}')"
}
download_debs() {
echo -e "${STEPS} Downloading kernel deb packages..."
# Check if a valid platform is specified
if [[ ! "${debs_install}" =~ ^(amlogic|allwinner|rockchip)$ ]]; then
echo -e "${NOTE} No valid platform specified, skipping kernel deb download."
return
fi
cd "${current_path}"
# Get the latest version from github releases(e.g: 6.12.41)
latest_version="$(
curl -fsSL \
${kernel_api}/releases/expanded_assets/fnnas_debs |
grep -oP "${kernel_verpatch}\.[0-9]+(?=\.tar\.gz)" |
sort -urV | head -n 1
)"
[[ -n "${latest_version}" ]] || error_msg "Failed to query kernel deb version from [ ${kernel_api} ]."
echo -e "${INFO} Latest kernel deb version: [ ${latest_version} ]"
# Set the debs download path
tmp_debs_path="$(mktemp -d)"
debs_down_from="https://github.com/${debs_repo}/releases/download/fnnas_debs/${latest_version}.tar.gz"
# Download the kernel deb packages (retry up to 10 times on failure)
echo -e "${INFO} Downloading kernel deb packages from: [ ${debs_down_from} ]"
for t in {1..10}; do
curl -fsSL "${debs_down_from}" -o "${tmp_debs_path}/${latest_version}.tar.gz"
[[ "${?}" -eq 0 ]] && break || sleep 60
done
[[ "${?}" -eq 0 ]] || error_msg "Failed to download kernel deb packages from github.com."
# Extract the downloaded deb packages
echo -e "${INFO} Extracting kernel deb packages..."
tar -xzf "${tmp_debs_path}/${latest_version}.tar.gz" -C "${fnnas_debs}/"
[[ "${?}" -ne 0 ]] && error_msg "Failed to extract kernel deb packages."
echo -e "${INFO} Kernel deb packages extracted to [ ${fnnas_debs} ]"
# Cleanup temporary download directory
rm -rf "${tmp_debs_path}"
}
download_dtbs() {
echo -e "${STEPS} Downloading kernel DTB files..."
# Check if dtbs download is enabled
if [[ ! "${dtbs_install}" =~ ^(true|yes)$ ]]; then
echo -e "${NOTE} DTB download disabled, skipping."
return
fi
# Check if sufficient dtbs files already exist
dtbs_num="$(find "${fnnas_dtbs}" -type f -name "*.dtb" 2>/dev/null | wc -l)"
if [[ -n "${dtbs_num}" && "${dtbs_num}" -ge "456" ]]; then
echo -e "${NOTE} Sufficient DTB files already exist (${dtbs_num} files), skipping download."
return
fi
cd "${current_path}"
# Get the latest version from github releases(e.g: 6.12.41)
latest_version="$(
curl -fsSL \
https://github.com/ophub/kernel/releases/expanded_assets/kernel_stable |
grep -oP "${kernel_verpatch}\.[0-9]+(?=\.tar\.gz)" |
sort -urV | head -n 1
)"
[[ -n "${latest_version}" ]] || error_msg "Failed to query kernel DTB version from [ https://github.com/ophub/kernel ]."
echo -e "${INFO} Latest kernel DTB version: [ ${latest_version} ]"
# Set the dtbs download path
tmp_dtbs_path="$(mktemp -d)"
mkdir -p "${tmp_dtbs_path}"/{allwinner,amlogic,rockchip}
dtbs_down_from="https://github.com/ophub/kernel/releases/download/kernel_stable/${latest_version}.tar.gz"
# Download the kernel DTB files (retry up to 10 times on failure)
echo -e "${INFO} Downloading kernel DTB files from: [ ${dtbs_down_from} ]"
for t in {1..10}; do
curl -fsSL "${dtbs_down_from}" -o "${tmp_dtbs_path}/${latest_version}.tar.gz"
[[ "${?}" -eq 0 ]] && break || sleep 60
done
[[ "${?}" -eq 0 ]] || error_msg "Failed to download kernel DTB files from github.com."
# Extract the downloaded DTB files
echo -e "${INFO} Extracting kernel DTB files..."
tar -xzf "${tmp_dtbs_path}/${latest_version}.tar.gz" -C "${tmp_dtbs_path}/"
[[ "${?}" -ne 0 ]] && error_msg "Failed to extract kernel DTB files."
echo -e "${INFO} Kernel DTB files extracted to [ ${tmp_dtbs_path} ]"
# Extract individual platform dtbs
tar -xzf ${tmp_dtbs_path}/${latest_version}/dtb-amlogic-${latest_version}*.tar.gz -C "${tmp_dtbs_path}/amlogic/"
tar -xzf ${tmp_dtbs_path}/${latest_version}/dtb-allwinner-${latest_version}*.tar.gz -C "${tmp_dtbs_path}/allwinner/"
tar -xzf ${tmp_dtbs_path}/${latest_version}/dtb-rockchip-${latest_version}*.tar.gz -C "${tmp_dtbs_path}/rockchip/"
cp -af "${tmp_dtbs_path}"/{allwinner,amlogic,rockchip} "${fnnas_dtbs}/"
echo -e "${INFO} Kernel DTB files organized to [ ${fnnas_dtbs} ]"
# Cleanup temporary download directory
rm -rf "${tmp_dtbs_path}"
}
find_fnnas() {
cd "${current_path}"
echo -e "${STEPS} Searching for FnNAS image file..."
# Find whether the fnnas file exists
[[ -d "${fnnas_path}" ]] || error_msg "Directory not found: ${fnnas_path}"
# Find the first matching fnnas image file
fnnas_default_file="$(ls "${fnnas_path}"/${fnnas_renas_file} 2>/dev/null | head -n 1 | awk -F "/" '{print $NF}')"
if [[ -n "${fnnas_default_file}" ]]; then
echo -e "${INFO} FnNAS image file found: [ ${fnnas_default_file} ]"
else
error_msg "No [ ${fnnas_renas_file} ] file found in [ ${fnnas_path} ] directory."
fi
}
extract_fnnas() {
echo -e "${STEPS} Extracting FnNAS image..."
cd "${current_path}"
# Clean and recreate temp directories
rm -rf "${tmp_path}"
mkdir -p "${tmp_outpath}"/{boot,dtb,modules,headers} "${tmp_fnnas}" "${tmp_image}"
fnnas_image_file="${tmp_image}/fnnas.img"
cp -f "${fnnas_path}/${fnnas_default_file}" "${fnnas_image_file}"
# Setup loop device
loop_old="$(losetup -P -f --show "${fnnas_image_file}")"
[[ -n "${loop_old}" ]] || error_msg "losetup ${fnnas_image_file} failed."
# Export loop device variable for cleanup function
export GLOBAL_LOOP_DEV="${loop_old}"
echo -e "${INFO} Image attached to loop device: ${loop_old}"
# Mount rootfs partition (p2)
mount_try "${loop_old}p2" "${tmp_fnnas}"
# Mount bootfs partition (p1)
mount_try "${loop_old}p1" "${tmp_fnnas}/boot"
}
unmount_fnnas() {
echo -e "${STEPS} Unmounting chroot environment..."
cd "${current_path}"
sync && sleep 3
# Unmount virtual filesystems
umount -l "${tmp_fnnas}/dev/pts" 2>/dev/null
umount -R -l "${tmp_fnnas}/dev" 2>/dev/null
umount -l "${tmp_fnnas}/run" 2>/dev/null
umount -l "${tmp_fnnas}/sys" 2>/dev/null
umount -l "${tmp_fnnas}/proc" 2>/dev/null
# Unmount physical partitions
umount -l "${tmp_fnnas}/boot" 2>/dev/null
umount -l "${tmp_fnnas}" 2>/dev/null
# Only detach the specific loop device we created.
if [[ -n "${GLOBAL_LOOP_DEV}" ]]; then
losetup -d "${GLOBAL_LOOP_DEV}" 2>/dev/null
echo -e "${INFO} Loop device [ ${GLOBAL_LOOP_DEV} ] detached."
fi
# Cleanup temp directory
rm -f ${tmp_fnnas}/root/chroot_fnnas.sh
rm -f ${tmp_fnnas}/root/*.deb
rm -rf ${tmp_path}
echo -e "${INFO} Temporary files cleaned up."
sync && sleep 3
}
chroot_fnnas() {
cd "${current_path}"
echo -e "${STEPS} Preparing chroot environment..."
# Copy QEMU static binary for ARM64 emulation on x86_64 hosts
if [[ "${arch_info}" == "x86_64" ]]; then
if [[ -f "/usr/bin/${qemu_binary_arm64}" ]]; then
echo -e "${INFO} Copying [ ${qemu_binary_arm64} ] to chroot..."
cp -f "/usr/bin/${qemu_binary_arm64}" "${tmp_fnnas}/usr/bin/"
else
# Check if QEMU binary already exists in the image
if [[ ! -f "${tmp_fnnas}/usr/bin/${qemu_binary_arm64}" ]]; then
error_msg "QEMU binary [ ${qemu_binary_arm64} ] not found on host or target."
fi
fi
fi
# Copy DNS configuration for network access inside chroot
if [[ -f "/etc/resolv.conf" ]]; then
echo -e "${INFO} Copying DNS config..."
cp -f /etc/resolv.conf "${tmp_fnnas}/etc/resolv.conf" 2>/dev/null
fi
# Copy the chroot execution script
echo -e "${INFO} Copying [ chroot_fnnas.sh ] script..."
# Ensure script_path is correct relative to where you run this
if [[ ! -f "${script_path}/chroot_fnnas.sh" ]]; then
error_msg "Script not found: ${script_path}/chroot_fnnas.sh"
fi
cp -f "${script_path}/chroot_fnnas.sh" "${tmp_fnnas}/root/"
chmod +x "${tmp_fnnas}/root/chroot_fnnas.sh"
# Copy additional deb packages if a platform is specified
if [[ "${debs_install}" =~ ^(amlogic|allwinner|rockchip)$ ]]; then
echo -e "${INFO} Preparing to install additional deb packages for platform: [ ${debs_install} ]..."
# Copy deb packages into chroot
if [[ -d "${fnnas_debs}/${debs_install}" ]]; then
echo -e "${INFO} Copying .deb packages for platform [ ${debs_install} ]..."
# Using *.deb to avoid copying subdirectories or other files
cp -f "${fnnas_debs}/${debs_install}"/*.deb "${tmp_fnnas}/root/" 2>/dev/null ||
echo -e "${WARNING} No .deb files found in [ ${fnnas_debs}/${debs_install} ]"
else
error_msg "Deb packages directory not found for platform: [ ${fnnas_debs}/${debs_install} ]"
fi
else
echo -e "${INFO} No additional deb packages to install."
fi
# Mount virtual filesystems
echo -e "${STEPS} Mounting virtual filesystems..."
[[ -d "${tmp_fnnas}/dev/pts" ]] || mkdir -p "${tmp_fnnas}/dev/pts"
mount -t proc /proc "${tmp_fnnas}/proc"
mount -t sysfs /sys "${tmp_fnnas}/sys"
mount --bind /dev "${tmp_fnnas}/dev"
mount -t devpts devpts "${tmp_fnnas}/dev/pts"
mount --bind /run "${tmp_fnnas}/run"
chmod 0666 "${tmp_fnnas}/dev/null"
sync && sleep 3
# Register trap to ensure unmount happens even if script is interrupted
trap unmount_fnnas EXIT
echo -e "${INFO} Entering chroot to generate kernel artifacts..."
chroot "${tmp_fnnas}" /bin/bash -c "/root/chroot_fnnas.sh ${debs_install}"
# Verify chroot execution result
[[ ${?} -ne 0 ]] && error_msg "Chroot script execution failed."
# Cleanup copied files
rm -f "${tmp_fnnas}/root/chroot_fnnas.sh"
rm -f "${tmp_fnnas}/root/"*.deb
# Verify kernel version output file exists
[[ -f "${tmp_fnnas}/${kernel_version_output}" ]] || error_msg "Kernel version output file not found."
# Read kernel version and platform name generated inside chroot
source "${tmp_fnnas}/${kernel_version_output}"
# Trim whitespace just in case
kernel_version="$(echo "${kernel_version}" | tr -d '[:space:]')"
platform_name="$(echo "${platform_name}" | tr -d '[:space:]')"
# Validate retrieved values
[[ -z "${kernel_version}" || -z "${platform_name}" ]] && error_msg "Failed to retrieve valid kernel information."
echo -e "${INFO} Retrieved Kernel Version: [ ${kernel_version} ]"
echo -e "${INFO} Retrieved Platform Name: [ ${platform_name} ]"
cd "${current_path}"
# Extract generated kernel artifacts from chroot
echo -e "${INFO} Extracting kernel artifacts from chroot..."
# 1. Boot files
cp -rf "${tmp_fnnas}/boot/"*${kernel_version} "${tmp_outpath}/boot/"
# 2. DTB files: Use official FnNAS DTBs to overwrite, then add chroot-generated DTBs
[[ "${dtbs_install}" =~ ^(true|yes)$ && -d "${fnnas_dtbs}/${platform_name}" ]] && {
echo -e "${INFO} Adding DTB files for platform: [ ${platform_name} ]..."
cp -rf "${fnnas_dtbs}/${platform_name}/"* "${tmp_outpath}/dtb/"
}
echo -e "${INFO} Copying DTB files from chroot..."
cp -rf "${tmp_fnnas}/boot/dtb/${platform_name}/"* "${tmp_outpath}/dtb/"
# 3. Modules
echo -e "${INFO} Copying modules from chroot..."
cp -rf "${tmp_fnnas}/usr/lib/modules/${kernel_version}" "${tmp_outpath}/modules/"
# 4. Headers
echo -e "${INFO} Copying headers from chroot..."
cp -rf "${tmp_fnnas}/usr/src/linux-headers-${kernel_version}/"* "${tmp_outpath}/headers/"
# Package kernel artifacts
echo -e "${STEPS} Packaging kernel artifacts to [ ${out_path} ]..."
kernel_out="$(echo ${kernel_version} | awk -F'-' '{print $1}')-${platform_name}"
[[ -d "${out_path}/${kernel_out}" ]] && rm -rf "${out_path}/${kernel_out}"
mkdir -p "${out_path}/${kernel_out}"
# Create tar.gz files
echo -e "${INFO} Creating kernel archive files..."
(cd "${tmp_outpath}/boot" && tar -czf "${out_path}/${kernel_out}/boot-${platform_name}-${kernel_version}.tar.gz" *)
(cd "${tmp_outpath}/dtb" && tar -czf "${out_path}/${kernel_out}/dtb-${platform_name}-${kernel_version}.tar.gz" *)
(cd "${tmp_outpath}/modules" && tar -czf "${out_path}/${kernel_out}/modules-${platform_name}-${kernel_version}.tar.gz" *)
(cd "${tmp_outpath}/headers" && tar -czf "${out_path}/${kernel_out}/header-${platform_name}-${kernel_version}.tar.gz" *)
sync && sleep 3
# Add sha256sum integrity verification file
echo -e "${INFO} Generating sha256sums file..."
cd "${out_path}/${kernel_out}/"
sha256sum * >sha256sums
# Final packaging
cd "${current_path}"
rm -f ${out_path}/${kernel_out}.tar.gz
tar -czf "${out_path}/${kernel_out}.tar.gz" -C "${out_path}" "${kernel_out}"
rm -rf "${out_path}/${kernel_out}"
}
clean_tmp() {
echo -e "${STEPS} Cleaning up temporary files..."
# Remove trap to avoid double unmount
trap - EXIT
# Unmount fnnas base image
unmount_fnnas
}
#================================== Main Execution ==================================
# Show welcome message
echo -e "${STEPS} Welcome to the FnNAS kernel repackaging tool!"
# Check script permission
[[ "$(id -u)" == 0 ]] || error_msg "Please run this script as root: [ sudo ./${0} ]"
# Initialize variables
init_var "${@}"
# Download kernel deb packages
[[ "${debs_install}" =~ ^(amlogic|allwinner|rockchip)$ ]] && download_debs
# Download kernel DTB files
[[ "${dtbs_install}" =~ ^(true|yes)$ ]] && download_dtbs
# Locate FnNAS base image
find_fnnas
# Extract and mount FnNAS base image
extract_fnnas
# Generate kernel artifacts inside chroot
chroot_fnnas
# Clean up temporary files and unmount
clean_tmp
echo -e "${SUCCESS} Kernel files packaged successfully!"
echo -e "${INFO} Output directory: [ ${out_path} ]\n$(ls -hl ${out_path})"