blob: fd54a232d56cbb2595c2625900e936b96f1643cd [file] [log] [blame]
Keun young Parkb7342262021-10-25 08:09:27 -07001/*
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
29namespace {
30
31constexpr char kTmpFileSuffix[] = ".tmp";
32constexpr char kBackupFileSuffix[] = ".backup";
33
34std::string GetTmpFilePath(const std::string& path) {
35 return android::base::StringPrintf("%s%s", path.c_str(), kTmpFileSuffix);
36}
37
38std::string GetBackupFilePath(const std::string& path) {
39 return android::base::StringPrintf("%s%s", path.c_str(), kBackupFileSuffix);
40}
41
42void 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
51bool FileExists(const std::string& path) {
52 struct stat st;
53 return ::stat(path.c_str(), &st) == 0;
54}
55
56} // namespace
57
58namespace android {
59namespace installd {
60
61RestorableFile::RestorableFile() : RestorableFile(-1, "") {}
62
63RestorableFile::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
69RestorableFile::~RestorableFile() {
70 reset();
71}
72
73void 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
82bool 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
95bool 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
112bool 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
125void RestorableFile::RemoveBackupFile() {
126 UnlinkPossiblyNonExistingFile(GetBackupFilePath(path()));
127}
128
129const UniqueFile& RestorableFile::GetUniqueFile() const {
130 return unique_file_;
131}
132
133void RestorableFile::ResetAndRemoveAllFiles() {
134 std::string path(unique_file_.path());
135 reset();
136 RemoveAllFiles(path);
137}
138
139RestorableFile 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
154void 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