apache / apache/nuttx

FAT: CAN'T SEEK TO END OF FILE IF READ-ONLY

Open
#3,438 0 comments 0 reactions 0 assignees View on GitHub
Type: Migrated TODO
Dominant language
C
Stars
4k
Forks
1.7k
Avg merge
1d 17h
Merged PRs (30d)
237

Description

```
Description: If the size of the underlying file is an exact multiple of the
FAT cluster size, then you cannot seek to the end of the file
if the file was opened read-only. In that case, the FAT lseek
logic will return ENOSPC.

This is because seeking to the end of the file involves seeking
to an offset that is the size of the file (number of bytes
allocated for file + 1). In order to seek to a position, the
current FAT implementation insists that there be allocated file
space at the seek position. Seeking beyond the end of the file
has the side effect of extending the file.

[NOTE: This automatic extension of the file cluster allocation
is probably unnecessary and another issue of its own.]

For example, suppose you have a cluster size that is 4096 bytes
and a file that is 8192 bytes long. Then the file will consist
of 2 allocated clusters at offsets 0 through 8191.

If the file is opened O_RDWR or O_WRONLY, then the statement:

offset = lseek(fd, 0, SET_SEEK);

will seek to offset 8192 which beyond the end of the file so a
new (empty) cluster will be added. Now the file consists of
three clusters and the file position refers to the first byte of
the third cluster.

If the file is open O_RDONLY, however, then that same lseek
statement will fail. It is not possible to seek to position
8192. That is beyond the end of the allocated cluster chain
and since the file is read-only, it is not permitted to extend
the cluster chain. Hence, the error ENOSPC is returned.

This code snippet will duplicate the problem. It assumes a
cluster size of 512 and that /tmp is a mounted FAT file system:

#define BUFSIZE 1024 //8192, depends on cluster size
static char buffer[BUFSIZE];

#if defined(BUILD_MODULE)
int main(int argc, FAR char *argv[])
#else
int hello_main(int argc, char *argv[])
#endif
{
ssize_t nwritten;
off_t pos;
int fd;
int ch;
int i;

for (i = 0, ch = ' '; i < BUFSIZE; i++)
{
buffer[i] = ch;

if (++ch == 0x7f)
{
ch = ' ';
}
}

fd = open("/tmp/testfile", O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd < 0)
{
printf("open failed: %d\n", errno);
return 1;
}

nwritten = write(fd, buffer, BUFSIZE);
if (nwritten < 0)
{
printf("write failed: %d\n", errno);
return 1;
}

close(fd);

fd = open("/tmp/testfile", O_RDONLY);
if (fd < 0)
{
printf("open failed: %d\n", errno);
return 1;
}

pos = lseek(fd, 0, SEEK_END);
if (pos < 0)
{
printf("lseek failed: %d\n", errno);
return 1;
}
else if (pos != BUFSIZE)
{
printf("lseek failed: %d\n", pos);
return 1;
}

close(fd);
return 0;
}

Status: Open
Priority: Medium. Although this is a significant design error, the problem
has existed for 11 years without being previously reported. I
conclude, then that the exposure from this problem is not great.

Why would you seek to the end of a file using a read=only file
descriptor anyway? Only one reason I can think of: To get the
size of the file. The alternative (and much more efficient) way
to do that is via stat().
```

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.