Пример #1
0
        // Reads attributes specific to the GNU format.
        // Throws if any conversion fails.
        private void ReadGnuAttributes(Span <byte> buffer)
        {
            // Convert byte arrays
            long aTime = (long)TarHelpers.ParseOctal <ulong>(buffer.Slice(FieldLocations.ATime, FieldLengths.ATime));

            _aTime = TarHelpers.GetDateTimeOffsetFromSecondsSinceEpoch(aTime);

            long cTime = (long)TarHelpers.ParseOctal <ulong>(buffer.Slice(FieldLocations.CTime, FieldLengths.CTime));

            _cTime = TarHelpers.GetDateTimeOffsetFromSecondsSinceEpoch(cTime);

            // TODO: Read the bytes of the currently unsupported GNU fields, in case user wants to write this entry into another GNU archive, they need to be preserved. https://github.com/dotnet/runtime/issues/68230
        }
Пример #2
0
        // 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);
        }
Пример #3
0
        // Attempts to read the fields shared by all formats and stores them in their expected data type.
        // Throws if any data type conversion fails.
        // Returns true on success, false if checksum is zero.
        private static TarHeader?TryReadCommonAttributes(Span <byte> buffer, TarEntryFormat initialFormat)
        {
            // Start by collecting fields that need special checks that return early when data is wrong

            // Empty checksum means this is an invalid (all blank) entry, finish early
            Span <byte> spanChecksum = buffer.Slice(FieldLocations.Checksum, FieldLengths.Checksum);

            if (TarHelpers.IsAllNullBytes(spanChecksum))
            {
                return(null);
            }
            int checksum = (int)TarHelpers.ParseOctal <uint>(spanChecksum);

            // Zero checksum means the whole header is empty
            if (checksum == 0)
            {
                return(null);
            }

            long size = (int)TarHelpers.ParseOctal <uint>(buffer.Slice(FieldLocations.Size, FieldLengths.Size));

            if (size < 0)
            {
                throw new FormatException(string.Format(SR.TarSizeFieldNegative));
            }

            // Continue with the rest of the fields that require no special checks
            TarHeader header = new(initialFormat,
                                   name : TarHelpers.GetTrimmedUtf8String(buffer.Slice(FieldLocations.Name, FieldLengths.Name)),
                                   mode : (int)TarHelpers.ParseOctal <uint>(buffer.Slice(FieldLocations.Mode, FieldLengths.Mode)),
                                   mTime : TarHelpers.GetDateTimeOffsetFromSecondsSinceEpoch((long)TarHelpers.ParseOctal <ulong>(buffer.Slice(FieldLocations.MTime, FieldLengths.MTime))),
                                   typeFlag : (TarEntryType)buffer[FieldLocations.TypeFlag])
            {
                _checksum = checksum,
                _size     = size,
                _uid      = (int)TarHelpers.ParseOctal <uint>(buffer.Slice(FieldLocations.Uid, FieldLengths.Uid)),
                _gid      = (int)TarHelpers.ParseOctal <uint>(buffer.Slice(FieldLocations.Gid, FieldLengths.Gid)),
                _linkName = TarHelpers.GetTrimmedUtf8String(buffer.Slice(FieldLocations.LinkName, FieldLengths.LinkName))
            };

            if (header._format == TarEntryFormat.Unknown)
            {
                header._format = header._typeFlag switch
                {
                    TarEntryType.ExtendedAttributes or
                    TarEntryType.GlobalExtendedAttributes => TarEntryFormat.Pax,

                                 TarEntryType.DirectoryList or
                                 TarEntryType.LongLink or
                                 TarEntryType.LongPath or
                                 TarEntryType.MultiVolume or
                                 TarEntryType.RenamedOrSymlinked or
                                 TarEntryType.TapeVolume => TarEntryFormat.Gnu,

                    // V7 is the only one that uses 'V7RegularFile'.
                                 TarEntryType.V7RegularFile => TarEntryFormat.V7,

                                 TarEntryType.SparseFile => throw new NotSupportedException(string.Format(SR.TarEntryTypeNotSupported, header._typeFlag)),

                          // We can quickly determine the *minimum* possible format if the entry type
                          // is the POSIX 'RegularFile', although later we could upgrade it to PAX or GNU
                          _ => (header._typeFlag == TarEntryType.RegularFile) ? TarEntryFormat.Ustar : TarEntryFormat.V7
                };
            }

            return(header);
        }