The following C program creates an empty file of a specified length. I have tested this on my local Ubuntu 20.04 laptop and it works. I am now running it in an instance at Digital Ocean (Ubuntu 22.04), but the line "fclose(outfile);" throws a segmentation fault. In the cloud I am running it in a Docker container, but not locally.
#include <stdio.h>
#include <string.h>
#include <errno.h>
size_t create_empty_file (char * out_fname, size_t file_size)
{
FILE *outfile;
char buf[1024];
outfile = fopen(out_fname, "wb+");
if (outfile == NULL) {
strcpy(buf, strerror(errno));
printf("%d --> %sn", errno, buf); }
fseek(outfile, file_size, SEEK_SET);
if (errno != 0) {
strcpy(buf, strerror(errno));
printf("%d --> %sn", errno, buf); }
fputc('', outfile);
if (errno != 0) {
strcpy(buf, strerror(errno));
printf("%d --> %sn", errno, buf); }
fclose( outfile );
return 0;
}
The file does get created AND the file does get closed — I know that because it shows a file size of zero until the file is closed, and the file does show its 4GB specified length.
In the usual case, fclose throws a segfault when the file pointer is null, but that’s not the case here. And it works on my local laptop.
Here are the segfault details from GDB:
Program received signal SIGSEGV, Segmentation fault.
0x00007fe121321c70 in __GI__IO_setb (f=f@entry=0x145b210, b=b@entry=0x0, eb=eb@entry=0x0, a=a@entry=0) at ./libio/genops.c:338
338 ./libio/genops.c: No such file or directory.
Thanks for any help with this.
2
Answers
Based on the suggestion by Shawn above, to use ftruncate, I rewrote this using open, ftruncate and close, and it works perfectly.
size_t create_empty_file (char * out_fname, off_t file_size, mode_t mode) {
}
You are not handling the errors correctly, and that may cause memory corruption, which breaks
fclose(...)
. Most likely the bug is in your program, not infclose(...)
.Here is how to handle the errors correctly:
After changing your function to this, you won’t get a segmentation fault. You may get an I/O error though (printed by
fprintf(stderr, ...)
above), pay attention to it. Maybe the reason for the I/O error is that your filesystem is full. (However, that’s unusual in this case, creating a huge file by seeking shouldn’t use much filesystem space, most filesystems optimize those holes away.)Here are some useful rules of thumb for error checking:
In general, check the return value of each function you call.
Only use
errno
if the return value of the previous I/O function was not success.For stdio buffered output (e.g.
fputc(...)
), you may skip the error checking, and check the error after flushing explicitly (if (ferror(outfile) || fflush(outfile) != 0)
in the code above).In general, stop I/O on a file (and close it) after the first error.
Propagate the error indication in the return value of your function.
Print the error the stderr.
I’ve fixed two more problems above:
+
is not needed infopen(..., "wb+")
, because you don’t want to read from the file.file_size - 1
for seeking, becausefputc
adds 1 more byte.