/// <summary> /// Asynchronously adds a file to the tar archive. /// </summary> /// <param name="filename"> /// The name of the file to add. /// </param> /// <param name="fileMode"> /// The <see cref="LinuxFileMode"/> of the file to add. /// </param> /// <param name="lastModified"> /// The date and time at which the file was last modified. /// </param> /// <param name="entryStream"> /// A <see cref="Stream"/> which represents the data to add. /// </param> /// <param name="cancellationToken"> /// A <see cref="CancellationToken"/> which can be used to cancel the asynchronous operation. /// </param> /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns> public async Task AddFileAsync(string filename, LinuxFileMode fileMode, DateTimeOffset lastModified, Stream entryStream, CancellationToken cancellationToken) { if (filename == null) { throw new ArgumentNullException(nameof(filename)); } if (entryStream == null) { throw new ArgumentNullException(nameof(entryStream)); } var header = new TarHeader() { FileName = filename, FileMode = fileMode, UserId = 0, GroupId = 0, FileSize = (uint)entryStream.Length, LastModified = lastModified, TypeFlag = TarTypeFlag.RegType, LinkName = string.Empty, Magic = "ustar\0", Version = 0, UserName = string.Empty, GroupName = string.Empty, DevMajor = 0, DevMinor = 0, Prefix = string.Empty, }; header.Write(this.headerBuffer); // Write the header for the current tar entry await this.tarStream.WriteAsync(this.headerBuffer, cancellationToken).ConfigureAwait(false); // Write the actual entry await entryStream.CopyToAsync(this.tarStream, cancellationToken); // Align the stream if (entryStream.Length % 512 != 0) { var length = 512 - ((int)entryStream.Length % 512); var buffer = this.headerBuffer.AsMemory(0, length); buffer.Span.Clear(); await this.tarStream.WriteAsync(buffer, cancellationToken).ConfigureAwait(false); } await this.tarStream.FlushAsync(cancellationToken).ConfigureAwait(false); }
public static void WriteEntry(Stream output, string name, LinuxFileMode mode, Stream data) { var hdr = new ArHeader { EndChar = "`\n", FileMode = mode, FileName = name, FileSize = (uint)data.Length, GroupId = 0, OwnerId = 0, LastModified = DateTimeOffset.UtcNow }; WriteEntry(output, hdr, data); }
/// <summary> /// Reads data from a <see cref="Span{T}"/>. /// </summary> /// <param name="buffer"> /// A buffer which contains the header data. /// </param> /// <returns> /// The number of bytes read. /// </returns> public int ReadFrom(Span <byte> buffer) { this.Signature = ReadOctalString(buffer.Slice(0, 6)); this.Dev = ReadOctalString(buffer.Slice(6, 6)); this.Ino = ReadOctalString(buffer.Slice(12, 6)); this.Mode = (LinuxFileMode)ReadOctalString(buffer.Slice(18, 6)); this.Uid = ReadOctalString(buffer.Slice(24, 6)); this.Gid = ReadOctalString(buffer.Slice(30, 6)); this.Nlink = ReadOctalString(buffer.Slice(36, 6)); this.Rdev = ReadOctalString(buffer.Slice(42, 6)); this.Mtime = DateTimeOffset.FromUnixTimeSeconds(ReadOctalString(buffer.Slice(48, 11))); this.Namesize = ReadOctalString(buffer.Slice(59, 6)); this.Filesize = ReadOctalString(buffer.Slice(65, 11)); return(Size); }
public Collection <ArchiveEntry> FromLinuxFolders(ITaskItem[] metadata) { Collection <ArchiveEntry> value = new Collection <ArchiveEntry>(); // This can be null if the user did not define any folders. // In that case: nothing to do. if (metadata != null) { foreach (var folder in metadata) { var path = folder.ItemSpec.Replace("\\", "/"); // Default file mode LinuxFileMode mode = LinuxFileMode.S_IXOTH | LinuxFileMode.S_IROTH | LinuxFileMode.S_IXGRP | LinuxFileMode.S_IRGRP | LinuxFileMode.S_IXUSR | LinuxFileMode.S_IWUSR | LinuxFileMode.S_IRUSR | LinuxFileMode.S_IFDIR; mode = this.GetFileMode(path, folder, mode); // Write out an entry for the directory ArchiveEntry directoryEntry = new ArchiveEntry() { FileSize = 0x00001000, Sha256 = Array.Empty <byte>(), Mode = mode, Modified = DateTime.Now, Group = folder.GetGroup(), Owner = folder.GetOwner(), Inode = this.inode++, TargetPath = path, LinkTo = string.Empty, RemoveOnUninstall = folder.GetRemoveOnUninstall() }; value.Add(directoryEntry); } } return(value); }
/// <summary> /// Gets the file mode for a file or directory entry, based on a default value /// and the value of the LinuxFileMode attribute, if set. /// </summary> /// <param name="name"> /// The name (path) of the entry. Only used in error messages. /// </param> /// <param name="metadata"> /// The metadata for the current entry. /// </param> /// <param name="defaultMode"> /// The default mode. This mode is used to determine the file type (directory/file), /// and when no LinuxFileMode value is specified. /// </param> /// <returns> /// The <see cref="LinuxFileMode"/> for the current entry. /// </returns> private LinuxFileMode GetFileMode(string name, ITaskItem metadata, LinuxFileMode defaultMode) { LinuxFileMode mode = defaultMode; LinuxFileMode defaultFileTypeMask = defaultMode & LinuxFileMode.FileTypeMask; // If the user has chosen to override the file node, respect that var overridenFileMode = metadata?.GetLinuxFileMode(); if (overridenFileMode != null) { // We expect the user to specify the file mode in its octal representation, // such as 755. We don't expect users to specify the higher bits (e.g. // S_IFREG). try { mode = (LinuxFileMode)Convert.ToUInt32(overridenFileMode, 8); LinuxFileMode fileType = mode & LinuxFileMode.FileTypeMask; if (fileType != LinuxFileMode.None && fileType != defaultFileTypeMask) { this.Log.LogWarning($"An invalid file type of '{fileType}' has been set for file '{name}'. The file type will be reset to {defaultFileTypeMask}."); } // Override the file type mask and hardcode it to the file type mask from the default mode. // In practice this will ensure the S_IFREG or S_IFDIR flag is set. mode = (mode & ~LinuxFileMode.FileTypeMask) | defaultFileTypeMask; } catch (Exception) { throw new Exception($"Could not parse the file mode '{overridenFileMode}' for file '{name}'. Make sure to set the LinuxFileMode attriubute to an octal representation of a Unix file mode."); } } return(mode); }
public static void WriteEntry(Stream output, string name, LinuxFileMode mode, string data) => WriteEntry(output, name, mode, new MemoryStream(Encoding.UTF8.GetBytes(data)));