Skip to content

Commit af4eb1c

Browse files
committed
Fix: --unmount-all now triggers pending staged upgrade
'gvfs service --unmount-all' sets SkipUnregister on each unmount to preserve automount registration. This meant no UnregisterRepoRequest reached the service, so RequestHandler never called TryDeferredPendingUpgradeCheck and staged upgrades were never applied. Add PendingUpgradeCheckRequest message type. ServiceVerb sends it after the --unmount-all loop completes so the service can schedule the same deferred upgrade check that individual unmounts trigger. Also add 'unmount-all-triggers-upgrade' scenario to upgrade-tests.yaml CI matrix to cover this path end-to-end. Assisted-by: Claude Opus 4.6 Signed-off-by: Tyrie Vella <tyrielv@gmail.com>
1 parent 3c18fe5 commit af4eb1c

4 files changed

Lines changed: 112 additions & 0 deletions

File tree

.github/workflows/upgrade-tests.yaml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ jobs:
3333
- double-staging
3434
- staging-then-clean
3535
- mount-safety-deferral
36+
- unmount-all-triggers-upgrade
3637
fail-fast: false
3738

3839
steps:
@@ -274,6 +275,35 @@ jobs:
274275
Write-Host "PASS: Mount safety deferral works correctly"
275276
}
276277
278+
"unmount-all-triggers-upgrade" {
279+
Write-Host "=== Scenario: unmount-all triggers staged upgrade ==="
280+
# Install LKG, mount, staging upgrade, then unmount via
281+
# 'gvfs service --unmount-all' instead of 'gvfs unmount'.
282+
# --unmount-all skips the unregister message, so without the
283+
# PendingUpgradeCheckRequest fix the upgrade would never apply.
284+
Install-GVFS $lkgInstaller
285+
Assert-ServiceRunning
286+
$mountPid = Mount-TestRepo
287+
288+
Install-GVFS $newInstaller @("/STAGEIFMOUNTED=true")
289+
Assert-MountAlive $mountPid
290+
Assert-PendingUpgrade $true
291+
292+
# Unmount via --unmount-all (not per-repo unmount)
293+
& "$installDir\gvfs.exe" service --unmount-all 2>&1 | Write-Host
294+
if ($LASTEXITCODE -ne 0) { throw "unmount-all failed" }
295+
296+
# The deferred upgrade timer fires after ~5 seconds.
297+
# Wait for it to complete.
298+
$deadline = (Get-Date).AddSeconds(30)
299+
while ((Test-Path "$installDir\PendingUpgrade") -and (Get-Date) -lt $deadline) {
300+
Start-Sleep -Seconds 2
301+
}
302+
303+
Assert-PendingUpgrade $false
304+
Write-Host "PASS: unmount-all triggers staged upgrade"
305+
}
306+
277307
default {
278308
throw "Unknown scenario: ${{ matrix.scenario }}"
279309
}

GVFS/GVFS.Common/NamedPipes/NamedPipeMessages.cs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,29 @@ public static Response FromMessage(Message message)
427427
}
428428
}
429429

430+
public class PendingUpgradeCheckRequest
431+
{
432+
public const string Header = nameof(PendingUpgradeCheckRequest);
433+
434+
public static PendingUpgradeCheckRequest FromMessage(Message message)
435+
{
436+
return GVFSJsonOptions.Deserialize<PendingUpgradeCheckRequest>(message.Body);
437+
}
438+
439+
public Message ToMessage()
440+
{
441+
return new Message(Header, GVFSJsonOptions.Serialize(this));
442+
}
443+
444+
public class Response : BaseResponse<PendingUpgradeCheckRequest>
445+
{
446+
public static Response FromMessage(Message message)
447+
{
448+
return GVFSJsonOptions.Deserialize<Response>(message.Body);
449+
}
450+
}
451+
}
452+
430453
public class GetActiveRepoListRequest
431454
{
432455
public const string Header = nameof(GetActiveRepoListRequest);

GVFS/GVFS.Service/Handlers/RequestHandler.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,18 @@ protected virtual void HandleMessage(
9595

9696
break;
9797

98+
case NamedPipeMessages.PendingUpgradeCheckRequest.Header:
99+
this.requestDescription = "pending upgrade check";
100+
101+
this.TryDeferredPendingUpgradeCheck(this.tracer);
102+
103+
NamedPipeMessages.PendingUpgradeCheckRequest.Response upgradeCheckResponse =
104+
new NamedPipeMessages.PendingUpgradeCheckRequest.Response();
105+
upgradeCheckResponse.State = NamedPipeMessages.CompletionState.Success;
106+
this.TrySendResponse(tracer, upgradeCheckResponse.ToMessage().ToString(), connection);
107+
108+
break;
109+
98110
case NamedPipeMessages.GetActiveRepoListRequest.Header:
99111
this.requestDescription = RepoListRequestDescription;
100112
NamedPipeMessages.GetActiveRepoListRequest repoListRequest = NamedPipeMessages.GetActiveRepoListRequest.FromMessage(message);

GVFS/GVFS/CommandLine/ServiceVerb.cs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,12 @@ public override void Execute()
136136
}
137137
}
138138

139+
// Notify the service so it can check for a pending staged
140+
// upgrade. Individual unmounts skip unregister (to preserve
141+
// automount registration), so the service's normal unmount
142+
// trigger never fires during --unmount-all.
143+
this.TryNotifyPendingUpgradeCheck();
144+
139145
if (failedRepoRoots.Count() > 0)
140146
{
141147
string errorString = $"The following repos failed to unmount:{Environment.NewLine}{string.Join(Environment.NewLine, failedRepoRoots.ToArray())}";
@@ -217,5 +223,46 @@ private bool IsRepoMounted(string repoRoot)
217223

218224
return false;
219225
}
226+
227+
private void TryNotifyPendingUpgradeCheck()
228+
{
229+
NamedPipeMessages.PendingUpgradeCheckRequest request = new NamedPipeMessages.PendingUpgradeCheckRequest();
230+
231+
try
232+
{
233+
using (NamedPipeClient client = new NamedPipeClient(this.ServicePipeName))
234+
{
235+
if (!client.Connect())
236+
{
237+
this.Output.WriteLine(" WARNING: Could not notify GVFS.Service to check for pending upgrade (service not responding).");
238+
return;
239+
}
240+
241+
client.SendRequest(request.ToMessage());
242+
NamedPipeMessages.Message response = client.ReadResponse();
243+
if (response.Header == NamedPipeMessages.PendingUpgradeCheckRequest.Response.Header)
244+
{
245+
NamedPipeMessages.PendingUpgradeCheckRequest.Response typedResponse =
246+
NamedPipeMessages.PendingUpgradeCheckRequest.Response.FromMessage(response);
247+
if (typedResponse.State != NamedPipeMessages.CompletionState.Success)
248+
{
249+
this.Output.WriteLine(" WARNING: Pending upgrade check failed: " + typedResponse.ErrorMessage);
250+
}
251+
}
252+
else
253+
{
254+
this.Output.WriteLine(" WARNING: GVFS.Service responded with unexpected message: " + response.Header);
255+
}
256+
}
257+
}
258+
catch (BrokenPipeException)
259+
{
260+
this.Output.WriteLine(" WARNING: Could not notify GVFS.Service to check for pending upgrade.");
261+
}
262+
catch (Exception ex)
263+
{
264+
this.Output.WriteLine(" WARNING: Error notifying GVFS.Service to check for pending upgrade: " + ex.Message);
265+
}
266+
}
220267
}
221268
}

0 commit comments

Comments
 (0)