• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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