Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extract Symlinks from Archives #1718

Merged
merged 4 commits into from
May 4, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,22 @@ import scala.util.{Try, Using}
*/
object Archive {

/** An archive entry representing a symbolic link. */
sealed trait Symlink
object Symlink {

/** The TAR archive entry representing a symbolic link.
*
* @param target the symlink target
*/
case class TarArchiveSymlink(target: Path) extends Symlink

/** The ZIP archive entry representing a symbolic lint.
* The entry's contents contains the symlink target.
*/
case object ZipArchiveSymlink extends Symlink
}

private val logger = Logger[Archive.type]

/** Extracts the archive at `archivePath` to `destinationDirectory`.
Expand Down Expand Up @@ -205,6 +221,16 @@ object Archive {
case None =>
missingPermissions += 1
}
getSymlink(entry).foreach {
case Archive.Symlink.TarArchiveSymlink(target) =>
Files.deleteIfExists(destinationPath)
Files.createSymbolicLink(destinationPath, target)
case Archive.Symlink.ZipArchiveSymlink =>
val target =
Path.of(Files.readString(destinationPath).trim)
Files.deleteIfExists(destinationPath)
Files.createSymbolicLink(destinationPath, target)
}
}
}
}
Expand Down Expand Up @@ -255,6 +281,19 @@ object Archive {
None
}

/** Get the symlink information associated with that `entry`. */
private def getSymlink(entry: ApacheArchiveEntry): Option[Symlink] = {
entry match {
case entry: TarArchiveEntry if entry.isSymbolicLink =>
val target = Path.of(entry.getLinkName)
Some(Symlink.TarArchiveSymlink(target))
case entry: ZipArchiveEntry if entry.isUnixSymlink =>
Some(Symlink.ZipArchiveSymlink)
case _ =>
None
}
}

/** Opens the archive at `path` and executes the provided action.
*
* The action is given an [[ArchiveInputStream]] that can be used to read
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@ case class UninstallationError(message: String)
extends ComponentsException(message)

/** Indicates that the required executable was not found. */
// TODO: [DB] Mask sensitive user data
case class ExecutableNotFoundError(path: Path, name: String)
extends ComponentsException(
s"Executable with the name '$name' does not found on $path."
s"Executable with the name '$name' was not found on $path."
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have to be careful with log messages like these. Users may have sensitive data in their path and this may leak it. This is probably something we need to think about more broadly, and perhaps have an additional flag alongside the log level that controls whether we log possible PII.

Of course these logs are only on-user-disk, but by the same token we can't expect them to read through the entire log to check if it's okay to send.

)
Original file line number Diff line number Diff line change
Expand Up @@ -719,31 +719,41 @@ class RuntimeVersionManager(
s"Loading temporary runtime ${runtimeTemporaryPath.toAbsolutePath}."
)
val temporaryRuntime =
loadGraalRuntime(runtimeTemporaryPath).getOrElse {
throw InstallationError(
"Cannot load the installed runtime. The package may have been " +
"corrupted. Reverting installation."
loadGraalRuntime(runtimeTemporaryPath).recoverWith { error =>
Failure(
InstallationError(
"Cannot load the installed runtime. The package may have " +
s"been corrupted. Reverting installation.",
error
)
)
}
}.get
logger.debug(s"Installing GraalVM components to $temporaryRuntime.")
installRequiredRuntimeComponents(temporaryRuntime).getOrElse {
throw InstallationError(
"fatal: Cannot install the required runtime components."
)
}
installRequiredRuntimeComponents(temporaryRuntime).recoverWith {
error =>
Failure(
InstallationError(
s"fatal: Cannot install the required runtime components.",
error
)
)
}.get

val runtimePath =
distributionManager.paths.runtimes / runtimeDirectoryName
logger.debug(
s"Moving ${runtimeTemporaryPath.toAbsolutePath} to ${runtimePath.toAbsolutePath}."
)
FileSystem.atomicMove(runtimeTemporaryPath, runtimePath)
val runtime = loadGraalRuntime(runtimePath).getOrElse {
val runtime = loadGraalRuntime(runtimePath).recoverWith { error =>
FileSystem.removeDirectory(runtimePath)
throw InstallationError(
"fatal: Cannot load the installed runtime."
Failure(
InstallationError(
s"fatal: Cannot load the installed runtime.",
error
)
)
}
}.get
logger.debug(s"Installed $runtime.")
userInterface.logInfo(s"Installed $runtime.")

Expand Down