Issue
I'm currently working on a Java project which should enable the user to export projects bundled with Windows software (.exe) or OS X app (.app) to distribute the data to other workstations. Both the Windows and OS X software are stored as a compressed zip file and uncompressed in case the project is exported. My issue is that unzipping the OS X app on Windows breaks the symbolic links inside bundled frameworks. This, in turn, breaks the app's signature and causes issues when the app is started on OS X.
I'm using Apache Commons compress libraries to uncompress the packages, which enables me to detect symbolic links and their target. With OS X, I'm able to recreate the symbolic link with methods from java.nio.file.Files, but with Windows this would require administrator privileges, which I'm a bit hesitant to add as a prerequisite to use the software (even if enabled I'm not confident this would work - haven't tried).
I have a little understanding of the reason why the links are broken, but if I understood correctly the Windows file system does not include support for the file type of Unix symbolic link, and hence the link is unzipped as a normal file and will no longer be recognized as a symlink when opened on OS X.
So, my question is that can I somehow just bitwise copy the symbolic link file to Windows file system preserving the Unix specific bits or is preserving this information downright impossible? Or should I just change the export method to add the project files to existing zip file, in which case symlink information would probably be preserved until the zip is extracted on the target machine?
The current code looping over each ZipArchiveEntry
of the ZipFile
is as below:
byte data[] = new byte[BUFFER];
Enumeration<ZipArchiveEntry> entries = zipFile.getEntries();
while (entries.hasMoreElements()) {
ZipArchiveEntry zipEntry = entries.nextElement();
String destFilename = copyFolder + zipEntry.getName();
File destFile = new File(destFilename);
if (zipEntry.isUnixSymlink()) {
File target = new File(zipFile.getUnixSymlink(zipEntry));
try {
// Try to create symbolic link - currently only works with OS X
Files.createSymbolicLink(destFile.toPath(), target.toPath());
continue;
} catch (Exception e) {
System.out.println("Failed to create symbolic link: " +
destFile.getAbsolutePath() + " -> " +
target.getAbsolutePath());
}
}
// If file
int count;
FileOutputStream fos = new FileOutputStream(destFile);
try (BufferedOutputStream dest = new BufferedOutputStream(fos, BUFFER)) {
InputStream is = zipFile.getInputStream(zipEntry);
while ( (count = is.read(data, 0, BUFFER)) != -1) {
dest.write(data, 0, count);
}
}
}
Solution
Windows file system uses similar approach as UNIX to define what symlink is (like hard- or soft- links), but they are not 100% compatible. You can read more about that here: https://msdn.microsoft.com/en-us/library/windows/desktop/aa365680(v=vs.85).aspx The simple answer to you question - you can't just bitwise copy links to have the same Unix specific bits, because Windows NTFS just don't have them. JAVA is also designed to work in a sandbox, so you don't have access to system low-level API in order to make "whatever you want". I wouldn't go for such system-specific task with JAVA. Probably you will not have to.
Depends on your role at this project you can re-define what bundle should contain. Do you really need symlinks inside? May be it is more universal to have some kind of "property" file that maps different parts together? Or distribute it as database? Try not to depend from system specific realization.
Answered By - Vadim
Answer Checked By - David Goodson (JavaFixing Volunteer)