195ec533aSDimitry Andric //===-- TarWriter.cpp - Tar archive file creator --------------------------===// 295ec533aSDimitry Andric // 395ec533aSDimitry Andric // The LLVM Compiler Infrastructure 495ec533aSDimitry Andric // 595ec533aSDimitry Andric // This file is distributed under the University of Illinois Open Source 695ec533aSDimitry Andric // License. See LICENSE.TXT for details. 795ec533aSDimitry Andric // 895ec533aSDimitry Andric //===----------------------------------------------------------------------===// 995ec533aSDimitry Andric // 1095ec533aSDimitry Andric // TarWriter class provides a feature to create a tar archive file. 1195ec533aSDimitry Andric // 1295ec533aSDimitry Andric // I put emphasis on simplicity over comprehensiveness when implementing this 1395ec533aSDimitry Andric // class because we don't need a full-fledged archive file generator in LLVM 1495ec533aSDimitry Andric // at the moment. 1595ec533aSDimitry Andric // 1695ec533aSDimitry Andric // The filename field in the Unix V7 tar header is 100 bytes. Longer filenames 1795ec533aSDimitry Andric // are stored using the PAX extension. The PAX header is standardized in 1895ec533aSDimitry Andric // POSIX.1-2001. 1995ec533aSDimitry Andric // 2095ec533aSDimitry Andric // The struct definition of UstarHeader is copied from 2195ec533aSDimitry Andric // https://www.freebsd.org/cgi/man.cgi?query=tar&sektion=5 2295ec533aSDimitry Andric // 2395ec533aSDimitry Andric //===----------------------------------------------------------------------===// 2495ec533aSDimitry Andric 2595ec533aSDimitry Andric #include "llvm/Support/TarWriter.h" 2695ec533aSDimitry Andric #include "llvm/ADT/StringRef.h" 2795ec533aSDimitry Andric #include "llvm/Support/FileSystem.h" 2895ec533aSDimitry Andric #include "llvm/Support/MathExtras.h" 2924e2fe98SDimitry Andric #include "llvm/Support/Path.h" 3095ec533aSDimitry Andric 3195ec533aSDimitry Andric using namespace llvm; 3295ec533aSDimitry Andric 3395ec533aSDimitry Andric // Each file in an archive must be aligned to this block size. 3495ec533aSDimitry Andric static const int BlockSize = 512; 3595ec533aSDimitry Andric 3695ec533aSDimitry Andric struct UstarHeader { 3795ec533aSDimitry Andric char Name[100]; 3895ec533aSDimitry Andric char Mode[8]; 3995ec533aSDimitry Andric char Uid[8]; 4095ec533aSDimitry Andric char Gid[8]; 4195ec533aSDimitry Andric char Size[12]; 4295ec533aSDimitry Andric char Mtime[12]; 4395ec533aSDimitry Andric char Checksum[8]; 4495ec533aSDimitry Andric char TypeFlag; 4595ec533aSDimitry Andric char Linkname[100]; 4695ec533aSDimitry Andric char Magic[6]; 4795ec533aSDimitry Andric char Version[2]; 4895ec533aSDimitry Andric char Uname[32]; 4995ec533aSDimitry Andric char Gname[32]; 5095ec533aSDimitry Andric char DevMajor[8]; 5195ec533aSDimitry Andric char DevMinor[8]; 5295ec533aSDimitry Andric char Prefix[155]; 5395ec533aSDimitry Andric char Pad[12]; 5495ec533aSDimitry Andric }; 5595ec533aSDimitry Andric static_assert(sizeof(UstarHeader) == BlockSize, "invalid Ustar header"); 5695ec533aSDimitry Andric 57f1a29dd3SDimitry Andric static UstarHeader makeUstarHeader() { 58f1a29dd3SDimitry Andric UstarHeader Hdr = {}; 59f1a29dd3SDimitry Andric memcpy(Hdr.Magic, "ustar", 5); // Ustar magic 60f1a29dd3SDimitry Andric memcpy(Hdr.Version, "00", 2); // Ustar version 61f1a29dd3SDimitry Andric return Hdr; 62f1a29dd3SDimitry Andric } 63f1a29dd3SDimitry Andric 6495ec533aSDimitry Andric // A PAX attribute is in the form of "<length> <key>=<value>\n" 6595ec533aSDimitry Andric // where <length> is the length of the entire string including 6695ec533aSDimitry Andric // the length field itself. An example string is this. 6795ec533aSDimitry Andric // 6895ec533aSDimitry Andric // 25 ctime=1084839148.1212\n 6995ec533aSDimitry Andric // 7095ec533aSDimitry Andric // This function create such string. 7195ec533aSDimitry Andric static std::string formatPax(StringRef Key, StringRef Val) { 7295ec533aSDimitry Andric int Len = Key.size() + Val.size() + 3; // +3 for " ", "=" and "\n" 7395ec533aSDimitry Andric 7495ec533aSDimitry Andric // We need to compute total size twice because appending 7595ec533aSDimitry Andric // a length field could change total size by one. 7695ec533aSDimitry Andric int Total = Len + Twine(Len).str().size(); 7795ec533aSDimitry Andric Total = Len + Twine(Total).str().size(); 7895ec533aSDimitry Andric return (Twine(Total) + " " + Key + "=" + Val + "\n").str(); 7995ec533aSDimitry Andric } 8095ec533aSDimitry Andric 8195ec533aSDimitry Andric // Headers in tar files must be aligned to 512 byte boundaries. 8295ec533aSDimitry Andric // This function forwards the current file position to the next boundary. 8395ec533aSDimitry Andric static void pad(raw_fd_ostream &OS) { 8495ec533aSDimitry Andric uint64_t Pos = OS.tell(); 8595ec533aSDimitry Andric OS.seek(alignTo(Pos, BlockSize)); 8695ec533aSDimitry Andric } 8795ec533aSDimitry Andric 8895ec533aSDimitry Andric // Computes a checksum for a tar header. 8995ec533aSDimitry Andric static void computeChecksum(UstarHeader &Hdr) { 9095ec533aSDimitry Andric // Before computing a checksum, checksum field must be 9195ec533aSDimitry Andric // filled with space characters. 9295ec533aSDimitry Andric memset(Hdr.Checksum, ' ', sizeof(Hdr.Checksum)); 9395ec533aSDimitry Andric 9495ec533aSDimitry Andric // Compute a checksum and set it to the checksum field. 9595ec533aSDimitry Andric unsigned Chksum = 0; 9695ec533aSDimitry Andric for (size_t I = 0; I < sizeof(Hdr); ++I) 9795ec533aSDimitry Andric Chksum += reinterpret_cast<uint8_t *>(&Hdr)[I]; 9895ec533aSDimitry Andric snprintf(Hdr.Checksum, sizeof(Hdr.Checksum), "%06o", Chksum); 9995ec533aSDimitry Andric } 10095ec533aSDimitry Andric 10195ec533aSDimitry Andric // Create a tar header and write it to a given output stream. 10295ec533aSDimitry Andric static void writePaxHeader(raw_fd_ostream &OS, StringRef Path) { 10395ec533aSDimitry Andric // A PAX header consists of a 512-byte header followed 10495ec533aSDimitry Andric // by key-value strings. First, create key-value strings. 10595ec533aSDimitry Andric std::string PaxAttr = formatPax("path", Path); 10695ec533aSDimitry Andric 10795ec533aSDimitry Andric // Create a 512-byte header. 108f1a29dd3SDimitry Andric UstarHeader Hdr = makeUstarHeader(); 10995ec533aSDimitry Andric snprintf(Hdr.Size, sizeof(Hdr.Size), "%011zo", PaxAttr.size()); 11095ec533aSDimitry Andric Hdr.TypeFlag = 'x'; // PAX magic 11195ec533aSDimitry Andric computeChecksum(Hdr); 11295ec533aSDimitry Andric 11395ec533aSDimitry Andric // Write them down. 11495ec533aSDimitry Andric OS << StringRef(reinterpret_cast<char *>(&Hdr), sizeof(Hdr)); 11595ec533aSDimitry Andric OS << PaxAttr; 11695ec533aSDimitry Andric pad(OS); 11795ec533aSDimitry Andric } 11895ec533aSDimitry Andric 1192cab237bSDimitry Andric // Path fits in a Ustar header if 1202cab237bSDimitry Andric // 1212cab237bSDimitry Andric // - Path is less than 100 characters long, or 1222cab237bSDimitry Andric // - Path is in the form of "<prefix>/<name>" where <prefix> is less 1232cab237bSDimitry Andric // than or equal to 155 characters long and <name> is less than 100 1242cab237bSDimitry Andric // characters long. Both <prefix> and <name> can contain extra '/'. 1252cab237bSDimitry Andric // 1262cab237bSDimitry Andric // If Path fits in a Ustar header, updates Prefix and Name and returns true. 1272cab237bSDimitry Andric // Otherwise, returns false. 1282cab237bSDimitry Andric static bool splitUstar(StringRef Path, StringRef &Prefix, StringRef &Name) { 1292cab237bSDimitry Andric if (Path.size() < sizeof(UstarHeader::Name)) { 1302cab237bSDimitry Andric Prefix = ""; 1312cab237bSDimitry Andric Name = Path; 1322cab237bSDimitry Andric return true; 13324e2fe98SDimitry Andric } 13424e2fe98SDimitry Andric 1352cab237bSDimitry Andric size_t Sep = Path.rfind('/', sizeof(UstarHeader::Prefix) + 1); 1362cab237bSDimitry Andric if (Sep == StringRef::npos) 1372cab237bSDimitry Andric return false; 1382cab237bSDimitry Andric if (Path.size() - Sep - 1 >= sizeof(UstarHeader::Name)) 1392cab237bSDimitry Andric return false; 1402cab237bSDimitry Andric 1412cab237bSDimitry Andric Prefix = Path.substr(0, Sep); 1422cab237bSDimitry Andric Name = Path.substr(Sep + 1); 1432cab237bSDimitry Andric return true; 14424e2fe98SDimitry Andric } 14524e2fe98SDimitry Andric 14695ec533aSDimitry Andric // The PAX header is an extended format, so a PAX header needs 14795ec533aSDimitry Andric // to be followed by a "real" header. 1482cab237bSDimitry Andric static void writeUstarHeader(raw_fd_ostream &OS, StringRef Prefix, 1492cab237bSDimitry Andric StringRef Name, size_t Size) { 150f1a29dd3SDimitry Andric UstarHeader Hdr = makeUstarHeader(); 15124e2fe98SDimitry Andric memcpy(Hdr.Name, Name.data(), Name.size()); 15295ec533aSDimitry Andric memcpy(Hdr.Mode, "0000664", 8); 15395ec533aSDimitry Andric snprintf(Hdr.Size, sizeof(Hdr.Size), "%011zo", Size); 15424e2fe98SDimitry Andric memcpy(Hdr.Prefix, Prefix.data(), Prefix.size()); 15595ec533aSDimitry Andric computeChecksum(Hdr); 15695ec533aSDimitry Andric OS << StringRef(reinterpret_cast<char *>(&Hdr), sizeof(Hdr)); 15795ec533aSDimitry Andric } 15895ec533aSDimitry Andric 15995ec533aSDimitry Andric // Creates a TarWriter instance and returns it. 16095ec533aSDimitry Andric Expected<std::unique_ptr<TarWriter>> TarWriter::create(StringRef OutputPath, 16195ec533aSDimitry Andric StringRef BaseDir) { 16295ec533aSDimitry Andric int FD; 16395ec533aSDimitry Andric if (std::error_code EC = openFileForWrite(OutputPath, FD, sys::fs::F_None)) 16495ec533aSDimitry Andric return make_error<StringError>("cannot open " + OutputPath, EC); 16595ec533aSDimitry Andric return std::unique_ptr<TarWriter>(new TarWriter(FD, BaseDir)); 16695ec533aSDimitry Andric } 16795ec533aSDimitry Andric 16895ec533aSDimitry Andric TarWriter::TarWriter(int FD, StringRef BaseDir) 16995ec533aSDimitry Andric : OS(FD, /*shouldClose=*/true, /*unbuffered=*/false), BaseDir(BaseDir) {} 17095ec533aSDimitry Andric 17195ec533aSDimitry Andric // Append a given file to an archive. 17295ec533aSDimitry Andric void TarWriter::append(StringRef Path, StringRef Data) { 17395ec533aSDimitry Andric // Write Path and Data. 1742cab237bSDimitry Andric std::string Fullpath = BaseDir + "/" + sys::path::convert_to_slash(Path); 1752cab237bSDimitry Andric 1762cab237bSDimitry Andric // We do not want to include the same file more than once. 1772cab237bSDimitry Andric if (!Files.insert(Fullpath).second) 1782cab237bSDimitry Andric return; 1792cab237bSDimitry Andric 1802cab237bSDimitry Andric StringRef Prefix; 1812cab237bSDimitry Andric StringRef Name; 1822cab237bSDimitry Andric if (splitUstar(Fullpath, Prefix, Name)) { 1832cab237bSDimitry Andric writeUstarHeader(OS, Prefix, Name, Data.size()); 18495ec533aSDimitry Andric } else { 1852cab237bSDimitry Andric writePaxHeader(OS, Fullpath); 1862cab237bSDimitry Andric writeUstarHeader(OS, "", "", Data.size()); 18795ec533aSDimitry Andric } 18895ec533aSDimitry Andric 18995ec533aSDimitry Andric OS << Data; 19095ec533aSDimitry Andric pad(OS); 19195ec533aSDimitry Andric 19295ec533aSDimitry Andric // POSIX requires tar archives end with two null blocks. 19395ec533aSDimitry Andric // Here, we write the terminator and then seek back, so that 19495ec533aSDimitry Andric // the file being output is terminated correctly at any moment. 19595ec533aSDimitry Andric uint64_t Pos = OS.tell(); 19695ec533aSDimitry Andric OS << std::string(BlockSize * 2, '\0'); 19795ec533aSDimitry Andric OS.seek(Pos); 19895ec533aSDimitry Andric OS.flush(); 19995ec533aSDimitry Andric } 200