FatFileSFN.cpp 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. /**
  2. * Copyright (c) 2011-2022 Bill Greiman
  3. * This file is part of the SdFat library for SD memory cards.
  4. *
  5. * MIT License
  6. *
  7. * Permission is hereby granted, free of charge, to any person obtaining a
  8. * copy of this software and associated documentation files (the "Software"),
  9. * to deal in the Software without restriction, including without limitation
  10. * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  11. * and/or sell copies of the Software, and to permit persons to whom the
  12. * Software is furnished to do so, subject to the following conditions:
  13. *
  14. * The above copyright notice and this permission notice shall be included
  15. * in all copies or substantial portions of the Software.
  16. *
  17. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  18. * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  19. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  20. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  21. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  22. * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  23. * DEALINGS IN THE SOFTWARE.
  24. */
  25. #define DBG_FILE "FatFileSFN.cpp"
  26. #include "../common/DebugMacros.h"
  27. #include "FatLib.h"
  28. //------------------------------------------------------------------------------
  29. // open with filename in fname
  30. #define SFN_OPEN_USES_CHKSUM 0
  31. bool FatFile::open(FatFile* dirFile, FatSfn_t* fname, oflag_t oflag) {
  32. uint16_t date;
  33. uint16_t time;
  34. uint8_t ms10;
  35. bool emptyFound = false;
  36. #if SFN_OPEN_USES_CHKSUM
  37. uint8_t checksum;
  38. #endif // SFN_OPEN_USES_CHKSUM
  39. uint8_t lfnOrd = 0;
  40. uint16_t emptyIndex = 0;
  41. uint16_t index = 0;
  42. DirFat_t* dir;
  43. DirLfn_t* ldir;
  44. dirFile->rewind();
  45. while (true) {
  46. dir = dirFile->readDirCache(true);
  47. if (!dir) {
  48. if (dirFile->getError()) {
  49. DBG_FAIL_MACRO;
  50. goto fail;
  51. }
  52. // At EOF if no error.
  53. break;
  54. }
  55. if (dir->name[0] == FAT_NAME_DELETED || dir->name[0] == FAT_NAME_FREE) {
  56. if (!emptyFound) {
  57. emptyIndex = index;
  58. emptyFound = true;
  59. }
  60. if (dir->name[0] == FAT_NAME_FREE) {
  61. break;
  62. }
  63. lfnOrd = 0;
  64. } else if (isFatFileOrSubdir(dir)) {
  65. if (!memcmp(fname->sfn, dir->name, 11)) {
  66. // don't open existing file if O_EXCL
  67. if (oflag & O_EXCL) {
  68. DBG_FAIL_MACRO;
  69. goto fail;
  70. }
  71. #if SFN_OPEN_USES_CHKSUM
  72. if (lfnOrd && checksum != lfnChecksum(dir->name)) {
  73. DBG_FAIL_MACRO;
  74. goto fail;
  75. }
  76. #endif // SFN_OPEN_USES_CHKSUM
  77. if (!openCachedEntry(dirFile, index, oflag, lfnOrd)) {
  78. DBG_FAIL_MACRO;
  79. goto fail;
  80. }
  81. return true;
  82. } else {
  83. lfnOrd = 0;
  84. }
  85. } else if (isFatLongName(dir)) {
  86. ldir = reinterpret_cast<DirLfn_t*>(dir);
  87. if (ldir->order & FAT_ORDER_LAST_LONG_ENTRY) {
  88. lfnOrd = ldir->order & 0X1F;
  89. #if SFN_OPEN_USES_CHKSUM
  90. checksum = ldir->checksum;
  91. #endif // SFN_OPEN_USES_CHKSUM
  92. }
  93. } else {
  94. lfnOrd = 0;
  95. }
  96. index++;
  97. }
  98. // don't create unless O_CREAT and write mode
  99. if (!(oflag & O_CREAT) || !isWriteMode(oflag)) {
  100. DBG_FAIL_MACRO;
  101. goto fail;
  102. }
  103. if (emptyFound) {
  104. index = emptyIndex;
  105. } else {
  106. if (!dirFile->addDirCluster()) {
  107. DBG_FAIL_MACRO;
  108. goto fail;
  109. }
  110. }
  111. dir = reinterpret_cast<DirFat_t*>(dirFile->cacheDir(index));
  112. if (!dir) {
  113. DBG_FAIL_MACRO;
  114. goto fail;
  115. }
  116. // initialize as empty file
  117. memset(dir, 0, sizeof(DirFat_t));
  118. memcpy(dir->name, fname->sfn, 11);
  119. // Set base-name and extension lower case bits.
  120. dir->caseFlags = (FAT_CASE_LC_BASE | FAT_CASE_LC_EXT) & fname->flags;
  121. // Set timestamps.
  122. if (FsDateTime::callback) {
  123. // call user date/time function
  124. FsDateTime::callback(&date, &time, &ms10);
  125. setLe16(dir->createDate, date);
  126. setLe16(dir->createTime, time);
  127. dir->createTimeMs = ms10;
  128. } else {
  129. setLe16(dir->createDate, FS_DEFAULT_DATE);
  130. setLe16(dir->modifyDate, FS_DEFAULT_DATE);
  131. setLe16(dir->accessDate, FS_DEFAULT_DATE);
  132. if (FS_DEFAULT_TIME) {
  133. setLe16(dir->createTime, FS_DEFAULT_TIME);
  134. setLe16(dir->modifyTime, FS_DEFAULT_TIME);
  135. }
  136. }
  137. // Force write of entry to device.
  138. dirFile->m_vol->cacheDirty();
  139. // open entry in cache.
  140. return openCachedEntry(dirFile, index, oflag, 0);
  141. fail:
  142. return false;
  143. }
  144. //------------------------------------------------------------------------------
  145. bool FatFile::openExistingSFN(const char* path) {
  146. FatSfn_t fname;
  147. auto vol = FatVolume::cwv();
  148. while (*path == '/') {
  149. path++;
  150. }
  151. if (*path == 0) {
  152. return openRoot(vol);
  153. }
  154. *this = *vol->vwd();
  155. do {
  156. if (!parsePathName(path, &fname, &path)) {
  157. DBG_FAIL_MACRO;
  158. goto fail;
  159. }
  160. if (!openSFN(&fname)) {
  161. DBG_FAIL_MACRO;
  162. goto fail;
  163. }
  164. } while (*path);
  165. return true;
  166. fail:
  167. return false;
  168. }
  169. //------------------------------------------------------------------------------
  170. bool FatFile::openSFN(FatSfn_t* fname) {
  171. DirFat_t dir;
  172. DirLfn_t* ldir;
  173. auto vol = m_vol;
  174. uint8_t lfnOrd = 0;
  175. if (!isDir()) {
  176. DBG_FAIL_MACRO;
  177. goto fail;
  178. }
  179. while (true) {
  180. if (read(&dir, 32) != 32) {
  181. DBG_FAIL_MACRO;
  182. goto fail;
  183. }
  184. if (dir.name[0] == 0) {
  185. DBG_FAIL_MACRO;
  186. goto fail;
  187. }
  188. if (isFatFileOrSubdir(&dir) && memcmp(fname->sfn, dir.name, 11) == 0) {
  189. uint16_t dirIndex = (m_curPosition - 32) >> 5;
  190. uint32_t dirCluster = m_firstCluster;
  191. memset(this, 0 , sizeof(FatFile));
  192. m_attributes = dir.attributes & FS_ATTRIB_COPY;
  193. m_flags = FILE_FLAG_READ;
  194. if (isFatFile(&dir)) {
  195. m_attributes |= FILE_ATTR_FILE;
  196. if (!isReadOnly()) {
  197. m_attributes |= FS_ATTRIB_ARCHIVE;
  198. m_flags |= FILE_FLAG_WRITE;
  199. }
  200. }
  201. m_lfnOrd = lfnOrd;
  202. m_firstCluster = (uint32_t)getLe16(dir.firstClusterHigh) << 16;
  203. m_firstCluster |= getLe16(dir.firstClusterLow);
  204. m_fileSize = getLe32(dir.fileSize);
  205. m_vol = vol;
  206. m_dirCluster = dirCluster;
  207. m_dirSector = m_vol->cacheSectorNumber();
  208. m_dirIndex = dirIndex;
  209. return true;
  210. } else if (isFatLongName(&dir)) {
  211. ldir = reinterpret_cast<DirLfn_t*>(&dir);
  212. if (ldir->order & FAT_ORDER_LAST_LONG_ENTRY) {
  213. lfnOrd = ldir->order & 0X1F;
  214. }
  215. } else {
  216. lfnOrd = 0;
  217. }
  218. }
  219. fail:
  220. return false;
  221. }
  222. //------------------------------------------------------------------------------
  223. // format directory name field from a 8.3 name string
  224. bool FatFile::parsePathName(const char* path, FatSfn_t* fname,
  225. const char** ptr) {
  226. uint8_t uc = 0;
  227. uint8_t lc = 0;
  228. uint8_t bit = FNAME_FLAG_LC_BASE;
  229. // blank fill name and extension
  230. for (uint8_t i = 0; i < 11; i++) {
  231. fname->sfn[i] = ' ';
  232. }
  233. for (uint8_t i = 0, n = 7;; path++) {
  234. uint8_t c = *path;
  235. if (c == 0 || isDirSeparator(c)) {
  236. // Done.
  237. break;
  238. }
  239. if (c == '.' && n == 7) {
  240. n = 10; // max index for full 8.3 name
  241. i = 8; // place for extension
  242. // bit for extension.
  243. bit = FNAME_FLAG_LC_EXT;
  244. } else {
  245. if (sfnReservedChar(c) || i > n) {
  246. DBG_FAIL_MACRO;
  247. goto fail;
  248. }
  249. if ('a' <= c && c <= 'z') {
  250. c += 'A' - 'a';
  251. lc |= bit;
  252. } else if ('A' <= c && c <= 'Z') {
  253. uc |= bit;
  254. }
  255. fname->sfn[i++] = c;
  256. }
  257. }
  258. // must have a file name, extension is optional
  259. if (fname->sfn[0] == ' ') {
  260. DBG_FAIL_MACRO;
  261. goto fail;
  262. }
  263. // Set base-name and extension bits.
  264. fname->flags = lc & uc ? 0 : lc;
  265. while (isDirSeparator(*path)) {
  266. path++;
  267. }
  268. *ptr = path;
  269. return true;
  270. fail:
  271. return false;
  272. }
  273. #if !USE_LONG_FILE_NAMES
  274. //------------------------------------------------------------------------------
  275. bool FatFile::remove() {
  276. DirFat_t* dir;
  277. // Can't remove if LFN or not open for write.
  278. if (!isWritable() || isLFN()) {
  279. DBG_FAIL_MACRO;
  280. goto fail;
  281. }
  282. // Free any clusters.
  283. if (m_firstCluster && !m_vol->freeChain(m_firstCluster)) {
  284. DBG_FAIL_MACRO;
  285. goto fail;
  286. }
  287. // Cache directory entry.
  288. dir = cacheDirEntry(FsCache::CACHE_FOR_WRITE);
  289. if (!dir) {
  290. DBG_FAIL_MACRO;
  291. goto fail;
  292. }
  293. // Mark entry deleted.
  294. dir->name[0] = FAT_NAME_DELETED;
  295. // Set this file closed.
  296. m_attributes = FILE_ATTR_CLOSED;
  297. m_flags = 0;
  298. // Write entry to device.
  299. return m_vol->cacheSync();
  300. fail:
  301. return false;
  302. }
  303. #endif // !USE_LONG_FILE_NAMES