11package os
22
3+ import org .apache .tools .{zip => ant }
4+
35import java .net .URI
46import java .nio .file .{FileSystem , FileSystems , Files }
57import java .nio .file .attribute .{BasicFileAttributeView , FileTime , PosixFilePermissions }
@@ -115,7 +117,7 @@ object zip {
115117 compressionLevel : Int ,
116118 out : java.io.OutputStream
117119 ): Unit = {
118- val zipOut = new ZipOutputStream (out)
120+ val zipOut = new ant. ZipOutputStream (out)
119121 zipOut.setLevel(compressionLevel)
120122
121123 try {
@@ -125,6 +127,7 @@ object zip {
125127 includePatterns,
126128 (path, sub) => makeZipEntry(path, sub, preserveMtimes, zipOut)
127129 )
130+ zipOut.finish()
128131 } finally {
129132 zipOut.close()
130133 }
@@ -143,35 +146,38 @@ object zip {
143146 ! isExcluded && isIncluded
144147 }
145148
149+ private def toFileType (file : os.Path ): PermissionUtils .FileType = {
150+ if (os.isLink(file)) PermissionUtils .FileType .SYMLINK
151+ else if (os.isFile(file)) PermissionUtils .FileType .REGULAR_FILE
152+ else if (os.isDir(file)) PermissionUtils .FileType .DIR
153+ else PermissionUtils .FileType .OTHER
154+ }
155+
146156 private def makeZipEntry (
147157 file : os.Path ,
148158 sub : os.SubPath ,
149159 preserveMtimes : Boolean ,
150- zipOut : ZipOutputStream
160+ zipOut : ant. ZipOutputStream
151161 ) = {
162+ val zipEntry = new ant.ZipEntry (sub.toString)
152163
153- val mtimeOpt = if (preserveMtimes) Some (os.mtime(file)) else None
154-
155- val fis = if (os.isFile(file)) Some (os.read.inputStream(file)) else None
156- try makeZipEntry0(sub, fis, mtimeOpt, zipOut)
157- finally fis.foreach(_.close())
158- }
164+ val mtime = if (preserveMtimes) os.mtime(file) else 0
165+ zipEntry.setTime(mtime)
159166
160- private def makeZipEntry0 (
161- sub : os.SubPath ,
162- is : Option [java.io.InputStream ],
163- preserveMtimes : Option [Long ],
164- zipOut : ZipOutputStream
165- ) = {
166- val zipEntry = new ZipEntry (sub.toString)
167+ val mode = PermissionUtils .modeFromPermissions(os.perms(file).toSet(), toFileType(file))
168+ zipEntry.setUnixMode(mode)
167169
168- preserveMtimes match {
169- case Some (mtime) => zipEntry.setTime(mtime)
170- case None => zipEntry.setTime(0 )
171- }
170+ val fis =
171+ if (os.isLink(file))
172+ Some (new java.io.ByteArrayInputStream (os.readLink(file).toString().getBytes()))
173+ else if (os.isFile(file)) Some (os.read.inputStream(file))
174+ else None
172175
173- zipOut.putNextEntry(zipEntry)
174- is.foreach(os.Internals .transfer(_, zipOut, close = false ))
176+ try {
177+ zipOut.putNextEntry(zipEntry)
178+ fis.foreach(os.Internals .transfer(_, zipOut, close = false ))
179+ zipOut.closeEntry()
180+ } finally fis.foreach(_.close())
175181 }
176182
177183 /**
@@ -241,6 +247,10 @@ object unzip {
241247 } yield os.SubPath (zipEntry.getName)
242248 }
243249
250+ private lazy val S_IFMT : Int = java.lang.Integer .parseInt(" 0170000" , 8 )
251+ private def isSymLink (mode : Int ): Boolean =
252+ (mode & S_IFMT ) == ant.UnixStat .LINK_FLAG
253+
244254 /**
245255 * Extract the given zip file into the destination directory
246256 *
@@ -254,7 +264,39 @@ object unzip {
254264 excludePatterns : Seq [Regex ] = List (),
255265 includePatterns : Seq [Regex ] = List ()
256266 ): os.Path = {
257- stream(os.read.stream(source), dest, excludePatterns, includePatterns)
267+ checker.value.onWrite(dest)
268+
269+ val zipFile = new ant.ZipFile (source.toIO)
270+ val zipEntryInputStreams = zipFile.getEntries.asScala
271+ .filter(ze => os.zip.shouldInclude(ze.getName, excludePatterns, includePatterns))
272+ .map(ze => (ze, zipFile.getInputStream(ze)))
273+
274+ try {
275+ for ((zipEntry, zipInputStream) <- zipEntryInputStreams) {
276+ val newFile = dest / os.SubPath (zipEntry.getName)
277+ val mode = zipEntry.getUnixMode
278+ val perms = if (mode > 0 ) {
279+ os.PermSet .fromSet(PermissionUtils .permissionsFromMode(mode))
280+ } else null
281+
282+ if (zipEntry.isDirectory) {
283+ os.makeDir.all(newFile, perms = perms)
284+ } else if (isSymLink(mode)) {
285+ val target = scala.io.Source .fromInputStream(zipInputStream).mkString
286+ val path = java.nio.file.Paths .get(target)
287+ val dest = if (path.isAbsolute) os.Path (path) else os.RelPath (path)
288+ os.symlink(newFile, dest)
289+ } else {
290+ val outputStream = os.write.outputStream(newFile, createFolders = true )
291+ os.Internals .transfer(zipInputStream, outputStream, close = false )
292+ outputStream.close()
293+ if (perms != null ) os.perms.set(newFile, perms)
294+ }
295+ }
296+ } finally {
297+ zipFile.close()
298+ }
299+
258300 dest
259301 }
260302
0 commit comments