12namespace fs = std::filesystem;
21 using u8 =
unsigned char;
43 virtual bool eof()
const = 0;
65 f.read(
reinterpret_cast<char*
>(dst), std::streamsize(n));
73 bool eof()
const override {
return f.eof(); }
86 explicit GzReader(
const std::string& p) {
g = gzopen(p.c_str(),
"rb"); gzbuffer(
g, 1 << 20); }
102 int r = gzread(
g, dst + got,
unsigned(n - got));
103 if (r <= 0)
return false;
113 bool eof()
const override {
return gzeof(
g) != 0; }
122 static inline bool starts_with(
const fs::path& a,
const fs::path& base) {
123 auto A = fs::weakly_canonical(a);
124 auto B = fs::weakly_canonical(base);
125 auto ar = A.begin(), br = B.begin();
126 for (; br != B.end(); ++br, ++ar) {
127 if (ar == A.end() || *ar != *br)
return false;
138 static inline bool all_zero(
const u8* p,
size_t n) {
139 for (
size_t i = 0; i < n; ++i)
if (p[i] != 0)
return false;
152 while (i < n && (p[i] ==
' ' || p[i] ==
'\t' || p[i] ==
'\0')) ++i;
154 if (p[i] <
'0' || p[i] >
'7')
break;
155 v = (v << 3) + u64(p[i] -
'0');
167 for (
size_t i = 0; i < 512; ++i) {
168 if (i >= 148 && i < 156) s += 32;
221 const TarHdr& h = *
reinterpret_cast<const TarHdr*
>(blk);
242 fs::perms perm = fs::perms::none;
243 if (mode & 0400) perm |= fs::perms::owner_read;
244 if (mode & 0200) perm |= fs::perms::owner_write;
245 if (mode & 0100) perm |= fs::perms::owner_exec;
246 if (mode & 0040) perm |= fs::perms::group_read;
247 if (mode & 0020) perm |= fs::perms::group_write;
248 if (mode & 0010) perm |= fs::perms::group_exec;
249 if (mode & 0004) perm |= fs::perms::others_read;
250 if (mode & 0002) perm |= fs::perms::others_write;
251 if (mode & 0001) perm |= fs::perms::others_exec;
252 fs::permissions(p, perm, fs::perm_options::replace, ec);
262 auto tp = std::chrono::time_point<std::chrono::system_clock>(std::chrono::seconds(mtime));
263 auto ftime = fs::file_time_type::clock::now() + (tp - std::chrono::system_clock::now());
264 fs::last_write_time(p, ftime, ec);
285 tmp.resize(
size_t(std::min<u64>(n, 1 << 20)));
288 size_t chunk = size_t(std::min<u64>(left, tmp.size()));
289 if (!r.
read_exact(tmp.data(), chunk))
return false;
315 fs::create_directories(dest.
c_str(), ec);
318 std::ifstream probe(src.
c_str(), std::ios::binary);
319 if (!probe)
return -2;
320 unsigned char sig[2] = { 0,0 };
321 probe.read(
reinterpret_cast<char*
>(sig), 2);
324 std::unique_ptr<Reader> rd;
325 if (sig[0] == 0x1F && sig[1] == 0x8B) rd = std::make_unique<GzReader>(src.
c_str());
326 else rd = std::make_unique<FileReader>(src.
c_str());
328 if (
auto* gr =
dynamic_cast<GzReader*
>(rd.get()); gr && !gr->
g)
return -3;
329 if (
auto* fr =
dynamic_cast<FileReader*
>(rd.get()); fr && !fr->
f)
return -3;
331 fs::path base = fs::absolute(dest.
c_str());
332 std::vector<u8> block(512), tmp;
333 std::string pax_path, pax_linkpath, gnu_longname, gnu_longlink;
336 if (!
read_block(*rd, block.data()))
return -4;
339 if (
all_zero(block.data(), 512))
return 0;
343 const TarHdr& h = *
reinterpret_cast<const TarHdr*
>(block.data());
350 if (!gnu_longname.empty()) {
name = gnu_longname; gnu_longname.clear(); }
351 if (!pax_path.empty()) {
name = pax_path; pax_path.clear(); }
353 std::string linkname;
355 if (!gnu_longlink.empty()) { linkname = gnu_longlink; gnu_longlink.clear(); }
356 if (!pax_linkpath.empty()) { linkname = pax_linkpath; pax_linkpath.clear(); }
358 if (!std::strncmp(h.
magic,
"ustar", 5)) {}
362 pax.resize(
size_t(size));
364 tmp.resize(
size_t(size));
365 if (!rd->read_exact(tmp.data(),
size_t(size)))
return -6;
366 pax.assign(
reinterpret_cast<char*
>(tmp.data()), pax.size());
368 u64 pad = (512 - (size % 512)) % 512;
369 if (pad && !
skip_bytes(*rd, pad, tmp))
return -6;
371 while (i < pax.size()) {
372 size_t j = pax.find(
'\n', i);
373 if (j == std::string::npos)
break;
374 std::string line = pax.substr(i, j - i);
375 size_t sp = line.find(
' ');
376 if (sp != std::string::npos) {
377 std::string kv = line.substr(sp + 1);
378 if (kv.rfind(
"path=", 0) == 0) pax_path = kv.substr(5);
379 else if (kv.rfind(
"linkpath=", 0) == 0) pax_linkpath = kv.substr(9);
390 u64 pad = (512 - (size % 512)) % 512;
391 if (pad && !
skip_bytes(*rd, pad, tmp))
return -7;
396 if (size == 0)
continue;
397 tmp.resize(
size_t(size));
398 if (!rd->read_exact(tmp.data(),
size_t(size)))
return -8;
399 gnu_longname.assign(
reinterpret_cast<char*
>(tmp.data()),
size_t(size));
400 while (!gnu_longname.empty() && (gnu_longname.back() ==
'\0' || gnu_longname.back() ==
'\n')) gnu_longname.pop_back();
401 u64 pad = (512 - (size % 512)) % 512;
402 if (pad && !
skip_bytes(*rd, pad, tmp))
return -8;
407 if (size == 0)
continue;
408 tmp.resize(
size_t(size));
409 if (!rd->read_exact(tmp.data(),
size_t(size)))
return -9;
410 gnu_longlink.assign(
reinterpret_cast<char*
>(tmp.data()),
size_t(size));
411 while (!gnu_longlink.empty() && (gnu_longlink.back() ==
'\0' || gnu_longlink.back() ==
'\n')) gnu_longlink.pop_back();
412 u64 pad = (512 - (size % 512)) % 512;
413 if (pad && !
skip_bytes(*rd, pad, tmp))
return -9;
417 fs::path out = fs::absolute(base / fs::path(
name).lexically_normal());
419 u64 skip = size + ((512 - (size % 512)) % 512);
420 if (skip && !
skip_bytes(*rd, skip, tmp))
return -10;
428 fs::create_directories(out.parent_path(), ec);
429 std::ofstream of(out, std::ios::binary | std::ios::trunc);
432 tmp.resize(
size_t(std::min<u64>(left, 1 << 20)));
434 size_t chunk = size_t(std::min<u64>(left, tmp.size()));
435 if (!rd->read_exact(tmp.data(), chunk))
return -12;
436 of.write(
reinterpret_cast<char*
>(tmp.data()), std::streamsize(chunk));
443 u64 pad = (512 - (size % 512)) % 512;
444 if (pad && !
skip_bytes(*rd, pad, tmp))
return -12;
447 fs::create_directories(out, ec);
449 u64 pad = (512 - (size % 512)) % 512;
450 if (pad && !
skip_bytes(*rd, pad, tmp))
return -13;
453 fs::create_directories(out.parent_path(), ec);
455 fs::remove(out, sec);
456 if (!linkname.empty()) {
458 fs::create_symlink(linkname, out, lec);
460 u64 pad = (512 - (size % 512)) % 512;
461 if (pad && !
skip_bytes(*rd, pad, tmp))
return -14;
464 fs::create_directories(out.parent_path(), ec);
465 if (!linkname.empty()) {
467 if (fs::exists(base / linkname)) fs::create_hard_link(base / linkname, out, hec);
469 u64 pad = (512 - (size % 512)) % 512;
470 if (pad && !
skip_bytes(*rd, pad, tmp))
return -15;
473 u64 pad_all = size + ((512 - (size % 512)) % 512);
474 if (pad_all && !
skip_bytes(*rd, pad_all, tmp))
return -16;
Logic for reading or writing to a file as well as navigating filesystems or other common file operati...
constexpr const char * c_str() const
Used to access the raw memory of this string.
Provides utilities for reading and extracting TAR and TAR.GZ archives.
static int Extract(const File &src, const File &dest)
Extracts all entries from a TAR or TAR.GZ archive to a destination directory.
static bool skip_bytes(Reader &r, u64 n, std::vector< u8 > &tmp)
Skips over a specified number of bytes in the reader stream.
static bool valid_hdr(const u8 *blk)
Validates a 512-byte TAR header block by verifying its checksum.
static std::string read_pax_string(const std::string &rec)
Returns a PAX extended attribute record string as-is.
static bool read_block(Reader &r, u8 *buf)
Reads a single 512-byte block from the reader.
static bool all_zero(const u8 *p, size_t n)
Checks whether all bytes in a buffer are zero.
static u64 tar_checksum(const u8 *h)
Computes the TAR checksum for a 512-byte header block.
static void apply_mtime(const fs::path &p, u64 mtime)
Sets the last modification time of a file to the given Unix timestamp.
static bool starts_with(const fs::path &a, const fs::path &base)
Checks whether path a starts with the given base path using weakly canonical forms.
static std::string hdr_name(const TarHdr &h)
Extracts the full file name from a TAR header, combining prefix and name fields.
static u64 parse_octal(const char *p, size_t n)
Parses an octal number from a TAR header field string.
static void apply_mode(const fs::path &p, u64 mode)
Applies POSIX file permissions to the given path based on a TAR mode value.
The primary namespace for the NDEVR SDK.
@ type
The type identifier string for this model node.
@ name
The display name of the object.
Reader implementation for uncompressed files using std::ifstream.
bool eof() const override
Checks whether the end of the file has been reached.
FileReader(const std::string &p)
Constructs a FileReader and opens the specified file in binary mode.
bool read_exact(u8 *dst, size_t n) override
Reads exactly n bytes from the file into the destination buffer.
std::ifstream f
The underlying file input stream.
Reader implementation for gzip-compressed files using zlib's gzFile interface.
GzReader(const std::string &p)
Constructs a GzReader and opens the specified gzip file for reading.
bool read_exact(u8 *dst, size_t n) override
Reads exactly n bytes from the gzip stream into the destination buffer.
gzFile g
The underlying zlib gzip file handle.
bool eof() const override
Checks whether the end of the gzip stream has been reached.
~GzReader() override
Destructor that closes the gzip file handle if open.
Abstract base class for sequential byte stream reading.
virtual bool read_exact(u8 *dst, size_t n)=0
Reads exactly n bytes into the destination buffer.
virtual bool eof() const =0
Checks whether the end of the stream has been reached.
Represents the raw 512-byte POSIX TAR header structure (USTAR format).
char pad[12]
Padding to fill the 512-byte block.
char version[2]
USTAR version ("00").
char chksum[8]
Header checksum in octal ASCII.
char prefix[155]
Filename prefix for paths longer than 100 characters.
char typeflag
Entry type flag (e.g., '0' for regular file, '5' for directory).
char gid[8]
Owner group ID in octal ASCII.
char mtime[12]
Last modification time in octal ASCII (Unix epoch seconds).
char devminor[8]
Device minor number for special files.
char magic[6]
USTAR magic string ("ustar").
char uid[8]
Owner user ID in octal ASCII.
char devmajor[8]
Device major number for special files.
char linkname[100]
Name of the linked file for hard/symbolic links.
char name[100]
File name (null-terminated or full 100 chars).
char uname[32]
Owner user name.
char mode[8]
File mode in octal ASCII.
char size[12]
File size in octal ASCII.
char gname[32]
Owner group name.