-
Notifications
You must be signed in to change notification settings - Fork 219
Enable singing of single-file bundles #6885
Conversation
|
||
// Copy apphost to destination path so it inherits the same attributes/permissions. | ||
File.Copy(appHostSourceFilePath, appHostDestinationFilePath, overwrite: true); | ||
bundleHeaderOffset = accessor.ReadInt64(position - sizeof(Int64)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps this method should return false if bundleHeaderOffset
is zero, like marker_t::is_bundle()
does.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@AustinWise, change is in this commit: 75f1926
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, that should make for a more clear error message.
void bundle_runner_t::process_manifest_header(int64_t header_offset) | ||
{ | ||
seek(m_bundle_stream, header_offset, SEEK_SET); | ||
seek(m_bundle_stream, marker_t::header_offset(), SEEK_SET); | ||
|
||
manifest_header_t* header = manifest_header_t::read(m_bundle_stream); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since header
is not stored anywhere, and it's allocated in manifest_header_t
, the memory leaks. It might be a better idea to allocate on the stack here and have manifest_header_t::read()
take a point to it as a parameter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
} | ||
process_manifest_header(manifest_header_offset); | ||
// Read the bundle header | ||
seek(m_bundle_stream, marker_t::header_offset(), SEEK_SET); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the file is corrupt this might fail (the header offset might point beyond the end of the file) - I know that we'll catch it later on by validating the header itself, but I think for code cleanness it would be better to check the failure here as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's often a good idea to replace seek()
+read()
with pread()
if supported by the system. This way, if reading with an invalid offset, the pread()
call will fail. This is the sort of thing that the PAL could do.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@vitek-karas the seek()
function above already checks for failure, and throws the bundleIOerror exception. This is bundle_runner_t::seek
which does fseek
and the error check.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@lpereira I agree that the PAL can be improved by adding pread()
.
But I think this code will soon change to use memory-mapping of the whole bundle file instead of seek-read-write, etc. So, I left it as-is for now.
#pragma pack(pop) | ||
pal::string_t m_bundle_id; | ||
|
||
static const uint32_t m_current_major_version = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since we're about to ship this - should we maybe use version 1.0? - it's not an experimental feature per se.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If it's static, should it be prefixed with m_
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No private statics should be s_
, but this field can just be a const; so I'll remove the annotations.
|
||
// 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); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I find this a bit weird - naming wise - bundle_id
itself is not a path... so using methods like get_path_length
doesn't feel right. Maybe we should rename the method to simply get_string_length
or something similar. Especially since we have read_string
already.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
bundle_id
is actually a part of a path (that is, it should be path-compatible, and conform to length limitation). The get_path_length
method only reads two bytes in a length encoding (because of MAX_PATH
) restriction. So, that's why I had called it get_path_length
instead of get_string_length
I can rewrite the function more generically, call it read_string_length
and do the MAX_PATH
check elsewhere -- but it doesn't serve much purpose other than the symmetry with read_string()
function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK - it was just a nit anyway... :-)
@@ -50,10 +52,9 @@ namespace bundle | |||
void extract_file(file_entry_t* entry); | |||
|
|||
FILE* m_bundle_stream; | |||
header_t* m_header; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is destroying m_header
? The memory leak is still here. A few options, in order from the most preferred to the least preferred:
- Declare this inline as
header_t m_header
. No need to allocate a new struct from the heap. Goes away as soon asbundle_runner_t
goes away. - Make this a
std::unique_ptr<header_t>
instead. - Have a
delete m_header
inbundle_runner_t::~bundle_runner_t()
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added a destructor to the bundle_runner_t
.
I did not inline the manifest, because when files are directly processed from the bundle, the manifest will survive beyond the bundle_runner
object.
I also wanted to keep the interface of header_t
similar to manifest_t
and the managed code in HostModel
. Thanks.
This change removes the requirement that the bundle-signature be found at the end of the file, so that tools like signtool can append their custom information at the end of the file. The layout of .net core bundles was AppHost Code Bundled Files Manifest header Manifest entries Bundle footer <EOF> The bundle-footer is now removed, its functionality is served by a special global in apphost (Bundle-Placeholder) similar to the way APP-DLL name is encoded in the AppHost. The normal unbundled apphost has a default value for the bundle-placeholder. If the apphost is a single-file bundle, the bundler tool (in HostModel) updates the value of the variable on disk to a different value, and encodes the location of the manifest header in it. This way, there is no requirement that the bundle meta-data must remain at the end of the file. This change also simplifies the test for whether an apphost is a bundle: we only check a value in the loaded binary, rather than performing file reads.
Changes a storage class so that constants are held without warning.
Store bundle_header* in bundle_runner so that it is destructed after bundle processing (and eliminates redundant storage of bundle_header contents in bundle_runner).
To fix a build error on linux.
Leave an error code unused (rather than rewriting) to fix a failure in the lab jobs Also fix a spacing
The static placeholder array was optimized out in OSX release builds. This caused tests in release builds to fail with PlaceholderNotFound exception, whereas the debug builds passed.
The win-x64 debug failure says |
Disable this test since it is causing other tests to fail because of System.IO.IOException : There is not enough space on the disk This looks like an infrastructure issue, which I will follow up.
I hit the |
Thanks @dagood ; I'll try that (in a separate change). |
The Build_Windows_x64 debug leg passed with the test disabled. So, I'll merge the change, so that the fix is available in the upcoming preview. |
The above test was disabled in PR dotnet#6885 because the lab runs showed failures because of "not enough disk space" This PR tries to enable the test, and move the job to not use the hosted pool, as suggested [here](dotnet#6885 (comment)).
The above test was disabled in PR dotnet#6885 because the lab runs showed failures because of "not enough disk space" This PR now enables the test, since the job no longer uses the hosted pool.
The above test was disabled in PR dotnet#6885 because the lab runs showed failures because of "not enough disk space" This PR now enables the test, since the test job no longer uses the hosted pool.
The above test was disabled in PR #6885 because the lab runs showed failures because of "not enough disk space" This PR now enables the test, since the test job no longer uses the hosted pool.
int64_t marker_t::header_offset() | ||
{ | ||
// Contains the bundle_placeholder default value at compile time. | ||
// If this is a single-file bundle, the last 8 bytes are replaced |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe it is the first 8 bytes that are replaced?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch - thanks a lot!
dotnet/runtime#44874
* Enable singing of bundles This change removes the requirement that the bundle-signature be found at the end of the file, so that tools like signtool can append their custom information at the end of the file. The layout of .net core bundles was AppHost Code Bundled Files Manifest header Manifest entries Bundle footer <EOF> The bundle-footer is now removed, its functionality is served by a special global in apphost (Bundle-Placeholder) similar to the way APP-DLL name is encoded in the AppHost. The normal unbundled apphost has a default value for the bundle-placeholder. If the apphost is a single-file bundle, the bundler tool (in HostModel) updates the value of the variable on disk to a different value, and encodes the location of the manifest header in it. This way, there is no requirement that the bundle meta-data must remain at the end of the file. This change also simplifies the test for whether an apphost is a bundle: we only check a value in the loaded binary, rather than performing file reads. An additional change in this commit is that the Bundle version is changed from 0.1 to 1.0 This change adds a test case: TestWithAdditionalContentAfterBundleMetadata However, this caused a different test to fail in the lab because the Disable this test since it is causing other tests to fail because the machines ran out of disk space copying the test assets. So, this test is temporarily disabled, until the infrastructure issue is resolved (likely by not using the hosted-pool for testing). Commit migrated from dotnet/core-setup@19b99b3
…/core-setup#6968) The above test was disabled in PR dotnet/core-setup#6885 because the lab runs showed failures because of "not enough disk space" This PR now enables the test, since the test job no longer uses the hosted pool. Commit migrated from dotnet/core-setup@b44e5a2
This change removes the requirement that the bundle-signature
be found at the end of the file, so that tools like signtool
can append their custom information at the end of the file.
The layout of .net core bundles was
AppHost Code
Bundled Files
Manifest header
Manifest entries
Bundle footer
The bundle-footer is now removed, its functionality is served by a
special global in apphost (Bundle-Placeholder) similar to the way
APP-DLL name is encoded in the AppHost.
The normal unbundled apphost has a default value for the bundle-placeholder.
If the apphost is a single-file bundle, the bundler tool (in HostModel) updates
the value of the variable on disk to a different value, and encodes the location
of the manifest header in it.
This way, there is no requirement that the bundle meta-data must remain at
the end of the file.
This change also simplifies the test for whether an apphost is a bundle:
we only check a value in the loaded binary, rather than performing file reads.