aios/filesystem/fslib/tools/fsutil/FsUtil.cpp (1,247 lines of code) (raw):
/*
* Copyright 2014-present Alibaba Inc.
*
* 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.
*/
#include "tools/fsutil/FsUtil.h"
#include <algorithm>
#include <thread>
#include <time.h>
#include <zlib.h>
#include "autil/EnvUtil.h"
#include "autil/StringUtil.h"
#include "autil/cipher/AESCipherCreator.h"
#include "autil/legacy/md5.h"
#include "fslib/fs/FileSystem.h"
#include "fslib/util/SafeBuffer.h"
using namespace std;
FSLIB_USE_NAMESPACE(util);
FSLIB_USE_NAMESPACE(config);
FSLIB_USE_NAMESPACE(fs);
FSLIB_BEGIN_NAMESPACE(tools);
typedef autil::legacy::Md5Stream Md5Stream;
FSLIB_TYPEDEF_AUTO_PTR(Md5Stream);
string FsUtil::GETMETA_CMD = "getmeta";
string FsUtil::COPY_CMD = "cp";
string FsUtil::MD5_SUM_CMD = "md5sum";
string FsUtil::ENCRYPT_CMD = "encrypt";
string FsUtil::DECRYPT_CMD = "decrypt";
string FsUtil::DECRYPT_CAT_CMD = "dcat";
string FsUtil::DECRYPT_ZCAT_CMD = "dzcat";
string FsUtil::MOVE_CMD = "mv";
string FsUtil::MKDIR_CMD = "mkdir";
string FsUtil::LISTDIR_CMD = "ls";
string FsUtil::REMOVE_CMD = "rm";
string FsUtil::FLOCK_CMD = "flock";
string FsUtil::CAT_CMD = "cat";
string FsUtil::ZCAT_CMD = "zcat";
string FsUtil::ISEXIST_CMD = "isexist";
string FsUtil::RENAME_CMD = "rename";
string FsUtil::FORWARD_CMD = "forward";
const int32_t FsUtil::DEFAULT_THREAD_NUM = 128;
#pragma pack(push, 1)
struct GZipHeader {
unsigned char magic1; // 31
unsigned char magic2; // 139
unsigned char method; // 8
unsigned char flags;
uint32_t modTime;
unsigned char extraFlags;
unsigned char os;
};
#pragma pack(pop)
template <typename T>
class GZipHeaderParser {
public:
static bool parse(std::unique_ptr<T> &reader) {
// read header off
GZipHeader gzip_header;
if (reader->read((char *)&gzip_header, sizeof(gzip_header)) != sizeof(gzip_header)) {
cerr << "file header read failed" << endl;
return false;
}
if ((gzip_header.magic1 != 31) || (gzip_header.magic2 != 139) || (gzip_header.method != Z_DEFLATED) ||
(gzip_header.flags & 0xe0)) {
cerr << "gzip file header check failed" << endl;
return false;
}
if (gzip_header.flags & 4) { // extra field
char buf[2];
if (reader->read(buf, sizeof(buf)) != sizeof(buf)) {
cerr << "extra field length read failed" << endl;
return false;
}
unsigned int len = (unsigned)(buf[1]);
len <<= 8;
len += (unsigned)(buf[0]);
while (len--) {
char checkBuf[1];
if (reader->read(checkBuf, sizeof(checkBuf)) != sizeof(checkBuf)) {
cerr << "extra field data read failed" << endl;
return false;
}
if (checkBuf[0] < 0) {
break;
}
}
}
if (gzip_header.flags & 8) { // file name
char checkBuf[1];
do {
if (reader->read(checkBuf, sizeof(checkBuf)) != sizeof(checkBuf)) {
cerr << "file name read failed" << endl;
return false;
}
} while (checkBuf[0] > 0);
}
if (gzip_header.flags & 16) { // comment
char checkBuf[1];
do {
if (reader->read(checkBuf, sizeof(checkBuf)) != sizeof(checkBuf)) {
cerr << "comment read failed" << endl;
return false;
}
} while (checkBuf[0] > 0);
}
if (gzip_header.flags & 2) { // header crc
char checkBuf[2];
if (reader->read(checkBuf, sizeof(checkBuf)) != sizeof(checkBuf)) {
cerr << "header crc read failed" << endl;
return false;
}
}
return true;
}
};
FsUtil::~FsUtil() {}
void FsUtil::timeToStr(uint64_t time, char *timestr) {
time_t mytime = time;
struct tm result;
localtime_r(&mytime, &result);
sprintf(timestr,
"%d-%d-%d %d:%d:%d",
result.tm_year + 1900,
result.tm_mon + 1,
result.tm_mday,
result.tm_hour,
result.tm_min,
result.tm_sec);
}
ErrorCode FsUtil::runCopy(const char *srcPath, const char *dstPath, bool recursive) {
if (strcmp(srcPath, dstPath) == 0) {
cerr << "copy src <" << srcPath << "> to dst <" << dstPath << "> fail. cannot copy path to itself." << endl
<< endl
<< "operation fail! " << endl;
return EC_NOTSUP;
}
AbstractFileSystem *srcFs;
ErrorCode ret = FileSystem::parseInternal(srcPath, srcFs);
if (ret != EC_OK) {
cerr << "copy src <" << srcPath << "> to dst <" << dstPath << "> fail. parse src file fail. "
<< FileSystem::getErrorString(ret) << endl
<< endl
<< "operation fail! " << endl;
return ret;
}
AbstractFileSystem *dstFs;
ret = FileSystem::parseInternal(dstPath, dstFs);
if (ret != EC_OK) {
cerr << "copy src <" << srcPath << "> to dst <" << dstPath << "> fail. parse dst file fail."
<< FileSystem::getErrorString(ret) << endl
<< endl
<< "operation fail! " << endl;
return ret;
}
PathInfo srcInfo(srcFs, srcPath);
PathInfo dstInfo(dstFs, dstPath);
if (FileSystem::isZkLikeFileSystem(srcFs) && !FileSystem::isZkLikeFileSystem(dstFs)) {
ret = FileSystem::copyZKLikeFsToOtherFs(srcInfo, dstInfo, recursive, true);
if (ret == EC_OK) {
cout << endl << "operation success!" << endl;
return ret;
}
} else {
ret = FileSystem::copyAll(srcInfo, dstInfo, recursive, true);
if (ret == EC_OK) {
cout << endl << "operation success!" << endl;
return ret;
}
}
cerr << endl << "operation fail! " << endl;
return ret;
}
ErrorCode FsUtil::runEncrypt(const char *srcPath, const char *dstPath, const char *cipherOption, bool optionUseBase64) {
autil::cipher::CipherOption option(autil::cipher::CipherType::CT_UNKNOWN);
if (cipherOption == nullptr) {
if (!getCipherOption(option)) {
return EC_BADARGS;
}
} else {
if (!option.fromKVString(cipherOption, optionUseBase64)) {
return EC_BADARGS;
}
}
auto beginTs = autil::TimeUtility::currentTime();
cout << "-------------------------------------------------" << endl;
FileMeta fileMeta;
ErrorCode ret = FileSystem::getFileMeta(srcPath, fileMeta);
if (ret != EC_OK) {
cerr << "fail to get length of file <" << srcPath << ">. " << FileSystem::getErrorString(ret) << endl;
return ret;
}
unique_ptr<File> rFile(FileSystem::openFile(srcPath, READ));
if (!rFile->isOpened()) {
cerr << "open file <" << srcPath << "> for read fail. " << FileSystem::getErrorString(rFile->getLastError())
<< endl;
return rFile->getLastError();
}
if (std::string(dstPath) != "/dev/null" && FileSystem::isExist(dstPath) == EC_TRUE) {
cerr << "target path <" << dstPath << "> already exist. " << endl;
return EC_BADARGS;
}
unique_ptr<File> wFile(FileSystem::openFile(dstPath, WRITE));
if (!wFile->isOpened()) {
cerr << "open file <" << dstPath << "> for write fail. " << FileSystem::getErrorString(wFile->getLastError())
<< endl;
return wFile->getLastError();
}
volatile bool hasError = false;
ErrorCode rCode = EC_OK;
auto encrypter = autil::cipher::AESCipherCreator::createStreamEncrypter(option, 2 * 1024 * 1024 /* 2 MB */);
if (encrypter == nullptr) {
cerr << "create stream encrypter fail." << endl;
return EC_UNKNOWN;
}
Md5StreamPtr rFileMd5;
Md5StreamPtr wFileMd5;
if (autil::EnvUtil::getEnv("PRINT_MD5", false)) {
rFileMd5.reset(new Md5Stream);
wFileMd5.reset(new Md5Stream);
}
size_t totalEncryptLen = 0;
std::thread readThread([&]() {
size_t bufferLen = 2 * 1024 * 1024;
SafeBuffer buffer(bufferLen);
while (!hasError) {
auto readLen = rFile->read(buffer.getBuffer(), bufferLen);
if (readLen < 0) {
cerr << "read file <" << srcPath << "> fail. " << FileSystem::getErrorString(rFile->getLastError())
<< endl;
rCode = rFile->getLastError();
hasError = true;
continue;
}
if (readLen > 0) {
if (rFileMd5) {
rFileMd5->Put((const uint8_t *)buffer.getBuffer(), readLen);
}
if (!encrypter->append((const unsigned char *)buffer.getBuffer(), readLen)) {
cerr << "encrypter append data fail." << endl;
rCode = EC_UNKNOWN;
hasError = true;
continue;
}
}
totalEncryptLen += readLen;
if (rFile->isEof()) {
break;
}
}
if (!encrypter->seal()) {
cerr << "encrypter seal fail." << endl;
rCode = EC_UNKNOWN;
hasError = true;
}
rFile->close();
});
ErrorCode wCode = EC_OK;
size_t totalWriteLen = 0;
std::thread writeThread([&]() {
size_t bufferLen = 2 * 1024 * 1024;
SafeBuffer buffer(bufferLen);
while (!hasError) {
auto ret = encrypter->get((unsigned char *)buffer.getBuffer(), bufferLen);
if (ret > 0) {
if (wFileMd5) {
wFileMd5->Put((const uint8_t *)buffer.getBuffer(), ret);
}
if (wFile->write(buffer.getBuffer(), ret) != ret) {
cerr << "write to dst file <" << dstPath << "> fail. "
<< FileSystem::getErrorString(wFile->getLastError()) << endl;
wCode = wFile->getLastError();
hasError = true;
}
totalWriteLen += ret;
continue;
}
if (encrypter->isEof()) {
break;
}
}
wCode = wFile->close();
if (wCode != EC_OK) {
cerr << "close dst file <" << dstPath << "> fail. " << FileSystem::getErrorString(wFile->getLastError())
<< endl;
hasError = true;
}
});
readThread.join();
writeThread.join();
if (hasError) {
return rCode != EC_OK ? rCode : wCode;
}
auto interval = autil::TimeUtility::currentTime() - beginTs;
cout << "encryption interval:" << (double)interval / 1000000 << " seconds" << endl;
cout << "plain file length:" << totalEncryptLen << ", cipher file length:" << totalWriteLen << endl;
cout << "=================================================" << endl;
cout << "== key hex:" << encrypter->getKeyHexString() << endl;
cout << "== iv hex:" << encrypter->getIvHexString() << endl;
cout << "== salt hex:" << encrypter->getSaltHexString() << endl;
if (rFileMd5 && wFileMd5) {
cout << "++++++++++++++++++++++++++++++++++++++++++++++++++++" << endl;
cout << "++ src file md5:" << rFileMd5->GetMd5String() << endl;
cout << "++ dst file md5:" << wFileMd5->GetMd5String() << endl;
}
return EC_OK;
}
ErrorCode FsUtil::runDecrypt(const char *srcPath, const char *dstPath, const char *cipherOption, bool optionUseBase64) {
autil::cipher::CipherOption option(autil::cipher::CipherType::CT_UNKNOWN);
if (cipherOption == nullptr) {
if (!getCipherOption(option)) {
return EC_BADARGS;
}
} else {
if (!option.fromKVString(cipherOption, optionUseBase64)) {
return EC_BADARGS;
}
}
auto beginTs = autil::TimeUtility::currentTime();
cout << "-------------------------------------------------" << endl;
FileMeta fileMeta;
ErrorCode ret = FileSystem::getFileMeta(srcPath, fileMeta);
if (ret != EC_OK) {
cerr << "fail to get length of file <" << srcPath << ">. " << FileSystem::getErrorString(ret) << endl;
return ret;
}
unique_ptr<File> rFile(FileSystem::openFile(srcPath, READ));
if (!rFile->isOpened()) {
cerr << "open file <" << srcPath << "> for read fail. " << FileSystem::getErrorString(rFile->getLastError())
<< endl;
return rFile->getLastError();
}
if (std::string(dstPath) != "/dev/null" && FileSystem::isExist(dstPath) == EC_TRUE) {
cerr << "target path <" << dstPath << "> already exist. " << endl;
return EC_BADARGS;
}
unique_ptr<File> wFile(FileSystem::openFile(dstPath, WRITE));
if (!wFile->isOpened()) {
cerr << "open file <" << dstPath << "> for write fail. " << FileSystem::getErrorString(wFile->getLastError())
<< endl;
return wFile->getLastError();
}
volatile bool hasError = false;
ErrorCode rCode = EC_OK;
auto decrypter = autil::cipher::AESCipherCreator::createStreamDecrypter(option, 2 * 1024 * 1024 /* 2 MB */);
if (decrypter == nullptr) {
cerr << "create stream decrypter fail." << endl;
return EC_UNKNOWN;
}
Md5StreamPtr rFileMd5;
Md5StreamPtr wFileMd5;
if (autil::EnvUtil::getEnv("PRINT_MD5", false)) {
rFileMd5.reset(new Md5Stream);
wFileMd5.reset(new Md5Stream);
}
size_t totalDecryptLen = 0;
std::thread readThread([&]() {
size_t bufferLen = 2 * 1024 * 1024;
SafeBuffer buffer(bufferLen);
while (!hasError) {
auto readLen = rFile->read(buffer.getBuffer(), bufferLen);
if (readLen < 0) {
cerr << "read file <" << srcPath << "> fail. " << FileSystem::getErrorString(rFile->getLastError())
<< endl;
rCode = rFile->getLastError();
hasError = true;
continue;
}
if (readLen > 0) {
if (rFileMd5) {
rFileMd5->Put((const uint8_t *)buffer.getBuffer(), readLen);
}
if (!decrypter->append((const unsigned char *)buffer.getBuffer(), readLen)) {
cerr << "decrypter append data fail." << endl;
rCode = EC_UNKNOWN;
hasError = true;
continue;
}
}
totalDecryptLen += readLen;
if (rFile->isEof()) {
break;
}
}
if (!decrypter->seal()) {
cerr << "decrypter seal fail." << endl;
rCode = EC_UNKNOWN;
hasError = true;
}
rFile->close();
});
ErrorCode wCode = EC_OK;
size_t totalWriteLen = 0;
std::thread writeThread([&]() {
size_t bufferLen = 2 * 1024 * 1024;
SafeBuffer buffer(bufferLen);
while (!hasError) {
auto ret = decrypter->get((unsigned char *)buffer.getBuffer(), bufferLen);
if (ret > 0) {
if (wFileMd5) {
wFileMd5->Put((const uint8_t *)buffer.getBuffer(), ret);
}
if (wFile->write(buffer.getBuffer(), ret) != ret) {
cerr << "write to dst file <" << dstPath << "> fail. "
<< FileSystem::getErrorString(wFile->getLastError()) << endl;
wCode = wFile->getLastError();
hasError = true;
}
totalWriteLen += ret;
continue;
}
if (decrypter->isEof()) {
break;
}
}
wCode = wFile->close();
if (wCode != EC_OK) {
cerr << "close dst file <" << dstPath << "> fail. " << FileSystem::getErrorString(wFile->getLastError())
<< endl;
hasError = true;
}
});
readThread.join();
writeThread.join();
if (hasError) {
return rCode != EC_OK ? rCode : wCode;
}
auto interval = autil::TimeUtility::currentTime() - beginTs;
cout << "decryption interval:" << (double)interval / 1000000 << " seconds" << endl;
cout << "cipher file length:" << totalDecryptLen << ", plain file length:" << totalWriteLen << endl;
cout << "=================================================" << endl;
cout << "== key hex:" << decrypter->getKeyHexString() << endl;
cout << "== iv hex:" << decrypter->getIvHexString() << endl;
cout << "== salt hex:" << decrypter->getSaltHexString() << endl;
if (rFileMd5 && wFileMd5) {
cout << "++++++++++++++++++++++++++++++++++++++++++++++++++++" << endl;
cout << "++ src file md5:" << rFileMd5->GetMd5String() << endl;
cout << "++ dst file md5:" << wFileMd5->GetMd5String() << endl;
}
return EC_OK;
}
class AESCipherDataReader {
public:
AESCipherDataReader(std::unique_ptr<autil::cipher::AESCipherStreamDecrypter> &decrypter, volatile bool &hasError)
: _hasError(hasError), _decrypter(decrypter) {}
ssize_t read(char *output, uint32_t size) {
if (_decrypter == nullptr) {
return -1;
}
size_t sizeUsed = 0;
while (sizeUsed < size) {
uint32_t sizeToRead = size - sizeUsed;
char *buffer = output + sizeUsed;
uint32_t sizeRead = 0;
if (!innerGet(buffer, sizeToRead, sizeRead)) {
return _hasError ? -1 : sizeUsed;
}
sizeUsed += sizeRead;
}
return sizeUsed;
}
bool isEof() const { return _decrypter->isEof(); }
private:
bool innerGet(char *output, uint32_t size, uint32_t &sizeUsed) {
assert(_decrypter != nullptr);
while (!_hasError) {
auto ret = _decrypter->get((unsigned char *)output, size);
if (ret > 0) {
sizeUsed = ret;
return true;
}
if (_decrypter->isEof()) {
return false; // reach eof
}
}
return !_hasError;
}
private:
volatile bool &_hasError;
std::unique_ptr<autil::cipher::AESCipherStreamDecrypter> &_decrypter;
};
ErrorCode FsUtil::runDecryptZCat(const char *srcPath, const char *cipherOption, bool optionUseBase64) {
autil::cipher::CipherOption option(autil::cipher::CipherType::CT_UNKNOWN);
if (cipherOption == nullptr) {
if (!getCipherOption(option)) {
return EC_BADARGS;
}
} else {
if (!option.fromKVString(cipherOption, optionUseBase64)) {
return EC_BADARGS;
}
}
FileMeta fileMeta;
ErrorCode ret = FileSystem::getFileMeta(srcPath, fileMeta);
if (ret != EC_OK) {
cerr << "fail to get length of file <" << srcPath << ">. " << FileSystem::getErrorString(ret) << endl;
return ret;
}
unique_ptr<File> rFile(FileSystem::openFile(srcPath, READ));
if (!rFile->isOpened()) {
cerr << "open file <" << srcPath << "> for read fail. " << FileSystem::getErrorString(rFile->getLastError())
<< endl;
return rFile->getLastError();
}
volatile bool hasError = false;
ErrorCode rCode = EC_OK;
auto decrypter = autil::cipher::AESCipherCreator::createStreamDecrypter(option, 2 * 1024 * 1024 /* 2 MB */);
if (decrypter == nullptr) {
cerr << "create stream decrypter fail." << endl;
return EC_UNKNOWN;
}
size_t totalDecryptLen = 0;
std::thread readThread([&]() {
size_t bufferLen = 2 * 1024 * 1024;
SafeBuffer buffer(bufferLen);
while (!hasError) {
auto readLen = rFile->read(buffer.getBuffer(), bufferLen);
if (readLen < 0) {
cerr << "read file <" << srcPath << "> fail. " << FileSystem::getErrorString(rFile->getLastError())
<< endl;
rCode = rFile->getLastError();
hasError = true;
continue;
}
if (readLen > 0) {
if (!decrypter->append((const unsigned char *)buffer.getBuffer(), readLen)) {
cerr << "decrypter append data fail." << endl;
rCode = EC_UNKNOWN;
hasError = true;
continue;
}
}
totalDecryptLen += readLen;
if (rFile->isEof()) {
break;
}
}
if (!decrypter->seal()) {
cerr << "decrypter seal fail." << endl;
rCode = EC_UNKNOWN;
hasError = true;
}
});
ErrorCode wCode = EC_OK;
std::thread writeThread([&]() {
std::unique_ptr<AESCipherDataReader> reader(new AESCipherDataReader(decrypter, hasError));
if (!GZipHeaderParser<AESCipherDataReader>::parse(reader)) {
hasError = true;
wCode = EC_UNKNOWN;
return;
}
z_stream strm;
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
strm.avail_in = 0;
strm.next_in = Z_NULL;
if (inflateInit2(&strm, -MAX_WBITS) != Z_OK) {
cerr << "gzip infalte init error" << endl;
hasError = true;
wCode = EC_UNKNOWN;
return;
}
size_t bufferLen = 2 * 1024 * 1024;
SafeBuffer buffer(bufferLen + 1);
SafeBuffer outBuffer(bufferLen);
while (true) {
auto readLen = reader->read(buffer.getBuffer(), bufferLen);
if (readLen < 0) {
cerr << "read decrypted file data fail. " << endl;
inflateEnd(&strm);
hasError = true;
wCode = EC_UNKNOWN;
return;
}
if (readLen > 0) {
char *inBuf = buffer.getBuffer();
char *outBuf = outBuffer.getBuffer();
inBuf[readLen] = 0;
uint32_t bufferNow = 0;
uint32_t bufferEnd = readLen;
while (bufferNow < bufferEnd) {
strm.avail_out = bufferLen;
strm.next_out = (Bytef *)outBuf;
strm.avail_in = bufferEnd - bufferNow;
strm.next_in = (Bytef *)&inBuf[bufferNow];
int ret = inflate(&strm, Z_NO_FLUSH);
if ((ret != Z_STREAM_END) && (ret != Z_OK)) {
cerr << "infalte error(" << ret << ")" << endl;
inflateEnd(&strm);
hasError = true;
wCode = EC_UNKNOWN;
return;
}
bufferNow = bufferEnd - strm.avail_in;
auto dSize = bufferLen - strm.avail_out;
fwrite(outBuf, sizeof(char), dSize, stdout);
if (ret == Z_STREAM_END) {
inflateEnd(&strm);
return;
}
}
}
if (reader->isEof()) {
break;
}
}
inflateEnd(&strm);
});
readThread.join();
writeThread.join();
if (hasError) {
return rCode != EC_OK ? rCode : wCode;
}
return EC_OK;
}
ErrorCode FsUtil::runDecryptCat(const char *srcPath, const char *cipherOption, bool optionUseBase64) {
autil::cipher::CipherOption option(autil::cipher::CipherType::CT_UNKNOWN);
if (cipherOption == nullptr) {
if (!getCipherOption(option)) {
return EC_BADARGS;
}
} else {
if (!option.fromKVString(cipherOption, optionUseBase64)) {
return EC_BADARGS;
}
}
FileMeta fileMeta;
ErrorCode ret = FileSystem::getFileMeta(srcPath, fileMeta);
if (ret != EC_OK) {
cerr << "fail to get length of file <" << srcPath << ">. " << FileSystem::getErrorString(ret) << endl;
return ret;
}
unique_ptr<File> rFile(FileSystem::openFile(srcPath, READ));
if (!rFile->isOpened()) {
cerr << "open file <" << srcPath << "> for read fail. " << FileSystem::getErrorString(rFile->getLastError())
<< endl;
return rFile->getLastError();
}
volatile bool hasError = false;
ErrorCode rCode = EC_OK;
auto decrypter = autil::cipher::AESCipherCreator::createStreamDecrypter(option, 2 * 1024 * 1024 /* 2 MB */);
if (decrypter == nullptr) {
cerr << "create stream decrypter fail." << endl;
return EC_UNKNOWN;
}
size_t totalDecryptLen = 0;
std::thread readThread([&]() {
size_t bufferLen = 2 * 1024 * 1024;
SafeBuffer buffer(bufferLen);
while (!hasError) {
auto readLen = rFile->read(buffer.getBuffer(), bufferLen);
if (readLen < 0) {
cerr << "read file <" << srcPath << "> fail. " << FileSystem::getErrorString(rFile->getLastError())
<< endl;
rCode = rFile->getLastError();
hasError = true;
continue;
}
if (readLen > 0) {
if (!decrypter->append((const unsigned char *)buffer.getBuffer(), readLen)) {
cerr << "decrypter append data fail." << endl;
rCode = EC_UNKNOWN;
hasError = true;
continue;
}
}
totalDecryptLen += readLen;
if (rFile->isEof()) {
break;
}
}
if (!decrypter->seal()) {
cerr << "decrypter seal fail." << endl;
rCode = EC_UNKNOWN;
hasError = true;
}
});
std::thread writeThread([&]() {
size_t bufferLen = 2 * 1024 * 1024;
SafeBuffer buffer(bufferLen);
while (!hasError) {
auto ret = decrypter->get((unsigned char *)buffer.getBuffer(), bufferLen);
if (ret > 0) {
fwrite(buffer.getBuffer(), sizeof(char), ret, stdout);
continue;
}
if (decrypter->isEof()) {
break;
}
}
});
readThread.join();
writeThread.join();
return rCode;
}
template <typename T>
bool FsUtil::getUserConsoleInput(const std::string &hintStr,
const std::set<std::string> &validInputs,
const std::string &defaultValue,
size_t maxInputTimes,
T &value) {
bool ret = false;
std::string inputStr;
size_t cnt = 0;
while (true) {
if (cnt >= maxInputTimes) {
std::cout << "reach max input times: " << maxInputTimes << "!" << endl;
break;
}
std::cout << hintStr;
std::getline(cin, inputStr);
++cnt;
if (inputStr.empty() && !defaultValue.empty()) {
inputStr = defaultValue;
std::cout << "use default input value [" << defaultValue << "]" << endl;
}
if (inputStr.empty()) {
std::cout << "invalid empty input: [" << inputStr << "]" << endl;
continue;
}
if (!validInputs.empty() && validInputs.find(inputStr) == validInputs.end()) {
std::cout << "invalid input: [" << inputStr
<< "], candidates : " << autil::StringUtil::toString(validInputs, "|") << endl;
continue;
}
if (!autil::StringUtil::fromString<T>(inputStr, value)) {
std::cout << "invalid input: [" << inputStr << "], wrong type!" << endl;
continue;
}
ret = true;
break;
}
return ret;
}
bool FsUtil::getCipherOption(autil::cipher::CipherOption &option) {
int maxInputTimes = 3;
std::string typeStr;
if (!getUserConsoleInput("\n1. input cipher type, enter for default [aes-128-cbc] :",
{"aes-128-cbc", "aes-192-cbc", "aes-256-cbc", "aes-128-ctr", "aes-192-ctr", "aes-256-ctr"},
"aes-128-cbc",
maxInputTimes,
typeStr)) {
return false;
}
autil::cipher::CipherType type = autil::cipher::CipherOption::parseCipherType(typeStr);
if (type == autil::cipher::CipherType::CT_UNKNOWN) {
std::cout << "invalid cipher type string [" << typeStr << "]" << std::endl;
return false;
}
std::string mode;
if (!getUserConsoleInput("\n2. select cipher mode [passwd|key], enter for default[passwd] :",
{"passwd", "key"},
"passwd",
maxInputTimes,
mode)) {
return false;
}
if (mode == "passwd") {
std::string passwd;
getUserConsoleInput("\n3. input password:", {}, "", maxInputTimes, passwd);
std::string digist;
if (!getUserConsoleInput("\n4. select digist [md5|sha256], enter for default[md5] :",
{"md5", "sha256"},
"md5",
maxInputTimes,
digist)) {
return false;
}
autil::cipher::DigistType dgstType = autil::cipher::CipherOption::parseDigistType(digist);
if (dgstType == autil::cipher::DigistType::DT_UNKNOWN) {
std::cout << "invalid digist [" << digist << "]" << endl;
return false;
}
std::string usePbkdf2;
if (!getUserConsoleInput("\n5. use pbkdf2 ? yes or no, enter for default [no] :",
{"yes", "no"},
"no",
maxInputTimes,
usePbkdf2)) {
return false;
}
std::string needSaltStr;
if (!getUserConsoleInput("\n6. need salt ? yes or no, enter for default [yes] :",
{"yes", "no"},
"yes",
maxInputTimes,
needSaltStr)) {
return false;
}
if (needSaltStr == "no") {
option = autil::cipher::CipherOption::password(type, dgstType, passwd, usePbkdf2 == "yes");
return true;
}
std::string useRandomSaltStr;
if (!getUserConsoleInput("\n7. use random salt ? yes or no, enter for default [yes] :",
{"yes", "no"},
"yes",
maxInputTimes,
useRandomSaltStr)) {
return false;
}
if (useRandomSaltStr == "yes") {
option = autil::cipher::CipherOption::passwordWithRandomSalt(type, dgstType, passwd, usePbkdf2 == "yes");
return true;
}
std::string useDefineSalt;
getUserConsoleInput("\n8. input salt hex string:", {}, "", maxInputTimes, useDefineSalt);
option =
autil::cipher::CipherOption::passwordWithSalt(type, dgstType, passwd, useDefineSalt, usePbkdf2 == "yes");
return true;
}
if (mode == "key") {
std::string keyStr;
getUserConsoleInput("\n3. input key hex string:", {}, "", maxInputTimes, keyStr);
std::string ivStr;
getUserConsoleInput("\n4. input iv hex string:", {}, "", maxInputTimes, ivStr);
option = autil::cipher::CipherOption::secretKey(type, keyStr, ivStr);
return true;
}
assert(false);
return false;
}
ErrorCode FsUtil::runMove(const char *srcPath, const char *dstPath) {
ErrorCode ret = FileSystem::moveInternal(srcPath, dstPath, true);
if (ret == EC_OK) {
cout << endl << "operation success!" << endl;
} else {
cerr << endl << "operation fail! " << endl;
}
return ret;
}
ErrorCode FsUtil::runRename(const char *srcPath, const char *dstPath) {
ErrorCode ret = FileSystem::rename(srcPath, dstPath);
if (ret == EC_OK) {
cout << endl << "operation success!" << endl;
} else {
cerr << endl << "operation fail! " << endl;
}
return ret;
}
ErrorCode FsUtil::runMkDir(const char *dirname, bool recursive) {
ErrorCode ret = FileSystem::mkDir(dirname, recursive);
if (ret != EC_OK) {
cerr << "mkdir <" << dirname << "> fail! " << FileSystem::getErrorString(ret) << endl;
} else {
cout << "operation success!" << endl;
}
return ret;
}
ErrorCode FsUtil::runListDir(const char *dirname) {
FileList fileList;
ErrorCode ret = FileSystem::listDir(dirname, fileList);
if (ret != EC_OK) {
cerr << "list dir <" << dirname << "> fail! " << FileSystem::getErrorString(ret) << endl;
} else {
sort(fileList.begin(), fileList.end());
for (size_t i = 0; i < fileList.size(); i++) {
cout << fileList[i] << endl;
}
}
return ret;
}
ErrorCode FsUtil::runListDirWithRecursive(const char *dirname, const char *threadNumStr) {
int32_t threadNum;
if (strcmp(threadNumStr, "") == 0) {
threadNum = DEFAULT_THREAD_NUM;
} else {
if (!autil::StringUtil::strToInt32(threadNumStr, threadNum)) {
cerr << "parse threadNum[" << threadNumStr << "] Error!" << endl;
return EC_UNKNOWN;
}
}
EntryInfoMap entryMap;
ErrorCode ret = FileSystem::listDir(dirname, entryMap, threadNum);
if (ret != EC_OK) {
cerr << "list dir <" << dirname << "> fail! " << endl;
} else {
EntryInfoMap::const_iterator it = entryMap.begin();
for (; it != entryMap.end(); it++) {
cout << it->first << endl;
}
}
return ret;
}
ErrorCode FsUtil::runRemove(const char *path) {
ErrorCode ret = FileSystem::remove(path);
if (ret != EC_OK) {
cerr << "remove path <" << path << "> fail! " << FileSystem::getErrorString(ret) << endl;
} else {
cout << "operation success!" << endl;
}
return ret;
}
ErrorCode FsUtil::runIsExist(const char *pathname) {
ErrorCode ret = FileSystem::isExist(pathname);
if (ret == EC_TRUE) {
cout << "path exist!" << endl;
} else if (ret == EC_FALSE) {
cout << "path does not exist!" << endl;
} else {
cerr << FileSystem::getErrorString(ret) << endl << "operation fail!" << endl;
}
return ret;
}
ErrorCode FsUtil::runMd5Sum(const char *fileName) {
FileMeta fileMeta;
std::string fileNameCopy = std::string(fileName);
ErrorCode ret = FileSystem::getFileMeta(fileNameCopy, fileMeta);
if (ret != EC_OK) {
cerr << "fail to get length of file <" << fileNameCopy << ">. " << FileSystem::getErrorString(ret) << endl;
return ret;
}
unique_ptr<File> file(FileSystem::openFile(fileNameCopy, READ));
if (!file->isOpened()) {
cerr << "open file <" << fileNameCopy << "> for read fail. " << FileSystem::getErrorString(file->getLastError())
<< endl;
return file->getLastError();
}
int64_t len = fileMeta.fileLength;
if (len == 0) {
return EC_OK;
}
Md5StreamPtr rFileMd5(new Md5Stream);
size_t bufferLen = 2 * 1024 * 1024;
SafeBuffer buffer(bufferLen);
while (true) {
auto readLen = file->read(buffer.getBuffer(), bufferLen);
if (readLen < 0) {
cerr << "read file <" << fileNameCopy << "> fail. " << FileSystem::getErrorString(file->getLastError())
<< endl;
return file->getLastError();
}
if (readLen > 0) {
rFileMd5->Put((const uint8_t *)buffer.getBuffer(), readLen);
}
if (file->isEof()) {
break;
}
}
file->close();
cout << "file md5:" << rFileMd5->GetMd5String() << endl;
return EC_OK;
}
ErrorCode FsUtil::runCat(const char *fileName) {
FileMeta fileMeta;
std::string fileNameCopy = std::string(fileName);
ErrorCode ret = FileSystem::getFileMeta(fileNameCopy, fileMeta);
if (ret != EC_OK) {
cerr << "fail to get length of file <" << fileNameCopy << ">. " << FileSystem::getErrorString(ret) << endl;
return ret;
}
unique_ptr<File> file(FileSystem::openFile(fileNameCopy, READ));
if (!file->isOpened()) {
cerr << "open file <" << fileNameCopy << "> for read fail. " << FileSystem::getErrorString(file->getLastError())
<< endl;
return file->getLastError();
}
int64_t len = fileMeta.fileLength;
if (len == 0) {
return EC_OK;
}
size_t bufferLen = 2 * 1024 * 1024;
SafeBuffer buffer(bufferLen);
while (true) {
auto readLen = file->read(buffer.getBuffer(), bufferLen);
if (readLen < 0) {
cerr << "read file <" << fileNameCopy << "> fail. " << FileSystem::getErrorString(file->getLastError())
<< endl;
return file->getLastError();
}
if (readLen > 0) {
fwrite(buffer.getBuffer(), sizeof(char), readLen, stdout);
}
if (file->isEof()) {
break;
}
}
file->close();
return EC_OK;
}
ErrorCode FsUtil::runZCat(const char *fileName) {
FileMeta fileMeta;
std::string fileNameCopy = std::string(fileName);
ErrorCode ret = FileSystem::getFileMeta(fileNameCopy, fileMeta);
if (ret != EC_OK) {
cerr << "fail to get length of file <" << fileNameCopy << ">. " << FileSystem::getErrorString(ret) << endl;
return ret;
}
unique_ptr<File> file(FileSystem::openFile(fileNameCopy, READ));
if (!file->isOpened()) {
cerr << "open file <" << fileNameCopy << "> for read fail. " << FileSystem::getErrorString(file->getLastError())
<< endl;
return file->getLastError();
}
if (fileMeta.fileLength == 0) {
return EC_OK;
}
if (!GZipHeaderParser<File>::parse(file)) {
return EC_UNKNOWN;
}
z_stream strm;
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
strm.avail_in = 0;
strm.next_in = Z_NULL;
if (inflateInit2(&strm, -MAX_WBITS) != Z_OK) {
cerr << "gzip infalte init error" << endl;
return EC_UNKNOWN;
}
size_t bufferLen = 2 * 1024 * 1024;
SafeBuffer buffer(bufferLen + 1);
SafeBuffer outBuffer(bufferLen);
while (true) {
auto readLen = file->read(buffer.getBuffer(), bufferLen);
if (readLen < 0) {
cerr << "read file <" << fileNameCopy << "> fail. " << FileSystem::getErrorString(file->getLastError())
<< endl;
inflateEnd(&strm);
return file->getLastError();
}
if (readLen > 0) {
char *inBuf = buffer.getBuffer();
char *outBuf = outBuffer.getBuffer();
inBuf[readLen] = 0;
uint32_t bufferNow = 0;
uint32_t bufferEnd = readLen;
while (bufferNow < bufferEnd) {
strm.avail_out = bufferLen;
strm.next_out = (Bytef *)outBuf;
strm.avail_in = bufferEnd - bufferNow;
strm.next_in = (Bytef *)&inBuf[bufferNow];
int ret = inflate(&strm, Z_NO_FLUSH);
if ((ret != Z_STREAM_END) && (ret != Z_OK)) {
cerr << "infalte error(" << ret << ")" << endl;
inflateEnd(&strm);
return EC_UNKNOWN;
}
bufferNow = bufferEnd - strm.avail_in;
auto dSize = bufferLen - strm.avail_out;
fwrite(outBuf, sizeof(char), dSize, stdout);
if (ret == Z_STREAM_END) {
inflateEnd(&strm);
file->close();
return EC_OK;
}
}
}
if (file->isEof()) {
break;
}
}
inflateEnd(&strm);
file->close();
return EC_OK;
}
ErrorCode FsUtil::getPathMeta(const string &pathName, FileMeta &meta) {
ErrorCode ret = FileSystem::getFileMeta(pathName, meta);
if (ret != EC_OK) {
cerr << "get metainfo of file <" << pathName << "> fail!" << FileSystem::getErrorString(ret) << endl;
}
return ret;
}
ErrorCode FsUtil::getDirMeta(const string &pathName, uint32_t &fileCount, uint32_t &dirCount, uint64_t &size) {
FileList fileList;
ErrorCode ret = FileSystem::listDir(pathName, fileList);
if (ret != EC_OK) {
cerr << "list dir <" << pathName << "> fail!" << FileSystem::getErrorString(ret) << endl;
return ret;
}
string subPath;
for (size_t i = 0; i < fileList.size(); i++) {
if (!FileSystem::appendPath(pathName, fileList[i], subPath)) {
cerr << "fail to get subpath. "
<< "parent path <" << pathName << ">, patname <" << fileList[i] << ">" << endl;
return EC_PARSEFAIL;
}
ret = FileSystem::isFile(subPath);
if (ret == EC_TRUE) {
fileCount++;
FileMeta meta;
ret = getPathMeta(subPath, meta);
if (ret != EC_OK) {
return ret;
}
size += meta.fileLength;
} else if (ret == EC_FALSE) {
ret = FileSystem::isDirectory(subPath);
if (ret == EC_TRUE) {
dirCount++;
uint32_t subFileCount = 0;
uint32_t subDirCount = 0;
ret = getDirMeta(subPath, subFileCount, subDirCount, size);
if (ret != EC_OK) {
return ret;
}
} else if (ret == EC_FALSE) {
cerr << "path <" << subPath << "> is neither a file nor directory" << endl;
return EC_NOTSUP;
} else {
cerr << "get path <" << subPath << "> meta fail, " << FileSystem::getErrorString(ret) << endl;
return ret;
}
} else {
cerr << "get path <" << subPath << "> meta fail, " << FileSystem::getErrorString(ret) << endl;
return ret;
}
}
return EC_OK;
}
#define CONVERT_ZOO_TIME(meta, timeType, atime) \
while (true) { \
time_t time = meta.timeType / 1000; \
struct tm tmpTm; \
localtime_r(&time, &tmpTm); \
strftime(atime, 30, "%Y-%m-%d %T", &tmpTm); \
break; \
}
ErrorCode FsUtil::runGetMeta(const char *pathName) {
ErrorCode ret = EC_OK;
if (FileSystem::getFsType(pathName) == "zfs") {
cout << "this path is a zookeeper node." << endl;
uint32_t totalNodeCount = 0;
uint64_t size = 0;
FileMeta meta;
ret = getPathMeta(pathName, meta);
if (ret != EC_OK) {
cerr << endl << "operation fail!" << endl;
return ret;
}
ret = getZookeeperNodeMeta(pathName, totalNodeCount, size);
if (ret == EC_OK) {
cout << "current node size: " << meta.fileLength << endl;
cout << "total sub node count: " << totalNodeCount << endl;
cout << "total sub node size: " << size << endl;
char createTime[30];
char modifyTime[30];
memset(createTime, '\0', sizeof(createTime));
memset(createTime, '\0', sizeof(modifyTime));
CONVERT_ZOO_TIME(meta, createTime, createTime);
CONVERT_ZOO_TIME(meta, lastModifyTime, modifyTime);
cout << "node create time: " << string(createTime) << endl;
cout << "node last modify time: " << string(modifyTime) << endl;
} else {
cerr << endl << "operation fail!" << endl;
}
return ret;
}
ret = FileSystem::isDirectory(pathName);
if (ret == EC_TRUE) {
cout << "this path is a directory." << endl;
uint32_t fileCount = 0;
uint32_t dirCount = 0;
uint64_t size = 0;
ret = getDirMeta(pathName, fileCount, dirCount, size);
if (ret == EC_OK) {
cout << "file count: " << fileCount << endl;
cout << "dir count: " << dirCount << endl;
cout << "total size: " << size << endl;
return ret;
} else {
cerr << endl << "operation fail!" << endl;
return ret;
}
} else if (ret == EC_FALSE) {
ret = FileSystem::isFile(pathName);
if (ret == EC_TRUE) {
cout << "this path is a file." << endl;
FileMeta fileMeta;
ret = getPathMeta(pathName, fileMeta);
if (ret == EC_OK) {
cout << "file length: " << fileMeta.fileLength << endl;
char buffer[30];
timeToStr(fileMeta.createTime, buffer);
cout << "file create time: " << buffer << endl;
timeToStr(fileMeta.lastModifyTime, buffer);
cout << "file last modify time: " << buffer << endl;
return ret;
} else {
cerr << endl << "operation fail!" << endl;
return ret;
}
} else if (ret == EC_FALSE) {
cerr << "path <" << pathName << "> is neither a file or directory" << endl << "operation fail!" << endl;
return EC_NOTSUP;
} else {
cerr << "fail to get meta of path <" << pathName << ">, " << FileSystem::getErrorString(ret) << endl
<< "operation fail!" << endl;
return ret;
}
} else {
cerr << "fail to get meta of path <" << pathName << ">, " << FileSystem::getErrorString(ret) << endl
<< "operation fail!" << endl;
return ret;
}
return EC_OK;
}
#undef CONVERT_ZOO_TIME
ErrorCode FsUtil::getZookeeperNodeMeta(const string &pathName, uint32_t &totalNodeCount, uint64_t &size) {
FileList fileList;
ErrorCode ret = FileSystem::listDir(pathName, fileList);
if (ret != EC_OK) {
cerr << "list dir <" << pathName << "> fail!" << FileSystem::getErrorString(ret) << endl;
return ret;
}
totalNodeCount += fileList.size();
string subPath;
for (size_t i = 0; i < fileList.size(); i++) {
if (!FileSystem::appendPath(pathName, fileList[i], subPath)) {
cerr << "fail to get subpath. "
<< "parent path <" << pathName << ">, patname <" << fileList[i] << ">" << endl;
return EC_PARSEFAIL;
}
ret = getZookeeperNodeMeta(subPath, totalNodeCount, size);
if (ret != EC_OK) {
return ret;
}
FileMeta meta;
ret = getPathMeta(subPath, meta);
if (ret != EC_OK) {
return ret;
}
size += meta.fileLength;
}
return EC_OK;
}
ErrorCode FsUtil::runFlock(const char *lockname, const char *script) {
ScopedFileLock lock;
if (!FileSystem::getScopedFileLock(lockname, lock)) {
cerr << "get file lock fail! " << endl;
return EC_UNKNOWN;
}
if (0 != system(script)) {
return EC_UNKNOWN;
}
return EC_OK;
}
ErrorCode FsUtil::runForward(const char *command, const char *pathname, const char *args) {
string output;
ErrorCode ret = FileSystem::forward(command, pathname, args, output);
if (ret == EC_OK) {
cout << (output.empty() ? "OK!" : output) << endl;
} else if (ret == EC_NOTSUP) {
cerr << "not suppot command[" << command << "], "
<< "path[" << pathname << "], "
<< "args[" << args << "]" << endl;
} else {
cerr << FileSystem::getErrorString(ret) << endl << "operation fail!" << endl;
}
return ret;
}
void FsUtil::closeFileSystem() { FileSystem::close(); }
FSLIB_END_NAMESPACE(tools);