Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions localization/strings/en-US/Resources.resw
Original file line number Diff line number Diff line change
Expand Up @@ -3100,6 +3100,24 @@ On first run, creates the file with all settings commented out at their defaults
<data name="WSLCCLI_VolumeListLongDesc" xml:space="preserve">
<value>Lists all volumes in the session.</value>
</data>
<data name="WSLCCLI_VolumePruneDesc" xml:space="preserve">
<value>Remove unused local volumes.</value>
</data>
<data name="WSLCCLI_VolumePruneLongDesc" xml:space="preserve">
<value>Removes all unused anonymous local volumes. If --all is specified, also removes unused named volumes. A volume is considered unused when it is not referenced by any container.</value>
<comment>{Locked="--all "}Command line arguments, file names and string inserts should not be translated</comment>
</data>
<data name="WSLCCLI_VolumePruneAllArgDescription" xml:space="preserve">
<value>Remove all unused volumes, not just anonymous ones.</value>
</data>
<data name="WSLCCLI_VolumePruneDeleted" xml:space="preserve">
<value>Deleted: {}</value>
<comment>{FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated</comment>
</data>
<data name="WSLCCLI_VolumePruneSpaceReclaimed" xml:space="preserve">
<value>Total reclaimed space: {}</value>
<comment>{FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated</comment>
</data>
<data name="WSLCCLI_VolumeNameArgDescription" xml:space="preserve">
<value>Volume name</value>
</data>
Expand Down
16 changes: 12 additions & 4 deletions src/windows/wslc/arguments/ArgumentValidation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -182,10 +182,7 @@ void ValidateFilter(const std::vector<std::wstring>& values)
{
for (const auto& value : values)
{
if (value.find(L'=') == std::wstring::npos)
{
throw ArgumentException(Localization::WSLCCLI_InvalidFilterError(value));
}
std::ignore = ParseFilter(value);
}
}

Expand Down Expand Up @@ -422,4 +419,15 @@ std::pair<std::string, std::string> ParseDriverOption(const std::wstring& value)
return {WideToMultiByte(value.substr(0, pos)), WideToMultiByte(value.substr(pos + 1))};
}

std::pair<std::string, std::string> ParseFilter(const std::wstring& value)
{
auto pos = value.find(L'=');
if (pos == std::wstring::npos)
{
throw ArgumentException(Localization::WSLCCLI_InvalidFilterError(value));
}

return {WideToMultiByte(value.substr(0, pos)), WideToMultiByte(value.substr(pos + 1))};
}

} // namespace wsl::windows::wslc::validation
1 change: 1 addition & 0 deletions src/windows/wslc/arguments/ArgumentValidation.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,5 +83,6 @@ void ValidateFilter(const std::vector<std::wstring>& values);

std::pair<std::string, std::string> ParseLabel(const std::wstring& value);
std::pair<std::string, std::string> ParseDriverOption(const std::wstring& value);
std::pair<std::string, std::string> ParseFilter(const std::wstring& value);

} // namespace wsl::windows::wslc::validation
1 change: 1 addition & 0 deletions src/windows/wslc/commands/VolumeCommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ std::vector<std::unique_ptr<Command>> VolumeCommand::GetCommands() const
commands.push_back(std::make_unique<VolumeRemoveCommand>(FullName()));
commands.push_back(std::make_unique<VolumeInspectCommand>(FullName()));
commands.push_back(std::make_unique<VolumeListCommand>(FullName()));
commands.push_back(std::make_unique<VolumePruneCommand>(FullName()));
return commands;
}

Expand Down
15 changes: 15 additions & 0 deletions src/windows/wslc/commands/VolumeCommand.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,4 +92,19 @@ struct VolumeListCommand final : public Command
void ValidateArgumentsInternal(const ArgMap& execArgs) const override;
void ExecuteInternal(CLIExecutionContext& context) const override;
};

// Prune Command
struct VolumePruneCommand final : public Command
{
constexpr static std::wstring_view CommandName = L"prune";
VolumePruneCommand(const std::wstring& parent) : Command(CommandName, parent)
{
}
std::vector<Argument> GetArguments() const override;
std::wstring ShortDescription() const override;
std::wstring LongDescription() const override;

protected:
void ExecuteInternal(CLIExecutionContext& context) const override;
};
} // namespace wsl::windows::wslc
52 changes: 52 additions & 0 deletions src/windows/wslc/commands/VolumePruneCommand.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*++

Copyright (c) Microsoft. All rights reserved.

Module Name:

VolumePruneCommand.cpp

Abstract:

Implementation of command execution logic.

--*/

#include "VolumeCommand.h"
#include "CLIExecutionContext.h"
#include "SessionTasks.h"
#include "VolumeTasks.h"
#include "Task.h"

using namespace wsl::windows::wslc::execution;
using namespace wsl::windows::wslc::task;
using namespace wsl::shared;

namespace wsl::windows::wslc {
// Volume Prune Command
std::vector<Argument> VolumePruneCommand::GetArguments() const
{
return {
Argument::Create(ArgType::All, std::nullopt, std::nullopt, Localization::WSLCCLI_VolumePruneAllArgDescription()),
Argument::Create(ArgType::Filter, false, NO_LIMIT),
Argument::Create(ArgType::Session),
};
}

std::wstring VolumePruneCommand::ShortDescription() const
{
return Localization::WSLCCLI_VolumePruneDesc();
}

std::wstring VolumePruneCommand::LongDescription() const
{
return Localization::WSLCCLI_VolumePruneLongDesc();
}

void VolumePruneCommand::ExecuteInternal(CLIExecutionContext& context) const
{
context //
<< CreateSession //
<< PruneVolumes;
}
} // namespace wsl::windows::wslc
6 changes: 6 additions & 0 deletions src/windows/wslc/services/VolumeModel.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,10 @@ struct CreateVolumeOptions
std::vector<std::pair<std::string, std::string>> Labels{};
};

struct PruneVolumesResult
{
std::vector<std::string> PrunedVolumes;
ULONGLONG SpaceReclaimed{};
};

} // namespace wsl::windows::wslc::models
39 changes: 39 additions & 0 deletions src/windows/wslc/services/VolumeService.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Module Name:

--*/
#include "VolumeService.h"
#include "WarningCallback.h"
#include <wslutil.h>
#include <wslc.h>

Expand Down Expand Up @@ -81,4 +82,42 @@ wsl::windows::common::wslc_schema::InspectVolume VolumeService::Inspect(models::
THROW_IF_FAILED(session.Get()->InspectVolume(name.c_str(), &output));
return FromJson<wsl::windows::common::wslc_schema::InspectVolume>(output.get());
}

models::PruneVolumesResult VolumeService::Prune(models::Session& session, bool all, const std::vector<std::pair<std::string, std::string>>& filters)
{
const bool hasExplicitAll = std::any_of(filters.begin(), filters.end(), [](const auto& f) { return f.first == "all"; });

std::vector<WSLCFilter> filterEntries;
filterEntries.reserve(filters.size() + ((all && !hasExplicitAll) ? 1 : 0));
if (all && !hasExplicitAll)
{
filterEntries.push_back({.Key = "all", .Value = "true"});
}

for (const auto& [key, value] : filters)
{
filterEntries.push_back({.Key = key.c_str(), .Value = value.c_str()});
}

auto warningCallback = Microsoft::WRL::Make<WarningCallback>();
wil::unique_cotaskmem_array_ptr<WSLCVolumeName> volumes;
ULONGLONG spaceReclaimed = 0;
THROW_IF_FAILED(session.Get()->PruneVolumes(
filterEntries.empty() ? nullptr : filterEntries.data(),
static_cast<ULONG>(filterEntries.size()),
warningCallback.Get(),
&volumes,
volumes.size_address<ULONG>(),
&spaceReclaimed));

models::PruneVolumesResult result;
result.SpaceReclaimed = spaceReclaimed;
result.PrunedVolumes.reserve(volumes.size());
for (auto ptr = volumes.get(), end = volumes.get() + volumes.size(); ptr != end; ++ptr)
{
result.PrunedVolumes.emplace_back(*ptr);
}
Comment thread
AmelBawa-msft marked this conversation as resolved.

return result;
}
} // namespace wsl::windows::wslc::services
1 change: 1 addition & 0 deletions src/windows/wslc/services/VolumeService.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,6 @@ struct VolumeService
static void Delete(models::Session& session, const std::string& name);
static std::vector<WSLCVolumeInformation> List(models::Session& session);
static wsl::windows::common::wslc_schema::InspectVolume Inspect(models::Session& session, const std::string& name);
static models::PruneVolumesResult Prune(models::Session& session, bool all, const std::vector<std::pair<std::string, std::string>>& filters = {});
};
} // namespace wsl::windows::wslc::services
24 changes: 24 additions & 0 deletions src/windows/wslc/tasks/VolumeTasks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -194,4 +194,28 @@ void ListVolumes(CLIExecutionContext& context)
THROW_HR(E_UNEXPECTED);
}
}

void PruneVolumes(CLIExecutionContext& context)
{
WI_ASSERT(context.Data.Contains(Data::Session));
auto& session = context.Data.Get<Data::Session>();

const bool all = context.Args.Contains(ArgType::All);

std::vector<std::pair<std::string, std::string>> filters;
for (const auto& value : context.Args.GetAll<ArgType::Filter>())
{
filters.push_back(validation::ParseFilter(value));
}

auto result = VolumeService::Prune(session, all, filters);

for (const auto& volumeName : result.PrunedVolumes)
{
PrintMessage(Localization::WSLCCLI_VolumePruneDeleted(MultiByteToWide(volumeName)));
}

PrintMessage(L"");
PrintMessage(Localization::WSLCCLI_VolumePruneSpaceReclaimed(wsl::shared::string::FormatBytes(result.SpaceReclaimed)));
}
} // namespace wsl::windows::wslc::task
1 change: 1 addition & 0 deletions src/windows/wslc/tasks/VolumeTasks.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,5 @@ void DeleteVolumes(wsl::windows::wslc::execution::CLIExecutionContext& context);
void GetVolumes(wsl::windows::wslc::execution::CLIExecutionContext& context);
void InspectVolumes(wsl::windows::wslc::execution::CLIExecutionContext& context);
void ListVolumes(wsl::windows::wslc::execution::CLIExecutionContext& context);
void PruneVolumes(wsl::windows::wslc::execution::CLIExecutionContext& context);
} // namespace wsl::windows::wslc::task
Loading
Loading