/* * Copyright (c) 2020.Huawei Technologies Co., Ltd. All rights reserved. * 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 "FileManager.h" #include #include namespace { const int BUFFER_SIZE = 2048; #ifndef _WIN32 const mode_t DEFAULT_FILE_PERMISSION = 0077; #endif } #ifndef _WIN32 mode_t SetFileDefaultUmask() { return umask(DEFAULT_FILE_PERMISSION); } mode_t SetFileUmask(mode_t newUmask) { return umask(newUmask); } #endif /** * Check whether the file exists. * * @param filePath the file path we want to check * @return APP_ERR_OK if file exists, error code otherwise */ APP_ERROR ExistFile(const std::string &filePath) { std::string resolvedPath; APP_ERROR ret = GetRealPath(filePath, resolvedPath); if (ret != APP_ERR_OK) { return ret; } #ifndef _WIN32 struct stat fileStat = {0}; if (stat(resolvedPath.c_str(), &fileStat) == 0 && S_ISREG(fileStat.st_mode)) { #else DWORD fileStat = GetFileAttributes((LPCSTR)resolvedPath.c_str()); if ((fileStat == FILE_ATTRIBUTE_ARCHIVE) || (fileStat == FILE_ATTRIBUTE_NORMAL) || (fileStat == FILE_ATTRIBUTE_DIRECTORY)) { #endif return APP_ERR_OK; } return APP_ERR_COMM_FAILURE; } /** * Split the input path string with delimiter * * @param str path string * @param delimiters a set of delimiter * @return the vector of splitted path */ std::vector SplitPath(const std::string &str, const std::set delimiters) { std::vector result; std::string temp = str; auto start = temp.begin(); auto it = temp.begin(); for (; it != temp.end(); ++it) { if (delimiters.find(*it) != delimiters.end()) { if (it != start) { result.emplace_back(start, it); } else { result.emplace_back(""); } start = it + 1; } } result.emplace_back(start, it); return result; } /** * Create a directory * * @param dirPath the directory we want to create * @return APP_ERR_OK if create success, error code otherwise */ APP_ERROR CreateDir(const std::string &dirPath) { #ifndef _WIN32 SetFileDefaultUmask(); if (dirPath.length() > PATH_MAX) { LogError << dirPath << "is larger than " << std::to_string(PATH_MAX) << "."; return APP_ERR_COMM_NO_EXIST; } #else if (dirPath.length() > MAX_PATH) { LogError << dirPath << "is larger than " << std::to_string(MAX_PATH) << "."; } return APP_ERR_COMM_NO_EXIST; #endif // Check the write authority of directory, if not exist, create it int dirExist = access(dirPath.c_str(), W_OK); if (-1 == dirExist) { #ifdef _WIN32 if (_mkdir(dirPath.c_str()) == -1) { #else if (mkdir(dirPath.c_str(), S_IRUSR | S_IWUSR | S_IXUSR) == -1) { #endif return APP_ERR_COMM_NO_EXIST; } } return APP_ERR_OK; } /** * Create directory recursively * * @param file target directory to create */ void CreateDirRecursively(const std::string &file) { CreateDirRecursivelyByFile(file); if (access(file.c_str(), 0) != 0) { #ifndef _WIN32 int result = mkdir(file.c_str(), S_IRUSR | S_IWUSR | S_IXUSR); // for linux #else int result = _mkdir(file.c_str()); #endif if (result < 0) { LogError << "mkdir logs file " << file << " fail."; return; } } } /** * Create directory recursively by file * * @param file target file to create */ void CreateDirRecursivelyByFile(const std::string &file) { size_t pos = file.rfind('/'); // for linux std::string filePath = file.substr(0, pos); if (access(filePath.c_str(), 0) != 0) { CreateDirRecursivelyByFile(filePath); #ifndef _WIN32 int result = mkdir(filePath.c_str(), S_IRUSR | S_IWUSR | S_IXUSR); // for linux #else int result = _mkdir(filePath.c_str()); #endif if (result < 0) { LogError << "mkdir logs file " << filePath << " fail."; return; } } } /** * Read a file, store it into the RawData structure * * @param filePath file to read to * @param fileData RawData structure to store in * @return APP_ERR_OK if create success, error code otherwise */ APP_ERROR ReadFile(const std::string &filePath, RawData &fileData) { std::string resolvedPath; APP_ERROR ret = GetRealPath(filePath, resolvedPath); if (ret != APP_ERR_OK) { return ret; } // Open file with reading mode FILE *fp = fopen(resolvedPath.c_str(), "rb"); if (fp == nullptr) { LogError << "Failed to open file"; return APP_ERR_COMM_OPEN_FAIL; } // Get the length of input file fseek(fp, 0, SEEK_END); long fileSize = ftell(fp); fseek(fp, 0, SEEK_SET); // If file not empty, read it into FileInfo and return it if (fileSize > 0) { fileData.lenOfByte = fileSize; fileData.data = std::make_shared(); fileData.data.reset(new uint8_t[fileSize], std::default_delete()); size_t readRet = fread(fileData.data.get(), 1, fileSize, fp); if (readRet <= 0) { fclose(fp); return APP_ERR_COMM_READ_FAIL; } fclose(fp); return APP_ERR_OK; } fclose(fp); return APP_ERR_COMM_FAILURE; } /** * Read a binary file, store the data into a uint8_t array * * @param fileName the file for reading * @param buffShared a shared pointer to a uint8_t array for storing file * @param buffLength the length of the array * @return APP_ERR_OK if create success, error code otherwise */ APP_ERROR ReadBinaryFile(const std::string &fileName, std::shared_ptr &buffShared, int &buffLength) { // read binary file std::ifstream inFile(fileName, std::ios::in | std::ios::binary); if (!inFile) { LogError << "FaceFeatureLib: read file " << fileName << " fail."; return APP_ERR_COMM_READ_FAIL; } // get length of file: inFile.seekg(0, inFile.end); buffLength = inFile.tellg(); inFile.seekg(0, inFile.beg); auto tempShared = std::make_shared(); tempShared.reset(new uint8_t[buffLength], std::default_delete()); inFile.read((char *)tempShared.get(), buffLength); inFile.close(); buffShared = tempShared; LogDebug << "read file: fileName=" << fileName << ", size=" << buffLength << "."; return APP_ERR_OK; } /** * Read a file with specified offset * Only used in Jpegd * * @param fileName the file for reading * @param fileData RawData structure to store in * @param offset offset for file * @return APP_ERR_OK if create success, error code otherwise */ APP_ERROR ReadFileWithOffset(const std::string &fileName, RawData &fileData, const uint32_t offset) { std::string resolvedPath; APP_ERROR ret = GetRealPath(fileName, resolvedPath); if (ret != APP_ERR_OK) { return ret; } // Open file with reading mode FILE *fp = fopen(resolvedPath.c_str(), "rb"); if (fp == nullptr) { LogError << "Failed to open file"; return APP_ERR_COMM_OPEN_FAIL; } // Get the length of input file fseek(fp, 0, SEEK_END); long fileSize = ftell(fp); fseek(fp, 0, SEEK_SET); // If file not empty, read it into FileInfo and return it if (fileSize > 0) { fileData.lenOfByte = fileSize; fileData.data = std::make_shared(); fileData.data.reset(new uint8_t[fileSize + offset], std::default_delete()); size_t readRet = fread(fileData.data.get(), 1, fileSize, fp); if (readRet <= 0) { fclose(fp); return APP_ERR_COMM_READ_FAIL; } fclose(fp); return APP_ERR_OK; } fclose(fp); return APP_ERR_COMM_FAILURE; } APP_ERROR GetRealPath(const std::string &srcPath, std::string &resolvedPath) { // Get the absolute path of input file #ifndef _WIN32 char path[PATH_MAX + 1] = {0}; if ((strlen(srcPath.c_str()) > PATH_MAX) || (realpath(srcPath.c_str(), path) == nullptr)) { #else #pragma comment(lib, "Shlwapi.lib") char path[MAX_PATH + 1] = {0}; if ((strlen(srcPath.c_str()) > MAX_PATH) || (_fullpath(path, srcPath.c_str(), MAX_PATH) == nullptr)) { #endif LogError << "Failed to get canonicalize path----" << srcPath; return APP_ERR_COMM_NO_EXIST; } resolvedPath = path; return APP_ERR_OK; } /** * Get the extension name of input file * * @param filePath the file for reading extension name * @return extension name of the file */ std::string GetExtension(const std::string &filePath) { std::set delims { '.' }; std::vector path = SplitPath(filePath, delims); return path[path.size() - 1]; } /** * Get file canonical name * * @param filePath absolute path of the target file * @return filename of the file */ std::string GetName(const std::string &filePath) { std::set delims { '/' }; std::vector path = SplitPath(filePath, delims); return (path.size() < 1) ? "" : path[path.size() - 1]; } /** * Get the Parent of input file * * @param filePath file for looking for parent * @return parent of the file */ std::string GetParent(const std::string &filePath) { std::set delims { '/' }; std::vector path = SplitPath(filePath, delims); return (path.size() < TWO) ? "" : path[path.size() - TWO]; } /** * Change the current directory * * @param dir target directory to change to * @return APP_ERR_OK if create success, error code otherwise */ APP_ERROR ChangeDir(const std::string &dir) { std::string resolvedPath; APP_ERROR ret = GetRealPath(dir, resolvedPath); if (ret != APP_ERR_OK) { return ret; } #ifndef _WIN32 char path[PATH_MAX + 1] = {0}; resolvedPath.copy(path, resolvedPath.length()); char *dName = dirname(path); if (dName == nullptr) { return APP_ERR_COMM_NO_EXIST; } #else char path[MAX_PATH + 1] = {0}; resolvedPath.copy(path, resolvedPath.length()); if (!PathRemoveFileSpecA(path)) { return APP_ERR_COMM_NO_EXIST; } char *dName = path; #endif if (chdir(dName) != 0) { return APP_ERR_COMM_NO_EXIST; } return APP_ERR_OK; } /** * Append stream to file * * @param fileName to append to * @param stream content of string * @param streamLength length of string */ void SaveFileAppend(const std::string &fileName, const std::string &stream, const int streamLength) { LogDebug << "saving binary file by app: fileName=" << fileName << ", streamLength=" << streamLength; std::ofstream outfile(fileName, std::ios::app | std::ofstream::binary); outfile.write(stream.c_str(), streamLength); outfile.close(); } /** * Overwrite a file with stream * * @param fileName to overwrite to * @param stream content of string * @param streamLength length of string */ void SaveFileOverwrite(const std::string &fileName, const std::string &stream, const int streamLength) { LogDebug << "Saving binary file by over write: fileName=" << fileName << ", streamLength=" << streamLength; std::ofstream outfile(fileName, std::ios::out | std::ofstream::binary); outfile.write(stream.c_str(), streamLength); outfile.close(); } /** * Copy file * * @param srcFile from source * @param destFile to destination */ void CopyFile(const std::string &srcFile, const std::string &destFile) { std::ifstream in(srcFile, std::ios::binary); if (!in) { LogError << "Failed to get source file, it may be not exists. srcFile=" << srcFile; return; } std::ofstream out(destFile, std::ios::binary); if (!out) { LogError << "Failed to save destination file. destFile=" << destFile; in.close(); return; } char flush[BUFFER_SIZE]; while (!in.eof()) { in.read(flush, BUFFER_SIZE); out.write(flush, in.gcount()); } out.close(); in.close(); } /** * Save file with timestamp under specified folder * * @param dataBuffer buffer of file * @param bufferSize buffer size * @param folderName specified folder will be created if it not existed * @param fileName file name without suffix, the finally name will append time stamp to it * @param fileSuffix suffix name of file */ APP_ERROR SaveFileWithTimeStamp(std::shared_ptr dataBuffer, uint32_t bufferSize, std::string folderName, std::string fileName, std::string fileSuffix) { #ifndef _WIN32 SetFileDefaultUmask(); #endif APP_ERROR ret; if (folderName.length() != 0) { ret = CreateDir(folderName); if (ret != APP_ERR_OK) { return ret; } } // Result file name use the time stamp as a suffix std::string timeString; GetCurTimeString(timeString); // Create file under folderName directory std::stringstream resultPathName; if (folderName.length() == 0) { resultPathName << "./" << fileName << "_" << timeString << fileSuffix; } else { resultPathName << folderName << "/" << fileName << "_" << timeString << fileSuffix; } std::string resolvedPath; ret = GetRealPath(resultPathName.str(), resolvedPath); if (ret != APP_ERR_OK) { return ret; } FILE *fp = fopen(resolvedPath.c_str(), "wb"); if (fp == nullptr) { LogError << "Failed to open file"; return APP_ERR_COMM_OPEN_FAIL; } uint32_t result = fwrite(dataBuffer.get(), 1, bufferSize, fp); if (result != bufferSize) { LogError << "Failed to write file"; fclose(fp); return APP_ERR_COMM_WRITE_FAIL; } LogInfo << "Write result to file successfully"; uint32_t ff = fflush(fp); if (ff != 0) { LogError << "Failed to fflush file"; fclose(fp); return APP_ERR_COMM_DESTORY_FAIL; } uint32_t fc = fclose(fp); if (fc != 0) { LogError << "Failed to fclose file"; return APP_ERR_COMM_DESTORY_FAIL; } return APP_ERR_OK; } /** * Convert the current time to the format "%Y%m%d%H%M%S" * * @param timeString buffer to save the time string with format "%Y%m%d%H%M%S" */ void GetCurTimeString(std::string &timeString) { // Result file name use the time stamp as a suffix const int timeZoneDiff = 28800; // 8 hour time difference const int timeStringSize = 32; char timeStr[timeStringSize] = {0}; time_t tmValue = time(nullptr) + timeZoneDiff; struct tm tmStruct = {0}; #ifdef _WIN32 if (0 == gmtime_s(&tmStruct, &tmValue)) { #else if (nullptr != gmtime_r(&tmValue, &tmStruct)) { #endif strftime(timeStr, sizeof(timeStr), "%Y%m%d%H%M%S", &tmStruct); } timeString = timeStr; return; }