libzip
libzip copied to clipboard
zip_open fails with symbolic link on Windows
Describe the Bug On Windows, zip_open fails when a path to a symbolic link is given, which points to a zip file.
Expected Behavior zip_open opens the .zip file referenced by the symbolic link
Observed Behavior zip_open fails with error code 28 (ZIP_ER_OPNOTSUPP).
To Reproduce
int error = 0;
const char* path_to_symbolic_link = ...;
zip_open(path_to_symbolic_link, ZIP_RDONLY, &error);
To create a symbolic link on Windows, 1) open command prompt with Admin privilege, 2) run "mklink <name> <target>"
libzip Version v1.7.0 and newer
Operating System Windows
Test Files Any zip file.
Additional context Not sure if this is supposed to work or not, but I see it worked up until version 1.6.1. On version 1.7.3, the returned error code is 19 (ZIP_ER_NOZIP) instead.
We have no Windows expertise. I wasn't even aware that Windows now supports symlinks. I thought this was implemented as .LNK file (which is a normal file which references the other file in some way.) One possible problem might be that the link is opened as a zip archive instead of the file referenced by the link. You'll have to debug this yourself, sorry.
@0-wiz-0 This commit looks suspicious https://github.com/nih-at/libzip/commit/e049c609 (FILE_ATTRIBUTE_REPARSE_POINT ) should we check with Khaled Mardam-Bey since this appears to be his suggestion. Maybe the windows expert we are looking for?
No, I don't think he is.
But that's a good idea - removing | FILE_ATTRIBUTE_REPARSE_POINT there might improve the situation for symlinks.
@ktakahashimtb Can you please give this a try?
@0-wiz-0 @arsnyder16 Thanks for the feedback!
~~I removed FILE_ATTRIBUTE_REPARSE_POINT on my local build and that appears to fix the issue!~~
I removed FILE_ATTRIBUTE_REPARSE_POINT, and it still fails at a different point. Now zip_open returns error code 19 instead of 28. This looks to be because ::GetFileAttributesEx returns 0 for the file size so st->size is set to 0 several lines below.
I also found that one way to workaround this issue is to call zip_source_filep_create followed by zip_open_from_source instead of zip_open. This way we're passing FILE* instead of filepath, and does not call into a function which checks on FILE_ATTRIBUTE_REPASE_POINT. If anyone else is hitting this problem, this may be an option.
In your testing, please also try changing the file and report back if the symlinks is overwritten with a file, or the symlink target is replaced instead.
@0-wiz-0 Would you like to know how updating the file via symlink works on Windows in general or how libzip updates the file?
If it's the former, I tried notepad and ms word, and the target of the symlink is updated not the symlink file itself.
If the latter, my workaround seems to open the archive in readonly mode so my attempt of updating and overwriting the file did not do the trick. zip_open has this issue to begin with so I cannot open the archive to modify and save. I'm a newbie using libzip so I'm not sure if there are other ways to update archive with libzip calls. Any suggestion?
@0-wiz-0 I have a good news to share 😄
Spent some time trying to make zip_open work with removing FILE_ATTRIBUTE_REPARSE_POINT and a few more tweaking.
With GetFileAttributesEx call, the size of the file we receive is the size of the symbolic file itself, not the target.
We can instead first get the file handle by calling CreateFile (which returns the HANDLE of the target if we don't specify FILE_FLAG_OPEN_REPARSE_POINT. Then, call GetFileInformationByHandle, which returns the same kind of information (size, attributes, timestamp) as GetFileAttributesEx.
~~With this change, I can now both read and write to the target file of the symlink.~~
With this change, I can read the contents (testing with zip_get_num_entries) of the symlink's target file.
The write, however, replaces the symlink file itself. This seems because libzip calls MoveFileExW to move the contents of the temporarily created file to the actual file opened. With symlink, symlink file itself is updated so it ceases to be a symblic file.
That's good progress. I was expecting that the symlink would be replaced with a file, that's why I asked... Please let us know if you make progress on that end as well - we're looking forward to your patch 😃
Feedback timeout.