1 // Copyright 2019 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "brillo/files/file_util.h"
6
7 #include <fcntl.h>
8 #include <sys/stat.h>
9 #include <unistd.h>
10
11 #include <utility>
12
13 #include <base/files/file_util.h>
14 #include <base/logging.h>
15 #include <base/strings/stringprintf.h>
16 #include <brillo/syslog_logging.h>
17
18 namespace brillo {
19
20 namespace {
21
22 enum class FSObjectType {
23 RegularFile = 0,
24 Directory,
25 };
26
OpenOrRemake(SafeFD * parent,const std::string & name,FSObjectType type,int permissions,uid_t uid,gid_t gid,int flags)27 SafeFD::SafeFDResult OpenOrRemake(SafeFD* parent,
28 const std::string& name,
29 FSObjectType type,
30 int permissions,
31 uid_t uid,
32 gid_t gid,
33 int flags) {
34 SafeFD::Error err = IsValidFilename(name);
35 if (SafeFD::IsError(err)) {
36 return std::make_pair(SafeFD(), err);
37 }
38
39 SafeFD::SafeFDResult (SafeFD::*maker)(const base::FilePath&, mode_t, uid_t,
40 gid_t, int);
41 if (type == FSObjectType::Directory) {
42 maker = &SafeFD::MakeDir;
43 } else {
44 maker = &SafeFD::MakeFile;
45 }
46
47 SafeFD child;
48 std::tie(child, err) =
49 (parent->*maker)(base::FilePath(name), permissions, uid, gid, flags);
50 if (child.is_valid()) {
51 return std::make_pair(std::move(child), err);
52 }
53
54 // Rmdir should be used on directories. However, kWrongType indicates when
55 // a directory was expected and a non-directory was found or when a
56 // directory was found but not expected, so XOR was used.
57 if ((type == FSObjectType::Directory) ^ (err == SafeFD::Error::kWrongType)) {
58 err = parent->Rmdir(name, true /*recursive*/);
59 } else {
60 err = parent->Unlink(name);
61 }
62 if (SafeFD::IsError(err)) {
63 PLOG(ERROR) << "Failed to clean up \"" << name << "\"";
64 return std::make_pair(SafeFD(), err);
65 }
66
67 std::tie(child, err) =
68 (parent->*maker)(base::FilePath(name), permissions, uid, gid, flags);
69 return std::make_pair(std::move(child), err);
70 }
71
72 } // namespace
73
IsValidFilename(const std::string & filename)74 SafeFD::Error IsValidFilename(const std::string& filename) {
75 if (filename == "." || filename == ".." ||
76 filename.find("/") != std::string::npos) {
77 return SafeFD::Error::kBadArgument;
78 }
79 return SafeFD::Error::kNoError;
80 }
81
GetFDPath(int fd)82 base::FilePath GetFDPath(int fd) {
83 const base::FilePath proc_fd(base::StringPrintf("/proc/self/fd/%d", fd));
84 base::FilePath resolved;
85 if (!base::ReadSymbolicLink(proc_fd, &resolved)) {
86 LOG(ERROR) << "Failed to read " << proc_fd.value();
87 return base::FilePath();
88 }
89 return resolved;
90 }
91
OpenOrRemakeDir(SafeFD * parent,const std::string & name,int permissions,uid_t uid,gid_t gid,int flags)92 SafeFD::SafeFDResult OpenOrRemakeDir(SafeFD* parent,
93 const std::string& name,
94 int permissions,
95 uid_t uid,
96 gid_t gid,
97 int flags) {
98 return OpenOrRemake(parent, name, FSObjectType::Directory, permissions, uid,
99 gid, flags);
100 }
101
OpenOrRemakeFile(SafeFD * parent,const std::string & name,int permissions,uid_t uid,gid_t gid,int flags)102 SafeFD::SafeFDResult OpenOrRemakeFile(SafeFD* parent,
103 const std::string& name,
104 int permissions,
105 uid_t uid,
106 gid_t gid,
107 int flags) {
108 return OpenOrRemake(parent, name, FSObjectType::RegularFile, permissions, uid,
109 gid, flags);
110 }
111
112 } // namespace brillo
113