Skip to content

Conversation

@Acook1e
Copy link

@Acook1e Acook1e commented Dec 9, 2025

Actually, this pull request shouldn't be merged now, I just make it a feasible way.
There are many things should be done at mo2 not usvfs:

  • Pass the overwrite_dir parameter from mo2
  • Pass the mods_dir parameter from mo2
  • Pass the name of current_process parameter from mo2
    This Pull request give usvfs an ability to not modify mod source file, possible write will be routed to overwrite_dir

Now this is a true CoW implement.
Truncated operations will redict at NtCreate
Write Access will be redirect at NtWrite

Acook1e and others added 2 commits December 9, 2025 23:05
- Implemented detection of destructive file operations (FILE_OVERWRITE, FILE_SUPERSEDE) in NtCreateFile hook.
- Added checks for sensitive file extensions (.ini, .json, .txt, etc.) to target configuration files.
- Implemented 'Safety Copy' mechanism:
  - When a destructive write is detected on a file in 'mods_dir' (and not in 'exclude_mods'), the file is automatically copied to 'overwrite_dir'.
  - The copy preserves the relative path from the mod root (e.g., 'mods/ModA/SKSE/plugins/foo.ini' -> 'overwrite/SKSE/plugins/foo.ini').
- Updated VFS redirection table immediately after the copy to ensure the file handle and subsequent operations point to the new copy in 'overwrite_dir'.
- Enhanced 'LoadSettings' to support dynamic 'overwrite_dir' configuration:
  - Added 'output_directories' map to 'usvfs::settings'.
  - Parsed '[Output]' section from 'usvfs_redirect.ini'.
  - If 'current_process' matches an entry in 'output_directories', 'overwrite_dir' is updated to the specified relative path within 'mods_dir'.
- Added comprehensive logging for destructive operation detection, file copying, and VFS rerouting.
@AnyOldName3
Copy link
Member

Is this just doing it for the few file extensions you listed? The reason we've had for not doing the simple thing where we copy files in NtCreateFile when they're opened with write access is that loads of things open files with write access when all they're doing is reading them, so you'd rapidly end up with unmodified copies of everything in overwrite. I guess that might stop being a problem if people start playing games off ReFS instead of NTFS, as then we could create a CoW copy and delete it if the handle's closed and it's identical to the original, but that's not happening in the immediate future.

@Acook1e
Copy link
Author

Acook1e commented Dec 10, 2025

Yep, I have some troubles when developing, I don't know when writing happens. So, I decided just CoW a few extensions.
This is the first time I heard ReFS, I searched it and It's for enterprise and workstation, can we really use it in the future?

The commit is the second time I develop this feature.
The first time I used WriteFile, CloseHandle, SetEndOfFile, SetFilePointerEx, hooks at kernal32, I can capture write but at that time, source file at mods has been 0kb.
If I hook NtWriteFile and using a handle map, can we develop the true CoW?

@AnyOldName3
Copy link
Member

ReFS is vaguely positioned to maybe replace NTFS as the default filesystem for Windows, but it's not happening quickly. In 2012, it might have seemed like a safe bet that Windows installs would use ReFS by default in 2025, but 2025 has arrived and that's not the case yet, and Microsoft seem to be pivoting away from making Windows better, so it might never happen.

As for specifics of giving USVFS full CoW support, I'm probably not the best person to ask as I've never dealt with its guts, and just know high-level stuff. I did make a CoW-capable USVFS clone for a university project (actually, technically it was a hook.dll - the thing MO1 used - clone because USVFS didn't work properly back then), but, partially due to design decisions, it was too slow to use.

Something I do know, though, is that one of USVFS' strong points is its speed - because the name lookup is done based on stuff in RAM, it doesn't need to touch the disk until a handle is actually opened, and then the application is given a normal handle that works like any other, so USVFS doesn't need to get involved with anything else, so it ends up about the same speed as the native filesystem. If we start hooking all the functions that can actually modify files, there's a risk of undermining that and making things slower, plus there are a lot of unhooked functions we'd have to hook, so the maintenance burden and risk of mistakes would increase significantly.

To get CoW support, it might be a better idea to make a fresh filesystem using something easier to work with like Dokany, and then add a toggle to MO2 to pick whether to use the fast, non-CoW one (e.g. for playing games) or the slower CoW one (e.g. for using modding tools). Supposedly, when switching from hook.dll to USVFS, Tannin made that kind of thing easier, but I don't think anyone's taken a serious look at it in the last decade or so.

@Acook1e
Copy link
Author

Acook1e commented Dec 10, 2025

After a few times of developing, I'm very sure write operations will be captured by NtWriteFile.
And I also hook NtReadFile to make read rerouted file consistent.
If I use release build, usvfs with Cow is as fast as before.
The only things I need to do now is set the right file pointer for write and read, and other things have been tested on mo2.

@Acook1e
Copy link
Author

Acook1e commented Dec 11, 2025

Finally, implements of CoW correct.
Any truncated operations in mods_dir will be rerouted to overwrite_dir
Any write operations will be captured and set right file pointer and CoW to overwrite_dir, just like the test.txt

logs:
image
origin file:
image
CoW file:
image

@Silarn
Copy link
Member

Silarn commented Dec 12, 2025

So this is specifically checking for actual file writes before copying the file?

If it's still restricted to specific file extensions I think it could be reasonable, but copying something large like a BSA file still seems problematic from a performance perspective.

Do you have any specific test cases? It might be necessary to allow passing in allowed extensions for cow.

@Acook1e
Copy link
Author

Acook1e commented Dec 13, 2025

This CoW has three features, truncated operations will CoW immediately, write operations happens will CoW and upadte vfs mapping, delete file will only remove mappings (I think I should improve it, but how?)
All CoW only work in mods_dir and overwrite_dir.
And it's simple to check file extensions before cow.
What we need is pass the excluded extensions from settings file (this file is current for test, if mo2 update and adjust params I will update).
And if you still have performance concerns, we can add a max file size restrict.

Test cases I didn't create yet, for there is still a bug I don't know how to solve.
Any commits of usvfs, include mine and latest usvfs release 0.5.7.2, if I build it on my machine, for specific programs, such as DIP.exe (a tool of skyrim modding), will pass short path (Windows 8.3 format, for example: PROGRA1, STEAMA1).
If I use mo2 origin usvfs.dll, short path will not exist.

@Holt59
Copy link
Member

Holt59 commented Dec 14, 2025

Regardless of the actual features that this PR is trying to implement, it contains many unrelated changes (e.g., all the rewrite in the workflow file), so these should be moved to another PR if relevant.

@Acook1e
Copy link
Author

Acook1e commented Dec 15, 2025

Yes, you are right, I should distribute these features into different PR, and I need fork mo2 and adjust usvfs features.
I will close or change my master branch.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants