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