xref: /aosp_15_r20/external/libbrillo/brillo/files/safe_fd.cc (revision 1a96fba65179ea7d3f56207137718607415c5953)
1*1a96fba6SXin Li // Copyright 2019 The Chromium OS Authors. All rights reserved.
2*1a96fba6SXin Li // Use of this source code is governed by a BSD-style license that can be
3*1a96fba6SXin Li // found in the LICENSE file.
4*1a96fba6SXin Li 
5*1a96fba6SXin Li #include "brillo/files/safe_fd.h"
6*1a96fba6SXin Li 
7*1a96fba6SXin Li #include <fcntl.h>
8*1a96fba6SXin Li #include <sys/stat.h>
9*1a96fba6SXin Li #include <unistd.h>
10*1a96fba6SXin Li 
11*1a96fba6SXin Li #include <base/files/file_util.h>
12*1a96fba6SXin Li #include <base/logging.h>
13*1a96fba6SXin Li #include <base/posix/eintr_wrapper.h>
14*1a96fba6SXin Li #include <brillo/files/file_util.h>
15*1a96fba6SXin Li #include <brillo/files/scoped_dir.h>
16*1a96fba6SXin Li #include <brillo/syslog_logging.h>
17*1a96fba6SXin Li 
18*1a96fba6SXin Li namespace brillo {
19*1a96fba6SXin Li 
20*1a96fba6SXin Li namespace {
21*1a96fba6SXin Li 
MakeErrorResult(SafeFD::Error error)22*1a96fba6SXin Li SafeFD::SafeFDResult MakeErrorResult(SafeFD::Error error) {
23*1a96fba6SXin Li   return std::make_pair(SafeFD(), error);
24*1a96fba6SXin Li }
25*1a96fba6SXin Li 
MakeSuccessResult(SafeFD && fd)26*1a96fba6SXin Li SafeFD::SafeFDResult MakeSuccessResult(SafeFD&& fd) {
27*1a96fba6SXin Li   return std::make_pair(std::move(fd), SafeFD::Error::kNoError);
28*1a96fba6SXin Li }
29*1a96fba6SXin Li 
OpenPathComponentInternal(int parent_fd,const std::string & file,int flags,mode_t mode)30*1a96fba6SXin Li SafeFD::SafeFDResult OpenPathComponentInternal(int parent_fd,
31*1a96fba6SXin Li                                                const std::string& file,
32*1a96fba6SXin Li                                                int flags,
33*1a96fba6SXin Li                                                mode_t mode) {
34*1a96fba6SXin Li   if (file != "/" && file.find("/") != std::string::npos) {
35*1a96fba6SXin Li     return MakeErrorResult(SafeFD::Error::kBadArgument);
36*1a96fba6SXin Li   }
37*1a96fba6SXin Li   SafeFD fd;
38*1a96fba6SXin Li 
39*1a96fba6SXin Li   // O_NONBLOCK is used to avoid hanging on edge cases (e.g. a serial port with
40*1a96fba6SXin Li   // flow control, or a FIFO without a writer).
41*1a96fba6SXin Li   if (parent_fd >= 0 || parent_fd == AT_FDCWD) {
42*1a96fba6SXin Li     fd.UnsafeReset(HANDLE_EINTR(openat(parent_fd, file.c_str(),
43*1a96fba6SXin Li                                        flags | O_NONBLOCK | O_NOFOLLOW, mode)));
44*1a96fba6SXin Li   } else if (file == "/") {
45*1a96fba6SXin Li     fd.UnsafeReset(HANDLE_EINTR(open(
46*1a96fba6SXin Li         file.c_str(), flags | O_DIRECTORY | O_NONBLOCK | O_NOFOLLOW, mode)));
47*1a96fba6SXin Li   }
48*1a96fba6SXin Li 
49*1a96fba6SXin Li   if (!fd.is_valid()) {
50*1a96fba6SXin Li     // open(2) fails with ELOOP when the last component of the |path| is a
51*1a96fba6SXin Li     // symlink. It fails with ENXIO when |path| is a FIFO and |flags| is for
52*1a96fba6SXin Li     // writing because of the O_NONBLOCK flag added above.
53*1a96fba6SXin Li     switch (errno) {
54*1a96fba6SXin Li       case ENOENT:
55*1a96fba6SXin Li         // Do not write to the log because opening a non-existent file is a
56*1a96fba6SXin Li         // frequent occurrence.
57*1a96fba6SXin Li         return MakeErrorResult(SafeFD::Error::kDoesNotExist);
58*1a96fba6SXin Li       case ELOOP:
59*1a96fba6SXin Li         // PLOG prints something along the lines of the symlink depth being too
60*1a96fba6SXin Li         // great which is is misleading so LOG is used instead.
61*1a96fba6SXin Li         LOG(ERROR) << "Symlink detected! failed to open \"" << file
62*1a96fba6SXin Li                    << "\" safely.";
63*1a96fba6SXin Li         return MakeErrorResult(SafeFD::Error::kSymlinkDetected);
64*1a96fba6SXin Li       case EISDIR:
65*1a96fba6SXin Li         PLOG(ERROR) << "Directory detected! failed to open \"" << file
66*1a96fba6SXin Li                     << "\" safely";
67*1a96fba6SXin Li         return MakeErrorResult(SafeFD::Error::kWrongType);
68*1a96fba6SXin Li       case ENOTDIR:
69*1a96fba6SXin Li         PLOG(ERROR) << "Not a directory! failed to open \"" << file
70*1a96fba6SXin Li                     << "\" safely";
71*1a96fba6SXin Li         return MakeErrorResult(SafeFD::Error::kWrongType);
72*1a96fba6SXin Li       case ENXIO:
73*1a96fba6SXin Li         PLOG(ERROR) << "FIFO detected! failed to open \"" << file
74*1a96fba6SXin Li                     << "\" safely";
75*1a96fba6SXin Li         return MakeErrorResult(SafeFD::Error::kWrongType);
76*1a96fba6SXin Li       default:
77*1a96fba6SXin Li         PLOG(ERROR) << "Failed to open \"" << file << '"';
78*1a96fba6SXin Li         return MakeErrorResult(SafeFD::Error::kIOError);
79*1a96fba6SXin Li     }
80*1a96fba6SXin Li   }
81*1a96fba6SXin Li 
82*1a96fba6SXin Li   // Remove the O_NONBLOCK flag unless the original |flags| have it.
83*1a96fba6SXin Li   if ((flags & O_NONBLOCK) == 0) {
84*1a96fba6SXin Li     flags = fcntl(fd.get(), F_GETFL);
85*1a96fba6SXin Li     if (flags == -1) {
86*1a96fba6SXin Li       PLOG(ERROR) << "Failed to get fd flags for " << file;
87*1a96fba6SXin Li       return MakeErrorResult(SafeFD::Error::kIOError);
88*1a96fba6SXin Li     }
89*1a96fba6SXin Li     if (fcntl(fd.get(), F_SETFL, flags & ~O_NONBLOCK)) {
90*1a96fba6SXin Li       PLOG(ERROR) << "Failed to set fd flags for " << file;
91*1a96fba6SXin Li       return MakeErrorResult(SafeFD::Error::kIOError);
92*1a96fba6SXin Li     }
93*1a96fba6SXin Li   }
94*1a96fba6SXin Li 
95*1a96fba6SXin Li   return MakeSuccessResult(std::move(fd));
96*1a96fba6SXin Li }
97*1a96fba6SXin Li 
OpenSafelyInternal(int parent_fd,const base::FilePath & path,int flags,mode_t mode)98*1a96fba6SXin Li SafeFD::SafeFDResult OpenSafelyInternal(int parent_fd,
99*1a96fba6SXin Li                                         const base::FilePath& path,
100*1a96fba6SXin Li                                         int flags,
101*1a96fba6SXin Li                                         mode_t mode) {
102*1a96fba6SXin Li   std::vector<std::string> components;
103*1a96fba6SXin Li   path.GetComponents(&components);
104*1a96fba6SXin Li 
105*1a96fba6SXin Li   auto itr = components.begin();
106*1a96fba6SXin Li   if (itr == components.end()) {
107*1a96fba6SXin Li     LOG(ERROR) << "A path is required.";
108*1a96fba6SXin Li     return MakeErrorResult(SafeFD::Error::kBadArgument);
109*1a96fba6SXin Li   }
110*1a96fba6SXin Li 
111*1a96fba6SXin Li   SafeFD::SafeFDResult child_fd;
112*1a96fba6SXin Li   int parent_flags = flags | O_NONBLOCK | O_RDONLY | O_DIRECTORY | O_PATH;
113*1a96fba6SXin Li   for (; itr + 1 != components.end(); ++itr) {
114*1a96fba6SXin Li     child_fd = OpenPathComponentInternal(parent_fd, *itr, parent_flags, 0);
115*1a96fba6SXin Li     // Operation failed, so directly return the error result.
116*1a96fba6SXin Li     if (!child_fd.first.is_valid()) {
117*1a96fba6SXin Li       return child_fd;
118*1a96fba6SXin Li     }
119*1a96fba6SXin Li     parent_fd = child_fd.first.get();
120*1a96fba6SXin Li   }
121*1a96fba6SXin Li 
122*1a96fba6SXin Li   return OpenPathComponentInternal(parent_fd, *itr, flags, mode);
123*1a96fba6SXin Li }
124*1a96fba6SXin Li 
CheckAttributes(int fd,mode_t permissions,uid_t uid,gid_t gid)125*1a96fba6SXin Li SafeFD::Error CheckAttributes(int fd,
126*1a96fba6SXin Li                               mode_t permissions,
127*1a96fba6SXin Li                               uid_t uid,
128*1a96fba6SXin Li                               gid_t gid) {
129*1a96fba6SXin Li   struct stat fd_attributes;
130*1a96fba6SXin Li   if (fstat(fd, &fd_attributes) != 0) {
131*1a96fba6SXin Li     PLOG(ERROR) << "fstat failed";
132*1a96fba6SXin Li     return SafeFD::Error::kIOError;
133*1a96fba6SXin Li   }
134*1a96fba6SXin Li 
135*1a96fba6SXin Li   if (fd_attributes.st_uid != uid) {
136*1a96fba6SXin Li     LOG(ERROR) << "Owner uid is " << fd_attributes.st_uid << " instead of "
137*1a96fba6SXin Li                << uid;
138*1a96fba6SXin Li     return SafeFD::Error::kWrongUID;
139*1a96fba6SXin Li   }
140*1a96fba6SXin Li 
141*1a96fba6SXin Li   if (fd_attributes.st_gid != gid) {
142*1a96fba6SXin Li     LOG(ERROR) << "Owner gid is " << fd_attributes.st_gid << " instead of "
143*1a96fba6SXin Li                << gid;
144*1a96fba6SXin Li     return SafeFD::Error::kWrongGID;
145*1a96fba6SXin Li   }
146*1a96fba6SXin Li 
147*1a96fba6SXin Li   if ((0777 & (fd_attributes.st_mode ^ permissions)) != 0) {
148*1a96fba6SXin Li     mode_t mask = umask(0);
149*1a96fba6SXin Li     umask(mask);
150*1a96fba6SXin Li     LOG(ERROR) << "Permissions are " << std::oct
151*1a96fba6SXin Li                << (0777 & fd_attributes.st_mode) << " instead of "
152*1a96fba6SXin Li                << (0777 & permissions) << ". Umask is " << std::oct << mask
153*1a96fba6SXin Li                << std::dec;
154*1a96fba6SXin Li     return SafeFD::Error::kWrongPermissions;
155*1a96fba6SXin Li   }
156*1a96fba6SXin Li 
157*1a96fba6SXin Li   return SafeFD::Error::kNoError;
158*1a96fba6SXin Li }
159*1a96fba6SXin Li 
GetFileSize(int fd,size_t * file_size)160*1a96fba6SXin Li SafeFD::Error GetFileSize(int fd, size_t* file_size) {
161*1a96fba6SXin Li   struct stat fd_attributes;
162*1a96fba6SXin Li   if (fstat(fd, &fd_attributes) != 0) {
163*1a96fba6SXin Li     return SafeFD::Error::kIOError;
164*1a96fba6SXin Li   }
165*1a96fba6SXin Li 
166*1a96fba6SXin Li   *file_size = fd_attributes.st_size;
167*1a96fba6SXin Li   return SafeFD::Error::kNoError;
168*1a96fba6SXin Li }
169*1a96fba6SXin Li 
170*1a96fba6SXin Li }  // namespace
171*1a96fba6SXin Li 
IsError(SafeFD::Error err)172*1a96fba6SXin Li bool SafeFD::IsError(SafeFD::Error err) {
173*1a96fba6SXin Li   return err != Error::kNoError;
174*1a96fba6SXin Li }
175*1a96fba6SXin Li 
176*1a96fba6SXin Li const char* SafeFD::RootPath = "/";
177*1a96fba6SXin Li 
Root()178*1a96fba6SXin Li SafeFD::SafeFDResult SafeFD::Root() {
179*1a96fba6SXin Li   SafeFD::SafeFDResult root =
180*1a96fba6SXin Li       OpenPathComponentInternal(-1, "/", O_DIRECTORY, 0);
181*1a96fba6SXin Li   if (strcmp(SafeFD::RootPath, "/") == 0) {
182*1a96fba6SXin Li     return root;
183*1a96fba6SXin Li   }
184*1a96fba6SXin Li 
185*1a96fba6SXin Li   if (!root.first.is_valid()) {
186*1a96fba6SXin Li     LOG(ERROR) << "Failed to open root directory!";
187*1a96fba6SXin Li     return root;
188*1a96fba6SXin Li   }
189*1a96fba6SXin Li   return root.first.OpenExistingDir(base::FilePath(SafeFD::RootPath));
190*1a96fba6SXin Li }
191*1a96fba6SXin Li 
SetRootPathForTesting(const char * new_root_path)192*1a96fba6SXin Li void SafeFD::SetRootPathForTesting(const char* new_root_path) {
193*1a96fba6SXin Li   SafeFD::RootPath = new_root_path;
194*1a96fba6SXin Li }
195*1a96fba6SXin Li 
get() const196*1a96fba6SXin Li int SafeFD::get() const {
197*1a96fba6SXin Li   return fd_.get();
198*1a96fba6SXin Li }
199*1a96fba6SXin Li 
is_valid() const200*1a96fba6SXin Li bool SafeFD::is_valid() const {
201*1a96fba6SXin Li   return fd_.is_valid();
202*1a96fba6SXin Li }
203*1a96fba6SXin Li 
reset()204*1a96fba6SXin Li void SafeFD::reset() {
205*1a96fba6SXin Li   return fd_.reset();
206*1a96fba6SXin Li }
207*1a96fba6SXin Li 
UnsafeReset(int fd)208*1a96fba6SXin Li void SafeFD::UnsafeReset(int fd) {
209*1a96fba6SXin Li   return fd_.reset(fd);
210*1a96fba6SXin Li }
211*1a96fba6SXin Li 
Write(const char * data,size_t size)212*1a96fba6SXin Li SafeFD::Error SafeFD::Write(const char* data, size_t size) {
213*1a96fba6SXin Li   if (!fd_.is_valid()) {
214*1a96fba6SXin Li     return SafeFD::Error::kNotInitialized;
215*1a96fba6SXin Li   }
216*1a96fba6SXin Li   errno = 0;
217*1a96fba6SXin Li   if (!base::WriteFileDescriptor(fd_.get(), data, size)) {
218*1a96fba6SXin Li     PLOG(ERROR) << "Failed to write to file";
219*1a96fba6SXin Li     return SafeFD::Error::kIOError;
220*1a96fba6SXin Li   }
221*1a96fba6SXin Li 
222*1a96fba6SXin Li   if (HANDLE_EINTR(ftruncate(fd_.get(), size)) != 0) {
223*1a96fba6SXin Li     PLOG(ERROR) << "Failed to truncate file";
224*1a96fba6SXin Li     return SafeFD::Error::kIOError;
225*1a96fba6SXin Li   }
226*1a96fba6SXin Li   return SafeFD::Error::kNoError;
227*1a96fba6SXin Li }
228*1a96fba6SXin Li 
ReadContents(size_t max_size)229*1a96fba6SXin Li std::pair<std::vector<char>, SafeFD::Error> SafeFD::ReadContents(
230*1a96fba6SXin Li     size_t max_size) {
231*1a96fba6SXin Li   std::vector<char> buffer;
232*1a96fba6SXin Li   if (!fd_.is_valid()) {
233*1a96fba6SXin Li     return std::make_pair(std::move(buffer), SafeFD::Error::kNotInitialized);
234*1a96fba6SXin Li   }
235*1a96fba6SXin Li 
236*1a96fba6SXin Li   size_t file_size = 0;
237*1a96fba6SXin Li   SafeFD::Error err = GetFileSize(fd_.get(), &file_size);
238*1a96fba6SXin Li   if (IsError(err)) {
239*1a96fba6SXin Li     return std::make_pair(std::move(buffer), err);
240*1a96fba6SXin Li   }
241*1a96fba6SXin Li 
242*1a96fba6SXin Li   if (file_size > max_size) {
243*1a96fba6SXin Li     return std::make_pair(std::move(buffer), SafeFD::Error::kExceededMaximum);
244*1a96fba6SXin Li   }
245*1a96fba6SXin Li 
246*1a96fba6SXin Li   buffer.resize(file_size);
247*1a96fba6SXin Li 
248*1a96fba6SXin Li   err = Read(buffer.data(), buffer.size());
249*1a96fba6SXin Li   if (IsError(err)) {
250*1a96fba6SXin Li     buffer.clear();
251*1a96fba6SXin Li   }
252*1a96fba6SXin Li   return std::make_pair(std::move(buffer), err);
253*1a96fba6SXin Li }
254*1a96fba6SXin Li 
Read(char * data,size_t size)255*1a96fba6SXin Li SafeFD::Error SafeFD::Read(char* data, size_t size) {
256*1a96fba6SXin Li   if (!fd_.is_valid()) {
257*1a96fba6SXin Li     return SafeFD::Error::kNotInitialized;
258*1a96fba6SXin Li   }
259*1a96fba6SXin Li 
260*1a96fba6SXin Li   if (!base::ReadFromFD(fd_.get(), data, size)) {
261*1a96fba6SXin Li     PLOG(ERROR) << "Failed to read file";
262*1a96fba6SXin Li     return SafeFD::Error::kIOError;
263*1a96fba6SXin Li   }
264*1a96fba6SXin Li   return SafeFD::Error::kNoError;
265*1a96fba6SXin Li }
266*1a96fba6SXin Li 
OpenExistingFile(const base::FilePath & path,int flags)267*1a96fba6SXin Li SafeFD::SafeFDResult SafeFD::OpenExistingFile(const base::FilePath& path,
268*1a96fba6SXin Li                                               int flags) {
269*1a96fba6SXin Li   if (!fd_.is_valid()) {
270*1a96fba6SXin Li     return MakeErrorResult(SafeFD::Error::kNotInitialized);
271*1a96fba6SXin Li   }
272*1a96fba6SXin Li 
273*1a96fba6SXin Li   return OpenSafelyInternal(get(), path, flags, 0 /*mode*/);
274*1a96fba6SXin Li }
275*1a96fba6SXin Li 
OpenExistingDir(const base::FilePath & path,int flags)276*1a96fba6SXin Li SafeFD::SafeFDResult SafeFD::OpenExistingDir(const base::FilePath& path,
277*1a96fba6SXin Li                                              int flags) {
278*1a96fba6SXin Li   if (!fd_.is_valid()) {
279*1a96fba6SXin Li     return MakeErrorResult(SafeFD::Error::kNotInitialized);
280*1a96fba6SXin Li   }
281*1a96fba6SXin Li 
282*1a96fba6SXin Li   return OpenSafelyInternal(get(), path, O_DIRECTORY | flags /*flags*/,
283*1a96fba6SXin Li                             0 /*mode*/);
284*1a96fba6SXin Li }
285*1a96fba6SXin Li 
MakeFile(const base::FilePath & path,mode_t permissions,uid_t uid,gid_t gid,int flags)286*1a96fba6SXin Li SafeFD::SafeFDResult SafeFD::MakeFile(const base::FilePath& path,
287*1a96fba6SXin Li                                       mode_t permissions,
288*1a96fba6SXin Li                                       uid_t uid,
289*1a96fba6SXin Li                                       gid_t gid,
290*1a96fba6SXin Li                                       int flags) {
291*1a96fba6SXin Li   if (!fd_.is_valid()) {
292*1a96fba6SXin Li     return MakeErrorResult(SafeFD::Error::kNotInitialized);
293*1a96fba6SXin Li   }
294*1a96fba6SXin Li 
295*1a96fba6SXin Li   // Open (and create if necessary) the parent directory.
296*1a96fba6SXin Li   base::FilePath dir_name = path.DirName();
297*1a96fba6SXin Li   SafeFD::SafeFDResult parent_dir;
298*1a96fba6SXin Li   int parent_dir_fd = get();
299*1a96fba6SXin Li   if (!dir_name.empty() &&
300*1a96fba6SXin Li       dir_name.value() != base::FilePath::kCurrentDirectory) {
301*1a96fba6SXin Li     // Apply execute permission where read permission are present for parent
302*1a96fba6SXin Li     // directories.
303*1a96fba6SXin Li     int dir_permissions = permissions | ((permissions & 0444) >> 2);
304*1a96fba6SXin Li     parent_dir =
305*1a96fba6SXin Li         MakeDir(dir_name, dir_permissions, uid, gid, O_RDONLY | O_CLOEXEC);
306*1a96fba6SXin Li     if (!parent_dir.first.is_valid()) {
307*1a96fba6SXin Li       return parent_dir;
308*1a96fba6SXin Li     }
309*1a96fba6SXin Li     parent_dir_fd = parent_dir.first.get();
310*1a96fba6SXin Li   }
311*1a96fba6SXin Li 
312*1a96fba6SXin Li   // If file already exists, validate permissions.
313*1a96fba6SXin Li   SafeFDResult file = OpenPathComponentInternal(
314*1a96fba6SXin Li       parent_dir_fd, path.BaseName().value(), flags, permissions /*mode*/);
315*1a96fba6SXin Li   if (file.first.is_valid()) {
316*1a96fba6SXin Li     SafeFD::Error err =
317*1a96fba6SXin Li         CheckAttributes(file.first.get(), permissions, uid, gid);
318*1a96fba6SXin Li     if (IsError(err)) {
319*1a96fba6SXin Li       return MakeErrorResult(err);
320*1a96fba6SXin Li     }
321*1a96fba6SXin Li     return file;
322*1a96fba6SXin Li   } else if (errno != ENOENT) {
323*1a96fba6SXin Li     return file;
324*1a96fba6SXin Li   }
325*1a96fba6SXin Li 
326*1a96fba6SXin Li   // The file does exist, create it and set the ownership.
327*1a96fba6SXin Li   file =
328*1a96fba6SXin Li       OpenPathComponentInternal(parent_dir_fd, path.BaseName().value(),
329*1a96fba6SXin Li                                 O_CREAT | O_EXCL | flags, permissions /*mode*/);
330*1a96fba6SXin Li   if (!file.first.is_valid()) {
331*1a96fba6SXin Li     return file;
332*1a96fba6SXin Li   }
333*1a96fba6SXin Li   if (HANDLE_EINTR(fchown(file.first.get(), uid, gid)) != 0) {
334*1a96fba6SXin Li     PLOG(ERROR) << "Failed to set ownership in MakeFile() for \""
335*1a96fba6SXin Li                 << path.value() << '"';
336*1a96fba6SXin Li     return MakeErrorResult(SafeFD::Error::kIOError);
337*1a96fba6SXin Li   }
338*1a96fba6SXin Li   return file;
339*1a96fba6SXin Li }
340*1a96fba6SXin Li 
MakeDir(const base::FilePath & path,mode_t permissions,uid_t uid,gid_t gid,int flags)341*1a96fba6SXin Li SafeFD::SafeFDResult SafeFD::MakeDir(const base::FilePath& path,
342*1a96fba6SXin Li                                      mode_t permissions,
343*1a96fba6SXin Li                                      uid_t uid,
344*1a96fba6SXin Li                                      gid_t gid,
345*1a96fba6SXin Li                                      int flags) {
346*1a96fba6SXin Li   if (!fd_.is_valid()) {
347*1a96fba6SXin Li     return MakeErrorResult(SafeFD::Error::kNotInitialized);
348*1a96fba6SXin Li   }
349*1a96fba6SXin Li 
350*1a96fba6SXin Li   std::vector<std::string> components;
351*1a96fba6SXin Li   path.GetComponents(&components);
352*1a96fba6SXin Li   if (components.empty()) {
353*1a96fba6SXin Li     LOG(ERROR) << "Called MakeDir() with an empty path";
354*1a96fba6SXin Li     return MakeErrorResult(SafeFD::Error::kBadArgument);
355*1a96fba6SXin Li   }
356*1a96fba6SXin Li 
357*1a96fba6SXin Li   // Walk the path creating directories as necessary.
358*1a96fba6SXin Li   SafeFD dir;
359*1a96fba6SXin Li   SafeFDResult child_dir;
360*1a96fba6SXin Li   int parent_dir_fd = get();
361*1a96fba6SXin Li   int dir_flags = O_NONBLOCK | O_DIRECTORY | O_PATH;
362*1a96fba6SXin Li   bool made_dir = false;
363*1a96fba6SXin Li   for (const auto& component : components) {
364*1a96fba6SXin Li     if (mkdirat(parent_dir_fd, component.c_str(), permissions) != 0) {
365*1a96fba6SXin Li       if (errno != EEXIST) {
366*1a96fba6SXin Li         PLOG(ERROR) << "Failed to mkdirat() " << component << ": full_path=\""
367*1a96fba6SXin Li                     << path.value() << '"';
368*1a96fba6SXin Li         return MakeErrorResult(SafeFD::Error::kIOError);
369*1a96fba6SXin Li       }
370*1a96fba6SXin Li     } else {
371*1a96fba6SXin Li       made_dir = true;
372*1a96fba6SXin Li     }
373*1a96fba6SXin Li 
374*1a96fba6SXin Li     // For the last component in the path, use the flags provided by the caller.
375*1a96fba6SXin Li     if (&component == &components.back()) {
376*1a96fba6SXin Li       dir_flags = flags | O_DIRECTORY;
377*1a96fba6SXin Li     }
378*1a96fba6SXin Li     child_dir = OpenPathComponentInternal(parent_dir_fd, component, dir_flags,
379*1a96fba6SXin Li                                           0 /*mode*/);
380*1a96fba6SXin Li     if (!child_dir.first.is_valid()) {
381*1a96fba6SXin Li       return child_dir;
382*1a96fba6SXin Li     }
383*1a96fba6SXin Li 
384*1a96fba6SXin Li     dir = std::move(child_dir.first);
385*1a96fba6SXin Li     parent_dir_fd = dir.get();
386*1a96fba6SXin Li   }
387*1a96fba6SXin Li 
388*1a96fba6SXin Li   if (made_dir) {
389*1a96fba6SXin Li     // If the directory was created, set the ownership.
390*1a96fba6SXin Li     if (HANDLE_EINTR(fchown(dir.get(), uid, gid)) != 0) {
391*1a96fba6SXin Li       PLOG(ERROR) << "Failed to set ownership in MakeDir() for \""
392*1a96fba6SXin Li                   << path.value() << '"';
393*1a96fba6SXin Li       return MakeErrorResult(SafeFD::Error::kIOError);
394*1a96fba6SXin Li     }
395*1a96fba6SXin Li   }
396*1a96fba6SXin Li   // If the directory already existed, validate the permissions.
397*1a96fba6SXin Li   SafeFD::Error err = CheckAttributes(dir.get(), permissions, uid, gid);
398*1a96fba6SXin Li   if (IsError(err)) {
399*1a96fba6SXin Li     return MakeErrorResult(err);
400*1a96fba6SXin Li   }
401*1a96fba6SXin Li 
402*1a96fba6SXin Li   return MakeSuccessResult(std::move(dir));
403*1a96fba6SXin Li }
404*1a96fba6SXin Li 
Link(const SafeFD & source_dir,const std::string & source_name,const std::string & destination_name)405*1a96fba6SXin Li SafeFD::Error SafeFD::Link(const SafeFD& source_dir,
406*1a96fba6SXin Li                            const std::string& source_name,
407*1a96fba6SXin Li                            const std::string& destination_name) {
408*1a96fba6SXin Li   if (!fd_.is_valid() || !source_dir.is_valid()) {
409*1a96fba6SXin Li     return SafeFD::Error::kNotInitialized;
410*1a96fba6SXin Li   }
411*1a96fba6SXin Li 
412*1a96fba6SXin Li   SafeFD::Error err = IsValidFilename(source_name);
413*1a96fba6SXin Li   if (IsError(err)) {
414*1a96fba6SXin Li     return err;
415*1a96fba6SXin Li   }
416*1a96fba6SXin Li 
417*1a96fba6SXin Li   err = IsValidFilename(destination_name);
418*1a96fba6SXin Li   if (IsError(err)) {
419*1a96fba6SXin Li     return err;
420*1a96fba6SXin Li   }
421*1a96fba6SXin Li 
422*1a96fba6SXin Li   if (HANDLE_EINTR(linkat(source_dir.get(), source_name.c_str(), fd_.get(),
423*1a96fba6SXin Li                           destination_name.c_str(), 0)) != 0) {
424*1a96fba6SXin Li     PLOG(ERROR) << "Failed to link \"" << destination_name << "\"";
425*1a96fba6SXin Li     return SafeFD::Error::kIOError;
426*1a96fba6SXin Li   }
427*1a96fba6SXin Li   return SafeFD::Error::kNoError;
428*1a96fba6SXin Li }
429*1a96fba6SXin Li 
Unlink(const std::string & name)430*1a96fba6SXin Li SafeFD::Error SafeFD::Unlink(const std::string& name) {
431*1a96fba6SXin Li   if (!fd_.is_valid()) {
432*1a96fba6SXin Li     return SafeFD::Error::kNotInitialized;
433*1a96fba6SXin Li   }
434*1a96fba6SXin Li 
435*1a96fba6SXin Li   SafeFD::Error err = IsValidFilename(name);
436*1a96fba6SXin Li   if (IsError(err)) {
437*1a96fba6SXin Li     return err;
438*1a96fba6SXin Li   }
439*1a96fba6SXin Li 
440*1a96fba6SXin Li   if (HANDLE_EINTR(unlinkat(fd_.get(), name.c_str(), 0 /*flags*/)) != 0) {
441*1a96fba6SXin Li     PLOG(ERROR) << "Failed to unlink \"" << name << "\"";
442*1a96fba6SXin Li     return SafeFD::Error::kIOError;
443*1a96fba6SXin Li   }
444*1a96fba6SXin Li   return SafeFD::Error::kNoError;
445*1a96fba6SXin Li }
446*1a96fba6SXin Li 
Rmdir(const std::string & name,bool recursive,size_t max_depth,bool keep_going)447*1a96fba6SXin Li SafeFD::Error SafeFD::Rmdir(const std::string& name,
448*1a96fba6SXin Li                             bool recursive,
449*1a96fba6SXin Li                             size_t max_depth,
450*1a96fba6SXin Li                             bool keep_going) {
451*1a96fba6SXin Li   if (!fd_.is_valid()) {
452*1a96fba6SXin Li     return SafeFD::Error::kNotInitialized;
453*1a96fba6SXin Li   }
454*1a96fba6SXin Li 
455*1a96fba6SXin Li   if (max_depth == 0) {
456*1a96fba6SXin Li     return SafeFD::Error::kExceededMaximum;
457*1a96fba6SXin Li   }
458*1a96fba6SXin Li 
459*1a96fba6SXin Li   SafeFD::Error err = IsValidFilename(name);
460*1a96fba6SXin Li   if (IsError(err)) {
461*1a96fba6SXin Li     return err;
462*1a96fba6SXin Li   }
463*1a96fba6SXin Li 
464*1a96fba6SXin Li   SafeFD::Error last_err = SafeFD::Error::kNoError;
465*1a96fba6SXin Li 
466*1a96fba6SXin Li   if (recursive) {
467*1a96fba6SXin Li     SafeFD dir_fd;
468*1a96fba6SXin Li     std::tie(dir_fd, err) =
469*1a96fba6SXin Li         OpenPathComponentInternal(fd_.get(), name, O_DIRECTORY, 0);
470*1a96fba6SXin Li     if (!dir_fd.is_valid()) {
471*1a96fba6SXin Li       return err;
472*1a96fba6SXin Li     }
473*1a96fba6SXin Li 
474*1a96fba6SXin Li     // The ScopedDIR takes ownership of this so dup_fd is not scoped on its own.
475*1a96fba6SXin Li     int dup_fd = dup(dir_fd.get());
476*1a96fba6SXin Li     if (dup_fd < 0) {
477*1a96fba6SXin Li       PLOG(ERROR) << "dup failed";
478*1a96fba6SXin Li       return SafeFD::Error::kIOError;
479*1a96fba6SXin Li     }
480*1a96fba6SXin Li 
481*1a96fba6SXin Li     ScopedDIR dir(fdopendir(dup_fd));
482*1a96fba6SXin Li     if (!dir.is_valid()) {
483*1a96fba6SXin Li       PLOG(ERROR) << "fdopendir failed";
484*1a96fba6SXin Li       close(dup_fd);
485*1a96fba6SXin Li       return SafeFD::Error::kIOError;
486*1a96fba6SXin Li     }
487*1a96fba6SXin Li 
488*1a96fba6SXin Li     struct stat dir_info;
489*1a96fba6SXin Li     if (fstat(dir_fd.get(), &dir_info) != 0) {
490*1a96fba6SXin Li       return SafeFD::Error::kIOError;
491*1a96fba6SXin Li     }
492*1a96fba6SXin Li 
493*1a96fba6SXin Li     errno = 0;
494*1a96fba6SXin Li     const dirent* entry = HANDLE_EINTR_IF_EQ(readdir(dir.get()), nullptr);
495*1a96fba6SXin Li     while (entry != nullptr) {
496*1a96fba6SXin Li       SafeFD::Error err = [&]() {
497*1a96fba6SXin Li         if (strcmp(entry->d_name, ".") == 0 ||
498*1a96fba6SXin Li             strcmp(entry->d_name, "..") == 0) {
499*1a96fba6SXin Li           return SafeFD::Error::kNoError;
500*1a96fba6SXin Li         }
501*1a96fba6SXin Li 
502*1a96fba6SXin Li         struct stat child_info;
503*1a96fba6SXin Li         if (fstatat(dir_fd.get(), entry->d_name, &child_info,
504*1a96fba6SXin Li                     AT_NO_AUTOMOUNT | AT_SYMLINK_NOFOLLOW) != 0) {
505*1a96fba6SXin Li           return SafeFD::Error::kIOError;
506*1a96fba6SXin Li         }
507*1a96fba6SXin Li 
508*1a96fba6SXin Li         if (child_info.st_dev != dir_info.st_dev) {
509*1a96fba6SXin Li           return SafeFD::Error::kBoundaryDetected;
510*1a96fba6SXin Li         }
511*1a96fba6SXin Li 
512*1a96fba6SXin Li         if (entry->d_type != DT_DIR) {
513*1a96fba6SXin Li           return dir_fd.Unlink(entry->d_name);
514*1a96fba6SXin Li         }
515*1a96fba6SXin Li 
516*1a96fba6SXin Li         return dir_fd.Rmdir(entry->d_name, true, max_depth - 1, keep_going);
517*1a96fba6SXin Li       }();
518*1a96fba6SXin Li 
519*1a96fba6SXin Li       if (IsError(err)) {
520*1a96fba6SXin Li         if (!keep_going) {
521*1a96fba6SXin Li           return err;
522*1a96fba6SXin Li         }
523*1a96fba6SXin Li         last_err = err;
524*1a96fba6SXin Li       }
525*1a96fba6SXin Li 
526*1a96fba6SXin Li       errno = 0;
527*1a96fba6SXin Li       entry = HANDLE_EINTR_IF_EQ(readdir(dir.get()), nullptr);
528*1a96fba6SXin Li     }
529*1a96fba6SXin Li     if (errno != 0) {
530*1a96fba6SXin Li       PLOG(ERROR) << "readdir failed";
531*1a96fba6SXin Li       return SafeFD::Error::kIOError;
532*1a96fba6SXin Li     }
533*1a96fba6SXin Li   }
534*1a96fba6SXin Li 
535*1a96fba6SXin Li   if (HANDLE_EINTR(unlinkat(fd_.get(), name.c_str(), AT_REMOVEDIR)) != 0) {
536*1a96fba6SXin Li     PLOG(ERROR) << "unlinkat failed";
537*1a96fba6SXin Li     if (errno == ENOTDIR) {
538*1a96fba6SXin Li       return SafeFD::Error::kWrongType;
539*1a96fba6SXin Li     }
540*1a96fba6SXin Li     // If there was an error during the recursive delete, we expect unlink
541*1a96fba6SXin Li     // to fail with ENOTEMPTY and we bubble the error from recursion
542*1a96fba6SXin Li     // instead.
543*1a96fba6SXin Li     if (IsError(last_err) && errno == ENOTEMPTY) {
544*1a96fba6SXin Li       return last_err;
545*1a96fba6SXin Li     }
546*1a96fba6SXin Li     return SafeFD::Error::kIOError;
547*1a96fba6SXin Li   }
548*1a96fba6SXin Li 
549*1a96fba6SXin Li   return last_err;
550*1a96fba6SXin Li }
551*1a96fba6SXin Li 
552*1a96fba6SXin Li }  // namespace brillo
553