From: leichter@lrw.com (Jerry Leichter)
Newsgroups: comp.os.vms
Subject: re: Flushing file buffers to disk in VAX C
Date: 23 Jan 93 17:15:07 GMT
Distribution: world
Organization: The Internet


	The problem:
	------------
	I am running VMS 5.5-1 on a VAXstation 4000 model 60. I find that the
	C library call fsync() does not write the file buffers to disk as it
	is supposed to. Could someone else run the program below on  this or
	earlier versions of VMS and check it? Run the program on  one
	terminal, and when it stops to wait for input, kill the process using
	the STOP command from another terminal. If I understand the
	functionality of fsync() correctly, the buffers should have been
	flushed to disk - but when you look at the file after stopping the
	process, it is still empty. Is this a bug? 

Well, no.  Since fsync() is neither documented nor supported, it can't
possibly have any bugs; if its effect were to delete every other file in
your directory, that might be rather undesireable behavior, but it wouldn't
really be a BUG.  :-)

	Some more details:
	-----------------

	I am using VAX C version 3.2 - earlier versions of the RTL may not
	support the fsync() call. It is not documented in the RTL reference
	manual even in the current version,

As you see....  Could it be that perhaps you've found a REASON why it isn't 
documented or supported?

					    but it is available in the
	library. There is no other way to ensure that C file data has been
	written to disk and the disk version is up-to-date (short of closing
	the file).

	I do not think the bug is in the fsync() routine itself; you can
	verify  that the routine calls the RMS routine sys$flush() internally,
	as you might expect. (You can check this out by stepping through the
	assembly instructions with the debugger). I have tried calling
	sys$flush() directly, and it returns without error - still without
	writing the buffers to the file. So is the error in RMS?

No.  There's no error; you're just seeing an unexpected side effect of the
implementation.

In Unix, write() is a system call, and the standard I/O library is layered on
top of it (and a few other system calls).  VMS has not write() call; like all
the other Unix calls, it is emulated by the VAX C RTL.  However, for various
reasons the emulation does NOT follow the design used in Unix:  The standard
I/O library is NOT implemented on top of the emulated write().  Instead, some
common code ultimately underlies both.

When you do a write() with the VAX C RTL, the data goes into a buffer managed
by the RTL.  Later, the RTL will pass the data off to RMS - but not right
away.  As a result, when you do a SYS$FLUSH, either through fsync() or by
a direct call, RMS happily confirms that it has written out all the 0 bytes
it has been handed so far.

Even on Unix, if you want fsync() to work for a file opened through the stan-
dard I/O library, you have to use fflush() first to get the data out of the
stdio buffers and into the Unix system buffers, and only then do an fsync() to
force the data to the disk.  With the current VAX C RTL implementation, you
have to do the same thing - but you also have to do it for data written with
a write() call.

Unfortunately, however, there does not exist a call to flush a file described
by number, as opposed to one described by a FILE * - such a call would make no
sense on Unix.  There is a work-around, though:  By using fdopen(), you can
create a FILE * that points to a file opened using the "lower level" Unix I/O
functions, then use fflush() on that.  The resulting code - I've included a
modified version of the posted source below - works as you wanted.  (It should
also work on Unix, where the fflush() will always be a no-op.)

	Another puzzle which I wonder if someone could explain to me - the C
	write() routine does not call RMS sys$write() at all. It calls
	sys$read() instead! Is there something wrong with my debugger symbols
	or is this the right thing for write() to do? It works, i.e.  the file
	does get written to normally. Does this have something to do with the
	above problem?

No.  I suspect you are misinterpreting some Debugger display, but without all
the details I can't be sure.  (It's probably not worth it to try and resolve
this.)
							-- Jerry

---------------- Modified source code begins here ---------------------------
#include <stdio.h>
#include <file.h>

main()
{
	int fd;
	FILE *fdf;			/* Dummy FILE * for fd	*/
	char buf[] = "This is a test record\n";
	char testFileName[] = "chks.out";

	if ( (fd = open(testFileName, O_RDWR | O_CREAT)) < 0) {
		fprintf(stderr, "Error opening file. Exiting... \n");
		exit(0);
	}

	/*
	 * Associate a FILE * with fd.
	 */
	if ((fdf = fdopen(fd,"r+")) == NULL) {
		fprintf(stderr, "Error making FILE pointer. Exiting... \n");
		exit(0);
	}

	fprintf(stderr, "The file being used is %s\n\n", testFileName);
	fprintf(stderr, "%s%s%s%s%s",
	"A record has been written to the file and sync'ed. If the fsync()\n",
	"call works correctly the data should be in the file even if the\n",
	"process is stopped. You can now halt the process from another\n",
	"terminal or window using the STOP command, or hit any key to let\n",
	"it run to completion...\n"
		);
	fflush(stderr);

	write(fd, buf, sizeof(buf));
	fflush(fdf);			/* Get the record to RMS	*/
	fsync(fd);			/* Get it to the disk		*/

	/**
		You can now stop the process from another terminal or
		window while it waits, or let it complete.
	**/
	getchar();

	close(fd);
	fprintf(stderr, "Wrote the record and closed the file\n");
}

Newsgroups: comp.os.vms