TarStream.cpp 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. /*
  2. * Copyright (c) 2020, Peter Elliott <pelliott@serenityos.org>
  3. * Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
  4. * Copyright (c) 2022, the SerenityOS developers.
  5. *
  6. * SPDX-License-Identifier: BSD-2-Clause
  7. */
  8. #include <AK/Array.h>
  9. #include <LibArchive/TarStream.h>
  10. #include <string.h>
  11. namespace Archive {
  12. TarFileStream::TarFileStream(TarInputStream& tar_stream)
  13. : m_tar_stream(tar_stream)
  14. , m_generation(tar_stream.m_generation)
  15. {
  16. }
  17. size_t TarFileStream::read(Bytes bytes)
  18. {
  19. // verify that the stream has not advanced
  20. VERIFY(m_tar_stream.m_generation == m_generation);
  21. if (has_any_error())
  22. return 0;
  23. auto to_read = min(bytes.size(), m_tar_stream.header().size() - m_tar_stream.m_file_offset);
  24. auto nread = m_tar_stream.m_stream.read(bytes.trim(to_read));
  25. m_tar_stream.m_file_offset += nread;
  26. return nread;
  27. }
  28. bool TarFileStream::unreliable_eof() const
  29. {
  30. // verify that the stream has not advanced
  31. VERIFY(m_tar_stream.m_generation == m_generation);
  32. return m_tar_stream.m_stream.unreliable_eof()
  33. || m_tar_stream.m_file_offset >= m_tar_stream.header().size();
  34. }
  35. bool TarFileStream::read_or_error(Bytes bytes)
  36. {
  37. // verify that the stream has not advanced
  38. VERIFY(m_tar_stream.m_generation == m_generation);
  39. if (read(bytes) < bytes.size()) {
  40. set_fatal_error();
  41. return false;
  42. }
  43. return true;
  44. }
  45. bool TarFileStream::discard_or_error(size_t count)
  46. {
  47. // verify that the stream has not advanced
  48. VERIFY(m_tar_stream.m_generation == m_generation);
  49. if (count > m_tar_stream.header().size() - m_tar_stream.m_file_offset) {
  50. return false;
  51. }
  52. m_tar_stream.m_file_offset += count;
  53. return m_tar_stream.m_stream.discard_or_error(count);
  54. }
  55. TarInputStream::TarInputStream(InputStream& stream)
  56. : m_stream(stream)
  57. {
  58. if (!m_stream.read_or_error(Bytes(&m_header, sizeof(m_header)))) {
  59. m_finished = true;
  60. m_stream.handle_any_error(); // clear out errors so we dont assert
  61. return;
  62. }
  63. VERIFY(m_stream.discard_or_error(block_size - sizeof(TarFileHeader)));
  64. }
  65. static constexpr unsigned long block_ceiling(unsigned long offset)
  66. {
  67. return block_size * (1 + ((offset - 1) / block_size));
  68. }
  69. void TarInputStream::advance()
  70. {
  71. if (m_finished)
  72. return;
  73. m_generation++;
  74. VERIFY(m_stream.discard_or_error(block_ceiling(m_header.size()) - m_file_offset));
  75. m_file_offset = 0;
  76. if (!m_stream.read_or_error(Bytes(&m_header, sizeof(m_header)))) {
  77. m_finished = true;
  78. return;
  79. }
  80. if (!valid()) {
  81. m_finished = true;
  82. return;
  83. }
  84. VERIFY(m_stream.discard_or_error(block_size - sizeof(TarFileHeader)));
  85. }
  86. bool TarInputStream::valid() const
  87. {
  88. auto const header_magic = header().magic();
  89. auto const header_version = header().version();
  90. if (!((header_magic == gnu_magic && header_version == gnu_version)
  91. || (header_magic == ustar_magic && header_version == ustar_version)
  92. || (header_magic == posix1_tar_magic && header_version == posix1_tar_version)))
  93. return false;
  94. // POSIX.1-1988 tar does not have magic numbers, so we also need to verify the header checksum.
  95. return header().checksum() == header().expected_checksum();
  96. }
  97. TarFileStream TarInputStream::file_contents()
  98. {
  99. VERIFY(!m_finished);
  100. return TarFileStream(*this);
  101. }
  102. TarOutputStream::TarOutputStream(OutputStream& stream)
  103. : m_stream(stream)
  104. {
  105. }
  106. void TarOutputStream::add_directory(String const& path, mode_t mode)
  107. {
  108. VERIFY(!m_finished);
  109. TarFileHeader header {};
  110. header.set_size(0);
  111. header.set_filename_and_prefix(String::formatted("{}/", path)); // Old tar implementations assume directory names end with a /
  112. header.set_type_flag(TarFileType::Directory);
  113. header.set_mode(mode);
  114. header.set_magic(gnu_magic);
  115. header.set_version(gnu_version);
  116. header.calculate_checksum();
  117. VERIFY(m_stream.write_or_error(Bytes { &header, sizeof(header) }));
  118. u8 padding[block_size] = { 0 };
  119. VERIFY(m_stream.write_or_error(Bytes { &padding, block_size - sizeof(header) }));
  120. }
  121. void TarOutputStream::add_file(String const& path, mode_t mode, ReadonlyBytes bytes)
  122. {
  123. VERIFY(!m_finished);
  124. TarFileHeader header {};
  125. header.set_size(bytes.size());
  126. header.set_filename_and_prefix(path);
  127. header.set_type_flag(TarFileType::NormalFile);
  128. header.set_mode(mode);
  129. header.set_magic(gnu_magic);
  130. header.set_version(gnu_version);
  131. header.calculate_checksum();
  132. VERIFY(m_stream.write_or_error(ReadonlyBytes { &header, sizeof(header) }));
  133. constexpr Array<u8, block_size> padding { 0 };
  134. VERIFY(m_stream.write_or_error(ReadonlyBytes { &padding, block_size - sizeof(header) }));
  135. size_t n_written = 0;
  136. while (n_written < bytes.size()) {
  137. n_written += m_stream.write(bytes.slice(n_written, min(bytes.size() - n_written, block_size)));
  138. }
  139. VERIFY(m_stream.write_or_error(ReadonlyBytes { &padding, block_size - (n_written % block_size) }));
  140. }
  141. void TarOutputStream::finish()
  142. {
  143. VERIFY(!m_finished);
  144. constexpr Array<u8, block_size> padding { 0 };
  145. m_stream.write_or_error(ReadonlyBytes { &padding, block_size }); // 2 empty records that are used to signify the end of the archive
  146. m_stream.write_or_error(ReadonlyBytes { &padding, block_size });
  147. m_finished = true;
  148. }
  149. }