Rockchip RK3588 - Rockchip Linux Recovery recovery源码分析

----------------------------------------------------------------------------------------------------------------------------

开发板 :NanoPC-T4开发板eMMC16GBLPDDR34GB
显示屏 :15.6英寸HDMI接口显示屏uboot2017.09linux4.19
----------------------------------------------------------------------------------------------------------------------------

在《Rockchip RK3588 - Rockchip Linux Recovery updateEngine源码分析》中我们对updateEngine源码进行了深入分析,并提到updateEngine 升级命令由两部分组成;

  • normal系统下的升级:升级recovery分区,并修改misc分区,重启系统;
  • recovery系统下的升级:系统重启之后,会根据misc分区存放的字段来判断将要引导的系统是normal系统还是recovery系统,这里会进入到recovery系统,进行剩余分区的升级;

其中normal系统下的升级是通过updateEngine可执行程序完成的;

// 1. 在normal系统,updateEngine升级recovery分区;升级完函数就返回了,并不会升级其它分区
main(argc, argv)MiscUpdate(image_url, partition, save_path)		//  对需要执行的升级命令打标记(这里标记了parameter、recovery升级命令)RK_ota_set_partition(0x040000)   // 进行parameter、recovery分区升级RK_ota_start(handle_upgrade_callback, handle_print_callback)// 往misc偏移16k位置写入recovery信息,这样系统重启后会进入recovery系统执行剩余分区的升级set_bootloader_message(&msg)// 2. 触发系统重启system(" echo b > /proc/sysrq-trigger ")

recovery系统下的升级,是通过recovery可执行程序实现,具体命令如下:

/usr/bin/recovery --update_package=/userdata/update.img

recovery二进制bin程序部会根据编译配置调用updateEngine或者rkupdate进行升级,本章的目标就是对recovery源码进行分析,实验基于《Rockchip RK3399 - 从零开始制作recovery系统》中移植的recovery系统

一、recovery目标分析

1.1 目标recovery

定位到<Rockchip Linux SDK>/external/recovery目录下的Makefile文件,找到目标recovery;

OBJ = recovery.o \default_recovery_ui.o \rktools.o \roots.o \bootloader.o \safe_iop.o \strlcpy.o \strlcat.o \rkupdate.o \sdboot.o \usbboot.o \mtdutils/mounts.o \mtdutils/mtdutils.o \mtdutils/rk29.o \minzip/DirUtil.o \update_engine/log.oifdef RecoveryNoUi  
OBJ += noui.o      # 不走这里
else
OBJ += ui.o\       # 走这里  minzip/Hash.o \minzip/SysUtil.o \minzip/Zip.o \minui/events.o \minui/graphics.o \minui/resources.o \minui/graphics_drm.o
endifCFLAGS += -I$(PROJECT_DIR) -I/usr/include -I/usr/include/libdrm/ -lc -DUSE_UPDATEENGINE=ONifdef RecoveryNoUi
CFLAGS += -lpthread -lbz2        # 不走这里
else
CFLAGS += -lz -lpng -ldrm -lpthread -lcurl -lcrypto -lbz2   # 走这里  
endif$(PROM): $(OBJ)$(CC) -o $(PROM) $(OBJ) $(CFLAGS)

可以看到目标recovery是由若干个.o文件通过aarch64-buildroot-linux-gnu-gcc编译器链接生成的可执行文件。

.o文件实际上是由.c文件通过aarch64-buildroot-linux-gnu-gcc编译器编译生成的。

# build in buildroot, it need change work directory
recovery_version:cd $(PROJECT_DIR)/../../../../../external/recovery && \COMMIT_HASH=$$(git rev-parse --verify --short HEAD) && \GIT_COMMIT_TIME=$$(git log -1 --format=%cd --date=format:%y%m%d) && \GIT_DIRTY=$$(git diff-index --quiet HEAD -- || echo "-dirty") && \commit_info=-g$${COMMIT_HASH}-$${GIT_COMMIT_TIME}$${GIT_DIRTY} && \cd $(PROJECT_DIR) && \echo "#define GIT_COMMIT_INFO $${commit_info}" > recovery_autogenerate.h%.o: %.cpp$(CC) -c $< -o $@ $(CFLAGS)%.o: %.c recovery_version$(CC) -c $< -o $@ $(CFLAGS)

其中:%.o 表示所有以.o结尾的文件作为目标文件,%.cpp 表示所有以.c结尾的文件作为依赖文件。

1.2 编译

在《Rockchip RK3588 - Rockchip Linux SDK Buildroot文件系统构建》中介绍过buildroot编译命令;

root@ubuntu:/work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot$ sudo make -j8

或者使用如下命令单独编译recovery软件包:

root@ubuntu:/work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot$ sudo make recovery-dirclean
root@ubuntu:/work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot$ sudo make recovery

其中recovery编译日志如下:

>>> recovery develop Syncing from source dir /work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/../external/recovery
rsync -au --chmod=u=rwX,go=rX  --exclude .svn --exclude .git --exclude .hg --exclude .bzr --exclude CVS /work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/../external/recovery/ /work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/output/rockchip_rk3588_recovery/build/recovery-develop
>>> recovery develop Configuring
>>> recovery develop Building
PATH="/work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/output/rockchip_rk3588_recovery/host/bin:/work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/output/rockchip_rk3588_recovery/host/sbin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin" /usr/bin/make  -C /work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/output/rockchip_rk3588_recovery/build/recovery-develop CC="/work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/output/rockchip_rk3588_recovery/host/bin/aarch64-buildroot-linux-gnu-gcc" CFLAGS="-D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64  -Os -g0 -D_FORTIFY_SOURCE=1 -I. -fPIC -lpthread -lcurl -lssl -lcrypto -lbz2 -lpng -ldrm -lz -lm -I/work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/output/rockchip_rk3588_recovery/host/aarch64-buildroot-linux-gnu/sysroot/usr/include/libdrm -DUSE_UPDATEENGINE=ON -DSUCCESSFUL_BOOT=ON"
make[1]: 进入目录“/work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/output/rockchip_rk3588_recovery/build/recovery-develop”
cd /work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/output/rockchip_rk3588_recovery/build/recovery-develop/../../../../../external/recovery && \
COMMIT_HASH=$(git rev-parse --verify --short HEAD) && \
GIT_COMMIT_TIME=$(git log -1 --format=%cd --date=format:%y%m%d) && \
GIT_DIRTY=$(git diff-index --quiet HEAD -- || echo "-dirty") && \
commit_info=-g${COMMIT_HASH}-${GIT_COMMIT_TIME}${GIT_DIRTY} && \
cd /work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/output/rockchip_rk3588_recovery/build/recovery-develop && \
echo "#define GIT_COMMIT_INFO ${commit_info}" > recovery_autogenerate.h
/work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/output/rockchip_rk3588_recovery/host/bin/aarch64-buildroot-linux-gnu-gcc -c recovery.c -o recovery.o -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64  -Os -g0 -D_FORTIFY_SOURCE=1 -I. -fPIC -lpthread -lcurl -lssl -lcrypto -lbz2 -lpng -ldrm -lz -lm -I/work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/output/rockchip_rk3588_recovery/host/aarch64-buildroot-linux-gnu/sysroot/usr/include/libdrm -DUSE_UPDATEENGINE=ON -DSUCCESSFUL_BOOT=ON
......
/work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/output/rockchip_rk3588_recovery/host/bin/aarch64-buildroot-linux-gnu-gcc -o updateEngine mtdutils/mounts.o mtdutils/mtdutils.o mtdutils/rk29.o update_engine/rkbootloader.o update_engine/download.o update_engine/flash_image.o update_engine/log.o update_engine/main.o update_engine/md5sum.o update_engine/rkimage.o update_engine/rktools.o update_engine/rkboot.o update_engine/crc.o update_engine/update.o update_engine/do_patch.o -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64  -Os -g0 -D_FORTIFY_SOURCE=1 -I. -fPIC -lpthread -lcurl -lssl -lcrypto -lbz2 -lpng -ldrm -lz -lm -I/work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/output/rockchip_rk3588_recovery/host/aarch64-buildroot-linux-gnu/sysroot/usr/include/libdrm -DUSE_UPDATEENGINE=ON -DSUCCESSFUL_BOOT=ON
make[1]: 离开目录“/work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/output/rockchip_rk3588_recovery/build/recovery-develop”
>>> recovery develop Installing to target
/usr/bin/install -D -m 755 /work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/output/rockchip_rk3588_recovery/build/recovery-develop/recovery /work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/output/rockchip_rk3588_recovery/target/usr/bin/
mkdir -p /work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/output/rockchip_rk3588_recovery/target/res/images
cp /work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/output/rockchip_rk3588_recovery/build/recovery-develop/res/images/* /work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/output/rockchip_rk3588_recovery/target/res/images/
/usr/bin/install -D -m 755 /work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/output/rockchip_rk3588_recovery/build/recovery-develop/updateEngine /work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/output/rockchip_rk3588_recovery/target/usr/bin/
/usr/bin/install -D -m 755 package/rockchip/recovery//S40recovery /work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/output/rockchip_rk3588_recovery/target/etc/init.d/S40recovery

二、recovery源码分析

recovery程序的入口为recovery.c文件。recovery.c代码比较长,如下所示:

点开查看详情
int main(int argc, char **argv)
{bool bSDBoot    = false;bool bUDiskBoot = false;const char *sdupdate_package = NULL;const char *usbupdate_package = NULL;int previous_runs = 0;const char *send_intent = NULL;const char *update_package = NULL;const char *encrypted_fs_mode = NULL;int wipe_data = 0;int wipe_all = 0;int pcba_test = 0;  // add for pcba testint toggle_secure_fs = 0;int arg;bool isrkdebug = false;int log_level = LOG_DEBUG;encrypted_fs_info encrypted_fs_data;struct timeval start_time, end_time;long long elapsed_time;gettimeofday(&start_time, NULL);// 获取命令行参数get_args(&argc, &argv);strcpy(systemFlag, "false");// 解析命令行参数while ((arg = getopt_long(argc, argv, "", OPTIONS, NULL)) != -1) {switch (arg) {case 'p':previous_runs = atoi(optarg);break;case 's':send_intent = optarg;break;case 'u':update_package = optarg;break;case 'w':wipe_data = 1;break;case 'a':wipe_all = 1;break;case 'e':encrypted_fs_mode = optarg;toggle_secure_fs = 1;break;case 't':ui_show_text(1);break;case 'f':pcba_test = 1;break;  // add for pcba testcase 'r':isrkdebug = true;break;case 'i':gr_set_rotate(atoi(optarg));break;case '?':LOGE("Invalid command argument\n");continue;}}time_t start = time(NULL);if ((access("/.rkdebug", F_OK) != 0) && (isrkdebug != true)) {// If these fail, there's not really anywhere to complain...if (freopen(TEMPORARY_LOG_FILE, "a", stdout) == NULL) {LOGW("freopen stdout error");}setbuf(stdout, NULL);if (freopen(TEMPORARY_LOG_FILE, "a", stderr) == NULL) {LOGE("freopen stderr error");}setbuf(stderr, NULL);}printf("\n");printf("*********************************************************\n");printf("            ROCKCHIP recovery system                     \n");printf("*********************************************************\n");printf("**** version : %s ****\n", recovery_version);LOGI("Starting recovery on %s\n", ctime(&start));while (access(coldboot_done, F_OK) != 0) {LOGI("coldboot not done, wait...\n");sleep(1);}#ifndef RecoveryNoUiLOGI("Recovery System have UI defined.\n");
#endifui_init();ui_set_background(BACKGROUND_ICON_INSTALLING);load_volume_table();setFlashPoint();bSDBoot = is_boot_from_SD();bUDiskBoot = is_boot_from_udisk();if (bSDBoot || bUDiskBoot) {char imageFile[64] = {0};if (bSDBoot) {if (is_sdcard_update()) {strlcpy(imageFile, EX_SDCARD_ROOT, sizeof(imageFile));strlcat(imageFile, "/sdupdate.img", sizeof(imageFile));if (access(imageFile, F_OK) == 0) {sdupdate_package = strdup(imageFile);bSDBootUpdate = true;ui_show_text(1);LOGI("sdupdate_package = %s\n", sdupdate_package);}}}if (bUDiskBoot) {if (is_udisk_update()) {strlcpy(imageFile, EX_UDISK_ROOT, sizeof(imageFile));strlcat(imageFile, "/sdupdate.img", sizeof(imageFile));if (access(imageFile, F_OK) == 0) {usbupdate_package = strdup(imageFile);bUdiskUpdate = true;ui_show_text(1);LOGI("usbupdate_package = %s\n", usbupdate_package);}}}}device_recovery_start();LOGI("Command:");for (arg = 0; arg < argc; arg++) {printf(" \"%s\"", argv[arg]);}printf("\n");if (update_package) {// For backwards compatibility on the cache partition only, if// we're given an old 'root' path "CACHE:foo", change it to// "/cache/foo".if (strncmp(update_package, "CACHE:", 6) == 0) {int len = strlen(update_package) + 10;char* modified_path = malloc(len);strlcpy(modified_path, "/cache/", len);strlcat(modified_path, update_package + 6, len);LOGI("(replacing path \"%s\" with \"%s\")\n",update_package, modified_path);update_package = modified_path;}}printf("\n");int status = INSTALL_SUCCESS;if (toggle_secure_fs) {if (strcmp(encrypted_fs_mode, "on") == 0) {encrypted_fs_data.mode = MODE_ENCRYPTED_FS_ENABLED;ui_print("Enabling Encrypted FS.\n");} else if (strcmp(encrypted_fs_mode, "off") == 0) {encrypted_fs_data.mode = MODE_ENCRYPTED_FS_DISABLED;ui_print("Disabling Encrypted FS.\n");} else {ui_print("Error: invalid Encrypted FS setting.\n");status = INSTALL_ERROR;}// Recovery strategy: if the data partition is damaged, disable encrypted file systems.// This preventsthe device recycling endlessly in recovery mode.if ((encrypted_fs_data.mode == MODE_ENCRYPTED_FS_ENABLED) &&(read_encrypted_fs_info(&encrypted_fs_data))) {ui_print("Encrypted FS change aborted, resetting to disabled state.\n");encrypted_fs_data.mode = MODE_ENCRYPTED_FS_DISABLED;}if (status != INSTALL_ERROR) {if (erase_volume("/userdata")) {ui_print("Data wipe failed.\n");status = INSTALL_ERROR;
#if 0} else if (erase_volume("/cache")) {ui_print("Cache wipe failed.\n");status = INSTALL_ERROR;
#endif} else if ((encrypted_fs_data.mode == MODE_ENCRYPTED_FS_ENABLED) &&(restore_encrypted_fs_info(&encrypted_fs_data))) {ui_print("Encrypted FS change aborted.\n");status = INSTALL_ERROR;} else {ui_print("Successfully updated Encrypted FS.\n");status = INSTALL_SUCCESS;}}} else if (update_package != NULL) {int i, ret = 0;const char* binary = "/usr/bin/rkupdate";rockchip_partition_check();for (i = 0; i < 5; i++) {if (!ensure_path_mounted(update_package)) {LOGI("mounted %s Success.\n", update_package);break;}LOGW("mounted %s Failed. retry %d\n", update_package, i + 1);sleep(1);}if (i != 5) {LOGI(">>>rkflash will update from %s\n", update_package);
#ifdef USE_RKUPDATEstatus = do_rk_update(binary, update_package);
#endif
#ifdef USE_UPDATEENGINEconst char* updateEnginebin = "/usr/bin/updateEngine";status = do_rk_updateEngine(updateEnginebin, update_package);
#endifif (status == INSTALL_SUCCESS) {strcpy(systemFlag, update_package);/* update success, delete update.img. */if (access(update_package, F_OK) == 0)remove(update_package);ui_print("update.img images success!\n");} else {ui_print("update.img images failed!\n");}} else {LOGE("mounted %s Failed.\n", update_package);ui_print("mounted %s Failed.\n", update_package);}if (status != INSTALL_SUCCESS) ui_print("Installation aborted.\n");ui_print("update.img Installation done.\n");//ui_show_text(0);} else if (sdupdate_package != NULL) {rockchip_partition_check();// update image from sdcard
#ifdef USE_RKUPDATEconst char* binary = "/usr/bin/rkupdate";LOGI(">>>sdboot update will update from %s\n", sdupdate_package);status = do_rk_update(binary, sdupdate_package);
#endif#ifdef USE_UPDATEENGINE
#undef FACTORY_FIRMWARE_IMAGE
#undef CMD4RECOVERY_FILENAME
#define FACTORY_FIRMWARE_IMAGE "/mnt/sdcard/out_image.img"
#define CMD4RECOVERY_FILENAME "/mnt/sdcard/cmd4recovery"if ((access(FACTORY_FIRMWARE_IMAGE, F_OK)) && access(CMD4RECOVERY_FILENAME, F_OK)) {int tmp_fd = creat(CMD4RECOVERY_FILENAME, 0777);if (tmp_fd < 0) {LOGE("creat %s error.\n", CMD4RECOVERY_FILENAME);status = INSTALL_ERROR;} else {close(tmp_fd);const char* updateEnginebin = "/usr/bin/updateEngine";status = do_rk_updateEngine(updateEnginebin, sdupdate_package);}}if (isMtdDevice()) {LOGI("start flash write to /dev/mtd0.\n");size_t total_size;size_t erase_size;mtd_scan_partitions();const MtdPartition *part = mtd_find_partition_by_name("rk-nand");if ( part == NULL ) {part = mtd_find_partition_by_name("spi-nand0");}if (part == NULL || mtd_partition_info(part, &total_size, &erase_size, NULL)) {if ((!access(FACTORY_FIRMWARE_IMAGE, F_OK)) && mtd_find_partition_by_name("sfc_nor") != NULL) {LOGI("Info: start flash out_image.img to spi nor.\n");system("flashcp -v " FACTORY_FIRMWARE_IMAGE " /dev/mtd0");} elseLOGE("Error: Can't find rk-nand or spi-nand0.\n");} else {system("flash_erase /dev/mtd0 0x0 0");system("sh "CMD4RECOVERY_FILENAME);}} else {LOGI("Start to dd data to emmc partition.\n");system("sh "CMD4RECOVERY_FILENAME);LOGI("sdcard upgrade done\n");}#endifif (status == INSTALL_SUCCESS) {LOGI("update.img Installation success.\n");ui_print("update.img Installation success.\n");//ui_show_text(0);}} else if (usbupdate_package != NULL) {rockchip_partition_check();// update image from udisk
#ifdef USE_RKUPDATEconst char* binary = "/usr/bin/rkupdate";LOGI(">>>sdboot update will update from %s\n", usbupdate_package);status = do_rk_update(binary, usbupdate_package);
#endif#ifdef USE_UPDATEENGINE
#undef FACTORY_FIRMWARE_IMAGE
#undef CMD4RECOVERY_FILENAME
#define FACTORY_FIRMWARE_IMAGE "/mnt/usb_storage/out_image.img"
#define CMD4RECOVERY_FILENAME "/mnt/usb_storage/cmd4recovery"if ((access(FACTORY_FIRMWARE_IMAGE, F_OK)) && access(CMD4RECOVERY_FILENAME, F_OK)) {int tmp_fd = creat(CMD4RECOVERY_FILENAME, 0777);if (tmp_fd < 0) {LOGE("creat %s error.\n", CMD4RECOVERY_FILENAME);status = INSTALL_ERROR;} else {close(tmp_fd);const char* updateEnginebin = "/usr/bin/updateEngine";status = do_rk_updateEngine(updateEnginebin, usbupdate_package);}}if (isMtdDevice()) {LOGI("start flash write to /dev/mtd0.\n");size_t total_size;size_t erase_size;mtd_scan_partitions();const MtdPartition *part = mtd_find_partition_by_name("rk-nand");if ( part == NULL ) {part = mtd_find_partition_by_name("spi-nand0");}if (part == NULL || mtd_partition_info(part, &total_size, &erase_size, NULL)) {if ((!access(FACTORY_FIRMWARE_IMAGE, F_OK)) && mtd_find_partition_by_name("sfc_nor") != NULL) {LOGI("Info: start flash out_image.img to spi nor.\n");system("flashcp -v " FACTORY_FIRMWARE_IMAGE " /dev/mtd0");} elseLOGE("Error: Can't find rk-nand or spi-nand0.\n");} else {system("flash_erase /dev/mtd0 0x0 0");system("sh "CMD4RECOVERY_FILENAME);}} else {LOGI("Start to dd data to emmc partition.\n");system("sh "CMD4RECOVERY_FILENAME);LOGI("usb upgrade done\n");}
#endifif (status == INSTALL_SUCCESS) {LOGI("update.img Installation success.\n");ui_print("update.img Installation success.\n");//ui_show_text(0);}} else if (wipe_data) {if (device_wipe_data()) status = INSTALL_ERROR;if (erase_volume("/userdata")) status = INSTALL_ERROR;if (status != INSTALL_SUCCESS) ui_print("Data wipe failed.\n");} else if (wipe_all) {if (device_wipe_data()) status = INSTALL_ERROR;if (erase_volume("/userdata")) status = INSTALL_ERROR;if (status != INSTALL_SUCCESS) {ui_print("Data wipe failed.\n");LOGE("userdata wipe failed.\n");} else {ui_print("Data wipe done.\n");LOGI("userdata wipe done.\n");}//ui_show_text(0);} else if (pcba_test) {//pcba test todo...printf("------------------ pcba test start -------------\n");exit(EXIT_SUCCESS); //exit recovery bin directly, not start pcba here, in rkLanuch.shreturn 0;} else {if (argc == 1) { // No command specifiedif (!bSDBootUpdate && !bUdiskUpdate && ui_text_visible())prompt_and_wait();finish_recovery(NULL);reboot(RB_AUTOBOOT);return 0;}status = INSTALL_ERROR;  // No command specified}if (status != INSTALL_SUCCESS) ui_set_background(BACKGROUND_ICON_ERROR);if (status != INSTALL_SUCCESS) {LOGE("\n Install fail! \n");if (!bSDBootUpdate && !bUdiskUpdate && ui_text_visible())prompt_and_wait();}if (sdupdate_package != NULL && bSDBootUpdate) {if (status == INSTALL_SUCCESS) {char *SDDdevice =strdup(get_mounted_device_from_path(EX_SDCARD_ROOT));ensure_ex_path_unmounted(EX_SDCARD_ROOT);/* Updating is finished here, we must print this message* in console, it shows user a specific message that* updating is completely, remove SD CARD and reboot */fflush(stdout);freopen("/dev/console", "w", stdout);LOGI("\nPlease remove SD CARD!!!, wait for reboot.\n");ui_print("Please remove SD CARD!!!, wait for reboot.");while (access(SDDdevice, F_OK) == 0) { sleep(1); }free(SDDdevice);}} else if (usbupdate_package && bUdiskUpdate) {if (status == INSTALL_SUCCESS) {char *udiskDev = strdup(get_mounted_device_from_path(EX_SDCARD_ROOT));ensure_path_unmounted(EX_UDISK_ROOT);/* Updating is finished here, we must print this message* in console, it shows user a specific message that* updating is completely, remove U-disk and reboot */fflush(stdout);freopen("/dev/console", "w", stdout);LOGI("\nPlease remove U DISK!!!, wait for reboot.\n");ui_print("Please remove U DISK!!!, wait for reboot.");while (access(udiskDev, F_OK) == 0) { sleep(1); }free(udiskDev);}}// Otherwise, get ready to boot the main system...finish_recovery(send_intent);gettimeofday(&end_time, NULL);elapsed_time = (end_time.tv_sec - start_time.tv_sec) * 1000LL +(end_time.tv_usec - start_time.tv_usec) / 1000LL;LOGI("recovery usage time:%lld ms\n", elapsed_time);ui_print("Rebooting...\n");LOGI("Reboot...\n");ui_show_text(0);fflush(stdout);sync();reboot(RB_AUTOBOOT);return EXIT_SUCCESS;
}
2.1 获取命令行参数

首先调用get_args函数,该函数的目的是从多个来源获取命令行参数,并将这些参数存储在 argv 数组中,同时更新misc分区;

// command line args come from, in decreasing precedence:
//   - the actual command line
//   - the bootloader control block (one per line, after "recovery")
//   - the contents of COMMAND_FILE (one per line)
static void
get_args(int *argc, char ***argv)
{// 1. misc偏移16k位置存放的是recovery信息,因此recovery系统重启后会加载recovery信息struct bootloader_message boot;memset(&boot, 0, sizeof(boot));get_bootloader_message(&boot);  // this may fail, leaving a zeroed structure// 输出命令:Boot command: boot-recoveryif (boot.command[0] != 0 && boot.command[0] != 255) {LOGI("Boot command: %.*s\n", (int)sizeof(boot.command), boot.command);}// 输出状态if (boot.status[0] != 0 && boot.status[0] != 255) {LOGI("Boot status: %.*s\n", (int)sizeof(boot.status), boot.status);}// --- if arguments weren't supplied, look in the bootloader control block// 2. 如果argc小于或等于1(即没有提供命令行参数),函数会尝试从boot.recovery中提取参数if (*argc <= 1) {// 首先确保boot.recovery字符串以空字符结尾,然后使用strtok将boot.recovery字符串分割成以换行符\n为界的多个部分// \n对应的ASCII值是0x0a// boot.recovery="recovery\n--update_package=/userdata/update.img\n"boot.recovery[sizeof(boot.recovery) - 1] = '\0';  // Ensure termination// arg=”recovery”// boot.recovery="recovery\0--update_package=/userdata/update.img\n"const char *arg = strtok(boot.recovery, "\n");// 满足条件if (arg != NULL && !strcmp(arg, "recovery")) {      *argv = (char **) malloc(sizeof(char *) * MAX_ARGS);// 保存参数arg(*argv)[0] = strdup(arg);for (*argc = 1; *argc < MAX_ARGS; ++*argc) {// 获取下一个子字符串// arg=”--update_package=/userdata/update.img”// boot.recovery="recovery\0--update_package=/userdata/update.img\0"if ((arg = strtok(NULL, "\n")) == NULL) break;(*argv)[*argc] = strdup(arg);}LOGI("Got arguments from boot message\n");} else if (boot.recovery[0] != 0 && boot.recovery[0] != 255) {LOGE("Bad boot message\n\"%.20s\"\n", boot.recovery);}}// --- if that doesn't work, try the command fileif (*argc <= 1) {......}// --> write the arguments we have back into the bootloader control block// always boot into recovery after this (until finish_recovery() is called)// 3. 设置参数// 设置boot.command="boot-recovery"// 设置boot.recovery="recovery"strlcpy(boot.command, "boot-recovery", sizeof(boot.command));strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery));int i;// 合并参数,最后boot.recovery="recovery\n--update_package=/userdata/update.img\n"for (i = 1; i < *argc; ++i) {strlcat(boot.recovery, (*argv)[i], sizeof(boot.recovery));strlcat(boot.recovery, "\n", sizeof(boot.recovery));}//  往misc偏移16k位置写入recovery信息set_bootloader_message(&boot);
}

有关struct bootloader_message参考《Rockchip RK3588 - Rockchip Linux Recovery updateEngine源码分析》第4.4节。比如misc分区偏移16k的数据如下:

10000000: 62 6f 6f 74 2d 72 65 63 6f 76 65 72 79 00 00 00    boot-recovery...
10000010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
10000020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
10000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
10000040: 72 65 63 6f 76 65 72 79 0a 2d 2d 75 70 64 61 74    recovery.--updat
10000050: 65 5f 70 61 63 6b 61 67 65 3d 2f 75 73 65 72 64    e_package=/userd
10000060: 61 74 61 2f 75 70 64 61 74 65 2e 69 6d 67 0a 00    ata/update.img..
10000070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
10000080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
10000090: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
......
10000340: 00 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
......
2.2 解析命令行参数

由于recovery是通过传参来实现固件升级的,因此不难猜出recovery是基于命令行参数的程序。

接着使用了getopt_long函数来解析命令行参数,并根据参数的不同执行相应的操作。

static const struct option OPTIONS[] = {{ "send_intent", required_argument, NULL, 's' },{ "update_package", required_argument, NULL, 'u' },{ "wipe_data", no_argument, NULL, 'w' },{ "wipe_all", no_argument, NULL, 'a' },{ "set_encrypted_filesystems", required_argument, NULL, 'e' },{ "show_text", no_argument, NULL, 't' },{ "factory_pcba_test", no_argument, NULL, 'f' },{ "rkdebug", no_argument, NULL, 'r'},{ "ui_rotation", required_argument, NULL, 'i'},{ NULL, 0, NULL, 0 },
};while ((arg = getopt_long(argc, argv, "", OPTIONS, NULL)) != -1) {switch (arg) {case 'p':previous_runs = atoi(optarg);break;case 's':send_intent = optarg;break;case 'u':  // --update_package=/userdata/update.imgupdate_package = optarg;break;case 'w':wipe_data = 1;break;case 'a':wipe_all = 1;break;case 'e':encrypted_fs_mode = optarg;toggle_secure_fs = 1;break;case 't':ui_show_text(1);break;case 'f':pcba_test = 1;break;  // add for pcba testcase 'r':isrkdebug = true;break;case 'i':gr_set_rotate(atoi(optarg));break;case '?':LOGE("Invalid command argument\n");continue;}
}

getopt_long 这是一个用于获取命令行选项的函数。它可以处理长选项,并与短选项配合使用。

  • argcargv是标准的命令行参数;
  • OPTIONS是定义选项的字符串或结构体(通常包含短选项及其参数信息);
  • NULL 是指不使用长选项的返回值。

由于(*argv)[0]="recovery"(*argv)[1]="--update_package=/userdata/update.img",因此会进入u分支,设置update_package="/userdata/update.img"

2.3 输出版本信息

接着main函数输出当前recovery版本信息,比如:

*********************************************************ROCKCHIP recovery system
*********************************************************
**** version : V1.0.1-g28f720bc5-240524-dirty ****
LOG_INFO: Starting recovery on Sun Oct  6 09:04:09 2024

如果没有配置RecoveryNoUi,输出:

LOG_INFO: Recovery System have UI defined.
2.4 ui相关
2.4.1 ui_init

ui_init函数定义在ui.c文件;

void ui_init(void)
{int ret = 0;ret = gr_init();if (ret)return;ev_init((ev_callback)input_callback, NULL);text_col = text_row = 0;text_rows = gr_fb_height() / CHAR_HEIGHT;if (text_rows > MAX_ROWS) text_rows = MAX_ROWS;text_top = 1;text_cols = gr_fb_width() / CHAR_WIDTH;if (text_cols > MAX_COLS - 1) text_cols = MAX_COLS - 1;int i;for (i = 0; BITMAPS[i].name != NULL; ++i) {int result = res_create_display_surface(BITMAPS[i].name, BITMAPS[i].surface);if (result < 0) {if (result == -2) {LOGI("Bitmap %s missing header\n", BITMAPS[i].name);} else {LOGE("Missing bitmap %s\n(Code %d)\n", BITMAPS[i].name, result);}*BITMAPS[i].surface = NULL;}}pthread_t t;pthread_create(&t, NULL, progress_thread, NULL);pthread_create(&t, NULL, input_thread, NULL);
}
2.4.2 ui_set_background

ui_set_background函数定义在ui.c文件;

void ui_set_background(int icon)
{if (!gr_draw)return;pthread_mutex_lock(&gUpdateMutex);gCurrentIcon = gBackgroundIcon[icon];update_screen_locked();pthread_mutex_unlock(&gUpdateMutex);
}
2.5 加载/etc/fstab

load_volume_table函数用于输出/etc/fstab中定义文件系统的挂载点信息:

rk3399 login: recovery filesystem table
=========================0 (null) /tmp ramdisk (null) (null) (null)1 /dev/root / ext2 rw,noauto 0 12 proc /proc proc defaults 0 03 devpts /dev/pts devpts defaults,gid=5,mode=620,ptmxmode=0666 0 04 tmpfs /dev/shm tmpfs mode=1777 0 05 tmpfs /tmp tmpfs mode=1777 0 06 tmpfs /run tmpfs mode=0755,nosuid,nodev 0 07 sysfs /sys sysfs defaults 0 08 configfs /sys/kernel/config configfs defaults 0 09 debugfs /sys/kernel/debug debugfs defaults 0 0

load_volume_table函数定义在roots.c

void load_volume_table()
{int alloc = 2;device_volumes = malloc(alloc * sizeof(Volume));// Insert an entry for /tmp, which is the ramdisk and is always mounted.device_volumes[0].mount_point = "/tmp";device_volumes[0].fs_type = "ramdisk";device_volumes[0].device = NULL;device_volumes[0].device2 = NULL;device_volumes[0].option = NULL;device_volumes[0].dump = NULL;device_volumes[0].pass = NULL;num_volumes = 1;FILE* fstab = fopen("/etc/fstab", "r");if (fstab == NULL) {LOGE("failed to open /etc/fstab (%d)\n", errno);return;}char buffer[1024];char file_system[1024];char mount_point[1024];char fs_type[1024];char option[1024];char dump[1024];char pass[1024];char device[1024];int i;while (fgets(buffer, sizeof(buffer) - 1, fstab)) {i = sscanf(buffer, "%s %s %s %s %s %s", file_system,mount_point, fs_type, option, dump, pass);if (file_system[0] == '#') continue;//printf("load_volume_table file_system:%s, mount_point:%s, fs_type:%s, option:%s, dump:%s, pass:%s\n", file_system, mount_point, fs_type, option, dump, pass);/* HACK: Convert *LABEL to "by-name" symlink */if (strstr(file_system, "LABEL="))snprintf(device, sizeof(device), "/dev/block/by-name/%s",strstr(file_system, "LABEL=") + strlen("LABEL="));elsestrcpy(device, file_system);if (i == 6) {while (num_volumes >= alloc) {alloc *= 2;device_volumes = realloc(device_volumes, alloc * sizeof(Volume));}device_volumes[num_volumes].mount_point = strdup(mount_point);device_volumes[num_volumes].fs_type = strdup(fs_type);device_volumes[num_volumes].option = strdup(option);device_volumes[num_volumes].dump = strdup(dump);device_volumes[num_volumes].pass = strdup(pass);device_volumes[num_volumes].device = strdup(device);;device_volumes[num_volumes].device2 = NULL;++num_volumes;} else {LOGE("skipping malformed recovery.fstab line: %s\n", buffer);}}fclose(fstab);printf("recovery filesystem table\n");printf("=========================\n");for (i = 0; i < num_volumes; ++i) {Volume* v = &device_volumes[i];printf("  %d %s %s %s %s %s %s\n", i, v->device, v->mount_point, v->fs_type, v->option, v->dump, v->pass);}printf("\n");
}
2.6 setFlashPoint

setFlashPoint函数定义在rktools.c

void setFlashPoint()
{// 判断是不是MTD设备(Nor/Nand Flash设备),对于SD、eMMC不属于MTD设备if (!isMtdDevice())   // 进入// 等待设备节点/dev/block/by-name/misc可用wait_for_device(MISC_PARTITION_NAME_BLOCK);// 通过读取/sys/bus/mmc/devices/目录中的条目来查找SD或eMMC设备,并更新result_pointinit_sd_emmc_point();// 设置环境变量emmc_point_name="/dev/mmcblk2"setenv(EMMC_POINT_NAME, result_point[MMC], 1);    // 判断SD类型设备是否存在,设备节点为/dev/mmcblk0if (access(result_point[SD], F_OK) == 0)// 设置环境变量sd_point_name_2setenv(SD_POINT_NAME_2, result_point[SD], 1);// /dev/mmcblk0p1char name_t[22];if (strlen(result_point[SD]) > 0) {        strcpy(name_t, result_point[SD]);strcat(name_t, "p1");}if (access(name_t, F_OK) == 0)// 设置环境变量sd_point_namesetenv(SD_POINT_NAME, name_t, 1);// emmc_point_nameLOGI("emmc_point is %s\n", getenv(EMMC_POINT_NAME));// sd_point_nameLOGI("sd_point is %s\n", getenv(SD_POINT_NAME));// sd_point_name_2LOGI("sd_point_2 is %s\n", getenv(SD_POINT_NAME_2));
}

函数执行完,输出信息大致如下;

LOG_INFO: devices is not MTD.
LOG_INFO: emmc_point is /dev/mmcblk2
LOG_INFO: sd_point is (null)
LOG_INFO: sd_point_2 is (null)
2.6.1 wait_for_device

wait_for_device用于 确保某个文件(例如设备节点,挂载点或配置文件)可用;

static void wait_for_device(const char* fn)
{int tries = 0;int ret;struct stat buf;do {++tries;// 检查文件状态ret = stat(fn, &buf);// 回非零值,表示路径不可用	if (ret) {LOGI("stat %s try %d: %s\n", fn, tries, strerror(errno));sleep(1);}} while (ret && tries < 10);if (ret) {LOGI("failed to stat %s\n", fn);}
}
2.6.2 init_sd_emmc_point

init_sd_emmc_point通过读取/sys/bus/mmc/devices/目录中的条目来查找SDeMMC设备,并更新result_point

static const char *point_items[] = {"/dev/mmcblk0",     // MMC类型设备对应的设备节点"/dev/mmcblk1",     // SD类型设备对应的设备节点"/dev/mmcblk2",     // SDIO类型设备对应的设备节点"/dev/mmcblk3",     // SDcombo类型设备对应的设备节点
};enum type {MMC,            // MMC类型SD,             // SD类型SDIO,           // SDIO类型SDcombo,        // .....
};static const char *typeName[] = {"MMC","SD","SDIO","SDcombo",
};/***  设置flash 节点*/
static char result_point[4][20] = {'\0'}; //0-->MMC, 1-->SD, 2-->SDIO, 3-->SDcombo// 读取dir目录下的filename文件
int readFile(DIR* dir, char* filename)
{char name[30] = {'\0'};int i;// 构建文件路径:mmc0:0001/type// 构建文件路径:mmc1:0001/type// 构建文件路径:mmc2:0001/typestrcpy(name, filename);strcat(name, "/type");// 打开文件int fd = openat(dirfd(dir), name, O_RDONLY);if (fd == -1) {LOGE("Error: openat %s error %s.\n", name, strerror(errno));return -1;}// 读取文件内容到resultBuf中char resultBuf[10] = {'\0'};if (read(fd, resultBuf, sizeof(resultBuf)) < 1) {return -1;}// 将换行符替换为字符串结束符for (i = 0; i < strlen(resultBuf); i++) {if (resultBuf[i] == '\n') {resultBuf[i] = '\0';break;}}// 比较读取的内容与预定义的类型名称typeName,如果匹配则返回索引for (i = 0; i < 4; i++) {if (strcmp(typeName[i], resultBuf) == 0) {//printf("type is %s.\n", typeName[i]);return i;}}LOGE("Error:no found type!\n");return -1;
}// 通过读取/sys/bus/mmc/devices/目录中的条目来查找SD或eMMC设备
void init_sd_emmc_point()
{DIR* dir = opendir("/sys/bus/mmc/devices/");// 目录存在if (dir != NULL) {struct dirent* de;// 循环读取目录中的每个条目,比如mmc0:0001、mmc1:0001、mmc2:0001while ((de = readdir(dir))) {// 排除当前目录 (.) 和父目录 (..)if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0 )continue;//if (de->d_type == 4)    //dir//    printf("dir name : %s \n", de->d_name);// 检查目录项名称是否以mmc开头,以确定是否是mmc设备。	if (strncmp(de->d_name, "mmc", 3) == 0) {//printf("find mmc is %s.\n", de->d_name);char flag = de->d_name[3];int ret = -1;// 读取dir目录下的mmc0:0001/type文件,获取设备类型ret = readFile(dir, de->d_name);// 设备类型存在if (ret != -1) {// mmc0:0001/type -> result_point[SDIO]="/dev/mmcblk2"// mmc1:0001/type -> result_point[SD]="/dev/mmcblk1"// mmc2:0001/type -> result_point[MMC]="/dev/mmcblk0"strcpy(result_point[ret], point_items[flag - '0']);} else {// 无效strcpy(result_point[ret], "");}}}}closedir(dir);
}

NanoPC-T4开发板为例(未接入SD卡):

pi@NanoPC-T4:~$ ls -l  /sys/bus/mmc/devices
total 0
lrwxrwxrwx 1 root root 0 Oct  6 14:41 mmc0:0001 -> ../../../devices/platform/fe310000.mmc/mmc_host/mmc0/mmc0:0001
lrwxrwxrwx 1 root root 0 Oct  6 14:41 mmc2:0001 -> ../../../devices/platform/fe330000.sdhci/mmc_host/mmc2/mmc2:0001
pi@NanoPC-T4:~$ cat  /sys/bus/mmc/devices/mmc0\:0001/type
SDIO
pi@NanoPC-T4:~$ cat  /sys/bus/mmc/devices/mmc2\:0001/type
MMC

函数执行完毕:

result_point[MMC]="/dev/mmcblk0"
result_point[SD]=""
result_point[SDIO]="/dev/mmcblk2"
result_point[SDcombo]=""

如果插入SD卡:

pi@NanoPC-T4:~$ ls -l  /sys/bus/mmc/devices
total 0
lrwxrwxrwx 1 root root 0 Oct  6 14:41 mmc0:0001 -> ../../../devices/platform/fe310000.mmc/mmc_host/mmc0/mmc0:0001
lrwxrwxrwx 1 root root 0 Oct  6 15:02 mmc1:0001 -> ../../../devices/platform/fe320000.mmc/mmc_host/mmc1/mmc1:0001
lrwxrwxrwx 1 root root 0 Oct  6 14:41 mmc2:0001 -> ../../../devices/platform/fe330000.sdhci/mmc_host/mmc2/mmc2:0001pi@NanoPC-T4:~$ cat  /sys/bus/mmc/devices/mmc1\:0001/type
SD
2.7 启动方式判定

接着通过读取命令行信息判断启动方式,用于检测是否从SD/USB启动;

bSDBoot = is_boot_from_SD();
bUDiskBoot = is_boot_from_udisk();if (bSDBoot || bUDiskBoot) {char imageFile[64] = {0};if (bSDBoot) {if (is_sdcard_update()) {strlcpy(imageFile, EX_SDCARD_ROOT, sizeof(imageFile));strlcat(imageFile, "/sdupdate.img", sizeof(imageFile));if (access(imageFile, F_OK) == 0) {sdupdate_package = strdup(imageFile);bSDBootUpdate = true;ui_show_text(1);LOGI("sdupdate_package = %s\n", sdupdate_package);}}}if (bUDiskBoot) {if (is_udisk_update()) {strlcpy(imageFile, EX_UDISK_ROOT, sizeof(imageFile));strlcat(imageFile, "/sdupdate.img", sizeof(imageFile));if (access(imageFile, F_OK) == 0) {usbupdate_package = strdup(imageFile);bUdiskUpdate = true;ui_show_text(1);LOGI("usbupdate_package = %s\n", usbupdate_package);}}}
}

由于我们采用的eMMC启动方式,所以输出如下日志:

LOG_INFO: read cmdline
LOG_INFO: >>> Boot from non-SDcard
LOG_INFO: read cmdline
LOG_INFO: >>> Boot from non-U-Disk
2.7.1 is_boot_from_SD

is_boot_from_SD函数定义在sdboot.c

bool is_boot_from_SD(void)
{bool bSDBoot = false;char param[1024];int fd, ret;char *s = NULL;LOGI("read cmdline\n");memset(param, 0, 1024);// 读取命令行fd = open("/proc/cmdline", O_RDONLY);ret = read(fd, (char*)param, 1024);// 查找sdfwupdate字符串s = strstr(param, "sdfwupdate");if (s != NULL) {bSDBoot = true;LOGI(">>> Boot from SDcard\n");} else {bSDBoot = false;LOGI(">>> Boot from non-SDcard\n");}close(fd);return bSDBoot;
}
2.7.2 is_boot_from_udisk

is_boot_from_udisk函数定义在usbboot.c

bool is_boot_from_udisk(void)
{bool bUDisk = false;char param[1024];int fd, ret;char *s = NULL;LOGI("read cmdline\n");memset(param, 0, 1024);// 读取命令行fd = open("/proc/cmdline", O_RDONLY);ret = read(fd, (char*)param, 1024);// 查找usbfwupdate字符串s = strstr(param, "usbfwupdate");if (s != NULL) {bUDisk = true;LOGI(">>> Boot from U-Disk\n");} else {bUDisk = false;LOGI(">>> Boot from non-U-Disk\n");}close(fd);return bUDisk;
}

参考文章:

[1] Rockchip RK3588 - Rockchip Linux Recovery updateEngine源码分析

[2] Rockchip RK3588 - Rockchip Linux Recovery updateEngine测试

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/881172.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

PhotoMaker部署文档

一、介绍 PhotoMaker&#xff1a;一种高效的、个性化的文本转图像生成方法&#xff0c;能通过堆叠 ID 嵌入自定义逼真的人类照片。相当于把一张人的照片特征提取出来&#xff0c;然后可以生成你想要的不同风格照片&#xff0c;如写真等等。 主要特点&#xff1a; 在几秒钟内…

[C语言]指针和数组

目录 1.数组的地址 2.通过指针访问数组 3.数组和指针的不同点 4.指针数组 1.数组的地址 数组的地址是什么&#xff1f; 看下面一组代码 #include <stdio.h> int main() { int arr[5] {5,4,3,2,1}; printf("&arr[0] %p\n", &arr[0]); printf(&qu…

【c++】string类 (一)

简介 由于c的历史包袱&#xff0c;c要兼容c语言&#xff0c;c的字符串要兼容c语言&#xff0c;在 C 中&#xff0c;字符串通常使用两种主要的方式来表示&#xff1a; C风格字符串&#xff08;C-style strings&#xff09;&#xff1a; 依然是以 \0 结尾的字符数组。这种表示方…

设置服务器走本地代理

勾选&#xff1a; 然后&#xff1a; git clone https://github.com/rofl0r/proxychains-ng.git./configure --prefix/home/wangguisen/usr --sysconfdir/home/wangguisen/etcmakemake install# 在最后配置成本地代理地址 vim /home/wangguisen/etc/proxychains.confsocks4 17…

Web安全 - 文件上传漏洞(File Upload Vulnerability)

文章目录 OWASP 2023 TOP 10导图定义攻击场景1. 上传恶意脚本2. 目录遍历3. 覆盖现有文件4. 文件上传结合社会工程攻击 防御措施1. 文件类型验证2. 文件名限制3. 文件存储位置4. 文件权限设置5. 文件内容检测6. 访问控制7. 服务器配置 文件类型验证实现Hutool的FileTypeUtil使用…

计算机网络:计算机网络体系结构 —— OSI 模型 与 TCP/IP 模型

文章目录 计算机网络体系结构OSI 参考模型TCP/IP 参考模型分层的必要性物理层的主要问题数据链路层的主要问题网络层的主要问题运输层的主要问题应用层的主要问题 分层思想的处理方法发送请求路由器转发接受请求发送响应接收响应 计算机网络体系结构 计算机网络体系结构是指将…

简单部署vue+springboot项目

vue 参考博客 先将vue项目打包 npm run build 再创建项目文件夹front,在front中新建nginx.conf server {listen 80;server_name localhost;# 请求体的大小限制client_max_body_size 50m;# 日志文件存放地址access_log /var/log/nginx/host.access.log main;error…

openpnp - 图像传送方向要在高级校正之前设置好

文章目录 openpnp - 图像传送方向要在高级校正之前设置好笔记图像传送方向的确定END openpnp - 图像传送方向要在高级校正之前设置好 笔记 图像传送方向和JOG面板的移动控制和实际设备的顶部摄像头/底部摄像头要一致&#xff0c;这样才能和贴板子时的实际操作方向对应起来。 …

C++ | Leetcode C++题解之第456题132模式

题目&#xff1a; 题解&#xff1a; class Solution { public:bool find132pattern(vector<int>& nums) {int n nums.size();vector<int> candidate_i {nums[0]};vector<int> candidate_j {nums[0]};for (int k 1; k < n; k) {auto it_i upper_…

测试-BUG篇

文章目录 软件测试的生命周期BUGbug的概念描述bug的要素bug级别bug的生命周期 与开发产生争执怎么办&#xff08;高频考题&#xff09; 软件测试的生命周期 软件测试贯穿于软件的整个生命周期 BUG bug的概念 是指计算机程序中存在的一个错误(error)、缺陷(flaw)、疏忽(mista…

docker环境下配置cerbot获取免费ssl证书并自动续期

文章目录 实践场景了解certbot查看nginx的映射情况操作目标配置nginx配置的ssl证书设置自动续签 实践场景 本人使用docker部署了一个nginx容器&#xff0c;通过容器卷&#xff0c;实现本地html&#xff0c;ssl&#xff0c;conf和ngiinx容器映射的&#xff0c; 经常需要手动部署…

适合跑步的开放式耳机哪个品牌好?怎么选?可入的蓝牙耳机推荐

想必很多爱晨跑的朋友&#xff0c;一定都有过这般令人困扰的经历。耳机戴久了总觉得不舒适&#xff0c;或是尺寸不合&#xff0c;或是材质欠佳&#xff0c;反正无论怎样调整&#xff0c;都很难找到最舒适的佩戴方式。而且&#xff0c;有时候戴的时间久了&#xff0c;还很容易掉…

FLUX的ID保持项目也来了! 字节开源PuLID-FLUX-v0.9.0,开启一致性风格写真新纪元!

之前的文章已经和大家介绍过字节开源的ID保持项目PuLID。随着FLUX模型的发布&#xff0c;PuLID也开源了 FLUX 版本的模型&#xff0c;不得不说FLUX的强大&#xff0c;两个月生态就赶上了SDXL。这次新发布PuLID-FLUX-v0.9.0模型&#xff0c;它为FLUX.1-dev提供了无需调整的ID定制…

4S店4S店客户管理系统小程序(lw+演示+源码+运行)

社会的发展和科学技术的进步&#xff0c;互联网技术越来越受欢迎。手机也逐渐受到广大人民群众的喜爱&#xff0c;也逐渐进入了每个用户的使用。手机具有便利性&#xff0c;速度快&#xff0c;效率高&#xff0c;成本低等优点。 因此&#xff0c;构建符合自己要求的操作系统是非…

VirtulBOX Ubuntu22安装dpdk23.11

目录 依赖包安装 Python安装 numa安装 ​编辑Python pip3安装 ​编辑pyelftools安装 meson和ninja安装 ​编辑构建与编译 Meson构建DPDK ​编辑Ninja安装DPDK ​编辑VFIO-PCI驱动安装 大页内存和IOMMU配置 ​编辑VFIO-PCI加载 ​编辑VFIO-PCI驱动绑定 ​编辑dpdk…

Linux网络操作命令与函数全面总结

1. 引言 Linux作为服务器和开发平台&#xff0c;网络操作是其核心功能之一。本文旨在全面总结Linux系统中的网络操作方法&#xff0c;包括命令行工具和编程接口&#xff0c;帮助读者深入理解Linux网络管理的机制。 2. 命令行工具 2.1 ping 命令 ping 命令用于测试网络连接和…

【Linux】信号知识三把斧——信号的产生、保存和处理

目录​​​​​​​ 1、关于信号的前置知识 1.1.什么是信号&#xff1f; 1.2.为什么要学习信号&#xff1f; 1.3.如何学习信号&#xff1f; 1.4.一些常见的信号 1.5.信号的处理方式 1.6.为什么每一个进程都可以系统调用&#xff1f; 2.信号的产生 2.1.kill命令产生信号…

计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-09-28

计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-09-28 目录 文章目录 计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-09-28目录前言1. Cognitive phantoms in LLMs through the lens of latent variables摘要研究背景问题与挑战创新点算法模型实验效果…

【C++】二叉搜索树+变身 = AVL树

&#x1f680;个人主页&#xff1a;小羊 &#x1f680;所属专栏&#xff1a;C 很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~ 目录 前言一、AVL树二、AVL树的实现2.1 平衡因子2.2 旋转处理2.2.1 左单旋&#xff1a;插入新节点后单纯的右边高2.2.2 …

html5 + css3(上)

目录 HTML初识基础认知web标准vscode的简介和使用注释 HTML标签学习排版标签标题和段落换行和水平线标签 文本格式化标签媒体标签图片标签图片-基本使用图片-属性 路径绝对路径相对路径 音频标签视频标签链接标签 HTML基础列表标签列表-无序和有序列表-自定义 表格标签表格-使用…