kvm: trim whitespace around backup repository mount options on restore - #14096
kvm: trim whitespace around backup repository mount options on restore#14096stag7824 wants to merge 1 commit into
Conversation
A repository saved with mount options such as "vers=4.1 " backs up but fails to restore. mountBackupDirectory builds the mount command as an argv list and runs it without a shell, so ProcessBuilder hands the option list to mount verbatim. libmount splits that list on commas only, so the blank stays glued to the last option and the kernel rejects it as part of the option value. The other backup operations pass the options to nasbackup.sh, which expands MOUNT_OPTS unquoted, so shell word splitting drops the blank there. Restore is the only one of the four that builds the mount command in Java. Trim each comma-separated option and drop the empty ones before the list is used. Only the whitespace around the delimiters is removed; validating the option text belongs to the API layer. The normalisation runs before the cifs branch appends nobrl, otherwise the blank would be moved into the middle of the list rather than removed. Fixes apache#14013
|
Congratulations on your first Pull Request and welcome to the Apache CloudStack community! If you have any issues or are unsure about any anything please check our Contribution Guide (https://github.com/apache/cloudstack/blob/main/CONTRIBUTING.md)
|
|
@stag7824 |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## 4.22 #14096 +/- ##
============================================
- Coverage 17.86% 17.86% -0.01%
- Complexity 16037 16040 +3
============================================
Files 5928 5928
Lines 534479 534490 +11
Branches 65410 65412 +2
============================================
- Hits 95468 95460 -8
- Misses 428173 428199 +26
+ Partials 10838 10831 -7
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
Not on a full CloudStack zone, no — I don't have a KVM host with a NAS backup repository, and I didn't want to imply otherwise in the description. What I could do is exercise the real code path against a real CIFS server, with nothing mocked. I ran
The control row is there to show the harness isn't just reporting success, and that the change is inert for well-formed options. The command that the unfixed path actually hands to mount, captured from the process table: which is the case in the description — Worth flagging separately, because it changes how this looks in the field: What I still can't cover locally is the full path: management server, agent, a real backup and restore of a VM volume. If that's needed before this can go in, could someone with a NAS repository confirm it? Happy to adjust the approach — including moving the normalisation elsewhere — if you'd rather it not live in the agent. |
Description
Fixes #14013
A backup repository whose mount options carry stray whitespace, for example
vers=4.1,backs up fine but fails to restore:
LibvirtRestoreBackupCommandWrapper.mountBackupDirectorybuilds the mount command as an argvlist and runs it without a shell:
Script.executeCommand(String...)hands each element toProcessBuilderverbatim, so mountreceives the literal option list
vers=4.1. libmount splits that list on commas only, so theblank stays glued to the last option and the kernel rejects it as part of the option value.
Backup, delete and stats do not hit this today because they pass the options to
nasbackup.sh,which expands them unquoted:
Shell word splitting drops the blank there, which is why restore is the only operation that
fails: it is the only one of the four that builds the mount command in Java.
Fixed by normalising the option list before it is used. Each comma-separated option is trimmed
and empty ones are dropped. Only the whitespace around the delimiters is removed; the option
text itself is passed through unchanged, since deciding whether an option is well formed
belongs to the API layer rather than the agent.
The normalisation happens before the cifs branch appends
,nobrl. Trimming afterwardswould leave
vers=3.0 ,nobrl, moving the blank into the middle of the list where libmountcannot ignore it either.
Relationship to #14009
#14009 stops this class of value from being stored: it rejects whitespace per option in
AddBackupRepositoryCmd/UpdateBackupRepositoryCmd, and it quotes"${MOUNT_OPTS}"innasbackup.sh. That is the right place for validation and I have deliberately not duplicatedany of it here — the two changes touch no common files.
They are complementary rather than alternatives, because #14009 validates on the way in and
cannot clean rows that are already in the database. On an existing deployment such as the one
in #14013, the stored
vers=4.1survives, and onceMOUNT_OPTSis quoted it will break thescript-based operations too, not just restore. Normalising at the point of use fixes those
repositories without a schema migration.
Types of changes
Feature/Enhancement Scale or Bug Severity
Bug Severity
How Has This Been Tested?
Added seven unit tests to
LibvirtRestoreBackupCommandWrapperTestthat invokemountBackupDirectoryand assert on the exact-oargument handed tomount:-oargumentvers=4.1vers=4.1vers=4.1 , softvers=4.1,softvers=4.1,,softvers=4.1,softvers=3.0vers=3.0,nobrlusername=some userusername=some user,nobrl-oat allnobrlThe last three already hold today. They are there to pin behaviour the fix must not change:
that trimming is scoped to the delimiters rather than stripping whitespace throughout, and
that an all-whitespace value still collapses to no options at all (or to bare
nobrlforcifs) instead of becoming an empty option.
Removing just the
normalizeMountOptionscall and rerunning gives 4 failures, each showingthe blank that mount rejects:
The last of those is the ordering point: the blank lands in the middle of the list once
nobrlis appended after it.I do not have a KVM zone with a NAS repository available, so this is unit-level only; the
mount argument that the kernel rejects is asserted directly rather than end to end.
The same construction is present on 4.20. I targeted 4.22 because that is the version the
issue is reported against, but happy to retarget if it should land on 4.20 first.