blob: 03ce4ea7e904575c3017d264cb26b1e456adb363 [file] [log] [blame]
Dan Albertaac6b7c2015-03-16 10:08:46 -07001/*
2 * Copyright (C) 2015 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
Elliott Hughesb6351622015-12-04 22:00:26 -080017#include "android-base/file.h"
Dan Albertaac6b7c2015-03-16 10:08:46 -070018
19#include <errno.h>
20#include <fcntl.h>
21#include <sys/stat.h>
22#include <sys/types.h>
Elliott Hughesa634a9a2016-08-23 15:53:45 -070023#include <unistd.h>
Dan Albertaac6b7c2015-03-16 10:08:46 -070024
25#include <string>
Elliott Hughesa634a9a2016-08-23 15:53:45 -070026#include <vector>
Dan Albertaac6b7c2015-03-16 10:08:46 -070027
Elliott Hughesb6351622015-12-04 22:00:26 -080028#include "android-base/macros.h" // For TEMP_FAILURE_RETRY on Darwin.
Elliott Hughes7d9a4792016-07-28 15:15:28 -070029#include "android-base/logging.h"
Elliott Hughesb6351622015-12-04 22:00:26 -080030#include "android-base/utf8.h"
Dan Albert5f770222015-03-26 23:33:28 -070031#include "utils/Compat.h"
Dan Albertaac6b7c2015-03-16 10:08:46 -070032
Elliott Hughes48f0eb52016-08-31 15:07:18 -070033#if defined(__APPLE__)
34#import <Carbon/Carbon.h>
35#endif
36#if defined(_WIN32)
37#include <windows.h>
38#endif
39
Dan Albertaac6b7c2015-03-16 10:08:46 -070040namespace android {
41namespace base {
42
Elliott Hughes774d7f62015-11-11 18:02:29 +000043// Versions of standard library APIs that support UTF-8 strings.
44using namespace android::base::utf8;
45
Dan Albertaac6b7c2015-03-16 10:08:46 -070046bool ReadFdToString(int fd, std::string* content) {
47 content->clear();
48
49 char buf[BUFSIZ];
50 ssize_t n;
51 while ((n = TEMP_FAILURE_RETRY(read(fd, &buf[0], sizeof(buf)))) > 0) {
52 content->append(buf, n);
53 }
54 return (n == 0) ? true : false;
55}
56
57bool ReadFileToString(const std::string& path, std::string* content) {
58 content->clear();
59
Elliott Hughesd5f8ae62015-09-01 13:35:44 -070060 int fd = TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW | O_BINARY));
Dan Albertaac6b7c2015-03-16 10:08:46 -070061 if (fd == -1) {
62 return false;
63 }
64 bool result = ReadFdToString(fd, content);
Nick Kralevich82921302015-05-20 08:59:21 -070065 close(fd);
Dan Albertaac6b7c2015-03-16 10:08:46 -070066 return result;
67}
68
69bool WriteStringToFd(const std::string& content, int fd) {
70 const char* p = content.data();
71 size_t left = content.size();
72 while (left > 0) {
73 ssize_t n = TEMP_FAILURE_RETRY(write(fd, p, left));
74 if (n == -1) {
75 return false;
76 }
77 p += n;
78 left -= n;
79 }
80 return true;
81}
82
83static bool CleanUpAfterFailedWrite(const std::string& path) {
84 // Something went wrong. Let's not leave a corrupt file lying around.
85 int saved_errno = errno;
86 unlink(path.c_str());
87 errno = saved_errno;
88 return false;
89}
90
91#if !defined(_WIN32)
92bool WriteStringToFile(const std::string& content, const std::string& path,
93 mode_t mode, uid_t owner, gid_t group) {
Elliott Hughesd5f8ae62015-09-01 13:35:44 -070094 int flags = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW | O_BINARY;
95 int fd = TEMP_FAILURE_RETRY(open(path.c_str(), flags, mode));
Dan Albertaac6b7c2015-03-16 10:08:46 -070096 if (fd == -1) {
Elliott Hughes7d9a4792016-07-28 15:15:28 -070097 PLOG(ERROR) << "android::WriteStringToFile open failed";
Dan Albertaac6b7c2015-03-16 10:08:46 -070098 return false;
99 }
100
101 // We do an explicit fchmod here because we assume that the caller really
102 // meant what they said and doesn't want the umask-influenced mode.
103 if (fchmod(fd, mode) == -1) {
Elliott Hughes7d9a4792016-07-28 15:15:28 -0700104 PLOG(ERROR) << "android::WriteStringToFile fchmod failed";
Dan Albertaac6b7c2015-03-16 10:08:46 -0700105 return CleanUpAfterFailedWrite(path);
106 }
107 if (fchown(fd, owner, group) == -1) {
Elliott Hughes7d9a4792016-07-28 15:15:28 -0700108 PLOG(ERROR) << "android::WriteStringToFile fchown failed";
Dan Albertaac6b7c2015-03-16 10:08:46 -0700109 return CleanUpAfterFailedWrite(path);
110 }
111 if (!WriteStringToFd(content, fd)) {
Elliott Hughes7d9a4792016-07-28 15:15:28 -0700112 PLOG(ERROR) << "android::WriteStringToFile write failed";
Dan Albertaac6b7c2015-03-16 10:08:46 -0700113 return CleanUpAfterFailedWrite(path);
114 }
Nick Kralevich82921302015-05-20 08:59:21 -0700115 close(fd);
Dan Albertaac6b7c2015-03-16 10:08:46 -0700116 return true;
117}
118#endif
119
120bool WriteStringToFile(const std::string& content, const std::string& path) {
Elliott Hughesd5f8ae62015-09-01 13:35:44 -0700121 int flags = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW | O_BINARY;
122 int fd = TEMP_FAILURE_RETRY(open(path.c_str(), flags, DEFFILEMODE));
Dan Albertaac6b7c2015-03-16 10:08:46 -0700123 if (fd == -1) {
124 return false;
125 }
126
127 bool result = WriteStringToFd(content, fd);
Nick Kralevich82921302015-05-20 08:59:21 -0700128 close(fd);
Dan Albertaac6b7c2015-03-16 10:08:46 -0700129 return result || CleanUpAfterFailedWrite(path);
130}
131
Elliott Hughes20abc872015-04-24 21:57:16 -0700132bool ReadFully(int fd, void* data, size_t byte_count) {
133 uint8_t* p = reinterpret_cast<uint8_t*>(data);
134 size_t remaining = byte_count;
135 while (remaining > 0) {
136 ssize_t n = TEMP_FAILURE_RETRY(read(fd, p, remaining));
137 if (n <= 0) return false;
138 p += n;
139 remaining -= n;
140 }
141 return true;
142}
143
144bool WriteFully(int fd, const void* data, size_t byte_count) {
145 const uint8_t* p = reinterpret_cast<const uint8_t*>(data);
146 size_t remaining = byte_count;
147 while (remaining > 0) {
148 ssize_t n = TEMP_FAILURE_RETRY(write(fd, p, remaining));
149 if (n == -1) return false;
150 p += n;
151 remaining -= n;
152 }
153 return true;
154}
155
Yabin Cui8f6a5a02016-01-29 17:25:54 -0800156bool RemoveFileIfExists(const std::string& path, std::string* err) {
157 struct stat st;
158#if defined(_WIN32)
159 //TODO: Windows version can't handle symbol link correctly.
160 int result = stat(path.c_str(), &st);
161 bool file_type_removable = (result == 0 && S_ISREG(st.st_mode));
162#else
163 int result = lstat(path.c_str(), &st);
164 bool file_type_removable = (result == 0 && (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)));
165#endif
166 if (result == 0) {
167 if (!file_type_removable) {
168 if (err != nullptr) {
169 *err = "is not a regular or symbol link file";
170 }
171 return false;
172 }
173 if (unlink(path.c_str()) == -1) {
174 if (err != nullptr) {
175 *err = strerror(errno);
176 }
177 return false;
178 }
179 }
180 return true;
181}
182
Elliott Hughesa634a9a2016-08-23 15:53:45 -0700183#if !defined(_WIN32)
184bool Readlink(const std::string& path, std::string* result) {
185 result->clear();
186
187 // Most Linux file systems (ext2 and ext4, say) limit symbolic links to
188 // 4095 bytes. Since we'll copy out into the string anyway, it doesn't
189 // waste memory to just start there. We add 1 so that we can recognize
190 // whether it actually fit (rather than being truncated to 4095).
191 std::vector<char> buf(4095 + 1);
192 while (true) {
193 ssize_t size = readlink(path.c_str(), &buf[0], buf.size());
194 // Unrecoverable error?
195 if (size == -1) return false;
196 // It fit! (If size == buf.size(), it may have been truncated.)
197 if (static_cast<size_t>(size) < buf.size()) {
198 result->assign(&buf[0], size);
199 return true;
200 }
201 // Double our buffer and try again.
202 buf.resize(buf.size() * 2);
203 }
204}
205#endif
206
Elliott Hughes48f0eb52016-08-31 15:07:18 -0700207std::string GetExecutablePath() {
208#if defined(__linux__)
209 std::string path;
210 android::base::Readlink("/proc/self/exe", &path);
211 return path;
212#elif defined(__APPLE__)
213 // TODO: use _NSGetExecutablePath instead (http://b/31240820)?
214 CFBundleRef mainBundle = CFBundleGetMainBundle();
215 CFURLRef executableURL = CFBundleCopyExecutableURL(mainBundle);
216 CFStringRef executablePathString = CFURLCopyFileSystemPath(executableURL, kCFURLPOSIXPathStyle);
217 CFRelease(executableURL);
218
219 char path[PATH_MAX + 1];
220 CFStringGetFileSystemRepresentation(executablePathString, path, sizeof(PATH_MAX)-1);
221 CFRelease(executablePathString);
222 return path;
223#elif defined(_WIN32)
224 char path[PATH_MAX + 1];
225 DWORD result = GetModuleFileName(NULL, path, sizeof(path) - 1);
226 if (result == 0 || result == sizeof(path) - 1) return "";
227 path[PATH_MAX - 1] = 0;
228 return path;
229#else
230#error unknown OS
231#endif
232}
233
Dan Albertaac6b7c2015-03-16 10:08:46 -0700234} // namespace base
235} // namespace android