1 /*
2 * Copyright (C) 2010 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 <android-base/logging.h>
18 #include <fcntl.h>
19 #include <sys/stat.h>
20 #include <sys/types.h>
21
22 #include <cstddef>
23 #include <cstring>
24 #include <memory>
25
26 #include "base/zip_archive.h"
27 #include "os.h"
28 #include "unix_file/fd_file.h"
29
30 namespace art {
31
Invalid()32 FileWithRange FileWithRange::Invalid() { return {.file = nullptr, .start = 0, .length = 0}; }
33
OpenFileForReading(const char * name)34 File* OS::OpenFileForReading(const char* name) {
35 return OpenFileWithFlags(name, O_RDONLY);
36 }
37
OpenFileReadWrite(const char * name)38 File* OS::OpenFileReadWrite(const char* name) {
39 return OpenFileWithFlags(name, O_RDWR);
40 }
41
CreateEmptyFile(const char * name,int extra_flags)42 static File* CreateEmptyFile(const char* name, int extra_flags) {
43 // In case the file exists, unlink it so we get a new file. This is necessary as the previous
44 // file may be in use and must not be changed.
45 unlink(name);
46
47 return OS::OpenFileWithFlags(name, O_CREAT | extra_flags);
48 }
49
CreateEmptyFile(const char * name)50 File* OS::CreateEmptyFile(const char* name) {
51 return art::CreateEmptyFile(name, O_RDWR | O_TRUNC);
52 }
53
CreateEmptyFileWriteOnly(const char * name)54 File* OS::CreateEmptyFileWriteOnly(const char* name) {
55 #ifdef _WIN32
56 int flags = O_WRONLY | O_TRUNC;
57 #else
58 int flags = O_WRONLY | O_TRUNC | O_NOFOLLOW | O_CLOEXEC;
59 #endif
60 return art::CreateEmptyFile(name, flags);
61 }
62
OpenFileWithFlags(const char * name,int flags,bool auto_flush)63 File* OS::OpenFileWithFlags(const char* name, int flags, bool auto_flush) {
64 CHECK(name != nullptr);
65 bool read_only = ((flags & O_ACCMODE) == O_RDONLY);
66 bool check_usage = !read_only && auto_flush;
67 std::unique_ptr<File> file(
68 new File(name, flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH, check_usage));
69 if (!file->IsOpened()) {
70 return nullptr;
71 }
72 return file.release();
73 }
74
FileExists(const char * name,bool check_file_type)75 bool OS::FileExists(const char* name, bool check_file_type) {
76 struct stat st;
77 if (stat(name, &st) == 0) {
78 if (check_file_type) {
79 return S_ISREG(st.st_mode); // TODO: Deal with symlinks?
80 } else {
81 return true;
82 }
83 } else {
84 return false;
85 }
86 }
87
DirectoryExists(const char * name)88 bool OS::DirectoryExists(const char* name) {
89 struct stat st;
90 if (stat(name, &st) == 0) {
91 return S_ISDIR(st.st_mode); // TODO: Deal with symlinks?
92 } else {
93 return false;
94 }
95 }
96
GetFileSizeBytes(const char * name)97 int64_t OS::GetFileSizeBytes(const char* name) {
98 struct stat st;
99 if (stat(name, &st) == 0) {
100 return st.st_size; // TODO: Deal with symlinks? According to the documentation,
101 // the st_size for a symlink is "the length of the pathname
102 // it contains, without a terminating null byte."
103 } else {
104 return -1;
105 }
106 }
107
OpenFileDirectlyOrFromZip(const std::string & name_and_zip_entry,const char * zip_separator,size_t alignment,std::string * error_msg)108 FileWithRange OS::OpenFileDirectlyOrFromZip(const std::string& name_and_zip_entry,
109 const char* zip_separator,
110 size_t alignment,
111 std::string* error_msg) {
112 std::string filename = name_and_zip_entry;
113 std::string zip_entry_name;
114 size_t pos = filename.find(zip_separator);
115 if (pos != std::string::npos) {
116 zip_entry_name = filename.substr(pos + strlen(zip_separator));
117 filename.resize(pos);
118 if (filename.empty() || zip_entry_name.empty()) {
119 *error_msg = ART_FORMAT("Malformed zip path '{}'", name_and_zip_entry);
120 return FileWithRange::Invalid();
121 }
122 }
123
124 std::unique_ptr<File> file(OS::OpenFileForReading(filename.c_str()));
125 if (file == nullptr) {
126 *error_msg = ART_FORMAT("Failed to open '{}' for reading: {}", filename, strerror(errno));
127 return FileWithRange::Invalid();
128 }
129
130 off_t start = 0;
131 int64_t total_file_length = file->GetLength();
132 if (total_file_length < 0) {
133 *error_msg = ART_FORMAT("Failed to get file length of '{}': {}", filename, strerror(errno));
134 return FileWithRange::Invalid();
135 }
136 size_t length = total_file_length;
137
138 if (!zip_entry_name.empty()) {
139 std::unique_ptr<ZipArchive> zip_archive(
140 ZipArchive::OpenFromOwnedFd(file->Fd(), filename.c_str(), error_msg));
141 if (zip_archive == nullptr) {
142 *error_msg = ART_FORMAT("Failed to open '{}' as zip", filename);
143 return FileWithRange::Invalid();
144 }
145 std::unique_ptr<ZipEntry> zip_entry(zip_archive->Find(zip_entry_name.c_str(), error_msg));
146 if (zip_entry == nullptr) {
147 *error_msg = ART_FORMAT("Failed to find entry '{}' in zip '{}'", zip_entry_name, filename);
148 return FileWithRange::Invalid();
149 }
150 if (!zip_entry->IsUncompressed() || !zip_entry->IsAlignedTo(alignment)) {
151 *error_msg =
152 ART_FORMAT("The entry '{}' in zip '{}' must be uncompressed and aligned to {} bytes",
153 zip_entry_name,
154 filename,
155 alignment);
156 return FileWithRange::Invalid();
157 }
158 start = zip_entry->GetOffset();
159 length = zip_entry->GetUncompressedLength();
160 if (start + length > static_cast<size_t>(total_file_length)) {
161 *error_msg = ART_FORMAT(
162 "Invalid zip entry offset or length (offset: {}, length: {}, total_file_length: {})",
163 start,
164 length,
165 total_file_length);
166 return FileWithRange::Invalid();
167 }
168 }
169
170 return {.file = std::move(file), .start = start, .length = length};
171 }
172
173 } // namespace art
174