Why does an open DLL Handle not protect file from being moved? - windows

I just had a surprising bug where a DLL file that was loaded using the LoadLibrary API call was renamed while being loaded. Apparently, having an open DLL handle on a file does not prevent that file from being renamed, or even moved to a different path. It is however protected from deletion and being moved to a different disk. The program using the DLL continues to work fine if this happens. ProcessExplorer shows that the path of the DLL handle updates accordingly.
This behavior is different from ordinary file handles in Windows. For example, when keeping an open std::ifstream to the same DLL, renaming is no longer allowed by the operating system. I find this behavior quite surprising and was wondering if anyone could give an explanation for it? In particular I'd be interested in the rationale for allowing this, as I'd imagine the tracking of the file on disk to be more difficult than just locking it in place. So the OS probably has to actively support this feature, which means there has to be a use case for it?

It is not a bug. LoadLibrary uses File Mapping to access a file. While you have a mapped section to a file it cannot be deleted (or moved to another disk). It seems that LoadLibrary closes a file handle (it's not needed) and uses only a handle to the mapped section so you can freely rename the file but cannot delete it.
On the other hand std::ifstream uses a file handle to access a file. And it doesn't set FILE_SHARE_DELETE share access that is required for rename and delete operations.
Actually there is no special tracking of a file on the disk. A file handle points to the file and that's all. After you have opened a file and got its handle the file can be renamed or even deleted and you still have an access to that file (a limited access if the file has been deleted, but you can undelete the file and have a full access).

Related

Write to file in use by another program

I'm creating a program (doesn't really matter the objective but it happens to be purely to mess around and learn more about windows) which reads and writes to a file which is in use by another program (for example notepad or word).
Obviously I'm having trouble deleting it as I'm getting an access denied error because the file is in use.
My first idea was I should use CloseHandle (kernel32.dll) to close the handle to that file, but I have no clue how to find that handle in the first place.
Any ideas? I'm doing this in Rust, so if there are any language-specific suggestions that would be best but if not, that's more than fine too.
On another note, what would happen to the program after the handle has been closed? Would word or notepad still be able to edit it or would a subsequent save delete the changes made by my program or perhaps it wouldn't even save?
This behaviour you observe is not related to Rust or to any other programming language, since this is system-specific.
The CreateFileA() win32 call offers, thanks to its third parameter (dwShareMode), a means to explicitly specify how sharing could happen with the open file.
Unfortunately (for you) this call is performed beforehand by the other program you try to hijack, not yours; your program cannot do anything, it's too late once the file is open.
Not that on UNIX the situation is different because the path to the file in the file-system is just a reference to the content of this file, as an open() operation is.
Thus, if you remove (rm) the file indicated by this path, you just remove the reference (unlink()) but not its actual content if it is still referenced by an open file descriptor.
The actual deletion of the file content only happens when no reference to it exists anymore.

How Windows differentiates between Copied files and Created files

I am looking for a bit of advice on how Windows file system differentiates between files that are copied(copy and pasted from another location) and files that are created (a new file created in a a folder).
A bit of background to this so it makes more sense: I have an application that is used to move files. The application will monitor a directory and when a file is placed in the directory it will move it elsewhere. However, I am having issues where the application will not pick up a file that is created within the monitored directory but will pick up files that have been created else where and are copied into the monitored directory.
Any advice on how Windows differentiates, or if it does at all, would be greatly appreciated.
This is running on Microsoft Windows Server 2008 R2 Standard. I can't dig into the code and see what is going on under the hood unfortunately, so need to get an idea of the difference if any there would be.
The filesystems don't know the operation of "copying" the file. Any copying is a sequence of file open/read/write/close operations. The same applies to moving to the different filesystem. Moving within the same filesystem, though, is an operation native to the filesystems and it can be done with one command to the filesystem.
Now about your problem. Most likely you catch the creation of the file (before the data is written), and when your application reacts, the file is still opened for writing. So you need to wait until the file is closed.
Depending on how you do monitoring, such waiting is done in different ways. In filesystem filters you wait for file close operation. With .NET FileSystemWatcher there's no way to track file close operation, but I saw a couple of tricks here on StackOverflow (don't have a link though, sorry).
A file existing in D: drive, from creation
The same file which was copied to E: drive
As you can see, the file which was copied to E: drive, has a creation time as the latest, when it was copied to and the modification time as the last modification time for that file in previous location.
So I guess this illustrates, how windows differentiates between copied files and created files.

Deleting a directory under Windows in a race-free manner?

http://code.google.com/p/guava-libraries/issues/detail?id=365 discusses the potential race-conditions that may occur while deleting a directory recursively.
According to http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7148952 this can be implemented in a race-free manner under Linux using openat(). Is there an equivalent mechanism under Windows?
A key difference between Windows filesystem behavior and linux filesystem behavior is locking and reference counting.
In Windows if a process has a file open, then that file and the path leading to that file are protected.
So, if somebody has "C:\a\b\c\d\file.txt" open, then nobody is allowed to rename or delete any part of the path "C:\a\b\c\d\file.txt".
The linux model is much different, any part of that path can be changed and even the file can be deleted. The process holding the handle to "file.txt" still has a reference, and the file wouldn't be removed from the filesystem until all handles are closed.
The Win32 API doesn't expose a direct way to hold a handle to a directory (though, there are APIs for this - see the "Zw" functions, FindFirstFile may I'm not sure, the backup APIs, etc) - but your process "current directory" does hold a handle for that directory.
Thus, you can get "openat" behavior by changing your working directory and then opening the file directly. Better would be to use something like ZwCreateFile() to open a handle to the directory - since the "current dir" is process global.
Search Stackoverflow and Microsoft.com for ZwCreateFile information.

Invisible hardlink

I have a small application that displays the contents of a log file, somewhat transmogrified for readability. As the log file gets rewritten occasionally and Windows file system semantics prohibit deletion of open files, I create a hardlink to the file.
Obviously, this needs to happen on the same file system as the original file -- at present, I create the harddisk in the same directory, which I believe can be reasonably assumed to fulfill this requirement; the result is that a temporary file shows up in the directory listing where the user just clicked to open the file, which is ugly.
Is there a way to create a hardlink so that it does not show up (the customer where the program is used has several junctions in their directory tree, so it cannot be assumed that a specific directory is on the same filesystem), or is there a better method to read a file that another process may want to delete and rewrite (e.g. by catching their access and closing the file before letting the other process's access go through), so the program can be used on archived (readonly) log files without modification?
No
It won't help if you could. Sharing spans links.
Use the solution posed by Hans Passant as a comment.

Renaming A Running Process' File Image On Windows

I have a Windows service application on Vista SP1 and I've found that users are renaming its executable file (while it's running) and then rebooting, thus causing it to fail to start on next bootup because the service manager can no longer find the exe file since it's been renamed.
I seem to recall that with older versions of Windows you couldn't do this because the OS placed a lock on the file. Even with Vista SP1 I still cannot copy over the existing file when it's running - Windows reports that the file is in use - makes sense. So why should I be allowed to rename it? What happens if Windows needs to page in a new code page from the exe but the file has been renamed since it was started? I ran Process Monitor while renaming the exe file, etc, but Process Mon didn't report anything strange and just logged changing the filename like any other file.
Does anyone know what's going on here behind the scenes? It's seem counter intuitive that Windows would allow a running process' filename (or its dependent DLLs) to be changed. What am I missing here?
your concept is wrong ... the filename is not the center of the file-io universe ... the handle to the open file is. the file is not moved to a different section of disk when you rename it, it's still in the same place and the part of the disk the internal data structure for the open file is still pointing to the same place. bottom line is that your observations are correct. you can rename a running program without causing problems. you can create a new file with the same name as the running program once you've renamed it. this is actually useful behavior if you want to update software while the software is running.
As long as the file is still there, Windows can still read from it - it's the underlying file that matters, not its name.
I can happily rename running executables on my XP machine.
The OS keeps an open handle to the .exe file,. Renaming the file simply changes some filesystem metadata about the file, without invalidating open handles. So when the OS goes to page in more code, it just uses the file handle it already has open.
Replacing the file (writing over its contents) is another matter entirely, and I'm guessing the OS opens with the FILE_SHARE_WRITE flag unset, so no other processes can write to the .exe file.
Might be a stupid question but, why do users have access to rename the file if they are not suppose to rename the file? But yeah, it's allowed because, as the good answers point out, the open handle to the file isn't lost until the application exits. And there are some uses for it as well, even though I'm not convinced updating an application by renaming its file is a good practice.
You might consider having your service listen to changes to the directory that your service is installed in. If it detects a rename, then it could rename itself back to what it's supposed to be.
There are two aspects to the notion of file here:
The data on the disk - that's the actual file.
The file-name (could be several or none) which you can give that data - called directory entries.
What you are renaming is the directory entry, which still references the same data. Windows doesn't care about your doing so, as it still can access the data when it needs to. The running process is mapped to the data, not the name.

Resources