Skip to content

Commit 2aa8e0a

Browse files
committed
OTA partition table upgrade review results:
- add erasing the flash region to store upgrade to ensure clean start and/or reformat in case of a corrupted filesystem - add store2 unmount to store downgrade - add file size check to file copy to detect changes and a common case of /store filesystem corruptions (metadata mismatch)
1 parent b7d9ad0 commit 2aa8e0a

2 files changed

Lines changed: 107 additions & 78 deletions

File tree

vehicle/OVMS.V3/components/ovms_ota/src/ovms_partitions.cpp

Lines changed: 105 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -377,8 +377,17 @@ bool ovms_partition_table_upgrade_store(OvmsWriter* writer)
377377
OVMS_MD5_Update(&md5_ctx, (const uint8_t*)p, sizeof(ovms_esp_partition_t));
378378
writer->puts("Added new store2 partition to partition table");
379379

380+
// Erase the region to ensure a clean start with auto reformatting on mount
381+
writer->printf("Erasing store2 data (%" PRId32 " bytes)...\n", p->data.entry.size);
382+
if (spi_flash_erase_range(p->data.entry.address, p->data.entry.size) != ESP_OK)
383+
{
384+
writer->puts("Warning: erasing store2 data failed, may need manual erase/reflash if formatting fails");
385+
// This is only an issue if store2 is also corrupted and doesn't trigger a reformat
386+
// on the following mount attempt -- in this case the user will need to reflash via USB
387+
}
388+
380389
// Then we need to add the MD5 checksum record
381-
p++;
390+
p++; // safe to assume we have one of the max 96 partition slots left to use
382391
ovms_partition_table_void_entry(p);
383392
p->magic_id = PARTITION_MD5_MAGIC;
384393
OVMS_MD5_Final(p->data.md5.digest, &md5_ctx);
@@ -411,6 +420,11 @@ bool ovms_partition_table_downgrade_store(OvmsWriter* writer)
411420
return false;
412421
}
413422

423+
if (ovms_store2_mounted)
424+
{
425+
ovms_partition_table_unmount_store2(writer);
426+
}
427+
414428
size_t offset = ovms_partition_table_find();
415429
if (offset == 0)
416430
{
@@ -430,22 +444,23 @@ bool ovms_partition_table_downgrade_store(OvmsWriter* writer)
430444
OVMS_MD5_Init(&md5_ctx);
431445
for (; p->magic_id == PARTITION_ENTRY_MAGIC; p++)
432446
{
433-
if (p->magic_id == PARTITION_ENTRY_MAGIC &&
434-
p->data.entry.type == ESP_PARTITION_TYPE_DATA &&
447+
if (p->data.entry.type == ESP_PARTITION_TYPE_DATA &&
435448
p->data.entry.subtype == ESP_PARTITION_SUBTYPE_DATA_FAT &&
436449
p->data.entry.address == 0xe10000 &&
437450
p->data.entry.size == 0x1f0000)
438451
{
439452
// We have found the store2 partition
440453
writer->puts("Removing store2 partition from partition table");
441454
memset((void*)p, 0, sizeof(ovms_esp_partition_t));
455+
// safe to assume this is our last partition, so we can assign this slot to the terminating MD5 checksum:
442456
p->magic_id = PARTITION_MD5_MAGIC;
443457
OVMS_MD5_Final(p->data.md5.digest, &md5_ctx);
444458
p++; ovms_partition_table_void_entry(p); // Clear the following redundant MD5 checksum record
445459
if (ovms_partition_table_rewrite(table, writer))
446460
{
447461
writer->puts("Store partition downgraded successfully - reboot required");
448462
partition_table_type = OVMS_FlashPartition_30;
463+
ovms_partition_table_free(table);
449464
return true;
450465
}
451466
}
@@ -596,7 +611,7 @@ static bool copy_store_ensure_dir(const std::string& dst, OvmsWriter* writer, un
596611

597612
// Copy a single file from the source to the destination
598613
static void copy_store_one_file(OvmsWriter* writer, const std::string& src, const std::string& dst,
599-
uint8_t* buf, size_t buf_size, unsigned& files_copied, unsigned& errors)
614+
off_t expected_size, uint8_t* buf, size_t buf_size, unsigned& files_copied, unsigned& errors)
600615
{
601616
struct stat st_src;
602617
if (stat(src.c_str(), &st_src) != 0)
@@ -615,12 +630,19 @@ static void copy_store_one_file(OvmsWriter* writer, const std::string& src, cons
615630
struct stat st_dst;
616631
if (stat(dst.c_str(), &st_dst) == 0)
617632
{
618-
if (S_ISREG(st_dst.st_mode)) return;
619-
writer->printf("Error: destination exists but is not a file: %s\n", dst.c_str());
620-
errors++;
621-
return;
633+
if (!S_ISREG(st_dst.st_mode))
634+
{
635+
writer->printf("Error: destination exists but is not a file: %s\n", dst.c_str());
636+
errors++;
637+
return;
638+
}
639+
else if (st_dst.st_size == expected_size)
640+
{
641+
// skip, file already copied (Note: won't detect same size changes)
642+
return;
643+
}
622644
}
623-
if (errno != ENOENT)
645+
else if (errno != ENOENT)
624646
{
625647
writer->printf("Error: stat %s: %s\n", dst.c_str(), strerror(errno));
626648
errors++;
@@ -647,6 +669,7 @@ static void copy_store_one_file(OvmsWriter* writer, const std::string& src, cons
647669
writer->printf("Copy file: %s -> %s\n", src.c_str(), dst.c_str());
648670

649671
bool io_ok = true;
672+
off_t copied_size = 0;
650673
for (;;)
651674
{
652675
ssize_t nr = read(fd_in, buf, buf_size);
@@ -676,6 +699,7 @@ static void copy_store_one_file(OvmsWriter* writer, const std::string& src, cons
676699
off += (size_t)nw;
677700
}
678701
if (!io_ok) break;
702+
copied_size += nr;
679703
}
680704

681705
if (close(fd_in) != 0)
@@ -696,6 +720,13 @@ static void copy_store_one_file(OvmsWriter* writer, const std::string& src, cons
696720
io_ok = false;
697721
}
698722

723+
if (io_ok && copied_size != expected_size)
724+
{
725+
writer->printf("Error: size mismatch on %s: %ld bytes copied, %ld expected (/store corrupted)\n",
726+
src.c_str(), copied_size, expected_size);
727+
io_ok = false;
728+
}
729+
699730
if (!io_ok)
700731
{
701732
if (unlink(dst.c_str()) != 0 && errno != ENOENT)
@@ -837,7 +868,7 @@ bool ovms_partition_table_copy_store(OvmsWriter* writer)
837868
}
838869
else if (S_ISREG(st_ent.st_mode))
839870
{
840-
copy_store_one_file(writer, child_src, child_dst, buf, kBufSize, files_copied, errors);
871+
copy_store_one_file(writer, child_src, child_dst, st_ent.st_size, buf, kBufSize, files_copied, errors);
841872
}
842873
else
843874
{
@@ -854,6 +885,7 @@ bool ovms_partition_table_copy_store(OvmsWriter* writer)
854885
}
855886

856887
free(buf);
888+
857889
writer->printf("Copy finished: %u files copied, %u errors\n", files_copied, errors);
858890
return (errors == 0);
859891
}
@@ -923,78 +955,75 @@ bool ovms_partition_table_upgrade_factory(OvmsWriter* writer)
923955
OVMS_MD5_Init(&md5_ctx);
924956
for (; p->magic_id == PARTITION_ENTRY_MAGIC; p++)
925957
{
926-
if (p->magic_id == PARTITION_ENTRY_MAGIC)
958+
if (p->data.entry.type == ESP_PARTITION_TYPE_APP && p->data.entry.subtype == ESP_PARTITION_SUBTYPE_APP_FACTORY)
927959
{
928-
if (p->data.entry.type == ESP_PARTITION_TYPE_APP && p->data.entry.subtype == ESP_PARTITION_SUBTYPE_APP_FACTORY)
929-
{
930-
// We have found the factory partition, so we need to change it to OTA 0
931-
factory_offset = p->data.entry.address;
932-
p->data.entry.subtype = ESP_PARTITION_SUBTYPE_APP_OTA_0;
933-
p->data.entry.size = 0x700000;
934-
memset((void*)p->data.entry.label, 0, sizeof(p->data.entry.label));
935-
strncpy(p->data.entry.label, "ota_0", sizeof(p->data.entry.label));
936-
OVMS_MD5_Update(&md5_ctx, (const uint8_t*)p, sizeof(ovms_esp_partition_t));
937-
writer->printf("0x%08" PRIx32 " Converted factory partition to 6MB OTA 0\n", factory_offset);
938-
}
939-
else if (p->data.entry.type == ESP_PARTITION_TYPE_APP && p->data.entry.subtype == ESP_PARTITION_SUBTYPE_APP_OTA_0)
940-
{
941-
// We have found the OTA 0 partition, so we need to change it to OTA 1
942-
ota1_offset = factory_offset + 0x700000;
943-
p->data.entry.subtype = ESP_PARTITION_SUBTYPE_APP_OTA_1;
944-
p->data.entry.address = ota1_offset;
945-
p->data.entry.size = 0x700000;
946-
memset((void*)p->data.entry.label, 0, sizeof(p->data.entry.label));
947-
strncpy(p->data.entry.label, "ota_1", sizeof(p->data.entry.label));
948-
OVMS_MD5_Update(&md5_ctx, (const uint8_t*)p, sizeof(ovms_esp_partition_t));
949-
writer->printf("0x%08" PRIx32 " Converted OTA 0 partition to 6MB OTA 1\n", ota1_offset);
950-
}
951-
else if (p->data.entry.type == ESP_PARTITION_TYPE_APP && p->data.entry.subtype == ESP_PARTITION_SUBTYPE_APP_OTA_1)
960+
// We have found the factory partition, so we need to change it to OTA 0
961+
factory_offset = p->data.entry.address;
962+
p->data.entry.subtype = ESP_PARTITION_SUBTYPE_APP_OTA_0;
963+
p->data.entry.size = 0x700000;
964+
memset((void*)p->data.entry.label, 0, sizeof(p->data.entry.label));
965+
strncpy(p->data.entry.label, "ota_0", sizeof(p->data.entry.label));
966+
OVMS_MD5_Update(&md5_ctx, (const uint8_t*)p, sizeof(ovms_esp_partition_t));
967+
writer->printf("0x%08" PRIx32 " Converted factory partition to 6MB OTA 0\n", factory_offset);
968+
}
969+
else if (p->data.entry.type == ESP_PARTITION_TYPE_APP && p->data.entry.subtype == ESP_PARTITION_SUBTYPE_APP_OTA_0)
970+
{
971+
// We have found the OTA 0 partition, so we need to change it to OTA 1
972+
ota1_offset = factory_offset + 0x700000; // safe to assume we found the factory_offset here
973+
p->data.entry.subtype = ESP_PARTITION_SUBTYPE_APP_OTA_1;
974+
p->data.entry.address = ota1_offset;
975+
p->data.entry.size = 0x700000;
976+
memset((void*)p->data.entry.label, 0, sizeof(p->data.entry.label));
977+
strncpy(p->data.entry.label, "ota_1", sizeof(p->data.entry.label));
978+
OVMS_MD5_Update(&md5_ctx, (const uint8_t*)p, sizeof(ovms_esp_partition_t));
979+
writer->printf("0x%08" PRIx32 " Converted OTA 0 partition to 6MB OTA 1\n", ota1_offset);
980+
}
981+
else if (p->data.entry.type == ESP_PARTITION_TYPE_APP && p->data.entry.subtype == ESP_PARTITION_SUBTYPE_APP_OTA_1)
982+
{
983+
// We have found the old OTA 1 partition, so now have
984+
// p[0] -> OTA_1
985+
// p[1] -> old store
986+
// p[2] -> new store
987+
// p[3] -> MD5 checksum
988+
// We need to set this as the new store, the one after as the checksum, and then clear the two following that
989+
memcpy((void*)p, (void*)&p[2], sizeof(ovms_esp_partition_t));
990+
memset((void*)p->data.entry.label, 0, sizeof(p->data.entry.label));
991+
strncpy(p->data.entry.label, "store", sizeof(p->data.entry.label));
992+
OVMS_MD5_Update(&md5_ctx, (const uint8_t*)p, sizeof(ovms_esp_partition_t));
993+
writer->printf("0x%08" PRIx32 " Moved %s/%s partition up two positions\n",
994+
p->data.entry.address,
995+
ovms_partition_type_name((esp_partition_type_t)p->data.entry.type),
996+
ovms_partition_subtype_name((esp_partition_type_t)p->data.entry.type, (esp_partition_subtype_t)p->data.entry.subtype));
997+
p++; // now @ old store, make this the new MD5 checksum slot:
998+
ovms_partition_table_void_entry(p);
999+
p->magic_id = PARTITION_MD5_MAGIC;
1000+
OVMS_MD5_Final(p->data.md5.digest, &md5_ctx);
1001+
writer->puts(" Recalculated MD5 checksum");
1002+
ovms_partition_table_void_entry(&p[1]); // void previous new store
1003+
ovms_partition_table_void_entry(&p[2]); // void previous MD5 checksum
1004+
if (ovms_partition_table_rewrite(table, writer))
9521005
{
953-
// We have found the old OTA 1 partition, so now have
954-
// p[0] -> OTA_1
955-
// p[1] -> old store
956-
// p[2] -> new store
957-
// p[3] -> MD5 checksum
958-
// We need to set this as the new store, the one after as the checksum, and then clear the two following that
959-
memcpy((void*)p, (void*)&p[2], sizeof(ovms_esp_partition_t));
960-
memset((void*)p->data.entry.label, 0, sizeof(p->data.entry.label));
961-
strncpy(p->data.entry.label, "store", sizeof(p->data.entry.label));
962-
OVMS_MD5_Update(&md5_ctx, (const uint8_t*)p, sizeof(ovms_esp_partition_t));
963-
writer->printf("0x%08" PRIx32 " Moved %s/%s partition up two positions\n",
964-
p->data.entry.address,
965-
ovms_partition_type_name((esp_partition_type_t)p->data.entry.type),
966-
ovms_partition_subtype_name((esp_partition_type_t)p->data.entry.type, (esp_partition_subtype_t)p->data.entry.subtype));
967-
p++;
968-
ovms_partition_table_void_entry(p);
969-
p->magic_id = PARTITION_MD5_MAGIC;
970-
OVMS_MD5_Final(p->data.md5.digest, &md5_ctx);
971-
writer->puts(" Recalculated MD5 checksum");
972-
ovms_partition_table_void_entry(&p[1]);
973-
ovms_partition_table_void_entry(&p[2]);
974-
if (ovms_partition_table_rewrite(table, writer))
975-
{
976-
writer->puts("Partition table upgraded successfully - reboot required");
977-
partition_table_type = OVMS_FlashPartition_35;
978-
ovms_partition_table_free(table);
979-
return true;
980-
}
981-
else
982-
{
983-
ovms_partition_table_free(table);
984-
writer->puts("Error: Failed to upgrade partition table");
985-
return false;
986-
}
1006+
writer->puts("Partition table upgraded successfully - reboot required");
1007+
partition_table_type = OVMS_FlashPartition_35;
1008+
ovms_partition_table_free(table);
1009+
return true;
9871010
}
9881011
else
9891012
{
990-
// Just update the MD5 checksum
991-
OVMS_MD5_Update(&md5_ctx, (const uint8_t*)p, sizeof(ovms_esp_partition_t));
992-
writer->printf("0x%08" PRIx32 " Skipping over %s/%s partition\n",
993-
p->data.entry.address,
994-
ovms_partition_type_name((esp_partition_type_t)p->data.entry.type),
995-
ovms_partition_subtype_name((esp_partition_type_t)p->data.entry.type, (esp_partition_subtype_t)p->data.entry.subtype));
1013+
ovms_partition_table_free(table);
1014+
writer->puts("Error: Failed to upgrade partition table");
1015+
return false;
9961016
}
9971017
}
1018+
else
1019+
{
1020+
// Just update the MD5 checksum
1021+
OVMS_MD5_Update(&md5_ctx, (const uint8_t*)p, sizeof(ovms_esp_partition_t));
1022+
writer->printf("0x%08" PRIx32 " Skipping over %s/%s partition\n",
1023+
p->data.entry.address,
1024+
ovms_partition_type_name((esp_partition_type_t)p->data.entry.type),
1025+
ovms_partition_subtype_name((esp_partition_type_t)p->data.entry.type, (esp_partition_subtype_t)p->data.entry.subtype));
1026+
}
9981027
}
9991028

10001029
ovms_partition_table_free(table);
@@ -1033,4 +1062,4 @@ bool ovms_partition_table_rewrite(ovms_esp_partition_t* table, OvmsWriter* write
10331062
void ovms_partition_table_void_entry(ovms_esp_partition_t* entry)
10341063
{
10351064
memset((void*)entry, 0xff, sizeof(ovms_esp_partition_t));
1036-
}
1065+
}

vehicle/OVMS.V3/components/ovms_ota/src/ovms_partitions.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
#include <esp_partition.h>
3636
#include "ovms_command.h"
3737

38-
#define PARTITION_TABLE_SIZE 0x0C00 // Maximum size of the partition table in flash (32 bytes per partition record)
38+
#define PARTITION_TABLE_SIZE 0x0C00 // Maximum size of the partition table (32 bytes per partition record x 96 partitions)
3939
#define PARTITION_TABLE_BLOCK_SIZE 0x1000 // Size allocated for the partition table in flash
4040
#define PARTITION_TABLE_RECORD_SIZE 32 // Size of the partition table record (32 bytes)
4141
#define PARTITION_ENTRY_MAGIC 0x50aa
@@ -72,7 +72,7 @@ typedef enum
7272
{
7373
OVMS_FlashPartition_Unknown, // Unknown partition format (or not yet discovered)
7474
OVMS_FlashPartition_30, // Original v3 partition format (factory, ota1, ota2, 1MB store)
75-
OVMS_FlashPartition_34, // Original v3 partition format (factory, ota1, ota2, dual store)
75+
OVMS_FlashPartition_34, // Transitory in-upgrade partition format (factory, ota1, ota2, dual store)
7676
OVMS_FlashPartition_35, // New partition format (ota1, ota2, no factory, maximized store)
7777
} ovms_flashpartition_t;
7878

0 commit comments

Comments
 (0)