libzip icon indicating copy to clipboard operation
libzip copied to clipboard

zip_open fails with symbolic link on Windows

Open ktakahashimtb opened this issue 2 years ago • 8 comments

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.

ktakahashimtb avatar May 11 '23 14:05 ktakahashimtb

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 avatar May 19 '23 11:05 0-wiz-0

@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?

arsnyder16 avatar May 24 '23 12:05 arsnyder16

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 avatar May 24 '23 12:05 0-wiz-0

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

ktakahashimtb avatar May 24 '23 13:05 ktakahashimtb

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 avatar May 24 '23 16:05 0-wiz-0

@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?

ktakahashimtb avatar May 24 '23 19:05 ktakahashimtb

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

ktakahashimtb avatar May 25 '23 17:05 ktakahashimtb

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 😃

0-wiz-0 avatar May 26 '23 05:05 0-wiz-0

Feedback timeout.

dillof avatar Aug 21 '24 16:08 dillof