pak::pkg_install() fails on corporate Windows where AppLocker blocks cmdunzip.exe

Open
#849 0 comments 2 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
4/5
Estimated time
3-5 days
Newbie friendliness
35/100
Issue type
Bug
Clarity
Mostly clear
Activity status
Stale
Tech stack
r

Research direction

Start with pkgdepends:::make_install_process(), tracing its make_unzip_process() and zip::unzip_process() calls on Windows. Compare the proposed internal zip::unzip() path with the existing process-based path, and verify that package installation works when cmdunzip.exe is blocked by AppLocker without breaking non-zip archives.

Written by the indexing model from the issue text.

Description

Problem

pak::pkg_install() downloads packages successfully but fails during extraction on corporate Windows machines with AppLocker or Software Restriction Policies:

pak::pkg_install("dplyr")
#> Downloaded 27 packages (53.27 MB) in 40.9s
#> Error:
#> ! error in pak subprocess
#> Caused by error in `process_initialize(...)`:
#> ! Native call to `processx_exec` failed
#> Caused by error:
#> ! create process 'C:/Users/.../pak/library/zip/bin/x64/cmdunzip.exe'
#>   (system error 1260, This program is blocked by group policy.)

The root cause is that pkgdepends:::make_install_process() calls make_unzip_process()zip::unzip_process(), which spawns cmdunzip.exe as an external process. Corporate group policy blocks this .exe.

Standard R configuration (R_UNZIPCMD, options(unzip=), TAR) has no effect (I had TAR available on my corporate PC) because pak's internal code path ignores all of these.

Impact

This completely blocks pak on corporate Windows. The alternative is install.packages(), which loses pak's parallel downloads and dependency resolution and takes 6-10x longer (over an hour vs ~10 minutes in my case).

Proposed solution

Short term: Add a pak configuration option (e.g., PKG_ZIP_METHOD = "internal") that tells pkgdepends to use zip::unzip() (in-process C code, no external .exe) instead of zip::unzip_process() for .zip extraction.

Long term: The upstream fix belongs in the zip package — see r-lib/zip#135. If zip::unzip_process() could fall back to zip::unzip() when process creation fails, pak would inherit the fix automatically.

Working workaround

We developed a patch that replaces make_install_process in pak's worker subprocess. It extracts .zip files synchronously using zip::unzip(), then returns a real processx::process object (a finished cmd /c exit 0) so pak's async polling loop continues normally. Non-zip archives use the original code path.

library(pak)

pak:::remote(function() {
  ns <- asNamespace("pkgdepends")
  original <- get("make_install_process", envir = ns)

  replacement <- function(filename, lib = .libPaths()[[1L]], metadata = NULL) {
    type <- ns$detect_package_archive_type(filename)
    if (type != "zip") return(original(filename, lib = lib, metadata = metadata))

    now <- Sys.time()
    lib_cache <- ns$library_cache(lib)
    ns$mkdirp(pkg_cache <- tempfile(tmpdir = lib_cache))
    zip::unzip(filename, exdir = pkg_cache, overwrite = TRUE)
    ns$install_extracted_binary(filename, lib_cache, pkg_cache, lib, metadata, now)

    p <- processx::process$new("cmd.exe", c("/c", "exit", "0"),
                                stdout = "|", stderr = "2>&1")
    p$wait()
    reg.finalizer(p, function(...) unlink(pkg_cache, recursive = TRUE), onexit = TRUE)
    p
  }

  unlockBinding("make_install_process", ns)
  assign("make_install_process", replacement, envir = ns)
  lockBinding("make_install_process", ns)
  invisible(TRUE)
})

pak::pkg_install("dplyr")  # works

Tested successfully with 100+ packages on Windows 11 Enterprise with AppLocker.
Only issue with this is I have to source("patch_pak.R") after each library(pak) load or jam it into my .Rprofile, which would be fine if it was just me, but there's a few hundred R users where I am.

Environment

  • Windows 11 Enterprise with AppLocker/SRP enabled
  • R 4.4.x
  • pak 0.8.0+
Dominant language
C
Stars
821
Forks
88
Avg merge
17m
Merged PRs (30d)
1

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from r-lib/pak

All issues in r-lib/pak

Similar issues

More C issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.