COSA
An Object-Oriented Platform for Arduino Programming
CFFS.cpp
Go to the documentation of this file.
1 
21 #include "CFFS.hh"
22 
24 uint32_t CFFS::current_dir_addr = 0L;
25 
26 int
27 CFFS::File::open(const char* filename, uint8_t oflag)
28 {
29  // Check that the file is not open and the open mode
30  if (is_open()) return (EBUSY);
31 
32  // Check if the file should be created
33  if (oflag & O_CREAT) {
34  oflag |= O_WRITE;
35  int res = CFFS::create(filename, FILE_ENTRY_TYPE, oflag, m_entry, m_entry_addr);
36  if (res < 0) return (res);
38  m_current_pos = 0L;
39  m_file_size = 0L;
40  }
41 
42  // Check that the file exists; open file
43  else {
44  if ((oflag & O_WRITE) == 0) oflag |= O_READ;
45  int res = lookup(filename, m_entry, m_entry_addr);
46  if (res < 0) return (res);
48  if (res < 0) return (res);
50  }
51 
52  // Check if the position should be from the start of the file
53  if (((oflag & O_RDWR) == O_READ) || (oflag & O_CREAT)) {
55  m_current_pos = 0L;
56  }
57 
58  // Save flags
59  m_flags = oflag;
60  return (0);
61 }
62 
63 int
65 {
66  if (m_flags == 0) return (ENXIO);
67  m_flags = 0;
69 }
70 
71 int
73 {
74  if (m_flags == 0) return (ENXIO);
75  m_flags = 0;
76  return (0);
77 }
78 
79 int
80 CFFS::File::seek(uint32_t pos, uint8_t whence)
81 {
82  // Check mode and parameters
83  if ((m_flags & O_READ) == 0) return (EPERM);
84  if (pos > m_file_size) return (EINVAL);
85 
86  // Fix: Should implement all seek variants
87  if (whence != SEEK_SET) return (EINVAL);
88 
89  // Find sector and position in sector
90  descr_t entry;
91  uint32_t addr = m_entry.ref;
92  m_current_pos = pos;
93  while (pos != 0) {
94  // Read sector header and check that it is a file block
95  if (device->read(&entry, addr, sizeof(entry)) != sizeof(entry))
96  return (EIO);
97  if (entry.type != FILE_BLOCK_TYPE)
98  return (ENXIO);
99  // Check if additional sector skip is needed
100  uint32_t size = entry.size - sizeof(descr_t);
101  if (pos >= size) {
102  addr = entry.ref;
103  if (addr == NULL_REF)
104  return (ENXIO);
105  pos -= size;
106  // First byte in next sector
107  if (pos == 0)
108  m_current_addr = addr + sizeof(descr_t);
109  }
110  else {
111  // Byte in current sector
112  m_current_addr = addr + sizeof(descr_t) + pos;
113  pos = 0;
114  }
115  }
116 
117  // Found the position
118  return (0);
119 }
120 
121 int
122 CFFS::File::write(const void *buf, size_t size)
123 {
124  return (write(buf, size, false));
125 }
126 
127 int
128 CFFS::File::write_P(const void* buf, size_t size)
129 {
130  return (write(buf, size, true));
131 }
132 
133 int
135 {
136  char c;
137  if (read(&c, sizeof(c)) != sizeof(c)) return (IOStream::EOF);
138  return (c & 0xff);
139 }
140 
141 int
142 CFFS::File::read(void* buf, size_t size)
143 {
144  // Check file access mode
145  if ((m_flags & O_READ) == 0) return (EPERM);
146 
147  // Adjust requested size if needed
148  uint32_t remains = m_file_size - m_current_pos;
149  if (size > remains) size = remains;
150  int count = size;
151 
152  // Read sectors until buffer is filled
153  while (size != 0) {
154  int res = CFFS::read(buf, m_current_addr, size);
155  if (res < 0) return (EIO);
156  size -= res;
157  m_current_pos += res;
158  m_current_addr += res;
159 
160  // Read next sector header if needed
161  if ((m_current_addr & device->SECTOR_MASK) == 0) {
162  descr_t header;
163  uint32_t addr = m_current_addr - device->SECTOR_BYTES;
164  if (device->read(&header, addr, sizeof(header)) != sizeof(header))
165  return (EIO);
166  if (header.type != FILE_BLOCK_TYPE) return (ENXIO);
167  if (header.size != device->SECTOR_BYTES) return (ENXIO);
168  if (header.ref == NULL_REF) return (ENXIO);
169  m_current_addr = header.ref + sizeof(header);
170  }
171  }
172 
173  // Return number of bytes read
174  return (count);
175 }
176 
177 int
178 CFFS::File::write(const void* buf, size_t size, bool progmem)
179 {
180  // Check access mode
181  if ((m_flags & O_WRITE) == 0) return (EPERM);
182 
183  // Check write position; must be end of file
184  if (m_current_pos != m_file_size) return (EINVAL);
185  int count = size;
186 
187  // Write sectors with buffer data
188  while (size != 0) {
189  int res;
190  if (progmem)
191  res = CFFS::write_P(m_current_addr, buf, size);
192  else
193  res = CFFS::write(m_current_addr, buf, size);
194  if (res < 0) return (res);
195  m_current_addr += res;
196  m_current_pos += res;
197  m_file_size += res;
198  size -= res;
199 
200  // Check if sector is exhaused
201  if ((m_current_addr & device->SECTOR_MASK) == 0) {
202  // Allocate a new sector
203  uint32_t sector = next_free_sector();
204  if (sector == 0L) return (ENOSPC);
205 
206  // Update current sector header
207  descr_t header;
208  uint32_t addr = m_current_addr - device->SECTOR_BYTES;
209  if (device->read(&header, addr, sizeof(header)) != sizeof(header))
210  return (EIO);
211  if (header.type != FILE_BLOCK_TYPE) return (ENXIO);
212  if (header.size != device->SECTOR_BYTES) return (ENXIO);
213  if (header.ref != NULL_REF) return (ENXIO);
214 
215  // Append new sector
216  header.ref = sector;
217  if (device->write(addr, &header, sizeof(header)) != sizeof(header))
218  return (EIO);
219 
220  // Continue write in new sector
221  m_current_addr = sector + sizeof(header);
222  }
223  }
224  return (count);
225 }
226 
227 bool
229 {
230  // Check that the file system access is not already initiated
231  if (device != NULL) return (false);
232 
233  // Check that the device is formatted and contains a file system
234  descr_t entry;
235  uint32_t addr = 0L;
236  if ((flash->read(&entry, addr, sizeof(entry)) != sizeof(entry))
237  || (entry.type != CFFS_TYPE))
238  return (false);
239  addr += sizeof(entry);
240  if ((flash->read(&entry, addr, sizeof(entry)) != sizeof(entry))
241  || (entry.type != DIR_BLOCK_TYPE)
242  || (entry.ref != addr)
243  || strcmp_P(entry.name, PSTR("..")))
244  return (false);
245 
246  // A file system and root directory exists
247  device = flash;
248  current_dir_addr = addr;
249  return (true);
250 }
251 
252 int
254 {
255  // Check that the file system driver is initiated
256  if (device == NULL) return (ENXIO);
257 
258  // Read directory header for number of entries
259  uint32_t addr = current_dir_addr;
260  descr_t entry;
261  uint8_t j = 0;
262  if (device->read(&entry, addr, sizeof(entry)) != sizeof(entry))
263  return (EIO);
264  const uint16_t ENTRY_MAX = entry.size / sizeof(entry);
265 
266  // Print file names to given output stream
267  for (uint16_t i = 0; i < ENTRY_MAX; i++, addr += sizeof(entry)) {
268  if (device->read(&entry, addr, sizeof(entry)) != sizeof(entry))
269  return (EIO);
270  if ((entry.type == DIR_BLOCK_TYPE)
271  || (entry.type == DIR_ENTRY_TYPE)
272  || (entry.type == FILE_ENTRY_TYPE)) {
273  outs << entry.name << tab;
274  j += 1;
275  if ((j & 0x7) == 0) outs << endl;
276  }
277  }
278  if (j & 0x7) outs << endl;
279 
280  // Return successful
281  return (0);
282 }
283 
284 int
285 CFFS::rm(const char* filename)
286 {
287  File file;
288  int res = file.open(filename, O_READ);
289  return (res < 0 ? res : file.remove());
290 }
291 
292 int
293 CFFS::cd(const char* filename)
294 {
295  descr_t entry;
296  uint32_t addr;
297  int res = CFFS::lookup(filename, entry, addr);
298  if (res < 0)
299  return (res);
300  if ((entry.type != DIR_BLOCK_TYPE) && (entry.type != DIR_ENTRY_TYPE))
301  return (ENOTDIR);
302  current_dir_addr = entry.ref;
303  return (0);
304 }
305 
306 int
307 CFFS::mkdir(const char* filename)
308 {
309  descr_t entry;
310  uint32_t addr;
311  return (CFFS::create(filename, CFFS::DIR_ENTRY_TYPE, O_EXCL, entry, addr));
312 }
313 
314 int
315 CFFS::rmdir(const char* filename)
316 {
317  UNUSED(filename);
318  return (ENOSYS);
319 }
320 
321 int
322 CFFS::format(Flash::Device* flash, const char* name)
323 {
324  // Check that the file system driver is initiated
325  if (device != NULL) return (EPERM);
326 
327  // Check that the drive name is not too long
328  if (strlen(name) >= FILENAME_MAX) return (ENAMETOOLONG);
329 
330  // Erase sectors
331  descr_t header;
332  uint32_t addr = 0L;
333  const uint8_t SIZE = flash->SECTOR_BYTES / 1024;
334  for (uint16_t i = 0; i < flash->SECTOR_MAX; i++) {
335  if (flash->read(&header, addr, sizeof(header)) != sizeof(header))
336  return (EIO);
337  if (header.type != FREE_TYPE) {
338  if (flash->erase(addr, SIZE) != 0) return (EIO);
339  }
340  addr += flash->SECTOR_BYTES;
341  }
342 
343  // Write file system header with drive name
344  addr = 0L;
345  memset(&header, 0, sizeof(header));
346  header.type = CFFS_TYPE;
347  header.size = sizeof(header);
348  header.ref = sizeof(header);
349  strcpy(header.name, name);
350  if (flash->write(addr, &header, sizeof(header)) != sizeof(header))
351  return (EIO);
352 
353  // Write root directory
354  addr += sizeof(header);
355  memset(&header, 0, sizeof(header));
356  header.type = DIR_BLOCK_TYPE;
357  header.size = flash->DEFAULT_SECTOR_BYTES - sizeof(header);
358  header.ref = addr;
359  strcpy_P(header.name, PSTR(".."));
360  if (flash->write(addr, &header, sizeof(header)) != sizeof(header))
361  return (EIO);
362  return (0);
363 }
364 
365 int
366 CFFS::lookup(const char* filename, descr_t &entry, uint32_t& addr)
367 {
368  // Check that the file system driver is initiated
369  if (device == NULL) return (ENXIO);
370 
371  // Read current director and lookup entry with given filename
372  addr = current_dir_addr;
373  if (device->read(&entry, addr, sizeof(entry)) != sizeof(entry))
374  return (EIO);
375  const uint16_t ENTRY_MAX = entry.size / sizeof(descr_t);
376  addr = entry.ref;
377  for (uint16_t i = 0; i < ENTRY_MAX; i++, addr += sizeof(descr_t)) {
378  if (device->read(&entry, addr, sizeof(entry)) != sizeof(entry))
379  return (EIO);
380  if (entry.type == FREE_TYPE) break;
381  if ((entry.type & ALLOC_MASK) == 0) continue;
382  if (strcmp(filename, entry.name)) continue;
383  return (0);
384  }
385 
386  // Not found
387  return (ENOENT);
388 }
389 
390 int
391 CFFS::create(const char* filename, uint16_t type, uint8_t flags,
392  descr_t &entry, uint32_t &addr)
393 {
394  // Check that the file system driver is initiated
395  if (device == NULL) return (ENXIO);
396 
397  // Check parameters
398  if ((type != DIR_ENTRY_TYPE) && (type != FILE_ENTRY_TYPE)) return (EINVAL);
399  if (strlen(filename) >= FILENAME_MAX) return (ENAMETOOLONG);
400 
401  // Search through the current directory
402  addr = current_dir_addr;
403  if (device->read(&entry, addr, sizeof(entry)) != sizeof(entry))
404  return (EIO);
405  uint16_t ENTRY_MAX = entry.size / sizeof(descr_t);
406  for (uint16_t i = 0; i < ENTRY_MAX; i++, addr += sizeof(descr_t)) {
407  if (device->read(&entry, addr, sizeof(entry)) != sizeof(entry))
408  return (EIO);
409  // Skip deleted entries
410  if ((entry.type & ALLOC_MASK) == 0) continue;
411  // Check if file name is already used; error or remove
412  if (!strcmp(filename, entry.name)) {
413  if ((flags & O_EXCL) || (type == DIR_ENTRY_TYPE)) return (EEXIST);
414  int res = remove(addr, entry.type);
415  if (res < 0) return (res);
416  }
417  else if (entry.type == FREE_TYPE) {
418  // Creating a directory or file entry
419  if (type == DIR_ENTRY_TYPE) {
420  uint32_t dir = next_free_directory();
421  if (dir == 0L) return (ENOSPC);
422  entry.ref = dir;
423  }
424  else {
425  uint32_t sector = next_free_sector();
426  if (sector == 0L) return (ENOSPC);
427  entry.ref = sector;
428  }
429  strcpy(entry.name, filename);
430  entry.type = type;
431  entry.size = sizeof(entry);
432  // Write the entry and return the address
433  if (device->write(addr, &entry, sizeof(entry)) != sizeof(entry))
434  return (EIO);
435  return (0);
436  }
437  }
438 
439  // Directory is full
440  return (ENOSPC);
441 }
442 
443 int
444 CFFS::remove(uint32_t addr, uint16_t type)
445 {
446  // Check that the file system driver is initiated
447  if (device == NULL) return (ENXIO);
448 
449  // Read directory entry and check type
450  descr_t entry;
451  if (device->read(&entry, addr, sizeof(entry)) != sizeof(entry))
452  return (EIO);
453  if ((entry.type != type))
454  return (EINVAL);
455 
456  // Save reference to sector to erase
457  uint32_t ref = entry.ref;
458 
459  // Mark the entry as removed in the directory block
460  memset(&entry, 0, sizeof(entry));
461  if (device->write(addr, &entry, sizeof(entry)) != sizeof(entry))
462  return (EIO);
463 
464  // Erase sectors
465  while (ref != NULL_REF) {
466  if (device->read(&entry, ref, sizeof(entry)) != sizeof(entry))
467  return (EIO);
468  if (device->erase(ref, entry.size / 1024) != 0) return (EIO);
469  ref = entry.ref;
470  }
471  return (0);
472 }
473 
474 int
475 CFFS::read(void* dest, uint32_t src, size_t size)
476 {
477  if (device == NULL) return (ENXIO);
478  size_t avail = device->SECTOR_BYTES - (src & device->SECTOR_MASK);
479  if (size > avail) size = avail;
480  return (device->read(dest, src, size));
481 }
482 
483 int
484 CFFS::write(uint32_t dest, const void* src, size_t size)
485 {
486  if (device == NULL) return (ENXIO);
487  size_t avail = device->SECTOR_BYTES - (dest & device->SECTOR_MASK);
488  if (size > avail) size = avail;
489  return (device->write(dest, src, size));
490 }
491 
492 int
493 CFFS::write_P(uint32_t dest, const void* src, size_t size)
494 {
495  if (device == NULL) return (ENXIO);
496  size_t avail = device->SECTOR_BYTES - (dest & device->SECTOR_MASK);
497  if (size > avail) size = avail;
498  return (device->write_P(dest, src, size));
499 }
500 
501 uint32_t
503 {
504  // Check that the file system driver is initiated
505  if (device == NULL) return (0L);
506 
507  // Search for a free sector
508  descr_t header;
509  uint32_t addr = device->SECTOR_BYTES;
510  for (uint16_t i = 1; i < device->SECTOR_MAX; i++) {
511  if (device->read(&header, addr, sizeof(header)) != sizeof(header))
512  return (0L);
513  if (header.type != FREE_TYPE) {
514  addr += device->SECTOR_BYTES;
515  continue;
516  }
517  // Initiate the sector header
518  header.type = FILE_BLOCK_TYPE;
519  header.size = device->SECTOR_BYTES;
520  header.ref = NULL_REF;
521  memset(header.name, 0, sizeof(header.name));
522  if (device->write(addr, &header, sizeof(header)) != sizeof(header))
523  return (0L);
524  // Return address of sector
525  return (addr);
526  }
527  return (0L);
528 }
529 
530 uint32_t
532 {
533  // Check that the file system driver is initiated
534  if (device == NULL) return (0L);
535 
536  // Search for a free directory
537  descr_t header;
538  uint32_t addr;
540  addr = device->SECTOR_BYTES;
541  for (uint16_t i = 1; i < device->SECTOR_MAX; i++, addr += device->SECTOR_BYTES) {
542  if (device->read(&header, addr, sizeof(header)) != sizeof(header))
543  return (0L);
544  if (header.type == FREE_TYPE) break;
545  }
546  }
547  else {
549  for (uint16_t i = 1; i < DIR_MAX; i++, addr += device->DEFAULT_SECTOR_BYTES) {
550  if (device->read(&header, addr, sizeof(header)) != sizeof(header))
551  return (0L);
552  if (header.type == FREE_TYPE) break;
553  }
554  }
555  if (header.type != FREE_TYPE) return (0L);
556 
557  // Initiate the parent directory reference
558  memset(&header, 0, sizeof(header));
559  header.type = DIR_BLOCK_TYPE;
560  header.size = device->DEFAULT_SECTOR_BYTES;
561  header.ref = current_dir_addr;
562  strcpy_P(header.name, PSTR(".."));
563  if (device->write(addr, &header, sizeof(header)) != sizeof(header))
564  return (0L);
565  // Return the directory address
566  return (addr);
567 }
568 
569 int
570 CFFS::find_end_of_file(uint32_t addr, uint32_t &pos, uint32_t &size)
571 {
572  // Check that the file system driver is initiated
573  if (device == NULL) return (ENXIO);
574 
575  // Locate last sector
576  descr_t header;
577  size = 0L;
578  while (1) {
579  if (device->read(&header, addr, sizeof(header)) != sizeof(header))
580  return (EIO);
581  if (header.type != FILE_BLOCK_TYPE) return (ENXIO);
582  if (header.size != device->SECTOR_BYTES) return (ENXIO);
583  if (header.ref == NULL_REF) break;
584  addr = header.ref;
585  size += (header.size - sizeof(header));
586  }
587 
588  // Locate end of sector
589  uint8_t buf[256];
590  addr += device->SECTOR_BYTES;
591  const uint16_t BUF_MAX = device->SECTOR_BYTES / sizeof(buf);
592  for (uint16_t i = 0; i < BUF_MAX; i++) {
593  addr -= sizeof(buf);
594  if (device->read(buf, addr, sizeof(buf)) != sizeof(buf))
595  return (EIO);
596  uint16_t j = sizeof(buf) - 1;
597  while (buf[j] == 0xff) if (j == 0) break; else j--;
598  if (j == 0) continue;
599  addr += j + 1;
600  break;
601  }
602 
603  // And return position and size
604  pos = addr;
605  size += (addr & device->SECTOR_MASK) - sizeof(header);
606  return (0);
607 }
#define EIO
Definition: Errno.h:32
static int write(uint32_t dest, const void *src, size_t size)
Definition: CFFS.cpp:484
#define EINVAL
Definition: Errno.h:49
uint16_t type
Type of file and entry state.
Definition: CFFS.hh:45
Absolute position.
Definition: FS.hh:47
virtual int write(uint32_t dest, const void *src, size_t size)=0
static const size_t FILENAME_MAX
Definition: CFFS.hh:38
#define NULL
Definition: Types.h:101
static int find_end_of_file(uint32_t sector, uint32_t &pos, uint32_t &size)
Definition: CFFS.cpp:570
static uint32_t current_dir_addr
Definition: CFFS.hh:323
Directory reference entry.
Definition: CFFS.hh:78
CFFS::descr_t m_entry
Cached directory entry.
Definition: CFFS.hh:232
Open for reading.
Definition: FS.hh:28
#define PSTR(s)
Definition: Types.h:202
File System Master header.
Definition: CFFS.hh:75
virtual int getchar()
Definition: CFFS.cpp:134
uint32_t m_entry_addr
Entry address.
Definition: CFFS.hh:231
#define ENOTDIR
Definition: Errno.h:47
#define EEXIST
Definition: Errno.h:44
virtual int read(void *dest, uint32_t src, size_t size)=0
IOStream & tab(IOStream &outs)
Definition: IOStream.hh:805
static int rm(const char *filename)
Definition: CFFS.cpp:285
static int cd(const char *filename)
Definition: CFFS.cpp:293
int remove()
Definition: CFFS.cpp:64
Directory block.
Definition: CFFS.hh:79
#define ENOSYS
Definition: Errno.h:65
static Flash::Device * device
Definition: CFFS.hh:320
static int mkdir(const char *filename)
Definition: CFFS.cpp:307
static const size_t DIR_MAX
Definition: CFFS.hh:317
#define ENXIO
Definition: Errno.h:33
virtual int erase(uint32_t dest, uint8_t size)=0
uint8_t m_flags
File open flags.
Definition: CFFS.hh:230
const uint16_t SECTOR_MAX
Definition: Flash.hh:43
File data block.
Definition: CFFS.hh:77
static int lookup(const char *filename, descr_t &entry, uint32_t &addr)
Definition: CFFS.cpp:366
static int create(const char *filename, uint16_t type, uint8_t flags, descr_t &entry, uint32_t &addr)
Definition: CFFS.cpp:391
File descriptor entry.
Definition: CFFS.hh:76
const uint32_t SECTOR_BYTES
Definition: Flash.hh:34
char name[FILENAME_MAX]
Printable name of object(zero terminated).
Definition: CFFS.hh:48
static uint32_t next_free_sector()
Definition: CFFS.cpp:502
static const uint32_t NULL_REF
Definition: CFFS.hh:88
char * strcpy_P(char *s1, str_P s2)
Definition: Types.h:236
#define UNUSED(x)
Definition: ATmega328P.hh:31
static const int EOF
Definition: IOStream.hh:33
virtual int read(void *buf, size_t size)
Definition: CFFS.cpp:142
IOStream & endl(IOStream &outs)
Definition: IOStream.hh:817
Open for reading and writing.
Definition: FS.hh:32
uint32_t m_file_size
File size.
Definition: CFFS.hh:234
static const uint32_t DEFAULT_SECTOR_BYTES
Definition: Flash.hh:37
#define EPERM
Definition: Errno.h:28
Allocated mask.
Definition: CFFS.hh:81
static bool begin(Flash::Device *flash)
Definition: CFFS.cpp:228
uint32_t ref
Reference value (pointer).
Definition: CFFS.hh:47
static int ls(IOStream &outs)
Definition: CFFS.cpp:253
bool is_open(void) const
Definition: CFFS.hh:124
uint32_t m_current_addr
Current flash address.
Definition: CFFS.hh:235
static int write_P(uint32_t dest, const void *src, size_t size)
Definition: CFFS.cpp:493
uint32_t size()
Definition: CFFS.hh:177
#define ENAMETOOLONG
Definition: Errno.h:63
static int format(Flash::Device *flash, const char *name)
Definition: CFFS.cpp:322
virtual int write_P(uint32_t dest, const void *scr, size_t size)=0
#define EBUSY
Definition: Errno.h:43
static int read(void *dest, uint32_t src, size_t size)
Definition: CFFS.cpp:475
#define ENOENT
Definition: Errno.h:29
Create the file if nonexistent.
Definition: FS.hh:36
static uint32_t next_free_directory()
Definition: CFFS.cpp:531
#define ENOSPC
Definition: Errno.h:55
virtual int write_P(const void *buf, size_t size)
Definition: CFFS.cpp:128
uint32_t m_current_pos
Current logical position.
Definition: CFFS.hh:236
Free descriptor.
Definition: CFFS.hh:80
int open(const char *filename, uint8_t oflag=O_READ)
Definition: CFFS.cpp:27
int close()
Definition: CFFS.cpp:72
Open for write.
Definition: FS.hh:30
virtual int write(const void *buf, size_t size)
Definition: CFFS.cpp:122
int seek(uint32_t pos, uint8_t whence=SEEK_SET)
Definition: CFFS.cpp:80
Definition: FS.hh:37
static int rmdir(const char *filename)
Definition: CFFS.cpp:315
int strcmp_P(const char *s1, str_P s2)
Definition: Types.h:224
uint32_t size
Number of bytes (including header).
Definition: CFFS.hh:46
const uint32_t SECTOR_MASK
Definition: Flash.hh:40
static int remove(uint32_t addr, uint16_t type)
Definition: CFFS.cpp:444