[Ecls-list] open :supersede and rename-file advise
Geo Carncross
geocar at gmail.com
Mon Nov 26 13:12:27 UTC 2007
Hello again,
Under unix, robust applications tend to write to new files in the following way:
fd = open(tempfile, O_EXCL|O_CREAT, 0600);
... write to fd ...
if (fsync(fd) == -1) goto fail;
if (close(fd) == -1) goto fail; /* NFS can fail here */
if (rename(tempfile, realfile) == -1) goto fail;
This has the benefit of having no points where "realfile" has
incomplete or invalid data.
If I want to generate an error if "realname" exists with still having
that benefit, I do the rename-step as:
if (link(tempfile, realfile) == -1) goto fail;
(void)unlink(tempfile);
if (stat(realfile,&sb) == -1) goto fail;
if (sb.st_size != expected_size) goto fail;
Under sbcl I can use sb-posix:link, rename, and unlink to simulate the
last few steps, and under openmcl rename-file is implemented as a
link/unlink pair (and thus generates an error) while ccl::unix-rename
can be used to get a rename that clobbers the existing file.
Under clisp and ecl I presently use ffi, but in checking clhs it seems
that (open :supersede) is allowed to have these semantics. If so,
would you accept a patch that implements :supersede as such?
:overwrite would have the current semantics, but :supersede would be
crashproof.
I should also point out that cl:rename-file triggers an error on sbcl,
clisp, and openmcl whenever the target file already exists. ecl
differs in this regard.
FWIW, on Windows, rename/MoveFile works like unix's link/unlink
combination and won't clobber existing files. It's possible to use
MoveFile(,,MOVEFILE_REPLACE_EXISTING) to get unix-like renames but it
doesn't work with Win95. Novell on Win95 worked by using
SetFileAttributesA(newf,FILE_ATTRIBUTE_NORMAL); followed by
SetFileAttributesA(newf,FILE_ATTRIBUTE_TEMPORARY); - where the whole
set of contortions work like this:
em = SetErrorMode(0);
if (!MoveFile(old,newf)) {
switch (GetLastError()) {
case ERROR_ALREADY_EXISTS:
case ERROR_FILE_EXISTS:
break;
default:
SetErrorMode(em);
goto fail;
};
if (MoveFileEx(old,newf,MOVEFILE_REPLACE_EXISTING)) {
SetErrorMode(em);
goto success;
}
/* this sometimes work with win95 and novell shares */
chmod(newf,0777);
chmod(old,0777);
SetFileAttributesA(newf,FILE_ATTRIBUTE_NORMAL);
SetFileAttributesA(newf,FILE_ATTRIBUTE_TEMPORARY);
if (MoveFile(old, newf)) {
SetErrorMode(em);
goto success;
}
if (!DeleteFileA(newf)) {
SetErrorMode(em);
goto fail;
}
if (MoveFile(old, newf)) {
SetErrorMode(em);
goto success;
}
/* alright, thems the breaks. win95 eats your files,
and not a damn thing I can do about it.
*/
SetErrorMode(em);
goto fail;
}
More information about the ecl-devel
mailing list