/** * Copyright (c) 2011-2022 Bill Greiman * This file is part of the SdFat library for SD memory cards. * * MIT License * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include "../common/upcase.h" #include "ExFatLib.h" #include "ExFatVolume.h" #ifndef DOXYGEN_SHOULD_SKIP_THIS //------------------------------------------------------------------------------ static void printHex(print_t* pr, uint8_t h); static void printHex(print_t* pr, uint16_t val); static void printHex(print_t* pr, uint32_t val); static void printHex64(print_t* pr, uint64_t n); static void println64(print_t* pr, uint64_t n); //------------------------------------------------------------------------------ static void dmpDirData(print_t* pr, DirGeneric_t* dir) { for (uint8_t k = 0; k < 31; k++) { if (k) { pr->write(' '); } printHex(pr, dir->data[k]); } pr->println(); } //------------------------------------------------------------------------------ static uint16_t exFatDirChecksum(const void* dir, uint16_t checksum) { const uint8_t* data = reinterpret_cast(dir); bool skip = data[0] == EXFAT_TYPE_FILE; for (size_t i = 0; i < 32; i += (i == 1 && skip ? 3 : 1)) { checksum = ((checksum << 15) | (checksum >> 1)) + data[i]; } return checksum; } //------------------------------------------------------------------------------ static uint16_t hashDir(DirName_t* dir, uint16_t hash) { for (uint8_t i = 0; i < 30; i += 2) { uint16_t u = getLe16(dir->unicode + i); if (!u) { break; } uint16_t c = toUpcase(u); hash = ((hash << 15) | (hash >> 1)) + (c & 0XFF); hash = ((hash << 15) | (hash >> 1)) + (c >> 8); } return hash; } //------------------------------------------------------------------------------ static void printDateTime(print_t* pr, uint32_t timeDate, uint8_t ms, int8_t tz) { fsPrintDateTime(pr, timeDate, ms, tz); pr->println(); } //------------------------------------------------------------------------------ static void printDirBitmap(print_t* pr, DirBitmap_t* dir) { pr->print(F("dirBitmap: 0x")); pr->println(dir->type, HEX); pr->print(F("flags: 0x")); pr->println(dir->flags, HEX); pr->print(F("firstCluster: ")); pr->println(getLe32(dir->firstCluster)); pr->print(F("size: ")); println64(pr, getLe64(dir->size)); } //------------------------------------------------------------------------------ static void printDirFile(print_t* pr, DirFile_t* dir) { pr->print(F("dirFile: 0x")); pr->println(dir->type, HEX); pr->print(F("setCount: ")); pr->println(dir->setCount); pr->print(F("setChecksum: 0x")); pr->println(getLe16(dir->setChecksum), HEX); pr->print(F("attributes: 0x")); pr->println(getLe16(dir->attributes), HEX); pr->print(F("createTime: ")); printDateTime(pr, getLe32(dir->createTime), dir->createTimeMs, dir->createTimezone); pr->print(F("modifyTime: ")); printDateTime(pr, getLe32(dir->modifyTime), dir->modifyTimeMs, dir->modifyTimezone); pr->print(F("accessTime: ")); printDateTime(pr, getLe32(dir->accessTime), 0, dir->accessTimezone); } //------------------------------------------------------------------------------ static void printDirLabel(print_t* pr, DirLabel_t* dir) { pr->print(F("dirLabel: 0x")); pr->println(dir->type, HEX); pr->print(F("labelLength: ")); pr->println(dir->labelLength); pr->print(F("unicode: ")); for (size_t i = 0; i < dir->labelLength; i++) { pr->write(dir->unicode[2 * i]); } pr->println(); } //------------------------------------------------------------------------------ static void printDirName(print_t* pr, DirName_t* dir) { pr->print(F("dirName: 0x")); pr->println(dir->type, HEX); pr->print(F("unicode: ")); for (size_t i = 0; i < 30; i += 2) { uint16_t c = getLe16(dir->unicode + i); if (c == 0) break; if (c < 128) { pr->print(static_cast(c)); } else { pr->print("0x"); pr->print(c, HEX); } pr->print(' '); } pr->println(); } //------------------------------------------------------------------------------ static void printDirStream(print_t* pr, DirStream_t* dir) { pr->print(F("dirStream: 0x")); pr->println(dir->type, HEX); pr->print(F("flags: 0x")); pr->println(dir->flags, HEX); pr->print(F("nameLength: ")); pr->println(dir->nameLength); pr->print(F("nameHash: 0x")); pr->println(getLe16(dir->nameHash), HEX); pr->print(F("validLength: ")); println64(pr, getLe64(dir->validLength)); pr->print(F("firstCluster: ")); pr->println(getLe32(dir->firstCluster)); pr->print(F("dataLength: ")); println64(pr, getLe64(dir->dataLength)); } //------------------------------------------------------------------------------ static void printDirUpcase(print_t* pr, DirUpcase_t* dir) { pr->print(F("dirUpcase: 0x")); pr->println(dir->type, HEX); pr->print(F("checksum: 0x")); pr->println(getLe32(dir->checksum), HEX); pr->print(F("firstCluster: ")); pr->println(getLe32(dir->firstCluster)); pr->print(F("size: ")); println64(pr, getLe64(dir->size)); } //------------------------------------------------------------------------------ static void printExFatBoot(print_t* pr, pbs_t* pbs) { BpbExFat_t* ebs = reinterpret_cast(pbs->bpb); pr->print(F("bpbSig: 0x")); pr->println(getLe16(pbs->signature), HEX); pr->print(F("FileSystemName: ")); pr->write(reinterpret_cast(pbs->oemName), 8); pr->println(); for (size_t i = 0; i < sizeof(ebs->mustBeZero); i++) { if (ebs->mustBeZero[i]) { pr->println(F("mustBeZero error")); break; } } pr->print(F("PartitionOffset: 0x")); printHex64(pr, getLe64(ebs->partitionOffset)); pr->print(F("VolumeLength: ")); println64(pr, getLe64(ebs->volumeLength)); pr->print(F("FatOffset: 0x")); pr->println(getLe32(ebs->fatOffset), HEX); pr->print(F("FatLength: ")); pr->println(getLe32(ebs->fatLength)); pr->print(F("ClusterHeapOffset: 0x")); pr->println(getLe32(ebs->clusterHeapOffset), HEX); pr->print(F("ClusterCount: ")); pr->println(getLe32(ebs->clusterCount)); pr->print(F("RootDirectoryCluster: ")); pr->println(getLe32(ebs->rootDirectoryCluster)); pr->print(F("VolumeSerialNumber: 0x")); pr->println(getLe32(ebs->volumeSerialNumber), HEX); pr->print(F("FileSystemRevision: 0x")); pr->println(getLe32(ebs->fileSystemRevision), HEX); pr->print(F("VolumeFlags: 0x")); pr->println(getLe16(ebs->volumeFlags), HEX); pr->print(F("BytesPerSectorShift: ")); pr->println(ebs->bytesPerSectorShift); pr->print(F("SectorsPerClusterShift: ")); pr->println(ebs->sectorsPerClusterShift); pr->print(F("NumberOfFats: ")); pr->println(ebs->numberOfFats); pr->print(F("DriveSelect: 0x")); pr->println(ebs->driveSelect, HEX); pr->print(F("PercentInUse: ")); pr->println(ebs->percentInUse); } //------------------------------------------------------------------------------ static void printHex(print_t* pr, uint8_t h) { if (h < 16) { pr->write('0'); } pr->print(h, HEX); } //------------------------------------------------------------------------------ static void printHex(print_t* pr, uint16_t val) { bool space = true; for (uint8_t i = 0; i < 4; i++) { uint8_t h = (val >> (12 - 4 * i)) & 15; if (h || i == 3) { space = false; } if (space) { pr->write(' '); } else { pr->print(h, HEX); } } } //------------------------------------------------------------------------------ static void printHex(print_t* pr, uint32_t val) { bool space = true; for (uint8_t i = 0; i < 8; i++) { uint8_t h = (val >> (28 - 4 * i)) & 15; if (h || i == 7) { space = false; } if (space) { pr->write(' '); } else { pr->print(h, HEX); } } } //------------------------------------------------------------------------------ static void printHex64(print_t* pr, uint64_t n) { char buf[17]; char* str = &buf[sizeof(buf) - 1]; *str = '\0'; do { uint8_t h = n & 15; *--str = h < 10 ? h + '0' : h + 'A' - 10; n >>= 4; } while (n); pr->println(str); } //------------------------------------------------------------------------------ static void println64(print_t* pr, uint64_t n) { char buf[21]; char* str = &buf[sizeof(buf) - 1]; *str = '\0'; do { uint64_t m = n; n /= 10; *--str = m - 10 * n + '0'; } while (n); pr->println(str); } //------------------------------------------------------------------------------ static void printMbr(print_t* pr, MbrSector_t* mbr) { pr->print(F("mbrSig: 0x")); pr->println(getLe16(mbr->signature), HEX); for (int i = 0; i < 4; i++) { printHex(pr, mbr->part[i].boot); pr->write(' '); for (int k = 0; k < 3; k++) { printHex(pr, mbr->part[i].beginCHS[k]); pr->write(' '); } printHex(pr, mbr->part[i].type); pr->write(' '); for (int k = 0; k < 3; k++) { printHex(pr, mbr->part[i].endCHS[k]); pr->write(' '); } pr->print(getLe32(mbr->part[i].relativeSectors), HEX); pr->print(' '); pr->println(getLe32(mbr->part[i].totalSectors), HEX); } } //============================================================================== void ExFatPartition::checkUpcase(print_t* pr) { bool skip = false; uint16_t u = 0; uint8_t* upcase = nullptr; uint32_t size = 0; uint32_t sector = clusterStartSector(m_rootDirectoryCluster); uint8_t* cache = dataCachePrepare(sector, FsCache::CACHE_FOR_READ); if (!cache) { pr->println(F("read root failed")); return; } DirUpcase_t* dir = reinterpret_cast(cache); pr->println(F("\nChecking upcase table")); for (size_t i = 0; i < 16; i++) { if (dir[i].type == EXFAT_TYPE_UPCASE) { sector = clusterStartSector(getLe32(dir[i].firstCluster)); size = getLe64(dir[i].size); break; } } if (!size) { pr->println(F("upcase not found")); return; } for (size_t i = 0; i < size / 2; i++) { if ((i % 256) == 0) { upcase = dataCachePrepare(sector++, FsCache::CACHE_FOR_READ); if (!upcase) { pr->println(F("read upcase failed")); return; } } uint16_t v = getLe16(&upcase[2 * (i & 0XFF)]); if (skip) { pr->print("skip "); pr->print(u); pr->write(' '); pr->println(v); } if (v == 0XFFFF) { skip = true; } else if (skip) { for (uint16_t k = 0; k < v; k++) { uint16_t x = toUpcase(u + k); if (x != (u + k)) { printHex(pr, (uint16_t)(u + k)); pr->write(','); printHex(pr, x); pr->println("<<<<<<<<<<<<<<<<<<<<"); } } u += v; skip = false; } else { uint16_t x = toUpcase(u); if (v != x) { printHex(pr, u); pr->write(','); printHex(pr, x); pr->write(','); printHex(pr, v); pr->println(); } u++; } } pr->println(F("Done checkUpcase")); } //------------------------------------------------------------------------------ void ExFatPartition::dmpBitmap(print_t* pr) { pr->println(F("bitmap:")); dmpSector(pr, m_clusterHeapStartSector); } //------------------------------------------------------------------------------ void ExFatPartition::dmpCluster(print_t* pr, uint32_t cluster, uint32_t offset, uint32_t count) { uint32_t sector = clusterStartSector(cluster) + offset; for (uint32_t i = 0; i < count; i++) { pr->print(F("\nSector: ")); pr->println(sector + i, HEX); dmpSector(pr, sector + i); } } //------------------------------------------------------------------------------ void ExFatPartition::dmpFat(print_t* pr, uint32_t start, uint32_t count) { uint32_t sector = m_fatStartSector + start; uint32_t cluster = 128 * start; pr->println(F("FAT:")); for (uint32_t i = 0; i < count; i++) { uint8_t* cache = dataCachePrepare(sector + i, FsCache::CACHE_FOR_READ); if (!cache) { pr->println(F("cache read failed")); return; } uint32_t* fat = reinterpret_cast(cache); for (size_t k = 0; k < 128; k++) { if (0 == cluster % 8) { if (k) { pr->println(); } printHex(pr, cluster); } cluster++; pr->write(' '); printHex(pr, fat[k]); } pr->println(); } } //------------------------------------------------------------------------------ void ExFatPartition::dmpSector(print_t* pr, uint32_t sector) { uint8_t* cache = dataCachePrepare(sector, FsCache::CACHE_FOR_READ); if (!cache) { pr->println(F("dmpSector failed")); return; } for (uint16_t i = 0; i < m_bytesPerSector; i++) { if (i % 32 == 0) { if (i) { pr->println(); } printHex(pr, i); } pr->write(' '); printHex(pr, cache[i]); } pr->println(); } //------------------------------------------------------------------------------ bool ExFatPartition::printDir(print_t* pr, ExFatFile* file) { DirGeneric_t* dir = nullptr; DirFile_t* dirFile; DirStream_t* dirStream; DirName_t* dirName; uint16_t calcHash = 0; uint16_t nameHash = 0; uint16_t setChecksum = 0; uint16_t calcChecksum = 0; uint8_t nameLength = 0; uint8_t setCount = 0; uint8_t nUnicode; #define RAW_ROOT #ifndef RAW_ROOT while (1) { uint8_t buf[FS_DIR_SIZE]; if (file->read(buf, FS_DIR_SIZE) != FS_DIR_SIZE) { break; } dir = reinterpret_cast(buf); #else // RAW_ROOT (void)file; uint32_t nDir = 1UL << (m_sectorsPerClusterShift + 4); uint32_t sector = clusterStartSector(m_rootDirectoryCluster); for (uint32_t iDir = 0; iDir < nDir; iDir++) { size_t i = iDir % 16; if (i == 0) { uint8_t* cache = dataCachePrepare(sector++, FsCache::CACHE_FOR_READ); if (!cache) { return false; } dir = reinterpret_cast(cache); } else { dir++; } #endif // RAW_ROOT if (dir->type == EXFAT_TYPE_END_DIR) { break; } pr->println(); switch (dir->type) { case EXFAT_TYPE_BITMAP: printDirBitmap(pr, reinterpret_cast(dir)); break; case EXFAT_TYPE_UPCASE: printDirUpcase(pr, reinterpret_cast(dir)); break; case EXFAT_TYPE_LABEL: printDirLabel(pr, reinterpret_cast(dir)); break; case EXFAT_TYPE_FILE: dirFile = reinterpret_cast(dir); printDirFile(pr, dirFile); setCount = dirFile->setCount; setChecksum = getLe16(dirFile->setChecksum); calcChecksum = exFatDirChecksum(dir, 0); break; case EXFAT_TYPE_STREAM: dirStream = reinterpret_cast(dir); printDirStream(pr, dirStream); nameLength = dirStream->nameLength; nameHash = getLe16(dirStream->nameHash); calcChecksum = exFatDirChecksum(dir, calcChecksum); setCount--; calcHash = 0; break; case EXFAT_TYPE_NAME: dirName = reinterpret_cast(dir); printDirName(pr, dirName); calcChecksum = exFatDirChecksum(dir, calcChecksum); nUnicode = nameLength > 15 ? 15 : nameLength; calcHash = hashDir(dirName, calcHash); nameLength -= nUnicode; setCount--; if (nameLength == 0 || setCount == 0) { pr->print(F("setChecksum: 0x")); pr->print(setChecksum, HEX); if (setChecksum != calcChecksum) { pr->print(F(" != calcChecksum: 0x")); } else { pr->print(F(" == calcChecksum: 0x")); } pr->println(calcChecksum, HEX); pr->print(F("nameHash: 0x")); pr->print(nameHash, HEX); if (nameHash != calcHash) { pr->print(F(" != calcHash: 0x")); } else { pr->print(F(" == calcHash: 0x")); } pr->println(calcHash, HEX); } break; default: if (dir->type & EXFAT_TYPE_USED) { pr->print(F("Unknown dirType: 0x")); } else { pr->print(F("Unused dirType: 0x")); } pr->println(dir->type, HEX); dmpDirData(pr, dir); break; } } pr->println(F("Done")); return true; } //------------------------------------------------------------------------------ void ExFatPartition::printFat(print_t* pr) { uint32_t next; pr->println(F("FAT:")); for (uint32_t cluster = 0; cluster < 16; cluster++) { int8_t status = fatGet(cluster, &next); pr->print(cluster, HEX); pr->write(' '); if (status == 0) { next = EXFAT_EOC; } pr->println(next, HEX); } } //------------------------------------------------------------------------------ void ExFatPartition::printUpcase(print_t* pr) { uint8_t* upcase = nullptr; uint32_t sector; uint32_t size = 0; uint32_t checksum = 0; DirUpcase_t* dir; sector = clusterStartSector(m_rootDirectoryCluster); upcase = dataCachePrepare(sector, FsCache::CACHE_FOR_READ); dir = reinterpret_cast(upcase); if (!dir) { pr->println(F("read root dir failed")); return; } for (size_t i = 0; i < 16; i++) { if (dir[i].type == EXFAT_TYPE_UPCASE) { sector = clusterStartSector(getLe32(dir[i].firstCluster)); size = getLe64(dir[i].size); break; } } if (!size) { pr->println(F("upcase not found")); return; } for (uint16_t i = 0; i < size / 2; i++) { if ((i % 256) == 0) { upcase = dataCachePrepare(sector++, FsCache::CACHE_FOR_READ); if (!upcase) { pr->println(F("read upcase failed")); return; } } if (i % 16 == 0) { pr->println(); printHex(pr, i); } pr->write(' '); uint16_t uc = getLe16(&upcase[2 * (i & 0XFF)]); printHex(pr, uc); checksum = upcaseChecksum(uc, checksum); } pr->println(); pr->print(F("checksum: ")); printHex(pr, checksum); pr->println(); } //------------------------------------------------------------------------------ bool ExFatPartition::printVolInfo(print_t* pr) { uint8_t* cache = dataCachePrepare(0, FsCache::CACHE_FOR_READ); if (!cache) { pr->println(F("read mbr failed")); return false; } MbrSector_t* mbr = reinterpret_cast(cache); printMbr(pr, mbr); uint32_t volStart = getLe32(mbr->part->relativeSectors); uint32_t volSize = getLe32(mbr->part->totalSectors); if (volSize == 0) { pr->print(F("bad partition size")); return false; } cache = dataCachePrepare(volStart, FsCache::CACHE_FOR_READ); if (!cache) { pr->println(F("read pbs failed")); return false; } printExFatBoot(pr, reinterpret_cast(cache)); return true; } #endif // DOXYGEN_SHOULD_SKIP_THIS