1

I am looking for how to do a simple transactional POSIX-style rename() in .net:

If the link named by the new argument exists, it shall be removed and old renamed to new. In this case, a link named new shall remain visible to other processes throughout the renaming operation and refer either to the file referred to by new or old before the operation began.

I shouldn’t have to delete the destination file before renaming the new file which is to replace it. Deleting and then renaming means that the new name could return “file not found” for a nonzero amount of time, which is even worse if the process or machine crashes at that point.

The point is to implement the simple transactional file update pattern where I only want to replace the file if I can successfully write a replacement file to disk first. I’m really hoping that support for such a basic filesystem access operation does not require pinvoke or anything fancy. I should be able to:

  1. Write a new file.
  2. Close its FileStream which flushes it.
  3. Rename that file over the old file (with the assumption that the FS will, with help of its journal, guarantee that this step is not persisted until the file is actually flushed to hardware and that this rename operation itself is atomic).

Where is this rename functionality hiding in .net’s BCL? Is it not there? If it’s not there, is it because .net was written when Windows 9x was still a supported environment? Or does modern Windows still not support such operations for unprivileged processes?

binki
  • 7,754
  • 5
  • 64
  • 110

1 Answers1

0

Apparently this is just an issue of naming. I didn’t know to look for a method called File.Replace() until I had written this question and came across this answer to another post. To atomically move a file at oldPath over another existing file at newPath:

File.Replace(oldPath, newPath, null);

The documentation for File.Replace() has these interesting tidbits in it:

PlatformNotSupportedException | The operating system is Windows 98 Second Edition or earlier and the files system is not NTFS.

and

If the sourceFileName and destinationFileName are on different volumes, this method will raise an exception.

The note about not supporting unjournaled filesystems or Windows 98 and that it won’t work across volumes means that this method will not silently call File.Copy() followed by File.Delete()—it won’t silently translate an operation you intended to be atomic into an unsafe one.

However, unlike rename(), this method fails if the destination file does not already exist. However, you can use the fact that File.Move() refuses to overwrite an existing file and the fact that File.Replace() can transactionally rename something over an existing file to perform a rename operation which, while it may fail sometimes, will at least not leave files in an invalid state. If keeping files in a consistent state is more important than keeping your program from crashing, you can use:

static void ExceptionalRename(string oldPath, string newPath)
{
    if (File.Exists(newPath))
        File.Replace(oldPath, newPath, null);
    else
        File.Move(oldPath, newPath);
}

I wish I knew of something in .net that behaves more like rename() out of the box, though. At least File.Replace() provides the transactional renaming over an existing file which I thought was missing altogether from .net.

Community
  • 1
  • 1
binki
  • 7,754
  • 5
  • 64
  • 110