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

Add File::BadExecutableError #13491

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
11 changes: 10 additions & 1 deletion spec/std/process_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,18 @@ describe Process do
end
end

pending_win32 "raises if command is not executable" do
it "raises if command is not executable" do
with_tempfile("crystal-spec-run") do |path|
File.touch path
expect_raises({% if flag?(:win32) %} File::BadExecutableError {% else %} File::AccessDeniedError {% end %}, "Error executing process: '#{path.inspect_unquoted}'") do
Process.new(path)
end
end
end

it "raises if command is not executable" do
with_tempfile("crystal-spec-run") do |path|
Dir.mkdir path
expect_raises(File::AccessDeniedError, "Error executing process: '#{path.inspect_unquoted}'") do
Process.new(path)
end
Expand Down
2 changes: 1 addition & 1 deletion src/crystal/system/unix/process.cr
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ struct Crystal::System::Process

private def self.raise_exception_from_errno(command, errno = Errno.value)
case errno
when Errno::EACCES, Errno::ENOENT
when Errno::EACCES, Errno::ENOENT, Errno::ENOEXEC
raise ::File::Error.from_os_error("Error executing process", errno, file: command)
else
raise IO::Error.from_os_error("Error executing process: '#{command}'", errno)
Expand Down
2 changes: 1 addition & 1 deletion src/crystal/system/win32/process.cr
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ struct Crystal::System::Process
) == 0
error = WinError.value
case error.to_errno
when Errno::EACCES, Errno::ENOENT
when Errno::EACCES, Errno::ENOENT, Errno::ENOEXEC
raise ::File::Error.from_os_error("Error executing process", error, file: command_args)
else
raise IO::Error.from_os_error("Error executing process: '#{command_args}'", error)
Expand Down
18 changes: 17 additions & 1 deletion src/file/error.cr
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ class File::Error < IO::Error
File::NotFoundError.new(message, **opts)
when Errno::EEXIST, WinError::ERROR_ALREADY_EXISTS
File::AlreadyExistsError.new(message, **opts)
when Errno::EACCES, WinError::ERROR_PRIVILEGE_NOT_HELD
when Errno::EACCES, WinError::ERROR_ACCESS_DENIED, WinError::ERROR_PRIVILEGE_NOT_HELD
File::AccessDeniedError.new(message, **opts)
when Errno::ENOEXEC, WinError::ERROR_BAD_EXE_FORMAT
File::BadExecutableError.new(message, **opts)
else
super message, os_error, **opts
end
Expand All @@ -26,6 +28,17 @@ class File::Error < IO::Error
"#{message}: '#{file.inspect_unquoted}' -> '#{other.inspect_unquoted}'"
end

{% if flag?(:win32) %}
protected def self.os_error_message(os_error : WinError, *, file : String) : String?
case os_error
when WinError::ERROR_BAD_EXE_FORMAT
os_error.formatted_message(file)
else
super
end
end
{% end %}

def initialize(message, *, file : String | Path, @other : String? = nil)
@file = file.to_s
super message
Expand All @@ -40,3 +53,6 @@ end

class File::AccessDeniedError < File::Error
end

class File::BadExecutableError < File::Error
end
2 changes: 1 addition & 1 deletion src/system_error.cr
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
# Returns the respective error message for *os_error*.
# By default it returns the result of `Errno#message` or `WinError#message`.
# This method can be overridden for customization of the error message based
# on *or_error* and *opts*.
# on *os_error* and *opts*.
module SystemError
macro included
extend ::SystemError::ClassMethods
Expand Down
19 changes: 19 additions & 0 deletions src/winerror.cr
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,11 @@ enum WinError : UInt32
#
# On non-win32 platforms the result is always an empty string.
def message : String
formatted_message
end

# :nodoc:
def formatted_message : String
{% if flag?(:win32) %}
buffer = uninitialized UInt16[256]
size = LibC.FormatMessageW(LibC::FORMAT_MESSAGE_FROM_SYSTEM, nil, value, 0, buffer, buffer.size, nil)
Expand All @@ -70,6 +75,20 @@ enum WinError : UInt32
{% end %}
end

# :nodoc:
def formatted_message(*args : String) : String
{% if flag?(:win32) %}
buffer = uninitialized UInt16[512]
args = args.to_static_array.map do |arg|
Crystal::System.to_wstr(arg)
end
size = LibC.FormatMessageW(LibC::FORMAT_MESSAGE_FROM_SYSTEM | LibC::FORMAT_MESSAGE_ARGUMENT_ARRAY, nil, value, 0, buffer, buffer.size, args)
String.from_utf16(buffer.to_slice[0, size]).strip
{% else %}
""
{% end %}
end

# Transforms this `WinError` value to the equivalent `Errno` value.
#
# This is only defined for some values. If no transformation is defined for
Expand Down