Sat, 07 Nov 2015 13:24:46 +0100
several modifications to support laser enable - still needs cleanup
0 | 1 | /* Arduino SdFat Library |
2 | * Copyright (C) 2009 by William Greiman | |
3 | * | |
4 | * This file is part of the Arduino SdFat Library | |
5 | * | |
6 | * This Library is free software: you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License as published by | |
8 | * the Free Software Foundation, either version 3 of the License, or | |
9 | * (at your option) any later version. | |
10 | * | |
11 | * This Library is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License | |
17 | * along with the Arduino SdFat Library. If not, see | |
18 | * <http://www.gnu.org/licenses/>. | |
19 | */ | |
20 | ||
21 | #include "Marlin.h" | |
22 | #ifdef SDSUPPORT | |
23 | ||
24 | #include "SdBaseFile.h" | |
25 | //------------------------------------------------------------------------------ | |
26 | // pointer to cwd directory | |
27 | SdBaseFile* SdBaseFile::cwd_ = 0; | |
28 | // callback function for date/time | |
29 | void (*SdBaseFile::dateTime_)(uint16_t* date, uint16_t* time) = 0; | |
30 | //------------------------------------------------------------------------------ | |
31 | // add a cluster to a file | |
32 | bool SdBaseFile::addCluster() { | |
33 | if (!vol_->allocContiguous(1, &curCluster_)) goto fail; | |
34 | ||
35 | // if first cluster of file link to directory entry | |
36 | if (firstCluster_ == 0) { | |
37 | firstCluster_ = curCluster_; | |
38 | flags_ |= F_FILE_DIR_DIRTY; | |
39 | } | |
40 | return true; | |
41 | ||
42 | fail: | |
43 | return false; | |
44 | } | |
45 | //------------------------------------------------------------------------------ | |
46 | // Add a cluster to a directory file and zero the cluster. | |
47 | // return with first block of cluster in the cache | |
48 | bool SdBaseFile::addDirCluster() { | |
49 | uint32_t block; | |
50 | // max folder size | |
51 | if (fileSize_/sizeof(dir_t) >= 0XFFFF) goto fail; | |
52 | ||
53 | if (!addCluster()) goto fail; | |
54 | if (!vol_->cacheFlush()) goto fail; | |
55 | ||
56 | block = vol_->clusterStartBlock(curCluster_); | |
57 | ||
58 | // set cache to first block of cluster | |
59 | vol_->cacheSetBlockNumber(block, true); | |
60 | ||
61 | // zero first block of cluster | |
62 | memset(vol_->cacheBuffer_.data, 0, 512); | |
63 | ||
64 | // zero rest of cluster | |
65 | for (uint8_t i = 1; i < vol_->blocksPerCluster_; i++) { | |
66 | if (!vol_->writeBlock(block + i, vol_->cacheBuffer_.data)) goto fail; | |
67 | } | |
68 | // Increase directory file size by cluster size | |
69 | fileSize_ += 512UL << vol_->clusterSizeShift_; | |
70 | return true; | |
71 | ||
72 | fail: | |
73 | return false; | |
74 | } | |
75 | //------------------------------------------------------------------------------ | |
76 | // cache a file's directory entry | |
77 | // return pointer to cached entry or null for failure | |
78 | dir_t* SdBaseFile::cacheDirEntry(uint8_t action) { | |
79 | if (!vol_->cacheRawBlock(dirBlock_, action)) goto fail; | |
80 | return vol_->cache()->dir + dirIndex_; | |
81 | ||
82 | fail: | |
83 | return 0; | |
84 | } | |
85 | //------------------------------------------------------------------------------ | |
86 | /** Close a file and force cached data and directory information | |
87 | * to be written to the storage device. | |
88 | * | |
89 | * \return The value one, true, is returned for success and | |
90 | * the value zero, false, is returned for failure. | |
91 | * Reasons for failure include no file is open or an I/O error. | |
92 | */ | |
93 | bool SdBaseFile::close() { | |
94 | bool rtn = sync(); | |
95 | type_ = FAT_FILE_TYPE_CLOSED; | |
96 | return rtn; | |
97 | } | |
98 | //------------------------------------------------------------------------------ | |
99 | /** Check for contiguous file and return its raw block range. | |
100 | * | |
101 | * \param[out] bgnBlock the first block address for the file. | |
102 | * \param[out] endBlock the last block address for the file. | |
103 | * | |
104 | * \return The value one, true, is returned for success and | |
105 | * the value zero, false, is returned for failure. | |
106 | * Reasons for failure include file is not contiguous, file has zero length | |
107 | * or an I/O error occurred. | |
108 | */ | |
109 | bool SdBaseFile::contiguousRange(uint32_t* bgnBlock, uint32_t* endBlock) { | |
110 | // error if no blocks | |
111 | if (firstCluster_ == 0) goto fail; | |
112 | ||
113 | for (uint32_t c = firstCluster_; ; c++) { | |
114 | uint32_t next; | |
115 | if (!vol_->fatGet(c, &next)) goto fail; | |
116 | ||
117 | // check for contiguous | |
118 | if (next != (c + 1)) { | |
119 | // error if not end of chain | |
120 | if (!vol_->isEOC(next)) goto fail; | |
121 | *bgnBlock = vol_->clusterStartBlock(firstCluster_); | |
122 | *endBlock = vol_->clusterStartBlock(c) | |
123 | + vol_->blocksPerCluster_ - 1; | |
124 | return true; | |
125 | } | |
126 | } | |
127 | ||
128 | fail: | |
129 | return false; | |
130 | } | |
131 | //------------------------------------------------------------------------------ | |
132 | /** Create and open a new contiguous file of a specified size. | |
133 | * | |
134 | * \note This function only supports short DOS 8.3 names. | |
135 | * See open() for more information. | |
136 | * | |
137 | * \param[in] dirFile The directory where the file will be created. | |
138 | * \param[in] path A path with a valid DOS 8.3 file name. | |
139 | * \param[in] size The desired file size. | |
140 | * | |
141 | * \return The value one, true, is returned for success and | |
142 | * the value zero, false, is returned for failure. | |
143 | * Reasons for failure include \a path contains | |
144 | * an invalid DOS 8.3 file name, the FAT volume has not been initialized, | |
145 | * a file is already open, the file already exists, the root | |
146 | * directory is full or an I/O error. | |
147 | * | |
148 | */ | |
149 | bool SdBaseFile::createContiguous(SdBaseFile* dirFile, | |
150 | const char* path, uint32_t size) { | |
151 | uint32_t count; | |
152 | // don't allow zero length file | |
153 | if (size == 0) goto fail; | |
154 | if (!open(dirFile, path, O_CREAT | O_EXCL | O_RDWR)) goto fail; | |
155 | ||
156 | // calculate number of clusters needed | |
157 | count = ((size - 1) >> (vol_->clusterSizeShift_ + 9)) + 1; | |
158 | ||
159 | // allocate clusters | |
160 | if (!vol_->allocContiguous(count, &firstCluster_)) { | |
161 | remove(); | |
162 | goto fail; | |
163 | } | |
164 | fileSize_ = size; | |
165 | ||
166 | // insure sync() will update dir entry | |
167 | flags_ |= F_FILE_DIR_DIRTY; | |
168 | ||
169 | return sync(); | |
170 | ||
171 | fail: | |
172 | return false; | |
173 | } | |
174 | //------------------------------------------------------------------------------ | |
175 | /** Return a file's directory entry. | |
176 | * | |
177 | * \param[out] dir Location for return of the file's directory entry. | |
178 | * | |
179 | * \return The value one, true, is returned for success and | |
180 | * the value zero, false, is returned for failure. | |
181 | */ | |
182 | bool SdBaseFile::dirEntry(dir_t* dir) { | |
183 | dir_t* p; | |
184 | // make sure fields on SD are correct | |
185 | if (!sync()) goto fail; | |
186 | ||
187 | // read entry | |
188 | p = cacheDirEntry(SdVolume::CACHE_FOR_READ); | |
189 | if (!p) goto fail; | |
190 | ||
191 | // copy to caller's struct | |
192 | memcpy(dir, p, sizeof(dir_t)); | |
193 | return true; | |
194 | ||
195 | fail: | |
196 | return false; | |
197 | } | |
198 | //------------------------------------------------------------------------------ | |
199 | /** Format the name field of \a dir into the 13 byte array | |
200 | * \a name in standard 8.3 short name format. | |
201 | * | |
202 | * \param[in] dir The directory structure containing the name. | |
203 | * \param[out] name A 13 byte char array for the formatted name. | |
204 | */ | |
205 | void SdBaseFile::dirName(const dir_t& dir, char* name) { | |
206 | uint8_t j = 0; | |
207 | for (uint8_t i = 0; i < 11; i++) { | |
208 | if (dir.name[i] == ' ')continue; | |
209 | if (i == 8) name[j++] = '.'; | |
210 | name[j++] = dir.name[i]; | |
211 | } | |
212 | name[j] = 0; | |
213 | } | |
214 | //------------------------------------------------------------------------------ | |
215 | /** Test for the existence of a file in a directory | |
216 | * | |
217 | * \param[in] name Name of the file to be tested for. | |
218 | * | |
219 | * The calling instance must be an open directory file. | |
220 | * | |
221 | * dirFile.exists("TOFIND.TXT") searches for "TOFIND.TXT" in the directory | |
222 | * dirFile. | |
223 | * | |
224 | * \return true if the file exists else false. | |
225 | */ | |
226 | bool SdBaseFile::exists(const char* name) { | |
227 | SdBaseFile file; | |
228 | return file.open(this, name, O_READ); | |
229 | } | |
230 | //------------------------------------------------------------------------------ | |
231 | /** | |
232 | * Get a string from a file. | |
233 | * | |
234 | * fgets() reads bytes from a file into the array pointed to by \a str, until | |
235 | * \a num - 1 bytes are read, or a delimiter is read and transferred to \a str, | |
236 | * or end-of-file is encountered. The string is then terminated | |
237 | * with a null byte. | |
238 | * | |
239 | * fgets() deletes CR, '\\r', from the string. This insures only a '\\n' | |
240 | * terminates the string for Windows text files which use CRLF for newline. | |
241 | * | |
242 | * \param[out] str Pointer to the array where the string is stored. | |
243 | * \param[in] num Maximum number of characters to be read | |
244 | * (including the final null byte). Usually the length | |
245 | * of the array \a str is used. | |
246 | * \param[in] delim Optional set of delimiters. The default is "\n". | |
247 | * | |
248 | * \return For success fgets() returns the length of the string in \a str. | |
249 | * If no data is read, fgets() returns zero for EOF or -1 if an error occurred. | |
250 | **/ | |
251 | int16_t SdBaseFile::fgets(char* str, int16_t num, char* delim) { | |
252 | char ch; | |
253 | int16_t n = 0; | |
254 | int16_t r = -1; | |
255 | while ((n + 1) < num && (r = read(&ch, 1)) == 1) { | |
256 | // delete CR | |
257 | if (ch == '\r') continue; | |
258 | str[n++] = ch; | |
259 | if (!delim) { | |
260 | if (ch == '\n') break; | |
261 | } else { | |
262 | if (strchr(delim, ch)) break; | |
263 | } | |
264 | } | |
265 | if (r < 0) { | |
266 | // read error | |
267 | return -1; | |
268 | } | |
269 | str[n] = '\0'; | |
270 | return n; | |
271 | } | |
272 | //------------------------------------------------------------------------------ | |
273 | /** Get a file's name | |
274 | * | |
275 | * \param[out] name An array of 13 characters for the file's name. | |
276 | * | |
277 | * \return The value one, true, is returned for success and | |
278 | * the value zero, false, is returned for failure. | |
279 | */ | |
280 | bool SdBaseFile::getFilename(char* name) { | |
281 | if (!isOpen()) return false; | |
282 | ||
283 | if (isRoot()) { | |
284 | name[0] = '/'; | |
285 | name[1] = '\0'; | |
286 | return true; | |
287 | } | |
288 | // cache entry | |
289 | dir_t* p = cacheDirEntry(SdVolume::CACHE_FOR_READ); | |
290 | if (!p) return false; | |
291 | ||
292 | // format name | |
293 | dirName(*p, name); | |
294 | return true; | |
295 | } | |
296 | //------------------------------------------------------------------------------ | |
297 | void SdBaseFile::getpos(fpos_t* pos) { | |
298 | pos->position = curPosition_; | |
299 | pos->cluster = curCluster_; | |
300 | } | |
301 | ||
302 | //------------------------------------------------------------------------------ | |
303 | /** List directory contents. | |
304 | * | |
305 | * \param[in] pr Print stream for list. | |
306 | * | |
307 | * \param[in] flags The inclusive OR of | |
308 | * | |
309 | * LS_DATE - %Print file modification date | |
310 | * | |
311 | * LS_SIZE - %Print file size. | |
312 | * | |
313 | * LS_R - Recursive list of subdirectories. | |
314 | * | |
315 | * \param[in] indent Amount of space before file name. Used for recursive | |
316 | * list to indicate subdirectory level. | |
317 | */ | |
318 | void SdBaseFile::ls(uint8_t flags, uint8_t indent) { | |
319 | rewind(); | |
320 | int8_t status; | |
321 | while ((status = lsPrintNext( flags, indent))) { | |
322 | if (status > 1 && (flags & LS_R)) { | |
323 | uint16_t index = curPosition()/32 - 1; | |
324 | SdBaseFile s; | |
325 | if (s.open(this, index, O_READ)) s.ls( flags, indent + 2); | |
326 | seekSet(32 * (index + 1)); | |
327 | } | |
328 | } | |
329 | } | |
330 | //------------------------------------------------------------------------------ | |
331 | // saves 32 bytes on stack for ls recursion | |
332 | // return 0 - EOF, 1 - normal file, or 2 - directory | |
333 | int8_t SdBaseFile::lsPrintNext( uint8_t flags, uint8_t indent) { | |
334 | dir_t dir; | |
335 | uint8_t w = 0; | |
336 | ||
337 | while (1) { | |
338 | if (read(&dir, sizeof(dir)) != sizeof(dir)) return 0; | |
339 | if (dir.name[0] == DIR_NAME_FREE) return 0; | |
340 | ||
341 | // skip deleted entry and entries for . and .. | |
342 | if (dir.name[0] != DIR_NAME_DELETED && dir.name[0] != '.' | |
343 | && DIR_IS_FILE_OR_SUBDIR(&dir)) break; | |
344 | } | |
345 | // indent for dir level | |
346 | for (uint8_t i = 0; i < indent; i++) MYSERIAL.write(' '); | |
347 | ||
348 | // print name | |
349 | for (uint8_t i = 0; i < 11; i++) { | |
350 | if (dir.name[i] == ' ')continue; | |
351 | if (i == 8) { | |
352 | MYSERIAL.write('.'); | |
353 | w++; | |
354 | } | |
355 | MYSERIAL.write(dir.name[i]); | |
356 | w++; | |
357 | } | |
358 | if (DIR_IS_SUBDIR(&dir)) { | |
359 | MYSERIAL.write('/'); | |
360 | w++; | |
361 | } | |
362 | if (flags & (LS_DATE | LS_SIZE)) { | |
363 | while (w++ < 14) MYSERIAL.write(' '); | |
364 | } | |
365 | // print modify date/time if requested | |
366 | if (flags & LS_DATE) { | |
367 | MYSERIAL.write(' '); | |
368 | printFatDate( dir.lastWriteDate); | |
369 | MYSERIAL.write(' '); | |
370 | printFatTime( dir.lastWriteTime); | |
371 | } | |
372 | // print size if requested | |
373 | if (!DIR_IS_SUBDIR(&dir) && (flags & LS_SIZE)) { | |
374 | MYSERIAL.write(' '); | |
375 | MYSERIAL.print(dir.fileSize); | |
376 | } | |
377 | MYSERIAL.println(); | |
378 | return DIR_IS_FILE(&dir) ? 1 : 2; | |
379 | } | |
380 | //------------------------------------------------------------------------------ | |
381 | // format directory name field from a 8.3 name string | |
382 | bool SdBaseFile::make83Name(const char* str, uint8_t* name, const char** ptr) { | |
383 | uint8_t c; | |
384 | uint8_t n = 7; // max index for part before dot | |
385 | uint8_t i = 0; | |
386 | // blank fill name and extension | |
387 | while (i < 11) name[i++] = ' '; | |
388 | i = 0; | |
389 | while (*str != '\0' && *str != '/') { | |
390 | c = *str++; | |
391 | if (c == '.') { | |
392 | if (n == 10) goto fail; // only one dot allowed | |
393 | n = 10; // max index for full 8.3 name | |
394 | i = 8; // place for extension | |
395 | } else { | |
396 | // illegal FAT characters | |
397 | PGM_P p = PSTR("|<>^+=?/[];,*\"\\"); | |
398 | uint8_t b; | |
399 | while ((b = pgm_read_byte(p++))) if (b == c) goto fail; | |
400 | // check size and only allow ASCII printable characters | |
401 | if (i > n || c < 0X21 || c > 0X7E)goto fail; | |
402 | // only upper case allowed in 8.3 names - convert lower to upper | |
403 | name[i++] = c < 'a' || c > 'z' ? c : c + ('A' - 'a'); | |
404 | } | |
405 | } | |
406 | *ptr = str; | |
407 | // must have a file name, extension is optional | |
408 | return name[0] != ' '; | |
409 | ||
410 | fail: | |
411 | return false; | |
412 | } | |
413 | //------------------------------------------------------------------------------ | |
414 | /** Make a new directory. | |
415 | * | |
416 | * \param[in] parent An open SdFat instance for the directory that will contain | |
417 | * the new directory. | |
418 | * | |
419 | * \param[in] path A path with a valid 8.3 DOS name for the new directory. | |
420 | * | |
421 | * \param[in] pFlag Create missing parent directories if true. | |
422 | * | |
423 | * \return The value one, true, is returned for success and | |
424 | * the value zero, false, is returned for failure. | |
425 | * Reasons for failure include this file is already open, \a parent is not a | |
426 | * directory, \a path is invalid or already exists in \a parent. | |
427 | */ | |
428 | bool SdBaseFile::mkdir(SdBaseFile* parent, const char* path, bool pFlag) { | |
429 | uint8_t dname[11]; | |
430 | SdBaseFile dir1, dir2; | |
431 | SdBaseFile* sub = &dir1; | |
432 | SdBaseFile* start = parent; | |
433 | ||
434 | if (!parent || isOpen()) goto fail; | |
435 | ||
436 | if (*path == '/') { | |
437 | while (*path == '/') path++; | |
438 | if (!parent->isRoot()) { | |
439 | if (!dir2.openRoot(parent->vol_)) goto fail; | |
440 | parent = &dir2; | |
441 | } | |
442 | } | |
443 | while (1) { | |
444 | if (!make83Name(path, dname, &path)) goto fail; | |
445 | while (*path == '/') path++; | |
446 | if (!*path) break; | |
447 | if (!sub->open(parent, dname, O_READ)) { | |
448 | if (!pFlag || !sub->mkdir(parent, dname)) { | |
449 | goto fail; | |
450 | } | |
451 | } | |
452 | if (parent != start) parent->close(); | |
453 | parent = sub; | |
454 | sub = parent != &dir1 ? &dir1 : &dir2; | |
455 | } | |
456 | return mkdir(parent, dname); | |
457 | ||
458 | fail: | |
459 | return false; | |
460 | } | |
461 | //------------------------------------------------------------------------------ | |
462 | bool SdBaseFile::mkdir(SdBaseFile* parent, const uint8_t dname[11]) { | |
463 | uint32_t block; | |
464 | dir_t d; | |
465 | dir_t* p; | |
466 | ||
467 | if (!parent->isDir()) goto fail; | |
468 | ||
469 | // create a normal file | |
470 | if (!open(parent, dname, O_CREAT | O_EXCL | O_RDWR)) goto fail; | |
471 | ||
472 | // convert file to directory | |
473 | flags_ = O_READ; | |
474 | type_ = FAT_FILE_TYPE_SUBDIR; | |
475 | ||
476 | // allocate and zero first cluster | |
477 | if (!addDirCluster())goto fail; | |
478 | ||
479 | // force entry to SD | |
480 | if (!sync()) goto fail; | |
481 | ||
482 | // cache entry - should already be in cache due to sync() call | |
483 | p = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); | |
484 | if (!p) goto fail; | |
485 | ||
486 | // change directory entry attribute | |
487 | p->attributes = DIR_ATT_DIRECTORY; | |
488 | ||
489 | // make entry for '.' | |
490 | memcpy(&d, p, sizeof(d)); | |
491 | d.name[0] = '.'; | |
492 | for (uint8_t i = 1; i < 11; i++) d.name[i] = ' '; | |
493 | ||
494 | // cache block for '.' and '..' | |
495 | block = vol_->clusterStartBlock(firstCluster_); | |
496 | if (!vol_->cacheRawBlock(block, SdVolume::CACHE_FOR_WRITE)) goto fail; | |
497 | ||
498 | // copy '.' to block | |
499 | memcpy(&vol_->cache()->dir[0], &d, sizeof(d)); | |
500 | ||
501 | // make entry for '..' | |
502 | d.name[1] = '.'; | |
503 | if (parent->isRoot()) { | |
504 | d.firstClusterLow = 0; | |
505 | d.firstClusterHigh = 0; | |
506 | } else { | |
507 | d.firstClusterLow = parent->firstCluster_ & 0XFFFF; | |
508 | d.firstClusterHigh = parent->firstCluster_ >> 16; | |
509 | } | |
510 | // copy '..' to block | |
511 | memcpy(&vol_->cache()->dir[1], &d, sizeof(d)); | |
512 | ||
513 | // write first block | |
514 | return vol_->cacheFlush(); | |
515 | ||
516 | fail: | |
517 | return false; | |
518 | } | |
519 | //------------------------------------------------------------------------------ | |
520 | /** Open a file in the current working directory. | |
521 | * | |
522 | * \param[in] path A path with a valid 8.3 DOS name for a file to be opened. | |
523 | * | |
524 | * \param[in] oflag Values for \a oflag are constructed by a bitwise-inclusive | |
525 | * OR of open flags. see SdBaseFile::open(SdBaseFile*, const char*, uint8_t). | |
526 | * | |
527 | * \return The value one, true, is returned for success and | |
528 | * the value zero, false, is returned for failure. | |
529 | */ | |
530 | bool SdBaseFile::open(const char* path, uint8_t oflag) { | |
531 | return open(cwd_, path, oflag); | |
532 | } | |
533 | //------------------------------------------------------------------------------ | |
534 | /** Open a file or directory by name. | |
535 | * | |
536 | * \param[in] dirFile An open SdFat instance for the directory containing the | |
537 | * file to be opened. | |
538 | * | |
539 | * \param[in] path A path with a valid 8.3 DOS name for a file to be opened. | |
540 | * | |
541 | * \param[in] oflag Values for \a oflag are constructed by a bitwise-inclusive | |
542 | * OR of flags from the following list | |
543 | * | |
544 | * O_READ - Open for reading. | |
545 | * | |
546 | * O_RDONLY - Same as O_READ. | |
547 | * | |
548 | * O_WRITE - Open for writing. | |
549 | * | |
550 | * O_WRONLY - Same as O_WRITE. | |
551 | * | |
552 | * O_RDWR - Open for reading and writing. | |
553 | * | |
554 | * O_APPEND - If set, the file offset shall be set to the end of the | |
555 | * file prior to each write. | |
556 | * | |
557 | * O_AT_END - Set the initial position at the end of the file. | |
558 | * | |
559 | * O_CREAT - If the file exists, this flag has no effect except as noted | |
560 | * under O_EXCL below. Otherwise, the file shall be created | |
561 | * | |
562 | * O_EXCL - If O_CREAT and O_EXCL are set, open() shall fail if the file exists. | |
563 | * | |
564 | * O_SYNC - Call sync() after each write. This flag should not be used with | |
565 | * write(uint8_t), write_P(PGM_P), writeln_P(PGM_P), or the Arduino Print class. | |
566 | * These functions do character at a time writes so sync() will be called | |
567 | * after each byte. | |
568 | * | |
569 | * O_TRUNC - If the file exists and is a regular file, and the file is | |
570 | * successfully opened and is not read only, its length shall be truncated to 0. | |
571 | * | |
572 | * WARNING: A given file must not be opened by more than one SdBaseFile object | |
573 | * of file corruption may occur. | |
574 | * | |
575 | * \note Directory files must be opened read only. Write and truncation is | |
576 | * not allowed for directory files. | |
577 | * | |
578 | * \return The value one, true, is returned for success and | |
579 | * the value zero, false, is returned for failure. | |
580 | * Reasons for failure include this file is already open, \a dirFile is not | |
581 | * a directory, \a path is invalid, the file does not exist | |
582 | * or can't be opened in the access mode specified by oflag. | |
583 | */ | |
584 | bool SdBaseFile::open(SdBaseFile* dirFile, const char* path, uint8_t oflag) { | |
585 | uint8_t dname[11]; | |
586 | SdBaseFile dir1, dir2; | |
587 | SdBaseFile *parent = dirFile; | |
588 | SdBaseFile *sub = &dir1; | |
589 | ||
590 | if (!dirFile) goto fail; | |
591 | ||
592 | // error if already open | |
593 | if (isOpen()) goto fail; | |
594 | ||
595 | if (*path == '/') { | |
596 | while (*path == '/') path++; | |
597 | if (!dirFile->isRoot()) { | |
598 | if (!dir2.openRoot(dirFile->vol_)) goto fail; | |
599 | parent = &dir2; | |
600 | } | |
601 | } | |
602 | while (1) { | |
603 | if (!make83Name(path, dname, &path)) goto fail; | |
604 | while (*path == '/') path++; | |
605 | if (!*path) break; | |
606 | if (!sub->open(parent, dname, O_READ)) goto fail; | |
607 | if (parent != dirFile) parent->close(); | |
608 | parent = sub; | |
609 | sub = parent != &dir1 ? &dir1 : &dir2; | |
610 | } | |
611 | return open(parent, dname, oflag); | |
612 | ||
613 | fail: | |
614 | return false; | |
615 | } | |
616 | //------------------------------------------------------------------------------ | |
617 | // open with filename in dname | |
618 | bool SdBaseFile::open(SdBaseFile* dirFile, | |
619 | const uint8_t dname[11], uint8_t oflag) { | |
620 | bool emptyFound = false; | |
621 | bool fileFound = false; | |
622 | uint8_t index; | |
623 | dir_t* p; | |
624 | ||
625 | vol_ = dirFile->vol_; | |
626 | ||
627 | dirFile->rewind(); | |
628 | // search for file | |
629 | ||
630 | while (dirFile->curPosition_ < dirFile->fileSize_) { | |
631 | index = 0XF & (dirFile->curPosition_ >> 5); | |
632 | p = dirFile->readDirCache(); | |
633 | if (!p) goto fail; | |
634 | ||
635 | if (p->name[0] == DIR_NAME_FREE || p->name[0] == DIR_NAME_DELETED) { | |
636 | // remember first empty slot | |
637 | if (!emptyFound) { | |
638 | dirBlock_ = dirFile->vol_->cacheBlockNumber(); | |
639 | dirIndex_ = index; | |
640 | emptyFound = true; | |
641 | } | |
642 | // done if no entries follow | |
643 | if (p->name[0] == DIR_NAME_FREE) break; | |
644 | } else if (!memcmp(dname, p->name, 11)) { | |
645 | fileFound = true; | |
646 | break; | |
647 | } | |
648 | } | |
649 | if (fileFound) { | |
650 | // don't open existing file if O_EXCL | |
651 | if (oflag & O_EXCL) goto fail; | |
652 | } else { | |
653 | // don't create unless O_CREAT and O_WRITE | |
654 | if (!(oflag & O_CREAT) || !(oflag & O_WRITE)) goto fail; | |
655 | if (emptyFound) { | |
656 | index = dirIndex_; | |
657 | p = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); | |
658 | if (!p) goto fail; | |
659 | } else { | |
660 | if (dirFile->type_ == FAT_FILE_TYPE_ROOT_FIXED) goto fail; | |
661 | ||
662 | // add and zero cluster for dirFile - first cluster is in cache for write | |
663 | if (!dirFile->addDirCluster()) goto fail; | |
664 | ||
665 | // use first entry in cluster | |
666 | p = dirFile->vol_->cache()->dir; | |
667 | index = 0; | |
668 | } | |
669 | // initialize as empty file | |
670 | memset(p, 0, sizeof(dir_t)); | |
671 | memcpy(p->name, dname, 11); | |
672 | ||
673 | // set timestamps | |
674 | if (dateTime_) { | |
675 | // call user date/time function | |
676 | dateTime_(&p->creationDate, &p->creationTime); | |
677 | } else { | |
678 | // use default date/time | |
679 | p->creationDate = FAT_DEFAULT_DATE; | |
680 | p->creationTime = FAT_DEFAULT_TIME; | |
681 | } | |
682 | p->lastAccessDate = p->creationDate; | |
683 | p->lastWriteDate = p->creationDate; | |
684 | p->lastWriteTime = p->creationTime; | |
685 | ||
686 | // write entry to SD | |
687 | if (!dirFile->vol_->cacheFlush()) goto fail; | |
688 | } | |
689 | // open entry in cache | |
690 | return openCachedEntry(index, oflag); | |
691 | ||
692 | fail: | |
693 | return false; | |
694 | } | |
695 | //------------------------------------------------------------------------------ | |
696 | /** Open a file by index. | |
697 | * | |
698 | * \param[in] dirFile An open SdFat instance for the directory. | |
699 | * | |
700 | * \param[in] index The \a index of the directory entry for the file to be | |
701 | * opened. The value for \a index is (directory file position)/32. | |
702 | * | |
703 | * \param[in] oflag Values for \a oflag are constructed by a bitwise-inclusive | |
704 | * OR of flags O_READ, O_WRITE, O_TRUNC, and O_SYNC. | |
705 | * | |
706 | * See open() by path for definition of flags. | |
707 | * \return true for success or false for failure. | |
708 | */ | |
709 | bool SdBaseFile::open(SdBaseFile* dirFile, uint16_t index, uint8_t oflag) { | |
710 | dir_t* p; | |
711 | ||
712 | vol_ = dirFile->vol_; | |
713 | ||
714 | // error if already open | |
715 | if (isOpen() || !dirFile) goto fail; | |
716 | ||
717 | // don't open existing file if O_EXCL - user call error | |
718 | if (oflag & O_EXCL) goto fail; | |
719 | ||
720 | // seek to location of entry | |
721 | if (!dirFile->seekSet(32 * index)) goto fail; | |
722 | ||
723 | // read entry into cache | |
724 | p = dirFile->readDirCache(); | |
725 | if (!p) goto fail; | |
726 | ||
727 | // error if empty slot or '.' or '..' | |
728 | if (p->name[0] == DIR_NAME_FREE || | |
729 | p->name[0] == DIR_NAME_DELETED || p->name[0] == '.') { | |
730 | goto fail; | |
731 | } | |
732 | // open cached entry | |
733 | return openCachedEntry(index & 0XF, oflag); | |
734 | ||
735 | fail: | |
736 | return false; | |
737 | } | |
738 | //------------------------------------------------------------------------------ | |
739 | // open a cached directory entry. Assumes vol_ is initialized | |
740 | bool SdBaseFile::openCachedEntry(uint8_t dirIndex, uint8_t oflag) { | |
741 | // location of entry in cache | |
742 | dir_t* p = &vol_->cache()->dir[dirIndex]; | |
743 | ||
744 | // write or truncate is an error for a directory or read-only file | |
745 | if (p->attributes & (DIR_ATT_READ_ONLY | DIR_ATT_DIRECTORY)) { | |
746 | if (oflag & (O_WRITE | O_TRUNC)) goto fail; | |
747 | } | |
748 | // remember location of directory entry on SD | |
749 | dirBlock_ = vol_->cacheBlockNumber(); | |
750 | dirIndex_ = dirIndex; | |
751 | ||
752 | // copy first cluster number for directory fields | |
753 | firstCluster_ = (uint32_t)p->firstClusterHigh << 16; | |
754 | firstCluster_ |= p->firstClusterLow; | |
755 | ||
756 | // make sure it is a normal file or subdirectory | |
757 | if (DIR_IS_FILE(p)) { | |
758 | fileSize_ = p->fileSize; | |
759 | type_ = FAT_FILE_TYPE_NORMAL; | |
760 | } else if (DIR_IS_SUBDIR(p)) { | |
761 | if (!vol_->chainSize(firstCluster_, &fileSize_)) goto fail; | |
762 | type_ = FAT_FILE_TYPE_SUBDIR; | |
763 | } else { | |
764 | goto fail; | |
765 | } | |
766 | // save open flags for read/write | |
767 | flags_ = oflag & F_OFLAG; | |
768 | ||
769 | // set to start of file | |
770 | curCluster_ = 0; | |
771 | curPosition_ = 0; | |
772 | if ((oflag & O_TRUNC) && !truncate(0)) return false; | |
773 | return oflag & O_AT_END ? seekEnd(0) : true; | |
774 | ||
775 | fail: | |
776 | type_ = FAT_FILE_TYPE_CLOSED; | |
777 | return false; | |
778 | } | |
779 | //------------------------------------------------------------------------------ | |
780 | /** Open the next file or subdirectory in a directory. | |
781 | * | |
782 | * \param[in] dirFile An open SdFat instance for the directory containing the | |
783 | * file to be opened. | |
784 | * | |
785 | * \param[in] oflag Values for \a oflag are constructed by a bitwise-inclusive | |
786 | * OR of flags O_READ, O_WRITE, O_TRUNC, and O_SYNC. | |
787 | * | |
788 | * See open() by path for definition of flags. | |
789 | * \return true for success or false for failure. | |
790 | */ | |
791 | bool SdBaseFile::openNext(SdBaseFile* dirFile, uint8_t oflag) { | |
792 | dir_t* p; | |
793 | uint8_t index; | |
794 | ||
795 | if (!dirFile) goto fail; | |
796 | ||
797 | // error if already open | |
798 | if (isOpen()) goto fail; | |
799 | ||
800 | vol_ = dirFile->vol_; | |
801 | ||
802 | while (1) { | |
803 | index = 0XF & (dirFile->curPosition_ >> 5); | |
804 | ||
805 | // read entry into cache | |
806 | p = dirFile->readDirCache(); | |
807 | if (!p) goto fail; | |
808 | ||
809 | // done if last entry | |
810 | if (p->name[0] == DIR_NAME_FREE) goto fail; | |
811 | ||
812 | // skip empty slot or '.' or '..' | |
813 | if (p->name[0] == DIR_NAME_DELETED || p->name[0] == '.') { | |
814 | continue; | |
815 | } | |
816 | // must be file or dir | |
817 | if (DIR_IS_FILE_OR_SUBDIR(p)) { | |
818 | return openCachedEntry(index, oflag); | |
819 | } | |
820 | } | |
821 | ||
822 | fail: | |
823 | return false; | |
824 | } | |
825 | //------------------------------------------------------------------------------ | |
826 | /** Open a directory's parent directory. | |
827 | * | |
828 | * \param[in] dir Parent of this directory will be opened. Must not be root. | |
829 | * | |
830 | * \return The value one, true, is returned for success and | |
831 | * the value zero, false, is returned for failure. | |
832 | */ | |
833 | bool SdBaseFile::openParent(SdBaseFile* dir) { | |
834 | dir_t entry; | |
835 | dir_t* p; | |
836 | SdBaseFile file; | |
837 | uint32_t c; | |
838 | uint32_t cluster; | |
839 | uint32_t lbn; | |
840 | // error if already open or dir is root or dir is not a directory | |
841 | if (isOpen() || !dir || dir->isRoot() || !dir->isDir()) goto fail; | |
842 | vol_ = dir->vol_; | |
843 | // position to '..' | |
844 | if (!dir->seekSet(32)) goto fail; | |
845 | // read '..' entry | |
846 | if (dir->read(&entry, sizeof(entry)) != 32) goto fail; | |
847 | // verify it is '..' | |
848 | if (entry.name[0] != '.' || entry.name[1] != '.') goto fail; | |
849 | // start cluster for '..' | |
850 | cluster = entry.firstClusterLow; | |
851 | cluster |= (uint32_t)entry.firstClusterHigh << 16; | |
852 | if (cluster == 0) return openRoot(vol_); | |
853 | // start block for '..' | |
854 | lbn = vol_->clusterStartBlock(cluster); | |
855 | // first block of parent dir | |
856 | if (!vol_->cacheRawBlock(lbn, SdVolume::CACHE_FOR_READ)) { | |
857 | goto fail; | |
858 | } | |
859 | p = &vol_->cacheBuffer_.dir[1]; | |
860 | // verify name for '../..' | |
861 | if (p->name[0] != '.' || p->name[1] != '.') goto fail; | |
862 | // '..' is pointer to first cluster of parent. open '../..' to find parent | |
863 | if (p->firstClusterHigh == 0 && p->firstClusterLow == 0) { | |
864 | if (!file.openRoot(dir->volume())) goto fail; | |
865 | } else { | |
866 | if (!file.openCachedEntry(1, O_READ)) goto fail; | |
867 | } | |
868 | // search for parent in '../..' | |
869 | do { | |
870 | if (file.readDir(&entry) != 32) goto fail; | |
871 | c = entry.firstClusterLow; | |
872 | c |= (uint32_t)entry.firstClusterHigh << 16; | |
873 | } while (c != cluster); | |
874 | // open parent | |
875 | return open(&file, file.curPosition()/32 - 1, O_READ); | |
876 | ||
877 | fail: | |
878 | return false; | |
879 | } | |
880 | //------------------------------------------------------------------------------ | |
881 | /** Open a volume's root directory. | |
882 | * | |
883 | * \param[in] vol The FAT volume containing the root directory to be opened. | |
884 | * | |
885 | * \return The value one, true, is returned for success and | |
886 | * the value zero, false, is returned for failure. | |
887 | * Reasons for failure include the file is already open, the FAT volume has | |
888 | * not been initialized or it a FAT12 volume. | |
889 | */ | |
890 | bool SdBaseFile::openRoot(SdVolume* vol) { | |
891 | // error if file is already open | |
892 | if (isOpen()) goto fail; | |
893 | ||
894 | if (vol->fatType() == 16 || (FAT12_SUPPORT && vol->fatType() == 12)) { | |
895 | type_ = FAT_FILE_TYPE_ROOT_FIXED; | |
896 | firstCluster_ = 0; | |
897 | fileSize_ = 32 * vol->rootDirEntryCount(); | |
898 | } else if (vol->fatType() == 32) { | |
899 | type_ = FAT_FILE_TYPE_ROOT32; | |
900 | firstCluster_ = vol->rootDirStart(); | |
901 | if (!vol->chainSize(firstCluster_, &fileSize_)) goto fail; | |
902 | } else { | |
903 | // volume is not initialized, invalid, or FAT12 without support | |
904 | return false; | |
905 | } | |
906 | vol_ = vol; | |
907 | // read only | |
908 | flags_ = O_READ; | |
909 | ||
910 | // set to start of file | |
911 | curCluster_ = 0; | |
912 | curPosition_ = 0; | |
913 | ||
914 | // root has no directory entry | |
915 | dirBlock_ = 0; | |
916 | dirIndex_ = 0; | |
917 | return true; | |
918 | ||
919 | fail: | |
920 | return false; | |
921 | } | |
922 | //------------------------------------------------------------------------------ | |
923 | /** Return the next available byte without consuming it. | |
924 | * | |
925 | * \return The byte if no error and not at eof else -1; | |
926 | */ | |
927 | int SdBaseFile::peek() { | |
928 | fpos_t pos; | |
929 | getpos(&pos); | |
930 | int c = read(); | |
931 | if (c >= 0) setpos(&pos); | |
932 | return c; | |
933 | } | |
934 | ||
935 | //------------------------------------------------------------------------------ | |
936 | /** %Print the name field of a directory entry in 8.3 format. | |
937 | * \param[in] pr Print stream for output. | |
938 | * \param[in] dir The directory structure containing the name. | |
939 | * \param[in] width Blank fill name if length is less than \a width. | |
940 | * \param[in] printSlash Print '/' after directory names if true. | |
941 | */ | |
942 | void SdBaseFile::printDirName(const dir_t& dir, | |
943 | uint8_t width, bool printSlash) { | |
944 | uint8_t w = 0; | |
945 | for (uint8_t i = 0; i < 11; i++) { | |
946 | if (dir.name[i] == ' ')continue; | |
947 | if (i == 8) { | |
948 | MYSERIAL.write('.'); | |
949 | w++; | |
950 | } | |
951 | MYSERIAL.write(dir.name[i]); | |
952 | w++; | |
953 | } | |
954 | if (DIR_IS_SUBDIR(&dir) && printSlash) { | |
955 | MYSERIAL.write('/'); | |
956 | w++; | |
957 | } | |
958 | while (w < width) { | |
959 | MYSERIAL.write(' '); | |
960 | w++; | |
961 | } | |
962 | } | |
963 | //------------------------------------------------------------------------------ | |
964 | // print uint8_t with width 2 | |
965 | static void print2u( uint8_t v) { | |
966 | if (v < 10) MYSERIAL.write('0'); | |
967 | MYSERIAL.print(v, DEC); | |
968 | } | |
969 | //------------------------------------------------------------------------------ | |
970 | /** %Print a directory date field to Serial. | |
971 | * | |
972 | * Format is yyyy-mm-dd. | |
973 | * | |
974 | * \param[in] fatDate The date field from a directory entry. | |
975 | */ | |
976 | ||
977 | //------------------------------------------------------------------------------ | |
978 | /** %Print a directory date field. | |
979 | * | |
980 | * Format is yyyy-mm-dd. | |
981 | * | |
982 | * \param[in] pr Print stream for output. | |
983 | * \param[in] fatDate The date field from a directory entry. | |
984 | */ | |
985 | void SdBaseFile::printFatDate(uint16_t fatDate) { | |
986 | MYSERIAL.print(FAT_YEAR(fatDate)); | |
987 | MYSERIAL.write('-'); | |
988 | print2u( FAT_MONTH(fatDate)); | |
989 | MYSERIAL.write('-'); | |
990 | print2u( FAT_DAY(fatDate)); | |
991 | } | |
992 | ||
993 | //------------------------------------------------------------------------------ | |
994 | /** %Print a directory time field. | |
995 | * | |
996 | * Format is hh:mm:ss. | |
997 | * | |
998 | * \param[in] pr Print stream for output. | |
999 | * \param[in] fatTime The time field from a directory entry. | |
1000 | */ | |
1001 | void SdBaseFile::printFatTime( uint16_t fatTime) { | |
1002 | print2u( FAT_HOUR(fatTime)); | |
1003 | MYSERIAL.write(':'); | |
1004 | print2u( FAT_MINUTE(fatTime)); | |
1005 | MYSERIAL.write(':'); | |
1006 | print2u( FAT_SECOND(fatTime)); | |
1007 | } | |
1008 | //------------------------------------------------------------------------------ | |
1009 | /** Print a file's name to Serial | |
1010 | * | |
1011 | * \return The value one, true, is returned for success and | |
1012 | * the value zero, false, is returned for failure. | |
1013 | */ | |
1014 | bool SdBaseFile::printName() { | |
1015 | char name[13]; | |
1016 | if (!getFilename(name)) return false; | |
1017 | MYSERIAL.print(name); | |
1018 | return true; | |
1019 | } | |
1020 | //------------------------------------------------------------------------------ | |
1021 | /** Read the next byte from a file. | |
1022 | * | |
1023 | * \return For success read returns the next byte in the file as an int. | |
1024 | * If an error occurs or end of file is reached -1 is returned. | |
1025 | */ | |
1026 | int16_t SdBaseFile::read() { | |
1027 | uint8_t b; | |
1028 | return read(&b, 1) == 1 ? b : -1; | |
1029 | } | |
1030 | //------------------------------------------------------------------------------ | |
1031 | /** Read data from a file starting at the current position. | |
1032 | * | |
1033 | * \param[out] buf Pointer to the location that will receive the data. | |
1034 | * | |
1035 | * \param[in] nbyte Maximum number of bytes to read. | |
1036 | * | |
1037 | * \return For success read() returns the number of bytes read. | |
1038 | * A value less than \a nbyte, including zero, will be returned | |
1039 | * if end of file is reached. | |
1040 | * If an error occurs, read() returns -1. Possible errors include | |
1041 | * read() called before a file has been opened, corrupt file system | |
1042 | * or an I/O error occurred. | |
1043 | */ | |
1044 | int16_t SdBaseFile::read(void* buf, uint16_t nbyte) { | |
1045 | uint8_t* dst = reinterpret_cast<uint8_t*>(buf); | |
1046 | uint16_t offset; | |
1047 | uint16_t toRead; | |
1048 | uint32_t block; // raw device block number | |
1049 | ||
1050 | // error if not open or write only | |
1051 | if (!isOpen() || !(flags_ & O_READ)) goto fail; | |
1052 | ||
1053 | // max bytes left in file | |
1054 | if (nbyte >= (fileSize_ - curPosition_)) { | |
1055 | nbyte = fileSize_ - curPosition_; | |
1056 | } | |
1057 | // amount left to read | |
1058 | toRead = nbyte; | |
1059 | while (toRead > 0) { | |
1060 | offset = curPosition_ & 0X1FF; // offset in block | |
1061 | if (type_ == FAT_FILE_TYPE_ROOT_FIXED) { | |
1062 | block = vol_->rootDirStart() + (curPosition_ >> 9); | |
1063 | } else { | |
1064 | uint8_t blockOfCluster = vol_->blockOfCluster(curPosition_); | |
1065 | if (offset == 0 && blockOfCluster == 0) { | |
1066 | // start of new cluster | |
1067 | if (curPosition_ == 0) { | |
1068 | // use first cluster in file | |
1069 | curCluster_ = firstCluster_; | |
1070 | } else { | |
1071 | // get next cluster from FAT | |
1072 | if (!vol_->fatGet(curCluster_, &curCluster_)) goto fail; | |
1073 | } | |
1074 | } | |
1075 | block = vol_->clusterStartBlock(curCluster_) + blockOfCluster; | |
1076 | } | |
1077 | uint16_t n = toRead; | |
1078 | ||
1079 | // amount to be read from current block | |
1080 | if (n > (512 - offset)) n = 512 - offset; | |
1081 | ||
1082 | // no buffering needed if n == 512 | |
1083 | if (n == 512 && block != vol_->cacheBlockNumber()) { | |
1084 | if (!vol_->readBlock(block, dst)) goto fail; | |
1085 | } else { | |
1086 | // read block to cache and copy data to caller | |
1087 | if (!vol_->cacheRawBlock(block, SdVolume::CACHE_FOR_READ)) goto fail; | |
1088 | uint8_t* src = vol_->cache()->data + offset; | |
1089 | memcpy(dst, src, n); | |
1090 | } | |
1091 | dst += n; | |
1092 | curPosition_ += n; | |
1093 | toRead -= n; | |
1094 | } | |
1095 | return nbyte; | |
1096 | ||
1097 | fail: | |
1098 | return -1; | |
1099 | } | |
1100 | //------------------------------------------------------------------------------ | |
1101 | /** Read the next directory entry from a directory file. | |
1102 | * | |
1103 | * \param[out] dir The dir_t struct that will receive the data. | |
1104 | * | |
1105 | * \return For success readDir() returns the number of bytes read. | |
1106 | * A value of zero will be returned if end of file is reached. | |
1107 | * If an error occurs, readDir() returns -1. Possible errors include | |
1108 | * readDir() called before a directory has been opened, this is not | |
1109 | * a directory file or an I/O error occurred. | |
1110 | */ | |
1111 | int8_t SdBaseFile::readDir(dir_t* dir) { | |
1112 | int16_t n; | |
1113 | // if not a directory file or miss-positioned return an error | |
1114 | if (!isDir() || (0X1F & curPosition_)) return -1; | |
1115 | ||
1116 | while (1) { | |
1117 | n = read(dir, sizeof(dir_t)); | |
1118 | if (n != sizeof(dir_t)) return n == 0 ? 0 : -1; | |
1119 | // last entry if DIR_NAME_FREE | |
1120 | if (dir->name[0] == DIR_NAME_FREE) return 0; | |
1121 | // skip empty entries and entry for . and .. | |
1122 | if (dir->name[0] == DIR_NAME_DELETED || dir->name[0] == '.') continue; | |
1123 | // return if normal file or subdirectory | |
1124 | if (DIR_IS_FILE_OR_SUBDIR(dir)) return n; | |
1125 | } | |
1126 | } | |
1127 | //------------------------------------------------------------------------------ | |
1128 | // Read next directory entry into the cache | |
1129 | // Assumes file is correctly positioned | |
1130 | dir_t* SdBaseFile::readDirCache() { | |
1131 | uint8_t i; | |
1132 | // error if not directory | |
1133 | if (!isDir()) goto fail; | |
1134 | ||
1135 | // index of entry in cache | |
1136 | i = (curPosition_ >> 5) & 0XF; | |
1137 | ||
1138 | // use read to locate and cache block | |
1139 | if (read() < 0) goto fail; | |
1140 | ||
1141 | // advance to next entry | |
1142 | curPosition_ += 31; | |
1143 | ||
1144 | // return pointer to entry | |
1145 | return vol_->cache()->dir + i; | |
1146 | ||
1147 | fail: | |
1148 | return 0; | |
1149 | } | |
1150 | //------------------------------------------------------------------------------ | |
1151 | /** Remove a file. | |
1152 | * | |
1153 | * The directory entry and all data for the file are deleted. | |
1154 | * | |
1155 | * \note This function should not be used to delete the 8.3 version of a | |
1156 | * file that has a long name. For example if a file has the long name | |
1157 | * "New Text Document.txt" you should not delete the 8.3 name "NEWTEX~1.TXT". | |
1158 | * | |
1159 | * \return The value one, true, is returned for success and | |
1160 | * the value zero, false, is returned for failure. | |
1161 | * Reasons for failure include the file read-only, is a directory, | |
1162 | * or an I/O error occurred. | |
1163 | */ | |
1164 | bool SdBaseFile::remove() { | |
1165 | dir_t* d; | |
1166 | // free any clusters - will fail if read-only or directory | |
1167 | if (!truncate(0)) goto fail; | |
1168 | ||
1169 | // cache directory entry | |
1170 | d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); | |
1171 | if (!d) goto fail; | |
1172 | ||
1173 | // mark entry deleted | |
1174 | d->name[0] = DIR_NAME_DELETED; | |
1175 | ||
1176 | // set this file closed | |
1177 | type_ = FAT_FILE_TYPE_CLOSED; | |
1178 | ||
1179 | // write entry to SD | |
1180 | return vol_->cacheFlush(); | |
1181 | return true; | |
1182 | ||
1183 | fail: | |
1184 | return false; | |
1185 | } | |
1186 | //------------------------------------------------------------------------------ | |
1187 | /** Remove a file. | |
1188 | * | |
1189 | * The directory entry and all data for the file are deleted. | |
1190 | * | |
1191 | * \param[in] dirFile The directory that contains the file. | |
1192 | * \param[in] path Path for the file to be removed. | |
1193 | * | |
1194 | * \note This function should not be used to delete the 8.3 version of a | |
1195 | * file that has a long name. For example if a file has the long name | |
1196 | * "New Text Document.txt" you should not delete the 8.3 name "NEWTEX~1.TXT". | |
1197 | * | |
1198 | * \return The value one, true, is returned for success and | |
1199 | * the value zero, false, is returned for failure. | |
1200 | * Reasons for failure include the file is a directory, is read only, | |
1201 | * \a dirFile is not a directory, \a path is not found | |
1202 | * or an I/O error occurred. | |
1203 | */ | |
1204 | bool SdBaseFile::remove(SdBaseFile* dirFile, const char* path) { | |
1205 | SdBaseFile file; | |
1206 | if (!file.open(dirFile, path, O_WRITE)) goto fail; | |
1207 | return file.remove(); | |
1208 | ||
1209 | fail: | |
1210 | // can't set iostate - static function | |
1211 | return false; | |
1212 | } | |
1213 | //------------------------------------------------------------------------------ | |
1214 | /** Rename a file or subdirectory. | |
1215 | * | |
1216 | * \param[in] dirFile Directory for the new path. | |
1217 | * \param[in] newPath New path name for the file/directory. | |
1218 | * | |
1219 | * \return The value one, true, is returned for success and | |
1220 | * the value zero, false, is returned for failure. | |
1221 | * Reasons for failure include \a dirFile is not open or is not a directory | |
1222 | * file, newPath is invalid or already exists, or an I/O error occurs. | |
1223 | */ | |
1224 | bool SdBaseFile::rename(SdBaseFile* dirFile, const char* newPath) { | |
1225 | dir_t entry; | |
1226 | uint32_t dirCluster = 0; | |
1227 | SdBaseFile file; | |
1228 | dir_t* d; | |
1229 | ||
1230 | // must be an open file or subdirectory | |
1231 | if (!(isFile() || isSubDir())) goto fail; | |
1232 | ||
1233 | // can't move file | |
1234 | if (vol_ != dirFile->vol_) goto fail; | |
1235 | ||
1236 | // sync() and cache directory entry | |
1237 | sync(); | |
1238 | d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); | |
1239 | if (!d) goto fail; | |
1240 | ||
1241 | // save directory entry | |
1242 | memcpy(&entry, d, sizeof(entry)); | |
1243 | ||
1244 | // mark entry deleted | |
1245 | d->name[0] = DIR_NAME_DELETED; | |
1246 | ||
1247 | // make directory entry for new path | |
1248 | if (isFile()) { | |
1249 | if (!file.open(dirFile, newPath, O_CREAT | O_EXCL | O_WRITE)) { | |
1250 | goto restore; | |
1251 | } | |
1252 | } else { | |
1253 | // don't create missing path prefix components | |
1254 | if (!file.mkdir(dirFile, newPath, false)) { | |
1255 | goto restore; | |
1256 | } | |
1257 | // save cluster containing new dot dot | |
1258 | dirCluster = file.firstCluster_; | |
1259 | } | |
1260 | // change to new directory entry | |
1261 | dirBlock_ = file.dirBlock_; | |
1262 | dirIndex_ = file.dirIndex_; | |
1263 | ||
1264 | // mark closed to avoid possible destructor close call | |
1265 | file.type_ = FAT_FILE_TYPE_CLOSED; | |
1266 | ||
1267 | // cache new directory entry | |
1268 | d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); | |
1269 | if (!d) goto fail; | |
1270 | ||
1271 | // copy all but name field to new directory entry | |
1272 | memcpy(&d->attributes, &entry.attributes, sizeof(entry) - sizeof(d->name)); | |
1273 | ||
1274 | // update dot dot if directory | |
1275 | if (dirCluster) { | |
1276 | // get new dot dot | |
1277 | uint32_t block = vol_->clusterStartBlock(dirCluster); | |
1278 | if (!vol_->cacheRawBlock(block, SdVolume::CACHE_FOR_READ)) goto fail; | |
1279 | memcpy(&entry, &vol_->cache()->dir[1], sizeof(entry)); | |
1280 | ||
1281 | // free unused cluster | |
1282 | if (!vol_->freeChain(dirCluster)) goto fail; | |
1283 | ||
1284 | // store new dot dot | |
1285 | block = vol_->clusterStartBlock(firstCluster_); | |
1286 | if (!vol_->cacheRawBlock(block, SdVolume::CACHE_FOR_WRITE)) goto fail; | |
1287 | memcpy(&vol_->cache()->dir[1], &entry, sizeof(entry)); | |
1288 | } | |
1289 | return vol_->cacheFlush(); | |
1290 | ||
1291 | restore: | |
1292 | d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); | |
1293 | if (!d) goto fail; | |
1294 | // restore entry | |
1295 | d->name[0] = entry.name[0]; | |
1296 | vol_->cacheFlush(); | |
1297 | ||
1298 | fail: | |
1299 | return false; | |
1300 | } | |
1301 | //------------------------------------------------------------------------------ | |
1302 | /** Remove a directory file. | |
1303 | * | |
1304 | * The directory file will be removed only if it is empty and is not the | |
1305 | * root directory. rmdir() follows DOS and Windows and ignores the | |
1306 | * read-only attribute for the directory. | |
1307 | * | |
1308 | * \note This function should not be used to delete the 8.3 version of a | |
1309 | * directory that has a long name. For example if a directory has the | |
1310 | * long name "New folder" you should not delete the 8.3 name "NEWFOL~1". | |
1311 | * | |
1312 | * \return The value one, true, is returned for success and | |
1313 | * the value zero, false, is returned for failure. | |
1314 | * Reasons for failure include the file is not a directory, is the root | |
1315 | * directory, is not empty, or an I/O error occurred. | |
1316 | */ | |
1317 | bool SdBaseFile::rmdir() { | |
1318 | // must be open subdirectory | |
1319 | if (!isSubDir()) goto fail; | |
1320 | ||
1321 | rewind(); | |
1322 | ||
1323 | // make sure directory is empty | |
1324 | while (curPosition_ < fileSize_) { | |
1325 | dir_t* p = readDirCache(); | |
1326 | if (!p) goto fail; | |
1327 | // done if past last used entry | |
1328 | if (p->name[0] == DIR_NAME_FREE) break; | |
1329 | // skip empty slot, '.' or '..' | |
1330 | if (p->name[0] == DIR_NAME_DELETED || p->name[0] == '.') continue; | |
1331 | // error not empty | |
1332 | if (DIR_IS_FILE_OR_SUBDIR(p)) goto fail; | |
1333 | } | |
1334 | // convert empty directory to normal file for remove | |
1335 | type_ = FAT_FILE_TYPE_NORMAL; | |
1336 | flags_ |= O_WRITE; | |
1337 | return remove(); | |
1338 | ||
1339 | fail: | |
1340 | return false; | |
1341 | } | |
1342 | //------------------------------------------------------------------------------ | |
1343 | /** Recursively delete a directory and all contained files. | |
1344 | * | |
1345 | * This is like the Unix/Linux 'rm -rf *' if called with the root directory | |
1346 | * hence the name. | |
1347 | * | |
1348 | * Warning - This will remove all contents of the directory including | |
1349 | * subdirectories. The directory will then be removed if it is not root. | |
1350 | * The read-only attribute for files will be ignored. | |
1351 | * | |
1352 | * \note This function should not be used to delete the 8.3 version of | |
1353 | * a directory that has a long name. See remove() and rmdir(). | |
1354 | * | |
1355 | * \return The value one, true, is returned for success and | |
1356 | * the value zero, false, is returned for failure. | |
1357 | */ | |
1358 | bool SdBaseFile::rmRfStar() { | |
1359 | uint16_t index; | |
1360 | SdBaseFile f; | |
1361 | rewind(); | |
1362 | while (curPosition_ < fileSize_) { | |
1363 | // remember position | |
1364 | index = curPosition_/32; | |
1365 | ||
1366 | dir_t* p = readDirCache(); | |
1367 | if (!p) goto fail; | |
1368 | ||
1369 | // done if past last entry | |
1370 | if (p->name[0] == DIR_NAME_FREE) break; | |
1371 | ||
1372 | // skip empty slot or '.' or '..' | |
1373 | if (p->name[0] == DIR_NAME_DELETED || p->name[0] == '.') continue; | |
1374 | ||
1375 | // skip if part of long file name or volume label in root | |
1376 | if (!DIR_IS_FILE_OR_SUBDIR(p)) continue; | |
1377 | ||
1378 | if (!f.open(this, index, O_READ)) goto fail; | |
1379 | if (f.isSubDir()) { | |
1380 | // recursively delete | |
1381 | if (!f.rmRfStar()) goto fail; | |
1382 | } else { | |
1383 | // ignore read-only | |
1384 | f.flags_ |= O_WRITE; | |
1385 | if (!f.remove()) goto fail; | |
1386 | } | |
1387 | // position to next entry if required | |
1388 | if (curPosition_ != (32*(index + 1))) { | |
1389 | if (!seekSet(32*(index + 1))) goto fail; | |
1390 | } | |
1391 | } | |
1392 | // don't try to delete root | |
1393 | if (!isRoot()) { | |
1394 | if (!rmdir()) goto fail; | |
1395 | } | |
1396 | return true; | |
1397 | ||
1398 | fail: | |
1399 | return false; | |
1400 | } | |
1401 | //------------------------------------------------------------------------------ | |
1402 | /** Create a file object and open it in the current working directory. | |
1403 | * | |
1404 | * \param[in] path A path with a valid 8.3 DOS name for a file to be opened. | |
1405 | * | |
1406 | * \param[in] oflag Values for \a oflag are constructed by a bitwise-inclusive | |
1407 | * OR of open flags. see SdBaseFile::open(SdBaseFile*, const char*, uint8_t). | |
1408 | */ | |
1409 | SdBaseFile::SdBaseFile(const char* path, uint8_t oflag) { | |
1410 | type_ = FAT_FILE_TYPE_CLOSED; | |
1411 | writeError = false; | |
1412 | open(path, oflag); | |
1413 | } | |
1414 | //------------------------------------------------------------------------------ | |
1415 | /** Sets a file's position. | |
1416 | * | |
1417 | * \param[in] pos The new position in bytes from the beginning of the file. | |
1418 | * | |
1419 | * \return The value one, true, is returned for success and | |
1420 | * the value zero, false, is returned for failure. | |
1421 | */ | |
1422 | bool SdBaseFile::seekSet(uint32_t pos) { | |
1423 | uint32_t nCur; | |
1424 | uint32_t nNew; | |
1425 | // error if file not open or seek past end of file | |
1426 | if (!isOpen() || pos > fileSize_) goto fail; | |
1427 | ||
1428 | if (type_ == FAT_FILE_TYPE_ROOT_FIXED) { | |
1429 | curPosition_ = pos; | |
1430 | goto done; | |
1431 | } | |
1432 | if (pos == 0) { | |
1433 | // set position to start of file | |
1434 | curCluster_ = 0; | |
1435 | curPosition_ = 0; | |
1436 | goto done; | |
1437 | } | |
1438 | // calculate cluster index for cur and new position | |
1439 | nCur = (curPosition_ - 1) >> (vol_->clusterSizeShift_ + 9); | |
1440 | nNew = (pos - 1) >> (vol_->clusterSizeShift_ + 9); | |
1441 | ||
1442 | if (nNew < nCur || curPosition_ == 0) { | |
1443 | // must follow chain from first cluster | |
1444 | curCluster_ = firstCluster_; | |
1445 | } else { | |
1446 | // advance from curPosition | |
1447 | nNew -= nCur; | |
1448 | } | |
1449 | while (nNew--) { | |
1450 | if (!vol_->fatGet(curCluster_, &curCluster_)) goto fail; | |
1451 | } | |
1452 | curPosition_ = pos; | |
1453 | ||
1454 | done: | |
1455 | return true; | |
1456 | ||
1457 | fail: | |
1458 | return false; | |
1459 | } | |
1460 | //------------------------------------------------------------------------------ | |
1461 | void SdBaseFile::setpos(fpos_t* pos) { | |
1462 | curPosition_ = pos->position; | |
1463 | curCluster_ = pos->cluster; | |
1464 | } | |
1465 | //------------------------------------------------------------------------------ | |
1466 | /** The sync() call causes all modified data and directory fields | |
1467 | * to be written to the storage device. | |
1468 | * | |
1469 | * \return The value one, true, is returned for success and | |
1470 | * the value zero, false, is returned for failure. | |
1471 | * Reasons for failure include a call to sync() before a file has been | |
1472 | * opened or an I/O error. | |
1473 | */ | |
1474 | bool SdBaseFile::sync() { | |
1475 | // only allow open files and directories | |
1476 | if (!isOpen()) goto fail; | |
1477 | ||
1478 | if (flags_ & F_FILE_DIR_DIRTY) { | |
1479 | dir_t* d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); | |
1480 | // check for deleted by another open file object | |
1481 | if (!d || d->name[0] == DIR_NAME_DELETED) goto fail; | |
1482 | ||
1483 | // do not set filesize for dir files | |
1484 | if (!isDir()) d->fileSize = fileSize_; | |
1485 | ||
1486 | // update first cluster fields | |
1487 | d->firstClusterLow = firstCluster_ & 0XFFFF; | |
1488 | d->firstClusterHigh = firstCluster_ >> 16; | |
1489 | ||
1490 | // set modify time if user supplied a callback date/time function | |
1491 | if (dateTime_) { | |
1492 | dateTime_(&d->lastWriteDate, &d->lastWriteTime); | |
1493 | d->lastAccessDate = d->lastWriteDate; | |
1494 | } | |
1495 | // clear directory dirty | |
1496 | flags_ &= ~F_FILE_DIR_DIRTY; | |
1497 | } | |
1498 | return vol_->cacheFlush(); | |
1499 | ||
1500 | fail: | |
1501 | writeError = true; | |
1502 | return false; | |
1503 | } | |
1504 | //------------------------------------------------------------------------------ | |
1505 | /** Copy a file's timestamps | |
1506 | * | |
1507 | * \param[in] file File to copy timestamps from. | |
1508 | * | |
1509 | * \note | |
1510 | * Modify and access timestamps may be overwritten if a date time callback | |
1511 | * function has been set by dateTimeCallback(). | |
1512 | * | |
1513 | * \return The value one, true, is returned for success and | |
1514 | * the value zero, false, is returned for failure. | |
1515 | */ | |
1516 | bool SdBaseFile::timestamp(SdBaseFile* file) { | |
1517 | dir_t* d; | |
1518 | dir_t dir; | |
1519 | ||
1520 | // get timestamps | |
1521 | if (!file->dirEntry(&dir)) goto fail; | |
1522 | ||
1523 | // update directory fields | |
1524 | if (!sync()) goto fail; | |
1525 | ||
1526 | d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); | |
1527 | if (!d) goto fail; | |
1528 | ||
1529 | // copy timestamps | |
1530 | d->lastAccessDate = dir.lastAccessDate; | |
1531 | d->creationDate = dir.creationDate; | |
1532 | d->creationTime = dir.creationTime; | |
1533 | d->creationTimeTenths = dir.creationTimeTenths; | |
1534 | d->lastWriteDate = dir.lastWriteDate; | |
1535 | d->lastWriteTime = dir.lastWriteTime; | |
1536 | ||
1537 | // write back entry | |
1538 | return vol_->cacheFlush(); | |
1539 | ||
1540 | fail: | |
1541 | return false; | |
1542 | } | |
1543 | //------------------------------------------------------------------------------ | |
1544 | /** Set a file's timestamps in its directory entry. | |
1545 | * | |
1546 | * \param[in] flags Values for \a flags are constructed by a bitwise-inclusive | |
1547 | * OR of flags from the following list | |
1548 | * | |
1549 | * T_ACCESS - Set the file's last access date. | |
1550 | * | |
1551 | * T_CREATE - Set the file's creation date and time. | |
1552 | * | |
1553 | * T_WRITE - Set the file's last write/modification date and time. | |
1554 | * | |
1555 | * \param[in] year Valid range 1980 - 2107 inclusive. | |
1556 | * | |
1557 | * \param[in] month Valid range 1 - 12 inclusive. | |
1558 | * | |
1559 | * \param[in] day Valid range 1 - 31 inclusive. | |
1560 | * | |
1561 | * \param[in] hour Valid range 0 - 23 inclusive. | |
1562 | * | |
1563 | * \param[in] minute Valid range 0 - 59 inclusive. | |
1564 | * | |
1565 | * \param[in] second Valid range 0 - 59 inclusive | |
1566 | * | |
1567 | * \note It is possible to set an invalid date since there is no check for | |
1568 | * the number of days in a month. | |
1569 | * | |
1570 | * \note | |
1571 | * Modify and access timestamps may be overwritten if a date time callback | |
1572 | * function has been set by dateTimeCallback(). | |
1573 | * | |
1574 | * \return The value one, true, is returned for success and | |
1575 | * the value zero, false, is returned for failure. | |
1576 | */ | |
1577 | bool SdBaseFile::timestamp(uint8_t flags, uint16_t year, uint8_t month, | |
1578 | uint8_t day, uint8_t hour, uint8_t minute, uint8_t second) { | |
1579 | uint16_t dirDate; | |
1580 | uint16_t dirTime; | |
1581 | dir_t* d; | |
1582 | ||
1583 | if (!isOpen() | |
1584 | || year < 1980 | |
1585 | || year > 2107 | |
1586 | || month < 1 | |
1587 | || month > 12 | |
1588 | || day < 1 | |
1589 | || day > 31 | |
1590 | || hour > 23 | |
1591 | || minute > 59 | |
1592 | || second > 59) { | |
1593 | goto fail; | |
1594 | } | |
1595 | // update directory entry | |
1596 | if (!sync()) goto fail; | |
1597 | ||
1598 | d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); | |
1599 | if (!d) goto fail; | |
1600 | ||
1601 | dirDate = FAT_DATE(year, month, day); | |
1602 | dirTime = FAT_TIME(hour, minute, second); | |
1603 | if (flags & T_ACCESS) { | |
1604 | d->lastAccessDate = dirDate; | |
1605 | } | |
1606 | if (flags & T_CREATE) { | |
1607 | d->creationDate = dirDate; | |
1608 | d->creationTime = dirTime; | |
1609 | // seems to be units of 1/100 second not 1/10 as Microsoft states | |
1610 | d->creationTimeTenths = second & 1 ? 100 : 0; | |
1611 | } | |
1612 | if (flags & T_WRITE) { | |
1613 | d->lastWriteDate = dirDate; | |
1614 | d->lastWriteTime = dirTime; | |
1615 | } | |
1616 | return vol_->cacheFlush(); | |
1617 | ||
1618 | fail: | |
1619 | return false; | |
1620 | } | |
1621 | //------------------------------------------------------------------------------ | |
1622 | /** Truncate a file to a specified length. The current file position | |
1623 | * will be maintained if it is less than or equal to \a length otherwise | |
1624 | * it will be set to end of file. | |
1625 | * | |
1626 | * \param[in] length The desired length for the file. | |
1627 | * | |
1628 | * \return The value one, true, is returned for success and | |
1629 | * the value zero, false, is returned for failure. | |
1630 | * Reasons for failure include file is read only, file is a directory, | |
1631 | * \a length is greater than the current file size or an I/O error occurs. | |
1632 | */ | |
1633 | bool SdBaseFile::truncate(uint32_t length) { | |
1634 | uint32_t newPos; | |
1635 | // error if not a normal file or read-only | |
1636 | if (!isFile() || !(flags_ & O_WRITE)) goto fail; | |
1637 | ||
1638 | // error if length is greater than current size | |
1639 | if (length > fileSize_) goto fail; | |
1640 | ||
1641 | // fileSize and length are zero - nothing to do | |
1642 | if (fileSize_ == 0) return true; | |
1643 | ||
1644 | // remember position for seek after truncation | |
1645 | newPos = curPosition_ > length ? length : curPosition_; | |
1646 | ||
1647 | // position to last cluster in truncated file | |
1648 | if (!seekSet(length)) goto fail; | |
1649 | ||
1650 | if (length == 0) { | |
1651 | // free all clusters | |
1652 | if (!vol_->freeChain(firstCluster_)) goto fail; | |
1653 | firstCluster_ = 0; | |
1654 | } else { | |
1655 | uint32_t toFree; | |
1656 | if (!vol_->fatGet(curCluster_, &toFree)) goto fail; | |
1657 | ||
1658 | if (!vol_->isEOC(toFree)) { | |
1659 | // free extra clusters | |
1660 | if (!vol_->freeChain(toFree)) goto fail; | |
1661 | ||
1662 | // current cluster is end of chain | |
1663 | if (!vol_->fatPutEOC(curCluster_)) goto fail; | |
1664 | } | |
1665 | } | |
1666 | fileSize_ = length; | |
1667 | ||
1668 | // need to update directory entry | |
1669 | flags_ |= F_FILE_DIR_DIRTY; | |
1670 | ||
1671 | if (!sync()) goto fail; | |
1672 | ||
1673 | // set file to correct position | |
1674 | return seekSet(newPos); | |
1675 | ||
1676 | fail: | |
1677 | return false; | |
1678 | } | |
1679 | //------------------------------------------------------------------------------ | |
1680 | /** Write data to an open file. | |
1681 | * | |
1682 | * \note Data is moved to the cache but may not be written to the | |
1683 | * storage device until sync() is called. | |
1684 | * | |
1685 | * \param[in] buf Pointer to the location of the data to be written. | |
1686 | * | |
1687 | * \param[in] nbyte Number of bytes to write. | |
1688 | * | |
1689 | * \return For success write() returns the number of bytes written, always | |
1690 | * \a nbyte. If an error occurs, write() returns -1. Possible errors | |
1691 | * include write() is called before a file has been opened, write is called | |
1692 | * for a read-only file, device is full, a corrupt file system or an I/O error. | |
1693 | * | |
1694 | */ | |
1695 | int16_t SdBaseFile::write(const void* buf, uint16_t nbyte) { | |
1696 | // convert void* to uint8_t* - must be before goto statements | |
1697 | const uint8_t* src = reinterpret_cast<const uint8_t*>(buf); | |
1698 | ||
1699 | // number of bytes left to write - must be before goto statements | |
1700 | uint16_t nToWrite = nbyte; | |
1701 | ||
1702 | // error if not a normal file or is read-only | |
1703 | if (!isFile() || !(flags_ & O_WRITE)) goto fail; | |
1704 | ||
1705 | // seek to end of file if append flag | |
1706 | if ((flags_ & O_APPEND) && curPosition_ != fileSize_) { | |
1707 | if (!seekEnd()) goto fail; | |
1708 | } | |
1709 | ||
1710 | while (nToWrite > 0) { | |
1711 | uint8_t blockOfCluster = vol_->blockOfCluster(curPosition_); | |
1712 | uint16_t blockOffset = curPosition_ & 0X1FF; | |
1713 | if (blockOfCluster == 0 && blockOffset == 0) { | |
1714 | // start of new cluster | |
1715 | if (curCluster_ == 0) { | |
1716 | if (firstCluster_ == 0) { | |
1717 | // allocate first cluster of file | |
1718 | if (!addCluster()) goto fail; | |
1719 | } else { | |
1720 | curCluster_ = firstCluster_; | |
1721 | } | |
1722 | } else { | |
1723 | uint32_t next; | |
1724 | if (!vol_->fatGet(curCluster_, &next)) goto fail; | |
1725 | if (vol_->isEOC(next)) { | |
1726 | // add cluster if at end of chain | |
1727 | if (!addCluster()) goto fail; | |
1728 | } else { | |
1729 | curCluster_ = next; | |
1730 | } | |
1731 | } | |
1732 | } | |
1733 | // max space in block | |
1734 | uint16_t n = 512 - blockOffset; | |
1735 | ||
1736 | // lesser of space and amount to write | |
1737 | if (n > nToWrite) n = nToWrite; | |
1738 | ||
1739 | // block for data write | |
1740 | uint32_t block = vol_->clusterStartBlock(curCluster_) + blockOfCluster; | |
1741 | if (n == 512) { | |
1742 | // full block - don't need to use cache | |
1743 | if (vol_->cacheBlockNumber() == block) { | |
1744 | // invalidate cache if block is in cache | |
1745 | vol_->cacheSetBlockNumber(0XFFFFFFFF, false); | |
1746 | } | |
1747 | if (!vol_->writeBlock(block, src)) goto fail; | |
1748 | } else { | |
1749 | if (blockOffset == 0 && curPosition_ >= fileSize_) { | |
1750 | // start of new block don't need to read into cache | |
1751 | if (!vol_->cacheFlush()) goto fail; | |
1752 | // set cache dirty and SD address of block | |
1753 | vol_->cacheSetBlockNumber(block, true); | |
1754 | } else { | |
1755 | // rewrite part of block | |
1756 | if (!vol_->cacheRawBlock(block, SdVolume::CACHE_FOR_WRITE)) goto fail; | |
1757 | } | |
1758 | uint8_t* dst = vol_->cache()->data + blockOffset; | |
1759 | memcpy(dst, src, n); | |
1760 | } | |
1761 | curPosition_ += n; | |
1762 | src += n; | |
1763 | nToWrite -= n; | |
1764 | } | |
1765 | if (curPosition_ > fileSize_) { | |
1766 | // update fileSize and insure sync will update dir entry | |
1767 | fileSize_ = curPosition_; | |
1768 | flags_ |= F_FILE_DIR_DIRTY; | |
1769 | } else if (dateTime_ && nbyte) { | |
1770 | // insure sync will update modified date and time | |
1771 | flags_ |= F_FILE_DIR_DIRTY; | |
1772 | } | |
1773 | ||
1774 | if (flags_ & O_SYNC) { | |
1775 | if (!sync()) goto fail; | |
1776 | } | |
1777 | return nbyte; | |
1778 | ||
1779 | fail: | |
1780 | // return for write error | |
1781 | writeError = true; | |
1782 | return -1; | |
1783 | } | |
1784 | //------------------------------------------------------------------------------ | |
1785 | // suppress cpplint warnings with NOLINT comment | |
1786 | #if ALLOW_DEPRECATED_FUNCTIONS && !defined(DOXYGEN) | |
1787 | void (*SdBaseFile::oldDateTime_)(uint16_t& date, uint16_t& time) = 0; // NOLINT | |
1788 | #endif // ALLOW_DEPRECATED_FUNCTIONS | |
1789 | ||
1790 | ||
1791 | #endif |