1 /*
2  * Copyright (C) 2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "backup_hi_audit.h"
17 
18 #include <chrono>
19 #include <ctime>
20 #include <dirent.h>
21 #include <fcntl.h>
22 #include <iomanip>
23 #include <sstream>
24 #include <sys/time.h>
25 #include <unistd.h>
26 
27 #include "backup_zip_util.h"
28 #include "media_log.h"
29 
30 namespace OHOS::Media {
31 struct BackupHiAuditConfig {
32     std::string logPath;
33     std::string logName;
34     uint32_t logSize;
35     uint32_t fileSize;
36     uint32_t fileCount;
37 };
38 
39 const BackupHiAuditConfig HIAUDIT_CONFIG = { "/data/storage/el2/log/audit/", "media_library_backup", 2 * 1024,
40     3 * 1024 * 1024, 10 };
41 constexpr int8_t MILLISECONDS_LENGTH = 3;
42 constexpr int64_t SEC_TO_MILLISEC = 1000;
43 constexpr int MAX_TIME_BUFF = 64;
44 const std::string HIAUDIT_LOG_NAME = HIAUDIT_CONFIG.logPath + HIAUDIT_CONFIG.logName + "_audit.csv";
45 
BackupHiAudit()46 BackupHiAudit::BackupHiAudit()
47 {
48     Init();
49 }
50 
~BackupHiAudit()51 BackupHiAudit::~BackupHiAudit()
52 {
53     if (writeFd_ >= 0) {
54         close(writeFd_);
55     }
56 }
57 
GetInstance()58 BackupHiAudit& BackupHiAudit::GetInstance()
59 {
60     static BackupHiAudit hiAudit;
61     return hiAudit;
62 }
63 
Init()64 void BackupHiAudit::Init()
65 {
66     if (access(HIAUDIT_CONFIG.logPath.c_str(), F_OK) != 0) {
67         int ret = mkdir(HIAUDIT_CONFIG.logPath.c_str(), S_IRWXU | S_IRWXG | S_IRWXO);
68         if (ret != 0) {
69             MEDIA_ERR_LOG("Failed to create directory %{public}s.", HIAUDIT_CONFIG.logPath.c_str());
70         }
71     }
72 
73     std::lock_guard<std::mutex> lock(mutex_);
74     writeFd_ = open(HIAUDIT_LOG_NAME.c_str(), O_CREAT | O_APPEND | O_RDWR,
75         S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
76     if (writeFd_ < 0) {
77         MEDIA_ERR_LOG("writeFd_ open error errno: %{public}d", errno);
78     }
79     struct stat st;
80     writeLogSize_ = stat(HIAUDIT_LOG_NAME.c_str(), &st) ? 0 : static_cast<uint64_t>(st.st_size);
81     MEDIA_INFO_LOG("writeLogSize: %{public}u", writeLogSize_.load());
82 }
83 
GetMilliseconds()84 uint64_t BackupHiAudit::GetMilliseconds()
85 {
86     auto now = std::chrono::system_clock::now();
87     auto millisecs = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch());
88     return millisecs.count();
89 }
90 
GetFormattedTimestamp(time_t timeStamp,const std::string & format)91 std::string BackupHiAudit::GetFormattedTimestamp(time_t timeStamp, const std::string& format)
92 {
93     auto seconds = timeStamp / SEC_TO_MILLISEC;
94     char date[MAX_TIME_BUFF] = {0};
95     struct tm result {};
96     if (localtime_r(&seconds, &result) != nullptr) {
97         strftime(date, MAX_TIME_BUFF, format.c_str(), &result);
98     }
99     return std::string(date);
100 }
101 
GetFormattedTimestampEndWithMilli()102 std::string BackupHiAudit::GetFormattedTimestampEndWithMilli()
103 {
104     uint64_t milliSeconds = GetMilliseconds();
105     std::string formattedTimeStamp = GetFormattedTimestamp(milliSeconds, "%Y%m%d%H%M%S");
106     std::stringstream ss;
107     ss << formattedTimeStamp;
108     milliSeconds = milliSeconds % SEC_TO_MILLISEC;
109     ss << std::setfill('0') << std::setw(MILLISECONDS_LENGTH) << milliSeconds;
110     return ss.str();
111 }
112 
Write(const BackupAuditLog & auditLog)113 void BackupHiAudit::Write(const BackupAuditLog& auditLog)
114 {
115     std::lock_guard<std::mutex> lock(mutex_);
116     if (writeLogSize_ == 0) {
117         WriteToFile(auditLog.TitleString() + "\n");
118     }
119     std::string writeLog = GetFormattedTimestampEndWithMilli() + "," +
120         HIAUDIT_CONFIG.logName + ",NO," + auditLog.ToString();
121     if (writeLog.length() > HIAUDIT_CONFIG.logSize) {
122         MEDIA_INFO_LOG("write exceeds %{public}u: %{public}s.", HIAUDIT_CONFIG.logSize, writeLog.c_str());
123         writeLog = writeLog.substr(0, HIAUDIT_CONFIG.logSize);
124     }
125     writeLog = writeLog + "\n";
126     WriteToFile(writeLog);
127 }
128 
GetWriteFilePath()129 void BackupHiAudit::GetWriteFilePath()
130 {
131     if (writeLogSize_ < HIAUDIT_CONFIG.fileSize) {
132         return;
133     }
134 
135     close(writeFd_);
136     ZipAuditLog();
137     CleanOldAuditFile();
138 
139     writeFd_ = open(HIAUDIT_LOG_NAME.c_str(), O_CREAT | O_TRUNC | O_RDWR,
140         S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
141     if (writeFd_ < 0) {
142         MEDIA_ERR_LOG("fd open error errno: %{public}d", errno);
143     }
144 
145     writeLogSize_ = 0;
146 }
147 
CleanOldAuditFile()148 void BackupHiAudit::CleanOldAuditFile()
149 {
150     DIR* dir = opendir(HIAUDIT_CONFIG.logPath.c_str());
151     if (dir == nullptr) {
152         MEDIA_ERR_LOG("failed open dir, errno: %{public}d.", errno);
153         return;
154     }
155     uint32_t zipFileSize = 0;
156     std::string oldestAuditFile;
157     struct dirent *ptr = nullptr;
158     while ((ptr = readdir(dir)) != nullptr) {
159         if (std::string(ptr->d_name).find(HIAUDIT_CONFIG.logName) != std::string::npos &&
160             std::string(ptr->d_name).find("zip") != std::string::npos) {
161             zipFileSize = zipFileSize + 1;
162             if (oldestAuditFile.empty()) {
163                 oldestAuditFile = HIAUDIT_CONFIG.logPath + std::string(ptr->d_name);
164                 continue;
165             }
166             struct stat st;
167             stat((HIAUDIT_CONFIG.logPath + std::string(ptr->d_name)).c_str(), &st);
168             struct stat oldestSt;
169             stat(oldestAuditFile.c_str(), &oldestSt);
170             if (st.st_mtime < oldestSt.st_mtime) {
171                 oldestAuditFile = HIAUDIT_CONFIG.logPath + std::string(ptr->d_name);
172             }
173         }
174     }
175     closedir(dir);
176     if (zipFileSize > HIAUDIT_CONFIG.fileCount) {
177         remove(oldestAuditFile.c_str());
178     }
179 }
180 
WriteToFile(const std::string & content)181 void BackupHiAudit::WriteToFile(const std::string& content)
182 {
183     GetWriteFilePath();
184     if (writeFd_ < 0) {
185         MEDIA_ERR_LOG("fd invalid.");
186         return;
187     }
188     write(writeFd_, content.c_str(), content.length());
189     writeLogSize_ = writeLogSize_ + content.length();
190 }
191 
ZipAuditLog()192 void BackupHiAudit::ZipAuditLog()
193 {
194     std::string zipFileName = HIAUDIT_CONFIG.logPath + HIAUDIT_CONFIG.logName + "_audit_" +
195         GetFormattedTimestamp(GetMilliseconds(), "%Y%m%d%H%M%S");
196     std::rename(HIAUDIT_LOG_NAME.c_str(), (zipFileName + ".csv").c_str());
197     zipFile compressZip = BackupZipUtil::CreateZipFile(zipFileName + ".zip");
198     if (compressZip == nullptr) {
199         MEDIA_WARN_LOG("open zip file failed.");
200         return;
201     }
202     if (BackupZipUtil::AddFileInZip(compressZip, zipFileName + ".csv", KEEP_NONE_PARENT_PATH) == 0) {
203         remove((zipFileName + ".csv").c_str());
204     }
205     BackupZipUtil::CloseZipFile(compressZip);
206 }
207 } // namespace OHOS::Media