internal static bool IsReadOnly(Interop.Sys.FileStatus fileStatus) { #if TARGET_BROWSER const Interop.Sys.Permissions readBit = Interop.Sys.Permissions.S_IRUSR; const Interop.Sys.Permissions writeBit = Interop.Sys.Permissions.S_IWUSR; #else Interop.Sys.Permissions readBit, writeBit; if (fileStatus.Uid == Interop.Sys.GetEUid()) { // User effectively owns the file readBit = Interop.Sys.Permissions.S_IRUSR; writeBit = Interop.Sys.Permissions.S_IWUSR; } else if (fileStatus.Gid == Interop.Sys.GetEGid()) { // User belongs to a group that effectively owns the file readBit = Interop.Sys.Permissions.S_IRGRP; writeBit = Interop.Sys.Permissions.S_IWGRP; } else { // Others permissions readBit = Interop.Sys.Permissions.S_IROTH; writeBit = Interop.Sys.Permissions.S_IWOTH; } #endif return((fileStatus.Mode & (int)readBit) != 0 && // has read permission (fileStatus.Mode & (int)writeBit) == 0); // but not write permission }
partial void VerifyPlatformSpecificMetadata(string filePath, TarEntry entry) { Interop.Sys.FileStatus status = default; status.Mode = default; status.Dev = default; Interop.CheckIo(Interop.Sys.LStat(filePath, out status)); Assert.Equal((int)status.Uid, entry.Uid); Assert.Equal((int)status.Gid, entry.Gid); if (entry is PosixTarEntry posix) { string gname = Interop.Sys.GetGroupName(status.Gid); string uname = Interop.Sys.GetUserNameFromPasswd(status.Uid); Assert.Equal(gname, posix.GroupName); Assert.Equal(uname, posix.UserName); if (entry.EntryType is not TarEntryType.BlockDevice and not TarEntryType.CharacterDevice) { Assert.Equal(DefaultDeviceMajor, posix.DeviceMajor); Assert.Equal(DefaultDeviceMinor, posix.DeviceMinor); } } if (entry.EntryType is not TarEntryType.Directory) { TarFileMode expectedMode = (TarFileMode)(status.Mode & 4095); // First 12 bits DateTimeOffset expectedMTime = DateTimeOffset.FromUnixTimeSeconds(status.MTime); DateTimeOffset expectedATime = DateTimeOffset.FromUnixTimeSeconds(status.ATime); DateTimeOffset expectedCTime = DateTimeOffset.FromUnixTimeSeconds(status.CTime); Assert.Equal(expectedMode, entry.Mode); Assert.Equal(expectedMTime, entry.ModificationTime); if (entry is PaxTarEntry pax) { Assert.NotNull(pax.ExtendedAttributes); Assert.True(pax.ExtendedAttributes.Count >= 4); Assert.Contains("path", pax.ExtendedAttributes); VerifyExtendedAttributeTimestamp(pax, "mtime"); VerifyExtendedAttributeTimestamp(pax, "atime"); VerifyExtendedAttributeTimestamp(pax, "ctime"); } else if (entry is GnuTarEntry gnu) { Assert.Equal(expectedATime, gnu.AccessTime); Assert.Equal(expectedCTime, gnu.ChangeTime); } } }
protected void VerifyPlatformSpecificMetadata(string filePath, TarEntry entry) { Interop.Sys.FileStatus status = default; status.Mode = default; status.Dev = default; Interop.CheckIo(Interop.Sys.LStat(filePath, out status)); Assert.Equal((int)status.Uid, entry.Uid); Assert.Equal((int)status.Gid, entry.Gid); if (entry is PosixTarEntry posix) { string gname = Interop.Sys.GetGroupName(status.Gid); string uname = Interop.Sys.GetUserNameFromPasswd(status.Uid); Assert.Equal(gname, posix.GroupName); Assert.Equal(uname, posix.UserName); if (entry.EntryType is not TarEntryType.BlockDevice and not TarEntryType.CharacterDevice) { Assert.Equal(DefaultDeviceMajor, posix.DeviceMajor); Assert.Equal(DefaultDeviceMinor, posix.DeviceMinor); } } if (entry.EntryType is not TarEntryType.Directory) { UnixFileMode expectedMode = (UnixFileMode)(status.Mode & 4095); // First 12 bits Assert.Equal(expectedMode, entry.Mode); Assert.True(entry.ModificationTime > DateTimeOffset.UnixEpoch); if (entry is PaxTarEntry pax) { VerifyExtendedAttributeTimestamps(pax); } if (entry is GnuTarEntry gnu) { VerifyGnuTimestamps(gnu); } } }
private void Verify_Extract_SpecialFiles(string destination, PosixTarEntry entry, TarEntryType entryType) { Assert.True(File.Exists(destination)); Interop.Sys.FileStatus status = default; status.Mode = default; status.Dev = default; Interop.CheckIo(Interop.Sys.LStat(destination, out status)); int fileType = status.Mode & Interop.Sys.FileTypes.S_IFMT; if (entryType is TarEntryType.BlockDevice) { Assert.Equal(Interop.Sys.FileTypes.S_IFBLK, fileType); } else if (entryType is TarEntryType.CharacterDevice) { Assert.Equal(Interop.Sys.FileTypes.S_IFCHR, fileType); } else if (entryType is TarEntryType.Fifo) { Assert.Equal(Interop.Sys.FileTypes.S_IFIFO, fileType); } if (entryType is TarEntryType.BlockDevice or TarEntryType.CharacterDevice) { uint major; uint minor; unsafe { Interop.Sys.GetDeviceIdentifiers((ulong)status.RDev, &major, &minor); } Assert.Equal((int)major, entry.DeviceMajor); Assert.Equal((int)minor, entry.DeviceMinor); } AssertFileModeEquals(destination, TestPermission1); }
// Unix specific implementation of the method that reads an entry from disk and writes it into the archive stream. partial void ReadFileFromDiskAndWriteToArchiveStreamAsEntry(string fullPath, string entryName) { Interop.Sys.FileStatus status = default; status.Mode = default; status.Dev = default; Interop.CheckIo(Interop.Sys.LStat(fullPath, out status)); TarEntryType entryType = (status.Mode & (uint)Interop.Sys.FileTypes.S_IFMT) switch { // Hard links are treated as regular files. // Unix socket files do not get added to tar files. Interop.Sys.FileTypes.S_IFBLK => TarEntryType.BlockDevice, Interop.Sys.FileTypes.S_IFCHR => TarEntryType.CharacterDevice, Interop.Sys.FileTypes.S_IFIFO => TarEntryType.Fifo, Interop.Sys.FileTypes.S_IFLNK => TarEntryType.SymbolicLink, Interop.Sys.FileTypes.S_IFREG => Format is TarFormat.V7 ? TarEntryType.V7RegularFile : TarEntryType.RegularFile, Interop.Sys.FileTypes.S_IFDIR => TarEntryType.Directory, _ => throw new IOException(string.Format(SR.TarUnsupportedFile, fullPath)), }; FileSystemInfo info = entryType is TarEntryType.Directory ? new DirectoryInfo(fullPath) : new FileInfo(fullPath); TarEntry entry = Format switch { TarFormat.V7 => new V7TarEntry(entryType, entryName), TarFormat.Ustar => new UstarTarEntry(entryType, entryName), TarFormat.Pax => new PaxTarEntry(entryType, entryName), TarFormat.Gnu => new GnuTarEntry(entryType, entryName), _ => throw new FormatException(string.Format(SR.TarInvalidFormat, Format)), }; if ((entryType is TarEntryType.BlockDevice or TarEntryType.CharacterDevice) && status.Dev > 0) { uint major; uint minor; unsafe { Interop.CheckIo(Interop.Sys.GetDeviceIdentifiers((ulong)status.Dev, &major, &minor)); } entry._header._devMajor = (int)major; entry._header._devMinor = (int)minor; } entry._header._mTime = TarHelpers.GetDateTimeFromSecondsSinceEpoch(status.MTime); entry._header._aTime = TarHelpers.GetDateTimeFromSecondsSinceEpoch(status.ATime); entry._header._cTime = TarHelpers.GetDateTimeFromSecondsSinceEpoch(status.CTime); entry._header._mode = (status.Mode & 4095); // First 12 bits entry.Uid = (int)status.Uid; entry.Gid = (int)status.Gid; // TODO: Add these p/invokes https://github.com/dotnet/runtime/issues/68230 entry._header._uName = ""; // Interop.Sys.GetUName(); entry._header._gName = ""; // Interop.Sys.GetGName(); if (entry.EntryType == TarEntryType.SymbolicLink) { entry.LinkName = info.LinkTarget ?? string.Empty; } if (entry.EntryType is TarEntryType.RegularFile or TarEntryType.V7RegularFile) { FileStreamOptions options = new() { Mode = FileMode.Open, Access = FileAccess.Read, Share = FileShare.Read, Options = FileOptions.None }; Debug.Assert(entry._header._dataStream == null); entry._header._dataStream = File.Open(fullPath, options); } WriteEntry(entry); if (entry._header._dataStream != null) { entry._header._dataStream.Dispose(); } } }
// Checks if the specified cache (lstat=_fileCache, stat=_symlinkCache) has the directory attribute set // Only call if Refresh has been successfully called at least once, and you're // certain the passed-in cache was successfully retrieved static bool CacheHasDirectoryFlag(Interop.Sys.FileStatus cache) => (cache.Mode & Interop.Sys.FileTypes.S_IFMT) == Interop.Sys.FileTypes.S_IFDIR;
// Creates an entry for writing using the specified path and entryName. If this is being called from an async method, FileOptions should contain Asynchronous. private TarEntry ConstructEntryForWriting(string fullPath, string entryName, FileOptions fileOptions) { Debug.Assert(!string.IsNullOrEmpty(fullPath)); Interop.Sys.FileStatus status = default; status.Mode = default; status.Dev = default; Interop.CheckIo(Interop.Sys.LStat(fullPath, out status)); TarEntryType entryType = (status.Mode & (uint)Interop.Sys.FileTypes.S_IFMT) switch { // Hard links are treated as regular files. // Unix socket files do not get added to tar files. Interop.Sys.FileTypes.S_IFBLK => TarEntryType.BlockDevice, Interop.Sys.FileTypes.S_IFCHR => TarEntryType.CharacterDevice, Interop.Sys.FileTypes.S_IFIFO => TarEntryType.Fifo, Interop.Sys.FileTypes.S_IFLNK => TarEntryType.SymbolicLink, Interop.Sys.FileTypes.S_IFREG => Format is TarEntryFormat.V7 ? TarEntryType.V7RegularFile : TarEntryType.RegularFile, Interop.Sys.FileTypes.S_IFDIR => TarEntryType.Directory, _ => throw new IOException(string.Format(SR.TarUnsupportedFile, fullPath)), }; FileSystemInfo info = entryType is TarEntryType.Directory ? new DirectoryInfo(fullPath) : new FileInfo(fullPath); TarEntry entry = Format switch { TarEntryFormat.V7 => new V7TarEntry(entryType, entryName), TarEntryFormat.Ustar => new UstarTarEntry(entryType, entryName), TarEntryFormat.Pax => new PaxTarEntry(entryType, entryName), TarEntryFormat.Gnu => new GnuTarEntry(entryType, entryName), _ => throw new FormatException(string.Format(SR.TarInvalidFormat, Format)), }; if (entryType is TarEntryType.BlockDevice or TarEntryType.CharacterDevice) { uint major; uint minor; unsafe { Interop.Sys.GetDeviceIdentifiers((ulong)status.RDev, &major, &minor); } entry._header._devMajor = (int)major; entry._header._devMinor = (int)minor; } entry._header._mTime = TarHelpers.GetDateTimeOffsetFromSecondsSinceEpoch(status.MTime); entry._header._aTime = TarHelpers.GetDateTimeOffsetFromSecondsSinceEpoch(status.ATime); entry._header._cTime = TarHelpers.GetDateTimeOffsetFromSecondsSinceEpoch(status.CTime); entry._header._mode = status.Mode & 4095; // First 12 bits // Uid and UName entry._header._uid = (int)status.Uid; if (!_userIdentifiers.TryGetValue(status.Uid, out string?uName)) { uName = Interop.Sys.GetUserNameFromPasswd(status.Uid); _userIdentifiers.Add(status.Uid, uName); } entry._header._uName = uName; // Gid and GName entry._header._gid = (int)status.Gid; if (!_groupIdentifiers.TryGetValue(status.Gid, out string?gName)) { gName = Interop.Sys.GetGroupName(status.Gid); _groupIdentifiers.Add(status.Gid, gName); } entry._header._gName = gName; if (entry.EntryType == TarEntryType.SymbolicLink) { entry.LinkName = info.LinkTarget ?? string.Empty; } if (entry.EntryType is TarEntryType.RegularFile or TarEntryType.V7RegularFile) { Debug.Assert(entry._header._dataStream == null); entry._header._dataStream = new FileStream(fullPath, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, fileOptions); } return(entry); }
internal static bool IsSymLink(Interop.Sys.FileStatus fileStatus) { return((fileStatus.Mode & Interop.Sys.FileTypes.S_IFMT) == Interop.Sys.FileTypes.S_IFLNK); }
internal static bool IsHidden(Interop.Sys.FileStatus fileStatus) { return((fileStatus.UserFlags & (uint)Interop.Sys.UserFlags.UF_HIDDEN) == (uint)Interop.Sys.UserFlags.UF_HIDDEN); }
internal static bool IsDirectory(Interop.Sys.FileStatus fileStatus) { return((fileStatus.Mode & Interop.Sys.FileTypes.S_IFMT) == Interop.Sys.FileTypes.S_IFDIR); }