TarStream.cpp 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. /*
  2. * Copyright (c) 2020, Peter Elliott <pelliott@ualberta.ca>
  3. * Copyright (c) 2021, Idan Horowitz <idan.horowitz@gmail.com>
  4. * All rights reserved.
  5. *
  6. * Redistribution and use in source and binary forms, with or without
  7. * modification, are permitted provided that the following conditions are met:
  8. *
  9. * 1. Redistributions of source code must retain the above copyright notice, this
  10. * list of conditions and the following disclaimer.
  11. *
  12. * 2. Redistributions in binary form must reproduce the above copyright notice,
  13. * this list of conditions and the following disclaimer in the documentation
  14. * and/or other materials provided with the distribution.
  15. *
  16. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  17. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  18. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  19. * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  20. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  21. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  22. * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  23. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  24. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  25. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  26. */
  27. #include <LibArchive/TarStream.h>
  28. #include <string.h>
  29. namespace Archive {
  30. TarFileStream::TarFileStream(TarInputStream& tar_stream)
  31. : m_tar_stream(tar_stream)
  32. , m_generation(tar_stream.m_generation)
  33. {
  34. }
  35. size_t TarFileStream::read(Bytes bytes)
  36. {
  37. // verify that the stream has not advanced
  38. VERIFY(m_tar_stream.m_generation == m_generation);
  39. if (has_any_error())
  40. return 0;
  41. auto to_read = min(bytes.size(), m_tar_stream.header().size() - m_tar_stream.m_file_offset);
  42. auto nread = m_tar_stream.m_stream.read(bytes.trim(to_read));
  43. m_tar_stream.m_file_offset += nread;
  44. return nread;
  45. }
  46. bool TarFileStream::unreliable_eof() const
  47. {
  48. // verify that the stream has not advanced
  49. VERIFY(m_tar_stream.m_generation == m_generation);
  50. return m_tar_stream.m_stream.unreliable_eof()
  51. || m_tar_stream.m_file_offset >= m_tar_stream.header().size();
  52. }
  53. bool TarFileStream::read_or_error(Bytes bytes)
  54. {
  55. // verify that the stream has not advanced
  56. VERIFY(m_tar_stream.m_generation == m_generation);
  57. if (read(bytes) < bytes.size()) {
  58. set_fatal_error();
  59. return false;
  60. }
  61. return true;
  62. }
  63. bool TarFileStream::discard_or_error(size_t count)
  64. {
  65. // verify that the stream has not advanced
  66. VERIFY(m_tar_stream.m_generation == m_generation);
  67. if (count > m_tar_stream.header().size() - m_tar_stream.m_file_offset) {
  68. return false;
  69. }
  70. m_tar_stream.m_file_offset += count;
  71. return m_tar_stream.m_stream.discard_or_error(count);
  72. }
  73. TarInputStream::TarInputStream(InputStream& stream)
  74. : m_stream(stream)
  75. {
  76. if (!m_stream.read_or_error(Bytes(&m_header, sizeof(m_header)))) {
  77. m_finished = true;
  78. m_stream.handle_any_error(); // clear out errors so we dont assert
  79. return;
  80. }
  81. VERIFY(m_stream.discard_or_error(block_size - sizeof(TarFileHeader)));
  82. }
  83. static constexpr unsigned long block_ceiling(unsigned long offset)
  84. {
  85. return block_size * (1 + ((offset - 1) / block_size));
  86. }
  87. void TarInputStream::advance()
  88. {
  89. if (m_finished)
  90. return;
  91. m_generation++;
  92. VERIFY(m_stream.discard_or_error(block_ceiling(m_header.size()) - m_file_offset));
  93. m_file_offset = 0;
  94. if (!m_stream.read_or_error(Bytes(&m_header, sizeof(m_header)))) {
  95. m_finished = true;
  96. return;
  97. }
  98. if (!valid()) {
  99. m_finished = true;
  100. return;
  101. }
  102. VERIFY(m_stream.discard_or_error(block_size - sizeof(TarFileHeader)));
  103. }
  104. bool TarInputStream::valid() const
  105. {
  106. auto& header_magic = header().magic();
  107. auto& header_version = header().version();
  108. if (!((header_magic == gnu_magic && header_version == gnu_version)
  109. || (header_magic == ustar_magic && header_version == ustar_version)
  110. || (header_magic == posix1_tar_magic && header_version == posix1_tar_version)))
  111. return false;
  112. // POSIX.1-1988 tar does not have magic numbers, so we also neet to verify the header checksum.
  113. return header().checksum() == header().expected_checksum();
  114. }
  115. TarFileStream TarInputStream::file_contents()
  116. {
  117. VERIFY(!m_finished);
  118. return TarFileStream(*this);
  119. }
  120. TarOutputStream::TarOutputStream(OutputStream& stream)
  121. : m_stream(stream)
  122. {
  123. }
  124. void TarOutputStream::add_directory(const String& path, mode_t mode)
  125. {
  126. VERIFY(!m_finished);
  127. TarFileHeader header;
  128. memset(&header, 0, sizeof(header));
  129. header.set_size(0);
  130. header.set_file_name(String::formatted("{}/", path)); // Old tar implementations assume directory names end with a /
  131. header.set_type_flag(TarFileType::Directory);
  132. header.set_mode(mode);
  133. header.set_magic(gnu_magic);
  134. header.set_version(gnu_version);
  135. header.calculate_checksum();
  136. VERIFY(m_stream.write_or_error(Bytes { &header, sizeof(header) }));
  137. u8 padding[block_size] = { 0 };
  138. VERIFY(m_stream.write_or_error(Bytes { &padding, block_size - sizeof(header) }));
  139. }
  140. void TarOutputStream::add_file(const String& path, mode_t mode, const ReadonlyBytes& bytes)
  141. {
  142. VERIFY(!m_finished);
  143. TarFileHeader header;
  144. memset(&header, 0, sizeof(header));
  145. header.set_size(bytes.size());
  146. header.set_file_name(path);
  147. header.set_type_flag(TarFileType::NormalFile);
  148. header.set_mode(mode);
  149. header.set_magic(gnu_magic);
  150. header.set_version(gnu_version);
  151. header.calculate_checksum();
  152. VERIFY(m_stream.write_or_error(Bytes { &header, sizeof(header) }));
  153. u8 padding[block_size] = { 0 };
  154. VERIFY(m_stream.write_or_error(Bytes { &padding, block_size - sizeof(header) }));
  155. size_t n_written = 0;
  156. while (n_written < bytes.size()) {
  157. n_written += m_stream.write(bytes.slice(n_written, min(bytes.size() - n_written, block_size)));
  158. }
  159. VERIFY(m_stream.write_or_error(Bytes { &padding, block_size - (n_written % block_size) }));
  160. }
  161. void TarOutputStream::finish()
  162. {
  163. VERIFY(!m_finished);
  164. u8 padding[block_size] = { 0 };
  165. m_stream.write_or_error(Bytes { &padding, block_size }); // 2 empty records that are used to signify the end of the archive
  166. m_stream.write_or_error(Bytes { &padding, block_size });
  167. m_finished = true;
  168. }
  169. }