15cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell// Copyright 2014 The Chromium OS Authors. All rights reserved.
25cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell// Use of this source code is governed by a BSD-style license that can be
35cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell// found in the LICENSE file.
45cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell
59ed0cab99f18acb3570a35e9408f24355f6b8324Alex Vakulenko#include "brillo/file_utils.h"
65cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell
75cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell#include <fcntl.h>
85cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell#include <unistd.h>
95cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell
105cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell#include <base/files/file_path.h>
115cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell#include <base/files/file_util.h>
125cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell#include <base/files/scoped_file.h>
135cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell#include <base/logging.h>
145cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell#include <base/posix/eintr_wrapper.h>
155cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell
169ed0cab99f18acb3570a35e9408f24355f6b8324Alex Vakulenkonamespace brillo {
175cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell
185cadeae88cd2ab9b4aebf6d880455df602001c90David Pursellnamespace {
195cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell
205cadeae88cd2ab9b4aebf6d880455df602001c90David Pursellenum {
215cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell  kPermissions600 = S_IRUSR | S_IWUSR,
225cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell  kPermissions777 = S_IRWXU | S_IRWXG | S_IRWXO
235cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell};
245cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell
255cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell// Verify that base file permission enums are compatible with S_Ixxx. If these
265cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell// asserts ever fail, we'll need to ensure that users of these functions switch
275cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell// away from using base permission enums and add a note to the function comments
285cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell// indicating that base enums can not be used.
295cadeae88cd2ab9b4aebf6d880455df602001c90David Pursellstatic_assert(base::FILE_PERMISSION_READ_BY_USER == S_IRUSR,
305cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell              "base file permissions don't match unistd.h permissions");
315cadeae88cd2ab9b4aebf6d880455df602001c90David Pursellstatic_assert(base::FILE_PERMISSION_WRITE_BY_USER == S_IWUSR,
325cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell              "base file permissions don't match unistd.h permissions");
335cadeae88cd2ab9b4aebf6d880455df602001c90David Pursellstatic_assert(base::FILE_PERMISSION_EXECUTE_BY_USER == S_IXUSR,
345cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell              "base file permissions don't match unistd.h permissions");
355cadeae88cd2ab9b4aebf6d880455df602001c90David Pursellstatic_assert(base::FILE_PERMISSION_READ_BY_GROUP == S_IRGRP,
365cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell              "base file permissions don't match unistd.h permissions");
375cadeae88cd2ab9b4aebf6d880455df602001c90David Pursellstatic_assert(base::FILE_PERMISSION_WRITE_BY_GROUP == S_IWGRP,
385cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell              "base file permissions don't match unistd.h permissions");
395cadeae88cd2ab9b4aebf6d880455df602001c90David Pursellstatic_assert(base::FILE_PERMISSION_EXECUTE_BY_GROUP == S_IXGRP,
405cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell              "base file permissions don't match unistd.h permissions");
415cadeae88cd2ab9b4aebf6d880455df602001c90David Pursellstatic_assert(base::FILE_PERMISSION_READ_BY_OTHERS == S_IROTH,
425cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell              "base file permissions don't match unistd.h permissions");
435cadeae88cd2ab9b4aebf6d880455df602001c90David Pursellstatic_assert(base::FILE_PERMISSION_WRITE_BY_OTHERS == S_IWOTH,
445cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell              "base file permissions don't match unistd.h permissions");
455cadeae88cd2ab9b4aebf6d880455df602001c90David Pursellstatic_assert(base::FILE_PERMISSION_EXECUTE_BY_OTHERS == S_IXOTH,
465cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell              "base file permissions don't match unistd.h permissions");
475cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell
485cadeae88cd2ab9b4aebf6d880455df602001c90David Pursellenum RegularFileOrDeleteResult {
495cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell  kFailure = 0,      // Failed to delete whatever was at the path.
505cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell  kRegularFile = 1,  // Regular file existed and was unchanged.
515cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell  kEmpty = 2         // Anything that was at the path has been deleted.
525cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell};
535cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell
545cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell// Checks if a regular file owned by |uid| and |gid| exists at |path|, otherwise
555cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell// deletes anything that might be at |path|. Returns a RegularFileOrDeleteResult
565cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell// enum indicating what is at |path| after the function finishes.
575cadeae88cd2ab9b4aebf6d880455df602001c90David PursellRegularFileOrDeleteResult RegularFileOrDelete(const base::FilePath& path,
585cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell                                              uid_t uid,
595cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell                                              gid_t gid) {
605cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell  // Check for symlinks by setting O_NOFOLLOW and checking for ELOOP. This lets
615cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell  // us use the safer fstat() instead of having to use lstat().
625cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell  base::ScopedFD scoped_fd(HANDLE_EINTR(openat(
635cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell      AT_FDCWD, path.value().c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW)));
645cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell  bool path_not_empty = (errno == ELOOP || scoped_fd != -1);
655cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell
665cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell  // If there is a file/directory at |path|, see if it matches our criteria.
675cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell  if (scoped_fd != -1) {
685cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell    struct stat file_stat;
695cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell    if (fstat(scoped_fd.get(), &file_stat) != -1 &&
7005d29044d14a60775ed6c51c75a414eb0cb50347Alex Vakulenko        S_ISREG(file_stat.st_mode) && file_stat.st_uid == uid &&
715cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell        file_stat.st_gid == gid) {
725cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell      return kRegularFile;
735cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell    }
745cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell  }
755cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell
765cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell  // If we get here and anything was at |path|, try to delete it so we can put
775cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell  // our file there.
785cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell  if (path_not_empty) {
795cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell    if (!base::DeleteFile(path, true)) {
805cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell      PLOG(WARNING) << "Failed to delete entity at \"" << path.value() << '"';
815cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell      return kFailure;
825cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell    }
835cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell  }
845cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell
855cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell  return kEmpty;
865cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell}
875cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell
885cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell// Handles common touch functionality but also provides an optional |fd_out|
895cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell// so that any further modifications to the file (e.g. permissions) can safely
905cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell// use the fd rather than the path. |fd_out| will only be set if a new file
915cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell// is created, otherwise it will be unchanged.
925cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell// If |fd_out| is null, this function will close the file, otherwise it's
935cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell// expected that |fd_out| will close the file when it goes out of scope.
945cadeae88cd2ab9b4aebf6d880455df602001c90David Pursellbool TouchFileInternal(const base::FilePath& path,
955cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell                       uid_t uid,
965cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell                       gid_t gid,
975cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell                       base::ScopedFD* fd_out) {
985cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell  RegularFileOrDeleteResult result = RegularFileOrDelete(path, uid, gid);
995cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell  switch (result) {
1005cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell    case kFailure:
1015cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell      return false;
1025cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell    case kRegularFile:
1035cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell      return true;
1045cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell    case kEmpty:
1055cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell      break;
1065cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell  }
1075cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell
1085cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell  // base::CreateDirectory() returns true if the directory already existed.
1095cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell  if (!base::CreateDirectory(path.DirName())) {
1105cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell    PLOG(WARNING) << "Failed to create directory for \"" << path.value() << '"';
1115cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell    return false;
1125cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell  }
1135cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell
1145cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell  // Create the file as owner-only initially.
1155cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell  base::ScopedFD scoped_fd(
1165cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell      HANDLE_EINTR(openat(AT_FDCWD,
1175cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell                          path.value().c_str(),
1185cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell                          O_RDONLY | O_NOFOLLOW | O_CREAT | O_EXCL | O_CLOEXEC,
1195cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell                          kPermissions600)));
1205cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell  if (scoped_fd == -1) {
1215cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell    PLOG(WARNING) << "Failed to create file \"" << path.value() << '"';
1225cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell    return false;
1235cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell  }
1245cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell
1255cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell  if (fd_out) {
1265cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell    fd_out->swap(scoped_fd);
1275cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell  }
1285cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell  return true;
1295cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell}
1305cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell
1315cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell}  // namespace
1325cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell
1335cadeae88cd2ab9b4aebf6d880455df602001c90David Pursellbool TouchFile(const base::FilePath& path,
1345cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell               int new_file_permissions,
1355cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell               uid_t uid,
1365cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell               gid_t gid) {
1375cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell  // Make sure |permissions| doesn't have any out-of-range bits.
1385cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell  if (new_file_permissions & ~kPermissions777) {
1395cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell    LOG(WARNING) << "Illegal permissions: " << new_file_permissions;
1405cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell    return false;
1415cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell  }
1425cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell
1435cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell  base::ScopedFD scoped_fd;
1445cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell  if (!TouchFileInternal(path, uid, gid, &scoped_fd)) {
1455cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell    return false;
1465cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell  }
1475cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell
1485cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell  // scoped_fd is valid only if a new file was created.
1495cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell  if (scoped_fd != -1 &&
1505cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell      HANDLE_EINTR(fchmod(scoped_fd.get(), new_file_permissions)) == -1) {
1515cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell    PLOG(WARNING) << "Failed to set permissions for \"" << path.value() << '"';
1525cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell    base::DeleteFile(path, false);
1535cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell    return false;
1545cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell  }
1555cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell
1565cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell  return true;
1575cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell}
1585cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell
1595cadeae88cd2ab9b4aebf6d880455df602001c90David Pursellbool TouchFile(const base::FilePath& path) {
1605cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell  // Use TouchFile() instead of TouchFileInternal() to explicitly set
1615cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell  // permissions to 600 in case umask is set strangely.
162bf74b6a523872c7aa73214e911201c28b13a786bDavid Pursell  return TouchFile(path, kPermissions600, geteuid(), getegid());
1635cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell}
1645cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell
1659ed0cab99f18acb3570a35e9408f24355f6b8324Alex Vakulenko}  // namespace brillo
166