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