1 /*
2 * Copyright (c) 2021-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 "utils/file_utils.h"
17
18 #include <cerrno>
19 #include <cstdlib>
20 #include <cstring>
21 #include <dirent.h>
22 #include <fcntl.h>
23 #include <fstream>
24 #include <sys/stat.h>
25 #include <sys/types.h>
26 #include <sys/wait.h>
27 #include <unistd.h>
28 #include "securec.h"
29 #include "storage_service_errno.h"
30 #include "storage_service_log.h"
31 #include "string_ex.h"
32 #ifdef USE_LIBRESTORECON
33 #include "policycoreutils.h"
34 #endif
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 const uint8_t KILL_RETRY_TIME = 5;
40 const uint32_t KILL_RETRY_INTERVAL_MS = 100 * 1000;
41 const std::string MOUNT_POINT_INFO = "/proc/mounts";
42
ChMod(const std::string & path,mode_t mode)43 int32_t ChMod(const std::string &path, mode_t mode)
44 {
45 return TEMP_FAILURE_RETRY(chmod(path.c_str(), mode));
46 }
47
ChOwn(const std::string & path,uid_t uid,gid_t gid)48 int32_t ChOwn(const std::string &path, uid_t uid, gid_t gid)
49 {
50 return TEMP_FAILURE_RETRY(chown(path.c_str(), uid, gid));
51 }
52
MkDir(const std::string & path,mode_t mode)53 int32_t MkDir(const std::string &path, mode_t mode)
54 {
55 return TEMP_FAILURE_RETRY(mkdir(path.c_str(), mode));
56 }
57
RmDir(const std::string & path)58 int32_t RmDir(const std::string &path)
59 {
60 return TEMP_FAILURE_RETRY(rmdir(path.c_str()));
61 }
62
Mount(const std::string & source,const std::string & target,const char * type,unsigned long flags,const void * data)63 int32_t Mount(const std::string &source, const std::string &target, const char *type,
64 unsigned long flags, const void *data)
65 {
66 return TEMP_FAILURE_RETRY(mount(source.c_str(), target.c_str(), type, flags, data));
67 }
68
UMount(const std::string & path)69 int32_t UMount(const std::string &path)
70 {
71 return TEMP_FAILURE_RETRY(umount(path.c_str()));
72 }
73
UMount2(const std::string & path,int flag)74 int32_t UMount2(const std::string &path, int flag)
75 {
76 return TEMP_FAILURE_RETRY(umount2(path.c_str(), flag));
77 }
78
IsDir(const std::string & path)79 bool IsDir(const std::string &path)
80 {
81 // check whether the path exists
82 struct stat st;
83 int ret = TEMP_FAILURE_RETRY(lstat(path.c_str(), &st));
84 if (ret) {
85 return false;
86 }
87
88 return S_ISDIR(st.st_mode);
89 }
90
IsFile(const std::string & path)91 bool IsFile(const std::string &path)
92 {
93 // check whether the path exists
94 struct stat buf = {};
95 if (stat(path.c_str(), &buf) != 0) {
96 return false;
97 }
98 return S_ISREG(buf.st_mode);
99 }
100
MkDirRecurse(const std::string & path,mode_t mode)101 bool MkDirRecurse(const std::string& path, mode_t mode)
102 {
103 std::string::size_type index = 0;
104 do {
105 std::string subPath = path;
106 index = path.find('/', index + 1);
107 if (index != std::string::npos) {
108 subPath = path.substr(0, index);
109 }
110
111 if (TEMP_FAILURE_RETRY(access(subPath.c_str(), F_OK)) != 0) {
112 if (MkDir(subPath, mode) != 0 && errno != EEXIST) {
113 return false;
114 }
115 }
116 } while (index != std::string::npos);
117
118 return TEMP_FAILURE_RETRY(access(path.c_str(), F_OK)) == 0;
119 }
120
121 // 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)122 bool PrepareDir(const std::string &path, mode_t mode, uid_t uid, gid_t gid)
123 {
124 LOGI("prepare for %{public}s", path.c_str());
125
126 // check whether the path exists
127 struct stat st;
128 if (TEMP_FAILURE_RETRY(lstat(path.c_str(), &st)) == E_ERR) {
129 if (errno != ENOENT) {
130 LOGE("failed to lstat, errno %{public}d", errno);
131 return false;
132 }
133 } else {
134 if (!S_ISDIR(st.st_mode)) {
135 LOGE("%{public}s exists and is not a directory", path.c_str());
136 return false;
137 }
138
139 if (((st.st_mode & ALL_PERMS) != mode) && ChMod(path, mode)) {
140 LOGE("dir exists and failed to chmod, errno %{public}d", errno);
141 return false;
142 }
143
144 if (((st.st_uid != uid) || (st.st_gid != gid)) && ChOwn(path, uid, gid)) {
145 LOGE("dir exists and failed to chown, errno %{public}d", errno);
146 return false;
147 }
148
149 return true;
150 }
151
152 mode_t mask = umask(0);
153 if (MkDir(path, mode)) {
154 LOGE("failed to mkdir, errno %{public}d", errno);
155 umask(mask);
156 return false;
157 }
158 umask(mask);
159
160 if (ChMod(path, mode)) {
161 LOGE("failed to chmod, errno %{public}d", errno);
162 return false;
163 }
164
165 if (ChOwn(path, uid, gid)) {
166 LOGE("failed to chown, errno %{public}d", errno);
167 return false;
168 }
169
170 #ifdef USE_LIBRESTORECON
171 int err = Restorecon(path.c_str());
172 if (err) {
173 LOGE("failed to restorecon, err:%{public}d", err);
174 return false;
175 }
176 #endif
177
178 return true;
179 }
180
RmDirRecurse(const std::string & path)181 bool RmDirRecurse(const std::string &path)
182 {
183 LOGD("rm dir %{public}s", path.c_str());
184 DIR *dir = opendir(path.c_str());
185 if (!dir) {
186 if (errno == ENOENT) {
187 return true;
188 }
189
190 LOGE("failed to open dir %{public}s, errno %{public}d", path.c_str(), errno);
191 return false;
192 }
193
194 for (struct dirent *ent = readdir(dir); ent != nullptr; ent = readdir(dir)) {
195 if (ent->d_type == DT_DIR) {
196 if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) {
197 continue;
198 }
199
200 if (!RmDirRecurse(path + "/" + ent->d_name)) {
201 LOGE("failed to RmDirRecurse %{public}s, errno %{public}d", path.c_str(), errno);
202 (void)closedir(dir);
203 return false;
204 }
205 } else {
206 if (unlink((path + "/" + ent->d_name).c_str())) {
207 LOGE("failed to unlink file %{public}s, errno %{public}d", ent->d_name, errno);
208 (void)closedir(dir);
209 return false;
210 }
211 }
212 }
213
214 (void)closedir(dir);
215 if (rmdir(path.c_str())) {
216 LOGE("failed to rm dir %{public}s, errno %{public}d", path.c_str(), errno);
217 return false;
218 }
219 return true;
220 }
221
TravelChmod(const std::string & path,mode_t mode)222 void TravelChmod(const std::string &path, mode_t mode)
223 {
224 struct stat st;
225 DIR *d = nullptr;
226 struct dirent *dp = nullptr;
227 const char *skip1 = ".";
228 const char *skip2 = "..";
229
230 if (stat(path.c_str(), &st) < 0 || !S_ISDIR(st.st_mode)) {
231 LOGE("invalid path");
232 return;
233 }
234
235 (void)ChMod(path, mode);
236 if (!(d = opendir(path.c_str()))) {
237 LOGE("opendir failed");
238 return;
239 }
240
241 while ((dp = readdir(d)) != nullptr) {
242 if ((!strncmp(dp->d_name, skip1, strlen(skip1))) || (!strncmp(dp->d_name, skip2, strlen(skip2)))) {
243 continue;
244 }
245 std::string subpath = path + "/" + dp->d_name;
246 stat(subpath.c_str(), &st);
247 (void)ChMod(subpath, mode);
248 if (S_ISDIR(st.st_mode)) {
249 TravelChmod(subpath, mode);
250 }
251 }
252 (void)closedir(d);
253 }
254
StringToUint32(const std::string & str,uint32_t & num)255 bool StringToUint32(const std::string &str, uint32_t &num)
256 {
257 if (str.empty()) {
258 return false;
259 }
260 if (!IsNumericStr(str)) {
261 LOGE("Not numeric entry");
262 return false;
263 }
264
265 int value;
266 if (!StrToInt(str, value)) {
267 LOGE("String to int convert failed");
268 return false;
269 }
270 num = static_cast<uint32_t>(value);
271
272 return true;
273 }
274
GetSubDirs(const std::string & path,std::vector<std::string> & dirList)275 void GetSubDirs(const std::string &path, std::vector<std::string> &dirList)
276 {
277 dirList.clear();
278
279 struct stat st;
280 int ret = TEMP_FAILURE_RETRY(lstat(path.c_str(), &st));
281 if (ret != 0 || ((st.st_mode & S_IFDIR) != S_IFDIR)) {
282 LOGE("path is not dir");
283 return;
284 }
285
286 DIR *dir = opendir(path.c_str());
287 if (!dir) {
288 LOGE("failed to open dir %{public}s, errno %{public}d", path.c_str(), errno);
289 return;
290 }
291
292 for (struct dirent *ent = readdir(dir); ent != nullptr; ent = readdir(dir)) {
293 if ((ent->d_type != DT_DIR) ||
294 (strcmp(ent->d_name, ".") == 0) ||
295 (strcmp(ent->d_name, "..") == 0)) {
296 continue;
297 }
298 dirList.push_back(ent->d_name);
299 }
300
301 (void)closedir(dir);
302 }
303
ReadDigitDir(const std::string & path,std::vector<FileList> & dirInfo)304 void ReadDigitDir(const std::string &path, std::vector<FileList> &dirInfo)
305 {
306 struct stat st;
307 int ret = TEMP_FAILURE_RETRY(lstat(path.c_str(), &st));
308 if (ret != 0 || ((st.st_mode & S_IFDIR) != S_IFDIR)) {
309 LOGE("path is not dir");
310 return;
311 }
312
313 DIR *dir = opendir(path.c_str());
314 if (!dir) {
315 LOGE("failed to open dir %{public}s, errno %{public}d", path.c_str(), errno);
316 return;
317 }
318
319 for (struct dirent *ent = readdir(dir); ent != nullptr; ent = readdir(dir)) {
320 if ((ent->d_type != DT_DIR) ||
321 (strcmp(ent->d_name, ".") == 0) ||
322 (strcmp(ent->d_name, "..") == 0)) {
323 continue;
324 }
325
326 uint32_t userId;
327 std::string name(ent->d_name);
328 if (!StringToUint32(name, userId)) {
329 continue;
330 }
331 FileList entry = {
332 .userId = userId,
333 .path = path + "/" + name
334 };
335 dirInfo.push_back(entry);
336 }
337
338 (void)closedir(dir);
339 }
340
OpenSubFile(const std::string & path,std::vector<std::string> & file)341 void OpenSubFile(const std::string &path, std::vector<std::string> &file)
342 {
343 struct stat st;
344 int ret = TEMP_FAILURE_RETRY(lstat(path.c_str(), &st));
345 if (ret != 0 || ((st.st_mode & S_IFDIR) != S_IFDIR)) {
346 LOGI("path is not dir");
347 return;
348 }
349
350 DIR *dir = opendir(path.c_str());
351 if (!dir) {
352 LOGI("failed to open dir %{public}s, errno %{public}d", path.c_str(), errno);
353 return;
354 }
355 for (struct dirent *ent = readdir(dir); ent != nullptr; ent = readdir(dir)) {
356 if ((ent->d_type != DT_DIR)) {
357 std::string name(ent->d_name);
358 std::string filePath = path + "/" + name;
359 LOGI("filePath is %{public}s", filePath.c_str());
360 file.push_back(filePath);
361 continue;
362 } else {
363 if ((strcmp(ent->d_name, ".") == 0) || (strcmp(ent->d_name, "..") == 0)) {
364 continue;
365 }
366 std::string name(ent->d_name);
367 std::string filePath = path + "/" + name;
368 OpenSubFile(filePath, file);
369 }
370 }
371 (void)closedir(dir);
372 }
373
ReadFile(const std::string & path,std::string * str)374 bool ReadFile(const std::string &path, std::string *str)
375 {
376 std::ifstream infile;
377 int cnt = 0;
378
379 std::string rpath(PATH_MAX + 1, '\0');
380 if ((path.length() > PATH_MAX) || (realpath(path.c_str(), rpath.data()) == nullptr)) {
381 LOGE("realpath failed");
382 return false;
383 }
384
385 infile.open(rpath.c_str());
386 if (!infile) {
387 LOGE("Cannot open file");
388 return false;
389 }
390
391 while (1) {
392 std::string subStr;
393 infile >> subStr;
394 if (subStr == "") {
395 break;
396 }
397 cnt++;
398 *str = *str + subStr + '\n';
399 }
400
401 infile.close();
402 return cnt == 0 ? false : true;
403 }
404
FromatCmd(std::vector<std::string> & cmd)405 static std::vector<char*> FromatCmd(std::vector<std::string> &cmd)
406 {
407 std::vector<char*>res;
408 res.reserve(cmd.size() + 1);
409
410 for (auto& line : cmd) {
411 LOGI("cmd %{public}s", line.c_str());
412 res.emplace_back(const_cast<char*>(line.c_str()));
413 }
414 res.emplace_back(nullptr);
415
416 return res;
417 }
418
ForkExec(std::vector<std::string> & cmd,std::vector<std::string> * output)419 int ForkExec(std::vector<std::string> &cmd, std::vector<std::string> *output)
420 {
421 int pipe_fd[2];
422 pid_t pid;
423 int status;
424 auto args = FromatCmd(cmd);
425
426 if (pipe(pipe_fd) < 0) {
427 LOGE("creat pipe failed");
428 return E_ERR;
429 }
430
431 pid = fork();
432 if (pid == -1) {
433 LOGE("fork failed");
434 return E_ERR;
435 } else if (pid == 0) {
436 (void)close(pipe_fd[0]);
437 if (dup2(pipe_fd[1], STDOUT_FILENO) == -1) {
438 LOGE("dup2 failed");
439 _exit(1);
440 }
441 (void)close(pipe_fd[1]);
442 execvp(args[0], const_cast<char **>(args.data()));
443 LOGE("execvp failed errno: %{public}d", errno);
444 _exit(1);
445 } else {
446 (void)close(pipe_fd[1]);
447 if (output) {
448 char buf[BUF_LEN] = { 0 };
449 (void)memset_s(buf, sizeof(buf), 0, sizeof(buf));
450 output->clear();
451 while (read(pipe_fd[0], buf, BUF_LEN - 1) > 0) {
452 LOGI("get result %{public}s", buf);
453 output->push_back(buf);
454 }
455 }
456
457 (void)close(pipe_fd[0]);
458 waitpid(pid, &status, 0);
459 if (errno == ECHILD) {
460 return E_NO_CHILD;
461 }
462 if (!WIFEXITED(status)) {
463 LOGE("Process exits abnormally");
464 return E_ERR;
465 }
466 }
467 return E_OK;
468 }
469
TraverseDirUevent(const std::string & path,bool flag)470 void TraverseDirUevent(const std::string &path, bool flag)
471 {
472 DIR *dir = opendir(path.c_str());
473 if (dir == nullptr) {
474 return;
475 }
476
477 int dirFd = dirfd(dir);
478 int fd = openat(dirFd, "uevent", O_WRONLY | O_CLOEXEC);
479 if (fd >= 0) {
480 std::string writeStr = "add\n";
481 write(fd, writeStr.c_str(), writeStr.length());
482 (void)close(fd);
483 }
484
485 for (struct dirent *ent = readdir(dir); ent != nullptr; ent = readdir(dir)) {
486 if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) {
487 continue;
488 }
489
490 if (ent->d_type != DT_DIR && !flag) {
491 continue;
492 }
493
494 TraverseDirUevent(path + "/" + ent->d_name, false);
495 }
496
497 (void)closedir(dir);
498 }
499
IsSameGidUid(const std::string & dir,uid_t uid,gid_t gid)500 int IsSameGidUid(const std::string &dir, uid_t uid, gid_t gid)
501 {
502 struct stat st;
503 if (TEMP_FAILURE_RETRY(lstat(dir.c_str(), &st)) == E_ERR) {
504 LOGE("failed to lstat, errno %{public}d", errno);
505 if (errno == ENOENT) {
506 return E_NON_EXIST;
507 }
508 return E_SYS_ERR;
509 }
510 return (st.st_uid == uid) && (st.st_gid == gid) ? E_OK : E_DIFF_UID_GID;
511 }
512
MoveDataShell(const std::string & from,const std::string & to)513 bool MoveDataShell(const std::string &from, const std::string &to)
514 {
515 LOGI("MoveDataShell start");
516 if (TEMP_FAILURE_RETRY(access(from.c_str(), F_OK)) != 0) {
517 return true;
518 }
519 std::vector<std::string> cmd = {
520 "/system/bin/mv",
521 from,
522 to
523 };
524 std::vector<std::string> out;
525 int32_t err = ForkExec(cmd, &out);
526 if (err != 0) {
527 LOGE("MoveDataShell failed err:%{public}d", err);
528 }
529 return true;
530 }
531
MoveFileManagerData(const std::string & filesPath)532 void MoveFileManagerData(const std::string &filesPath)
533 {
534 std::string docsPath = filesPath + "Docs/";
535 MoveDataShell(filesPath + "Download/", docsPath);
536 MoveDataShell(filesPath + "Documents/", docsPath);
537 MoveDataShell(filesPath + "Desktop/", docsPath);
538 MoveDataShell(filesPath + ".Trash/", docsPath);
539 }
540
ChownRecursion(const std::string & dir,uid_t uid,gid_t gid)541 void ChownRecursion(const std::string &dir, uid_t uid, gid_t gid)
542 {
543 std::vector<std::string> cmd = {
544 "/system/bin/chown",
545 "-R",
546 std::to_string(uid) + ":" + std::to_string(gid),
547 dir
548 };
549 std::vector<std::string> out;
550 int32_t err = ForkExec(cmd, &out);
551 if (err != 0) {
552 LOGE("path: %{public}s chown failed err:%{public}d", cmd.back().c_str(), err);
553 }
554 }
555
IsPathMounted(std::string & path)556 bool IsPathMounted(std::string &path)
557 {
558 if (path.empty()) {
559 return true;
560 }
561 if (path.back() == '/') {
562 path.pop_back();
563 }
564 std::ifstream inputStream(MOUNT_POINT_INFO.c_str(), std::ios::in);
565 if (!inputStream.is_open()) {
566 LOGE("unable to open /proc/mounts, errno is %{public}d", errno);
567 return false;
568 }
569 std::string tmpLine;
570 while (std::getline(inputStream, tmpLine)) {
571 std::stringstream ss(tmpLine);
572 std::string dst;
573 ss >> dst;
574 ss >> dst;
575 if (path == dst) {
576 inputStream.close();
577 return true;
578 }
579 }
580 inputStream.close();
581 return false;
582 }
583
KillProcess(const std::vector<ProcessInfo> & processList,std::vector<ProcessInfo> & killFailList)584 void KillProcess(const std::vector<ProcessInfo> &processList, std::vector<ProcessInfo> &killFailList)
585 {
586 if (processList.empty()) {
587 return;
588 }
589 for (const auto &item: processList) {
590 int pid = item.pid;
591 LOGI("kill pid %{public}d", pid);
592 kill(pid, SIGKILL);
593 bool isAlive = true;
594 for (int i = 0; i < KILL_RETRY_TIME; i++) {
595 if (!IsProcessAlive(pid)) {
596 LOGI("kill pid %{public}d success.", pid);
597 isAlive = false;
598 break;
599 }
600 usleep(KILL_RETRY_INTERVAL_MS);
601 }
602 if (isAlive) {
603 LOGE("kill pid %{public}d failed.", pid);
604 killFailList.push_back(item);
605 }
606 }
607 }
608
IsProcessAlive(int pid)609 bool IsProcessAlive(int pid)
610 {
611 std::stringstream procPath;
612 procPath << "/proc/" << pid << "/stat";
613 std::ifstream statFile(procPath.str());
614 if (!statFile) {
615 statFile.close();
616 return false;
617 }
618 statFile.close();
619 return true;
620 }
621
ProcessToString(std::vector<ProcessInfo> & processList)622 std::string ProcessToString(std::vector<ProcessInfo> &processList)
623 {
624 if (processList.empty()) {
625 return "";
626 }
627 std::string result;
628 for (auto & iter : processList) {
629 result += std::to_string(iter.pid) + "_" + iter.name + ",";
630 }
631 return result.empty() ? "" : result.substr(0, result.length() -1);
632 }
633 } // STORAGE_DAEMON
634 } // OHOS
635