Update to v106r65 release.

byuu says:

This synchronizes bsnes/higan with many recent internal nall changes.

This will be the last WIP until I am situated in Japan. Apologies for the
bugfixes that didn't get applied yet, I ran out of time.
This commit is contained in:
Tim Allen
2018-10-04 20:11:23 +10:00
parent 336d20123f
commit 03b06257d3
75 changed files with 2242 additions and 1371 deletions

View File

@@ -1,272 +0,0 @@
#pragma once
#include <nall/random.hpp>
#include <nall/cipher/chacha20.hpp>
#include <nall/elliptic-curve/ed25519.hpp>
#include <nall/encode/base.hpp>
#include <nall/decode/base.hpp>
#include <nall/encode/lzsa.hpp>
#include <nall/decode/lzsa.hpp>
namespace nall { namespace Beat {
struct Archive {
struct Encryption {
string type;
uint256_t key = 0;
uint192_t nonce = 0;
};
struct Signature {
string type;
uint256_t privateKey = 0;
uint256_t publicKey = 0;
uint512_t signature = 0;
};
struct Compression {
string type;
uint size = 0;
};
//timestamps are human-readable strings in ISO 8601 format; save for T=>space
//times are stored in UTC, rather than local times
struct Timestamps {
string created;
string modified;
string accessed;
};
struct Permissions {
string name;
bool readable = false;
bool writable = false;
bool executable = false;
};
struct Node {
string name;
//paths and files
Timestamps timestamps;
struct {
Permissions owner;
Permissions group;
Permissions other;
} permissions;
//files only
uint offset = 0;
uint size = 0;
Compression compression;
string filename;
vector<uint8_t> filedata;
};
auto append(const Node& node) -> bool;
auto encryptionManifest() -> string;
auto manifest() -> string;
auto create() -> vector<uint8_t>;
//internal functions
auto encode() -> vector<uint8_t>;
auto encode(Node& node, uint64_t offset) -> vector<uint8_t>;
Encryption encryption;
Signature signature;
Compression compression; //solid archiving
vector<Node> nodes;
};
auto Archive::append(const Node& node) -> bool {
//prevent multiple nodes with the same name
if(nodes.find([&](auto& item) { return item.name == node.name; })) return false;
nodes.append(node);
return true;
}
auto Archive::encryptionManifest() -> string {
string manifest;
manifest.append("encryption\n");
manifest.append(" type: ", encryption.type, "\n");
manifest.append(" nonce: ", Encode::Base<57>(encryption.nonce), "\n");
return manifest;
}
auto Archive::manifest() -> string {
string manifest;
manifest.append("archive\n");
for(auto& node : nodes) {
if(node.name.endsWith("/")) {
manifest.append(" path: ", string{node.name}.trimRight("/", 1L), "\n");
} else {
manifest.append(" file: ", node.name, "\n");
manifest.append(" offset: ", node.offset, "\n");
manifest.append(" size: ", node.size, "\n");
if(node.compression.type) {
manifest.append(" compression: ", node.compression.type, "\n");
manifest.append(" size: ", node.compression.size, "\n");
}
}
if(node.timestamps.created || node.timestamps.modified || node.timestamps.accessed) {
manifest.append(" timestamp\n");
if(auto timestamp = node.timestamps.created ) manifest.append(" created: ", timestamp, "\n");
if(auto timestamp = node.timestamps.modified) manifest.append(" modified: ", timestamp, "\n");
if(auto timestamp = node.timestamps.accessed) manifest.append(" accessed: ", timestamp, "\n");
}
if(node.permissions.owner.name || node.permissions.group.name || node.permissions.other.name) {
manifest.append(" permission\n");
if(node.permissions.owner.name) {
manifest.append(" owner: ", node.permissions.owner.name, "\n");
if(node.permissions.owner.readable ) manifest.append(" readable\n");
if(node.permissions.owner.writable ) manifest.append(" writable\n");
if(node.permissions.owner.executable) manifest.append(" executable\n");
}
if(node.permissions.group.name) {
manifest.append(" group: ", node.permissions.group.name, "\n");
if(node.permissions.group.readable ) manifest.append(" readable\n");
if(node.permissions.group.writable ) manifest.append(" writable\n");
if(node.permissions.group.executable) manifest.append(" executable\n");
}
if(node.permissions.other.name) {
manifest.append(" other\n");
if(node.permissions.other.readable ) manifest.append(" readable\n");
if(node.permissions.other.writable ) manifest.append(" writable\n");
if(node.permissions.other.executable) manifest.append(" executable\n");
}
}
}
if(compression.type) {
manifest.append(" compression: ", compression.type, "\n");
manifest.append(" size: ", compression.size, "\n");
}
if(signature.type == "ed25519") {
manifest.append(" signature: ", signature.type, "\n");
manifest.append(" publicKey: ", Encode::Base<57>(signature.publicKey), "\n");
manifest.append(" signature: ", Encode::Base<57>(signature.signature), "\n");
}
return manifest;
}
auto Archive::create() -> vector<uint8_t> {
vector<uint8_t> output;
output.append('B');
output.append('P');
output.append('A');
output.append('1');
nodes.sort([&](auto& lhs, auto& rhs) {
return string::compare(lhs.name, rhs.name) < 0;
});
auto content = encode();
if(compression.type == "lzsa") {
content = Encode::LZSA(content);
compression.size = content.size();
}
if(signature.type == "ed25519") {
EllipticCurve::Ed25519 ed25519;
signature.publicKey = ed25519.publicKey(signature.privateKey);
signature.signature = ed25519.sign(content, signature.privateKey);
}
if(encryption.type == "xchacha20") {
//a randomly generated nonce is preferred
if(!encryption.nonce) {
CSPRNG csprng;
encryption.nonce = csprng.random<uint192_t>();
}
Cipher::XChaCha20 xchacha20{encryption.key, encryption.nonce};
content = xchacha20.encrypt(content);
string manifest;
manifest.append("encryption\n");
manifest.append(" type: ", encryption.type, "\n");
manifest.append(" nonce: ", Encode::Base<57>(encryption.nonce), "\n");
output.append(content);
for(uint8_t byte : manifest) output.append(byte);
output.appendl(manifest.size(), 8);
} else {
encryption = {};
output.append(content);
}
auto sha256 = Hash::SHA256(output).value();
output.appendl(sha256, 32);
return output;
}
//
auto Archive::encode() -> vector<uint8_t> {
vector<uint8_t> output;
for(auto& node : nodes) {
if(node.filename) {
node.timestamps.created = chrono::utc::datetime(inode::timestamp(node.filename, inode::time::create));
node.timestamps.accessed = chrono::utc::datetime(inode::timestamp(node.filename, inode::time::access));
node.timestamps.modified = chrono::utc::datetime(inode::timestamp(node.filename, inode::time::modify));
uint mode = inode::mode(node.filename);
node.permissions.owner.name = inode::user(node.filename);
node.permissions.owner.executable = mode & 0100;
node.permissions.owner.writable = mode & 0200;
node.permissions.owner.readable = mode & 0400;
node.permissions.group.name = inode::group(node.filename);
node.permissions.group.executable = mode & 0010;
node.permissions.group.writable = mode & 0020;
node.permissions.group.readable = mode & 0040;
node.permissions.other.name = " ";
node.permissions.other.executable = mode & 0001;
node.permissions.other.writable = mode & 0002;
node.permissions.other.readable = mode & 0004;
}
if(node.name.endsWith("/")) continue;
auto buffer = encode(node, output.size());
output.append(buffer);
}
auto manifest = this->manifest();
for(auto byte : manifest) output.append(byte);
for(auto byte : range(8)) output.append((uint64_t)manifest.size() >> byte * 8);
return output;
}
auto Archive::encode(Node& node, uint64_t offset) -> vector<uint8_t> {
node.offset = offset;
vector<uint8_t> output;
if(node.filename) {
output = file::read(node.filename);
} else {
output = node.filedata;
}
node.size = output.size();
if(node.compression.type == "lzsa") {
output = Encode::LZSA(output);
node.compression.size = output.size();
} else {
node.compression = {};
}
return output;
}
}}

View File

@@ -0,0 +1,200 @@
#pragma once
#include <nall/beat/archive/node.hpp>
namespace nall { namespace Beat { namespace Archive {
struct Container {
Container(array_view<uint8_t> = {});
~Container();
auto isCompressed() const -> bool { return (bool)compression.type; }
auto isSigned() const -> bool { return (bool)signature.type; }
auto isEncrypted() const -> bool { return (bool)encryption.type; }
auto compressLZSA() -> void;
auto signEd25519(uint256_t privateKey) -> void;
auto encryptXChaCha20(uint256_t privateKey, uint192_t nonce = 0) -> void;
auto validate() -> bool;
auto decryptXChaCha20(uint256_t privateKey) -> bool;
auto verifyEd25519(uint256_t publicKey) -> bool;
auto decompressLZSA() -> bool;
auto append(string name, string location) -> shared_pointer<Node>;
auto appendPath(string name) -> shared_pointer<Node>;
auto appendFile(string name, array_view<uint8_t> memory) -> shared_pointer<Node>;
auto remove(string name) -> bool;
auto find(string name) -> shared_pointer<Node>;
auto sort() -> void;
auto begin() { return nodes.begin(); }
auto end() { return nodes.end(); }
auto begin() const { return nodes.begin(); }
auto end() const { return nodes.end(); }
auto rbegin() { return nodes.rbegin(); }
auto rend() { return nodes.rend(); }
auto rbegin() const { return nodes.rbegin(); }
auto rend() const { return nodes.rend(); }
vector<shared_pointer<Node>> nodes;
vector<uint8_t> memory;
string metadata;
struct Compression {
string type;
} compression;
struct Signature {
string type;
uint256_t privateKey = 0;
uint256_t publicKey = 0;
uint512_t value = 0;
} signature;
struct Encryption {
string type;
uint256_t privateKey = 0;
uint192_t nonce = 0;
} encryption;
};
Container::Container(array_view<uint8_t> memory) {
this->memory.resize(memory.size());
nall::memory::copy(this->memory.data(), memory.data(), memory.size());
}
Container::~Container() {
metadata = {};
signature = {};
encryption = {};
}
//
auto Container::compressLZSA() -> void {
compression.type = "lzsa";
}
auto Container::signEd25519(uint256_t privateKey) -> void {
signature.type = "ed25519";
signature.privateKey = privateKey;
}
auto Container::encryptXChaCha20(uint256_t privateKey, uint192_t nonce) -> void {
if(!nonce) {
CSPRNG::XChaCha20 csprng;
nonce = csprng.random<uint192_t>();
}
encryption.type = "xchacha20";
encryption.privateKey = privateKey;
encryption.nonce = nonce;
}
//
auto Container::validate() -> bool {
array_view<uint8_t> memory = this->memory;
if(memory.size() < 44) return false; //8 (metadata size) + 32 (SHA256) + 4 (signature)
if(memory[memory.size() - 4] != 'B') return false;
if(memory[memory.size() - 3] != 'P') return false;
if(memory[memory.size() - 2] != 'A') return false;
if(memory[memory.size() - 1] != '1') return false;
auto sha256 = memory.readl<uint256_t>(memory.size() - 36, 32);
if(Hash::SHA256({memory.data(), memory.size() - 36}).value() != sha256) return false;
auto size = memory.readl<uint64_t>(memory.size() - 44, 8);
if(size & 1ull << 63) {
size -= 1ull << 63;
metadata = memory.view(memory.size() - 44 - size, size);
uint64_t offset = memory.size() - 44 - size;
for(auto& byte : metadata) byte ^= offset++;
} else {
metadata = memory.view(memory.size() - 44 - size, size);
}
auto document = BML::unserialize(metadata);
if(auto node = document["archive/encryption"]) {
if(node.text() == "xchacha20") {
encryption.type = node.text();
encryption.nonce = Decode::Base<57, uint192_t>(node["nonce"].text());
}
}
if(auto node = document["archive/signature"]) {
if(node.text() == "ed25519") {
signature.type = node.text();
signature.publicKey = Decode::Base<57, uint256_t>(node["publicKey"].text());
signature.value = Decode::Base<57, uint512_t>(node["value"].text());
}
}
if(auto node = document["archive/compression"]) {
compression.type = node.text();
}
return true;
}
auto Container::decryptXChaCha20(uint256_t privateKey) -> bool {
encryption.privateKey = privateKey;
Cipher::XChaCha20 xchacha20{encryption.privateKey, encryption.nonce};
auto size = memory.readl<uint64_t>(memory.size() - 44, 8);
memory = xchacha20.decrypt(memory.view(0, memory.size() - 44 - size));
return true;
}
auto Container::verifyEd25519(uint256_t publicKey) -> bool {
EllipticCurve::Ed25519 ed25519;
auto size = memory.readl<uint64_t>(memory.size() - 44, 8);
return ed25519.verify(memory.view(0, memory.size() - 44 - size), signature.value, publicKey);
}
auto Container::decompressLZSA() -> bool {
memory = Decode::LZSA(memory);
return (bool)memory;
}
//
auto Container::append(string name, string location) -> shared_pointer<Node> {
for(auto& node : nodes) if(node->name == name) return {};
if(auto node = Node::create(name, location)) return nodes.append(node), node;
return {};
}
auto Container::appendPath(string name) -> shared_pointer<Node> {
for(auto& node : nodes) if(node->name == name) return {};
if(auto node = Node::createPath(name)) return nodes.append(node), node;
return {};
}
auto Container::appendFile(string name, array_view<uint8_t> memory) -> shared_pointer<Node> {
for(auto& node : nodes) if(node->name == name) return {};
if(auto node = Node::createFile(name, memory)) return nodes.append(node), node;
return {};
}
auto Container::remove(string name) -> bool {
if(auto offset = nodes.find([&](auto& node) { return node->name == name; })) return nodes.remove(*offset), true;
return false;
}
auto Container::find(string name) -> shared_pointer<Node> {
if(auto offset = nodes.find([&](auto& node) { return node->name == name; })) return nodes[*offset];
return {};
}
auto Container::sort() -> void {
nodes.sort([&](auto& lhs, auto& rhs) { return string::icompare(lhs->name, rhs->name) < 0; });
}
}}}

View File

@@ -0,0 +1,86 @@
#pragma once
#include <nall/beat/archive/node.hpp>
#include <nall/beat/archive/container.hpp>
namespace nall { namespace Beat { namespace Archive {
auto create(Container& container, string name) -> vector<uint8_t> {
auto& metadata = container.metadata;
metadata = {};
metadata.append("archive: ", Location::file(name), "\n");
vector<uint8_t> memory;
container.sort();
for(auto& node : container.nodes) {
if(node->isFile()) {
node->offset = memory.size();
memory.append(node->memory);
}
metadata.append(node->metadata());
}
metadata.append(" size: ", memory.size(), "\n");
if(container.compression.type == "lzsa") {
memory = Encode::LZSA(memory);
metadata.append(" compression: lzsa\n");
metadata.append(" size: ", memory.size(), "\n");
}
if(container.signature.type == "ed25519") {
EllipticCurve::Ed25519 ed25519;
container.signature.publicKey = ed25519.publicKey(container.signature.privateKey);
container.signature.value = ed25519.sign(memory, container.signature.privateKey);
metadata.append(" signature: ed25519\n");
metadata.append(" publicKey: ", Encode::Base<57>(container.signature.publicKey), "\n");
metadata.append(" value: ", Encode::Base<57>(container.signature.value), "\n");
}
for(auto& byte : metadata) memory.append(byte);
memory.appendl((uint64_t)metadata.size(), 8);
auto sha256 = Hash::SHA256(memory).value();
memory.appendl((uint256_t)sha256, 32);
memory.append('B');
memory.append('P');
memory.append('A');
memory.append('1');
if(container.encryption.type == "xchacha20") {
Cipher::XChaCha20 xchacha20{container.encryption.privateKey, container.encryption.nonce};
memory = xchacha20.encrypt(memory);
metadata = {};
metadata.append("archive\n");
metadata.append(" encryption: xchacha20\n");
metadata.append(" nonce: ", Encode::Base<57>(container.encryption.nonce), "\n");
if(container.signature.type == "ed25519") {
EllipticCurve::Ed25519 ed25519;
container.signature.value = ed25519.sign(memory, container.signature.privateKey);
metadata.append(" signature: ed25519\n");
//metadata.append(" publicKey: ", Encode::Base<57>(container.signature.publicKey), "\n");
metadata.append(" value: ", Encode::Base<57>(container.signature.value), "\n");
}
for(auto& byte : metadata) memory.append(byte ^ memory.size());
memory.appendl((uint64_t)metadata.size() | 1ull << 63, 8);
auto sha256 = Hash::SHA256(memory).value();
memory.appendl((uint256_t)sha256, 32);
memory.append('B');
memory.append('P');
memory.append('A');
memory.append('1');
}
return memory;
}
}}}

View File

@@ -0,0 +1,27 @@
#pragma once
#include <nall/beat/archive/node.hpp>
#include <nall/beat/archive/container.hpp>
namespace nall { namespace Beat { namespace Archive {
auto extract(Container& container) -> bool {
function<void (Markup::Node)> extract = [&](auto metadata) {
if(metadata.name() != "path" && metadata.name() != "file") return;
shared_pointer<Node> node = new Node;
if(node->unserialize(container.memory, metadata)) {
container.nodes.append(node);
}
if(metadata.name() != "path") return;
for(auto node : metadata) extract(node);
};
container.nodes.reset();
auto document = BML::unserialize(container.metadata);
for(auto node : document["archive"]) extract(node);
container.sort();
return true;
}
}}}

332
nall/beat/archive/node.hpp Normal file
View File

@@ -0,0 +1,332 @@
#pragma once
#include <nall/arithmetic.hpp>
#include <nall/array-view.hpp>
#include <nall/random.hpp>
#include <nall/cipher/chacha20.hpp>
#include <nall/elliptic-curve/ed25519.hpp>
#include <nall/decode/base.hpp>
#include <nall/encode/base.hpp>
#include <nall/decode/lzsa.hpp>
#include <nall/encode/lzsa.hpp>
namespace nall { namespace Beat { namespace Archive {
struct Node {
static auto create(string name, string location) -> shared_pointer<Node>;
static auto createPath(string name) -> shared_pointer<Node>;
static auto createFile(string name, array_view<uint8_t> memory) -> shared_pointer<Node>;
explicit operator bool() const { return (bool)name; }
auto isPath() const -> bool { return name.endsWith("/"); }
auto isFile() const -> bool { return !name.endsWith("/"); }
auto isCompressed() const -> bool { return (bool)compression.type; }
auto metadata(bool indented = true) const -> string;
auto compressLZSA() -> bool;
auto unserialize(array_view<uint8_t> container, Markup::Node metadata) -> bool;
auto decompress() -> bool;
auto getTimestamp(string) const -> uint64_t;
auto getPermissions() const -> uint;
auto getOwner() const -> string;
auto getGroup() const -> string;
//files and paths
string name;
bool timestamps = false;
struct Timestamp {
string created;
string modified;
string accessed;
} timestamp;
bool permissions = false;
struct Permission {
struct Owner {
string name;
bool readable = false;
bool writable = false;
bool executable = false;
} owner;
struct Group {
string name;
bool readable = false;
bool writable = false;
bool executable = false;
} group;
struct Other {
bool readable = false;
bool writable = false;
bool executable = false;
} other;
} permission;
//files only
vector<uint8_t> memory;
uint64_t offset = 0;
struct Compression {
string type;
uint size = 0; //decompressed size; memory.size() == compressed size
} compression;
};
auto Node::create(string name, string location) -> shared_pointer<Node> {
if(!inode::exists(location)) return {};
shared_pointer<Node> node = new Node;
node->name = name;
node->timestamps = true;
node->timestamp.created = chrono::utc::datetime(inode::timestamp(location, inode::time::create));
node->timestamp.modified = chrono::utc::datetime(inode::timestamp(location, inode::time::modify));
node->timestamp.accessed = chrono::utc::datetime(inode::timestamp(location, inode::time::access));
uint mode = inode::mode(location);
node->permissions = true;
node->permission.owner.name = inode::owner(location);
node->permission.group.name = inode::group(location);
node->permission.owner.readable = mode & 0400;
node->permission.owner.writable = mode & 0200;
node->permission.owner.executable = mode & 0100;
node->permission.group.readable = mode & 0040;
node->permission.group.writable = mode & 0020;
node->permission.group.executable = mode & 0010;
node->permission.other.readable = mode & 0004;
node->permission.other.writable = mode & 0002;
node->permission.other.executable = mode & 0001;
if(file::exists(location)) {
node->memory = file::read(location);
}
return node;
}
auto Node::createPath(string name) -> shared_pointer<Node> {
if(!name) return {};
shared_pointer<Node> node = new Node;
node->name = name;
return node;
}
auto Node::createFile(string name, array_view<uint8_t> memory) -> shared_pointer<Node> {
if(!name) return {};
shared_pointer<Node> node = new Node;
node->name = name;
node->memory.resize(memory.size());
memory::copy(node->memory.data(), memory.data(), memory.size());
return node;
}
auto Node::metadata(bool indented) const -> string {
string metadata;
if(!name) return metadata;
string indent;
if(indented) {
indent.append(" ");
auto bytes = string{name}.trimRight("/");
for(auto& byte : bytes) {
if(byte == '/') indent.append(" ");
}
}
if(isPath()) {
metadata.append(indent, "path: ", name, "\n");
}
if(isFile()) {
metadata.append(indent, "file: ", name, "\n");
}
if(timestamps) {
metadata.append(indent, " timestamp\n");
if(timestamp.created != timestamp.modified)
metadata.append(indent, " created: ", timestamp.created, "\n");
metadata.append(indent, " modified: ", timestamp.modified, "\n");
if(timestamp.accessed != timestamp.modified)
metadata.append(indent, " accessed: ", timestamp.accessed, "\n");
}
if(permissions) {
metadata.append(indent, " permission\n");
metadata.append(indent, " owner: ", permission.owner.name, "\n");
if(permission.owner.readable)
metadata.append(indent, " readable\n");
if(permission.owner.writable)
metadata.append(indent, " writable\n");
if(permission.owner.executable)
metadata.append(indent, " executable\n");
metadata.append(indent, " group: ", permission.group.name, "\n");
if(permission.group.readable)
metadata.append(indent, " readable\n");
if(permission.group.writable)
metadata.append(indent, " writable\n");
if(permission.group.executable)
metadata.append(indent, " executable\n");
metadata.append(indent, " other\n");
if(permission.other.readable)
metadata.append(indent, " readable\n");
if(permission.other.writable)
metadata.append(indent, " writable\n");
if(permission.other.executable)
metadata.append(indent, " executable\n");
}
if(isFile()) {
metadata.append(indent, " offset: ", offset, "\n");
if(!isCompressed()) {
metadata.append(indent, " size: ", memory.size(), "\n");
} else {
metadata.append(indent, " size: ", compression.size, "\n");
metadata.append(indent, " compression: ", compression.type, "\n");
metadata.append(indent, " size: ", memory.size(), "\n");
}
}
return metadata;
}
auto Node::unserialize(array_view<uint8_t> container, Markup::Node metadata) -> bool {
*this = {};
if(!metadata.text()) return false;
name = metadata.text();
if(auto node = metadata["timestamp"]) {
timestamps = true;
if(auto created = node["created" ]) timestamp.created = created.text();
if(auto modified = node["modified"]) timestamp.modified = modified.text();
if(auto accessed = node["accessed"]) timestamp.accessed = accessed.text();
}
if(auto node = metadata["permission"]) {
permissions = true;
if(auto owner = node["owner"]) {
permission.owner.name = owner.text();
permission.owner.readable = (bool)owner["readable"];
permission.owner.writable = (bool)owner["writable"];
permission.owner.executable = (bool)owner["executable"];
}
if(auto group = node["group"]) {
permission.group.name = group.text();
permission.group.readable = (bool)group["readable"];
permission.group.writable = (bool)group["writable"];
permission.group.executable = (bool)group["executable"];
}
if(auto other = node["other"]) {
permission.other.readable = (bool)other["readable"];
permission.other.writable = (bool)other["writable"];
permission.other.executable = (bool)other["executable"];
}
}
if(isPath()) return true;
uint offset = metadata["offset"].natural();
uint size = metadata["size"].natural();
if(metadata["compression"]) {
size = metadata["compression/size"].natural();
compression.type = metadata["compression"].text();
}
if(offset + size >= container.size()) return false;
memory.reallocate(size);
nall::memory::copy(memory.data(), container.view(offset, size), size);
return true;
}
auto Node::compressLZSA() -> bool {
if(!memory) return true; //don't compress empty files
if(isCompressed()) return true; //don't recompress files
auto compressedMemory = Encode::LZSA(memory);
if(compressedMemory.size() >= memory.size()) return true; //can't compress smaller than original size
compression.type = "lzsa";
compression.size = memory.size();
memory = move(compressedMemory);
return true;
}
auto Node::decompress() -> bool {
if(!isCompressed()) return true;
if(compression.type == "lzsa") {
compression = {};
memory = Decode::LZSA(memory);
return (bool)memory;
}
return false;
}
auto Node::getTimestamp(string type) const -> uint64_t {
if(!timestamps) return time(nullptr);
string value = chrono::utc::datetime();
if(type == "created" ) value = timestamp.created;
if(type == "modified") value = timestamp.modified;
if(type == "accessed") value = timestamp.accessed;
#if !defined(PLATFORM_WINDOWS)
struct tm timeInfo{};
if(strptime(value, "%Y-%m-%d %H:%M:%S", &timeInfo) != nullptr) {
//todo: not thread safe ...
auto tz = getenv("TZ");
setenv("TZ", "", 1);
timeInfo.tm_isdst = -1;
auto result = mktime(&timeInfo);
if(tz) setenv("TZ", tz, 1);
else unsetenv("TZ");
if(result != -1) return result;
}
#endif
return time(nullptr);
}
auto Node::getPermissions() const -> uint {
if(!permissions) return 0755;
uint mode = 0;
if(permission.owner.readable ) mode |= 0400;
if(permission.owner.writable ) mode |= 0200;
if(permission.owner.executable) mode |= 0100;
if(permission.group.readable ) mode |= 0040;
if(permission.group.writable ) mode |= 0020;
if(permission.group.executable) mode |= 0010;
if(permission.other.readable ) mode |= 0004;
if(permission.other.writable ) mode |= 0002;
if(permission.other.executable) mode |= 0001;
return mode;
}
auto Node::getOwner() const -> string {
if(!permissions || !permission.owner.name) {
#if !defined(PLATFORM_WINDOWS)
struct passwd* pwd = getpwuid(getuid());
assert(pwd);
return pwd->pw_name;
#endif
}
return permission.owner.name;
}
auto Node::getGroup() const -> string {
if(!permissions || !permission.group.name) {
#if !defined(PLATFORM_WINDOWS)
struct group* grp = getgrgid(getgid());
assert(grp);
return grp->gr_name;
#endif
}
return permission.group.name;
}
}}}