blob: fe9c0c66cc37536d31441eb71bfb17c719e79ca8 [file] [log] [blame]
// Copyright (C) 2018 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <sys/types.h> // for gid_t, mode_t, uid_t
#include <cstdint> // for uint64_t, uint8_t
#include <iosfwd> // for basic_ios
#include <ostream> // for istream, ostream
#include <string> // for string
#include "android/utils/file_io.h" // for android_mkdir, android_stat
#ifdef _MSC_VER
#include "aemu/base/msvc.h"
using uid_t = uint32_t;
using gid_t = uint32_t;
#endif
namespace android {
namespace base {
enum TarType {
REGTYPE = 0, // regular file
LNKTYPE = 1, // link (inside tarfile)
SYMTYPE = 2, // symbolic link
CHRTYPE = 3, // character special device
BLKTYPE = 4, // block special device
DIRTYPE = 5, // directory
FIFOTYPE = 6, // fifo special device
CONTTYPE = 7, // contiguous file
};
enum HeaderTypes : char {
XHDTYPE = 'x', // POSIX.1-2001 extended header
XGLTYPE = 'g', // POSIX.1-2001 global header
SOLARIS_XHDTYPE = 'X', // Solaris extended header
GNUTYPE_LONGNAME = 'L', // GNU tar longname
GNUTYPE_LONGLINK = 'K', // GNU tar longlink
GNUTYPE_SPARSE = 'S' // GNU tar sparse file
};
enum class TarFormat {
USTAR_FORMAT = 0, // POSIX.1-1988 (ustar) format
GNU_FORMAT = 1, // GNU tar format
PAX_FORMAT = 2 // POSIX.1-2001 (pax) format
};
// MAGIC USTAR HEADER
#define USTAR "ustar "
#define TARBLOCK 512
/* POSIX header. */
using octalnr = char;
/* tar Header Block, from POSIX 1003.1-1990.
should be exactly the size of a tarblock.
*/
struct posix_header { /* byte offset */
char name[100]; /* 0 */
octalnr mode[8]; /* 100 */
octalnr uid[8]; /* 108 */
octalnr gid[8]; /* 116 */
octalnr size[12]; /* 124 */
octalnr mtime[12]; /* 136 */
octalnr chksum[8]; /* 148 */
uint8_t typeflag; /* 156 */
char linkname[100]; /* 157 */
char magic[6]; /* 257, should contain "ustar\0" */
octalnr version[2]; /* 263 */
char uname[32]; /* 265 */
char gname[32]; /* 297 */
octalnr devmajor[8]; /* 329 */
octalnr devminor[8]; /* 337 */
octalnr prefix[155]; /* 345 */
octalnr padding[12]; /* 500 */
/* 512 */
};
static_assert(sizeof(posix_header) == TARBLOCK,
"Size of the tar header should be 512 bytes");
// A class that can add files/directories to a stream as tar objects.
// The tar entries will be written using the ustar fromat.
// Only files and directories are supported (no symlinks).
// And filenames up to 99 characters in length.
//
// Make sure to call close when you are finished adding files.
class TarWriter : public std::ios {
public:
TarWriter(std::string cwd, std::ostream& dest, size_t bufsize = 4096);
~TarWriter();
// Add a file entry using the given stream, filename and stats.
bool addFileEntryFromStream(std::istream& src,
std::string fname,
struct stat sb);
bool addFileEntry(std::string fname);
bool addDirectoryEntry(std::string dname);
bool addDirectory(std::string dname);
// Closes the tar archive and flushes the underlying stream.
bool close();
std::string error_msg() { return mErrMsg; };
private:
bool error(std::string msg);
bool writeTarHeader(std::string name);
bool writeTarHeader(std::string fname, bool isDir, struct stat sb);
std::ostream& mDest;
std::string mCwd{};
std::string mErrMsg{};
bool mClosed = false;
size_t mBufferSize;
};
struct TarInfo {
std::string name; // member name
mode_t mode = 0644; // file permissions
uid_t uid = 0; // user id
gid_t gid = 0; // group id
uint64_t size = 0; // file size
uint64_t mtime = 0; // modification time
TarType type = REGTYPE; // member type
std::string uname = ""; // user name
std::string gname = ""; // group name
uint64_t offset = 0; // offset in stream
bool valid = false; // True if this is a valid entry.
};
class TarReader : public std::ios {
public:
TarReader(std::string cwd, std::istream& src, bool has_seek = false);
TarInfo next(TarInfo from);
TarInfo first() { return next({}); }
bool extract(TarInfo src);
std::string error_msg() { return mErrMsg; };
private:
void seekg(std::streampos pos);
bool error(std::string msg);
std::istream& mSrc;
std::string mCwd{};
std::string mErrMsg{};
bool mSeek = false;
};
} // namespace base
} // namespace android