/****************************************************************************** * ventoy_http.c ---- ventoy http * Copyright (c) 2021, longpanda * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "fat_filelib.h" static char *g_pub_out_buf = NULL; static int g_pub_out_max = 0; static pthread_mutex_t g_api_mutex; static char g_cur_language[128]; static int g_cur_part_style = 0; static int g_cur_show_all = 0; static char g_cur_server_token[64]; static struct mg_context *g_ventoy_http_ctx = NULL; static uint32_t g_efi_part_offset = 0; static uint8_t *g_efi_part_raw_img = NULL; static uint8_t *g_grub_stg1_raw_img = NULL; static char g_cur_process_diskname[64]; static char g_cur_process_type[64]; static volatile int g_cur_process_result = 0; static volatile PROGRESS_POINT g_current_progress = PT_FINISH; static int ventoy_load_mbr_template(void) { FILE *fp = NULL; fp = fopen("boot/boot.img", "rb"); if (fp == NULL) { vlog("Failed to open file boot/boot.img\n"); return 1; } fread(g_mbr_template, 1, 512, fp); fclose(fp); ventoy_gen_preudo_uuid(g_mbr_template + 0x180); return 0; } static int ventoy_disk_xz_flush(void *src, unsigned int size) { memcpy(g_efi_part_raw_img + g_efi_part_offset, src, size); g_efi_part_offset += size; g_current_progress = PT_LOAD_DISK_IMG + (g_efi_part_offset / SIZE_1MB); return (int)size; } static int ventoy_unxz_efipart_img(void) { int rc; int inlen; int xzlen; void *xzbuf = NULL; uint8_t *buf = NULL; rc = ventoy_read_file_to_buf(VENTOY_FILE_DISK_IMG, 0, &xzbuf, &xzlen); vdebug("read disk.img.xz rc:%d len:%d\n", rc, xzlen); if (g_efi_part_raw_img) { buf = g_efi_part_raw_img; } else { buf = malloc(VTOYEFI_PART_BYTES); if (!buf) { check_free(xzbuf); return 1; } } g_efi_part_offset = 0; g_efi_part_raw_img = buf; rc = unxz(xzbuf, xzlen, NULL, ventoy_disk_xz_flush, buf, &inlen, NULL); vdebug("ventoy_unxz_efipart_img len:%d rc:%d unxzlen:%u\n", inlen, rc, g_efi_part_offset); check_free(xzbuf); return 0; } static int ventoy_unxz_stg1_img(void) { int rc; int inlen; int xzlen; void *xzbuf = NULL; uint8_t *buf = NULL; rc = ventoy_read_file_to_buf(VENTOY_FILE_STG1_IMG, 0, &xzbuf, &xzlen); vdebug("read core.img.xz rc:%d len:%d\n", rc, xzlen); if (g_grub_stg1_raw_img) { buf = g_grub_stg1_raw_img; } else { buf = zalloc(SIZE_1MB); if (!buf) { check_free(xzbuf); return 1; } } rc = unxz(xzbuf, xzlen, NULL, NULL, buf, &inlen, NULL); vdebug("ventoy_unxz_stg1_img len:%d rc:%d\n", inlen, rc); g_grub_stg1_raw_img = buf; check_free(xzbuf); return 0; } static int ventoy_http_save_cfg(void) { FILE *fp; fp = fopen(g_ini_file, "w"); if (!fp) { vlog("Failed to open %s code:%d\n", g_ini_file, errno); return 0; } fprintf(fp, "[Ventoy]\nLanguage=%s\nPartStyle=%d\nShowAllDevice=%d\n", g_cur_language, g_cur_part_style, g_cur_show_all); fclose(fp); return 0; } static int ventoy_http_load_cfg(void) { int i; int len; char line[256]; FILE *fp; fp = fopen(g_ini_file, "r"); if (!fp) { return 0; } while (fgets(line, sizeof(line), fp)) { len = (int)strlen(line); for (i = len - 1; i >= 0; i--) { if (line[i] == ' ' || line[i] == '\t' || line[i] == '\r' || line[i] == '\n') { line[i] = 0; } else { break; } } len = (int)strlen("Language="); if (strncmp(line, "Language=", len) == 0) { scnprintf(g_cur_language, "%s", line + len); } else if (strncmp(line, "PartStyle=", strlen("PartStyle=")) == 0) { g_cur_part_style = (int)strtol(line + strlen("PartStyle="), NULL, 10); } else if (strncmp(line, "ShowAllDevice=", strlen("ShowAllDevice=")) == 0) { g_cur_show_all = (int)strtol(line + strlen("ShowAllDevice="), NULL, 10); } } fclose(fp); return 0; } static int ventoy_json_result(struct mg_connection *conn, const char *err) { if (conn) { mg_printf(conn, "HTTP/1.1 200 OK \r\n" "Content-Type: application/json\r\n" "Content-Length: %d\r\n" "\r\n%s", (int)strlen(err), err); } else { memcpy(g_pub_out_buf, err, (int)strlen(err) + 1); } return 0; } static int ventoy_json_buffer(struct mg_connection *conn, const char *json_buf, int json_len) { if (conn) { mg_printf(conn, "HTTP/1.1 200 OK \r\n" "Content-Type: application/json\r\n" "Content-Length: %d\r\n" "\r\n%s", json_len, json_buf); } else { if (json_len >= g_pub_out_max) { vlog("json buffer overflow\n"); } else { memcpy(g_pub_out_buf, json_buf, json_len); g_pub_out_buf[json_len] = 0; } } return 0; } static int ventoy_api_sysinfo(struct mg_connection *conn, VTOY_JSON *json) { int busy = 0; int pos = 0; int buflen = 0; char buf[512]; (void)json; busy = (g_current_progress == PT_FINISH) ? 0 : 1; buflen = sizeof(buf) - 1; VTOY_JSON_FMT_BEGIN(pos, buf, buflen); VTOY_JSON_FMT_OBJ_BEGIN(); VTOY_JSON_FMT_STRN("token", g_cur_server_token); VTOY_JSON_FMT_STRN("language", g_cur_language); VTOY_JSON_FMT_STRN("ventoy_ver", ventoy_get_local_version()); VTOY_JSON_FMT_UINT("partstyle", g_cur_part_style); VTOY_JSON_FMT_BOOL("busy", busy); VTOY_JSON_FMT_STRN("process_disk", g_cur_process_diskname); VTOY_JSON_FMT_STRN("process_type", g_cur_process_type); VTOY_JSON_FMT_OBJ_END(); VTOY_JSON_FMT_END(pos); ventoy_json_buffer(conn, buf, pos); return 0; } static int ventoy_api_get_percent(struct mg_connection *conn, VTOY_JSON *json) { int pos = 0; int buflen = 0; int percent = 0; char buf[128]; (void)json; percent = g_current_progress * 100 / PT_FINISH; buflen = sizeof(buf) - 1; VTOY_JSON_FMT_BEGIN(pos, buf, buflen); VTOY_JSON_FMT_OBJ_BEGIN(); VTOY_JSON_FMT_STRN("result", g_cur_process_result ? "failed" : "success"); VTOY_JSON_FMT_STRN("process_disk", g_cur_process_diskname); VTOY_JSON_FMT_STRN("process_type", g_cur_process_type); VTOY_JSON_FMT_UINT("percent", percent); VTOY_JSON_FMT_OBJ_END(); VTOY_JSON_FMT_END(pos); ventoy_json_buffer(conn, buf, pos); return 0; } static int ventoy_api_set_language(struct mg_connection *conn, VTOY_JSON *json) { const char *lang = NULL; lang = vtoy_json_get_string_ex(json, "language"); if (lang) { scnprintf(g_cur_language, "%s", lang); ventoy_http_save_cfg(); } ventoy_json_result(conn, VTOY_JSON_SUCCESS_RET); return 0; } static int ventoy_api_set_partstyle(struct mg_connection *conn, VTOY_JSON *json) { int ret; int style = 0; ret = vtoy_json_get_int(json, "partstyle", &style); if (JSON_SUCCESS == ret) { if ((style == 0) || (style == 1)) { g_cur_part_style = style; ventoy_http_save_cfg(); } } ventoy_json_result(conn, VTOY_JSON_SUCCESS_RET); return 0; } static int ventoy_clean_disk(int fd, uint64_t size) { int zerolen; ssize_t len; off_t offset; void *buf = NULL; vdebug("ventoy_clean_disk fd:%d size:%llu\n", fd, (_ull)size); zerolen = 64 * 1024; buf = zalloc(zerolen); if (!buf) { vlog("failed to alloc clean buffer\n"); return 1; } offset = lseek(fd, 0, SEEK_SET); len = write(fd, buf, zerolen); vdebug("write disk at off:%llu writelen:%lld datalen:%d\n", (_ull)offset, (_ll)len, zerolen); offset = lseek(fd, size - zerolen, SEEK_SET); len = write(fd, buf, zerolen); vdebug("write disk at off:%llu writelen:%lld datalen:%d\n", (_ull)offset, (_ll)len, zerolen); fsync(fd); free(buf); return 0; } static int ventoy_write_legacy_grub(int fd, int partstyle) { ssize_t len; off_t offset; if (partstyle) { vlog("Write GPT stage1 ...\n"); offset = lseek(fd, 512 * 34, SEEK_SET); g_grub_stg1_raw_img[500] = 35;//update blocklist len = write(fd, g_grub_stg1_raw_img, SIZE_1MB - 512 * 34); vlog("lseek offset:%llu(%u) writelen:%llu(%u)\n", (_ull)offset, 512 * 34, (_ull)len, SIZE_1MB - 512 * 34); if (SIZE_1MB - 512 * 34 != len) { vlog("write length error\n"); return 1; } } else { vlog("Write MBR stage1 ...\n"); offset = lseek(fd, 512, SEEK_SET); len = write(fd, g_grub_stg1_raw_img, SIZE_1MB - 512); vlog("lseek offset:%llu(%u) writelen:%llu(%u)\n", (_ull)offset, 512, (_ull)len, SIZE_1MB - 512); if (SIZE_1MB - 512 != len) { vlog("write length error\n"); return 1; } } return 0; } static int VentoyFatMemRead(uint32 Sector, uint8 *Buffer, uint32 SectorCount) { uint32 i; uint32 offset; for (i = 0; i < SectorCount; i++) { offset = (Sector + i) * 512; memcpy(Buffer + i * 512, g_efi_part_raw_img + offset, 512); } return 1; } static int VentoyFatMemWrite(uint32 Sector, uint8 *Buffer, uint32 SectorCount) { uint32 i; uint32 offset; for (i = 0; i < SectorCount; i++) { offset = (Sector + i) * 512; memcpy(g_efi_part_raw_img + offset, Buffer + i * 512, 512); } return 1; } static int VentoyProcSecureBoot(int SecureBoot) { int rc = 0; int size; char *filebuf = NULL; void *file = NULL; vlog("VentoyProcSecureBoot %d ...\n", SecureBoot); if (SecureBoot) { vlog("Secure boot is enabled ...\n"); return 0; } fl_init(); if (0 == fl_attach_media(VentoyFatMemRead, VentoyFatMemWrite)) { file = fl_fopen("/EFI/BOOT/grubx64_real.efi", "rb"); vlog("Open ventoy efi file %p \n", file); if (file) { fl_fseek(file, 0, SEEK_END); size = (int)fl_ftell(file); fl_fseek(file, 0, SEEK_SET); vlog("ventoy efi file size %d ...\n", size); filebuf = (char *)malloc(size); if (filebuf) { fl_fread(filebuf, 1, size, file); } fl_fclose(file); vlog("Now delete all efi files ...\n"); fl_remove("/EFI/BOOT/BOOTX64.EFI"); fl_remove("/EFI/BOOT/grubx64.efi"); fl_remove("/EFI/BOOT/grubx64_real.efi"); fl_remove("/EFI/BOOT/MokManager.efi"); fl_remove("/EFI/BOOT/mmx64.efi"); fl_remove("/ENROLL_THIS_KEY_IN_MOKMANAGER.cer"); file = fl_fopen("/EFI/BOOT/BOOTX64.EFI", "wb"); vlog("Open bootx64 efi file %p \n", file); if (file) { if (filebuf) { fl_fwrite(filebuf, 1, size, file); } fl_fflush(file); fl_fclose(file); } if (filebuf) { free(filebuf); } } file = fl_fopen("/EFI/BOOT/grubia32_real.efi", "rb"); vlog("Open ventoy efi file %p\n", file); if (file) { fl_fseek(file, 0, SEEK_END); size = (int)fl_ftell(file); fl_fseek(file, 0, SEEK_SET); vlog("ventoy efi file size %d ...\n", size); filebuf = (char *)malloc(size); if (filebuf) { fl_fread(filebuf, 1, size, file); } fl_fclose(file); vlog("Now delete all efi files ...\n"); fl_remove("/EFI/BOOT/BOOTIA32.EFI"); fl_remove("/EFI/BOOT/grubia32.efi"); fl_remove("/EFI/BOOT/grubia32_real.efi"); fl_remove("/EFI/BOOT/mmia32.efi"); file = fl_fopen("/EFI/BOOT/BOOTIA32.EFI", "wb"); vlog("Open bootia32 efi file %p\n", file); if (file) { if (filebuf) { fl_fwrite(filebuf, 1, size, file); } fl_fflush(file); fl_fclose(file); } if (filebuf) { free(filebuf); } } } else { rc = 1; } fl_shutdown(); return rc; } static int ventoy_check_efi_part_data(int fd, uint64_t offset) { int i; ssize_t len; char *buf; buf = malloc(SIZE_1MB); if (!buf) { return 0; } lseek(fd, offset, SEEK_SET); for (i = 0; i < 32; i++) { len = read(fd, buf, SIZE_1MB); if (len != SIZE_1MB || memcmp(buf, g_efi_part_raw_img + i * SIZE_1MB, SIZE_1MB)) { vlog("part2 data check failed i=%d len:%llu\n", i, (_ull)len); return 1; } g_current_progress = PT_CHECK_PART2 + (i / 4); } return 0; } static int ventoy_write_efipart(int fd, uint64_t offset, uint32_t secureboot) { int i; ssize_t len; vlog("Formatting part2 EFI offset:%llu ...\n", (_ull)offset); lseek(fd, offset, SEEK_SET); VentoyProcSecureBoot((int)secureboot); g_current_progress = PT_WRITE_VENTOY_START; for (i = 0; i < 32; i++) { len = write(fd, g_efi_part_raw_img + i * SIZE_1MB, SIZE_1MB); vlog("write disk writelen:%lld datalen:%d [ %s ]\n", (_ll)len, SIZE_1MB, (len == SIZE_1MB) ? "success" : "failed"); if (len != SIZE_1MB) { vlog("failed to format part2 EFI\n"); return 1; } g_current_progress = PT_WRITE_VENTOY_START + i / 4; } return 0; } static int VentoyFillBackupGptHead(VTOY_GPT_INFO *pInfo, VTOY_GPT_HDR *pHead) { uint64_t LBA; uint64_t BackupLBA; memcpy(pHead, &pInfo->Head, sizeof(VTOY_GPT_HDR)); LBA = pHead->EfiStartLBA; BackupLBA = pHead->EfiBackupLBA; pHead->EfiStartLBA = BackupLBA; pHead->EfiBackupLBA = LBA; pHead->PartTblStartLBA = BackupLBA + 1 - 33; pHead->Crc = 0; pHead->Crc = ventoy_crc32(pHead, pHead->Length); return 0; } static int ventoy_write_gpt_part_table(int fd, uint64_t disksize, VTOY_GPT_INFO *gpt) { ssize_t len; off_t offset; VTOY_GPT_HDR BackupHead; VentoyFillBackupGptHead(gpt, &BackupHead); offset = lseek(fd, disksize - 512, SEEK_SET); len = write(fd, &BackupHead, sizeof(VTOY_GPT_HDR)); vlog("write backup gpt part table off:%llu len:%llu\n", (_ull)offset, (_ull)len); if (offset != disksize - 512 || len != sizeof(VTOY_GPT_HDR)) { return 1; } offset = lseek(fd, disksize - 512 * 33, SEEK_SET); len = write(fd, gpt->PartTbl, sizeof(gpt->PartTbl)); vlog("write main gpt part table off:%llu len:%llu\n", (_ull)offset, (_ull)len); if (offset != disksize - 512 * 33 || len != sizeof(gpt->PartTbl)) { return 1; } offset = lseek(fd, 0, SEEK_SET); len = write(fd, gpt, sizeof(VTOY_GPT_INFO)); vlog("write gpt part head off:%llu len:%llu\n", (_ull)offset, (_ull)len); if (offset != 0 || len != sizeof(VTOY_GPT_INFO)) { return 1; } return 0; } static int ventoy_mbr_need_update(ventoy_disk *disk, MBR_HEAD *mbr) { int update = 0; int partition_style; MBR_HEAD LocalMBR; partition_style = disk->vtoydata.partition_style; memcpy(mbr, &(disk->vtoydata.gptinfo.MBR), 512); VentoyGetLocalBootImg(&LocalMBR); memcpy(LocalMBR.BootCode + 0x180, mbr->BootCode + 0x180, 16); if (partition_style) { LocalMBR.BootCode[92] = 0x22; } if (memcmp(LocalMBR.BootCode, mbr->BootCode, 440)) { memcpy(mbr->BootCode, LocalMBR.BootCode, 440); vlog("MBR boot code different, must update it.\n"); update = 1; } if (partition_style == 0 && mbr->PartTbl[0].Active == 0) { mbr->PartTbl[0].Active = 0x80; mbr->PartTbl[1].Active = 0; mbr->PartTbl[2].Active = 0; mbr->PartTbl[3].Active = 0; vlog("set MBR partition 1 active flag enabled\n"); update = 1; } return update; } static void * ventoy_update_thread(void *data) { int fd; ssize_t len; off_t offset; MBR_HEAD MBR; ventoy_disk *disk = NULL; ventoy_thread_data *thread = (ventoy_thread_data *)data; vdebug("ventoy_update_thread run ...\n"); fd = thread->diskfd; disk = thread->disk; g_current_progress = PT_PRAPARE_FOR_CLEAN; vdebug("check disk %s\n", disk->disk_name); if (ventoy_is_disk_mounted(disk->disk_path)) { vlog("disk is mounted, now try to unmount it ...\n"); ventoy_try_umount_disk(disk->disk_path); } if (ventoy_is_disk_mounted(disk->disk_path)) { vlog("%s is mounted and can't umount!\n", disk->disk_path); goto err; } else { vlog("disk is not mounted now, we can do continue ...\n"); } g_current_progress = PT_LOAD_CORE_IMG; ventoy_unxz_stg1_img(); g_current_progress = PT_LOAD_DISK_IMG; ventoy_unxz_efipart_img(); g_current_progress = PT_FORMAT_PART2; vlog("Formatting part2 EFI ...\n"); if (0 != ventoy_write_efipart(fd, disk->vtoydata.part2_start_sector * 512, thread->secure_boot)) { vlog("Failed to format part2 efi ...\n"); goto err; } g_current_progress = PT_WRITE_STG1_IMG; vlog("Writing legacy grub ...\n"); if (0 != ventoy_write_legacy_grub(fd, disk->vtoydata.partition_style)) { vlog("ventoy_write_legacy_grub failed ...\n"); goto err; } offset = lseek(fd, 512 * 2040, SEEK_SET); len = write(fd, disk->vtoydata.rsvdata, sizeof(disk->vtoydata.rsvdata)); vlog("Writing reserve data offset:%llu len:%llu ...\n", (_ull)offset, (_ull)len); if (ventoy_mbr_need_update(disk, &MBR)) { offset = lseek(fd, 0, SEEK_SET); len = write(fd, &MBR, 512); vlog("update MBR offset:%llu len:%llu\n", (_ull)offset, (_ull)len); } else { vlog("No need to update MBR\n"); } g_current_progress = PT_SYNC_DATA1; vlog("fsync data1...\n"); fsync(fd); vtoy_safe_close_fd(fd); g_current_progress = PT_SYNC_DATA2; vlog("====================================\n"); vlog("====== ventoy update success ======\n"); vlog("====================================\n"); goto end; err: g_cur_process_result = 1; vtoy_safe_close_fd(fd); end: g_current_progress = PT_FINISH; check_free(thread); return NULL; } static void * ventoy_install_thread(void *data) { int fd; ssize_t len; off_t offset; MBR_HEAD MBR; ventoy_disk *disk = NULL; VTOY_GPT_INFO *gpt = NULL; ventoy_thread_data *thread = (ventoy_thread_data *)data; uint64_t Part1StartSector = 0; uint64_t Part1SectorCount = 0; uint64_t Part2StartSector = 0; vdebug("ventoy_install_thread run ...\n"); fd = thread->diskfd; disk = thread->disk; g_current_progress = PT_PRAPARE_FOR_CLEAN; vdebug("check disk %s\n", disk->disk_name); if (ventoy_is_disk_mounted(disk->disk_path)) { vlog("disk is mounted, now try to unmount it ...\n"); ventoy_try_umount_disk(disk->disk_path); } if (ventoy_is_disk_mounted(disk->disk_path)) { vlog("%s is mounted and can't umount!\n", disk->disk_path); goto err; } else { vlog("disk is not mounted now, we can do continue ...\n"); } g_current_progress = PT_DEL_ALL_PART; ventoy_clean_disk(fd, disk->size_in_byte); g_current_progress = PT_LOAD_CORE_IMG; ventoy_unxz_stg1_img(); g_current_progress = PT_LOAD_DISK_IMG; ventoy_unxz_efipart_img(); if (thread->partstyle) { vdebug("Fill GPT part table\n"); gpt = zalloc(sizeof(VTOY_GPT_INFO)); ventoy_fill_gpt(disk->size_in_byte, thread->reserveBytes, thread->align4kb, gpt); Part1StartSector = gpt->PartTbl[0].StartLBA; Part1SectorCount = gpt->PartTbl[0].LastLBA - Part1StartSector + 1; Part2StartSector = gpt->PartTbl[1].StartLBA; } else { vdebug("Fill MBR part table\n"); ventoy_fill_mbr(disk->size_in_byte, thread->reserveBytes, thread->align4kb, &MBR); Part1StartSector = MBR.PartTbl[0].StartSectorId; Part1SectorCount = MBR.PartTbl[0].SectorCount; Part2StartSector = MBR.PartTbl[1].StartSectorId; } vlog("Part1StartSector:%llu Part1SectorCount:%llu Part2StartSector:%llu\n", (_ull)Part1StartSector, (_ull)Part1SectorCount, (_ull)Part2StartSector); if (thread->partstyle != disk->partstyle) { vlog("Wait for format part1 (partstyle changed) ...\n"); sleep(1); } g_current_progress = PT_FORMAT_PART1; vlog("Formatting part1 exFAT %s ...\n", disk->disk_path); if (0 != mkexfat_main(disk->disk_path, fd, Part1SectorCount)) { vlog("Failed to format exfat ...\n"); goto err; } g_current_progress = PT_FORMAT_PART2; vlog("Formatting part2 EFI ...\n"); if (0 != ventoy_write_efipart(fd, Part2StartSector * 512, thread->secure_boot)) { vlog("Failed to format part2 efi ...\n"); goto err; } g_current_progress = PT_WRITE_STG1_IMG; vlog("Writing legacy grub ...\n"); if (0 != ventoy_write_legacy_grub(fd, thread->partstyle)) { vlog("ventoy_write_legacy_grub failed ...\n"); goto err; } g_current_progress = PT_SYNC_DATA1; vlog("fsync data1...\n"); fsync(fd); vtoy_safe_close_fd(fd); /* reopen for check part2 data */ vlog("Checking part2 efi data %s ...\n", disk->disk_path); g_current_progress = PT_CHECK_PART2; fd = open(disk->disk_path, O_RDONLY | O_BINARY); if (fd < 0) { vlog("failed to open %s for check fd:%d err:%d\n", disk->disk_path, fd, errno); goto err; } if (0 == ventoy_check_efi_part_data(fd, Part2StartSector * 512)) { vlog("efi part data check success\n"); } else { vlog("efi part data check failed\n"); goto err; } vtoy_safe_close_fd(fd); /* reopen for write part table */ g_current_progress = PT_WRITE_PART_TABLE; vlog("Writting Partition Table style:%d...\n", thread->partstyle); fd = open(disk->disk_path, O_RDWR | O_BINARY); if (fd < 0) { vlog("failed to open %s for part table fd:%d err:%d\n", disk->disk_path, fd, errno); goto err; } if (thread->partstyle) { ventoy_write_gpt_part_table(fd, disk->size_in_byte, gpt); } else { offset = lseek(fd, 0, SEEK_SET); len = write(fd, &MBR, 512); vlog("Writting MBR Partition Table %llu %llu\n", (_ull)offset, (_ull)len); if (offset != 0 || len != 512) { goto err; } } g_current_progress = PT_SYNC_DATA2; vlog("fsync data2...\n"); fsync(fd); vtoy_safe_close_fd(fd); vlog("====================================\n"); vlog("====== ventoy install success ======\n"); vlog("====================================\n"); goto end; err: g_cur_process_result = 1; vtoy_safe_close_fd(fd); end: g_current_progress = PT_FINISH; check_free(gpt); check_free(thread); return NULL; } static int ventoy_api_clean(struct mg_connection *conn, VTOY_JSON *json) { int i = 0; int fd = 0; ventoy_disk *disk = NULL; const char *diskname = NULL; char path[128]; if (g_current_progress != PT_FINISH) { ventoy_json_result(conn, VTOY_JSON_BUSY_RET); return 0; } diskname = vtoy_json_get_string_ex(json, "disk"); if (diskname == NULL) { ventoy_json_result(conn, VTOY_JSON_INVALID_RET); return 0; } for (i = 0; i < g_disk_num; i++) { if (strcmp(g_disk_list[i].disk_name, diskname) == 0) { disk = g_disk_list + i; break; } } if (disk == NULL) { vlog("disk %s not found\n", diskname); ventoy_json_result(conn, VTOY_JSON_NOTFOUND_RET); return 0; } scnprintf(path, "/sys/block/%s", diskname); if (access(path, F_OK) < 0) { vlog("File %s not exist anymore\n", path); ventoy_json_result(conn, VTOY_JSON_NOTFOUND_RET); return 0; } vlog("==================================\n"); vlog("===== ventoy clean %s =====\n", disk->disk_path); vlog("==================================\n"); if (ventoy_is_disk_mounted(disk->disk_path)) { vlog("disk is mounted, now try to unmount it ...\n"); ventoy_try_umount_disk(disk->disk_path); } if (ventoy_is_disk_mounted(disk->disk_path)) { vlog("%s is mounted and can't umount!\n", disk->disk_path); ventoy_json_result(conn, VTOY_JSON_FAILED_RET); return 0; } else { vlog("disk is not mounted now, we can do the clean ...\n"); } fd = open(disk->disk_path, O_RDWR | O_BINARY); if (fd < 0) { vlog("failed to open %s fd:%d err:%d\n", disk->disk_path, fd, errno); ventoy_json_result(conn, VTOY_JSON_FAILED_RET); return 0; } vdebug("start clean %s ...\n", disk->disk_model); ventoy_clean_disk(fd, disk->size_in_byte); vtoy_safe_close_fd(fd); ventoy_json_result(conn, VTOY_JSON_SUCCESS_RET); return 0; } static int ventoy_api_install(struct mg_connection *conn, VTOY_JSON *json) { int i = 0; int ret = 0; int fd = 0; uint32_t align4kb = 0; uint32_t style = 0; uint32_t secure_boot = 0; uint64_t reserveBytes = 0; ventoy_disk *disk = NULL; const char *diskname = NULL; const char *reserve_space = NULL; ventoy_thread_data *thread = NULL; char path[128]; if (g_current_progress != PT_FINISH) { ventoy_json_result(conn, VTOY_JSON_BUSY_RET); return 0; } diskname = vtoy_json_get_string_ex(json, "disk"); reserve_space = vtoy_json_get_string_ex(json, "reserve_space"); ret += vtoy_json_get_uint(json, "partstyle", &style); ret += vtoy_json_get_uint(json, "secure_boot", &secure_boot); ret += vtoy_json_get_uint(json, "align_4kb", &align4kb); if (diskname == NULL || reserve_space == NULL || ret != JSON_SUCCESS) { ventoy_json_result(conn, VTOY_JSON_INVALID_RET); return 0; } reserveBytes = (uint64_t)strtoull(reserve_space, NULL, 10); for (i = 0; i < g_disk_num; i++) { if (strcmp(g_disk_list[i].disk_name, diskname) == 0) { disk = g_disk_list + i; break; } } if (disk == NULL) { vlog("disk %s not found\n", diskname); ventoy_json_result(conn, VTOY_JSON_NOTFOUND_RET); return 0; } if (disk->is4kn) { vlog("disk %s is 4k native, not supported.\n", diskname); ventoy_json_result(conn, VTOY_JSON_4KN_RET); return 0; } scnprintf(path, "/sys/block/%s", diskname); if (access(path, F_OK) < 0) { vlog("File %s not exist anymore\n", path); ventoy_json_result(conn, VTOY_JSON_NOTFOUND_RET); return 0; } if (disk->size_in_byte > 2199023255552ULL && style == 0) { vlog("disk %s is more than 2TB and GPT is needed\n", path); ventoy_json_result(conn, VTOY_JSON_MBR_2TB_RET); return 0; } if ((reserveBytes + VTOYEFI_PART_BYTES * 2) > disk->size_in_byte) { vlog("reserve space %llu is too big for disk %s %llu\n", (_ull)reserveBytes, path, (_ull)disk->size_in_byte); ventoy_json_result(conn, VTOY_JSON_INVALID_RSV_RET); return 0; } vlog("==================================================================================\n"); vlog("===== ventoy install %s style:%s secureboot:%u align4K:%u reserve:%llu =========\n", disk->disk_path, (style ? "GPT" : "MBR"), secure_boot, align4kb, (_ull)reserveBytes); vlog("==================================================================================\n"); if (ventoy_is_disk_mounted(disk->disk_path)) { vlog("disk is mounted, now try to unmount it ...\n"); ventoy_try_umount_disk(disk->disk_path); } if (ventoy_is_disk_mounted(disk->disk_path)) { vlog("%s is mounted and can't umount!\n", disk->disk_path); ventoy_json_result(conn, VTOY_JSON_FAILED_RET); return 0; } else { vlog("disk is not mounted now, we can do the install ...\n"); } fd = open(disk->disk_path, O_RDWR | O_BINARY); if (fd < 0) { vlog("failed to open %s fd:%d err:%d\n", disk->disk_path, fd, errno); ventoy_json_result(conn, VTOY_JSON_FAILED_RET); return 0; } vdebug("start install thread %s ...\n", disk->disk_model); thread = zalloc(sizeof(ventoy_thread_data)); if (!thread) { vtoy_safe_close_fd(fd); vlog("failed to alloc thread data err:%d\n", errno); ventoy_json_result(conn, VTOY_JSON_FAILED_RET); return 0; } g_current_progress = PT_START; g_cur_process_result = 0; scnprintf(g_cur_process_type, "%s", "install"); scnprintf(g_cur_process_diskname, "%s", disk->disk_name); thread->disk = disk; thread->diskfd = fd; thread->align4kb = align4kb; thread->partstyle = style; thread->secure_boot = secure_boot; thread->reserveBytes = reserveBytes; mg_start_thread(ventoy_install_thread, thread); ventoy_json_result(conn, VTOY_JSON_SUCCESS_RET); return 0; } static int ventoy_api_update(struct mg_connection *conn, VTOY_JSON *json) { int i = 0; int ret = 0; int fd = 0; uint32_t secure_boot = 0; ventoy_disk *disk = NULL; const char *diskname = NULL; ventoy_thread_data *thread = NULL; char path[128]; if (g_current_progress != PT_FINISH) { ventoy_json_result(conn, VTOY_JSON_BUSY_RET); return 0; } diskname = vtoy_json_get_string_ex(json, "disk"); ret += vtoy_json_get_uint(json, "secure_boot", &secure_boot); if (diskname == NULL || ret != JSON_SUCCESS) { ventoy_json_result(conn, VTOY_JSON_INVALID_RET); return 0; } for (i = 0; i < g_disk_num; i++) { if (strcmp(g_disk_list[i].disk_name, diskname) == 0) { disk = g_disk_list + i; break; } } if (disk == NULL) { vlog("disk %s not found\n", diskname); ventoy_json_result(conn, VTOY_JSON_NOTFOUND_RET); return 0; } if (disk->vtoydata.ventoy_valid == 0) { vlog("disk %s is not ventoy disk\n", diskname); ventoy_json_result(conn, VTOY_JSON_FAILED_RET); return 0; } scnprintf(path, "/sys/block/%s", diskname); if (access(path, F_OK) < 0) { vlog("File %s not exist anymore\n", path); ventoy_json_result(conn, VTOY_JSON_NOTFOUND_RET); return 0; } vlog("==========================================================\n"); vlog("===== ventoy update %s new_secureboot:%u =========\n", disk->disk_path, secure_boot); vlog("==========================================================\n"); vlog("%s version:%s partstyle:%u oldsecureboot:%u reserve:%llu\n", disk->disk_path, disk->vtoydata.ventoy_ver, disk->vtoydata.partition_style, disk->vtoydata.secure_boot_flag, (_ull)(disk->vtoydata.preserved_space) ); if (ventoy_is_disk_mounted(disk->disk_path)) { vlog("disk is mounted, now try to unmount it ...\n"); ventoy_try_umount_disk(disk->disk_path); } if (ventoy_is_disk_mounted(disk->disk_path)) { vlog("%s is mounted and can't umount!\n", disk->disk_path); ventoy_json_result(conn, VTOY_JSON_FAILED_RET); return 0; } else { vlog("disk is not mounted now, we can do the update ...\n"); } fd = open(disk->disk_path, O_RDWR | O_BINARY); if (fd < 0) { vlog("failed to open %s fd:%d err:%d\n", disk->disk_path, fd, errno); ventoy_json_result(conn, VTOY_JSON_FAILED_RET); return 0; } vdebug("start update thread %s ...\n", disk->disk_model); thread = zalloc(sizeof(ventoy_thread_data)); if (!thread) { vtoy_safe_close_fd(fd); vlog("failed to alloc thread data err:%d\n", errno); ventoy_json_result(conn, VTOY_JSON_FAILED_RET); return 0; } g_current_progress = PT_START; g_cur_process_result = 0; scnprintf(g_cur_process_type, "%s", "update"); scnprintf(g_cur_process_diskname, "%s", disk->disk_name); thread->disk = disk; thread->diskfd = fd; thread->secure_boot = secure_boot; mg_start_thread(ventoy_update_thread, thread); ventoy_json_result(conn, VTOY_JSON_SUCCESS_RET); return 0; } static int ventoy_api_refresh_device(struct mg_connection *conn, VTOY_JSON *json) { (void)json; if (g_current_progress == PT_FINISH) { g_disk_num = 0; ventoy_disk_enumerate_all(); } ventoy_json_result(conn, VTOY_JSON_SUCCESS_RET); return 0; } static int ventoy_api_get_dev_list(struct mg_connection *conn, VTOY_JSON *json) { int i = 0; int rc = 0; int pos = 0; int buflen = 0; uint32_t alldev = 0; char *buf = NULL; ventoy_disk *cur = NULL; rc = vtoy_json_get_uint(json, "alldev", &alldev); if (JSON_SUCCESS != rc) { alldev = 0; } buflen = g_disk_num * 1024; buf = (char *)malloc(buflen + 1024); if (!buf) { ventoy_json_result(conn, VTOY_JSON_FAILED_RET); return 0; } VTOY_JSON_FMT_BEGIN(pos, buf, buflen); VTOY_JSON_FMT_OBJ_BEGIN(); VTOY_JSON_FMT_KEY("list"); VTOY_JSON_FMT_ARY_BEGIN(); for (i = 0; i < g_disk_num; i++) { cur = g_disk_list + i; if (alldev == 0 && cur->type != VTOY_DEVICE_USB) { continue; } VTOY_JSON_FMT_OBJ_BEGIN(); VTOY_JSON_FMT_STRN("name", cur->disk_name); VTOY_JSON_FMT_STRN("model", cur->disk_model); VTOY_JSON_FMT_STRN("size", cur->human_readable_size); VTOY_JSON_FMT_UINT("vtoy_valid", cur->vtoydata.ventoy_valid); VTOY_JSON_FMT_STRN("vtoy_ver", cur->vtoydata.ventoy_ver); VTOY_JSON_FMT_UINT("vtoy_secure_boot", cur->vtoydata.secure_boot_flag); VTOY_JSON_FMT_UINT("vtoy_partstyle", cur->vtoydata.partition_style); VTOY_JSON_FMT_OBJ_ENDEX(); } VTOY_JSON_FMT_ARY_END(); VTOY_JSON_FMT_OBJ_END(); VTOY_JSON_FMT_END(pos); ventoy_json_buffer(conn, buf, pos); return 0; } static JSON_CB g_ventoy_json_cb[] = { { "sysinfo", ventoy_api_sysinfo }, { "sel_language", ventoy_api_set_language }, { "sel_partstyle", ventoy_api_set_partstyle }, { "refresh_device", ventoy_api_refresh_device }, { "get_dev_list", ventoy_api_get_dev_list }, { "install", ventoy_api_install }, { "update", ventoy_api_update }, { "clean", ventoy_api_clean }, { "get_percent", ventoy_api_get_percent }, }; static int ventoy_json_handler(struct mg_connection *conn, VTOY_JSON *json) { int i; const char *token = NULL; const char *method = NULL; method = vtoy_json_get_string_ex(json, "method"); if (!method) { ventoy_json_result(conn, VTOY_JSON_SUCCESS_RET); return 0; } if (strcmp(method, "sysinfo")) { token = vtoy_json_get_string_ex(json, "token"); if (token == NULL || strcmp(token, g_cur_server_token)) { ventoy_json_result(conn, VTOY_JSON_TOKEN_ERR_RET); return 0; } } for (i = 0; i < (int)(sizeof(g_ventoy_json_cb) / sizeof(g_ventoy_json_cb[0])); i++) { if (strcmp(method, g_ventoy_json_cb[i].method) == 0) { g_ventoy_json_cb[i].callback(conn, json); break; } } return 0; } int ventoy_func_handler(const char *jsonstr, char *jsonbuf, int buflen) { int i; const char *method = NULL; VTOY_JSON *json = NULL; g_pub_out_buf = jsonbuf; g_pub_out_max = buflen; json = vtoy_json_create(); if (JSON_SUCCESS == vtoy_json_parse(json, jsonstr)) { pthread_mutex_lock(&g_api_mutex); method = vtoy_json_get_string_ex(json->pstChild, "method"); for (i = 0; i < (int)(sizeof(g_ventoy_json_cb) / sizeof(g_ventoy_json_cb[0])); i++) { if (method && strcmp(method, g_ventoy_json_cb[i].method) == 0) { g_ventoy_json_cb[i].callback(NULL, json->pstChild); break; } } pthread_mutex_unlock(&g_api_mutex); } else { ventoy_json_result(NULL, VTOY_JSON_INVALID_RET); } vtoy_json_destroy(json); return 0; } static int ventoy_request_handler(struct mg_connection *conn) { int post_data_len; int post_buf_len; VTOY_JSON *json = NULL; char *post_data_buf = NULL; const struct mg_request_info *ri = NULL; char stack_buf[512]; ri = mg_get_request_info(conn); if (strcmp(ri->uri, "/vtoy/json") == 0) { if (ri->content_length > 500) { post_data_buf = malloc(ri->content_length + 4); post_buf_len = ri->content_length + 1; } else { post_data_buf = stack_buf; post_buf_len = sizeof(stack_buf); } post_data_len = mg_read(conn, post_data_buf, post_buf_len); post_data_buf[post_data_len] = 0; json = vtoy_json_create(); if (JSON_SUCCESS == vtoy_json_parse(json, post_data_buf)) { pthread_mutex_lock(&g_api_mutex); ventoy_json_handler(conn, json->pstChild); pthread_mutex_unlock(&g_api_mutex); } else { ventoy_json_result(conn, VTOY_JSON_INVALID_RET); } vtoy_json_destroy(json); if (post_data_buf != stack_buf) { free(post_data_buf); } return 1; } else { return 0; } } int ventoy_http_start(const char *ip, const char *port) { uint8_t uuid[16]; char addr[128]; struct mg_callbacks callbacks; const char *options[] = { "listening_ports", "24680", "document_root", "WebUI", "error_log_file", g_log_file, "request_timeout_ms", "10000", NULL }; /* unique token */ ventoy_gen_preudo_uuid(uuid); scnprintf(g_cur_server_token, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7], uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15]); /* option */ scnprintf(addr, "%s:%s", ip, port); options[1] = addr; memset(&callbacks, 0, sizeof(callbacks)); callbacks.begin_request = ventoy_request_handler; g_ventoy_http_ctx = mg_start(&callbacks, NULL, options); return g_ventoy_http_ctx ? 0 : 1; } int ventoy_http_stop(void) { if (g_ventoy_http_ctx) { mg_stop(g_ventoy_http_ctx); } return 0; } int ventoy_http_init(void) { pthread_mutex_init(&g_api_mutex, NULL); ventoy_http_load_cfg(); ventoy_load_mbr_template(); return 0; } void ventoy_http_exit(void) { pthread_mutex_destroy(&g_api_mutex); check_free(g_efi_part_raw_img); g_efi_part_raw_img = NULL; } const char * ventoy_code_get_cur_language(void) { return g_cur_language; } int ventoy_code_get_cur_part_style(void) { return g_cur_part_style; } void ventoy_code_set_cur_part_style(int style) { pthread_mutex_lock(&g_api_mutex); g_cur_part_style = style; ventoy_http_save_cfg(); pthread_mutex_unlock(&g_api_mutex); } int ventoy_code_get_cur_show_all(void) { return g_cur_show_all; } void ventoy_code_set_cur_show_all(int show_all) { pthread_mutex_lock(&g_api_mutex); g_cur_show_all = show_all; ventoy_http_save_cfg(); pthread_mutex_unlock(&g_api_mutex); } void ventoy_code_set_cur_language(const char *lang) { pthread_mutex_lock(&g_api_mutex); scnprintf(g_cur_language, "%s", lang); ventoy_http_save_cfg(); pthread_mutex_unlock(&g_api_mutex); } void ventoy_code_refresh_device(void) { if (g_current_progress == PT_FINISH) { g_disk_num = 0; ventoy_disk_enumerate_all(); } } int ventoy_code_is_busy(void) { return (g_current_progress == PT_FINISH) ? 0 : 1; } int ventoy_code_get_percent(void) { return g_current_progress * 100 / PT_FINISH; } int ventoy_code_get_result(void) { return g_cur_process_result; } void ventoy_code_save_cfg(void) { ventoy_http_save_cfg(); }