Do CopyFile and CopyFileEx work if the source file is open? - winapi

I want to call a copyFile function from within the source file to be copied. The VBA FileCopy function does not allow the source file to be open while it is copied. I am thinking of using CopyFile or CopyFileEx (to show a progress bar) instead.
Therefore, do those functions work if the source file is open?

Yes, CopyFileEx() has the COPY_FILE_OPEN_SOURCE_FOR_WRITE option, allowing you to copy a file that was opened for writing. You'd still need cooperation from the owner of the file, it would have had to open the file allowing read sharing.
Beware of the trouble you can get into with this option, you'll essentially get a random snapshot of the file, you could copy the file while the app was in the middle of writing to the file. Such a copy isn't usually readable anymore. You'd be lucky if the app trying to read such a copy crashes and dies. But the more likely outcome is subtly corrupt data.

Yes, Win32 CopyFile and CopyFileEx (and CopyFile2) all will open a file that is already open by someone else, as long as the other party has specified FILE_SHARE_READ.
Specifically, my latest tries (on Windows 10 1809) show that these function will try to open the file
1st, with GENERIC_READ + FILE_SHARE_READ - thus blocking any later writers.
2nd, transparently(!), fall back to GENERIC_READ + FILE_SHARE_READ & FILE_SHARE_WRITE if someone else has the file already open for write access, thereby even copying a file that is written by someone else.

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.

Deleting a locked file created with TempFile

I have a GUI (lxn/walk) app patcher that downloads a file via ftp, streams it to a temporary file and extracts the contents to update the local files. The remove file command is deferred.
This works unless the user exits the program while the file is downloading, then the file isn’t deleted.
I tried to fix this by doing a graceful exit by catching the signal and removing the file there. But unfortunately it throws an error that the file can’t be deleted because it is being used by another program. Which makes sense because the another program is actually itself still writing to the temporary file.
Now I’m stuck and don’t know what to do to make sure that the temporary file is automatically gone once the patcher is not running. How do I do that correctly?
The file could also be created as a normal file, not just a temp file. I would just like to ask too, where in windows is best to write a temporary file?
Now I’m stuck and don’t know what to do to make sure that the temporary file is automatically gone once the patcher is not running. How do I do that correctly?
There are no guaranteed ways to accomplish this as many things beyond the control of the application can cause it to exit. A power failure or kernel panic due to some hardware issue can crash the machine or force it to be restarted.
A strategy that is in common use is to implement a check on program startup for the status of the previous run. Some applications create a lock file at start and remove it on graceful exit. If this lock file exists when the program is restarted, this means the previous run did not result in a clean exit, and the application can take any corrective action. The exact action to be taken depends on the nature of the application, some refuse to start, others give warnings to users.
I would just like to ask too, where in windows is best to write a temporary file?
Each OS has its own location for temporary files. If you eliminate the dir argument to TempFile, it will create it in the appropriate location, as mentioned in the documentation:
TempFile creates a new temporary file in the directory dir, opens the
file for reading and writing, and returns the resulting *os.File. The
filename is generated by taking pattern and adding a random string to
the end. If pattern includes a "*", the random string replaces the
last "*". If dir is the empty string, TempFile uses the default
directory for temporary files (see os.TempDir). Multiple programs
calling TempFile simultaneously will not choose the same file. The
caller can use f.Name() to find the pathname of the file. It is the
caller's responsibility to remove the file when no longer needed.
From os.TempDir we see the following:
On Unix systems, it returns $TMPDIR if non-empty, else /tmp. On
Windows, it uses GetTempPath, returning the first non-empty value
from %TMP%, %TEMP%, %USERPROFILE%, or the Windows directory. On
Plan 9, it returns /tmp.
The directory is neither guaranteed to exist nor have accessible
permissions.

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

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).

Does CopyFile place any lock on the source file?

When I copy a file with CopyFile, will the source file be locked in any way?
Say - if the copy operation starts, and then someone wants to open the source file, will it succeed? What if the "someone" requests exclusive access to the file, will his Win32 API call (probably a CreateFile) fail, or will it wait (or whatever) until the copy operation is done?
If somebody requests exclusive access to a file that's already opened, that request will fail. Windows does not have a file open mode that consists of, "If somebody else is using the file, bump him off."
As I recall, CopyFile opens the source file so that others can read, but not write it.

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