[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