-
Notifications
You must be signed in to change notification settings - Fork 64
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This is an initial port of the GVFS.Platform.Linux classes and the Scripts/Linux scripts from both the Mac equivalents in this repository as well as the corresponding classes in the VFSForGit repository. Quoting from the primary VFSForGit commit microsoft/VFSForGit@b304cf7 from which this work was derived: We add the core GVFS.Platform.Linux classes, derived from their GVFS.Platform.Mac equivalents but with appropriate changes for syscall argument signatures and flag values, type definitions, and structure fields (e.g., mode_t is a uint, and the layout of struct stat is quite different). We also include all or portions of microsoft/VFSForGit@4ec09a8, microsoft/VFSForGit@7c3c4fa, and microsoft/VFSForGit@ef3201b. Co-authored-by: Ashe Connor <ashe@kivikakk.ee>
- Loading branch information
1 parent
2dc2a2e
commit 5e942ea
Showing
11 changed files
with
596 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
using Scalar.Common; | ||
using Scalar.Common.FileSystem; | ||
using Scalar.Common.Tracing; | ||
using System; | ||
using System.IO; | ||
using System.Runtime.InteropServices; | ||
|
||
namespace Scalar.Platform.Linux | ||
{ | ||
public class LinuxFileBasedLock : FileBasedLock | ||
{ | ||
private int lockFileDescriptor; | ||
|
||
public LinuxFileBasedLock( | ||
PhysicalFileSystem fileSystem, | ||
ITracer tracer, | ||
string lockPath) | ||
: base(fileSystem, tracer, lockPath) | ||
{ | ||
this.lockFileDescriptor = NativeMethods.InvalidFileDescriptor; | ||
} | ||
|
||
public override bool TryAcquireLock() | ||
{ | ||
if (this.lockFileDescriptor == NativeMethods.InvalidFileDescriptor) | ||
{ | ||
this.FileSystem.CreateDirectory(Path.GetDirectoryName(this.LockPath)); | ||
|
||
this.lockFileDescriptor = NativeMethods.Open( | ||
this.LockPath, | ||
NativeMethods.OpenCreate | NativeMethods.OpenWriteOnly, | ||
NativeMethods.FileMode644); | ||
|
||
if (this.lockFileDescriptor == NativeMethods.InvalidFileDescriptor) | ||
{ | ||
int errno = Marshal.GetLastWin32Error(); | ||
EventMetadata metadata = this.CreateEventMetadata(errno); | ||
this.Tracer.RelatedWarning( | ||
metadata, | ||
$"{nameof(LinuxFileBasedLock)}.{nameof(this.TryAcquireLock)}: Failed to open lock file"); | ||
|
||
return false; | ||
} | ||
} | ||
|
||
if (NativeMethods.FLock(this.lockFileDescriptor, NativeMethods.LockEx | NativeMethods.LockNb) != 0) | ||
{ | ||
int errno = Marshal.GetLastWin32Error(); | ||
if (errno != NativeMethods.EIntr && errno != NativeMethods.EWouldBlock) | ||
{ | ||
EventMetadata metadata = this.CreateEventMetadata(errno); | ||
this.Tracer.RelatedWarning( | ||
metadata, | ||
$"{nameof(LinuxFileBasedLock)}.{nameof(this.TryAcquireLock)}: Unexpected error when locking file"); | ||
} | ||
|
||
return false; | ||
} | ||
|
||
return true; | ||
} | ||
|
||
public override void Dispose() | ||
{ | ||
if (this.lockFileDescriptor != NativeMethods.InvalidFileDescriptor) | ||
{ | ||
if (NativeMethods.Close(this.lockFileDescriptor) != 0) | ||
{ | ||
// Failures of close() are logged for diagnostic purposes only. | ||
// It's possible that errors from a previous operation (e.g. write(2)) | ||
// are only reported in close(). We should *not* retry the close() if | ||
// it fails since it may cause a re-used file descriptor from another | ||
// thread to be closed. | ||
|
||
int errno = Marshal.GetLastWin32Error(); | ||
EventMetadata metadata = this.CreateEventMetadata(errno); | ||
this.Tracer.RelatedWarning( | ||
metadata, | ||
$"{nameof(LinuxFileBasedLock)}.{nameof(this.Dispose)}: Error when closing lock fd"); | ||
} | ||
|
||
this.lockFileDescriptor = NativeMethods.InvalidFileDescriptor; | ||
} | ||
} | ||
|
||
private EventMetadata CreateEventMetadata(int errno = 0) | ||
{ | ||
EventMetadata metadata = new EventMetadata(); | ||
metadata.Add("Area", nameof(LinuxFileBasedLock)); | ||
metadata.Add(nameof(this.LockPath), this.LockPath); | ||
if (errno != 0) | ||
{ | ||
metadata.Add(nameof(errno), errno); | ||
} | ||
|
||
return metadata; | ||
} | ||
|
||
private static class NativeMethods | ||
{ | ||
// #define O_WRONLY 0x0001 /* open for writing only */ | ||
public const int OpenWriteOnly = 0x0001; | ||
|
||
// #define O_CREAT 0x0200 /* create if nonexistant */ | ||
public const int OpenCreate = 0x0040; | ||
|
||
// #define EINTR 4 /* Interrupted system call */ | ||
public const int EIntr = 4; | ||
|
||
// #define EAGAIN 11 /* Resource temporarily unavailable */ | ||
// #define EWOULDBLOCK EAGAIN /* Operation would block */ | ||
public const int EWouldBlock = 11; | ||
|
||
public const int LockSh = 1; // #define LOCK_SH 1 /* shared lock */ | ||
public const int LockEx = 2; // #define LOCK_EX 2 /* exclusive lock */ | ||
public const int LockNb = 4; // #define LOCK_NB 4 /* don't block when locking */ | ||
public const int LockUn = 8; // #define LOCK_UN 8 /* unlock */ | ||
|
||
public const int InvalidFileDescriptor = -1; | ||
|
||
public static readonly uint FileMode644 = Convert.ToUInt32("644", 8); | ||
|
||
[DllImport("libc", EntryPoint = "open", SetLastError = true)] | ||
public static extern int Open(string pathname, int flags, uint mode); | ||
|
||
[DllImport("libc", EntryPoint = "close", SetLastError = true)] | ||
public static extern int Close(int fd); | ||
|
||
[DllImport("libc", EntryPoint = "flock", SetLastError = true)] | ||
public static extern int FLock(int fd, int operation); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
using Scalar.Common; | ||
using Scalar.Platform.POSIX; | ||
using System; | ||
using System.IO; | ||
using System.Runtime.InteropServices; | ||
|
||
namespace Scalar.Platform.Linux | ||
{ | ||
public class LinuxFileSystem : POSIXFileSystem | ||
{ | ||
public override bool IsExecutable(string fileName) | ||
{ | ||
NativeStat.StatBuffer statBuffer = this.StatFile(fileName); | ||
return NativeStat.IsExecutable(statBuffer.Mode); | ||
} | ||
|
||
public override bool IsSocket(string fileName) | ||
{ | ||
NativeStat.StatBuffer statBuffer = this.StatFile(fileName); | ||
return NativeStat.IsSock(statBuffer.Mode); | ||
} | ||
|
||
public override bool IsFileSystemSupported(string path, out string error) | ||
{ | ||
error = null; | ||
return true; | ||
} | ||
|
||
private NativeStat.StatBuffer StatFile(string fileName) | ||
{ | ||
if (NativeStat.Stat(fileName, out NativeStat.StatBuffer statBuffer) != 0) | ||
{ | ||
NativeMethods.ThrowLastWin32Exception($"Failed to stat {fileName}"); | ||
} | ||
|
||
return statBuffer; | ||
} | ||
|
||
private static class NativeStat | ||
{ | ||
// #define S_IFMT 0170000 /* [XSI] type of file mask */ | ||
private static readonly uint IFMT = Convert.ToUInt32("170000", 8); | ||
|
||
// #define S_IFSOCK 0140000 /* [XSI] socket */ | ||
private static readonly uint IFSOCK = Convert.ToUInt32("0140000", 8); | ||
|
||
// #define S_IXUSR 0000100 /* [XSI] X for owner */ | ||
private static readonly uint IXUSR = Convert.ToUInt32("100", 8); | ||
|
||
// #define S_IXGRP 0000010 /* [XSI] X for group */ | ||
private static readonly uint IXGRP = Convert.ToUInt32("10", 8); | ||
|
||
// #define S_IXOTH 0000001 /* [XSI] X for other */ | ||
private static readonly uint IXOTH = Convert.ToUInt32("1", 8); | ||
|
||
// #define _STAT_VER 1 | ||
private static readonly int STAT_VER = 1; | ||
|
||
public static bool IsSock(uint mode) | ||
{ | ||
// #define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK) /* socket */ | ||
return (mode & IFMT) == IFSOCK; | ||
} | ||
|
||
public static bool IsExecutable(uint mode) | ||
{ | ||
return (mode & (IXUSR | IXGRP | IXOTH)) != 0; | ||
} | ||
|
||
public static int Stat(string path, [Out] out StatBuffer buf) | ||
{ | ||
return __XStat64(STAT_VER, path, out buf); | ||
} | ||
|
||
// TODO(Linux): assumes recent GNU libc or ABI-compatible libc | ||
[DllImport("libc", EntryPoint = "__xstat64", SetLastError = true)] | ||
private static extern int __XStat64(int vers, string path, [Out] out StatBuffer buf); | ||
|
||
[StructLayout(LayoutKind.Sequential)] | ||
public struct TimeSpec | ||
{ | ||
public long Sec; | ||
public long Nsec; | ||
} | ||
|
||
// TODO(Linux): assumes stat64 field layout of x86-64 architecture | ||
[StructLayout(LayoutKind.Sequential)] | ||
public struct StatBuffer | ||
{ | ||
public ulong Dev; /* ID of device containing file */ | ||
public ulong Ino; /* File serial number */ | ||
public ulong NLink; /* Number of hard links */ | ||
public uint Mode; /* Mode of file (see below) */ | ||
public uint UID; /* User ID of the file */ | ||
public uint GID; /* Group ID of the file */ | ||
public uint Padding; /* RESERVED: DO NOT USE! */ | ||
public ulong RDev; /* Device ID if special file */ | ||
public long Size; /* file size, in bytes */ | ||
public long BlkSize; /* optimal blocksize for I/O */ | ||
public long Blocks; /* blocks allocated for file */ | ||
public TimeSpec ATimespec; /* time of last access */ | ||
public TimeSpec MTimespec; /* time of last data modification */ | ||
public TimeSpec CTimespec; /* time of last status change */ | ||
|
||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] | ||
private long[] reserved; /* RESERVED: DO NOT USE! */ | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
using System; | ||
using System.IO; | ||
using Scalar.Common; | ||
using Scalar.Platform.POSIX; | ||
|
||
namespace Scalar.Platform.Linux | ||
{ | ||
public partial class LinuxPlatform | ||
{ | ||
public static string GetDataRootForScalarImplementation() | ||
{ | ||
// TODO(Linux): determine installation location and data path | ||
string path = Environment.GetEnvironmentVariable("SCALAR_DATA_PATH"); | ||
return path ?? "/var/run/scalar"; | ||
} | ||
|
||
public static string GetDataRootForScalarComponentImplementation(string componentName) | ||
{ | ||
return Path.Combine(GetDataRootForScalarImplementation(), componentName); | ||
} | ||
|
||
public static string GetUpgradeHighestAvailableVersionDirectoryImplementation() | ||
{ | ||
return GetUpgradeNonProtectedDirectoryImplementation(); | ||
} | ||
|
||
public static string GetUpgradeNonProtectedDirectoryImplementation() | ||
{ | ||
return Path.Combine(GetDataRootForScalarImplementation(), ProductUpgraderInfo.UpgradeDirectoryName); | ||
} | ||
|
||
private string GetUpgradeNonProtectedDataDirectory() | ||
{ | ||
return GetUpgradeNonProtectedDirectoryImplementation(); | ||
} | ||
} | ||
} |
Oops, something went wrong.