1 /*
2 * Copyright (C) 2021 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "restorable_file.h"
18
19 #include <string>
20
21 #include <fcntl.h>
22 #include <sys/stat.h>
23 #include <sys/types.h>
24 #include <unistd.h>
25
26 #include <android-base/logging.h>
27 #include <android-base/stringprintf.h>
28
29 namespace {
30
31 constexpr char kTmpFileSuffix[] = ".tmp";
32 constexpr char kBackupFileSuffix[] = ".backup";
33
GetTmpFilePath(const std::string & path)34 std::string GetTmpFilePath(const std::string& path) {
35 return android::base::StringPrintf("%s%s", path.c_str(), kTmpFileSuffix);
36 }
37
GetBackupFilePath(const std::string & path)38 std::string GetBackupFilePath(const std::string& path) {
39 return android::base::StringPrintf("%s%s", path.c_str(), kBackupFileSuffix);
40 }
41
UnlinkPossiblyNonExistingFile(const std::string & path)42 void UnlinkPossiblyNonExistingFile(const std::string& path) {
43 if (unlink(path.c_str()) < 0) {
44 if (errno != ENOENT && errno != EROFS) { // EROFS reported even if it does not exist.
45 PLOG(ERROR) << "Cannot unlink: " << path;
46 }
47 }
48 }
49
50 // Check if file for the given path exists
FileExists(const std::string & path)51 bool FileExists(const std::string& path) {
52 struct stat st;
53 return ::stat(path.c_str(), &st) == 0;
54 }
55
56 } // namespace
57
58 namespace android {
59 namespace installd {
60
RestorableFile()61 RestorableFile::RestorableFile() : RestorableFile(-1, "") {}
62
RestorableFile(int value,const std::string & path)63 RestorableFile::RestorableFile(int value, const std::string& path) : unique_file_(value, path) {
64 // As cleanup is null, this does not make much difference but use unique_file_ only for closing
65 // tmp file.
66 unique_file_.DisableCleanup();
67 }
68
~RestorableFile()69 RestorableFile::~RestorableFile() {
70 reset();
71 }
72
reset()73 void RestorableFile::reset() {
74 // need to copy before reset clears it.
75 std::string path(unique_file_.path());
76 unique_file_.reset();
77 if (!path.empty()) {
78 UnlinkPossiblyNonExistingFile(GetTmpFilePath(path));
79 }
80 }
81
CreateBackupFile()82 bool RestorableFile::CreateBackupFile() {
83 if (path().empty() || !FileExists(path())) {
84 return true;
85 }
86 std::string backup = GetBackupFilePath(path());
87 UnlinkPossiblyNonExistingFile(backup);
88 if (rename(path().c_str(), backup.c_str()) < 0) {
89 PLOG(ERROR) << "Cannot rename " << path() << " to " << backup;
90 return false;
91 }
92 return true;
93 }
94
CommitWorkFile()95 bool RestorableFile::CommitWorkFile() {
96 std::string path(unique_file_.path());
97 // Keep the path with Commit for debugging purpose.
98 unique_file_.reset(-1, path);
99 if (!path.empty()) {
100 if (rename(GetTmpFilePath(path).c_str(), path.c_str()) < 0) {
101 PLOG(ERROR) << "Cannot rename " << GetTmpFilePath(path) << " to " << path;
102 // Remove both files as renaming can fail due to the original file as well.
103 UnlinkPossiblyNonExistingFile(path);
104 UnlinkPossiblyNonExistingFile(GetTmpFilePath(path));
105 return false;
106 }
107 }
108
109 return true;
110 }
111
RestoreBackupFile()112 bool RestorableFile::RestoreBackupFile() {
113 std::string backup = GetBackupFilePath(path());
114 if (path().empty() || !FileExists(backup)) {
115 return true;
116 }
117 UnlinkPossiblyNonExistingFile(path());
118 if (rename(backup.c_str(), path().c_str()) < 0) {
119 PLOG(ERROR) << "Cannot rename " << backup << " to " << path();
120 return false;
121 }
122 return true;
123 }
124
RemoveBackupFile()125 void RestorableFile::RemoveBackupFile() {
126 UnlinkPossiblyNonExistingFile(GetBackupFilePath(path()));
127 }
128
GetUniqueFile() const129 const UniqueFile& RestorableFile::GetUniqueFile() const {
130 return unique_file_;
131 }
132
ResetAndRemoveAllFiles()133 void RestorableFile::ResetAndRemoveAllFiles() {
134 std::string path(unique_file_.path());
135 reset();
136 RemoveAllFiles(path);
137 }
138
CreateWritableFile(const std::string & path,int permissions)139 RestorableFile RestorableFile::CreateWritableFile(const std::string& path, int permissions) {
140 std::string tmp_file_path = GetTmpFilePath(path);
141 // If old tmp file exists, delete it.
142 UnlinkPossiblyNonExistingFile(tmp_file_path);
143 int fd = -1;
144 if (!path.empty()) {
145 fd = open(tmp_file_path.c_str(), O_RDWR | O_CREAT, permissions);
146 if (fd < 0) {
147 PLOG(ERROR) << "Cannot create file: " << tmp_file_path;
148 }
149 }
150 RestorableFile rf(fd, path);
151 return rf;
152 }
153
RemoveAllFiles(const std::string & path)154 void RestorableFile::RemoveAllFiles(const std::string& path) {
155 UnlinkPossiblyNonExistingFile(GetTmpFilePath(path));
156 UnlinkPossiblyNonExistingFile(GetBackupFilePath(path));
157 UnlinkPossiblyNonExistingFile(path);
158 }
159
160 } // namespace installd
161 } // namespace android
162