Are there any circumstances in which an IO function can never fail? - c

Most IO functions (fopen, fread, etc) can fail if the system encounters an error, or if a passed argument is invalid (fseek to some value other than SEEK_SET, SEEK_CUR, or SEEK_END), but they can also fail for reasons beyond the programmers control. Checking for success is tedious and might sometimes unnecessary. Are there any circumstances in which a function will not fail even if it could fail in certain circumstances? Such as fcloseing a file opened in read only mode, and the file pointer was freshly returned by fopen? When do I not have to worry about an IO function failing? Are these such circumstances?
File was opened in READ mode
File pointer was returned by fopen and nullchecked
Read operations
What other circumstances?
I do not see why fclose would fail on a file opened in READ mode, because there is not going to be any buffers written to the file, all that needs to happen is freeing up memory, and free cannot fail so neither should fclose, is this assumption correct?
Also should I avoid rewind because it cannot indicate error?

Related

c language : fwrite doesnt do anything

{
FILE* f1 = fopen("C:\\num1.bin", "wb+");//it will create a new file
int A[] = { 1,3,6,28 }; //int arr
fwrite(A, sizeof(A), 1, f1); //should insert the A array to the file
}
I do see the file but even after the fwrite, the file remains empty (0 bytes), does anyone know why?
You need to close the file with fclose
Otherwise the write buffer will not (necessarily) force the file contents to be written to disk
A couple of things:
As #Grantly correctly noted above, you are missing a call to fclose or fflush after writing to the file. Without this any cached/pending writes will not necessarily be actually written to the open file.
You do not check the return value of fopen. If fopen fails for any reason it will return a NULL pointer and not a valid file pointer. Since you're writing directly to the root of the drive C:\ on a Windows platform, that's something you definitely do want to be checking for (not that you shouldn't in other cases too, but run under a regular user account that location is often write protected).
Result of fwrite is not required to appear in the fille immediately after it returns. That is because file operations usually work in a buffered manner, i.e. they are cached and then flushed to speed things up and improve the performance.
The content of the file will be updated after you call fclose:
fclose()
(...) Any unwritten buffered data are flushed to the OS. Any unread buffered
data are discarded.
You may also explicitly flush the internal buffer without closing the file using fflush:
fflush()
For output streams (and for update streams on which the last operation
was output), writes any unwritten data from the stream's buffer to the
associated output device.

Is there a guaranteed and safe way to truncate a file from ANSI C FILE pointer?

I know ANSI C defines fopen, fwrite, fread, fclose to modify a file's content. However, when it comes to truncating a file, we have to turn to OS specific function, e.g, truncate() on Linux, _chsize_s_() on Windows. But before we can call those OS specific functions, we have to obtain the file-handle from FILE pointer, by calling fileno, also an non-ANSI-C one.
My question is: Is it reliable to continue using FILE* after truncating the file? I mean, ANSI C FILE layer has its own buffer and does not know the file is truncated from beneath. In case the buffered bytes is beyond the truncated point, will the buffered content be flushed to the file when doing fclose() ?
If no guarantee, what is the best practice of using file I/O functions accompanied with truncate operation when write a Windows-Linux portable program?
Similar question: When querying file size from a file-handle returned by fileno , is it the accurate size when I later call fclose() -- without further fwrite()?
[EDIT 2012-12-11]
According to Joshua's suggestion. I conclude that current possible best practice is: Set the stream to unbuffered mode by calling setbuf(stream, NULL); , then truncate() or _chsize_s() can work peacefully with the stream.
Anyhow, no official document seems to explicitly confirm this behavior, whether Microsoft CRT or GNU glibc.
The POSIX way....
ftruncate() is what you're looking for, and it's been in POSIX base specifications since 2001, so it should be in every modern POSIX-compatible system by now.
Note that ftruncate() operates on a POSIX file descriptor (despite its potentially misleading name), not a STDIO stream FILE handle. Note also that mixing operations on the STDIO stream and on the underlying OS calls which operate on the file descriptor for the open stream can confuse the internal runtime state of the STDIO library.
So, to use ftruncate() safely with STDIO it may be necessary to first flush any STDIO buffers (with fflush()) if your program may have already written to the stream in question. This will avoid STDIO trying to flush the otherwise unwritten buffer to the file after the truncation has been done.
You can then use fileno() on the STDIO stream's FILE handle to find the underlying file descriptor for the open STDIO stream, and you would then use that file descriptor with ftruncate(). You might consider putting the call to fileno() right in the parameter list for the ftruncate() call so that you don't keep the file descriptor around and accidentally use it yet other ways which might further confuse the internal state of STDIO. Perhaps like this (say to truncate a file to the current STDIO stream offset):
/*
* NOTE: fflush() is not needed here if there have been no calls to fseek() since
* the last fwrite(), assuming it extended the length of the stream --
* ftello() will account for any unwritten buffers
*/
if (ftruncate(fileno(stdout), ftello(stdout)) == -1) {
fprintf(stderr, "%s: ftruncate(stdout) failed: %s\n", argv[0], strerror(errno));
exit(1);
}
/* fseek() is not necessary here since we truncated at the current offset */
Note also that the POSIX definition of ftruncate() says "The value of the seek pointer shall not be modified by a call to ftruncate()", so this means you may also need to use use fseek() to set the STDIO layer (and thus indirectly the file descriptor) either to the new end of the file, or perhaps back to the beginning of the file, or somewhere still within the boundaries of the file, as desired. (Note that the fseek() should not be necessary if the truncation point is found using ftello().)
You should not have to make the STDIO stream unbuffered if you follow the procedure above, though of course doing so could be an alternative to using fflush() (but not fseek()).
Without POSIX....
If you need to stick to strict ISO Standard C, say C99, then you have no portable way to truncate a file to a given length other than zero (0) length. The latest draft of C11 that I have says this in Section 7.21.3 (paragraph 2):
Binary files are not truncated, except as defined in 7.21.5.3. Whether a write on a text stream causes the associated file to be truncated beyond that point is implementation-defined.
(and 7.21.5.3 describes the flags to fopen() which allow a file to be truncated to a length of zero)
The caveat about text files is there because on silly systems that have both text and binary files (as opposed to just plain POSIX-style content agnostic files) then it is often possible to write a value to the file which will be stored in the file at the position written and which will be treated as an EOF indicator when the file is next read.
Other types of systems may have different underlying file I/O interfaces that are not compatible with POSIX while still providing a compatible ISO C STDIO library. In theory if such a system offers something similar to fileno() and ftrunctate() then a similar procedure could be used with them as well, provided that one took the same care to avoid confusing the internal runtime state of the STDIO library.
With regard to querying file size....
You also asked whether the file size found by querying the file descriptor returned by fileno() would be an accurate representation of the file size after a successful call to fclose(), even without any further calls to fwrite().
The answer is: Don't do that!
As I mentioned above, the POSIX file descriptor for a file opened as a STDIO stream must be used very carefully if you don't want to confuse the internal runtime state of the STDIO library. We can add here that it is important not to confuse yourself with it either.
The most correct way to find the current size of a file opened as a STDIO stream is to seek to the end of it and then ask where the stream pointer is by using only STDIO functions.
Isn't an unbuffered write of zero bytes supposed to truncate the file at that point?
See this question for how to set unbuffered: Unbuffered I/O in ANSI C

ftruncate on file opened with fopen

Platform is Ubuntu Linux on ARM.
I want to write a string to a file, but I want every time to truncate the file and then write the string, i.e. no append.
I have this code:
f=fopen("/home/user1/refresh.txt","w");
fputs( "{"some string",f);
fflush(f);
ftruncate(fileno(f),(off_t)0);
flcose(f);
If I run it and then check the file, it will be of zero length and when opened, there will be nothing in it.
If I remove the fflush call, it will NOT be 0 (will be 11) and when I open it there will be "some string" in it.
Is this the normal behavior?
I do not have a problem calling fflush, but I want to do this in a loop and calling fflush may increase the execution time considerably.
You should not really mix file handle and file descriptor calls like that.
What's almost certainly happening without the fflush is that the some string is waiting in file handle buffers for delivery to the file descriptor. You then truncate the file descriptor and fclose the file handle, flushing the string, hence it shows up in the file.
With the fflush, some string is sent to the file descriptor and then you truncate it. With no further flushing, the file stays truncated.
If you want to literally "truncate the file then write", then it's sufficient to:
f=fopen("/home/user1/refresh.txt","w");
fputs("some string",f);
fclose(f);
Opening the file in the mode w will truncate it (as opposed to mode a which is for appending to the end).
Also calling fclose will flush the output buffer so no data gets lost.
POSIX requires you to take specific actions (which ensure that no ugly side effects of buffering make your program go haywire) when switching between using a FILE stream and a file descriptor to access the same open file. This is described in XSH 2.5.1 Interaction of File Descriptors and Standard I/O Streams.
In your case, I believe it should suffice to just call fflush before ftruncate, like you're doing. Omitting this step, per the rules of 2.5.1, results in undefined behavior.

Can a failed fopen impact the filesystem?

If fopen( path, "w" ) succeeds, then the file will be truncated. If the fopen fails, are there an guarantees that the file is not modified?
No there are no guarantees about the state of a file if fopen(path, "w") fails. The failure could be coming from any operation from opening the file, committing the truncation to disk, etc ... The only guarantee a failure provides is that you don't have access to the file.
The only reason why fopen() would fail would be if the file is somehow inaccessible or cannot be modified. If you are worried, though, about the file being modified, you could instead use the open() command with the flag O_WRITE. You could then convert this to a FILE* pointer by using fdopen().
Excellent question, and I think the answer is no. fopen has to allocate a FILE structure, and the natural order of operations when implementing it would be to open the file first, then attempt allocating the FILE. This way, fopen is just a wrapper around fdopen (or a similar function with some leading underscores or whatnot for namespace conformance).
Personally I would not use stdio functions at all when you care about the state of your files after any failure. Even once you have the file open, stdio's buffering makes it almost impossible to know where an error occurred if a write function ever returns failure, and even more impossible to return your file to a usable, consistent state.

fwrite putting error indicator on stream in C

In what cases does does the function fwrite put an error indicator onto the stream, such that ferror will return true?
Specifically, I am wondering if it puts an error when all the bytes were not successfully written.
Please provide a link as to where you get your information from.
Thank you
If any error occurs, the error indicator for the stream will be set, and will not be cleared until clearerr is called. However, due to buffering, it's difficult for stdio functions to report errors. Often the error will not be seen until a later call, since buffering data never fails, but the subsequent write after the buffer is full might fail. If you're using stdio to write files, the only ways I know to handle write errors robustly are (choose one):
disable buffering (with setbuf or setvbuf - this must be the very first operation performed on the FILE after it's opened or otherwise it has UB)
keep track of the last successful fflush and assume any data after that might be only partially-written
treat any failed write as a completely unrecoverable file and just delete it
Also note that on a POSIX system, fwrite (and other stdio functions) are required to set errno to indicate the type of error, if an error occurs. As far as plain C is concerned, these functions may set errno, or they may not.
From the fwrite man page on my Linux system:
RETURN VALUE
fread() and fwrite() return the number of items successfully read or written
(i.e., not the number of characters). If an error occurs, or the end-of-file
is reached, the return value is a short item count (or zero).
fread() does not distinguish between end-of-file and error, and callers
must use feof(3) and ferror(3) to determine which occurred.
Just from reading the man page, it doesn't look like it will set errno.
On Windows fwrite may put error when try to write to read-opened stream.
For example if there was a call to seekg which sets read flag to the stream and seekp was not called before writing.

Resources