{
pal::string_t probe_path = path;
- if (pal::realpath(&probe_path) && pal::directory_exists(probe_path))
+ if (pal::realpath(&probe_path))
{
realpaths->push_back(probe_path);
}
else
{
- //Check if we can extrapolate |arch|<DIR_SEPARATOR>|tfm| for probing stores
- pal::string_t placeholder = _X("|arch|");
- placeholder.push_back(DIR_SEPARATOR);
- placeholder.append(_X("|tfm|"));
+ // Check if we can extrapolate |arch|<DIR_SEPARATOR>|tfm| for probing stores
+ // Check for for both forward and back slashes
+ pal::string_t placeholder = _X("|arch|\\|tfm|");
auto pos_placeholder = probe_path.find(placeholder);
+ if (pos_placeholder == pal::string_t::npos)
+ {
+ placeholder = _X("|arch|/|tfm|");
+ pos_placeholder = probe_path.find(placeholder);
+ }
if (pos_placeholder != pal::string_t::npos)
{
segment.append(tfm);
probe_path.replace(pos_placeholder, placeholder.length(), segment);
- if (pal::directory_exists(probe_path))
+ if (pal::realpath(&probe_path))
{
realpaths->push_back(probe_path);
}
return known_opts;
}
+// Returns '0' on success, 'AppArgNotRunnable' if should be routed to CLI, otherwise error code.
int fx_muxer_t::parse_args(
const pal::string_t& own_dir,
const pal::string_t& own_dll,
const pal::char_t* argv[],
bool exec_mode,
host_mode_t mode,
- bool* is_an_app,
int* new_argoff,
pal::string_t& app_candidate,
opt_map_t& opts)
{
- *is_an_app = true;
-
std::vector<host_option> known_opts = get_known_opts(exec_mode, mode);
// Parse the known arguments if any.
app_candidate = own_dll;
*new_argoff = argoff + num_parsed;
- if (mode != host_mode_t::standalone)
+ bool doesAppExist = false;
+ if (mode == host_mode_t::standalone)
+ {
+ doesAppExist = pal::realpath(&app_candidate);
+ }
+ else
{
trace::verbose(_X("Detected a non-standalone application, expecting app.dll to execute."));
if (*new_argoff >= argc)
}
app_candidate = argv[*new_argoff];
- bool is_app_managed = (ends_with(app_candidate, _X(".dll"), false) || ends_with(app_candidate, _X(".exe"), false)) && pal::realpath(&app_candidate);
+ bool is_app_managed = ends_with(app_candidate, _X(".dll"), false) || ends_with(app_candidate, _X(".exe"), false);
if (!is_app_managed)
{
trace::verbose(_X("Application '%s' is not a managed executable."), app_candidate.c_str());
+ if (!exec_mode)
+ {
+ // Route to CLI.
+ return AppArgNotRunnable;
+ }
+ }
- *is_an_app = false;
-
- if (exec_mode)
+ doesAppExist = pal::realpath(&app_candidate);
+ if (!doesAppExist)
+ {
+ trace::verbose(_X("Application '%s' does not exist."), app_candidate.c_str());
+ if (!exec_mode)
{
- trace::error(_X("dotnet exec needs a managed .dll or .exe extension. The application specified was '%s'"), app_candidate.c_str());
- return InvalidArgFailure;
+ // Route to CLI.
+ return AppArgNotRunnable;
}
+ }
- // Route to CLI.
- return AppArgNotRunnable;
+ if (!is_app_managed && doesAppExist)
+ {
+ assert(exec_mode == true);
+ trace::error(_X("dotnet exec needs a managed .dll or .exe extension. The application specified was '%s'"), app_candidate.c_str());
+ return InvalidArgFailure;
}
}
// App is managed executable.
- trace::verbose(_X("Treating application '%s' as a managed executable."), app_candidate.c_str());
-
- if (!pal::file_exists(app_candidate))
+ if (!doesAppExist)
{
trace::error(_X("The application to execute does not exist: '%s'"), app_candidate.c_str());
return InvalidArgFailure;
pal::string_t& runtime_config
)
{
- if (!runtime_config.empty() && (!pal::realpath(&runtime_config) || !pal::file_exists(runtime_config)))
+ if (!runtime_config.empty() && !pal::realpath(&runtime_config))
{
trace::error(_X("The specified runtimeconfig.json [%s] does not exist"), runtime_config.c_str());
return StatusCode::InvalidConfigFile;
pal::string_t runtime_config = get_last_known_arg(opts, opts_runtime_config, _X(""));
std::vector<pal::string_t> spec_probe_paths = opts.count(opts_probe_path) ? opts.find(opts_probe_path)->second : std::vector<pal::string_t>();
- if (!deps_file.empty() && (!pal::realpath(&deps_file) || !pal::file_exists(deps_file)))
+ if (!deps_file.empty() && !pal::realpath(&deps_file))
{
trace::error(_X("The specified deps.json [%s] does not exist"), deps_file.c_str());
return StatusCode::InvalidArgFailure;
// Detect invocation mode
host_mode_t mode = detect_operating_mode(own_dir, own_dll, own_name);
- bool is_an_app = true; // Indicates activation of an app instead of an invocation of the SDK.
int new_argoff;
pal::string_t app_candidate;
opt_map_t opts;
{
// Invoked as corehost
trace::verbose(_X("--- Executing in split/FX mode..."));
- result = parse_args(own_dir, own_dll, 1, argc, argv, false, mode, &is_an_app, &new_argoff, app_candidate, opts);
+ result = parse_args(own_dir, own_dll, 1, argc, argv, false, mode, &new_argoff, app_candidate, opts);
}
else if (mode == host_mode_t::standalone)
{
// Invoked from the application base.
trace::verbose(_X("--- Executing in standalone mode..."));
- result = parse_args(own_dir, own_dll, 1, argc, argv, false, mode, &is_an_app, &new_argoff, app_candidate, opts);
+ result = parse_args(own_dir, own_dll, 1, argc, argv, false, mode, &new_argoff, app_candidate, opts);
}
else
{
if (pal::strcasecmp(_X("exec"), argv[1]) == 0)
{
- result = parse_args(own_dir, own_dll, 2, argc, argv, true, mode, &is_an_app, &new_argoff, app_candidate, opts); // arg offset 2 for dotnet, exec
+ result = parse_args(own_dir, own_dll, 2, argc, argv, true, mode, &new_argoff, app_candidate, opts); // arg offset 2 for dotnet, exec
}
else
{
- result = parse_args(own_dir, own_dll, 1, argc, argv, false, mode, &is_an_app, &new_argoff, app_candidate, opts); // arg offset 1 for dotnet
+ result = parse_args(own_dir, own_dll, 1, argc, argv, false, mode, &new_argoff, app_candidate, opts); // arg offset 1 for dotnet
- if (!is_an_app)
+ if (result == AppArgNotRunnable)
{
return handle_cli(own_dir, own_dll, argc, argv);
}
}
}
- if (!result && is_an_app)
+ if (!result)
{
// Transform dotnet [exec] [--additionalprobingpath path] [--depsfile file] [dll] [args] -> dotnet [dll] [args]
result = handle_exec_host_command(host_command, own_dir, app_candidate, opts, argc, argv, new_argoff, mode, result_buffer, buffer_size, required_buffer_size);
trace::verbose(_X("Using dotnet SDK dll=[%s]"), sdk_dotnet.c_str());
- bool is_an_app;
int new_argoff;
pal::string_t app_candidate;
opt_map_t opts;
- int result = parse_args(own_dir, own_dll, 1, new_argv.size(), new_argv.data(), false, host_mode_t::muxer, &is_an_app, &new_argoff, app_candidate, opts); // arg offset 1 for dotnet
+ int result = parse_args(own_dir, own_dll, 1, new_argv.size(), new_argv.data(), false, host_mode_t::muxer, &new_argoff, app_candidate, opts); // arg offset 1 for dotnet
if (!result)
{
// Transform dotnet [exec] [--additionalprobingpath path] [--depsfile file] [dll] [args] -> dotnet [dll] [args]
return wchar_convert_helper(CP_UTF8, cstr, ::strlen(cstr), out);
}
+// Return if path is valid and file exists, return true and adjust path as appropriate.
bool pal::realpath(string_t* path)
{
-
- if (LongFile::IsNormalized(*path))
+ if (LongFile::IsNormalized(path->c_str()))
{
- return true;
+ WIN32_FILE_ATTRIBUTE_DATA data;
+ if (GetFileAttributesExW(path->c_str(), GetFileExInfoStandard, &data) != 0)
+ {
+ return true;
+ }
}
char_t buf[MAX_PATH];
auto size = ::GetFullPathNameW(path->c_str(), MAX_PATH, buf, nullptr);
-
if (size == 0)
{
trace::error(_X("Error resolving full path [%s]"), path->c_str());
return false;
}
+ string_t str;
if (size < MAX_PATH)
{
- path->assign(buf);
- return true;
+ str.assign(buf);
}
+ else
+ {
+ str.resize(size + LongFile::UNCExtendedPathPrefix.length(), 0);
- string_t str;
- str.resize(size + LongFile::UNCExtendedPathPrefix.length(), 0);
+ size = ::GetFullPathNameW(path->c_str(), size, (LPWSTR)str.data(), nullptr);
+ assert(size <= str.size());
- size = ::GetFullPathNameW(path->c_str() , size, (LPWSTR)str.data() , nullptr);
- assert(size <= str.size());
-
- if (size == 0)
- {
- trace::error(_X("Error resolving full path [%s]"), path->c_str());
- return false;
+ if (size == 0)
+ {
+ trace::error(_X("Error resolving full path [%s]"), path->c_str());
+ return false;
+ }
+
+ const string_t* prefix = &LongFile::ExtendedPrefix;
+ //Check if the resolved path is a UNC. By default we assume relative path to resolve to disk
+ if (str.compare(0, LongFile::UNCPathPrefix.length(), LongFile::UNCPathPrefix) == 0)
+ {
+ prefix = &LongFile::UNCExtendedPathPrefix;
+ str.erase(0, LongFile::UNCPathPrefix.length());
+ size = size - LongFile::UNCPathPrefix.length();
+ }
+
+ str.insert(0, *prefix);
+ str.resize(size + prefix->length());
+ str.shrink_to_fit();
}
- const string_t* prefix = &LongFile::ExtendedPrefix;
- //Check if the resolved path is a UNC. By default we assume relative path to resolve to disk
- if (str.compare(0, LongFile::UNCPathPrefix.length(), LongFile::UNCPathPrefix) == 0)
+ WIN32_FILE_ATTRIBUTE_DATA data;
+ if (GetFileAttributesExW(str.c_str(), GetFileExInfoStandard, &data) != 0)
{
- prefix = &LongFile::UNCExtendedPathPrefix;
- str.erase(0, LongFile::UNCPathPrefix.length());
- size = size - LongFile::UNCPathPrefix.length();
+ *path = str;
+ return true;
}
- str.insert(0, *prefix);
- str.resize(size + prefix->length());
- str.shrink_to_fit();
- *path = str;
-
- return true;
+ return false;
}
bool pal::file_exists(const string_t& path)
return false;
}
- auto pathstring = path.c_str();
- string_t normalized_path;
- if (LongFile::ShouldNormalize(path))
- {
- normalized_path = path;
- if (!pal::realpath(&normalized_path))
- {
- return false;
- }
- pathstring = normalized_path.c_str();
- }
-
- // We will attempt to fetch attributes for the file or folder in question that are
- // returned only if they exist.
- WIN32_FILE_ATTRIBUTE_DATA data;
- if (GetFileAttributesExW(pathstring, GetFileExInfoStandard, &data) != 0) {
- return true;
- }
-
- return false;
+ string_t tmp(path);
+ return pal::realpath(&tmp);
}
static void readdir(const pal::string_t& path, const pal::string_t& pattern, bool onlydirectories, std::vector<pal::string_t>* list)
--- /dev/null
+using FluentAssertions;
+using Microsoft.DotNet.CoreSetup.Test;
+using Microsoft.DotNet.Cli.Build.Framework;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using System.IO;
+using System.Runtime.InteropServices;
+using Xunit;
+
+namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.ArgValidation
+{
+ public class GivenThatICareAboutDotnetArgValidationScenarios
+ {
+ private RepoDirectoriesProvider RepoDirectories { get; set; }
+ private TestProjectFixture PreviouslyBuiltAndRestoredPortableTestProjectFixture { get; set; }
+
+ public GivenThatICareAboutDotnetArgValidationScenarios()
+ {
+ RepoDirectories = new RepoDirectoriesProvider();
+
+ PreviouslyBuiltAndRestoredPortableTestProjectFixture = new TestProjectFixture("PortableApp", RepoDirectories)
+ .EnsureRestored(RepoDirectories.CorehostPackages)
+ .BuildProject();
+ }
+
+ [Fact]
+ public void Muxer_Exec_With_Missing_App_Assembly_Fails()
+ {
+ var fixture = PreviouslyBuiltAndRestoredPortableTestProjectFixture
+ .Copy();
+
+ var dotnet = fixture.BuiltDotnet;
+
+ string assemblyName = Path.Combine(GetNonexistentAndUnnormalizedPath(), "foo.dll");
+
+ dotnet.Exec("exec", assemblyName)
+ .CaptureStdOut()
+ .CaptureStdErr()
+ .Execute()
+ .Should()
+ .Fail()
+ .And
+ .HaveStdErrContaining($"The application to execute does not exist: '{assemblyName}'");
+ }
+
+ [Fact]
+ public void Muxer_Exec_With_Missing_App_Assembly_And_Bad_Extension_Fails()
+ {
+ var fixture = PreviouslyBuiltAndRestoredPortableTestProjectFixture
+ .Copy();
+
+ var dotnet = fixture.BuiltDotnet;
+
+ string assemblyName = Path.Combine(GetNonexistentAndUnnormalizedPath(), "foo.xzy");
+
+ dotnet.Exec("exec", assemblyName)
+ .CaptureStdOut()
+ .CaptureStdErr()
+ .Execute()
+ .Should()
+ .Fail()
+ .And
+ .HaveStdErrContaining($"The application to execute does not exist: '{assemblyName}'");
+ }
+
+ [Fact]
+ public void Muxer_Exec_With_Bad_Extension_Fails()
+ {
+ var fixture = PreviouslyBuiltAndRestoredPortableTestProjectFixture
+ .Copy();
+
+ var dotnet = fixture.BuiltDotnet;
+
+ // Get a valid file name, but not exe or dll
+ string fxDir = Path.Combine(fixture.SdkDotnet.BinPath, "shared", "Microsoft.NETCore.App");
+ fxDir = new DirectoryInfo(fxDir).GetDirectories()[0].FullName;
+ string assemblyName = Path.Combine(fxDir, "Microsoft.NETCore.App.deps.json");
+
+ dotnet.Exec("exec", assemblyName)
+ .CaptureStdOut()
+ .CaptureStdErr()
+ .Execute()
+ .Should()
+ .Fail()
+ .And
+ .HaveStdErrContaining($"dotnet exec needs a managed .dll or .exe extension. The application specified was '{assemblyName}'");
+ }
+
+
+ // Return a non-exisitent path that contains a mix of / and \
+ private string GetNonexistentAndUnnormalizedPath()
+ {
+ return Path.Combine(PreviouslyBuiltAndRestoredPortableTestProjectFixture.SdkDotnet.BinPath, @"x\y/");
+ }
+ }
+}