file_utils.cc revision bf74b6a523872c7aa73214e911201c28b13a786b
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 55cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell#include "libchromeos/chromeos/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 165cadeae88cd2ab9b4aebf6d880455df602001c90David Pursellnamespace chromeos { 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 && 705cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell S_ISREG(file_stat.st_mode) && 715cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell file_stat.st_uid == uid && 725cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell file_stat.st_gid == gid) { 735cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell return kRegularFile; 745cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell } 755cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell } 765cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell 775cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell // If we get here and anything was at |path|, try to delete it so we can put 785cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell // our file there. 795cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell if (path_not_empty) { 805cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell if (!base::DeleteFile(path, true)) { 815cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell PLOG(WARNING) << "Failed to delete entity at \"" << path.value() << '"'; 825cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell return kFailure; 835cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell } 845cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell } 855cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell 865cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell return kEmpty; 875cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell} 885cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell 895cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell// Handles common touch functionality but also provides an optional |fd_out| 905cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell// so that any further modifications to the file (e.g. permissions) can safely 915cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell// use the fd rather than the path. |fd_out| will only be set if a new file 925cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell// is created, otherwise it will be unchanged. 935cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell// If |fd_out| is null, this function will close the file, otherwise it's 945cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell// expected that |fd_out| will close the file when it goes out of scope. 955cadeae88cd2ab9b4aebf6d880455df602001c90David Pursellbool TouchFileInternal(const base::FilePath& path, 965cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell uid_t uid, 975cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell gid_t gid, 985cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell base::ScopedFD* fd_out) { 995cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell RegularFileOrDeleteResult result = RegularFileOrDelete(path, uid, gid); 1005cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell switch (result) { 1015cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell case kFailure: 1025cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell return false; 1035cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell case kRegularFile: 1045cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell return true; 1055cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell case kEmpty: 1065cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell break; 1075cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell } 1085cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell 1095cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell // base::CreateDirectory() returns true if the directory already existed. 1105cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell if (!base::CreateDirectory(path.DirName())) { 1115cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell PLOG(WARNING) << "Failed to create directory for \"" << path.value() << '"'; 1125cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell return false; 1135cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell } 1145cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell 1155cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell // Create the file as owner-only initially. 1165cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell base::ScopedFD scoped_fd( 1175cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell HANDLE_EINTR(openat(AT_FDCWD, 1185cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell path.value().c_str(), 1195cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell O_RDONLY | O_NOFOLLOW | O_CREAT | O_EXCL | O_CLOEXEC, 1205cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell kPermissions600))); 1215cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell if (scoped_fd == -1) { 1225cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell PLOG(WARNING) << "Failed to create file \"" << path.value() << '"'; 1235cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell return false; 1245cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell } 1255cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell 1265cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell if (fd_out) { 1275cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell fd_out->swap(scoped_fd); 1285cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell } 1295cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell return true; 1305cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell} 1315cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell 1325cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell} // namespace 1335cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell 1345cadeae88cd2ab9b4aebf6d880455df602001c90David Pursellbool TouchFile(const base::FilePath& path, 1355cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell int new_file_permissions, 1365cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell uid_t uid, 1375cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell gid_t gid) { 1385cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell // Make sure |permissions| doesn't have any out-of-range bits. 1395cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell if (new_file_permissions & ~kPermissions777) { 1405cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell LOG(WARNING) << "Illegal permissions: " << new_file_permissions; 1415cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell return false; 1425cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell } 1435cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell 1445cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell base::ScopedFD scoped_fd; 1455cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell if (!TouchFileInternal(path, uid, gid, &scoped_fd)) { 1465cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell return false; 1475cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell } 1485cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell 1495cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell // scoped_fd is valid only if a new file was created. 1505cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell if (scoped_fd != -1 && 1515cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell HANDLE_EINTR(fchmod(scoped_fd.get(), new_file_permissions)) == -1) { 1525cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell PLOG(WARNING) << "Failed to set permissions for \"" << path.value() << '"'; 1535cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell base::DeleteFile(path, false); 1545cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell return false; 1555cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell } 1565cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell 1575cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell return true; 1585cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell} 1595cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell 1605cadeae88cd2ab9b4aebf6d880455df602001c90David Pursellbool TouchFile(const base::FilePath& path) { 1615cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell // Use TouchFile() instead of TouchFileInternal() to explicitly set 1625cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell // permissions to 600 in case umask is set strangely. 163bf74b6a523872c7aa73214e911201c28b13a786bDavid Pursell return TouchFile(path, kPermissions600, geteuid(), getegid()); 1645cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell} 1655cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell 1665cadeae88cd2ab9b4aebf6d880455df602001c90David Pursell} // namespace chromeos 167