Filesystem copyFile fails on Windows due to incompatibility between `copy` and `bypass_shell`

Under windows, copyFile in arcanist/src/filesystem/Filesystem.php invokes execx('copy /Y %s %s', $from, $to), which eventually leads to ExecFuture.php's invocation of proc_open function with bypass_shell turned on. However, copy is not an executable under windows, but implemented by the cmd shell. Thus when bypass_shell is turned on, copyFile will always fail with error similar to the following:

[2020-08-11 02:32:34] EXCEPTION: (CommandException) Command failed with error #1!
COMMAND
copy /Y "C:\projects\fc\doc\dev_setup.md" "C:\projects\fc\doc\dev_setup.md.linted"

STDOUT
(empty)

STDERR
Call to "proc_open()" to open a subprocess failed: proc_open(): CreateProcess failed, error code - 2 at [<arcanist>\src\future\exec\ExecFuture.php:421]
arcanist(head=stable, ref.master=ceb082ef6b29, ref.stable=ccd39feb6d3b), objc-format-linter(head=master, ref.master=bb666ea26a76)
  #0 ExecFuture::raiseResultError(array) called at [<arcanist>\src\future\exec\ExecFuture.php:325]
  #1 ExecFuture::resolvex() called at [<arcanist>\src\future\exec\execx.php:17]
  #2 execx(string, string, string) called at [<arcanist>\src\filesystem\Filesystem.php:272]
  #3 Filesystem::copyFile(string, string) called at [<arcanist>\src\lint\ArcanistLintPatcher.php:45]
  #4 ArcanistLintPatcher::writePatchToDisk() called at [<arcanist>\src\workflow\ArcanistLintWorkflow.php:316]
  #5 ArcanistLintWorkflow::run() called at [<arcanist>\src\workflow\ArcanistDiffWorkflow.php:1167]
  #6 ArcanistDiffWorkflow::runLint() called at [<arcanist>\src\workflow\ArcanistDiffWorkflow.php:1129]
  #7 ArcanistDiffWorkflow::runLintUnit() called at [<arcanist>\src\workflow\ArcanistDiffWorkflow.php:365]
  #8 ArcanistDiffWorkflow::run() called at [<arcanist>\scripts\arcanist.php:419]

Quick patch

A quick fix that worked for us is to replace execx('copy /Y %s %s', $from, $to) with execx('cmd /c copy /Y %s %s', $from, $to) in Filesystem.php. But I’m not sure about its bullet proofness.

Reproduction Instructions

  • Under windows 10, have a code base with Text Linter turned on.
  • Have file with incorrect Windows-style line ending as opposed to linux-style as part of the change set. With git, you need to turn off core.autocrlf so the commited file still has the incorrect line ending style.
  • run arc diff. The Text linter will convert the line ending to LInux style and copy the file to a name ending with .linted suffix using Filesystem’s copyFile function, thus triggering the bug.

Versions

  • arc version: arcanist ccd39feb6d3bbd84e122817b0d8b36284b6edd9d (25 Jul 2020)

Thanks, I filed this upstream as https://secure.phabricator.com/T13562.

1 Like