|
- /* FatLib Library
- * Copyright (C) 2012 by William Greiman
- *
- * This file is part of the FatLib Library
- *
- * This Library 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 Library 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 the FatLib Library. If not, see
- * <http://www.gnu.org/licenses/>.
- */
- #include "FatFile.h"
- //------------------------------------------------------------------------------
- //
- uint8_t FatFile::lfnChecksum(uint8_t* name) {
- uint8_t sum = 0;
- for (uint8_t i = 0; i < 11; i++) {
- sum = (((sum & 1) << 7) | ((sum & 0xfe) >> 1)) + name[i];
- }
- return sum;
- }
- #if USE_LONG_FILE_NAMES
- //------------------------------------------------------------------------------
- // Saves about 90 bytes of flash on 328 over tolower().
- inline char lfnToLower(char c) {
- return 'A' <= c && c <= 'Z' ? c + 'a' - 'A' : c;
- }
- //------------------------------------------------------------------------------
- // Daniel Bernstein University of Illinois at Chicago.
- // Original had + instead of ^
- static uint16_t Bernstein(uint16_t hash, const char *str, size_t len) {
- for (size_t i = 0; i < len; i++) {
- // hash = hash * 33 ^ str[i];
- hash = ((hash << 5) + hash) ^ str[i];
- }
- return hash;
- }
- //------------------------------------------------------------------------------
- /**
- * Fetch a 16-bit long file name character.
- *
- * \param[in] ldir Pointer to long file name directory entry.
- * \param[in] i Index of character.
- * \return The 16-bit character.
- */
- static uint16_t lfnGetChar(ldir_t *ldir, uint8_t i) {
- if (i < LDIR_NAME1_DIM) {
- return ldir->name1[i];
- } else if (i < (LDIR_NAME1_DIM + LDIR_NAME2_DIM)) {
- return ldir->name2[i - LDIR_NAME1_DIM];
- } else if (i < (LDIR_NAME1_DIM + LDIR_NAME2_DIM + LDIR_NAME2_DIM)) {
- return ldir->name3[i - LDIR_NAME1_DIM - LDIR_NAME2_DIM];
- }
- return 0;
- }
- //------------------------------------------------------------------------------
- static bool lfnGetName(ldir_t *ldir, char* name, size_t n) {
- uint8_t i;
- size_t k = 13*((ldir->ord & 0X1F) - 1);
- for (i = 0; i < 13; i++) {
- uint16_t c = lfnGetChar(ldir, i);
- if (c == 0 || k >= n) {
- break;
- }
- name[k++] = c >= 0X7F ? '?' : c;
- }
- // Terminate with zero byte if name fits.
- if (k < n && (ldir->ord & LDIR_ORD_LAST_LONG_ENTRY)) {
- name[k] = 0;
- }
- // Truncate if name is too long.
- name[n - 1] = 0;
- return true;
- }
- //------------------------------------------------------------------------------
- inline bool lfnLegalChar(char c) {
- if (c == '/' || c == '\\' || c == '"' || c == '*' ||
- c == ':' || c == '<' || c == '>' || c == '?' || c == '|') {
- return false;
- }
- return 0X1F < c && c < 0X7F;
- }
- //------------------------------------------------------------------------------
- /**
- * Store a 16-bit long file name character.
- *
- * \param[in] ldir Pointer to long file name directory entry.
- * \param[in] i Index of character.
- * \param[in] c The 16-bit character.
- */
- static void lfnPutChar(ldir_t *ldir, uint8_t i, uint16_t c) {
- if (i < LDIR_NAME1_DIM) {
- ldir->name1[i] = c;
- } else if (i < (LDIR_NAME1_DIM + LDIR_NAME2_DIM)) {
- ldir->name2[i - LDIR_NAME1_DIM] = c;
- } else if (i < (LDIR_NAME1_DIM + LDIR_NAME2_DIM + LDIR_NAME2_DIM)) {
- ldir->name3[i - LDIR_NAME1_DIM - LDIR_NAME2_DIM] = c;
- }
- }
- //------------------------------------------------------------------------------
- static void lfnPutName(ldir_t *ldir, const char* name, size_t n) {
- size_t k = 13*((ldir->ord & 0X1F) - 1);
- for (uint8_t i = 0; i < 13; i++, k++) {
- uint16_t c = k < n ? name[k] : k == n ? 0 : 0XFFFF;
- lfnPutChar(ldir, i, c);
- }
- }
- //==============================================================================
- bool FatFile::getName(char* name, size_t size) {
- FatFile dirFile;
- ldir_t* ldir;
- if (!isOpen() || size < 13) {
- DBG_FAIL_MACRO;
- goto fail;
- }
- if (!isLFN()) {
- return getSFN(name);
- }
- if (!dirFile.openCluster(this)) {
- DBG_FAIL_MACRO;
- goto fail;
- }
- for (uint8_t ord = 1; ord <= m_lfnOrd; ord++) {
- if (!dirFile.seekSet(32UL*(m_dirIndex - ord))) {
- DBG_FAIL_MACRO;
- goto fail;
- }
- ldir = reinterpret_cast<ldir_t*>(dirFile.readDirCache());
- if (!ldir) {
- DBG_FAIL_MACRO;
- goto fail;
- }
- if (ldir->attr != DIR_ATT_LONG_NAME) {
- DBG_FAIL_MACRO;
- goto fail;
- }
- if (ord != (ldir->ord & 0X1F)) {
- DBG_FAIL_MACRO;
- goto fail;
- }
- if (!lfnGetName(ldir, name, size)) {
- DBG_FAIL_MACRO;
- goto fail;
- }
- if (ldir->ord & LDIR_ORD_LAST_LONG_ENTRY) {
- return true;
- }
- }
- // Fall into fail.
- DBG_FAIL_MACRO;
-
- fail:
- name[0] = 0;
- return false;
- }
- //------------------------------------------------------------------------------
- bool FatFile::openCluster(FatFile* file) {
- if (file->m_dirCluster == 0) {
- return openRoot(file->m_vol);
- }
- memset(this, 0, sizeof(FatFile));
- m_attr = FILE_ATTR_SUBDIR;
- m_flags = O_READ;
- m_vol = file->m_vol;
- m_firstCluster = file->m_dirCluster;
- return true;
- }
- //------------------------------------------------------------------------------
- bool FatFile::parsePathName(const char* path,
- fname_t* fname, const char** ptr) {
- char c;
- bool is83;
- uint8_t bit = DIR_NT_LC_BASE;
- uint8_t lc = 0;
- uint8_t uc = 0;
- uint8_t i = 0;
- uint8_t in = 7;
- int end;
- int len = 0;
- int si;
- int dot;
-
- // Skip leading spaces.
- while (*path == ' ') {
- path++;
- }
- fname->lfn = path;
-
- for (len = 0; ; len++) {
- c = path[len];
- if (c == 0 || isDirSeparator(c)) {
- break;
- }
- if (!lfnLegalChar(c)) {
- return false;
- }
- }
- // Advance to next path component.
- for (end = len; path[end] == ' ' || isDirSeparator(path[end]); end++) {}
- *ptr = &path[end];
-
- // Back over spaces and dots.
- while (len) {
- c = path[len - 1];
- if (c != '.' && c != ' ') {
- break;
- }
- len--;
- }
- // Max length of LFN is 255.
- if (len > 255) {
- return false;
- }
- fname->len = len;
- // Blank file short name.
- for (uint8_t k = 0; k < 11; k++) {
- fname->sfn[k] = ' ';
- }
- // skip leading spaces and dots.
- for (si = 0; path[si] == '.' || path[si] == ' '; si++) {}
- // Not 8.3 if leading dot or space.
- is83 = !si;
-
- // find last dot.
- for (dot = len - 1; dot >= 0 && path[dot] != '.'; dot--) {}
- for (; si < len; si++) {
- c = path[si];
- if (c == ' ' || (c == '.' && dot != si)) {
- is83 = false;
- continue;
- }
- if (!legal83Char(c) && si != dot) {
- is83 = false;
- c = '_';
- }
- if (si == dot || i > in) {
- if (in == 10) {
- // Done - extension longer than three characters.
- is83 = false;
- break;
- }
- if (si != dot) {
- is83 = false;
- }
- // Break if no dot and base-name is longer than eight characters.
- if (si > dot) {
- break;
- }
- si = dot;
- in = 10; // Max index for full 8.3 name.
- i = 8; // Place for extension.
- bit = DIR_NT_LC_EXT; // bit for extension.
- } else {
- if ('a' <= c && c <= 'z') {
- c += 'A' - 'a';
- lc |= bit;
- } else if ('A' <= c && c <= 'Z') {
- uc |= bit;
- }
- fname->sfn[i++] = c;
- if (i < 7) {
- fname->seqPos = i;
- }
- }
- }
- if (fname->sfn[0] == ' ') {
- return false;
- }
-
- if (is83) {
- fname->flags = lc & uc ? FNAME_FLAG_MIXED_CASE : lc;
- } else {
- fname->flags = FNAME_FLAG_LOST_CHARS;
- fname->sfn[fname->seqPos] = '~';
- fname->sfn[fname->seqPos + 1] = '1';
- }
- return true;
- }
- //------------------------------------------------------------------------------
- bool FatFile::open(FatFile* dirFile, fname_t* fname, uint8_t oflag) {
- bool fnameFound = false;
- uint8_t lfnOrd = 0;
- uint8_t freeNeed;
- uint8_t freeFound = 0;
- uint8_t ord = 0;
- uint8_t chksum = 0;
- uint16_t freeIndex = 0;
- uint16_t curIndex;
- dir_t* dir;
- ldir_t* ldir;
- size_t len = fname->len;
-
- if (!dirFile->isDir() || isOpen()) {
- DBG_FAIL_MACRO;
- goto fail;
- }
- // Number of directory entries needed.
- freeNeed = fname->flags & FNAME_FLAG_NEED_LFN ? 1 + (len + 12)/13 : 1;
-
- dirFile->rewind();
- while (1) {
- curIndex = dirFile->m_curPosition/32;
- dir = dirFile->readDirCache(true);
- if (!dir) {
- if (dirFile->getError()) {
- DBG_FAIL_MACRO;
- goto fail;
- }
- // At EOF
- goto create;
- }
- if (dir->name[0] == DIR_NAME_DELETED || dir->name[0] == DIR_NAME_FREE) {
- if (freeFound == 0) {
- freeIndex = curIndex;
- }
- if (freeFound < freeNeed) {
- freeFound++;
- }
- if (dir->name[0] == DIR_NAME_FREE) {
- goto create;
- }
- } else {
- if (freeFound < freeNeed) {
- freeFound = 0;
- }
- }
- // skip empty slot or '.' or '..'
- if (dir->name[0] == DIR_NAME_DELETED || dir->name[0] == '.') {
- lfnOrd = 0;
- } else if (DIR_IS_LONG_NAME(dir)) {
- ldir_t *ldir = reinterpret_cast<ldir_t*>(dir);
- if (!lfnOrd) {
- if ((ldir->ord & LDIR_ORD_LAST_LONG_ENTRY) == 0) {
- continue;
- }
- lfnOrd = ord = ldir->ord & 0X1F;
- chksum = ldir->chksum;
- } else if (ldir->ord != --ord || chksum != ldir->chksum) {
- lfnOrd = 0;
- continue;
- }
- size_t k = 13*(ord - 1);
- if (k >= len) {
- // Not found.
- lfnOrd = 0;
- continue;
- }
- for (uint8_t i = 0; i < 13; i++) {
- uint16_t u = lfnGetChar(ldir, i);
- if (k == len) {
- if (u != 0) {
- // Not found.
- lfnOrd = 0;
- }
- break;
- }
- if (u > 255 || lfnToLower(u) != lfnToLower(fname->lfn[k++])) {
- // Not found.
- lfnOrd = 0;
- break;
- }
- }
- } else if (DIR_IS_FILE_OR_SUBDIR(dir)) {
- if (lfnOrd) {
- if (1 == ord && lfnChecksum(dir->name) == chksum) {
- goto found;
- }
- DBG_FAIL_MACRO;
- goto fail;
- }
- if (!memcmp(dir->name, fname->sfn, sizeof(fname->sfn))) {
- if (!(fname->flags & FNAME_FLAG_LOST_CHARS)) {
- goto found;
- }
- fnameFound = true;
- }
- } else {
- lfnOrd = 0;
- }
- }
-
- found:
- // Don't open if create only.
- if (oflag & O_EXCL) {
- DBG_FAIL_MACRO;
- goto fail;
- }
- goto open;
-
- create:
- // don't create unless O_CREAT and O_WRITE
- if (!(oflag & O_CREAT) || !(oflag & O_WRITE)) {
- DBG_FAIL_MACRO;
- goto fail;
- }
- // If at EOF start in next cluster.
- if (freeFound == 0) {
- freeIndex = curIndex;
- }
-
- while (freeFound < freeNeed) {
- dir = dirFile->readDirCache();
- if (!dir) {
- if (dirFile->getError()) {
- DBG_FAIL_MACRO;
- goto fail;
- }
- // EOF if no error.
- break;
- }
- freeFound++;
- }
- while (freeFound < freeNeed) {
- // Will fail if FAT16 root.
- if (!dirFile->addDirCluster()) {
- DBG_FAIL_MACRO;
- goto fail;
- }
- // Done if more than one block per cluster. Max freeNeed is 21.
- if (dirFile->m_vol->blocksPerCluster() > 1) {
- break;
- }
- freeFound += 16;
- }
- if (fnameFound) {
- if (!dirFile->lfnUniqueSfn(fname)) {
- goto fail;
- }
- }
- if (!dirFile->seekSet(32UL*freeIndex)) {
- DBG_FAIL_MACRO;
- goto fail;
- }
- lfnOrd = freeNeed - 1;
- for (uint8_t ord = lfnOrd ; ord ; ord--) {
- ldir = reinterpret_cast<ldir_t*>(dirFile->readDirCache());
- if (!ldir) {
- DBG_FAIL_MACRO;
- goto fail;
- }
- dirFile->m_vol->cacheDirty();
- ldir->ord = ord == lfnOrd ? LDIR_ORD_LAST_LONG_ENTRY | ord : ord;
- ldir->attr = DIR_ATT_LONG_NAME;
- ldir->type = 0;
- ldir->chksum = lfnChecksum(fname->sfn);
- ldir->mustBeZero = 0;
- lfnPutName(ldir, fname->lfn, len);
- }
- curIndex = dirFile->m_curPosition/32;
- dir = dirFile->readDirCache();
- if (!dir) {
- DBG_FAIL_MACRO;
- goto fail;
- }
- // initialize as empty file
- memset(dir, 0, sizeof(dir_t));
- memcpy(dir->name, fname->sfn, 11);
-
- // Set base-name and extension lower case bits.
- dir->reservedNT = (DIR_NT_LC_BASE | DIR_NT_LC_EXT) & fname->flags;
-
- // set timestamps
- if (m_dateTime) {
- // call user date/time function
- m_dateTime(&dir->creationDate, &dir->creationTime);
- } else {
- // use default date/time
- dir->creationDate = FAT_DEFAULT_DATE;
- dir->creationTime = FAT_DEFAULT_TIME;
- }
- dir->lastAccessDate = dir->creationDate;
- dir->lastWriteDate = dir->creationDate;
- dir->lastWriteTime = dir->creationTime;
-
- // Force write of entry to device.
- dirFile->m_vol->cacheDirty();
-
- open:
- // open entry in cache.
- if (!openCachedEntry(dirFile, curIndex, oflag, lfnOrd)) {
- DBG_FAIL_MACRO;
- goto fail;
- }
- return true;
-
- fail:
- return false;
- }
- //------------------------------------------------------------------------------
- size_t FatFile::printName(print_t* pr) {
- FatFile dirFile;
- uint16_t u;
- size_t n = 0;
- ldir_t* ldir;
-
- if (!isLFN()) {
- return printSFN(pr);
- }
- if (!dirFile.openCluster(this)) {
- DBG_FAIL_MACRO;
- goto fail;
- }
- for (uint8_t ord = 1; ord <= m_lfnOrd; ord++) {
- if (!dirFile.seekSet(32UL*(m_dirIndex - ord))) {
- DBG_FAIL_MACRO;
- goto fail;
- }
- ldir = reinterpret_cast<ldir_t*>(dirFile.readDirCache());
- if (!ldir) {
- DBG_FAIL_MACRO;
- goto fail;
- }
- if (ldir->attr != DIR_ATT_LONG_NAME ||
- ord != (ldir->ord & 0X1F)) {
- DBG_FAIL_MACRO;
- goto fail;
- }
- for (uint8_t i = 0; i < 13; i++) {
- u = lfnGetChar(ldir, i);
- if (u == 0) {
- // End of name.
- break;
- }
- if (u > 0X7E) {
- u = '?';
- }
- pr->write(static_cast<char>(u));
- n++;
- }
- if (ldir->ord & LDIR_ORD_LAST_LONG_ENTRY) {
- return n;
- }
- }
- // Fall into fail;
- DBG_FAIL_MACRO;
-
- fail:
- return 0;
- }
- //------------------------------------------------------------------------------
- bool FatFile::remove() {
- bool last;
- uint8_t chksum;
- uint8_t ord;
- FatFile dirFile;
- dir_t* dir;
- ldir_t* ldir;
-
- // Cant' remove not open for write.
- if (!isFile() || !(m_flags & O_WRITE)) {
- DBG_FAIL_MACRO;
- goto fail;
- }
- // Free any clusters.
- if (m_firstCluster && !m_vol->freeChain(m_firstCluster)) {
- DBG_FAIL_MACRO;
- goto fail;
- }
- // Cache directory entry.
- dir = cacheDirEntry(FatCache::CACHE_FOR_WRITE);
- if (!dir) {
- DBG_FAIL_MACRO;
- goto fail;
- }
- chksum = lfnChecksum(dir->name);
-
- // Mark entry deleted.
- dir->name[0] = DIR_NAME_DELETED;
-
- // Set this file closed.
- m_attr = FILE_ATTR_CLOSED;
-
- // Write entry to device.
- if (!m_vol->cacheSync()) {
- DBG_FAIL_MACRO;
- goto fail;
- }
- if (!isLFN()) {
- // Done, no LFN entries.
- return true;
- }
- if (!dirFile.openCluster(this)) {
- DBG_FAIL_MACRO;
- goto fail;
- }
- for (ord = 1; ord <= m_lfnOrd; ord++) {
- if (!dirFile.seekSet(32UL*(m_dirIndex - ord))) {
- DBG_FAIL_MACRO;
- goto fail;
- }
- ldir = reinterpret_cast<ldir_t*>(dirFile.readDirCache());
- if (!ldir) {
- DBG_FAIL_MACRO;
- goto fail;
- }
- if (ldir->attr != DIR_ATT_LONG_NAME ||
- ord != (ldir->ord & 0X1F) ||
- chksum != ldir->chksum) {
- DBG_FAIL_MACRO;
- goto fail;
- }
- last = ldir->ord & LDIR_ORD_LAST_LONG_ENTRY;
- ldir->ord = DIR_NAME_DELETED;
- m_vol->cacheDirty();
- if (last) {
- if (!m_vol->cacheSync()) {
- DBG_FAIL_MACRO;
- goto fail;
- }
- return true;
- }
- }
- // Fall into fail.
- DBG_FAIL_MACRO;
-
- fail:
- return false;
- }
- //------------------------------------------------------------------------------
- bool FatFile::lfnUniqueSfn(fname_t* fname) {
- const uint8_t FIRST_HASH_SEQ = 2; // min value is 2
- uint8_t pos = fname->seqPos;;
- dir_t *dir;
- uint16_t hex;
-
- DBG_HALT_IF(!(fname->flags & FNAME_FLAG_LOST_CHARS));
- DBG_HALT_IF(fname->sfn[pos] != '~' && fname->sfn[pos + 1] != '1');
-
- for (uint8_t seq = 2; seq < 100; seq++) {
- if (seq < FIRST_HASH_SEQ) {
- fname->sfn[pos + 1] = '0' + seq;
- } else {
- DBG_PRINT_IF(seq > FIRST_HASH_SEQ);
- hex = Bernstein(seq + fname->len, fname->lfn, fname->len);
- if (pos > 3) {
- // Make space in name for ~HHHH.
- pos = 3;
- }
- for (uint8_t i = pos + 4 ; i > pos; i--) {
- uint8_t h = hex & 0XF;
- fname->sfn[i] = h < 10 ? h + '0' : h + 'A' - 10;
- hex >>= 4;
- }
- }
- fname->sfn[pos] = '~';
- rewind();
- while (1) {
- dir = readDirCache(true);
- if (!dir) {
- if (!getError()) {
- // At EOF and name not found if no error.
- goto done;
- }
- DBG_FAIL_MACRO;
- goto fail;
- }
- if (dir->name[0] == DIR_NAME_FREE) {
- goto done;
- }
- if (DIR_IS_FILE_OR_SUBDIR(dir) && !memcmp(fname->sfn, dir->name, 11)) {
- // Name found - try another.
- break;
- }
- }
- }
- // fall inti fail - too many tries.
- DBG_FAIL_MACRO;
-
- fail:
- return false;
-
- done:
- return true;
- }
- #endif // #if USE_LONG_FILE_NAMES
|