You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

274 lines
6.7 KiB
C++

/* 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"
#include "FatFileSystem.h"
//------------------------------------------------------------------------------
bool FatFile::getSFN(char* name) {
dir_t* dir;
if (!isOpen()) {
DBG_FAIL_MACRO;
goto fail;
}
if (isRoot()) {
name[0] = '/';
name[1] = '\0';
return true;
}
// cache entry
dir = cacheDirEntry(FatCache::CACHE_FOR_READ);
if (!dir) {
DBG_FAIL_MACRO;
goto fail;
}
// format name
dirName(dir, name);
return true;
fail:
return false;
}
//------------------------------------------------------------------------------
size_t FatFile::printSFN(print_t* pr) {
char name[13];
if (!getSFN(name)) {
DBG_FAIL_MACRO;
goto fail;
}
return pr->write(name);
fail:
return 0;
}
#if !USE_LONG_FILE_NAMES
//------------------------------------------------------------------------------
bool FatFile::getName(char* name, size_t size) {
return size < 13 ? 0 : getSFN(name);
}
//------------------------------------------------------------------------------
// format directory name field from a 8.3 name string
bool FatFile::parsePathName(const char* path, fname_t* fname,
const char** ptr) {
uint8_t uc = 0;
uint8_t lc = 0;
uint8_t bit = FNAME_FLAG_LC_BASE;
// blank fill name and extension
for (uint8_t i = 0; i < 11; i++) {
fname->sfn[i] = ' ';
}
for (uint8_t i = 0, n = 7;; path++) {
uint8_t c = *path;
if (c == 0 || isDirSeparator(c)) {
// Done.
break;
}
if (c == '.' && n == 7) {
n = 10; // max index for full 8.3 name
i = 8; // place for extension
// bit for extension.
bit = FNAME_FLAG_LC_EXT;
} else {
if (!legal83Char(c) || i > n) {
DBG_FAIL_MACRO;
goto fail;
}
if ('a' <= c && c <= 'z') {
c += 'A' - 'a';
lc |= bit;
} else if ('A' <= c && c <= 'Z') {
uc |= bit;
}
fname->sfn[i++] = c;
}
}
// must have a file name, extension is optional
if (fname->sfn[0] == ' ') {
DBG_FAIL_MACRO;
goto fail;
}
// Set base-name and extension bits.
fname->flags = lc & uc ? 0 : lc;
while (isDirSeparator(*path)) {
path++;
}
*ptr = path;
return true;
fail:
return false;
}
//------------------------------------------------------------------------------
// open with filename in fname
#define SFN_OPEN_USES_CHKSUM 0
bool FatFile::open(FatFile* dirFile, fname_t* fname, uint8_t oflag) {
bool emptyFound = false;
#if SFN_OPEN_USES_CHKSUM
uint8_t chksum;
#endif
uint8_t lfnOrd = 0;
uint16_t emptyIndex;
uint16_t index = 0;
dir_t* dir;
ldir_t* ldir;
dirFile->rewind();
while (1) {
if (!emptyFound) {
emptyIndex = index;
}
dir = dirFile->readDirCache(true);
if (!dir) {
if (dirFile->getError()) {
DBG_FAIL_MACRO;
goto fail;
}
// At EOF if no error.
break;
}
if (dir->name[0] == DIR_NAME_FREE) {
emptyFound = true;
break;
}
if (dir->name[0] == DIR_NAME_DELETED) {
lfnOrd = 0;
emptyFound = true;
} else if (DIR_IS_FILE_OR_SUBDIR(dir)) {
if (!memcmp(fname->sfn, dir->name, 11)) {
// don't open existing file if O_EXCL
if (oflag & O_EXCL) {
DBG_FAIL_MACRO;
goto fail;
}
#if SFN_OPEN_USES_CHKSUM
if (lfnOrd && chksum != lfnChecksum(dir->name)) {
DBG_FAIL_MACRO;
goto fail;
}
#endif // SFN_OPEN_USES_CHKSUM
if (!openCachedEntry(dirFile, index, oflag, lfnOrd)) {
DBG_FAIL_MACRO;
goto fail;
}
return true;
} else {
lfnOrd = 0;
}
} else if (DIR_IS_LONG_NAME(dir)) {
ldir = reinterpret_cast<ldir_t*>(dir);
if (ldir->ord & LDIR_ORD_LAST_LONG_ENTRY) {
lfnOrd = ldir->ord & 0X1F;
#if SFN_OPEN_USES_CHKSUM
chksum = ldir->chksum;
#endif // SFN_OPEN_USES_CHKSUM
}
} else {
lfnOrd = 0;
}
index++;
}
// don't create unless O_CREAT and O_WRITE
if (!(oflag & O_CREAT) || !(oflag & O_WRITE)) {
DBG_FAIL_MACRO;
goto fail;
}
if (emptyFound) {
index = emptyIndex;
} else {
if (!dirFile->addDirCluster()) {
DBG_FAIL_MACRO;
goto fail;
}
}
if (!dirFile->seekSet(32UL*index)) {
DBG_FAIL_MACRO;
goto fail;
}
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 entry in cache.
return openCachedEntry(dirFile, index, oflag, 0);
fail:
return false;
}
//------------------------------------------------------------------------------
size_t FatFile::printName(print_t* pr) {
return printSFN(pr);
}
//------------------------------------------------------------------------------
bool FatFile::remove() {
dir_t* dir;
// Can't remove if LFN or not open for write.
if (!isFile() || isLFN() || !(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;
}
// Mark entry deleted.
dir->name[0] = DIR_NAME_DELETED;
// Set this file closed.
m_attr = FILE_ATTR_CLOSED;
// Write entry to device.
return m_vol->cacheSync();
fail:
return false;
}
#endif // !USE_LONG_FILE_NAMES