root / arduino-1.0 / libraries / SD / utility / SdFile.cpp @ 58d82c77
History | View | Annotate | Download (40.9 KB)
1 | 58d82c77 | Tom Mullins | /* 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 | #include <SdFat.h> |
||
21 | #include <avr/pgmspace.h> |
||
22 | #include <Arduino.h> |
||
23 | //------------------------------------------------------------------------------
|
||
24 | // callback function for date/time
|
||
25 | void (*SdFile::dateTime_)(uint16_t* date, uint16_t* time) = NULL; |
||
26 | |||
27 | #if ALLOW_DEPRECATED_FUNCTIONS
|
||
28 | // suppress cpplint warnings with NOLINT comment
|
||
29 | void (*SdFile::oldDateTime_)(uint16_t& date, uint16_t& time) = NULL; // NOLINT |
||
30 | #endif // ALLOW_DEPRECATED_FUNCTIONS |
||
31 | //------------------------------------------------------------------------------
|
||
32 | // add a cluster to a file
|
||
33 | uint8_t SdFile::addCluster() { |
||
34 | if (!vol_->allocContiguous(1, &curCluster_)) return false; |
||
35 | |||
36 | // if first cluster of file link to directory entry
|
||
37 | if (firstCluster_ == 0) { |
||
38 | firstCluster_ = curCluster_; |
||
39 | flags_ |= F_FILE_DIR_DIRTY; |
||
40 | } |
||
41 | return true; |
||
42 | } |
||
43 | //------------------------------------------------------------------------------
|
||
44 | // Add a cluster to a directory file and zero the cluster.
|
||
45 | // return with first block of cluster in the cache
|
||
46 | uint8_t SdFile::addDirCluster(void) {
|
||
47 | if (!addCluster()) return false; |
||
48 | |||
49 | // zero data in cluster insure first cluster is in cache
|
||
50 | uint32_t block = vol_->clusterStartBlock(curCluster_); |
||
51 | for (uint8_t i = vol_->blocksPerCluster_; i != 0; i--) { |
||
52 | if (!SdVolume::cacheZeroBlock(block + i - 1)) return false; |
||
53 | } |
||
54 | // Increase directory file size by cluster size
|
||
55 | fileSize_ += 512UL << vol_->clusterSizeShift_;
|
||
56 | return true; |
||
57 | } |
||
58 | //------------------------------------------------------------------------------
|
||
59 | // cache a file's directory entry
|
||
60 | // return pointer to cached entry or null for failure
|
||
61 | dir_t* SdFile::cacheDirEntry(uint8_t action) { |
||
62 | if (!SdVolume::cacheRawBlock(dirBlock_, action)) return NULL; |
||
63 | return SdVolume::cacheBuffer_.dir + dirIndex_;
|
||
64 | } |
||
65 | //------------------------------------------------------------------------------
|
||
66 | /**
|
||
67 | * Close a file and force cached data and directory information
|
||
68 | * to be written to the storage device.
|
||
69 | *
|
||
70 | * \return The value one, true, is returned for success and
|
||
71 | * the value zero, false, is returned for failure.
|
||
72 | * Reasons for failure include no file is open or an I/O error.
|
||
73 | */
|
||
74 | uint8_t SdFile::close(void) {
|
||
75 | if (!sync())return false; |
||
76 | type_ = FAT_FILE_TYPE_CLOSED; |
||
77 | return true; |
||
78 | } |
||
79 | //------------------------------------------------------------------------------
|
||
80 | /**
|
||
81 | * Check for contiguous file and return its raw block range.
|
||
82 | *
|
||
83 | * \param[out] bgnBlock the first block address for the file.
|
||
84 | * \param[out] endBlock the last block address for the file.
|
||
85 | *
|
||
86 | * \return The value one, true, is returned for success and
|
||
87 | * the value zero, false, is returned for failure.
|
||
88 | * Reasons for failure include file is not contiguous, file has zero length
|
||
89 | * or an I/O error occurred.
|
||
90 | */
|
||
91 | uint8_t SdFile::contiguousRange(uint32_t* bgnBlock, uint32_t* endBlock) { |
||
92 | // error if no blocks
|
||
93 | if (firstCluster_ == 0) return false; |
||
94 | |||
95 | for (uint32_t c = firstCluster_; ; c++) {
|
||
96 | uint32_t next; |
||
97 | if (!vol_->fatGet(c, &next)) return false; |
||
98 | |||
99 | // check for contiguous
|
||
100 | if (next != (c + 1)) { |
||
101 | // error if not end of chain
|
||
102 | if (!vol_->isEOC(next)) return false; |
||
103 | *bgnBlock = vol_->clusterStartBlock(firstCluster_); |
||
104 | *endBlock = vol_->clusterStartBlock(c) |
||
105 | + vol_->blocksPerCluster_ - 1;
|
||
106 | return true; |
||
107 | } |
||
108 | } |
||
109 | } |
||
110 | //------------------------------------------------------------------------------
|
||
111 | /**
|
||
112 | * Create and open a new contiguous file of a specified size.
|
||
113 | *
|
||
114 | * \note This function only supports short DOS 8.3 names.
|
||
115 | * See open() for more information.
|
||
116 | *
|
||
117 | * \param[in] dirFile The directory where the file will be created.
|
||
118 | * \param[in] fileName A valid DOS 8.3 file name.
|
||
119 | * \param[in] size The desired file size.
|
||
120 | *
|
||
121 | * \return The value one, true, is returned for success and
|
||
122 | * the value zero, false, is returned for failure.
|
||
123 | * Reasons for failure include \a fileName contains
|
||
124 | * an invalid DOS 8.3 file name, the FAT volume has not been initialized,
|
||
125 | * a file is already open, the file already exists, the root
|
||
126 | * directory is full or an I/O error.
|
||
127 | *
|
||
128 | */
|
||
129 | uint8_t SdFile::createContiguous(SdFile* dirFile, |
||
130 | const char* fileName, uint32_t size) { |
||
131 | // don't allow zero length file
|
||
132 | if (size == 0) return false; |
||
133 | if (!open(dirFile, fileName, O_CREAT | O_EXCL | O_RDWR)) return false; |
||
134 | |||
135 | // calculate number of clusters needed
|
||
136 | uint32_t count = ((size - 1) >> (vol_->clusterSizeShift_ + 9)) + 1; |
||
137 | |||
138 | // allocate clusters
|
||
139 | if (!vol_->allocContiguous(count, &firstCluster_)) {
|
||
140 | remove(); |
||
141 | return false; |
||
142 | } |
||
143 | fileSize_ = size; |
||
144 | |||
145 | // insure sync() will update dir entry
|
||
146 | flags_ |= F_FILE_DIR_DIRTY; |
||
147 | return sync();
|
||
148 | } |
||
149 | //------------------------------------------------------------------------------
|
||
150 | /**
|
||
151 | * Return a files directory entry
|
||
152 | *
|
||
153 | * \param[out] dir Location for return of the files directory entry.
|
||
154 | *
|
||
155 | * \return The value one, true, is returned for success and
|
||
156 | * the value zero, false, is returned for failure.
|
||
157 | */
|
||
158 | uint8_t SdFile::dirEntry(dir_t* dir) { |
||
159 | // make sure fields on SD are correct
|
||
160 | if (!sync()) return false; |
||
161 | |||
162 | // read entry
|
||
163 | dir_t* p = cacheDirEntry(SdVolume::CACHE_FOR_READ); |
||
164 | if (!p) return false; |
||
165 | |||
166 | // copy to caller's struct
|
||
167 | memcpy(dir, p, sizeof(dir_t));
|
||
168 | return true; |
||
169 | } |
||
170 | //------------------------------------------------------------------------------
|
||
171 | /**
|
||
172 | * Format the name field of \a dir into the 13 byte array
|
||
173 | * \a name in standard 8.3 short name format.
|
||
174 | *
|
||
175 | * \param[in] dir The directory structure containing the name.
|
||
176 | * \param[out] name A 13 byte char array for the formatted name.
|
||
177 | */
|
||
178 | void SdFile::dirName(const dir_t& dir, char* name) { |
||
179 | uint8_t j = 0;
|
||
180 | for (uint8_t i = 0; i < 11; i++) { |
||
181 | if (dir.name[i] == ' ')continue; |
||
182 | if (i == 8) name[j++] = '.'; |
||
183 | name[j++] = dir.name[i]; |
||
184 | } |
||
185 | name[j] = 0;
|
||
186 | } |
||
187 | //------------------------------------------------------------------------------
|
||
188 | /** List directory contents to Serial.
|
||
189 | *
|
||
190 | * \param[in] flags The inclusive OR of
|
||
191 | *
|
||
192 | * LS_DATE - %Print file modification date
|
||
193 | *
|
||
194 | * LS_SIZE - %Print file size.
|
||
195 | *
|
||
196 | * LS_R - Recursive list of subdirectories.
|
||
197 | *
|
||
198 | * \param[in] indent Amount of space before file name. Used for recursive
|
||
199 | * list to indicate subdirectory level.
|
||
200 | */
|
||
201 | void SdFile::ls(uint8_t flags, uint8_t indent) {
|
||
202 | dir_t* p; |
||
203 | |||
204 | rewind(); |
||
205 | while ((p = readDirCache())) {
|
||
206 | // done if past last used entry
|
||
207 | if (p->name[0] == DIR_NAME_FREE) break; |
||
208 | |||
209 | // skip deleted entry and entries for . and ..
|
||
210 | if (p->name[0] == DIR_NAME_DELETED || p->name[0] == '.') continue; |
||
211 | |||
212 | // only list subdirectories and files
|
||
213 | if (!DIR_IS_FILE_OR_SUBDIR(p)) continue; |
||
214 | |||
215 | // print any indent spaces
|
||
216 | for (int8_t i = 0; i < indent; i++) Serial.print(' '); |
||
217 | |||
218 | // print file name with possible blank fill
|
||
219 | printDirName(*p, flags & (LS_DATE | LS_SIZE) ? 14 : 0); |
||
220 | |||
221 | // print modify date/time if requested
|
||
222 | if (flags & LS_DATE) {
|
||
223 | printFatDate(p->lastWriteDate); |
||
224 | Serial.print(' ');
|
||
225 | printFatTime(p->lastWriteTime); |
||
226 | } |
||
227 | // print size if requested
|
||
228 | if (!DIR_IS_SUBDIR(p) && (flags & LS_SIZE)) {
|
||
229 | Serial.print(' ');
|
||
230 | Serial.print(p->fileSize); |
||
231 | } |
||
232 | Serial.println(); |
||
233 | |||
234 | // list subdirectory content if requested
|
||
235 | if ((flags & LS_R) && DIR_IS_SUBDIR(p)) {
|
||
236 | uint16_t index = curPosition()/32 - 1; |
||
237 | SdFile s; |
||
238 | if (s.open(this, index, O_READ)) s.ls(flags, indent + 2); |
||
239 | seekSet(32 * (index + 1)); |
||
240 | } |
||
241 | } |
||
242 | } |
||
243 | //------------------------------------------------------------------------------
|
||
244 | // format directory name field from a 8.3 name string
|
||
245 | uint8_t SdFile::make83Name(const char* str, uint8_t* name) { |
||
246 | uint8_t c; |
||
247 | uint8_t n = 7; // max index for part before dot |
||
248 | uint8_t i = 0;
|
||
249 | // blank fill name and extension
|
||
250 | while (i < 11) name[i++] = ' '; |
||
251 | i = 0;
|
||
252 | while ((c = *str++) != '\0') { |
||
253 | if (c == '.') { |
||
254 | if (n == 10) return false; // only one dot allowed |
||
255 | n = 10; // max index for full 8.3 name |
||
256 | i = 8; // place for extension |
||
257 | } else {
|
||
258 | // illegal FAT characters
|
||
259 | PGM_P p = PSTR("|<>^+=?/[];,*\"\\");
|
||
260 | uint8_t b; |
||
261 | while ((b = pgm_read_byte(p++))) if (b == c) return false; |
||
262 | // check size and only allow ASCII printable characters
|
||
263 | if (i > n || c < 0X21 || c > 0X7E)return false; |
||
264 | // only upper case allowed in 8.3 names - convert lower to upper
|
||
265 | name[i++] = c < 'a' || c > 'z' ? c : c + ('A' - 'a'); |
||
266 | } |
||
267 | } |
||
268 | // must have a file name, extension is optional
|
||
269 | return name[0] != ' '; |
||
270 | } |
||
271 | //------------------------------------------------------------------------------
|
||
272 | /** Make a new directory.
|
||
273 | *
|
||
274 | * \param[in] dir An open SdFat instance for the directory that will containing
|
||
275 | * the new directory.
|
||
276 | *
|
||
277 | * \param[in] dirName A valid 8.3 DOS name for the new directory.
|
||
278 | *
|
||
279 | * \return The value one, true, is returned for success and
|
||
280 | * the value zero, false, is returned for failure.
|
||
281 | * Reasons for failure include this SdFile is already open, \a dir is not a
|
||
282 | * directory, \a dirName is invalid or already exists in \a dir.
|
||
283 | */
|
||
284 | uint8_t SdFile::makeDir(SdFile* dir, const char* dirName) { |
||
285 | dir_t d; |
||
286 | |||
287 | // create a normal file
|
||
288 | if (!open(dir, dirName, O_CREAT | O_EXCL | O_RDWR)) return false; |
||
289 | |||
290 | // convert SdFile to directory
|
||
291 | flags_ = O_READ; |
||
292 | type_ = FAT_FILE_TYPE_SUBDIR; |
||
293 | |||
294 | // allocate and zero first cluster
|
||
295 | if (!addDirCluster())return false; |
||
296 | |||
297 | // force entry to SD
|
||
298 | if (!sync()) return false; |
||
299 | |||
300 | // cache entry - should already be in cache due to sync() call
|
||
301 | dir_t* p = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); |
||
302 | if (!p) return false; |
||
303 | |||
304 | // change directory entry attribute
|
||
305 | p->attributes = DIR_ATT_DIRECTORY; |
||
306 | |||
307 | // make entry for '.'
|
||
308 | memcpy(&d, p, sizeof(d));
|
||
309 | for (uint8_t i = 1; i < 11; i++) d.name[i] = ' '; |
||
310 | d.name[0] = '.'; |
||
311 | |||
312 | // cache block for '.' and '..'
|
||
313 | uint32_t block = vol_->clusterStartBlock(firstCluster_); |
||
314 | if (!SdVolume::cacheRawBlock(block, SdVolume::CACHE_FOR_WRITE)) return false; |
||
315 | |||
316 | // copy '.' to block
|
||
317 | memcpy(&SdVolume::cacheBuffer_.dir[0], &d, sizeof(d)); |
||
318 | |||
319 | // make entry for '..'
|
||
320 | d.name[1] = '.'; |
||
321 | if (dir->isRoot()) {
|
||
322 | d.firstClusterLow = 0;
|
||
323 | d.firstClusterHigh = 0;
|
||
324 | } else {
|
||
325 | d.firstClusterLow = dir->firstCluster_ & 0XFFFF;
|
||
326 | d.firstClusterHigh = dir->firstCluster_ >> 16;
|
||
327 | } |
||
328 | // copy '..' to block
|
||
329 | memcpy(&SdVolume::cacheBuffer_.dir[1], &d, sizeof(d)); |
||
330 | |||
331 | // set position after '..'
|
||
332 | curPosition_ = 2 * sizeof(d); |
||
333 | |||
334 | // write first block
|
||
335 | return SdVolume::cacheFlush();
|
||
336 | } |
||
337 | //------------------------------------------------------------------------------
|
||
338 | /**
|
||
339 | * Open a file or directory by name.
|
||
340 | *
|
||
341 | * \param[in] dirFile An open SdFat instance for the directory containing the
|
||
342 | * file to be opened.
|
||
343 | *
|
||
344 | * \param[in] fileName A valid 8.3 DOS name for a file to be opened.
|
||
345 | *
|
||
346 | * \param[in] oflag Values for \a oflag are constructed by a bitwise-inclusive
|
||
347 | * OR of flags from the following list
|
||
348 | *
|
||
349 | * O_READ - Open for reading.
|
||
350 | *
|
||
351 | * O_RDONLY - Same as O_READ.
|
||
352 | *
|
||
353 | * O_WRITE - Open for writing.
|
||
354 | *
|
||
355 | * O_WRONLY - Same as O_WRITE.
|
||
356 | *
|
||
357 | * O_RDWR - Open for reading and writing.
|
||
358 | *
|
||
359 | * O_APPEND - If set, the file offset shall be set to the end of the
|
||
360 | * file prior to each write.
|
||
361 | *
|
||
362 | * O_CREAT - If the file exists, this flag has no effect except as noted
|
||
363 | * under O_EXCL below. Otherwise, the file shall be created
|
||
364 | *
|
||
365 | * O_EXCL - If O_CREAT and O_EXCL are set, open() shall fail if the file exists.
|
||
366 | *
|
||
367 | * O_SYNC - Call sync() after each write. This flag should not be used with
|
||
368 | * write(uint8_t), write_P(PGM_P), writeln_P(PGM_P), or the Arduino Print class.
|
||
369 | * These functions do character at a time writes so sync() will be called
|
||
370 | * after each byte.
|
||
371 | *
|
||
372 | * O_TRUNC - If the file exists and is a regular file, and the file is
|
||
373 | * successfully opened and is not read only, its length shall be truncated to 0.
|
||
374 | *
|
||
375 | * \note Directory files must be opened read only. Write and truncation is
|
||
376 | * not allowed for directory files.
|
||
377 | *
|
||
378 | * \return The value one, true, is returned for success and
|
||
379 | * the value zero, false, is returned for failure.
|
||
380 | * Reasons for failure include this SdFile is already open, \a difFile is not
|
||
381 | * a directory, \a fileName is invalid, the file does not exist
|
||
382 | * or can't be opened in the access mode specified by oflag.
|
||
383 | */
|
||
384 | uint8_t SdFile::open(SdFile* dirFile, const char* fileName, uint8_t oflag) { |
||
385 | uint8_t dname[11];
|
||
386 | dir_t* p; |
||
387 | |||
388 | // error if already open
|
||
389 | if (isOpen())return false; |
||
390 | |||
391 | if (!make83Name(fileName, dname)) return false; |
||
392 | vol_ = dirFile->vol_; |
||
393 | dirFile->rewind(); |
||
394 | |||
395 | // bool for empty entry found
|
||
396 | uint8_t emptyFound = false;
|
||
397 | |||
398 | // search for file
|
||
399 | while (dirFile->curPosition_ < dirFile->fileSize_) {
|
||
400 | uint8_t index = 0XF & (dirFile->curPosition_ >> 5); |
||
401 | p = dirFile->readDirCache(); |
||
402 | if (p == NULL) return false; |
||
403 | |||
404 | if (p->name[0] == DIR_NAME_FREE || p->name[0] == DIR_NAME_DELETED) { |
||
405 | // remember first empty slot
|
||
406 | if (!emptyFound) {
|
||
407 | emptyFound = true;
|
||
408 | dirIndex_ = index; |
||
409 | dirBlock_ = SdVolume::cacheBlockNumber_; |
||
410 | } |
||
411 | // done if no entries follow
|
||
412 | if (p->name[0] == DIR_NAME_FREE) break; |
||
413 | } else if (!memcmp(dname, p->name, 11)) { |
||
414 | // don't open existing file if O_CREAT and O_EXCL
|
||
415 | if ((oflag & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) return false; |
||
416 | |||
417 | // open found file
|
||
418 | return openCachedEntry(0XF & index, oflag); |
||
419 | } |
||
420 | } |
||
421 | // only create file if O_CREAT and O_WRITE
|
||
422 | if ((oflag & (O_CREAT | O_WRITE)) != (O_CREAT | O_WRITE)) return false; |
||
423 | |||
424 | // cache found slot or add cluster if end of file
|
||
425 | if (emptyFound) {
|
||
426 | p = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); |
||
427 | if (!p) return false; |
||
428 | } else {
|
||
429 | if (dirFile->type_ == FAT_FILE_TYPE_ROOT16) return false; |
||
430 | |||
431 | // add and zero cluster for dirFile - first cluster is in cache for write
|
||
432 | if (!dirFile->addDirCluster()) return false; |
||
433 | |||
434 | // use first entry in cluster
|
||
435 | dirIndex_ = 0;
|
||
436 | p = SdVolume::cacheBuffer_.dir; |
||
437 | } |
||
438 | // initialize as empty file
|
||
439 | memset(p, 0, sizeof(dir_t)); |
||
440 | memcpy(p->name, dname, 11);
|
||
441 | |||
442 | // set timestamps
|
||
443 | if (dateTime_) {
|
||
444 | // call user function
|
||
445 | dateTime_(&p->creationDate, &p->creationTime); |
||
446 | } else {
|
||
447 | // use default date/time
|
||
448 | p->creationDate = FAT_DEFAULT_DATE; |
||
449 | p->creationTime = FAT_DEFAULT_TIME; |
||
450 | } |
||
451 | p->lastAccessDate = p->creationDate; |
||
452 | p->lastWriteDate = p->creationDate; |
||
453 | p->lastWriteTime = p->creationTime; |
||
454 | |||
455 | // force write of entry to SD
|
||
456 | if (!SdVolume::cacheFlush()) return false; |
||
457 | |||
458 | // open entry in cache
|
||
459 | return openCachedEntry(dirIndex_, oflag);
|
||
460 | } |
||
461 | //------------------------------------------------------------------------------
|
||
462 | /**
|
||
463 | * Open a file by index.
|
||
464 | *
|
||
465 | * \param[in] dirFile An open SdFat instance for the directory.
|
||
466 | *
|
||
467 | * \param[in] index The \a index of the directory entry for the file to be
|
||
468 | * opened. The value for \a index is (directory file position)/32.
|
||
469 | *
|
||
470 | * \param[in] oflag Values for \a oflag are constructed by a bitwise-inclusive
|
||
471 | * OR of flags O_READ, O_WRITE, O_TRUNC, and O_SYNC.
|
||
472 | *
|
||
473 | * See open() by fileName for definition of flags and return values.
|
||
474 | *
|
||
475 | */
|
||
476 | uint8_t SdFile::open(SdFile* dirFile, uint16_t index, uint8_t oflag) { |
||
477 | // error if already open
|
||
478 | if (isOpen())return false; |
||
479 | |||
480 | // don't open existing file if O_CREAT and O_EXCL - user call error
|
||
481 | if ((oflag & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) return false; |
||
482 | |||
483 | vol_ = dirFile->vol_; |
||
484 | |||
485 | // seek to location of entry
|
||
486 | if (!dirFile->seekSet(32 * index)) return false; |
||
487 | |||
488 | // read entry into cache
|
||
489 | dir_t* p = dirFile->readDirCache(); |
||
490 | if (p == NULL) return false; |
||
491 | |||
492 | // error if empty slot or '.' or '..'
|
||
493 | if (p->name[0] == DIR_NAME_FREE || |
||
494 | p->name[0] == DIR_NAME_DELETED || p->name[0] == '.') { |
||
495 | return false; |
||
496 | } |
||
497 | // open cached entry
|
||
498 | return openCachedEntry(index & 0XF, oflag); |
||
499 | } |
||
500 | //------------------------------------------------------------------------------
|
||
501 | // open a cached directory entry. Assumes vol_ is initializes
|
||
502 | uint8_t SdFile::openCachedEntry(uint8_t dirIndex, uint8_t oflag) { |
||
503 | // location of entry in cache
|
||
504 | dir_t* p = SdVolume::cacheBuffer_.dir + dirIndex; |
||
505 | |||
506 | // write or truncate is an error for a directory or read-only file
|
||
507 | if (p->attributes & (DIR_ATT_READ_ONLY | DIR_ATT_DIRECTORY)) {
|
||
508 | if (oflag & (O_WRITE | O_TRUNC)) return false; |
||
509 | } |
||
510 | // remember location of directory entry on SD
|
||
511 | dirIndex_ = dirIndex; |
||
512 | dirBlock_ = SdVolume::cacheBlockNumber_; |
||
513 | |||
514 | // copy first cluster number for directory fields
|
||
515 | firstCluster_ = (uint32_t)p->firstClusterHigh << 16;
|
||
516 | firstCluster_ |= p->firstClusterLow; |
||
517 | |||
518 | // make sure it is a normal file or subdirectory
|
||
519 | if (DIR_IS_FILE(p)) {
|
||
520 | fileSize_ = p->fileSize; |
||
521 | type_ = FAT_FILE_TYPE_NORMAL; |
||
522 | } else if (DIR_IS_SUBDIR(p)) { |
||
523 | if (!vol_->chainSize(firstCluster_, &fileSize_)) return false; |
||
524 | type_ = FAT_FILE_TYPE_SUBDIR; |
||
525 | } else {
|
||
526 | return false; |
||
527 | } |
||
528 | // save open flags for read/write
|
||
529 | flags_ = oflag & (O_ACCMODE | O_SYNC | O_APPEND); |
||
530 | |||
531 | // set to start of file
|
||
532 | curCluster_ = 0;
|
||
533 | curPosition_ = 0;
|
||
534 | |||
535 | // truncate file to zero length if requested
|
||
536 | if (oflag & O_TRUNC) return truncate(0); |
||
537 | return true; |
||
538 | } |
||
539 | //------------------------------------------------------------------------------
|
||
540 | /**
|
||
541 | * Open a volume's root directory.
|
||
542 | *
|
||
543 | * \param[in] vol The FAT volume containing the root directory to be opened.
|
||
544 | *
|
||
545 | * \return The value one, true, is returned for success and
|
||
546 | * the value zero, false, is returned for failure.
|
||
547 | * Reasons for failure include the FAT volume has not been initialized
|
||
548 | * or it a FAT12 volume.
|
||
549 | */
|
||
550 | uint8_t SdFile::openRoot(SdVolume* vol) { |
||
551 | // error if file is already open
|
||
552 | if (isOpen()) return false; |
||
553 | |||
554 | if (vol->fatType() == 16) { |
||
555 | type_ = FAT_FILE_TYPE_ROOT16; |
||
556 | firstCluster_ = 0;
|
||
557 | fileSize_ = 32 * vol->rootDirEntryCount();
|
||
558 | } else if (vol->fatType() == 32) { |
||
559 | type_ = FAT_FILE_TYPE_ROOT32; |
||
560 | firstCluster_ = vol->rootDirStart(); |
||
561 | if (!vol->chainSize(firstCluster_, &fileSize_)) return false; |
||
562 | } else {
|
||
563 | // volume is not initialized or FAT12
|
||
564 | return false; |
||
565 | } |
||
566 | vol_ = vol; |
||
567 | // read only
|
||
568 | flags_ = O_READ; |
||
569 | |||
570 | // set to start of file
|
||
571 | curCluster_ = 0;
|
||
572 | curPosition_ = 0;
|
||
573 | |||
574 | // root has no directory entry
|
||
575 | dirBlock_ = 0;
|
||
576 | dirIndex_ = 0;
|
||
577 | return true; |
||
578 | } |
||
579 | //------------------------------------------------------------------------------
|
||
580 | /** %Print the name field of a directory entry in 8.3 format to Serial.
|
||
581 | *
|
||
582 | * \param[in] dir The directory structure containing the name.
|
||
583 | * \param[in] width Blank fill name if length is less than \a width.
|
||
584 | */
|
||
585 | void SdFile::printDirName(const dir_t& dir, uint8_t width) { |
||
586 | uint8_t w = 0;
|
||
587 | for (uint8_t i = 0; i < 11; i++) { |
||
588 | if (dir.name[i] == ' ')continue; |
||
589 | if (i == 8) { |
||
590 | Serial.print('.');
|
||
591 | w++; |
||
592 | } |
||
593 | Serial.write(dir.name[i]); |
||
594 | w++; |
||
595 | } |
||
596 | if (DIR_IS_SUBDIR(&dir)) {
|
||
597 | Serial.print('/');
|
||
598 | w++; |
||
599 | } |
||
600 | while (w < width) {
|
||
601 | Serial.print(' ');
|
||
602 | w++; |
||
603 | } |
||
604 | } |
||
605 | //------------------------------------------------------------------------------
|
||
606 | /** %Print a directory date field to Serial.
|
||
607 | *
|
||
608 | * Format is yyyy-mm-dd.
|
||
609 | *
|
||
610 | * \param[in] fatDate The date field from a directory entry.
|
||
611 | */
|
||
612 | void SdFile::printFatDate(uint16_t fatDate) {
|
||
613 | Serial.print(FAT_YEAR(fatDate)); |
||
614 | Serial.print('-');
|
||
615 | printTwoDigits(FAT_MONTH(fatDate)); |
||
616 | Serial.print('-');
|
||
617 | printTwoDigits(FAT_DAY(fatDate)); |
||
618 | } |
||
619 | //------------------------------------------------------------------------------
|
||
620 | /** %Print a directory time field to Serial.
|
||
621 | *
|
||
622 | * Format is hh:mm:ss.
|
||
623 | *
|
||
624 | * \param[in] fatTime The time field from a directory entry.
|
||
625 | */
|
||
626 | void SdFile::printFatTime(uint16_t fatTime) {
|
||
627 | printTwoDigits(FAT_HOUR(fatTime)); |
||
628 | Serial.print(':');
|
||
629 | printTwoDigits(FAT_MINUTE(fatTime)); |
||
630 | Serial.print(':');
|
||
631 | printTwoDigits(FAT_SECOND(fatTime)); |
||
632 | } |
||
633 | //------------------------------------------------------------------------------
|
||
634 | /** %Print a value as two digits to Serial.
|
||
635 | *
|
||
636 | * \param[in] v Value to be printed, 0 <= \a v <= 99
|
||
637 | */
|
||
638 | void SdFile::printTwoDigits(uint8_t v) {
|
||
639 | char str[3]; |
||
640 | str[0] = '0' + v/10; |
||
641 | str[1] = '0' + v % 10; |
||
642 | str[2] = 0; |
||
643 | Serial.print(str); |
||
644 | } |
||
645 | //------------------------------------------------------------------------------
|
||
646 | /**
|
||
647 | * Read data from a file starting at the current position.
|
||
648 | *
|
||
649 | * \param[out] buf Pointer to the location that will receive the data.
|
||
650 | *
|
||
651 | * \param[in] nbyte Maximum number of bytes to read.
|
||
652 | *
|
||
653 | * \return For success read() returns the number of bytes read.
|
||
654 | * A value less than \a nbyte, including zero, will be returned
|
||
655 | * if end of file is reached.
|
||
656 | * If an error occurs, read() returns -1. Possible errors include
|
||
657 | * read() called before a file has been opened, corrupt file system
|
||
658 | * or an I/O error occurred.
|
||
659 | */
|
||
660 | int16_t SdFile::read(void* buf, uint16_t nbyte) {
|
||
661 | uint8_t* dst = reinterpret_cast<uint8_t*>(buf);
|
||
662 | |||
663 | // error if not open or write only
|
||
664 | if (!isOpen() || !(flags_ & O_READ)) return -1; |
||
665 | |||
666 | // max bytes left in file
|
||
667 | if (nbyte > (fileSize_ - curPosition_)) nbyte = fileSize_ - curPosition_;
|
||
668 | |||
669 | // amount left to read
|
||
670 | uint16_t toRead = nbyte; |
||
671 | while (toRead > 0) { |
||
672 | uint32_t block; // raw device block number
|
||
673 | uint16_t offset = curPosition_ & 0X1FF; // offset in block |
||
674 | if (type_ == FAT_FILE_TYPE_ROOT16) {
|
||
675 | block = vol_->rootDirStart() + (curPosition_ >> 9);
|
||
676 | } else {
|
||
677 | uint8_t blockOfCluster = vol_->blockOfCluster(curPosition_); |
||
678 | if (offset == 0 && blockOfCluster == 0) { |
||
679 | // start of new cluster
|
||
680 | if (curPosition_ == 0) { |
||
681 | // use first cluster in file
|
||
682 | curCluster_ = firstCluster_; |
||
683 | } else {
|
||
684 | // get next cluster from FAT
|
||
685 | if (!vol_->fatGet(curCluster_, &curCluster_)) return -1; |
||
686 | } |
||
687 | } |
||
688 | block = vol_->clusterStartBlock(curCluster_) + blockOfCluster; |
||
689 | } |
||
690 | uint16_t n = toRead; |
||
691 | |||
692 | // amount to be read from current block
|
||
693 | if (n > (512 - offset)) n = 512 - offset; |
||
694 | |||
695 | // no buffering needed if n == 512 or user requests no buffering
|
||
696 | if ((unbufferedRead() || n == 512) && |
||
697 | block != SdVolume::cacheBlockNumber_) { |
||
698 | if (!vol_->readData(block, offset, n, dst)) return -1; |
||
699 | dst += n; |
||
700 | } else {
|
||
701 | // read block to cache and copy data to caller
|
||
702 | if (!SdVolume::cacheRawBlock(block, SdVolume::CACHE_FOR_READ)) return -1; |
||
703 | uint8_t* src = SdVolume::cacheBuffer_.data + offset; |
||
704 | uint8_t* end = src + n; |
||
705 | while (src != end) *dst++ = *src++;
|
||
706 | } |
||
707 | curPosition_ += n; |
||
708 | toRead -= n; |
||
709 | } |
||
710 | return nbyte;
|
||
711 | } |
||
712 | //------------------------------------------------------------------------------
|
||
713 | /**
|
||
714 | * Read the next directory entry from a directory file.
|
||
715 | *
|
||
716 | * \param[out] dir The dir_t struct that will receive the data.
|
||
717 | *
|
||
718 | * \return For success readDir() returns the number of bytes read.
|
||
719 | * A value of zero will be returned if end of file is reached.
|
||
720 | * If an error occurs, readDir() returns -1. Possible errors include
|
||
721 | * readDir() called before a directory has been opened, this is not
|
||
722 | * a directory file or an I/O error occurred.
|
||
723 | */
|
||
724 | int8_t SdFile::readDir(dir_t* dir) { |
||
725 | int8_t n; |
||
726 | // if not a directory file or miss-positioned return an error
|
||
727 | if (!isDir() || (0X1F & curPosition_)) return -1; |
||
728 | |||
729 | while ((n = read(dir, sizeof(dir_t))) == sizeof(dir_t)) { |
||
730 | // last entry if DIR_NAME_FREE
|
||
731 | if (dir->name[0] == DIR_NAME_FREE) break; |
||
732 | // skip empty entries and entry for . and ..
|
||
733 | if (dir->name[0] == DIR_NAME_DELETED || dir->name[0] == '.') continue; |
||
734 | // return if normal file or subdirectory
|
||
735 | if (DIR_IS_FILE_OR_SUBDIR(dir)) return n; |
||
736 | } |
||
737 | // error, end of file, or past last entry
|
||
738 | return n < 0 ? -1 : 0; |
||
739 | } |
||
740 | //------------------------------------------------------------------------------
|
||
741 | // Read next directory entry into the cache
|
||
742 | // Assumes file is correctly positioned
|
||
743 | dir_t* SdFile::readDirCache(void) {
|
||
744 | // error if not directory
|
||
745 | if (!isDir()) return NULL; |
||
746 | |||
747 | // index of entry in cache
|
||
748 | uint8_t i = (curPosition_ >> 5) & 0XF; |
||
749 | |||
750 | // use read to locate and cache block
|
||
751 | if (read() < 0) return NULL; |
||
752 | |||
753 | // advance to next entry
|
||
754 | curPosition_ += 31;
|
||
755 | |||
756 | // return pointer to entry
|
||
757 | return (SdVolume::cacheBuffer_.dir + i);
|
||
758 | } |
||
759 | //------------------------------------------------------------------------------
|
||
760 | /**
|
||
761 | * Remove a file.
|
||
762 | *
|
||
763 | * The directory entry and all data for the file are deleted.
|
||
764 | *
|
||
765 | * \note This function should not be used to delete the 8.3 version of a
|
||
766 | * file that has a long name. For example if a file has the long name
|
||
767 | * "New Text Document.txt" you should not delete the 8.3 name "NEWTEX~1.TXT".
|
||
768 | *
|
||
769 | * \return The value one, true, is returned for success and
|
||
770 | * the value zero, false, is returned for failure.
|
||
771 | * Reasons for failure include the file read-only, is a directory,
|
||
772 | * or an I/O error occurred.
|
||
773 | */
|
||
774 | uint8_t SdFile::remove(void) {
|
||
775 | // free any clusters - will fail if read-only or directory
|
||
776 | if (!truncate(0)) return false; |
||
777 | |||
778 | // cache directory entry
|
||
779 | dir_t* d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); |
||
780 | if (!d) return false; |
||
781 | |||
782 | // mark entry deleted
|
||
783 | d->name[0] = DIR_NAME_DELETED;
|
||
784 | |||
785 | // set this SdFile closed
|
||
786 | type_ = FAT_FILE_TYPE_CLOSED; |
||
787 | |||
788 | // write entry to SD
|
||
789 | return SdVolume::cacheFlush();
|
||
790 | } |
||
791 | //------------------------------------------------------------------------------
|
||
792 | /**
|
||
793 | * Remove a file.
|
||
794 | *
|
||
795 | * The directory entry and all data for the file are deleted.
|
||
796 | *
|
||
797 | * \param[in] dirFile The directory that contains the file.
|
||
798 | * \param[in] fileName The name of the file to be removed.
|
||
799 | *
|
||
800 | * \note This function should not be used to delete the 8.3 version of a
|
||
801 | * file that has a long name. For example if a file has the long name
|
||
802 | * "New Text Document.txt" you should not delete the 8.3 name "NEWTEX~1.TXT".
|
||
803 | *
|
||
804 | * \return The value one, true, is returned for success and
|
||
805 | * the value zero, false, is returned for failure.
|
||
806 | * Reasons for failure include the file is a directory, is read only,
|
||
807 | * \a dirFile is not a directory, \a fileName is not found
|
||
808 | * or an I/O error occurred.
|
||
809 | */
|
||
810 | uint8_t SdFile::remove(SdFile* dirFile, const char* fileName) { |
||
811 | SdFile file; |
||
812 | if (!file.open(dirFile, fileName, O_WRITE)) return false; |
||
813 | return file.remove();
|
||
814 | } |
||
815 | //------------------------------------------------------------------------------
|
||
816 | /** Remove a directory file.
|
||
817 | *
|
||
818 | * The directory file will be removed only if it is empty and is not the
|
||
819 | * root directory. rmDir() follows DOS and Windows and ignores the
|
||
820 | * read-only attribute for the directory.
|
||
821 | *
|
||
822 | * \note This function should not be used to delete the 8.3 version of a
|
||
823 | * directory that has a long name. For example if a directory has the
|
||
824 | * long name "New folder" you should not delete the 8.3 name "NEWFOL~1".
|
||
825 | *
|
||
826 | * \return The value one, true, is returned for success and
|
||
827 | * the value zero, false, is returned for failure.
|
||
828 | * Reasons for failure include the file is not a directory, is the root
|
||
829 | * directory, is not empty, or an I/O error occurred.
|
||
830 | */
|
||
831 | uint8_t SdFile::rmDir(void) {
|
||
832 | // must be open subdirectory
|
||
833 | if (!isSubDir()) return false; |
||
834 | |||
835 | rewind(); |
||
836 | |||
837 | // make sure directory is empty
|
||
838 | while (curPosition_ < fileSize_) {
|
||
839 | dir_t* p = readDirCache(); |
||
840 | if (p == NULL) return false; |
||
841 | // done if past last used entry
|
||
842 | if (p->name[0] == DIR_NAME_FREE) break; |
||
843 | // skip empty slot or '.' or '..'
|
||
844 | if (p->name[0] == DIR_NAME_DELETED || p->name[0] == '.') continue; |
||
845 | // error not empty
|
||
846 | if (DIR_IS_FILE_OR_SUBDIR(p)) return false; |
||
847 | } |
||
848 | // convert empty directory to normal file for remove
|
||
849 | type_ = FAT_FILE_TYPE_NORMAL; |
||
850 | flags_ |= O_WRITE; |
||
851 | return remove();
|
||
852 | } |
||
853 | //------------------------------------------------------------------------------
|
||
854 | /** Recursively delete a directory and all contained files.
|
||
855 | *
|
||
856 | * This is like the Unix/Linux 'rm -rf *' if called with the root directory
|
||
857 | * hence the name.
|
||
858 | *
|
||
859 | * Warning - This will remove all contents of the directory including
|
||
860 | * subdirectories. The directory will then be removed if it is not root.
|
||
861 | * The read-only attribute for files will be ignored.
|
||
862 | *
|
||
863 | * \note This function should not be used to delete the 8.3 version of
|
||
864 | * a directory that has a long name. See remove() and rmDir().
|
||
865 | *
|
||
866 | * \return The value one, true, is returned for success and
|
||
867 | * the value zero, false, is returned for failure.
|
||
868 | */
|
||
869 | uint8_t SdFile::rmRfStar(void) {
|
||
870 | rewind(); |
||
871 | while (curPosition_ < fileSize_) {
|
||
872 | SdFile f; |
||
873 | |||
874 | // remember position
|
||
875 | uint16_t index = curPosition_/32;
|
||
876 | |||
877 | dir_t* p = readDirCache(); |
||
878 | if (!p) return false; |
||
879 | |||
880 | // done if past last entry
|
||
881 | if (p->name[0] == DIR_NAME_FREE) break; |
||
882 | |||
883 | // skip empty slot or '.' or '..'
|
||
884 | if (p->name[0] == DIR_NAME_DELETED || p->name[0] == '.') continue; |
||
885 | |||
886 | // skip if part of long file name or volume label in root
|
||
887 | if (!DIR_IS_FILE_OR_SUBDIR(p)) continue; |
||
888 | |||
889 | if (!f.open(this, index, O_READ)) return false; |
||
890 | if (f.isSubDir()) {
|
||
891 | // recursively delete
|
||
892 | if (!f.rmRfStar()) return false; |
||
893 | } else {
|
||
894 | // ignore read-only
|
||
895 | f.flags_ |= O_WRITE; |
||
896 | if (!f.remove()) return false; |
||
897 | } |
||
898 | // position to next entry if required
|
||
899 | if (curPosition_ != (32*(index + 1))) { |
||
900 | if (!seekSet(32*(index + 1))) return false; |
||
901 | } |
||
902 | } |
||
903 | // don't try to delete root
|
||
904 | if (isRoot()) return true; |
||
905 | return rmDir();
|
||
906 | } |
||
907 | //------------------------------------------------------------------------------
|
||
908 | /**
|
||
909 | * Sets a file's position.
|
||
910 | *
|
||
911 | * \param[in] pos The new position in bytes from the beginning of the file.
|
||
912 | *
|
||
913 | * \return The value one, true, is returned for success and
|
||
914 | * the value zero, false, is returned for failure.
|
||
915 | */
|
||
916 | uint8_t SdFile::seekSet(uint32_t pos) { |
||
917 | // error if file not open or seek past end of file
|
||
918 | if (!isOpen() || pos > fileSize_) return false; |
||
919 | |||
920 | if (type_ == FAT_FILE_TYPE_ROOT16) {
|
||
921 | curPosition_ = pos; |
||
922 | return true; |
||
923 | } |
||
924 | if (pos == 0) { |
||
925 | // set position to start of file
|
||
926 | curCluster_ = 0;
|
||
927 | curPosition_ = 0;
|
||
928 | return true; |
||
929 | } |
||
930 | // calculate cluster index for cur and new position
|
||
931 | uint32_t nCur = (curPosition_ - 1) >> (vol_->clusterSizeShift_ + 9); |
||
932 | uint32_t nNew = (pos - 1) >> (vol_->clusterSizeShift_ + 9); |
||
933 | |||
934 | if (nNew < nCur || curPosition_ == 0) { |
||
935 | // must follow chain from first cluster
|
||
936 | curCluster_ = firstCluster_; |
||
937 | } else {
|
||
938 | // advance from curPosition
|
||
939 | nNew -= nCur; |
||
940 | } |
||
941 | while (nNew--) {
|
||
942 | if (!vol_->fatGet(curCluster_, &curCluster_)) return false; |
||
943 | } |
||
944 | curPosition_ = pos; |
||
945 | return true; |
||
946 | } |
||
947 | //------------------------------------------------------------------------------
|
||
948 | /**
|
||
949 | * The sync() call causes all modified data and directory fields
|
||
950 | * to be written to the storage device.
|
||
951 | *
|
||
952 | * \return The value one, true, is returned for success and
|
||
953 | * the value zero, false, is returned for failure.
|
||
954 | * Reasons for failure include a call to sync() before a file has been
|
||
955 | * opened or an I/O error.
|
||
956 | */
|
||
957 | uint8_t SdFile::sync(void) {
|
||
958 | // only allow open files and directories
|
||
959 | if (!isOpen()) return false; |
||
960 | |||
961 | if (flags_ & F_FILE_DIR_DIRTY) {
|
||
962 | dir_t* d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); |
||
963 | if (!d) return false; |
||
964 | |||
965 | // do not set filesize for dir files
|
||
966 | if (!isDir()) d->fileSize = fileSize_;
|
||
967 | |||
968 | // update first cluster fields
|
||
969 | d->firstClusterLow = firstCluster_ & 0XFFFF;
|
||
970 | d->firstClusterHigh = firstCluster_ >> 16;
|
||
971 | |||
972 | // set modify time if user supplied a callback date/time function
|
||
973 | if (dateTime_) {
|
||
974 | dateTime_(&d->lastWriteDate, &d->lastWriteTime); |
||
975 | d->lastAccessDate = d->lastWriteDate; |
||
976 | } |
||
977 | // clear directory dirty
|
||
978 | flags_ &= ~F_FILE_DIR_DIRTY; |
||
979 | } |
||
980 | return SdVolume::cacheFlush();
|
||
981 | } |
||
982 | //------------------------------------------------------------------------------
|
||
983 | /**
|
||
984 | * Set a file's timestamps in its directory entry.
|
||
985 | *
|
||
986 | * \param[in] flags Values for \a flags are constructed by a bitwise-inclusive
|
||
987 | * OR of flags from the following list
|
||
988 | *
|
||
989 | * T_ACCESS - Set the file's last access date.
|
||
990 | *
|
||
991 | * T_CREATE - Set the file's creation date and time.
|
||
992 | *
|
||
993 | * T_WRITE - Set the file's last write/modification date and time.
|
||
994 | *
|
||
995 | * \param[in] year Valid range 1980 - 2107 inclusive.
|
||
996 | *
|
||
997 | * \param[in] month Valid range 1 - 12 inclusive.
|
||
998 | *
|
||
999 | * \param[in] day Valid range 1 - 31 inclusive.
|
||
1000 | *
|
||
1001 | * \param[in] hour Valid range 0 - 23 inclusive.
|
||
1002 | *
|
||
1003 | * \param[in] minute Valid range 0 - 59 inclusive.
|
||
1004 | *
|
||
1005 | * \param[in] second Valid range 0 - 59 inclusive
|
||
1006 | *
|
||
1007 | * \note It is possible to set an invalid date since there is no check for
|
||
1008 | * the number of days in a month.
|
||
1009 | *
|
||
1010 | * \note
|
||
1011 | * Modify and access timestamps may be overwritten if a date time callback
|
||
1012 | * function has been set by dateTimeCallback().
|
||
1013 | *
|
||
1014 | * \return The value one, true, is returned for success and
|
||
1015 | * the value zero, false, is returned for failure.
|
||
1016 | */
|
||
1017 | uint8_t SdFile::timestamp(uint8_t flags, uint16_t year, uint8_t month, |
||
1018 | uint8_t day, uint8_t hour, uint8_t minute, uint8_t second) { |
||
1019 | if (!isOpen()
|
||
1020 | || year < 1980
|
||
1021 | || year > 2107
|
||
1022 | || month < 1
|
||
1023 | || month > 12
|
||
1024 | || day < 1
|
||
1025 | || day > 31
|
||
1026 | || hour > 23
|
||
1027 | || minute > 59
|
||
1028 | || second > 59) {
|
||
1029 | return false; |
||
1030 | } |
||
1031 | dir_t* d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); |
||
1032 | if (!d) return false; |
||
1033 | |||
1034 | uint16_t dirDate = FAT_DATE(year, month, day); |
||
1035 | uint16_t dirTime = FAT_TIME(hour, minute, second); |
||
1036 | if (flags & T_ACCESS) {
|
||
1037 | d->lastAccessDate = dirDate; |
||
1038 | } |
||
1039 | if (flags & T_CREATE) {
|
||
1040 | d->creationDate = dirDate; |
||
1041 | d->creationTime = dirTime; |
||
1042 | // seems to be units of 1/100 second not 1/10 as Microsoft states
|
||
1043 | d->creationTimeTenths = second & 1 ? 100 : 0; |
||
1044 | } |
||
1045 | if (flags & T_WRITE) {
|
||
1046 | d->lastWriteDate = dirDate; |
||
1047 | d->lastWriteTime = dirTime; |
||
1048 | } |
||
1049 | SdVolume::cacheSetDirty(); |
||
1050 | return sync();
|
||
1051 | } |
||
1052 | //------------------------------------------------------------------------------
|
||
1053 | /**
|
||
1054 | * Truncate a file to a specified length. The current file position
|
||
1055 | * will be maintained if it is less than or equal to \a length otherwise
|
||
1056 | * it will be set to end of file.
|
||
1057 | *
|
||
1058 | * \param[in] length The desired length for the file.
|
||
1059 | *
|
||
1060 | * \return The value one, true, is returned for success and
|
||
1061 | * the value zero, false, is returned for failure.
|
||
1062 | * Reasons for failure include file is read only, file is a directory,
|
||
1063 | * \a length is greater than the current file size or an I/O error occurs.
|
||
1064 | */
|
||
1065 | uint8_t SdFile::truncate(uint32_t length) { |
||
1066 | // error if not a normal file or read-only
|
||
1067 | if (!isFile() || !(flags_ & O_WRITE)) return false; |
||
1068 | |||
1069 | // error if length is greater than current size
|
||
1070 | if (length > fileSize_) return false; |
||
1071 | |||
1072 | // fileSize and length are zero - nothing to do
|
||
1073 | if (fileSize_ == 0) return true; |
||
1074 | |||
1075 | // remember position for seek after truncation
|
||
1076 | uint32_t newPos = curPosition_ > length ? length : curPosition_; |
||
1077 | |||
1078 | // position to last cluster in truncated file
|
||
1079 | if (!seekSet(length)) return false; |
||
1080 | |||
1081 | if (length == 0) { |
||
1082 | // free all clusters
|
||
1083 | if (!vol_->freeChain(firstCluster_)) return false; |
||
1084 | firstCluster_ = 0;
|
||
1085 | } else {
|
||
1086 | uint32_t toFree; |
||
1087 | if (!vol_->fatGet(curCluster_, &toFree)) return false; |
||
1088 | |||
1089 | if (!vol_->isEOC(toFree)) {
|
||
1090 | // free extra clusters
|
||
1091 | if (!vol_->freeChain(toFree)) return false; |
||
1092 | |||
1093 | // current cluster is end of chain
|
||
1094 | if (!vol_->fatPutEOC(curCluster_)) return false; |
||
1095 | } |
||
1096 | } |
||
1097 | fileSize_ = length; |
||
1098 | |||
1099 | // need to update directory entry
|
||
1100 | flags_ |= F_FILE_DIR_DIRTY; |
||
1101 | |||
1102 | if (!sync()) return false; |
||
1103 | |||
1104 | // set file to correct position
|
||
1105 | return seekSet(newPos);
|
||
1106 | } |
||
1107 | //------------------------------------------------------------------------------
|
||
1108 | /**
|
||
1109 | * Write data to an open file.
|
||
1110 | *
|
||
1111 | * \note Data is moved to the cache but may not be written to the
|
||
1112 | * storage device until sync() is called.
|
||
1113 | *
|
||
1114 | * \param[in] buf Pointer to the location of the data to be written.
|
||
1115 | *
|
||
1116 | * \param[in] nbyte Number of bytes to write.
|
||
1117 | *
|
||
1118 | * \return For success write() returns the number of bytes written, always
|
||
1119 | * \a nbyte. If an error occurs, write() returns -1. Possible errors
|
||
1120 | * include write() is called before a file has been opened, write is called
|
||
1121 | * for a read-only file, device is full, a corrupt file system or an I/O error.
|
||
1122 | *
|
||
1123 | */
|
||
1124 | size_t SdFile::write(const void* buf, uint16_t nbyte) { |
||
1125 | // convert void* to uint8_t* - must be before goto statements
|
||
1126 | const uint8_t* src = reinterpret_cast<const uint8_t*>(buf); |
||
1127 | |||
1128 | // number of bytes left to write - must be before goto statements
|
||
1129 | uint16_t nToWrite = nbyte; |
||
1130 | |||
1131 | // error if not a normal file or is read-only
|
||
1132 | if (!isFile() || !(flags_ & O_WRITE)) goto writeErrorReturn; |
||
1133 | |||
1134 | // seek to end of file if append flag
|
||
1135 | if ((flags_ & O_APPEND) && curPosition_ != fileSize_) {
|
||
1136 | if (!seekEnd()) goto writeErrorReturn; |
||
1137 | } |
||
1138 | |||
1139 | while (nToWrite > 0) { |
||
1140 | uint8_t blockOfCluster = vol_->blockOfCluster(curPosition_); |
||
1141 | uint16_t blockOffset = curPosition_ & 0X1FF;
|
||
1142 | if (blockOfCluster == 0 && blockOffset == 0) { |
||
1143 | // start of new cluster
|
||
1144 | if (curCluster_ == 0) { |
||
1145 | if (firstCluster_ == 0) { |
||
1146 | // allocate first cluster of file
|
||
1147 | if (!addCluster()) goto writeErrorReturn; |
||
1148 | } else {
|
||
1149 | curCluster_ = firstCluster_; |
||
1150 | } |
||
1151 | } else {
|
||
1152 | uint32_t next; |
||
1153 | if (!vol_->fatGet(curCluster_, &next)) return false; |
||
1154 | if (vol_->isEOC(next)) {
|
||
1155 | // add cluster if at end of chain
|
||
1156 | if (!addCluster()) goto writeErrorReturn; |
||
1157 | } else {
|
||
1158 | curCluster_ = next; |
||
1159 | } |
||
1160 | } |
||
1161 | } |
||
1162 | // max space in block
|
||
1163 | uint16_t n = 512 - blockOffset;
|
||
1164 | |||
1165 | // lesser of space and amount to write
|
||
1166 | if (n > nToWrite) n = nToWrite;
|
||
1167 | |||
1168 | // block for data write
|
||
1169 | uint32_t block = vol_->clusterStartBlock(curCluster_) + blockOfCluster; |
||
1170 | if (n == 512) { |
||
1171 | // full block - don't need to use cache
|
||
1172 | // invalidate cache if block is in cache
|
||
1173 | if (SdVolume::cacheBlockNumber_ == block) {
|
||
1174 | SdVolume::cacheBlockNumber_ = 0XFFFFFFFF;
|
||
1175 | } |
||
1176 | if (!vol_->writeBlock(block, src)) goto writeErrorReturn; |
||
1177 | src += 512;
|
||
1178 | } else {
|
||
1179 | if (blockOffset == 0 && curPosition_ >= fileSize_) { |
||
1180 | // start of new block don't need to read into cache
|
||
1181 | if (!SdVolume::cacheFlush()) goto writeErrorReturn; |
||
1182 | SdVolume::cacheBlockNumber_ = block; |
||
1183 | SdVolume::cacheSetDirty(); |
||
1184 | } else {
|
||
1185 | // rewrite part of block
|
||
1186 | if (!SdVolume::cacheRawBlock(block, SdVolume::CACHE_FOR_WRITE)) {
|
||
1187 | goto writeErrorReturn;
|
||
1188 | } |
||
1189 | } |
||
1190 | uint8_t* dst = SdVolume::cacheBuffer_.data + blockOffset; |
||
1191 | uint8_t* end = dst + n; |
||
1192 | while (dst != end) *dst++ = *src++;
|
||
1193 | } |
||
1194 | nToWrite -= n; |
||
1195 | curPosition_ += n; |
||
1196 | } |
||
1197 | if (curPosition_ > fileSize_) {
|
||
1198 | // update fileSize and insure sync will update dir entry
|
||
1199 | fileSize_ = curPosition_; |
||
1200 | flags_ |= F_FILE_DIR_DIRTY; |
||
1201 | } else if (dateTime_ && nbyte) { |
||
1202 | // insure sync will update modified date and time
|
||
1203 | flags_ |= F_FILE_DIR_DIRTY; |
||
1204 | } |
||
1205 | |||
1206 | if (flags_ & O_SYNC) {
|
||
1207 | if (!sync()) goto writeErrorReturn; |
||
1208 | } |
||
1209 | return nbyte;
|
||
1210 | |||
1211 | writeErrorReturn:
|
||
1212 | // return for write error
|
||
1213 | //writeError = true;
|
||
1214 | setWriteError(); |
||
1215 | return 0; |
||
1216 | } |
||
1217 | //------------------------------------------------------------------------------
|
||
1218 | /**
|
||
1219 | * Write a byte to a file. Required by the Arduino Print class.
|
||
1220 | *
|
||
1221 | * Use SdFile::writeError to check for errors.
|
||
1222 | */
|
||
1223 | size_t SdFile::write(uint8_t b) { |
||
1224 | return write(&b, 1); |
||
1225 | } |
||
1226 | //------------------------------------------------------------------------------
|
||
1227 | /**
|
||
1228 | * Write a string to a file. Used by the Arduino Print class.
|
||
1229 | *
|
||
1230 | * Use SdFile::writeError to check for errors.
|
||
1231 | */
|
||
1232 | size_t SdFile::write(const char* str) { |
||
1233 | return write(str, strlen(str));
|
||
1234 | } |
||
1235 | //------------------------------------------------------------------------------
|
||
1236 | /**
|
||
1237 | * Write a PROGMEM string to a file.
|
||
1238 | *
|
||
1239 | * Use SdFile::writeError to check for errors.
|
||
1240 | */
|
||
1241 | void SdFile::write_P(PGM_P str) {
|
||
1242 | for (uint8_t c; (c = pgm_read_byte(str)); str++) write(c);
|
||
1243 | } |
||
1244 | //------------------------------------------------------------------------------
|
||
1245 | /**
|
||
1246 | * Write a PROGMEM string followed by CR/LF to a file.
|
||
1247 | *
|
||
1248 | * Use SdFile::writeError to check for errors.
|
||
1249 | */
|
||
1250 | void SdFile::writeln_P(PGM_P str) {
|
||
1251 | write_P(str); |
||
1252 | println(); |
||
1253 | } |