-
Notifications
You must be signed in to change notification settings - Fork 654
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
Fix symlink resolution when mounting #3673
base: main
Are you sure you want to change the base?
Conversation
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## main #3673 +/- ##
=======================================
Coverage 88.87% 88.87%
=======================================
Files 256 256
Lines 14579 14581 +2
=======================================
+ Hits 12957 12959 +2
Misses 1622 1622 ☔ View full report in Codecov by Sentry. |
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.
Thank you Trevor! Superficial review from my side, but it looks like it's headed in the right direction. I have a couple of comments inline.
src/daemon/daemon.cpp
Outdated
? std::make_unique<SSHFSMountHandler>(vm, config->ssh_key_provider.get(), target, mount) | ||
: vm->make_native_mount_handler(target, mount); | ||
? std::make_unique<SSHFSMountHandler>(vm, config->ssh_key_provider.get(), target, std::move(mount)) | ||
: vm->make_native_mount_handler(target, std::move(mount)); |
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.
This one doesn't help much unless you change make_native_mount_handler
to take by value and move too. Otherwise you're asking for one extra temporary.
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.
Hey Trevor, this works well for what it does and the code looks good. However, currently it covers only paths ending in a symlink. I guess we should cover symlinks anywhere in the path, don't you think?
ricab@ricab-XPS-9320:~/tmp/asdf $ ln -s .. l4
ricab@ricab-XPS-9320:~/tmp/asdf $ cd
ricab@ricab-XPS-9320:~ $ mp mount tmp/asdf/l4/subl-mdpreview/ amazed-topi:l4
ricab@ricab-XPS-9320:~ $ mp info amazed-topi
Name: amazed-topi
State: Running
Snapshots: 2
IPv4: 10.251.95.73
Release: Ubuntu 24.04.1 LTS
Image hash: 78547d336e4c (Ubuntu 24.04 LTS)
CPU(s): 2
Load: 0.00 0.00 0.00
Disk usage: 2.0GiB out of 4.8GiB
Memory usage: 342.3MiB out of 955.7MiB
Mounts: /home/ricab/tmp/asdf/l4/subl-mdpreview => l4
UID map: 1000:default
GID map: 1000:default
I expected the source to become just /home/ricab/tmp/subl-mdpreview
src/utils/vm_mount.cpp
Outdated
|
||
if (err) | ||
throw std::runtime_error( | ||
fmt::format("Mount symlink source path \"{}\" could not be made weakly canonical: {}.", |
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.
This would end up being shown to the user, right? If so, I would try to make it more user friendly. Perhaps:
fmt::format("Mount symlink source path \"{}\" could not be made weakly canonical: {}.", | |
fmt::format("Could not resolve symlinks in mount source path \"{}\": {}.", |
Hey Trevor, a quick experiment I did: #include <filesystem>
#include <iostream>
using namespace std;
namespace fs = std::filesystem;
int main()
{
// l4 -> ..
// l2 -> ../subl-mdpreview
auto src_path = "/home/ricab/tmp/asdf/l4/asdf/l4/asdf/l2/";
error_code ec{};
auto dst_path = fs::weakly_canonical(src_path, ec);
if (!ec)
cout << "\"" << src_path << "\" == " << dst_path << endl;
// prints "/home/ricab/tmp/asdf/l4/asdf/l4/asdf/l2/" == "/home/ricab/tmp/subl-mdpreview"
return bool(ec);
}
|
9b71479
to
ccac3af
Compare
I wonder if we should check elsewhere in the repo for usage of |
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.
Thank you @Sploder12! I would like for someone to do a primary review on this, but I am approving on secondary.
We ended up considering this moot, correct @Sploder12 ? |
There was one issue that came from it #3759, but no real impact. |
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.
@Sploder12 , thanks for the good work. It looks very good in general. I just have a few minor comments and questions. The functional testing went also fine.
tests/test_daemon_mount.cpp
Outdated
|
||
TEST_F(TestDaemonMount, symlinkSourceGetsResolved) | ||
{ | ||
const auto [temp_dir, filename] = plant_instance_json(fake_json_contents(mac_addr, extra_interfaces)); |
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.
filename field can be set to _
, right?
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.
_
is used later on line 277 so it'd cause a redefinition error. I have this one named over the other since the filename
might be useful, but the mock guard shouldn't be touched.
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.
In such case, I sometimes name it along the lines of ignored_filename
.
mp::Daemon daemon{config_builder.build()}; | ||
|
||
mp::MountRequest request; | ||
request.set_source_path(mount_dir.path().toStdString()); |
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.
Not sure I fully get the purpose of this unit test, mount_dir.path()
is a normal temp directory without dot, dot-dot, or symbolic, right? So we are not really testing resolving as far as I can tell.
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 point, this test isn't clear in what it's doing. The test does not test that resolution works, but rather that weakly_canonical
is called when making a mount request. But it also doesn't verify that the value from weakly_canonical
is actually used so it is a terrible test. Good catch!
" \"source_path\": \"{}\",\n" | ||
" \"target_path\": \"{}\",\n", | ||
" \"source_path\": {:?},\n" | ||
" \"target_path\": {:?},\n", |
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.
Two questions, here.
- What is the point of using debug specifier here if we are just injecting two
std::strings
and there is no debug or normal display implementation? - I am not sure {:?} is supported in c++
fmt::format
as well as it is in rust, do you know any official documentation where it introduces this feature.
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.
- Without the debug specifier the JSON on Windows will contain
"C:\some\path\"
, since the\
is not escaped when we parse the JSON the"
gets escaped and things break. - Yep, their syntax documentation describes the purpose of
'?'
, which is "Debug format. The string is quoted and special characters escaped." So we get"C:\\some\\path\\"
instead which gets parsed correctly.
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 see, so the difference between {:?} and {} on a std::string is just the quotes and escapes. {:?} always gives the raw and quoted string, which is definitely more robust.
@@ -521,7 +521,7 @@ TEST_F(TestBaseSnapshot, adoptsMountsFromJson) | |||
const auto [snapshot_mnt_dst, snapshot_mount] = *snapshot_mounts.begin(); | |||
|
|||
EXPECT_EQ(snapshot_mnt_dst, dst_path); | |||
EXPECT_EQ(snapshot_mount.get_source_path(), src_path); | |||
EXPECT_EQ(snapshot_mount.get_source_path(), mp::fs::weakly_canonical(src_path)); |
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.
src_path
does not seem to have anything to resolve, is this for future proof meaning that if src_path becomes resolvable it can still match the snapshot_mount.get_source_path()
?
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 don't remember, but my assumption is that my past self did so to fix a test failing on Windows. But maybe not.
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.
Huh, turns out it was for the test failing on MacOS.
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.
Approving on secondary again. Please don't wait for me to merge once the primary review is settled.
EXPECT_CALL(*mock_factory, create_virtual_machine).WillOnce(Return(std::move(mock_vm))); | ||
|
||
// setup to make the daemon happy | ||
MP_DELEGATE_MOCK_CALLS_ON_BASE(*mock_file_ops, mkpath, FileOps); |
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.
Nice macro usage, better than the boilerplate code before.
@@ -267,3 +268,51 @@ TEST_F(TestDaemonMount, performanceMountsNotImplementedHasErrorFails) | |||
EXPECT_EQ(status.error_code(), grpc::StatusCode::FAILED_PRECONDITION); | |||
EXPECT_THAT(status.error_message(), StrEq("The native mounts feature is not implemented on this backend.")); | |||
} | |||
|
|||
TEST_F(TestDaemonMount, mount_uses_resolved_source) | |||
{ |
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.
The new unit test name is definitely more expressive.
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 looks good to me now, thanks for addressing all the comments. Maybe do a rebase and we can get ready to merge.
cd722f5
to
48b8bf0
Compare
48b8bf0
to
d8bb07d
Compare
fixes #1722
Symbolic links are now resolved when performing a mount.
Previously they were not resolved.
MULTI-1549