[Hosts] Handle hidden hosts file (#34308)

* handle hidden hosts file

* don't remove hidden attribute
This commit is contained in:
Davide Giacometti 2024-08-16 20:36:26 +02:00 committed by GitHub
parent 1cbf512ed0
commit 9af757f5ce
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 50 additions and 11 deletions

View File

@ -2,6 +2,7 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.IO;
using System.IO.Abstractions.TestingHelpers;
using System.Linq;
using System.Threading.Tasks;
@ -248,25 +249,53 @@ namespace Hosts.Tests
{
var fileSystem = new CustomMockFileSystem();
var service = new HostsService(fileSystem, _userSettings.Object, _elevationHelper.Object);
var hostsFile = new MockFileData(string.Empty);
hostsFile.Attributes = System.IO.FileAttributes.ReadOnly;
var hostsFile = new MockFileData(string.Empty)
{
Attributes = FileAttributes.ReadOnly,
};
fileSystem.AddFile(service.HostsFilePath, hostsFile);
await Assert.ThrowsExceptionAsync<ReadOnlyHostsException>(async () => await service.WriteAsync("# Empty hosts file", Enumerable.Empty<Entry>()));
}
[TestMethod]
public void Remove_ReadOnly()
public void Remove_ReadOnly_Attribute()
{
var fileSystem = new CustomMockFileSystem();
var service = new HostsService(fileSystem, _userSettings.Object, _elevationHelper.Object);
var hostsFile = new MockFileData(string.Empty);
hostsFile.Attributes = System.IO.FileAttributes.ReadOnly;
var hostsFile = new MockFileData(string.Empty)
{
Attributes = FileAttributes.ReadOnly,
};
fileSystem.AddFile(service.HostsFilePath, hostsFile);
service.RemoveReadOnly();
var readOnly = fileSystem.FileInfo.FromFileName(service.HostsFilePath).Attributes.HasFlag(System.IO.FileAttributes.ReadOnly);
service.RemoveReadOnlyAttribute();
var readOnly = fileSystem.FileInfo.FromFileName(service.HostsFilePath).Attributes.HasFlag(FileAttributes.ReadOnly);
Assert.IsFalse(readOnly);
}
[TestMethod]
public async Task Save_Hidden_Hosts()
{
var fileSystem = new CustomMockFileSystem();
var service = new HostsService(fileSystem, _userSettings.Object, _elevationHelper.Object);
var hostsFile = new MockFileData(string.Empty)
{
Attributes = FileAttributes.Hidden,
};
fileSystem.AddFile(service.HostsFilePath, hostsFile);
await service.WriteAsync("# Empty hosts file", Enumerable.Empty<Entry>());
var hidden = fileSystem.FileInfo.FromFileName(service.HostsFilePath).Attributes.HasFlag(FileAttributes.Hidden);
Assert.IsTrue(hidden);
}
}
}

View File

@ -23,6 +23,7 @@ namespace HostsUILib.Helpers
public class HostsService : IHostsService, IDisposable
{
private const string _backupSuffix = $"_PowerToysBackup_";
private const int _defaultBufferSize = 4096; // From System.IO.File source code
private readonly SemaphoreSlim _asyncLock = new SemaphoreSlim(1, 1);
private readonly IFileSystem _fileSystem;
@ -197,7 +198,16 @@ namespace HostsUILib.Helpers
_backupDone = true;
}
await _fileSystem.File.WriteAllLinesAsync(HostsFilePath, lines, Encoding);
// FileMode.OpenOrCreate is necessary to prevent UnauthorizedAccessException when the hosts file is hidden
using var stream = _fileSystem.FileStream.Create(HostsFilePath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.Read, _defaultBufferSize, FileOptions.Asynchronous);
using var writer = new StreamWriter(stream, Encoding);
foreach (var line in lines)
{
await writer.WriteLineAsync(line.AsMemory());
}
stream.SetLength(stream.Position);
await writer.FlushAsync();
}
finally
{
@ -292,7 +302,7 @@ namespace HostsUILib.Helpers
}
}
public void RemoveReadOnly()
public void RemoveReadOnlyAttribute()
{
var fileInfo = _fileSystem.FileInfo.FromFileName(HostsFilePath);
if (fileInfo.IsReadOnly)

View File

@ -25,6 +25,6 @@ namespace HostsUILib.Helpers
void OpenHostsFile();
void RemoveReadOnly();
void RemoveReadOnlyAttribute();
}
}

View File

@ -283,7 +283,7 @@ namespace HostsUILib.ViewModels
[RelayCommand]
public void OverwriteHosts()
{
_hostsService.RemoveReadOnly();
_hostsService.RemoveReadOnlyAttribute();
_ = Task.Run(SaveAsync);
}