Operating System - OpenVMS
1748180 Members
4034 Online
108759 Solutions
New Discussion

C RTL: fwrite following fsync fails; what's going on?

 
SOLVED
Go to solution
chadjoan
Occasional Contributor

C RTL: fwrite following fsync fails; what's going on?

I am running the following C code on an OpenVMS v8.4 I64 machine:

 

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#ifdef __VMS
#define myfopen(fname, amode, ...) fopen(fname,amode,__VA_ARGS__)
#else
#define myfopen(fname, amode, ...) fopen(fname,amode)
#endif

void peep(FILE *otherStream)
{
	char buf[1024];
	
	fsync(fileno(otherStream));
	
	/*FILE *peeper = myfopen("hello.txt", "r", "shr=get,put,upd");
	fread( buf, 1, 8, peeper );
	fclose(peeper);
	printf("peeped: '%s'\n", buf);
	*/
}

int main( int argc, char *argv[] )
{
	const char *text = "I am definitely not angry right now. No, not me. Why would I be angry? Pish posh.";
	FILE *this_is_a_file = fopen("hello.txt","w");
	fclose(this_is_a_file);
	
	this_is_a_file = myfopen("hello.txt","r+", "shr=get,put,upd");
	peep(this_is_a_file);
	fwrite( text, 1, sizeof(text), this_is_a_file );
	if ( ferror(this_is_a_file) )
	{
		printf("Generic exclamation of surprise with subdued jest.\n");
		perror(NULL);
		exit(1);
	}
	peep(this_is_a_file);
	fclose( this_is_a_file );
}

 

The output looks like this:

Generic exclamation of surprise with subdued jest.
non-translatable vms error code: 0x1827A
%rms-e-eof, end of file detected

 

This works fine on Linux.  What am I doing wrong?

 

Background:

I'm trying to write unittests (and other code) that should be easily portable between VMS and Linux, so using direct RMS calls is highly undesirable.  I'd like to get it working using the standard C file I/O routines.

7 REPLIES 7
Steven Schweda
Honored Contributor

Re: C RTL: fwrite following fsync fails; what's going on?

 
Steven Schweda
Honored Contributor

Re: C RTL: fwrite following fsync fails; what's going on?

 
chadjoan
Occasional Contributor

Re: C RTL: fwrite following fsync fails; what's going on?

Yeah, checking the return value of fwrite allows me to do what I want.  Thanks!

 

I realized that I did my test case reduction poorly and removed a bunch of checks that were otherwise already in the original code.  fwrite was an exception, but the original code did check the return value of fsync.  In the original code I expected any failure in fsync to halt the program and prevent fwrite from being called at all.

 

I've found some interesting behavior because of this:

fsync will complete and return a success code (returns 0), yet set ferror to 1 (the end of file reached error).

 

This behavior is inconsistant with Linux, and I can't find any documentation that details how fsync is supposed to interact with ferror on either system.  It just isn't mentioned.  I believe this omission in the documents lead me to believe that checking fsync's return value was sufficient to ensure that no error flags would be set.

 

Docs checked:

http://h71000.www7.hp.com/commercial/c/docs/5763p029.html

http://linux.die.net/man/2/fsync

 

So fsync seems to be working: the new data appears in the file.  However, it is also setting ferror.  Does this have any implications on the integrity of the sync'd file handle? 

 

Here's a new test case:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

#ifdef __VMS
#define myfopen(fname, amode, ...) fopen(fname,amode,__VA_ARGS__)
#else
#define myfopen(fname, amode, ...) fopen(fname,amode)
#endif

void peep(FILE *otherStream)
{
	char buf[1024];
	
	printf("ferror before fsync == %d\n",ferror(otherStream));
	int errval = fsync(fileno(otherStream));
	printf("ferror after fsync == %d\n",ferror(otherStream));
	if ( errval != 0 || ferror(otherStream) )
	{
		printf("fsync failed.\n");
		perror(NULL);
		exit(1);
	}
	
	/*FILE *peeper = myfopen("hello.txt", "r", "shr=get,put,upd");
	fread( buf, 1, 8, peeper );
	fclose(peeper);
	printf("peeped: '%s'\n", buf);
	*/
}

int main( int argc, char *argv[] )
{
	const char *text = "I am definitely not angry right now. No, not me. Why would I be angry? Pish posh.";
	FILE *this_is_a_file = fopen("hello.txt","w");
	fclose(this_is_a_file);
	
	this_is_a_file = myfopen("hello.txt","r+", "shr=get,put,upd");
	peep(this_is_a_file);
	size_t nbytes = fwrite( text, 1, strlen(text), this_is_a_file );
	printf("strlen == %ld\n", strlen(text));
	printf("nbytes == %ld\n", nbytes);
	if ( ferror(this_is_a_file) )
	{
		printf("Generic exclamation of surprise with subdued jest.\n");
		perror(NULL);
		exit(1);
	}
	peep(this_is_a_file);
	fclose( this_is_a_file );
}

 

Results:

ferror before fsync == 0
ferror after fsync == 1
fsync failed.
non-translatable vms error code: 0x1827A
%rms-e-eof, end of file detected

 

As for the stream vs record format thing, I think I will use "ctx=stm" in my fopen options for now and hope for the best.  I'll keep that in mind for fwrite should I need to worry about the distinction.  Thanks for mentioning that.  Is there a way to determine at runtime which mode a FILE* handle is operating in?

Steven Schweda
Honored Contributor
Solution

Re: C RTL: fwrite following fsync fails; what's going on?

 
chadjoan
Occasional Contributor

Re: C RTL: fwrite following fsync fails; what's going on?

Understood.  Thank you for the info!

Steven Schweda
Honored Contributor

Re: C RTL: fwrite following fsync fails; what's going on?

 
Dennis Handly
Acclaimed Contributor

Re: C RTL: fwrite following fsync fails; what's going on?

>> fwrite(text, 1, sizeof(text), this_is_a_file );

 

>A pointer to char is almost the same as an array of char, but not quite

 

Right.  You can also fix it by changing the declaration to an array (also saves the wasted pointer):

static const char text[] = "I am definitely not angry right now. No, not me. Why would I be angry? Pish posh.";

 

You would also have to change the fwrite length:

items = fwrite(text, sizeof(text)-1, 1, this_is_a_file );