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