./bundle/file_entry.cpp
./bundle/manifest.cpp
./bundle/header.cpp
- ./bundle/bundle_runner.cpp
./bundle/marker.cpp
+ ./bundle/reader.cpp
+ ./bundle/extractor.cpp
+ ./bundle/runner.cpp
+ ./bundle/dir_utils.cpp
)
set(HEADERS
./bundle/file_entry.h
./bundle/manifest.h
./bundle/header.h
- ./bundle/bundle_runner.h
./bundle/marker.h
+ ./bundle/reader.h
+ ./bundle/extractor.h
+ ./bundle/runner.h
+ ./bundle/dir_utils.h
)
include(../exe.cmake)
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-#include "bundle_runner.h"
-#include "pal.h"
-#include "trace.h"
-#include "utils.h"
-
-using namespace bundle;
-
-void bundle_runner_t::seek(FILE* stream, long offset, int origin)
-{
- if (fseek(stream, offset, origin) != 0)
- {
- trace::error(_X("Failure processing application bundle; possible file corruption."));
- trace::error(_X("I/O seek failure within the bundle."));
- throw StatusCode::BundleExtractionIOError;
- }
-}
-
-void bundle_runner_t::write(const void* buf, size_t size, FILE *stream)
-{
- if (fwrite(buf, 1, size, stream) != size)
- {
- trace::error(_X("Failure extracting contents of the application bundle."));
- trace::error(_X("I/O failure when writing extracted files."));
- throw StatusCode::BundleExtractionIOError;
- }
-}
-
-void bundle_runner_t::read(void* buf, size_t size, FILE* stream)
-{
- if (fread(buf, 1, size, stream) != size)
- {
- trace::error(_X("Failure processing application bundle; possible file corruption."));
- trace::error(_X("I/O failure reading contents of the bundle."));
- throw StatusCode::BundleExtractionIOError;
- }
-}
-
-// Handle the relatively uncommon scenario where the bundle ID or
-// the relative-path of a file within the bundle is longer than 127 bytes
-size_t bundle_runner_t::get_path_length(int8_t first_byte, FILE* stream)
-{
- size_t length = 0;
-
- // If the high bit is set, it means there are more bytes to read.
- if ((first_byte & 0x80) == 0)
- {
- length = first_byte;
- }
- else
- {
- int8_t second_byte = 0;
- read(&second_byte, 1, stream);
-
- if (second_byte & 0x80)
- {
- // There can be no more than two bytes in path_length
- trace::error(_X("Failure processing application bundle; possible file corruption."));
- trace::error(_X("Path length encoding read beyond two bytes"));
-
- throw StatusCode::BundleExtractionFailure;
- }
-
- length = (second_byte << 7) | (first_byte & 0x7f);
- }
-
- if (length <= 0 || length > PATH_MAX)
- {
- trace::error(_X("Failure processing application bundle; possible file corruption."));
- trace::error(_X("Path length is zero or too long"));
- throw StatusCode::BundleExtractionFailure;
- }
-
- return length;
-}
-
-// Read a non-null terminated fixed length UTF8 string from a byte-stream
-// and transform it to pal::string_t
-void bundle_runner_t::read_string(pal::string_t &str, size_t size, FILE* stream)
-{
- std::unique_ptr<uint8_t[]> buffer{new uint8_t[size + 1]};
- read(buffer.get(), size, stream);
- buffer[size] = 0; // null-terminator
- pal::clr_palstring(reinterpret_cast<const char*>(buffer.get()), &str);
-}
-
-static bool has_dirs_in_path(const pal::string_t& path)
-{
- return path.find_last_of(DIR_SEPARATOR) != pal::string_t::npos;
-}
-
-static void create_directory_tree(const pal::string_t &path)
-{
- if (path.empty())
- {
- return;
- }
-
- if (pal::directory_exists(path))
- {
- return;
- }
-
- if (has_dirs_in_path(path))
- {
- create_directory_tree(get_directory(path));
- }
-
- if (!pal::mkdir(path.c_str(), 0700)) // Owner - rwx
- {
- if (pal::directory_exists(path))
- {
- // The directory was created since we last checked.
- return;
- }
-
- trace::error(_X("Failure processing application bundle."));
- trace::error(_X("Failed to create directory [%s] for extracting bundled files"), path.c_str());
- throw StatusCode::BundleExtractionIOError;
- }
-}
-
-static void remove_directory_tree(const pal::string_t& path)
-{
- if (path.empty())
- {
- return;
- }
-
- std::vector<pal::string_t> dirs;
- pal::readdir_onlydirectories(path, &dirs);
-
- for (const pal::string_t &dir : dirs)
- {
- remove_directory_tree(dir);
- }
-
- std::vector<pal::string_t> files;
- pal::readdir(path, &files);
-
- for (const pal::string_t &file : files)
- {
- if (!pal::remove(file.c_str()))
- {
- trace::warning(_X("Failed to remove temporary file [%s]."), file.c_str());
- }
- }
-
- if (!pal::rmdir(path.c_str()))
- {
- trace::warning(_X("Failed to remove temporary directory [%s]."), path.c_str());
- }
-}
-
-void bundle_runner_t::reopen_host_for_reading()
-{
- m_bundle_stream = pal::file_open(m_bundle_path, _X("rb"));
- if (m_bundle_stream == nullptr)
- {
- trace::error(_X("Failure processing application bundle."));
- trace::error(_X("Couldn't open host binary for reading contents"));
- throw StatusCode::BundleExtractionIOError;
- }
-}
-
-// Compute the final extraction location as:
-// m_extraction_dir = $DOTNET_BUNDLE_EXTRACT_BASE_DIR/<app>/<id>/...
-//
-// If DOTNET_BUNDLE_EXTRACT_BASE_DIR is not set in the environment, the
-// base directory defaults to $TMPDIR/.net
-void bundle_runner_t::determine_extraction_dir()
-{
- if (!pal::getenv(_X("DOTNET_BUNDLE_EXTRACT_BASE_DIR"), &m_extraction_dir))
- {
- if (!pal::get_temp_directory(m_extraction_dir))
- {
- trace::error(_X("Failure processing application bundle."));
- trace::error(_X("Failed to determine location for extracting embedded files"));
- throw StatusCode::BundleExtractionFailure;
- }
-
- append_path(&m_extraction_dir, _X(".net"));
- }
-
- pal::string_t host_name = strip_executable_ext(get_filename(m_bundle_path));
- append_path(&m_extraction_dir, host_name.c_str());
- append_path(&m_extraction_dir, bundle_id().c_str());
-
- trace::info(_X("Files embedded within the bundled will be extracted to [%s] directory"), m_extraction_dir.c_str());
-}
-
-// Compute the worker extraction location for this process, before the
-// extracted files are committed to the final location
-// m_working_extraction_dir = $DOTNET_BUNDLE_EXTRACT_BASE_DIR/<app>/<proc-id-hex>
-void bundle_runner_t::create_working_extraction_dir()
-{
- // Set the working extraction path
- m_working_extraction_dir = get_directory(m_extraction_dir);
- pal::char_t pid[32];
- pal::snwprintf(pid, 32, _X("%x"), pal::get_pid());
- append_path(&m_working_extraction_dir, pid);
-
- create_directory_tree(m_working_extraction_dir);
-
- trace::info(_X("Temporary directory used to extract bundled files is [%s]"), m_working_extraction_dir.c_str());
-}
-
-// Create a file to be extracted out on disk, including any intermediate sub-directories.
-FILE* bundle_runner_t::create_extraction_file(const pal::string_t& relative_path)
-{
- pal::string_t file_path = m_working_extraction_dir;
- append_path(&file_path, relative_path.c_str());
-
- // m_working_extraction_dir is assumed to exist,
- // so we only create sub-directories if relative_path contains directories
- if (has_dirs_in_path(relative_path))
- {
- create_directory_tree(get_directory(file_path));
- }
-
- FILE* file = pal::file_open(file_path.c_str(), _X("wb"));
-
- if (file == nullptr)
- {
- trace::error(_X("Failure processing application bundle."));
- trace::error(_X("Failed to open file [%s] for writing"), file_path.c_str());
- throw StatusCode::BundleExtractionIOError;
- }
-
- return file;
-}
-
-// Extract one file from the bundle to disk.
-void bundle_runner_t::extract_file(const file_entry_t& entry)
-{
- FILE* file = create_extraction_file(entry.relative_path());
- const int64_t buffer_size = 8 * 1024; // Copy the file in 8KB chunks
- uint8_t buffer[buffer_size];
- int64_t file_size = entry.size();
-
- seek(m_bundle_stream, entry.offset(), SEEK_SET);
- do {
- int64_t copy_size = (file_size <= buffer_size) ? file_size : buffer_size;
- read(buffer, copy_size, m_bundle_stream);
- write(buffer, copy_size, file);
- file_size -= copy_size;
- } while (file_size > 0);
-
- fclose(file);
-}
-
-bool bundle_runner_t::can_reuse_extraction()
-{
- // In this version, the extracted files are assumed to be
- // correct by construction.
- //
- // Files embedded in the bundle are first extracted to m_working_extraction_dir
- // Once all files are successfully extracted, the extraction location is
- // committed (renamed) to m_extraction_dir. Therefore, the presence of
- // m_extraction_dir means that the files are pre-extracted.
-
-
- return pal::directory_exists(m_extraction_dir);
-}
-
-// Current support for executing single-file bundles involves
-// extraction of embedded files to actual files on disk.
-// This method implements the file extraction functionality at startup.
-StatusCode bundle_runner_t::extract()
-{
- try
- {
- reopen_host_for_reading();
-
- // Read the bundle header
- seek(m_bundle_stream, marker_t::header_offset(), SEEK_SET);
- m_header = header_t::read(m_bundle_stream);
-
- // Determine if embedded files are already extracted, and available for reuse
- determine_extraction_dir();
- if (can_reuse_extraction())
- {
- return StatusCode::Success;
- }
-
- // Extract files to temporary working directory
- //
- // Files are extracted to a specific deterministic location on disk
- // on first run, and are available for reuse by subsequent similar runs.
- //
- // The extraction should be fault tolerant with respect to:
- // * Failures/crashes during extraction which result in partial-extraction
- // * Race between two or more processes concurrently attempting extraction
- //
- // In order to solve these issues, we implement a extraction as a two-phase approach:
- // 1) Files embedded in a bundle are extracted to a process-specific temporary
- // extraction location (m_working_extraction_dir)
- // 2) Upon successful extraction, m_working_extraction_dir is renamed to the actual
- // extraction location (m_extraction_dir)
- //
- // This effectively creates a file-lock to protect against races and failed extractions.
-
- create_working_extraction_dir();
-
- m_manifest = manifest_t::read(m_bundle_stream, num_embedded_files());
-
- for (const file_entry_t & entry : m_manifest.files) {
- extract_file(entry);
- }
-
- // Commit files to the final extraction directory
- // Retry the move operation with some wait in between the attempts. This is to workaround for possible file locking
- // caused by AV software. Basically the extraction process above writes a bunch of executable files to disk
- // and some AV software may decide to scan them on write. If this happens the files will be locked which blocks
- // our ablity to move them.
- int retry_count = 500;
- while (true)
- {
- if (pal::rename(m_working_extraction_dir.c_str(), m_extraction_dir.c_str()) == 0)
- break;
-
- bool should_retry = errno == EACCES;
- if (can_reuse_extraction())
- {
- // Another process successfully extracted the dependencies
- trace::info(_X("Extraction completed by another process, aborting current extraction."));
-
- remove_directory_tree(m_working_extraction_dir);
- break;
- }
-
- if (should_retry && (retry_count--) > 0)
- {
- trace::info(_X("Retrying extraction due to EACCES trying to rename the extraction folder to [%s]."), m_extraction_dir.c_str());
- pal::sleep(100);
- continue;
- }
- else
- {
- trace::error(_X("Failure processing application bundle."));
- trace::error(_X("Failed to commit extracted files to directory [%s]"), m_extraction_dir.c_str());
- throw StatusCode::BundleExtractionFailure;
- }
- }
-
- fclose(m_bundle_stream);
- return StatusCode::Success;
- }
- catch (StatusCode e)
- {
- fclose(m_bundle_stream);
- return e;
- }
-}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-#ifndef __BUNDLE_RUNNER_H__
-#define __BUNDLE_RUNNER_H__
-
-
-#include <cstdint>
-#include <memory>
-#include "header.h"
-#include "manifest.h"
-#include "marker.h"
-#include "error_codes.h"
-
-namespace bundle
-{
- class bundle_runner_t
- {
- public:
- bundle_runner_t(const pal::string_t& bundle_path)
- : m_bundle_stream(nullptr)
- , m_bundle_path(bundle_path)
- {
- }
-
- pal::string_t get_extraction_dir()
- {
- return m_extraction_dir;
- }
-
- StatusCode extract();
-
- static void read(void* buf, size_t size, FILE* stream);
- static void write(const void* buf, size_t size, FILE* stream);
- static size_t get_path_length(int8_t first_byte, FILE* stream);
- static void read_string(pal::string_t& str, size_t size, FILE* stream);
-
- private:
- void reopen_host_for_reading();
- static void seek(FILE* stream, long offset, int origin);
-
- int32_t num_embedded_files() { return m_header.num_embedded_files(); }
- const pal::string_t& bundle_id() { return m_header.bundle_id(); }
-
- void determine_extraction_dir();
- void create_working_extraction_dir();
- bool can_reuse_extraction();
-
- FILE* create_extraction_file(const pal::string_t& relative_path);
- void extract_file(const file_entry_t& entry);
-
- FILE* m_bundle_stream;
- header_t m_header;
- manifest_t m_manifest;
- pal::string_t m_bundle_path;
- pal::string_t m_extraction_dir;
- pal::string_t m_working_extraction_dir;
- };
-
-}
-
-#endif // __BUNDLE_RUNNER_H__
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#include "dir_utils.h"
+#include "error_codes.h"
+#include "utils.h"
+
+using namespace bundle;
+
+bool dir_utils_t::has_dirs_in_path(const pal::string_t& path)
+{
+ return path.find_last_of(DIR_SEPARATOR) != pal::string_t::npos;
+}
+
+void dir_utils_t::create_directory_tree(const pal::string_t &path)
+{
+ if (path.empty())
+ {
+ return;
+ }
+
+ if (pal::directory_exists(path))
+ {
+ return;
+ }
+
+ if (has_dirs_in_path(path))
+ {
+ create_directory_tree(get_directory(path));
+ }
+
+ if (!pal::mkdir(path.c_str(), 0700)) // Owner - rwx
+ {
+ if (pal::directory_exists(path))
+ {
+ // The directory was created since we last checked.
+ return;
+ }
+
+ trace::error(_X("Failure processing application bundle."));
+ trace::error(_X("Failed to create directory [%s] for extracting bundled files."), path.c_str());
+ throw StatusCode::BundleExtractionIOError;
+ }
+}
+
+void dir_utils_t::remove_directory_tree(const pal::string_t& path)
+{
+ if (path.empty())
+ {
+ return;
+ }
+
+ std::vector<pal::string_t> dirs;
+ pal::readdir_onlydirectories(path, &dirs);
+
+ for (const pal::string_t &dir : dirs)
+ {
+ remove_directory_tree(dir);
+ }
+
+ std::vector<pal::string_t> files;
+ pal::readdir(path, &files);
+
+ for (const pal::string_t &file : files)
+ {
+ if (!pal::remove(file.c_str()))
+ {
+ trace::warning(_X("Failed to remove temporary file [%s]."), file.c_str());
+ }
+ }
+
+ if (!pal::rmdir(path.c_str()))
+ {
+ trace::warning(_X("Failed to remove temporary directory [%s]."), path.c_str());
+ }
+}
+
+// Fixup a path to have current platform's directory separator.
+void dir_utils_t::fixup_path_separator(pal::string_t& path)
+{
+ const pal::char_t bundle_dir_separator = '/';
+
+ if (bundle_dir_separator != DIR_SEPARATOR)
+ {
+ for (size_t pos = path.find(bundle_dir_separator);
+ pos != pal::string_t::npos;
+ pos = path.find(bundle_dir_separator, pos))
+ {
+ path[pos] = DIR_SEPARATOR;
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#ifndef __DIR_UTIL_H__
+#define __DIR_UTIL_H__
+
+#include <cstdint>
+#include "pal.h"
+
+namespace bundle
+{
+ class dir_utils_t
+ {
+ public:
+ static bool has_dirs_in_path(const pal::string_t &path);
+ static void remove_directory_tree(const pal::string_t &path);
+ static void create_directory_tree(const pal::string_t &path);
+ static void fixup_path_separator(pal::string_t& path);
+ };
+}
+
+#endif // __DIR_UTIL_H__
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#include "extractor.h"
+#include "error_codes.h"
+#include "dir_utils.h"
+#include "pal.h"
+#include "utils.h"
+
+using namespace bundle;
+
+// Compute the final extraction location as:
+// m_extraction_dir = $DOTNET_BUNDLE_EXTRACT_BASE_DIR/<app>/<id>/...
+//
+// If DOTNET_BUNDLE_EXTRACT_BASE_DIR is not set in the environment, the
+// base directory defaults to $TMPDIR/.net
+void extractor_t::determine_extraction_dir()
+{
+ if (!pal::getenv(_X("DOTNET_BUNDLE_EXTRACT_BASE_DIR"), &m_extraction_dir))
+ {
+ if (!pal::get_temp_directory(m_extraction_dir))
+ {
+ trace::error(_X("Failure processing application bundle."));
+ trace::error(_X("Failed to determine location for extracting embedded files."));
+ throw StatusCode::BundleExtractionFailure;
+ }
+
+ append_path(&m_extraction_dir, _X(".net"));
+ }
+
+ pal::string_t host_name = strip_executable_ext(get_filename(m_bundle_path));
+ append_path(&m_extraction_dir, host_name.c_str());
+ append_path(&m_extraction_dir, m_bundle_id.c_str());
+
+ trace::info(_X("Files embedded within the bundled will be extracted to [%s] directory."), m_extraction_dir.c_str());
+}
+
+// Compute the working extraction location for this process, before the
+// extracted files are committed to the final location
+// m_working_extraction_dir = $DOTNET_BUNDLE_EXTRACT_BASE_DIR/<app>/<proc-id-hex>
+void extractor_t::determine_working_extraction_dir()
+{
+ m_working_extraction_dir = get_directory(extraction_dir());
+ pal::char_t pid[32];
+ pal::snwprintf(pid, 32, _X("%x"), pal::get_pid());
+ append_path(&m_working_extraction_dir, pid);
+
+ dir_utils_t::create_directory_tree(m_working_extraction_dir);
+
+ trace::info(_X("Temporary directory used to extract bundled files is [%s]."), m_working_extraction_dir.c_str());
+}
+
+// Create a file to be extracted out on disk, including any intermediate sub-directories.
+FILE* extractor_t::create_extraction_file(const pal::string_t& relative_path)
+{
+ pal::string_t file_path = m_working_extraction_dir;
+ append_path(&file_path, relative_path.c_str());
+
+ // m_working_extraction_dir is assumed to exist,
+ // so we only create sub-directories if relative_path contains directories
+ if (dir_utils_t::has_dirs_in_path(relative_path))
+ {
+ dir_utils_t::create_directory_tree(get_directory(file_path));
+ }
+
+ FILE* file = pal::file_open(file_path.c_str(), _X("wb"));
+
+ if (file == nullptr)
+ {
+ trace::error(_X("Failure processing application bundle."));
+ trace::error(_X("Failed to open file [%s] for writing."), file_path.c_str());
+ throw StatusCode::BundleExtractionIOError;
+ }
+
+ return file;
+}
+
+// Extract one file from the bundle to disk.
+void extractor_t::extract(const file_entry_t &entry, reader_t &reader)
+{
+ FILE* file = create_extraction_file(entry.relative_path());
+ reader.set_offset(entry.offset());
+ size_t size = entry.size();
+
+ if (fwrite(reader, 1, size, file) != size)
+ {
+ trace::error(_X("Failure extracting contents of the application bundle."));
+ trace::error(_X("I/O failure when writing extracted files."));
+ throw StatusCode::BundleExtractionIOError;
+ }
+
+ fclose(file);
+}
+
+pal::string_t& extractor_t::extraction_dir()
+{
+ if (m_extraction_dir.empty())
+ {
+ determine_extraction_dir();
+ }
+
+ return m_extraction_dir;
+}
+
+bool extractor_t::can_reuse_extraction()
+{
+ // In this version, the extracted files are assumed to be
+ // correct by construction.
+ //
+ // Files embedded in the bundle are first extracted to m_working_extraction_dir
+ // Once all files are successfully extracted, the extraction location is
+ // committed (renamed) to m_extraction_dir. Therefore, the presence of
+ // m_extraction_dir means that the files are pre-extracted.
+
+ return pal::directory_exists(extraction_dir());
+}
+
+void extractor_t::begin()
+{
+ // Files are extracted to a specific deterministic location on disk
+ // on first run, and are available for reuse by subsequent similar runs.
+ //
+ // The extraction should be fault tolerant with respect to:
+ // * Failures/crashes during extraction which result in partial-extraction
+ // * Race between two or more processes concurrently attempting extraction
+ //
+ // In order to solve these issues, we implement a extraction as a two-phase approach:
+ // 1) Files embedded in a bundle are extracted to a process-specific temporary
+ // extraction location (m_working_extraction_dir)
+ // 2) Upon successful extraction, m_working_extraction_dir is renamed to the actual
+ // extraction location (m_extraction_dir)
+ //
+ // This effectively creates a file-lock to protect against races and failed extractions.
+
+ determine_working_extraction_dir();
+}
+
+void extractor_t::commit()
+{
+ // Commit files to the final extraction directory
+ // Retry the move operation with some wait in between the attempts. This is to workaround for possible file locking
+ // caused by AV software. Basically the extraction process above writes a bunch of executable files to disk
+ // and some AV software may decide to scan them on write. If this happens the files will be locked which blocks
+ // our ablity to move them.
+ int retry_count = 500;
+ while (true)
+ {
+ if (pal::rename(m_working_extraction_dir.c_str(), m_extraction_dir.c_str()) == 0)
+ break;
+
+ bool should_retry = errno == EACCES;
+ if (can_reuse_extraction())
+ {
+ // Another process successfully extracted the dependencies
+ trace::info(_X("Extraction completed by another process, aborting current extraction."));
+
+ dir_utils_t::remove_directory_tree(m_working_extraction_dir);
+ break;
+ }
+
+ if (should_retry && (retry_count--) > 0)
+ {
+ trace::info(_X("Retrying extraction due to EACCES trying to rename the extraction folder to [%s]."), m_extraction_dir.c_str());
+ pal::sleep(100);
+ continue;
+ }
+ else
+ {
+ trace::error(_X("Failure processing application bundle."));
+ trace::error(_X("Failed to commit extracted files to directory [%s]."), m_extraction_dir.c_str());
+ throw StatusCode::BundleExtractionFailure;
+ }
+ }
+}
+
+void extractor_t::extract(const manifest_t& manifest, reader_t& reader)
+{
+ begin();
+ for (const file_entry_t& entry : manifest.files) {
+ extract(entry, reader);
+ }
+ commit();
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#ifndef __EXTRACTOR_H__
+#define __EXTRACTOR_H__
+
+#include "reader.h"
+#include "manifest.h"
+
+namespace bundle
+{
+ class extractor_t
+ {
+ public:
+ extractor_t(const pal::string_t &bundle_id, const pal::string_t& bundle_path)
+ :m_extraction_dir(), m_working_extraction_dir()
+ {
+ m_bundle_id = bundle_id;
+ m_bundle_path = bundle_path;
+ }
+
+ pal::string_t& extraction_dir();
+ bool can_reuse_extraction();
+
+ void extract(const manifest_t &manifest, reader_t& reader);
+
+ private:
+ void determine_extraction_dir();
+ void determine_working_extraction_dir();
+
+ FILE* create_extraction_file(const pal::string_t& relative_path);
+
+ void begin();
+ void extract(const file_entry_t& entry, reader_t& reader);
+ void commit();
+
+ pal::string_t m_bundle_id;
+ pal::string_t m_bundle_path;
+ pal::string_t m_extraction_dir;
+ pal::string_t m_working_extraction_dir;
+ };
+}
+
+#endif // __EXTRACTOR_H__
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
-#include "bundle_runner.h"
-#include "pal.h"
-#include "error_codes.h"
+#include "file_entry.h"
#include "trace.h"
-#include "utils.h"
+#include "dir_utils.h"
+#include "error_codes.h"
using namespace bundle;
-bool file_entry_t::is_valid()
+bool file_entry_t::is_valid() const
{
- return m_data.offset > 0 && m_data.size > 0 &&
- static_cast<file_type_t>(m_data.type) < file_type_t::__last;
+ return m_offset > 0 && m_size > 0 &&
+ static_cast<file_type_t>(m_type) < file_type_t::__last;
}
-file_entry_t file_entry_t::read(FILE* stream)
+file_entry_t file_entry_t::read(reader_t &reader)
{
- file_entry_t entry;
-
// First read the fixed-sized portion of file-entry
- bundle_runner_t::read(&entry.m_data, sizeof(entry.m_data), stream);
+ const file_entry_fixed_t* fixed_data = reinterpret_cast<const file_entry_fixed_t*>(reader.read_direct(sizeof(file_entry_fixed_t)));
+ file_entry_t entry(fixed_data);
+
if (!entry.is_valid())
{
trace::error(_X("Failure processing application bundle; possible file corruption."));
throw StatusCode::BundleExtractionFailure;
}
- size_t path_length =
- bundle_runner_t::get_path_length(entry.m_data.path_length_byte_1, stream);
-
- // Read the relative-path, given its length
- pal::string_t& path = entry.m_relative_path;
- bundle_runner_t::read_string(path, path_length, stream);
-
- // Fixup the relative-path to have current platform's directory separator.
- if (bundle_dir_separator != DIR_SEPARATOR)
- {
- for (size_t pos = path.find(bundle_dir_separator);
- pos != pal::string_t::npos;
- pos = path.find(bundle_dir_separator, pos))
- {
- path[pos] = DIR_SEPARATOR;
- }
- }
+ reader.read_path_string(entry.m_relative_path);
+ dir_utils_t::fixup_path_separator(entry.m_relative_path);
return entry;
}
#ifndef __FILE_ENTRY_H__
#define __FILE_ENTRY_H__
-#include <cstdint>
#include "file_type.h"
-#include "pal.h"
+#include "reader.h"
namespace bundle
{
-
// FileEntry: Records information about embedded files.
//
// The bundle manifest records the following meta-data for each
// file embedded in the bundle:
- // Fixed size portion (m_data)
+ // Fixed size portion (file_entry_fixed_t)
// - Offset
// - Size
// - File Entry Type
- // - path-length (7-bit extension encoding, 1 Byte due to MAX_PATH)
// Variable Size portion
- // - relative path ("path-length" Bytes)
+ // - relative path (7-bit extension encoded length prefixed string)
+
+#pragma pack(push, 1)
+ struct file_entry_fixed_t
+ {
+ int64_t offset;
+ int64_t size;
+ file_type_t type;
+ };
+#pragma pack(pop)
class file_entry_t
{
- private:
+ public:
+ file_entry_t()
+ : m_offset(0)
+ , m_size(0)
+ , m_type(bundle::file_type_t::__last)
+ , m_relative_path()
+ {
+ }
- // The inner structure represents the fields that can be
- // read contiguously for every file_entry.
-#pragma pack(push, 1)
- struct
+ file_entry_t(const file_entry_fixed_t *fixed_data)
+ :m_relative_path()
{
- int64_t offset;
- int64_t size;
- file_type_t type;
- int8_t path_length_byte_1;
- } m_data;
-#pragma pack(pop)
+ // File_entries in the bundle-manifest are expected to be used
+ // beyond startup (for loading files directly from bundle, lazy extraction, etc.).
+ // The contents of fixed_data are copied on to file_entry in order to
+ // avoid memory mapped IO later.
- pal::string_t m_relative_path; // Path of an embedded file, relative to the extraction directory.
+ m_offset = fixed_data->offset;
+ m_size = fixed_data->size;
+ m_type = fixed_data->type;
+ }
- public:
- const pal::string_t& relative_path() const { return m_relative_path; }
- int64_t offset() const { return m_data.offset; }
- int64_t size() const { return m_data.size; }
- file_type_t type() const { return m_data.type; }
+ const pal::string_t relative_path() const { return m_relative_path; }
+ int64_t offset() const { return m_offset; }
+ int64_t size() const { return m_size; }
+ file_type_t type() const { return m_type; }
- static file_entry_t read(FILE* stream);
+ static file_entry_t read(reader_t &reader);
private:
- static const pal::char_t bundle_dir_separator = '/';
- bool is_valid();
+ int64_t m_offset;
+ int64_t m_size;
+ file_type_t m_type;
+ pal::string_t m_relative_path; // Path of an embedded file, relative to the extraction directory.
+ bool is_valid() const;
};
}
#endif // __FILE_ENTRY_H__
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
-#include "bundle_runner.h"
+#include "header.h"
+#include "reader.h"
#include "error_codes.h"
#include "trace.h"
-#include "utils.h"
using namespace bundle;
-bool header_t::is_valid()
+bool header_fixed_t::is_valid() const
{
- return m_data.num_embedded_files > 0 &&
- ((m_data.major_version < current_major_version) ||
- (m_data.major_version == current_major_version && m_data.minor_version <= current_minor_version));
+ return num_embedded_files > 0 &&
+ ((major_version < header_t::major_version) ||
+ (major_version == header_t::major_version && minor_version <= header_t::minor_version));
}
-header_t header_t::read(FILE* stream)
+header_t header_t::read(reader_t& reader)
{
- header_t header;
+ const header_fixed_t* fixed_header = reinterpret_cast<const header_fixed_t*>(reader.read_direct(sizeof(header_fixed_t)));
- // First read the fixed size portion of the header
- bundle_runner_t::read(&header.m_data, sizeof(header.m_data), stream);
- if (!header.is_valid())
+ if (!fixed_header->is_valid())
{
trace::error(_X("Failure processing application bundle."));
- trace::error(_X("Bundle header version compatibility check failed"));
+ trace::error(_X("Bundle header version compatibility check failed."));
throw StatusCode::BundleExtractionFailure;
}
+ header_t header(fixed_header->num_embedded_files);
+
// bundle_id is a component of the extraction path
- size_t bundle_id_length =
- bundle_runner_t::get_path_length(header.m_data.bundle_id_length_byte_1, stream);
-
- // Next read the bundle-ID string, given its length
- bundle_runner_t::read_string(header.m_bundle_id, bundle_id_length, stream);
+ reader.read_path_string(header.m_bundle_id);
return header;
}
#include <cstdint>
#include "pal.h"
+#include "reader.h"
namespace bundle
{
// The Bundle Header contains:
- // Fixed size thunk (m_data)
+ // Fixed size thunk (header_fixed_t)
// - Major Version
// - Minor Version
// - Number of embedded files
- // - Bundle ID length
// Variable size portion:
- // - Bundle ID ("Bundle ID length" bytes)
+ // - Bundle ID (7-bit extension encoded length prefixed string)
+
+#pragma pack(push, 1)
+ struct header_fixed_t
+ {
+ public:
+ uint32_t major_version;
+ uint32_t minor_version;
+ int32_t num_embedded_files;
+
+ bool is_valid() const;
+ };
+#pragma pack(pop)
struct header_t
{
public:
- bool is_valid();
- static header_t read(FILE* stream);
+ header_t(int32_t num_embedded_files = 0)
+ : m_num_embedded_files(num_embedded_files)
+ , m_bundle_id()
+ {
+ }
+
+ static header_t read(reader_t& reader);
const pal::string_t& bundle_id() { return m_bundle_id; }
- int32_t num_embedded_files() { return m_data.num_embedded_files; }
+ int32_t num_embedded_files() { return m_num_embedded_files; }
+
+ static const uint32_t major_version = 1;
+ static const uint32_t minor_version = 0;
private:
-#pragma pack(push, 1)
- struct
- {
- uint32_t major_version;
- uint32_t minor_version;
- int32_t num_embedded_files;
- int8_t bundle_id_length_byte_1;
- } m_data;
-#pragma pack(pop)
+ int32_t m_num_embedded_files;
pal::string_t m_bundle_id;
- static const uint32_t current_major_version = 1;
- static const uint32_t current_minor_version = 0;
};
}
#endif // __HEADER_H__
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
-#include "bundle_runner.h"
-#include "pal.h"
-#include "error_codes.h"
-#include "trace.h"
-#include "utils.h"
+#include "manifest.h"
using namespace bundle;
-manifest_t manifest_t::read(FILE* stream, int32_t num_files)
+manifest_t manifest_t::read(reader_t& reader, int32_t num_files)
{
manifest_t manifest;
for (int32_t i = 0; i < num_files; i++)
{
- manifest.files.emplace_back(file_entry_t::read(stream));
+ manifest.files.emplace_back(file_entry_t::read(reader));
}
return manifest;
#ifndef __MANIFEST_H__
#define __MANIFEST_H__
-#include <cstdint>
#include <list>
#include "file_entry.h"
public:
std::vector<file_entry_t> files;
- static manifest_t read(FILE* host, int32_t num_files);
+ static manifest_t read(reader_t &reader, int32_t num_files);
};
}
#endif // __MANIFEST_H__
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#include "reader.h"
+#include "error_codes.h"
+#include "trace.h"
+
+using namespace bundle;
+
+const int8_t* reader_t::add_without_overflow(const int8_t* ptr, int64_t len)
+{
+ const int8_t* new_ptr = ptr + len;
+
+ // The following check will fail in case len < 0 (which is also an error while reading)
+ // even if the actual arthmetic didn't overflow.
+ if (new_ptr < ptr)
+ {
+ trace::error(_X("Failure processing application bundle; possible file corruption."));
+ trace::error(_X("Arithmetic overflow computing bundle-bounds."));
+ throw StatusCode::BundleExtractionFailure;
+ }
+
+ return new_ptr;
+}
+
+void reader_t::set_offset(int64_t offset)
+{
+ if (offset < 0 || offset >= m_bound)
+ {
+ trace::error(_X("Failure processing application bundle; possible file corruption."));
+ trace::error(_X("Arithmetic overflow while reading bundle."));
+ throw StatusCode::BundleExtractionFailure;
+ }
+
+ m_ptr = m_base_ptr + offset;
+}
+
+void reader_t::bounds_check(int64_t len)
+{
+ const int8_t* post_read_ptr = add_without_overflow(m_ptr, len);
+
+ // It is legal for post_read_ptr == m_bound_ptr after reading the last byte.
+ if (m_ptr < m_base_ptr || post_read_ptr > m_bound_ptr)
+ {
+ trace::error(_X("Failure processing application bundle; possible file corruption."));
+ trace::error(_X("Bounds check failed while reading the bundle."));
+ throw StatusCode::BundleExtractionFailure;
+ }
+}
+
+// Handle the relatively uncommon scenario where the bundle ID or
+// the relative-path of a file within the bundle is longer than 127 bytes
+size_t reader_t::read_path_length()
+{
+ size_t length = 0;
+
+ int8_t first_byte = read();
+
+ // If the high bit is set, it means there are more bytes to read.
+ if ((first_byte & 0x80) == 0)
+ {
+ length = first_byte;
+ }
+ else
+ {
+ int8_t second_byte = read();
+
+ if (second_byte & 0x80)
+ {
+ // There can be no more than two bytes in path_length
+ trace::error(_X("Failure processing application bundle; possible file corruption."));
+ trace::error(_X("Path length encoding read beyond two bytes."));
+
+ throw StatusCode::BundleExtractionFailure;
+ }
+
+ length = (second_byte << 7) | (first_byte & 0x7f);
+ }
+
+ if (length <= 0 || length > PATH_MAX)
+ {
+ trace::error(_X("Failure processing application bundle; possible file corruption."));
+ trace::error(_X("Path length is zero or too long."));
+ throw StatusCode::BundleExtractionFailure;
+ }
+
+ return length;
+}
+
+void reader_t::read_path_string(pal::string_t &str)
+{
+ size_t size = read_path_length();
+ std::unique_ptr<uint8_t[]> buffer{ new uint8_t[size + 1] };
+ read(buffer.get(), size);
+ buffer[size] = 0; // null-terminator
+ pal::clr_palstring(reinterpret_cast<const char*>(buffer.get()), &str);
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#ifndef __READER_H__
+#define __READER_H__
+
+#include <cstdint>
+#include "pal.h"
+
+namespace bundle
+{
+ // Helper class for reading sequentially from the memory-mapped bundle file.
+ struct reader_t
+ {
+ reader_t(const int8_t* base_ptr, int64_t bound)
+ : m_base_ptr(base_ptr)
+ , m_ptr(base_ptr)
+ , m_bound(bound)
+ , m_bound_ptr(add_without_overflow(base_ptr, bound))
+ {
+ }
+
+ public:
+
+ void set_offset(int64_t offset);
+
+ operator const int8_t*() const
+ {
+ return m_ptr;
+ }
+
+ int8_t read()
+ {
+ bounds_check();
+ return *m_ptr++;
+ }
+
+ // Copy len bytes from m_ptr to dest
+ void read(void* dest, int64_t len)
+ {
+ bounds_check(len);
+ memcpy(dest, m_ptr, len);
+ m_ptr += len;
+ }
+
+ // Return a pointer to the requested bytes within the memory-mapped file.
+ // Skip over len bytes.
+ const int8_t* read_direct(int64_t len)
+ {
+ bounds_check(len);
+ const int8_t *ptr = m_ptr;
+ m_ptr += len;
+ return ptr;
+ }
+
+ size_t read_path_length();
+ void read_path_string(pal::string_t &str);
+
+ private:
+
+ void bounds_check(int64_t len = 1);
+ static const int8_t* add_without_overflow(const int8_t* ptr, int64_t len);
+
+ const int8_t* const m_base_ptr;
+ const int8_t* m_ptr;
+ const int64_t m_bound;
+ const int8_t* const m_bound_ptr;
+ };
+}
+
+#endif // __READER_H__
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#include <memory>
+#include "extractor.h"
+#include "runner.h"
+#include "trace.h"
+#include "header.h"
+#include "marker.h"
+#include "manifest.h"
+
+using namespace bundle;
+
+void runner_t::map_host()
+{
+ m_bundle_map = (int8_t *) pal::map_file_readonly(m_bundle_path, m_bundle_length);
+
+ if (m_bundle_map == nullptr)
+ {
+ trace::error(_X("Failure processing application bundle."));
+ trace::error(_X("Couldn't memory map the bundle file for reading."));
+ throw StatusCode::BundleExtractionIOError;
+ }
+}
+
+void runner_t::unmap_host()
+{
+ if (!pal::unmap_file(m_bundle_map, m_bundle_length))
+ {
+ trace::warning(_X("Failed to unmap bundle after extraction."));
+ }
+}
+
+// Current support for executing single-file bundles involves
+// extraction of embedded files to actual files on disk.
+// This method implements the file extraction functionality at startup.
+StatusCode runner_t::extract()
+{
+ try
+ {
+ map_host();
+ reader_t reader(m_bundle_map, m_bundle_length);
+
+ // Read the bundle header
+ reader.set_offset(marker_t::header_offset());
+ header_t header = header_t::read(reader);
+
+ extractor_t extractor(header.bundle_id(), m_bundle_path);
+ m_extraction_dir = extractor.extraction_dir();
+
+ // Determine if embedded files are already extracted, and available for reuse
+ if (extractor.can_reuse_extraction())
+ {
+ return StatusCode::Success;
+ }
+
+ manifest_t manifest = manifest_t::read(reader, header.num_embedded_files());
+
+ extractor.extract(manifest, reader);
+
+ unmap_host();
+
+ return StatusCode::Success;
+ }
+ catch (StatusCode e)
+ {
+ return e;
+ }
+}
+
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#ifndef __RUNNER_H__
+#define __RUNNER_H__
+
+#include "error_codes.h"
+
+namespace bundle
+{
+ class runner_t
+ {
+ public:
+ runner_t(const pal::string_t& bundle_path)
+ : m_bundle_path(bundle_path)
+ , m_bundle_map(nullptr)
+ , m_bundle_length(0)
+ {
+ }
+
+ StatusCode extract();
+
+ pal::string_t extraction_dir()
+ {
+ return m_extraction_dir;
+ }
+
+ private:
+ void map_host();
+ void unmap_host();
+
+ pal::string_t m_bundle_path;
+ pal::string_t m_extraction_dir;
+ int8_t* m_bundle_map;
+ size_t m_bundle_length;
+ };
+}
+
+#endif // __RUNNER_H__
#include <mutex>
#include <sys/stat.h>
#include <sys/types.h>
+#include <sys/mman.h>
#define xerr std::cerr
#define xout std::cout
// temporarily wchar for Windows and char for Unix. Current implementation
// implicitly expects the contents on both Windows and Unix as char and
// converts them to wchar in code for Windows. This line should become:
- // typedef std::basic_ifstream<pal::char_t> ifstream_t.
+ // typedef std::basic_ifstream<char_t> ifstream_t.
typedef std::basic_ifstream<char> ifstream_t;
typedef std::istreambuf_iterator<ifstream_t::char_type> istreambuf_iterator_t;
typedef std::basic_istream<char> istream_t;
inline int strncasecmp(const char_t* str1, const char_t* str2, int len) { return ::_wcsnicmp(str1, str2, len); }
inline size_t strlen(const char_t* str) { return ::wcslen(str); }
- inline FILE * file_open(const pal::string_t& path, const char_t* mode) { return ::_wfopen(path.c_str(), mode); }
+ inline FILE * file_open(const string_t& path, const char_t* mode) { return ::_wfopen(path.c_str(), mode); }
+
inline void file_vprintf(FILE* f, const char_t* format, va_list vl) { ::vfwprintf(f, format, vl); ::fputwc(_X('\n'), f); }
inline void err_fputs(const char_t* message) { ::fputws(message, stderr); ::fputwc(_X('\n'), stderr); }
inline void out_vprintf(const char_t* format, va_list vl) { ::vfwprintf(stdout, format, vl); ::fputwc(_X('\n'), stdout); }
inline int str_vprintf(char_t* buffer, size_t count, const char_t* format, va_list vl) { return ::_vsnwprintf(buffer, count, format, vl); }
- bool pal_utf8string(const pal::string_t& str, std::vector<char>* out);
- bool utf8_palstring(const std::string& str, pal::string_t* out);
- bool pal_clrstring(const pal::string_t& str, std::vector<char>* out);
- bool clr_palstring(const char* cstr, pal::string_t* out);
+ bool pal_utf8string(const string_t& str, std::vector<char>* out);
+ bool utf8_palstring(const std::string& str, string_t* out);
+ bool pal_clrstring(const string_t& str, std::vector<char>* out);
+ bool clr_palstring(const char* cstr, string_t* out);
- inline bool mkdir(const pal::char_t* dir, int mode) { return CreateDirectoryW(dir, NULL) != 0; }
- inline bool rmdir (const pal::char_t* path) { return RemoveDirectoryW(path) != 0; }
- inline int rename(const pal::char_t* old_name, const pal::char_t* new_name) { return ::_wrename(old_name, new_name); }
- inline int remove(const pal::char_t* path) { return ::_wremove(path); }
+ inline bool mkdir(const char_t* dir, int mode) { return CreateDirectoryW(dir, NULL) != 0; }
+ inline bool rmdir (const char_t* path) { return RemoveDirectoryW(path) != 0; }
+ inline int rename(const char_t* old_name, const char_t* new_name) { return ::_wrename(old_name, new_name); }
+ inline int remove(const char_t* path) { return ::_wremove(path); }
+ inline bool unmap_file(void* addr, size_t length) { return UnmapViewOfFile(addr) != 0; }
inline int get_pid() { return GetCurrentProcessId(); }
inline void sleep(uint32_t milliseconds) { Sleep(milliseconds); }
-
#else
#ifdef EXPORT_SHARED_API
#define SHARED_API extern "C" __attribute__((__visibility__("default")))
inline int strncasecmp(const char_t* str1, const char_t* str2, int len) { return ::strncasecmp(str1, str2, len); }
inline size_t strlen(const char_t* str) { return ::strlen(str); }
- inline FILE * file_open(const pal::string_t& path, const char_t* mode) { return fopen(path.c_str(), mode); }
+ inline FILE * file_open(const string_t& path, const char_t* mode) { return fopen(path.c_str(), mode); }
inline void file_vprintf(FILE* f, const char_t* format, va_list vl) { ::vfprintf(f, format, vl); ::fputc('\n', f); }
inline void err_fputs(const char_t* message) { ::fputs(message, stderr); ::fputc(_X('\n'), stderr); }
inline void out_vprintf(const char_t* format, va_list vl) { ::vfprintf(stdout, format, vl); ::fputc('\n', stdout); }
inline int str_vprintf(char_t* str, size_t size, const char_t* format, va_list vl) { return ::vsnprintf(str, size, format, vl); }
- inline bool pal_utf8string(const pal::string_t& str, std::vector<char>* out) { out->assign(str.begin(), str.end()); out->push_back('\0'); return true; }
- inline bool utf8_palstring(const std::string& str, pal::string_t* out) { out->assign(str); return true; }
- inline bool pal_clrstring(const pal::string_t& str, std::vector<char>* out) { return pal_utf8string(str, out); }
- inline bool clr_palstring(const char* cstr, pal::string_t* out) { out->assign(cstr); return true; }
+ inline bool pal_utf8string(const string_t& str, std::vector<char>* out) { out->assign(str.begin(), str.end()); out->push_back('\0'); return true; }
+ inline bool utf8_palstring(const std::string& str, string_t* out) { out->assign(str); return true; }
+ inline bool pal_clrstring(const string_t& str, std::vector<char>* out) { return pal_utf8string(str, out); }
+ inline bool clr_palstring(const char* cstr, string_t* out) { out->assign(cstr); return true; }
- inline bool mkdir(const pal::char_t* dir, int mode) { return ::mkdir(dir, mode) == 0; }
- inline bool rmdir(const pal::char_t* path) { return ::rmdir(path) == 0; }
- inline int rename(const pal::char_t* old_name, const pal::char_t* new_name) { return ::rename(old_name, new_name); }
- inline int remove(const pal::char_t* path) { return ::remove(path); }
+ inline bool mkdir(const char_t* dir, int mode) { return ::mkdir(dir, mode) == 0; }
+ inline bool rmdir(const char_t* path) { return ::rmdir(path) == 0; }
+ inline int rename(const char_t* old_name, const char_t* new_name) { return ::rename(old_name, new_name); }
+ inline int remove(const char_t* path) { return ::remove(path); }
+ inline bool unmap_file(void* addr, size_t length) { return munmap(addr, length) == 0; }
inline int get_pid() { return getpid(); }
inline void sleep(uint32_t milliseconds) { usleep(milliseconds * 1000); }
return ret;
}
- pal::string_t to_string(int value);
- pal::string_t get_timestamp();
+ string_t to_string(int value);
+ string_t get_timestamp();
- bool getcwd(pal::string_t* recv);
- pal::string_t to_lower(const pal::string_t& in);
+ bool getcwd(string_t* recv);
+ string_t to_lower(const string_t& in);
inline void file_flush(FILE *f) { std::fflush(f); }
inline void out_flush() { std::fflush(stdout); }
// Based upon https://github.com/dotnet/core-setup/blob/master/src/Microsoft.DotNet.PlatformAbstractions/Native/PlatformApis.cs
- pal::string_t get_current_os_rid_platform();
- inline pal::string_t get_current_os_fallback_rid()
+ string_t get_current_os_rid_platform();
+ inline string_t get_current_os_fallback_rid()
{
- pal::string_t fallbackRid(FALLBACK_HOST_RID);
+ string_t fallbackRid(FALLBACK_HOST_RID);
return fallbackRid;
}
- bool touch_file(const pal::string_t& path);
+ void* map_file_readonly(const string_t& path, size_t& length);
+ bool touch_file(const string_t& path);
bool realpath(string_t* path, bool skip_error_logging = false);
bool file_exists(const string_t& path);
inline bool directory_exists(const string_t& path) { return file_exists(path); }
- void readdir(const string_t& path, const string_t& pattern, std::vector<pal::string_t>* list);
- void readdir(const string_t& path, std::vector<pal::string_t>* list);
- void readdir_onlydirectories(const string_t& path, const string_t& pattern, std::vector<pal::string_t>* list);
- void readdir_onlydirectories(const string_t& path, std::vector<pal::string_t>* list);
+ void readdir(const string_t& path, const string_t& pattern, std::vector<string_t>* list);
+ void readdir(const string_t& path, std::vector<string_t>* list);
+ void readdir_onlydirectories(const string_t& path, const string_t& pattern, std::vector<string_t>* list);
+ void readdir_onlydirectories(const string_t& path, std::vector<string_t>* list);
bool get_own_executable_path(string_t* recv);
bool get_own_module_path(string_t* recv);
bool get_default_servicing_directory(string_t* recv);
// Returns the globally registered install location (if any)
- bool get_dotnet_self_registered_dir(pal::string_t* recv);
+ bool get_dotnet_self_registered_dir(string_t* recv);
// Returns name of the global registry location (for error messages)
- bool get_dotnet_self_registered_config_location(pal::string_t* recv);
+ bool get_dotnet_self_registered_config_location(string_t* recv);
// Returns the default install location for a given platform
- bool get_default_installation_dir(pal::string_t* recv);
+ bool get_default_installation_dir(string_t* recv);
// Returns the global locations to search for SDK/Frameworks - used when multi-level lookup is enabled
- bool get_global_dotnet_dirs(std::vector<pal::string_t>* recv);
+ bool get_global_dotnet_dirs(std::vector<string_t>* recv);
bool get_default_breadcrumb_store(string_t* recv);
bool is_path_rooted(const string_t& path);
- bool get_temp_directory(pal::string_t& tmp_dir);
+ bool get_temp_directory(string_t& tmp_dir);
int xtoi(const char_t* input);
- bool get_loaded_library(const char_t *library_name, const char *symbol_name, /*out*/ dll_t *dll, /*out*/ pal::string_t *path);
+ bool get_loaded_library(const char_t *library_name, const char *symbol_name, /*out*/ dll_t *dll, /*out*/ string_t *path);
bool load_library(const string_t* path, dll_t* dll);
proc_t get_symbol(dll_t library, const char* name);
void unload_library(dll_t library);
return true;
}
+void* pal::map_file_readonly(const pal::string_t& path, size_t& length)
+{
+ int fd = open(path.c_str(), O_RDONLY, (S_IRUSR | S_IRGRP | S_IROTH));
+ if (fd == -1)
+ {
+ trace::warning(_X("Failed to map file. open(%s) failed with error %d"), path.c_str(), errno);
+ return nullptr;
+ }
+
+ struct stat buf;
+ if (fstat(fd, &buf) != 0)
+ {
+ trace::warning(_X("Failed to map file. fstat(%s) failed with error %d"), path.c_str(), errno);
+ close(fd);
+ return nullptr;
+ }
+
+ length = buf.st_size;
+ void* address = mmap(nullptr, length, PROT_READ, MAP_SHARED, fd, 0);
+
+ if(address == nullptr)
+ {
+ trace::warning(_X("Failed to map file. mmap(%s) failed with error %d"), path.c_str(), errno);
+ close(fd);
+ return nullptr;
+ }
+
+ close(fd);
+ return address;
+}
+
bool pal::getcwd(pal::string_t* recv)
{
recv->clear();
return true;
}
+void* pal::map_file_readonly(const pal::string_t& path, size_t &length)
+{
+ HANDLE file = CreateFileW(path.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+
+ if (file == INVALID_HANDLE_VALUE)
+ {
+ trace::warning(_X("Failed to map file. CreateFileW(%s) failed with error %d"), path.c_str(), GetLastError());
+ return nullptr;
+ }
+
+ LARGE_INTEGER fileSize;
+ if (GetFileSizeEx(file, &fileSize) == 0)
+ {
+ trace::warning(_X("Failed to map file. GetFileSizeEx(%s) failed with error %d"), path.c_str(), GetLastError());
+ CloseHandle(file);
+ return nullptr;
+ }
+ length = (size_t)fileSize.QuadPart;
+
+ HANDLE map = CreateFileMappingW(file, NULL, PAGE_READONLY, 0, 0, NULL);
+
+ if (map == NULL)
+ {
+ trace::warning(_X("Failed to map file. CreateFileMappingW(%s) failed with error %d"), path.c_str(), GetLastError());
+ CloseHandle(file);
+ return nullptr;
+ }
+
+ void *address = MapViewOfFile(map, FILE_MAP_READ, 0, 0, 0);
+
+ if (map == NULL)
+ {
+ trace::warning(_X("Failed to map file. MapViewOfFile(%s) failed with error %d"), path.c_str(), GetLastError());
+ CloseHandle(file);
+ return nullptr;
+ }
+
+ return address;
+}
+
bool pal::getcwd(pal::string_t* recv)
{
recv->clear();
#include "utils.h"
#if defined(FEATURE_APPHOST)
-#include "cli/apphost/bundle/bundle_runner.h"
#include "cli/apphost/bundle/marker.h"
+#include "cli/apphost/bundle/runner.h"
#define CURHOST_TYPE _X("apphost")
#define CUREXE_PKG_VER COMMON_HOST_PKG_VER
if (bundle::marker_t::is_bundle())
{
- bundle::bundle_runner_t extractor(host_path);
- StatusCode bundle_status = extractor.extract();
+ bundle::runner_t bundle_runner(host_path);
+ StatusCode bundle_status = bundle_runner.extract();
if (bundle_status != StatusCode::Success)
{
return bundle_status;
}
- app_path.assign(extractor.get_extraction_dir());
+ app_path.assign(bundle_runner.extraction_dir());
}
else
{