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
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
IsFile(const std::string & path)88 bool IsFile(const std::string &path)
89 {
90 // check whether the path exists
91 struct stat buf = {};
92 if (stat(path.c_str(), &buf) != 0) {
93 return false;
94 }
95 return S_ISREG(buf.st_mode);
96 }
97
MkDirRecurse(const std::string & path,mode_t mode)98 bool MkDirRecurse(const std::string& path, mode_t mode)
99 {
100 std::string::size_type index = 0;
101 do {
102 std::string subPath = path;
103 index = path.find('/', index + 1);
104 if (index != std::string::npos) {
105 subPath = path.substr(0, index);
106 }
107
108 if (TEMP_FAILURE_RETRY(access(subPath.c_str(), F_OK)) != 0) {
109 if (MkDir(subPath, mode) != 0 && errno != EEXIST) {
110 return false;
111 }
112 }
113 } while (index != std::string::npos);
114
115 return TEMP_FAILURE_RETRY(access(path.c_str(), F_OK)) == 0;
116 }
117
118 // 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)119 bool PrepareDir(const std::string &path, mode_t mode, uid_t uid, gid_t gid)
120 {
121 LOGI("prepare for %{public}s", path.c_str());
122
123 // check whether the path exists
124 struct stat st;
125 if (TEMP_FAILURE_RETRY(lstat(path.c_str(), &st)) == E_ERR) {
126 if (errno != ENOENT) {
127 LOGE("failed to lstat, errno %{public}d", errno);
128 return false;
129 }
130 } else {
131 if (!S_ISDIR(st.st_mode)) {
132 LOGE("%{public}s exists and is not a directory", path.c_str());
133 return false;
134 }
135
136 if (((st.st_mode & ALL_PERMS) != mode) && ChMod(path, mode)) {
137 LOGE("dir exists and failed to chmod, errno %{public}d", errno);
138 return false;
139 }
140
141 if (((st.st_uid != uid) || (st.st_gid != gid)) && ChOwn(path, uid, gid)) {
142 LOGE("dir exists and failed to chown, errno %{public}d", errno);
143 return false;
144 }
145
146 return true;
147 }
148
149 mode_t mask = umask(0);
150 if (MkDir(path, mode)) {
151 LOGE("failed to mkdir, errno %{public}d", errno);
152 umask(mask);
153 return false;
154 }
155 umask(mask);
156
157 if (ChMod(path, mode)) {
158 LOGE("failed to chmod, errno %{public}d", errno);
159 return false;
160 }
161
162 if (ChOwn(path, uid, gid)) {
163 LOGE("failed to chown, errno %{public}d", errno);
164 return false;
165 }
166
167 #ifdef USE_LIBRESTORECON
168 int err = Restorecon(path.c_str());
169 if (err) {
170 LOGE("failed to restorecon, err:%{public}d", err);
171 return false;
172 }
173 #endif
174
175 return true;
176 }
177
RmDirRecurse(const std::string & path)178 bool RmDirRecurse(const std::string &path)
179 {
180 LOGD("rm dir %{public}s", path.c_str());
181 DIR *dir = opendir(path.c_str());
182 if (!dir) {
183 if (errno == ENOENT) {
184 return true;
185 }
186
187 LOGE("failed to open dir %{public}s, errno %{public}d", path.c_str(), errno);
188 return false;
189 }
190
191 for (struct dirent *ent = readdir(dir); ent != nullptr; ent = readdir(dir)) {
192 if (ent->d_type == DT_DIR) {
193 if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) {
194 continue;
195 }
196
197 if (!RmDirRecurse(path + "/" + ent->d_name)) {
198 LOGE("failed to RmDirRecurse %{public}s, errno %{public}d", path.c_str(), errno);
199 (void)closedir(dir);
200 return false;
201 }
202 } else {
203 if (unlink((path + "/" + ent->d_name).c_str())) {
204 LOGE("failed to unlink file %{public}s, errno %{public}d", ent->d_name, errno);
205 (void)closedir(dir);
206 return false;
207 }
208 }
209 }
210
211 (void)closedir(dir);
212 if (rmdir(path.c_str())) {
213 LOGE("failed to rm dir %{public}s, errno %{public}d", path.c_str(), errno);
214 return false;
215 }
216 return true;
217 }
218
TravelChmod(const std::string & path,mode_t mode)219 void TravelChmod(const std::string &path, mode_t mode)
220 {
221 struct stat st;
222 DIR *d = nullptr;
223 struct dirent *dp = nullptr;
224 const char *skip1 = ".";
225 const char *skip2 = "..";
226
227 if (stat(path.c_str(), &st) < 0 || !S_ISDIR(st.st_mode)) {
228 LOGE("invalid path");
229 return;
230 }
231
232 (void)ChMod(path, mode);
233 if (!(d = opendir(path.c_str()))) {
234 LOGE("opendir failed");
235 return;
236 }
237
238 while ((dp = readdir(d)) != nullptr) {
239 if ((!strncmp(dp->d_name, skip1, strlen(skip1))) || (!strncmp(dp->d_name, skip2, strlen(skip2)))) {
240 continue;
241 }
242 std::string subpath = path + "/" + dp->d_name;
243 stat(subpath.c_str(), &st);
244 (void)ChMod(subpath, mode);
245 if (S_ISDIR(st.st_mode)) {
246 TravelChmod(subpath, mode);
247 }
248 }
249 (void)closedir(d);
250 }
251
StringToUint32(const std::string & str,uint32_t & num)252 bool StringToUint32(const std::string &str, uint32_t &num)
253 {
254 if (str.empty()) {
255 return false;
256 }
257 if (!IsNumericStr(str)) {
258 LOGE("Not numeric entry");
259 return false;
260 }
261
262 int value;
263 if (!StrToInt(str, value)) {
264 LOGE("String to int convert failed");
265 return false;
266 }
267 num = static_cast<uint32_t>(value);
268
269 return true;
270 }
271
GetSubDirs(const std::string & path,std::vector<std::string> & dirList)272 void GetSubDirs(const std::string &path, std::vector<std::string> &dirList)
273 {
274 dirList.clear();
275
276 struct stat st;
277 int ret = TEMP_FAILURE_RETRY(lstat(path.c_str(), &st));
278 if (ret != 0 || ((st.st_mode & S_IFDIR) != S_IFDIR)) {
279 LOGE("path is not dir");
280 return;
281 }
282
283 DIR *dir = opendir(path.c_str());
284 if (!dir) {
285 LOGE("failed to open dir %{public}s, errno %{public}d", path.c_str(), errno);
286 return;
287 }
288
289 for (struct dirent *ent = readdir(dir); ent != nullptr; ent = readdir(dir)) {
290 if ((ent->d_type != DT_DIR) ||
291 (strcmp(ent->d_name, ".") == 0) ||
292 (strcmp(ent->d_name, "..") == 0)) {
293 continue;
294 }
295 dirList.push_back(ent->d_name);
296 }
297
298 (void)closedir(dir);
299 }
300
ReadDigitDir(const std::string & path,std::vector<FileList> & dirInfo)301 void ReadDigitDir(const std::string &path, std::vector<FileList> &dirInfo)
302 {
303 struct stat st;
304 int ret = TEMP_FAILURE_RETRY(lstat(path.c_str(), &st));
305 if (ret != 0 || ((st.st_mode & S_IFDIR) != S_IFDIR)) {
306 LOGE("path is not dir");
307 return;
308 }
309
310 DIR *dir = opendir(path.c_str());
311 if (!dir) {
312 LOGE("failed to open dir %{public}s, errno %{public}d", path.c_str(), errno);
313 return;
314 }
315
316 for (struct dirent *ent = readdir(dir); ent != nullptr; ent = readdir(dir)) {
317 if ((ent->d_type != DT_DIR) ||
318 (strcmp(ent->d_name, ".") == 0) ||
319 (strcmp(ent->d_name, "..") == 0)) {
320 continue;
321 }
322
323 uint32_t userId;
324 std::string name(ent->d_name);
325 if (!StringToUint32(name, userId)) {
326 continue;
327 }
328 FileList entry = {
329 .userId = userId,
330 .path = path + "/" + name
331 };
332 dirInfo.push_back(entry);
333 }
334
335 (void)closedir(dir);
336 }
337
OpenSubFile(const std::string & path,std::vector<std::string> & file)338 void OpenSubFile(const std::string &path, std::vector<std::string> &file)
339 {
340 struct stat st;
341 int ret = TEMP_FAILURE_RETRY(lstat(path.c_str(), &st));
342 if (ret != 0 || ((st.st_mode & S_IFDIR) != S_IFDIR)) {
343 LOGI("path is not dir");
344 return;
345 }
346
347 DIR *dir = opendir(path.c_str());
348 if (!dir) {
349 LOGI("failed to open dir %{public}s, errno %{public}d", path.c_str(), errno);
350 return;
351 }
352 for (struct dirent *ent = readdir(dir); ent != nullptr; ent = readdir(dir)) {
353 if ((ent->d_type != DT_DIR)) {
354 std::string name(ent->d_name);
355 std::string filePath = path + "/" + name;
356 LOGI("filePath is %{public}s", filePath.c_str());
357 file.push_back(filePath);
358 continue;
359 } else {
360 if ((strcmp(ent->d_name, ".") == 0) || (strcmp(ent->d_name, "..") == 0)) {
361 continue;
362 }
363 std::string name(ent->d_name);
364 std::string filePath = path + "/" + name;
365 OpenSubFile(filePath, file);
366 }
367 }
368 (void)closedir(dir);
369 }
370
ReadFile(const std::string & path,std::string * str)371 bool ReadFile(const std::string &path, std::string *str)
372 {
373 std::ifstream infile;
374 int cnt = 0;
375
376 std::string rpath(PATH_MAX + 1, '\0');
377 if ((path.length() > PATH_MAX) || (realpath(path.c_str(), rpath.data()) == nullptr)) {
378 LOGE("realpath failed");
379 return false;
380 }
381
382 infile.open(rpath.c_str());
383 if (!infile) {
384 LOGE("Cannot open file");
385 return false;
386 }
387
388 while (1) {
389 std::string subStr;
390 infile >> subStr;
391 if (subStr == "") {
392 break;
393 }
394 cnt++;
395 *str = *str + subStr + '\n';
396 }
397
398 infile.close();
399 return cnt == 0 ? false : true;
400 }
401
FromatCmd(std::vector<std::string> & cmd)402 static std::vector<char*> FromatCmd(std::vector<std::string> &cmd)
403 {
404 std::vector<char*>res;
405 res.reserve(cmd.size() + 1);
406
407 for (auto& line : cmd) {
408 LOGI("cmd %{public}s", line.c_str());
409 res.emplace_back(const_cast<char*>(line.c_str()));
410 }
411 res.emplace_back(nullptr);
412
413 return res;
414 }
415
ForkExec(std::vector<std::string> & cmd,std::vector<std::string> * output)416 int ForkExec(std::vector<std::string> &cmd, std::vector<std::string> *output)
417 {
418 int pipe_fd[2];
419 pid_t pid;
420 int status;
421 auto args = FromatCmd(cmd);
422
423 if (pipe(pipe_fd) < 0) {
424 LOGE("creat pipe failed");
425 return E_ERR;
426 }
427
428 pid = fork();
429 if (pid == -1) {
430 LOGE("fork failed");
431 return E_ERR;
432 } else if (pid == 0) {
433 (void)close(pipe_fd[0]);
434 if (dup2(pipe_fd[1], STDOUT_FILENO) == -1) {
435 LOGE("dup2 failed");
436 exit(1);
437 }
438 (void)close(pipe_fd[1]);
439 execvp(args[0], const_cast<char **>(args.data()));
440 LOGE("execvp failed errno: %{public}d", errno);
441 exit(1);
442 } else {
443 (void)close(pipe_fd[1]);
444 if (output) {
445 char buf[BUF_LEN] = { 0 };
446 (void)memset_s(buf, sizeof(buf), 0, sizeof(buf));
447 output->clear();
448 while (read(pipe_fd[0], buf, BUF_LEN - 1) > 0) {
449 LOGI("get result %{public}s", buf);
450 output->push_back(buf);
451 }
452 }
453
454 (void)close(pipe_fd[0]);
455 waitpid(pid, &status, 0);
456 if (errno == ECHILD) {
457 return E_NO_CHILD;
458 }
459 if (!WIFEXITED(status)) {
460 LOGE("Process exits abnormally");
461 return E_ERR;
462 }
463 }
464 return E_OK;
465 }
466
TraverseDirUevent(const std::string & path,bool flag)467 void TraverseDirUevent(const std::string &path, bool flag)
468 {
469 DIR *dir = opendir(path.c_str());
470 if (dir == nullptr) {
471 return;
472 }
473
474 int dirFd = dirfd(dir);
475 int fd = openat(dirFd, "uevent", O_WRONLY | O_CLOEXEC);
476 if (fd >= 0) {
477 std::string writeStr = "add\n";
478 write(fd, writeStr.c_str(), writeStr.length());
479 (void)close(fd);
480 }
481
482 for (struct dirent *ent = readdir(dir); ent != nullptr; ent = readdir(dir)) {
483 if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) {
484 continue;
485 }
486
487 if (ent->d_type != DT_DIR && !flag) {
488 continue;
489 }
490
491 TraverseDirUevent(path + "/" + ent->d_name, false);
492 }
493
494 (void)closedir(dir);
495 }
496
IsSameGidUid(const std::string & dir,uid_t uid,gid_t gid)497 int IsSameGidUid(const std::string &dir, uid_t uid, gid_t gid)
498 {
499 struct stat st;
500 if (TEMP_FAILURE_RETRY(lstat(dir.c_str(), &st)) == E_ERR) {
501 LOGE("failed to lstat, errno %{public}d", errno);
502 if (errno == ENOENT) {
503 return E_NON_EXIST;
504 }
505 return E_SYS_ERR;
506 }
507 return (st.st_uid == uid) && (st.st_gid == gid) ? E_OK : E_DIFF_UID_GID;
508 }
509
MoveDataShell(const std::string & from,const std::string & to)510 bool MoveDataShell(const std::string &from, const std::string &to)
511 {
512 LOGD("MoveDataShell start");
513 if (TEMP_FAILURE_RETRY(access(from.c_str(), F_OK)) != 0) {
514 return true;
515 }
516 std::vector<std::string> cmd = {
517 "/system/bin/mv",
518 from,
519 to
520 };
521 std::vector<std::string> out;
522 int32_t err = ForkExec(cmd, &out);
523 if (err != 0) {
524 LOGE("MoveDataShell failed err:%{public}d", err);
525 }
526 return true;
527 }
528
MoveFileManagerData(const std::string & filesPath)529 void MoveFileManagerData(const std::string &filesPath)
530 {
531 std::string docsPath = filesPath + "Docs/";
532 MoveDataShell(filesPath + "Download/", docsPath);
533 MoveDataShell(filesPath + "Documents/", docsPath);
534 MoveDataShell(filesPath + "Desktop/", docsPath);
535 MoveDataShell(filesPath + ".Trash/", docsPath);
536 }
537
ChownRecursion(const std::string & dir,uid_t uid,gid_t gid)538 void ChownRecursion(const std::string &dir, uid_t uid, gid_t gid)
539 {
540 std::vector<std::string> cmd = {
541 "/system/bin/chown",
542 "-R",
543 std::to_string(uid) + ":" + std::to_string(gid),
544 dir
545 };
546 std::vector<std::string> out;
547 int32_t err = ForkExec(cmd, &out);
548 if (err != 0) {
549 LOGE("path: %{public}s chown failed err:%{public}d", cmd.back().c_str(), err);
550 }
551 }
552
553 } // STORAGE_DAEMON
554 } // OHOS
555