#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <unistd.h> | |
#include <fcntl.h> | |
#include <errno.h> | |
#define BUFFER_SIZE (1024 * 20) // Define the buffer size for each read | |
#define FIND_STR_LEN 128 // Define the length of the string to find | |
#define FOTA_UPDATE_STATUS_FILE "/cache/zte_fota/update_status" | |
// Custom memmem function | |
static void* custom_memmem(const void* haystack, size_t haystack_len, const void* needle, size_t needle_len) | |
{ | |
if (needle_len == 0 || haystack_len < needle_len) | |
{ | |
return NULL; | |
} | |
size_t i = 0; | |
const unsigned char* h = (const unsigned char*)haystack; | |
const unsigned char* n = (const unsigned char*)needle; | |
for (i = 0; i <= haystack_len - needle_len; ++i) | |
{ | |
if (memcmp(h + i, n, needle_len) == 0) | |
{ | |
return (void*)(h + i); | |
} | |
} | |
return NULL; | |
} | |
// Remove the last boundary marker from the file | |
static int remove_last_boundary(const char *filename, const char *boundary_marker, int boundary_marker_len) | |
{ | |
int fd = open(filename, O_RDWR); | |
if (fd == -1) | |
{ | |
perror("Unable to open file for reading and writing"); | |
return 1; | |
} | |
// Get the file size | |
off_t file_size = lseek(fd, 0, SEEK_END); | |
if (file_size == -1) | |
{ | |
perror("Unable to get file size"); | |
close(fd); | |
return 1; | |
} | |
// Search for the boundary_marker from the end of the file | |
off_t search_start = file_size - FIND_STR_LEN; | |
if (search_start < 0) | |
{ | |
search_start = 0; | |
} | |
lseek(fd, search_start, SEEK_SET); | |
char buffer[FIND_STR_LEN + 1]; // +1 for storing '\0' | |
int bytes_read = read(fd, buffer, FIND_STR_LEN); | |
if (bytes_read <= 0) | |
{ | |
perror("Unable to read file"); | |
close(fd); | |
return 1; | |
} | |
buffer[bytes_read] = '\0'; // Ensure the string is null-terminated | |
// Find the boundary_marker | |
char *boundary = custom_memmem(buffer, bytes_read, boundary_marker, boundary_marker_len); | |
if (boundary) | |
{ | |
// Found boundary_marker, calculate the truncate position | |
off_t truncate_pos = search_start + (boundary - buffer); | |
if (ftruncate(fd, truncate_pos) == -1) | |
{ | |
perror("Unable to truncate file"); | |
close(fd); | |
return 1; | |
} | |
} | |
close(fd); | |
return 0; | |
} | |
// Function to handle file upload | |
static int handle_file_upload(const char *source_filename, const char *target_filename, int content_length) | |
{ | |
char buffer[BUFFER_SIZE]; | |
int bytes_read; | |
int target_fd = -1; | |
int source_fd = -1; // Source file descriptor | |
char *content_start_ptr = NULL; | |
int content_start = 0; | |
const char *boundary_marker = "\r\n------WebKitFormBoundary"; // Boundary marker | |
int boundary_marker_len = strlen(boundary_marker); | |
// Open the target file | |
target_fd = open(target_filename, O_WRONLY | O_CREAT | O_TRUNC, 0644); | |
if (target_fd == -1) | |
{ | |
perror("Unable to open target file"); | |
return 1; | |
} | |
// Open the source file | |
source_fd = open(source_filename, O_RDONLY); | |
if (source_fd == -1) | |
{ | |
perror("Unable to open source file"); | |
close(target_fd); | |
return 1; | |
} | |
// Read the request header to find the start position of file content | |
bytes_read = read(source_fd, buffer, BUFFER_SIZE); | |
if (bytes_read <= 0) | |
{ | |
printf("Content-type: text/html\r\n\r\n"); | |
printf("<h1>Error: Failed to read data</h1>"); | |
printf("<p>Error number: %d - %s</p>", errno, strerror(errno)); | |
close(target_fd); | |
close(source_fd); | |
return 1; | |
} | |
// Find the start position of the file content (usually after \r\n\r\n) | |
content_start_ptr = strstr(buffer, "\r\n\r\n"); | |
if (!content_start_ptr) | |
{ | |
printf("Content-type: text/html\r\n\r\n"); | |
printf("<h1>Error: Unable to find the start position of file content</h1>"); | |
close(target_fd); | |
close(source_fd); | |
return 1; | |
} | |
// Calculate the actual start position of the file content | |
content_start = content_start_ptr - buffer + 4; | |
// Write the first part of the file content | |
if (content_start < bytes_read) | |
{ | |
if (write(target_fd, buffer + content_start, bytes_read - content_start) == -1) | |
{ | |
perror("Unable to write to target file"); | |
close(target_fd); | |
close(source_fd); | |
return 1; | |
} | |
} | |
// Update the remaining content length | |
content_length -= (bytes_read - content_start); | |
// Continue reading and writing the file in segments | |
while (content_length > 0) | |
{ | |
bytes_read = read(source_fd, buffer, BUFFER_SIZE); | |
if (bytes_read <= 0) | |
{ | |
// If reading is complete, normally break out of the loop | |
break; | |
} | |
// Write the file content | |
if (write(target_fd, buffer, bytes_read) == -1) | |
{ | |
perror("Unable to write to target file"); | |
close(target_fd); | |
close(source_fd); | |
return 1; | |
} | |
// Update the remaining content length | |
content_length -= bytes_read; | |
} | |
// Close file descriptors | |
close(target_fd); | |
close(source_fd); | |
// Remove the last boundary marker from the file | |
return remove_last_boundary(target_filename, boundary_marker, boundary_marker_len); | |
} | |
static int fota_is_file_exist(const char* path) | |
{ | |
if ( (path == NULL) || (*path == '\0') ) | |
return 0; | |
if (access(path, R_OK) != 0) | |
return 0; | |
return 1; | |
} | |
static int fota_read_file(const char*path, char*buf, size_t sz) | |
{ | |
int fd = -1; | |
size_t cnt; | |
fd = open(path, O_RDONLY, 0); | |
if(fd < 0) | |
{ | |
printf("fota_read_file failed to open %s: %s\n", path, strerror(errno)); | |
cnt = -1; | |
return cnt; | |
} | |
cnt = read(fd, buf, sz - 1); | |
if(cnt <= 0) | |
{ | |
printf("failed to read %s: %s\n", path, strerror(errno)); | |
close(fd); | |
cnt = -1; | |
return cnt; | |
} | |
buf[cnt] = '\0'; | |
if(buf[cnt - 1] == '\n') | |
{ | |
cnt--; | |
buf[cnt] = '\0'; | |
} | |
close(fd); | |
return cnt; | |
} | |
static int fota_read_file_int(const char* path, int *val) | |
{ | |
char buf[32]; | |
char *end; | |
int ret; | |
int tmp; | |
ret = fota_read_file(path, buf, sizeof(buf)); | |
if(ret < 0) | |
return -1; | |
errno = 0; | |
tmp = strtol(buf, &end, 0); | |
if (errno == ERANGE) | |
{ | |
printf("strtol errno %d: %s\n", errno, strerror(errno)); | |
} | |
if ((end == buf) || ((end < buf + sizeof(buf)) && (*end != '\0'))) | |
{ | |
return -1; | |
} | |
*val = tmp; | |
return 0; | |
} | |
static int fota_get_update_status(int *fota_status) | |
{ | |
int status = 0; | |
int ret = 0; | |
if(!fota_is_file_exist(FOTA_UPDATE_STATUS_FILE)) | |
{ | |
*fota_status = -1; | |
return -1; | |
} | |
ret = fota_read_file_int(FOTA_UPDATE_STATUS_FILE, &status); | |
if(ret < 0) | |
{ | |
*fota_status = -1; | |
return -1; | |
} | |
*fota_status = status; | |
return 0; | |
} | |
static void print_json_response(int success, const char* message) | |
{ | |
printf("Content-type: application/json\r\n\r\n"); | |
printf("{\"success\": %d, \"message\": \"%s\"}\n", success, message); | |
} | |
static void cgi_fota_update_progress() | |
{ | |
int upgradeStatus, result; | |
system("fota_upi -u verify > /dev/null 2>&1"); | |
result = fota_get_update_status(&upgradeStatus); | |
if(result < 0) | |
{ | |
print_json_response(0, "Fail to read update file"); | |
} | |
else if(upgradeStatus != 0) | |
{ | |
print_json_response(0, "Verify update file failed"); | |
} | |
else | |
{ | |
print_json_response(1, "File verification successful, start updating..."); | |
sleep(1); | |
system("fota_upi -u recovery > /dev/null 2>&1 &"); | |
} | |
} | |
int main() | |
{ | |
const char *source_filename = "/tmp/firmware_tmp_file"; // Source file path | |
const char *target_filename = "/cache/zte_fota/delta.package"; // Target file path | |
const char *content_length_str = NULL; | |
int content_length = 0; | |
system("rm -rf /cache/zte_fota"); | |
system("mkdir -p /cache/zte_fota"); | |
// Get environment variables | |
content_length_str = getenv("CONTENT_LENGTH"); | |
if (!content_length_str) | |
{ | |
system("rm -rf /tmp/firmware_tmp_file"); | |
print_json_response(0, "Missing CONTENT_LENGTH environment variable"); | |
return 1; | |
} | |
content_length = atoi(content_length_str); | |
if (content_length <= 0) | |
{ | |
system("rm -rf /tmp/firmware_tmp_file"); | |
print_json_response(0, "Invalid CONTENT_LENGTH"); | |
return 1; | |
} | |
// Call the file upload handling function | |
if (handle_file_upload(source_filename, target_filename, content_length) != 0) | |
{ | |
system("rm -rf /tmp/firmware_tmp_file"); | |
print_json_response(0, "File upload failed"); | |
return 1; | |
} | |
// Output success information | |
//print_json_response(1, "File upload successful"); | |
system("rm -rf /tmp/firmware_tmp_file"); | |
cgi_fota_update_progress(); | |
return 0; | |
} |