1 /*
2 * Copyright (c) 2021 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 "utils/file_utils.h"
17
18 #include <cerrno>
19 #include <fstream>
20 #include <unistd.h>
21 #include <cstring>
22 #include <dirent.h>
23 #include <cstdlib>
24 #include <fcntl.h>
25 #include <cerrno>
26 #include <sys/stat.h>
27 #include <sys/wait.h>
28 #include <sys/types.h>
29
30 #include "string_ex.h"
31 #include "securec.h"
32 #include "storage_service_errno.h"
33 #include "storage_service_log.h"
34
35 namespace OHOS {
36 namespace StorageDaemon {
37 constexpr uint32_t ALL_PERMS = (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO);
38 const int BUF_LEN = 1024;
39
ChMod(const std::string & path,mode_t mode)40 int32_t ChMod(const std::string &path, mode_t mode)
41 {
42 return TEMP_FAILURE_RETRY(chmod(path.c_str(), mode));
43 }
44
ChOwn(const std::string & path,uid_t uid,gid_t gid)45 int32_t ChOwn(const std::string &path, uid_t uid, gid_t gid)
46 {
47 return TEMP_FAILURE_RETRY(chown(path.c_str(), uid, gid));
48 }
49
MkDir(const std::string & path,mode_t mode)50 int32_t MkDir(const std::string &path, mode_t mode)
51 {
52 return TEMP_FAILURE_RETRY(mkdir(path.c_str(), mode));
53 }
54
RmDir(const std::string & path)55 int32_t RmDir(const std::string &path)
56 {
57 return TEMP_FAILURE_RETRY(rmdir(path.c_str()));
58 }
59
Mount(const std::string & source,const std::string & target,const char * type,unsigned long flags,const void * data)60 int32_t Mount(const std::string &source, const std::string &target, const char *type,
61 unsigned long flags, const void *data)
62 {
63 return TEMP_FAILURE_RETRY(mount(source.c_str(), target.c_str(), type, flags, data));
64 }
65
UMount(const std::string & path)66 int32_t UMount(const std::string &path)
67 {
68 return TEMP_FAILURE_RETRY(umount(path.c_str()));
69 }
70
UMount2(const std::string & path,int flag)71 int32_t UMount2(const std::string &path, int flag)
72 {
73 return TEMP_FAILURE_RETRY(umount2(path.c_str(), flag));
74 }
75
IsDir(const std::string & path)76 bool IsDir(const std::string &path)
77 {
78 // check whether the path exists
79 struct stat st;
80 int ret = TEMP_FAILURE_RETRY(lstat(path.c_str(), &st));
81 if (ret) {
82 return false;
83 }
84
85 return S_ISDIR(st.st_mode);
86 }
87
MkDirRecurse(const std::string & path,mode_t mode)88 bool MkDirRecurse(const std::string& path, mode_t mode)
89 {
90 std::string::size_type index = 0;
91 do {
92 std::string subPath = path;
93 index = path.find('/', index + 1);
94 if (index != std::string::npos) {
95 subPath = path.substr(0, index);
96 }
97
98 if (TEMP_FAILURE_RETRY(access(subPath.c_str(), F_OK)) != 0) {
99 if (MkDir(subPath, mode) != 0 && errno != EEXIST) {
100 return false;
101 }
102 }
103 } while (index != std::string::npos);
104
105 return TEMP_FAILURE_RETRY(access(path.c_str(), F_OK)) == 0;
106 }
107
108 // On success, true is returned. On error, false is returned, and errno is set appropriately.
PrepareDir(const std::string & path,mode_t mode,uid_t uid,gid_t gid)109 bool PrepareDir(const std::string &path, mode_t mode, uid_t uid, gid_t gid)
110 {
111 LOGI("prepare for %{public}s", path.c_str());
112
113 // check whether the path exists
114 struct stat st;
115 if (TEMP_FAILURE_RETRY(lstat(path.c_str(), &st)) == E_ERR) {
116 if (errno != ENOENT) {
117 LOGE("failed to lstat, errno %{public}d", errno);
118 return false;
119 }
120 } else {
121 if (!S_ISDIR(st.st_mode)) {
122 LOGE("%{public}s exists and is not a directory", path.c_str());
123 return false;
124 }
125
126 if (((st.st_mode & ALL_PERMS) != mode) && ChMod(path, mode)) {
127 LOGE("dir exists and failed to chmod, errno %{public}d", errno);
128 return false;
129 }
130
131 if (((st.st_uid != uid) || (st.st_gid != gid)) && ChOwn(path, uid, gid)) {
132 LOGE("dir exists and failed to chown, errno %{public}d", errno);
133 return false;
134 }
135
136 return true;
137 }
138
139 mode_t mask = umask(0);
140 if (MkDir(path, mode)) {
141 LOGE("failed to mkdir, errno %{public}d", errno);
142 umask(mask);
143 return false;
144 }
145 umask(mask);
146
147 if (ChMod(path, mode)) {
148 LOGE("failed to chmod, errno %{public}d", errno);
149 return false;
150 }
151
152 if (ChOwn(path, uid, gid)) {
153 LOGE("failed to chown, errno %{public}d", errno);
154 return false;
155 }
156
157 return true;
158 }
159
RmDirRecurse(const std::string & path)160 bool RmDirRecurse(const std::string &path)
161 {
162 LOGI("rm dir %{public}s", path.c_str());
163
164 DIR *dir = opendir(path.c_str());
165 if (!dir) {
166 if (errno == ENOENT) {
167 return true;
168 }
169
170 LOGE("failed to open dir %{public}s, errno %{public}d", path.c_str(), errno);
171 return false;
172 }
173
174 for (struct dirent *ent = readdir(dir); ent != nullptr; ent = readdir(dir)) {
175 if (ent->d_type == DT_DIR) {
176 if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) {
177 continue;
178 }
179
180 if (!RmDirRecurse(path + "/" + ent->d_name)) {
181 closedir(dir);
182 return false;
183 }
184 } else {
185 if (unlink((path + "/" + ent->d_name).c_str())) {
186 LOGE("failed to unlink file %{public}s, errno %{public}d", ent->d_name, errno);
187 closedir(dir);
188 return false;
189 }
190 }
191 }
192
193 closedir(dir);
194 if (rmdir(path.c_str())) {
195 LOGE("failed to rm dir %{public}s, errno %{public}d", path.c_str(), errno);
196 return false;
197 }
198
199 return true;
200 }
201
TravelChmod(std::string path,mode_t mode)202 void TravelChmod(std::string path, mode_t mode)
203 {
204 struct stat st;
205 DIR *d = NULL;
206 struct dirent *dp = NULL;
207 const char *skip1 = ".";
208 const char *skip2 = "..";
209
210 if (stat(path.c_str(), &st) < 0 || !S_ISDIR(st.st_mode)) {
211 LOGE("invalid path");
212 return;
213 }
214
215 ChMod(path, mode);
216 if (!(d = opendir(path.c_str()))) {
217 LOGE("opendir failed");
218 return;
219 }
220
221 while ((dp = readdir(d)) != NULL) {
222 if ((!strncmp(dp->d_name, skip1, strlen(skip1))) || (!strncmp(dp->d_name, skip2, strlen(skip2))))
223 continue;
224
225 std::string subpath = path + "/" + dp->d_name;
226 stat(subpath.c_str(), &st);
227 ChMod(subpath, mode);
228 if (S_ISDIR(st.st_mode))
229 TravelChmod(subpath, mode);
230 }
231 closedir(d);
232 }
233
StringToUint32(const std::string & str,uint32_t & num)234 bool StringToUint32(const std::string &str, uint32_t &num)
235 {
236 if (str.empty()) {
237 return false;
238 }
239 if (!IsNumericStr(str)) {
240 LOGE("Not numeric entry");
241 return false;
242 }
243
244 int value;
245 if (!StrToInt(str, value)) {
246 LOGE("String to int convert failed");
247 return false;
248 }
249 num = static_cast<uint32_t>(value);
250
251 return true;
252 }
253
GetSubDirs(const std::string & path,std::vector<std::string> & dirList)254 void GetSubDirs(const std::string &path, std::vector<std::string> &dirList)
255 {
256 dirList.clear();
257
258 struct stat st;
259 int ret = TEMP_FAILURE_RETRY(lstat(path.c_str(), &st));
260 if (ret != 0 || ((st.st_mode & S_IFDIR) != S_IFDIR)) {
261 LOGE("path is not dir");
262 return;
263 }
264
265 DIR *dir = opendir(path.c_str());
266 if (!dir) {
267 LOGE("failed to open dir %{public}s, errno %{public}d", path.c_str(), errno);
268 return;
269 }
270
271 for (struct dirent *ent = readdir(dir); ent != nullptr; ent = readdir(dir)) {
272 if ((ent->d_type != DT_DIR) ||
273 (strcmp(ent->d_name, ".") == 0) ||
274 (strcmp(ent->d_name, "..") == 0)) {
275 continue;
276 }
277 dirList.push_back(ent->d_name);
278 }
279
280 closedir(dir);
281 }
282
ReadDigitDir(const std::string & path,std::vector<FileList> & dirInfo)283 void ReadDigitDir(const std::string &path, std::vector<FileList> &dirInfo)
284 {
285 struct stat st;
286 int ret = TEMP_FAILURE_RETRY(lstat(path.c_str(), &st));
287 if (ret != 0 || ((st.st_mode & S_IFDIR) != S_IFDIR)) {
288 LOGE("path is not dir");
289 return;
290 }
291
292 DIR *dir = opendir(path.c_str());
293 if (!dir) {
294 LOGE("failed to open dir %{public}s, errno %{public}d", path.c_str(), errno);
295 return;
296 }
297
298 for (struct dirent *ent = readdir(dir); ent != nullptr; ent = readdir(dir)) {
299 if ((ent->d_type != DT_DIR) ||
300 (strcmp(ent->d_name, ".") == 0) ||
301 (strcmp(ent->d_name, "..") == 0)) {
302 continue;
303 }
304
305 uint32_t userId;
306 std::string name(ent->d_name);
307 if (!StringToUint32(name, userId)) {
308 continue;
309 }
310 FileList entry = {
311 .userId = userId,
312 .path = path + "/" + name
313 };
314 dirInfo.push_back(entry);
315 }
316
317 closedir(dir);
318 }
319
ReadFile(std::string path,std::string * str)320 bool ReadFile(std::string path, std::string *str)
321 {
322 std::ifstream infile;
323 int cnt = 0;
324 infile.open(path.c_str());
325 if (!infile) {
326 LOGE("Cannot open file");
327 return false;
328 }
329
330 while (1) {
331 std::string subStr;
332 infile >> subStr;
333 if (subStr == "") {
334 break;
335 }
336 cnt++;
337 *str = *str + subStr + '\n';
338 }
339
340 infile.close();
341 return cnt == 0 ? false : true;
342 }
343
FromatCmd(std::vector<std::string> & cmd)344 static std::vector<char*> FromatCmd(std::vector<std::string> &cmd)
345 {
346 std::vector<char*>res;
347 res.reserve(cmd.size() + 1);
348
349 for (auto& line : cmd) {
350 LOGI("cmd %{public}s", line.c_str());
351 res.emplace_back(const_cast<char*>(line.c_str()));
352 }
353 res.emplace_back(nullptr);
354
355 return res;
356 }
357
ForkExec(std::vector<std::string> & cmd,std::vector<std::string> * output)358 int ForkExec(std::vector<std::string> &cmd, std::vector<std::string> *output)
359 {
360 int pipe_fd[2];
361 pid_t pid;
362 int status;
363 auto args = FromatCmd(cmd);
364
365 if (pipe(pipe_fd) < 0) {
366 LOGE("creat pipe failed");
367 return E_ERR;
368 }
369
370 pid = fork();
371 if (pid == -1) {
372 LOGE("fork failed");
373 return E_ERR;
374 } else if (pid == 0) {
375 close(pipe_fd[0]);
376 if (dup2(pipe_fd[1], STDOUT_FILENO) == -1) {
377 LOGE("dup2 failed");
378 exit(1);
379 }
380 close(pipe_fd[1]);
381 execvp(args[0], const_cast<char **>(args.data()));
382 exit(0);
383 } else {
384 close(pipe_fd[1]);
385 if (output) {
386 char buf[BUF_LEN] = { 0 };
387 (void)memset_s(buf, sizeof(buf), 0, sizeof(buf));
388 output->clear();
389 while (read(pipe_fd[0], buf, BUF_LEN - 1) > 0) {
390 LOGI("get result %{public}s", buf);
391 output->push_back(buf);
392 }
393 return E_OK;
394 }
395
396 waitpid(pid, &status, 0);
397 if (errno == ECHILD) {
398 return E_NO_CHILD;
399 }
400 if (!WIFEXITED(status)) {
401 LOGE("Process exits abnormally");
402 return E_ERR;
403 }
404 }
405 return E_OK;
406 }
407
TraverseDirUevent(const std::string & path,bool flag)408 void TraverseDirUevent(const std::string &path, bool flag)
409 {
410 DIR *dir = opendir(path.c_str());
411 if (dir == nullptr) {
412 return;
413 }
414
415 int dirFd = dirfd(dir);
416 int fd = openat(dirFd, "uevent", O_WRONLY | O_CLOEXEC);
417 if (fd >= 0) {
418 std::string writeStr = "add\n";
419 int writeStrLen = writeStr.length();
420 write(fd, writeStr.c_str(), writeStrLen);
421 close(fd);
422 }
423
424 for (struct dirent *ent = readdir(dir); ent != nullptr; ent = readdir(dir)) {
425 if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) {
426 continue;
427 }
428
429 if (ent->d_type != DT_DIR && !flag) {
430 continue;
431 }
432
433 TraverseDirUevent(path + "/" + ent->d_name, false);
434 }
435
436 closedir(dir);
437 }
438 } // STORAGE_DAEMON
439 } // OHOS
440