FreeRTOS+FAT: ff_ftell() Oddities

The behavior of ff_ftell() surprises me. The FreeRTOS+FAT Standard API Reference claims that it “Returns the current read/write position of an open file in the embedded FAT file system. The position is returned as the number of bytes from the start of the file.” But I don’t think that’s true, unless I’m misunderstanding something.

Here is a little test. I start by simply opening (with truncation) a file, writing 8 bytes, and closing:

19      // FF_FILE *ff_fopen( const char *pcFile, const char *pcMode );
20      /* “w”      Open a file for reading and writing.
21      If the file already exists it will be truncated to zero length.
22      If the file does not already exist it will be created.*/
23      FF_FILE *file = ff_fopen( pcFileName, "w" );
24      configASSERT(file);
25      char buf[8];
26      memset(buf, '1', sizeof(buf) - 1);
27      buf[sizeof(buf) - 1] = 0;
28      // size_t ff_fwrite( const void *pvBuffer, size_t xSize, size_t xItems, FF_FILE * pxStream );
29      size_t ni = ff_fwrite(buf, sizeof buf, 1, file);
30      configASSERT(1 == ni);
31      // long ff_ftell( FF_FILE *pxStream );
32      /* Returns the current read/write position of an open file in the embedded FAT file system.
33      The position is returned as the number of bytes from the start of the file.*/
34      printf("%d: ff_ftell(): %ld\n", __LINE__, ff_ftell(file));
35      // int ff_feof( FF_FILE *pxStream );
36      /*Queries an open file in the embedded FAT file system to see if the file’s read/write pointer is at the end of the file.*/
37      printf("%d: ff_feof(): %d\n", __LINE__, ff_feof(file));
38      // void ff_rewind( FF_FILE *pxStream );
39      ff_rewind(file);
40      printf("%d: ff_ftell(): %ld\n", __LINE__, ff_ftell(file));
41      printf("%d: ff_feof(): %d\n", __LINE__, ff_feof(file));
42      // int ff_fclose( FF_FILE *pxStream );;
43      int ec = ff_fclose(file);
44      configASSERT(!ec);

which produces this output:

34: ff_ftell(): 8
37: ff_feof(): 1
40: ff_ftell(): 0
41: ff_feof(): 0

So far, so good.

Now, I open the same file for reading and writing with append, and it gets surprising (to me):

46      /* “a+”     Open a file for reading and writing.
47      If the file already exists then new data will be appended to the end of the file.
48      If the file does not already exist it will be created. */
49      file = ff_fopen( pcFileName, "a+" );
50      configASSERT(file);
51      // int ff_feof( FF_FILE *pxStream );
52      /*Queries an open file in the embedded FAT file system to see if the file’s read/write pointer is at the end of the file.*/
53      printf("%d: ff_feof(): %d\n", __LINE__, ff_feof(file));
54      printf("%d: ff_ftell(): %ld\n", __LINE__, ff_ftell(file));
55
56      memset(buf, '2', sizeof(buf));
57      ni = ff_fwrite(buf, sizeof buf, 1, file);
58      configASSERT(1 == ni);
59      printf("%d: ff_feof(): %d\n", __LINE__, ff_feof(file));
60      printf("%d: ff_ftell(): %ld\n", __LINE__, ff_ftell(file));
61      ec = ff_fseek( file, 0, FF_SEEK_END );
62      configASSERT(!ec);
63      printf("%d: ff_feof(): %d\n", __LINE__, ff_feof(file));
64      printf("%d: ff_ftell(): %ld\n", __LINE__, ff_ftell(file));
65      ff_rewind(file);
66      printf("%d: ff_feof(): %d\n", __LINE__, ff_feof(file));
67      printf("%d: ff_ftell(): %ld\n", __LINE__, ff_ftell(file));
68      while (!ff_feof(file)) {
69          ni = ff_fread(buf, sizeof buf, 1 , file);
70          if (1 == ni) {
71              printf("%.*s", sizeof(buf), buf);
72          } else {
73                  int error = stdioGET_ERRNO();
74                  printf("%d: read: %s (%d)\n", __LINE__, strerror(error), error);
75          }
76      };
77      putchar('\n');
78      ec = ff_fclose(file);
79      configASSERT(!ec);

which produces:

53: ff_feof(): 0
54: ff_ftell(): 0

Hmm, “If the file already exists then new data will be appended to the end of the file.”, but apparently the “current read/write position of an open file” is 0 in this case.

59: ff_feof(): 1
60: ff_ftell(): 16

So, now I’ve written 8 bytes (from a read/write position 0) and now I’m at 16? I mean, I should be, if the file is really opened for append, but how was the position 0 when I started?

63: ff_feof(): 1
64: ff_ftell(): 16

FF_SEEK_END made no difference, as expected.

66: ff_feof(): 0
67: ff_ftell(): 0
111111122222222

If I ff_rewind(), I’m back to a new position 0, apparently, and I can read all 16 bytes.

This caused a bug for me when I relied on ff_ftell() to determine whether a file opened in mode “a+” was already existing (and new data will be appended to the end of the file) or it was newly created and empty. (ff_feof() is no more helpful in this case).

I’ve gotten in the habit of putting an ff_fseek( file, 0, FF_SEEK_END ) before every ff_ftell().

If I read this correctly, the anomaly seems to be opening a file with a+ and then calling ff_tell() is reporting the position as 0, whereas it should be [in your posted case] 8. Is that correct, or are there other anomalies?

I just tried this on Windows using Visual studio and the Windows file system and got the same results as yourself - so the +FAT file system seems to be consistent with the Windows file system - so what you are seeing, if I understand it correctly, is probably the expected behaviour. The code I ran was:

FILE *f;
char buffer[ 8 ] = { 0 };

	f = fopen( "c:/temp/delete/myfile.txt", "w" );
	configASSERT( f );

	printf( "ftell for new file = %d\r\n", ftell( f ) );
	fwrite( buffer, sizeof( buffer ), 1, f );
	printf( "ftell after writing 8 bytes = %d\r\n", ftell( f ) );
	fclose( f );

	f = fopen( "c:/temp/delete/myfile.txt", "a+" );
	configASSERT( f );
	printf( "ftell for reopened file = %d\r\n", ftell( f ) );
	fwrite( buffer, sizeof( buffer ), 1, f );
	printf( "ftell after writing another 8 bytes = %d\r\n", ftell( f ) );
	fclose( f );

and the output I received was:

ftell for new file = 0
ftell after writing 8 bytes = 8
ftell for reopened file = 0
ftell after writing another 8 bytes = 16

The point to note being when I opened the file again with “a+” and did an ftell(), the returned value was 0, but after writing another 8 bytes, the returned value was 16.

@rtel : thanks for trying it out under Windows.

I think the behaviour is in line with the definition of the “a+” flag that I found here:

“a+” append/update:
Open a file for update (both for input and output) with all output operations
writing data at the end of the file. Repositioning operations (fseek, fsetpos,
rewind) affects the next input operations, but output operations move the
position back to the end of file. The file is created if it does not exist.

It means that a seek takes place implicitly before every output operation, i.e. ff_write(), and not after opening the file.
If you read from the file, the initial position will be zero.

I’ve gotten in the habit of putting an ff_fseek( file, 0, FF_SEEK_END )
before every ff_ftell().

Calling ff_fseek() is only necessary right after opening a file, and also after reading from the file in “append/update” mode.
Once you start appending data, the current file position will be at the end of the file.

Thanks, Richard and Hein!

I learn something new every day. I was expecting ftell() to give me an absolute offset from the beginning of the file at all times, but it looks like that expectation was wrong.

1 Like