OLD | NEW |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 #include "platform/globals.h" | 5 #include "platform/globals.h" |
6 #if defined(HOST_OS_ANDROID) | 6 #if defined(HOST_OS_ANDROID) |
7 | 7 |
8 #include "bin/directory.h" | 8 #include "bin/directory.h" |
9 | 9 |
10 #include <dirent.h> // NOLINT | 10 #include <dirent.h> // NOLINT |
11 #include <errno.h> // NOLINT | 11 #include <errno.h> // NOLINT |
| 12 #include <fcntl.h> // NOLINT |
| 13 #include <stdlib.h> // NOLINT |
12 #include <string.h> // NOLINT | 14 #include <string.h> // NOLINT |
13 #include <sys/param.h> // NOLINT | 15 #include <sys/param.h> // NOLINT |
14 #include <sys/stat.h> // NOLINT | 16 #include <sys/stat.h> // NOLINT |
15 #include <unistd.h> // NOLINT | 17 #include <unistd.h> // NOLINT |
16 | 18 |
| 19 #include "bin/crypto.h" |
17 #include "bin/dartutils.h" | 20 #include "bin/dartutils.h" |
| 21 #include "bin/fdutils.h" |
18 #include "bin/file.h" | 22 #include "bin/file.h" |
| 23 #include "bin/namespace.h" |
19 #include "bin/platform.h" | 24 #include "bin/platform.h" |
20 #include "platform/signal_blocker.h" | 25 #include "platform/signal_blocker.h" |
21 | 26 |
22 namespace dart { | 27 namespace dart { |
23 namespace bin { | 28 namespace bin { |
24 | 29 |
25 PathBuffer::PathBuffer() : length_(0) { | 30 PathBuffer::PathBuffer() : length_(0) { |
26 data_ = calloc(PATH_MAX + 1, sizeof(char)); // NOLINT | 31 data_ = calloc(PATH_MAX + 1, sizeof(char)); // NOLINT |
27 } | 32 } |
28 | 33 |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
73 uint64_t dev; | 78 uint64_t dev; |
74 uint64_t ino; | 79 uint64_t ino; |
75 LinkList* next; | 80 LinkList* next; |
76 }; | 81 }; |
77 | 82 |
78 ListType DirectoryListingEntry::Next(DirectoryListing* listing) { | 83 ListType DirectoryListingEntry::Next(DirectoryListing* listing) { |
79 if (done_) { | 84 if (done_) { |
80 return kListDone; | 85 return kListDone; |
81 } | 86 } |
82 | 87 |
| 88 if (fd_ == -1) { |
| 89 ASSERT(lister_ == 0); |
| 90 NamespaceScope ns(listing->namespc(), listing->path_buffer().AsString()); |
| 91 const int listingfd = TEMP_FAILURE_RETRY(openat( |
| 92 ns.fd(), ns.path(), O_DIRECTORY)); |
| 93 if (listingfd < 0) { |
| 94 done_ = true; |
| 95 return kListError; |
| 96 } |
| 97 fd_ = listingfd; |
| 98 } |
| 99 |
83 if (lister_ == 0) { | 100 if (lister_ == 0) { |
84 do { | 101 do { |
85 lister_ = reinterpret_cast<intptr_t>( | 102 lister_ = reinterpret_cast<intptr_t>(fdopendir(fd_)); |
86 opendir(listing->path_buffer().AsString())); | |
87 } while ((lister_ == 0) && (errno == EINTR)); | 103 } while ((lister_ == 0) && (errno == EINTR)); |
88 | |
89 if (lister_ == 0) { | 104 if (lister_ == 0) { |
90 done_ = true; | 105 done_ = true; |
91 return kListError; | 106 return kListError; |
92 } | 107 } |
93 if (parent_ != NULL) { | 108 if (parent_ != NULL) { |
94 if (!listing->path_buffer().Add(File::PathSeparator())) { | 109 if (!listing->path_buffer().Add(File::PathSeparator())) { |
95 return kListError; | 110 return kListError; |
96 } | 111 } |
97 } | 112 } |
98 path_length_ = listing->path_buffer().length(); | 113 path_length_ = listing->path_buffer().length(); |
99 } | 114 } |
100 // Reset. | 115 // Reset. |
101 listing->path_buffer().Reset(path_length_); | 116 listing->path_buffer().Reset(path_length_); |
102 ResetLink(); | 117 ResetLink(); |
103 | 118 |
104 // Iterate the directory and post the directories and files to the | 119 // Iterate the directory and post the directories and files to the |
105 // ports. | 120 // ports. |
106 int status = 0; | 121 errno = 0; |
107 dirent entry; | 122 dirent* entry = readdir(reinterpret_cast<DIR*>(lister_)); |
108 dirent* result; | 123 if (entry != NULL) { |
109 status = NO_RETRY_EXPECTED( | 124 if (!listing->path_buffer().Add(entry->d_name)) { |
110 readdir_r(reinterpret_cast<DIR*>(lister_), &entry, &result)); | |
111 if ((status == 0) && (result != NULL)) { | |
112 if (!listing->path_buffer().Add(entry.d_name)) { | |
113 done_ = true; | 125 done_ = true; |
114 return kListError; | 126 return kListError; |
115 } | 127 } |
116 switch (entry.d_type) { | 128 switch (entry->d_type) { |
117 case DT_DIR: | 129 case DT_DIR: |
118 if ((strcmp(entry.d_name, ".") == 0) || | 130 if ((strcmp(entry->d_name, ".") == 0) || |
119 (strcmp(entry.d_name, "..") == 0)) { | 131 (strcmp(entry->d_name, "..") == 0)) { |
120 return Next(listing); | 132 return Next(listing); |
121 } | 133 } |
122 return kListDirectory; | 134 return kListDirectory; |
123 case DT_BLK: | 135 case DT_BLK: |
124 case DT_CHR: | 136 case DT_CHR: |
125 case DT_FIFO: | 137 case DT_FIFO: |
126 case DT_SOCK: | 138 case DT_SOCK: |
127 case DT_REG: | 139 case DT_REG: |
128 return kListFile; | 140 return kListFile; |
129 case DT_LNK: | 141 case DT_LNK: |
130 if (!listing->follow_links()) { | 142 if (!listing->follow_links()) { |
131 return kListLink; | 143 return kListLink; |
132 } | 144 } |
133 // Else fall through to next case. | 145 // Else fall through to next case. |
134 // Fall through. | 146 // Fall through. |
135 case DT_UNKNOWN: { | 147 case DT_UNKNOWN: { |
136 // On some file systems the entry type is not determined by | 148 // On some file systems the entry type is not determined by |
137 // readdir_r. For those and for links we use stat to determine | 149 // readdir. For those and for links we use stat to determine |
138 // the actual entry type. Notice that stat returns the type of | 150 // the actual entry type. Notice that stat returns the type of |
139 // the file pointed to. | 151 // the file pointed to. |
| 152 NamespaceScope ns(listing->namespc(), |
| 153 listing->path_buffer().AsString()); |
140 struct stat entry_info; | 154 struct stat entry_info; |
141 int stat_success; | 155 int stat_success; |
142 stat_success = NO_RETRY_EXPECTED( | 156 stat_success = TEMP_FAILURE_RETRY(fstatat( |
143 lstat(listing->path_buffer().AsString(), &entry_info)); | 157 ns.fd(), ns.path(), &entry_info, AT_SYMLINK_NOFOLLOW)); |
144 if (stat_success == -1) { | 158 if (stat_success == -1) { |
145 return kListError; | 159 return kListError; |
146 } | 160 } |
147 if (listing->follow_links() && S_ISLNK(entry_info.st_mode)) { | 161 if (listing->follow_links() && S_ISLNK(entry_info.st_mode)) { |
148 // Check to see if we are in a loop created by a symbolic link. | 162 // Check to see if we are in a loop created by a symbolic link. |
149 LinkList current_link = {entry_info.st_dev, entry_info.st_ino, link_}; | 163 LinkList current_link = {entry_info.st_dev, entry_info.st_ino, link_}; |
150 LinkList* previous = link_; | 164 LinkList* previous = link_; |
151 while (previous != NULL) { | 165 while (previous != NULL) { |
152 if ((previous->dev == current_link.dev) && | 166 if ((previous->dev == current_link.dev) && |
153 (previous->ino == current_link.ino)) { | 167 (previous->ino == current_link.ino)) { |
154 // Report the looping link as a link, rather than following it. | 168 // Report the looping link as a link, rather than following it. |
155 return kListLink; | 169 return kListLink; |
156 } | 170 } |
157 previous = previous->next; | 171 previous = previous->next; |
158 } | 172 } |
159 stat_success = NO_RETRY_EXPECTED( | 173 stat_success = TEMP_FAILURE_RETRY(fstatat( |
160 stat(listing->path_buffer().AsString(), &entry_info)); | 174 ns.fd(), ns.path(), &entry_info, 0)); |
161 if (stat_success == -1) { | 175 if (stat_success == -1) { |
162 // Report a broken link as a link, even if follow_links is true. | 176 // Report a broken link as a link, even if follow_links is true. |
163 return kListLink; | 177 return kListLink; |
164 } | 178 } |
165 if (S_ISDIR(entry_info.st_mode)) { | 179 if (S_ISDIR(entry_info.st_mode)) { |
166 // Recurse into the subdirectory with current_link added to the | 180 // Recurse into the subdirectory with current_link added to the |
167 // linked list of seen file system links. | 181 // linked list of seen file system links. |
168 link_ = new LinkList(current_link); | 182 link_ = new LinkList(current_link); |
169 if ((strcmp(entry.d_name, ".") == 0) || | 183 if ((strcmp(entry->d_name, ".") == 0) || |
170 (strcmp(entry.d_name, "..") == 0)) { | 184 (strcmp(entry->d_name, "..") == 0)) { |
171 return Next(listing); | 185 return Next(listing); |
172 } | 186 } |
173 return kListDirectory; | 187 return kListDirectory; |
174 } | 188 } |
175 } | 189 } |
176 if (S_ISDIR(entry_info.st_mode)) { | 190 if (S_ISDIR(entry_info.st_mode)) { |
177 if ((strcmp(entry.d_name, ".") == 0) || | 191 if ((strcmp(entry->d_name, ".") == 0) || |
178 (strcmp(entry.d_name, "..") == 0)) { | 192 (strcmp(entry->d_name, "..") == 0)) { |
179 return Next(listing); | 193 return Next(listing); |
180 } | 194 } |
181 return kListDirectory; | 195 return kListDirectory; |
182 } else if (S_ISREG(entry_info.st_mode) || S_ISCHR(entry_info.st_mode) || | 196 } else if (S_ISREG(entry_info.st_mode) || S_ISCHR(entry_info.st_mode) || |
183 S_ISBLK(entry_info.st_mode) || | 197 S_ISBLK(entry_info.st_mode) || |
184 S_ISFIFO(entry_info.st_mode) || | 198 S_ISFIFO(entry_info.st_mode) || |
185 S_ISSOCK(entry_info.st_mode)) { | 199 S_ISSOCK(entry_info.st_mode)) { |
186 return kListFile; | 200 return kListFile; |
187 } else if (S_ISLNK(entry_info.st_mode)) { | 201 } else if (S_ISLNK(entry_info.st_mode)) { |
188 return kListLink; | 202 return kListLink; |
189 } else { | 203 } else { |
190 FATAL1("Unexpected st_mode: %d\n", entry_info.st_mode); | 204 FATAL1("Unexpected st_mode: %d\n", entry_info.st_mode); |
191 return kListError; | 205 return kListError; |
192 } | 206 } |
193 } | 207 } |
194 | 208 |
195 default: | 209 default: |
196 // We should have covered all the bases. If not, let's get an error. | 210 // We should have covered all the bases. If not, let's get an error. |
197 FATAL1("Unexpected d_type: %d\n", entry.d_type); | 211 FATAL1("Unexpected d_type: %d\n", entry->d_type); |
198 return kListError; | 212 return kListError; |
199 } | 213 } |
200 } | 214 } |
201 done_ = true; | 215 done_ = true; |
202 | 216 |
203 if (status != 0) { | 217 if (errno != 0) { |
204 errno = status; | |
205 return kListError; | 218 return kListError; |
206 } | 219 } |
207 | 220 |
208 return kListDone; | 221 return kListDone; |
209 } | 222 } |
210 | 223 |
211 DirectoryListingEntry::~DirectoryListingEntry() { | 224 DirectoryListingEntry::~DirectoryListingEntry() { |
212 ResetLink(); | 225 ResetLink(); |
213 if (lister_ != 0) { | 226 if (lister_ != 0) { |
214 closedir(reinterpret_cast<DIR*>(lister_)); | 227 VOID_NO_RETRY_EXPECTED(closedir(reinterpret_cast<DIR*>(lister_))); |
| 228 } |
| 229 if (fd_ != -1) { |
| 230 FDUtils::SaveErrorAndClose(fd_); |
215 } | 231 } |
216 } | 232 } |
217 | 233 |
218 void DirectoryListingEntry::ResetLink() { | 234 void DirectoryListingEntry::ResetLink() { |
219 if ((link_ != NULL) && ((parent_ == NULL) || (parent_->link_ != link_))) { | 235 if ((link_ != NULL) && ((parent_ == NULL) || (parent_->link_ != link_))) { |
220 delete link_; | 236 delete link_; |
221 link_ = NULL; | 237 link_ = NULL; |
222 } | 238 } |
223 if (parent_ != NULL) { | 239 if (parent_ != NULL) { |
224 link_ = parent_->link_; | 240 link_ = parent_->link_; |
225 } | 241 } |
226 } | 242 } |
227 | 243 |
228 static bool DeleteRecursively(PathBuffer* path); | 244 static bool DeleteRecursively(int dirfd, PathBuffer* path); |
229 | 245 |
230 static bool DeleteFile(char* file_name, PathBuffer* path) { | 246 static bool DeleteFile(int dirfd, char* file_name, PathBuffer* path) { |
231 return path->Add(file_name) && (unlink(path->AsString()) == 0); | 247 return path->Add(file_name) && |
| 248 (NO_RETRY_EXPECTED(unlinkat(dirfd, path->AsString(), 0)) == 0); |
232 } | 249 } |
233 | 250 |
234 static bool DeleteDir(char* dir_name, PathBuffer* path) { | 251 static bool DeleteDir(int dirfd, char* dir_name, PathBuffer* path) { |
235 if ((strcmp(dir_name, ".") == 0) || (strcmp(dir_name, "..") == 0)) { | 252 if ((strcmp(dir_name, ".") == 0) || (strcmp(dir_name, "..") == 0)) { |
236 return true; | 253 return true; |
237 } | 254 } |
238 return path->Add(dir_name) && DeleteRecursively(path); | 255 return path->Add(dir_name) && DeleteRecursively(dirfd, path); |
239 } | 256 } |
240 | 257 |
241 static bool DeleteRecursively(PathBuffer* path) { | 258 static bool DeleteRecursively(int dirfd, PathBuffer* path) { |
242 // Do not recurse into links for deletion. Instead delete the link. | 259 // Do not recurse into links for deletion. Instead delete the link. |
243 // If it's a file, delete it. | 260 // If it's a file, delete it. |
244 struct stat st; | 261 struct stat st; |
245 if (NO_RETRY_EXPECTED(lstat(path->AsString(), &st)) == -1) { | 262 if (TEMP_FAILURE_RETRY(fstatat(dirfd, |
| 263 path->AsString(), |
| 264 &st, |
| 265 AT_SYMLINK_NOFOLLOW)) == -1) { |
246 return false; | 266 return false; |
247 } else if (!S_ISDIR(st.st_mode)) { | 267 } else if (!S_ISDIR(st.st_mode)) { |
248 return (unlink(path->AsString()) == 0); | 268 return (NO_RETRY_EXPECTED(unlinkat(dirfd, path->AsString(), 0)) == 0); |
249 } | 269 } |
250 | 270 |
251 if (!path->Add(File::PathSeparator())) { | 271 if (!path->Add(File::PathSeparator())) { |
252 return false; | 272 return false; |
253 } | 273 } |
254 | 274 |
255 // Not a link. Attempt to open as a directory and recurse into the | 275 // Not a link. Attempt to open as a directory and recurse into the |
256 // directory. | 276 // directory. |
| 277 const int fd = TEMP_FAILURE_RETRY(openat( |
| 278 dirfd, path->AsString(), O_DIRECTORY)); |
| 279 if (fd < 0) { |
| 280 return false; |
| 281 } |
257 DIR* dir_pointer; | 282 DIR* dir_pointer; |
258 do { | 283 do { |
259 dir_pointer = opendir(path->AsString()); | 284 dir_pointer = fdopendir(fd); |
260 } while ((dir_pointer == NULL) && (errno == EINTR)); | 285 } while ((dir_pointer == NULL) && (errno == EINTR)); |
261 if (dir_pointer == NULL) { | 286 if (dir_pointer == NULL) { |
| 287 FDUtils::SaveErrorAndClose(fd); |
262 return false; | 288 return false; |
263 } | 289 } |
264 | 290 |
265 // Iterate the directory and delete all files and directories. | 291 // Iterate the directory and delete all files and directories. |
266 int path_length = path->length(); | 292 int path_length = path->length(); |
267 dirent entry; | 293 while (true) { |
268 dirent* result; | 294 // In case `readdir()` returns `NULL` we distinguish between end-of-stream |
269 while (NO_RETRY_EXPECTED(readdir_r(dir_pointer, &entry, &result)) == 0) { | 295 // and error by looking if `errno` was updated. |
270 if (result == NULL) { | 296 errno = 0; |
| 297 // In glibc 2.24+, readdir_r is deprecated. |
| 298 // According to the man page for readdir: |
| 299 // "readdir(3) is not required to be thread-safe. However, in modern |
| 300 // implementations (including the glibc implementation), concurrent calls to |
| 301 // readdir(3) that specify different directory streams are thread-safe." |
| 302 dirent* entry = readdir(dir_pointer); |
| 303 if (entry == NULL) { |
| 304 // Failed to read next directory entry. |
| 305 if (errno != 0) { |
| 306 break; |
| 307 } |
271 // End of directory. | 308 // End of directory. |
272 return NO_RETRY_EXPECTED(closedir(dir_pointer)) == 0 && | 309 int status = NO_RETRY_EXPECTED(closedir(dir_pointer)); |
273 NO_RETRY_EXPECTED(remove(path->AsString())) == 0; | 310 FDUtils::SaveErrorAndClose(fd); |
| 311 if (status != 0) { |
| 312 return false; |
| 313 } |
| 314 status = |
| 315 NO_RETRY_EXPECTED(unlinkat(dirfd, path->AsString(), AT_REMOVEDIR)); |
| 316 return status == 0; |
274 } | 317 } |
275 bool ok = false; | 318 bool ok = false; |
276 switch (entry.d_type) { | 319 switch (entry->d_type) { |
277 case DT_DIR: | 320 case DT_DIR: |
278 ok = DeleteDir(entry.d_name, path); | 321 ok = DeleteDir(dirfd, entry->d_name, path); |
279 break; | 322 break; |
280 case DT_BLK: | 323 case DT_BLK: |
281 case DT_CHR: | 324 case DT_CHR: |
282 case DT_FIFO: | 325 case DT_FIFO: |
283 case DT_SOCK: | 326 case DT_SOCK: |
284 case DT_REG: | 327 case DT_REG: |
285 case DT_LNK: | 328 case DT_LNK: |
286 // Treat all links as files. This will delete the link which | 329 // Treat all links as files. This will delete the link which |
287 // is what we want no matter if the link target is a file or a | 330 // is what we want no matter if the link target is a file or a |
288 // directory. | 331 // directory. |
289 ok = DeleteFile(entry.d_name, path); | 332 ok = DeleteFile(dirfd, entry->d_name, path); |
290 break; | 333 break; |
291 case DT_UNKNOWN: { | 334 case DT_UNKNOWN: { |
292 if (!path->Add(entry.d_name)) { | 335 if (!path->Add(entry->d_name)) { |
293 break; | 336 break; |
294 } | 337 } |
295 // On some file systems the entry type is not determined by | 338 // On some file systems the entry type is not determined by |
296 // readdir_r. For those we use lstat to determine the entry | 339 // readdir. For those we use lstat to determine the entry |
297 // type. | 340 // type. |
298 struct stat entry_info; | 341 struct stat entry_info; |
299 if (NO_RETRY_EXPECTED(lstat(path->AsString(), &entry_info)) == -1) { | 342 if (TEMP_FAILURE_RETRY(fstatat(dirfd, |
| 343 path->AsString(), |
| 344 &entry_info, |
| 345 AT_SYMLINK_NOFOLLOW)) == -1) { |
300 break; | 346 break; |
301 } | 347 } |
302 path->Reset(path_length); | 348 path->Reset(path_length); |
303 if (S_ISDIR(entry_info.st_mode)) { | 349 if (S_ISDIR(entry_info.st_mode)) { |
304 ok = DeleteDir(entry.d_name, path); | 350 ok = DeleteDir(dirfd, entry->d_name, path); |
305 } else { | 351 } else { |
306 // Treat links as files. This will delete the link which is | 352 // Treat links as files. This will delete the link which is |
307 // what we want no matter if the link target is a file or a | 353 // what we want no matter if the link target is a file or a |
308 // directory. | 354 // directory. |
309 ok = DeleteFile(entry.d_name, path); | 355 ok = DeleteFile(dirfd, entry->d_name, path); |
310 } | 356 } |
311 break; | 357 break; |
312 } | 358 } |
313 default: | 359 default: |
314 // We should have covered all the bases. If not, let's get an error. | 360 // We should have covered all the bases. If not, let's get an error. |
315 FATAL1("Unexpected d_type: %d\n", entry.d_type); | 361 FATAL1("Unexpected d_type: %d\n", entry->d_type); |
316 break; | 362 break; |
317 } | 363 } |
318 if (!ok) { | 364 if (!ok) { |
319 break; | 365 break; |
320 } | 366 } |
321 path->Reset(path_length); | 367 path->Reset(path_length); |
322 } | 368 } |
323 // Only happens if an error. | 369 // Only happens if an error. |
324 ASSERT(errno != 0); | 370 ASSERT(errno != 0); |
325 int err = errno; | 371 int err = errno; |
326 VOID_NO_RETRY_EXPECTED(closedir(dir_pointer)); | 372 VOID_NO_RETRY_EXPECTED(closedir(dir_pointer)); |
| 373 FDUtils::SaveErrorAndClose(fd); |
327 errno = err; | 374 errno = err; |
328 return false; | 375 return false; |
329 } | 376 } |
330 | 377 |
331 Directory::ExistsResult Directory::Exists(const char* dir_name) { | 378 Directory::ExistsResult Directory::Exists(Namespace* namespc, |
| 379 const char* dir_name) { |
| 380 NamespaceScope ns(namespc, dir_name); |
332 struct stat entry_info; | 381 struct stat entry_info; |
333 int success = NO_RETRY_EXPECTED(stat(dir_name, &entry_info)); | 382 int success = TEMP_FAILURE_RETRY(fstatat(ns.fd(), ns.path(), &entry_info, 0)); |
334 if (success == 0) { | 383 if (success == 0) { |
335 if (S_ISDIR(entry_info.st_mode)) { | 384 if (S_ISDIR(entry_info.st_mode)) { |
336 return EXISTS; | 385 return EXISTS; |
337 } else { | 386 } else { |
338 // An OSError may be constructed based on the return value of this | 387 // An OSError may be constructed based on the return value of this |
339 // function, so set errno to something that makes sense. | 388 // function, so set errno to something that makes sense. |
340 errno = ENOTDIR; | 389 errno = ENOTDIR; |
341 return DOES_NOT_EXIST; | 390 return DOES_NOT_EXIST; |
342 } | 391 } |
343 } else { | 392 } else { |
(...skipping 14 matching lines...) Expand all Loading... |
358 // Android's getcwd adheres closely to the POSIX standard. It won't | 407 // Android's getcwd adheres closely to the POSIX standard. It won't |
359 // allocate memory. We need to make our own copy. | 408 // allocate memory. We need to make our own copy. |
360 char buffer[PATH_MAX]; | 409 char buffer[PATH_MAX]; |
361 if (getcwd(buffer, PATH_MAX) == NULL) { | 410 if (getcwd(buffer, PATH_MAX) == NULL) { |
362 return NULL; | 411 return NULL; |
363 } | 412 } |
364 | 413 |
365 return strdup(buffer); | 414 return strdup(buffer); |
366 } | 415 } |
367 | 416 |
368 const char* Directory::Current() { | 417 bool Directory::Create(Namespace* namespc, const char* dir_name) { |
369 char buffer[PATH_MAX]; | 418 NamespaceScope ns(namespc, dir_name); |
370 if (getcwd(buffer, PATH_MAX) == NULL) { | |
371 return NULL; | |
372 } | |
373 return DartUtils::ScopedCopyCString(buffer); | |
374 } | |
375 | |
376 bool Directory::SetCurrent(const char* path) { | |
377 int result = NO_RETRY_EXPECTED(chdir(path)); | |
378 return (result == 0); | |
379 } | |
380 | |
381 bool Directory::Create(const char* dir_name) { | |
382 // Create the directory with the permissions specified by the | 419 // Create the directory with the permissions specified by the |
383 // process umask. | 420 // process umask. |
384 int result = NO_RETRY_EXPECTED(mkdir(dir_name, 0777)); | 421 const int result = NO_RETRY_EXPECTED(mkdirat(ns.fd(), ns.path(), 0777)); |
385 // If the directory already exists, treat it as a success. | 422 // If the directory already exists, treat it as a success. |
386 if ((result == -1) && (errno == EEXIST)) { | 423 if ((result == -1) && (errno == EEXIST)) { |
387 return (Exists(dir_name) == EXISTS); | 424 return (Exists(namespc, dir_name) == EXISTS); |
388 } | 425 } |
389 return (result == 0); | 426 return (result == 0); |
390 } | 427 } |
391 | 428 |
392 const char* Directory::SystemTemp() { | 429 const char* Directory::SystemTemp(Namespace* namespc) { |
393 if (Directory::system_temp_path_override_ != NULL) { | 430 if (Directory::system_temp_path_override_ != NULL) { |
394 return DartUtils::ScopedCopyCString(Directory::system_temp_path_override_); | 431 return DartUtils::ScopedCopyCString(Directory::system_temp_path_override_); |
395 } | 432 } |
396 // Android does not have a /tmp directory. A partial substitute, | 433 // Android does not have a /tmp directory. A partial substitute, |
397 // suitable for bring-up work and tests, is to create a tmp | 434 // suitable for bring-up work and tests, is to create a tmp |
398 // directory in /data/local/tmp. | 435 // directory in /data/local/tmp. |
399 // | 436 // |
400 // TODO(4413): In the long run, when running in an application we should | 437 // TODO(4413): In the long run, when running in an application we should |
401 // probably use the appropriate directory from the Android API, | 438 // probably use the appropriate directory from the Android API, |
402 // probably what File.createTempFile uses. | 439 // probably what File.createTempFile uses. |
403 #define ANDROID_TEMP_DIR "/data/local/tmp" | 440 const char* kAndroidTempDir = "/data/local/tmp"; |
404 struct stat st; | 441 struct stat st; |
405 if (stat(ANDROID_TEMP_DIR, &st) != 0) { | 442 if (stat(kAndroidTempDir, &st) != 0) { |
406 mkdir(ANDROID_TEMP_DIR, 0777); | 443 mkdir(kAndroidTempDir, 0777); |
407 } | 444 } |
408 return ANDROID_TEMP_DIR; | 445 return kAndroidTempDir; |
409 } | 446 } |
410 | 447 |
411 const char* Directory::CreateTemp(const char* prefix) { | 448 // Returns a new, unused directory name, adding characters to the end |
412 // Returns a new, unused directory name, adding characters to the end | 449 // of prefix. Creates the directory with the permissions specified |
413 // of prefix. Creates the directory with the permissions specified | 450 // by the process umask. |
414 // by the process umask. | 451 // The return value is Dart_ScopeAllocated. |
415 // The return value is Dart_ScopeAllocated. | 452 const char* Directory::CreateTemp(Namespace* namespc, const char* prefix) { |
416 PathBuffer path; | 453 PathBuffer path; |
| 454 const int firstchar = 'A'; |
| 455 const int numchars = 'Z' - 'A' + 1; |
| 456 uint8_t random_bytes[7]; |
| 457 |
| 458 // mkdtemp doesn't have an "at" variant, so we have to simulate it. |
417 if (!path.Add(prefix)) { | 459 if (!path.Add(prefix)) { |
418 return NULL; | 460 return NULL; |
419 } | 461 } |
420 if (!path.Add("XXXXXX")) { | 462 intptr_t prefix_length = path.length(); |
421 // Pattern has overflowed. | 463 while (true) { |
422 return NULL; | 464 Crypto::GetRandomBytes(6, random_bytes); |
423 } | 465 for (intptr_t i = 0; i < 6; i++) { |
424 char* result; | 466 random_bytes[i] = (random_bytes[i] % numchars) + firstchar; |
425 do { | |
426 result = mkdtemp(path.AsString()); | |
427 } while ((result == NULL) && (errno == EINTR)); | |
428 if (result == NULL) { | |
429 return NULL; | |
430 } | |
431 return path.AsScopedString(); | |
432 } | |
433 | |
434 bool Directory::Delete(const char* dir_name, bool recursive) { | |
435 if (!recursive) { | |
436 if ((File::GetType(dir_name, false) == File::kIsLink) && | |
437 (File::GetType(dir_name, true) == File::kIsDirectory)) { | |
438 return (NO_RETRY_EXPECTED(unlink(dir_name)) == 0); | |
439 } | 467 } |
440 return (NO_RETRY_EXPECTED(rmdir(dir_name)) == 0); | 468 random_bytes[6] = '\0'; |
441 } else { | 469 if (!path.Add(reinterpret_cast<char*>(random_bytes))) { |
442 PathBuffer path; | 470 return NULL; |
443 if (!path.Add(dir_name)) { | |
444 return false; | |
445 } | 471 } |
446 return DeleteRecursively(&path); | 472 NamespaceScope ns(namespc, path.AsString()); |
| 473 const int result = NO_RETRY_EXPECTED(mkdirat(ns.fd(), ns.path(), 0777)); |
| 474 if (result == 0) { |
| 475 return path.AsScopedString(); |
| 476 } else if (errno == EEXIST) { |
| 477 path.Reset(prefix_length); |
| 478 } else { |
| 479 return NULL; |
| 480 } |
447 } | 481 } |
448 } | 482 } |
449 | 483 |
450 bool Directory::Rename(const char* path, const char* new_path) { | 484 bool Directory::Delete( |
451 ExistsResult exists = Exists(path); | 485 Namespace* namespc, const char* dir_name, bool recursive) { |
| 486 NamespaceScope ns(namespc, dir_name); |
| 487 if (!recursive) { |
| 488 if ((File::GetType(namespc, dir_name, false) == File::kIsLink) && |
| 489 (File::GetType(namespc, dir_name, true) == File::kIsDirectory)) { |
| 490 return NO_RETRY_EXPECTED(unlinkat(ns.fd(), ns.path(), 0)) == 0; |
| 491 } |
| 492 return NO_RETRY_EXPECTED(unlinkat(ns.fd(), ns.path(), AT_REMOVEDIR)) == 0; |
| 493 } else { |
| 494 PathBuffer path; |
| 495 if (!path.Add(ns.path())) { |
| 496 return false; |
| 497 } |
| 498 return DeleteRecursively(ns.fd(), &path); |
| 499 } |
| 500 } |
| 501 |
| 502 bool Directory::Rename( |
| 503 Namespace* namespc, const char* old_path, const char* new_path) { |
| 504 ExistsResult exists = Exists(namespc, old_path); |
452 if (exists != EXISTS) { | 505 if (exists != EXISTS) { |
453 return false; | 506 return false; |
454 } | 507 } |
455 return (NO_RETRY_EXPECTED(rename(path, new_path)) == 0); | 508 NamespaceScope oldns(namespc, old_path); |
| 509 NamespaceScope newns(namespc, new_path); |
| 510 return (NO_RETRY_EXPECTED(renameat( |
| 511 oldns.fd(), oldns.path(), newns.fd(), newns.path())) == 0); |
456 } | 512 } |
457 | 513 |
458 } // namespace bin | 514 } // namespace bin |
459 } // namespace dart | 515 } // namespace dart |
460 | 516 |
461 #endif // defined(HOST_OS_ANDROID) | 517 #endif // defined(HOST_OS_ANDROID) |
OLD | NEW |