1 /* 2 * Copyright 2002-2009, Haiku Inc. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Tyler Dauwalder 7 * Ingo Weinhold, bonefish@users.sf.net 8 * Axel Dörfler, axeld@pinc-software.de 9 */ 10 11 12 #include "storage_support.h" 13 14 #include <fcntl.h> 15 #include <string.h> 16 17 #include <compat/sys/stat.h> 18 19 #include <Directory.h> 20 #include <Entry.h> 21 #include <File.h> 22 #include <fs_info.h> 23 #include <Path.h> 24 #include <SymLink.h> 25 26 #include <syscalls.h> 27 #include <umask.h> 28 29 30 BDirectory::BDirectory() 31 : 32 fDirFd(-1) 33 { 34 } 35 36 37 BDirectory::BDirectory(const BDirectory& dir) 38 : 39 fDirFd(-1) 40 { 41 *this = dir; 42 } 43 44 45 BDirectory::BDirectory(const entry_ref* ref) 46 : 47 fDirFd(-1) 48 { 49 SetTo(ref); 50 } 51 52 53 BDirectory::BDirectory(const node_ref* nref) 54 : 55 fDirFd(-1) 56 { 57 SetTo(nref); 58 } 59 60 61 BDirectory::BDirectory(const BEntry* entry) 62 : 63 fDirFd(-1) 64 { 65 SetTo(entry); 66 } 67 68 69 BDirectory::BDirectory(const char* path) 70 : 71 fDirFd(-1) 72 { 73 SetTo(path); 74 } 75 76 77 BDirectory::BDirectory(const BDirectory* dir, const char* path) 78 : 79 fDirFd(-1) 80 { 81 SetTo(dir, path); 82 } 83 84 85 BDirectory::~BDirectory() 86 { 87 // Also called by the BNode destructor, but we rather try to avoid 88 // problems with calling virtual functions in the base class destructor. 89 // Depending on the compiler implementation an object may be degraded to 90 // an object of the base class after the destructor of the derived class 91 // has been executed. 92 close_fd(); 93 } 94 95 96 status_t 97 BDirectory::SetTo(const entry_ref* ref) 98 { 99 // open node 100 status_t error = _SetTo(ref, true); 101 if (error != B_OK) 102 return error; 103 104 // open dir 105 fDirFd = _kern_open_dir_entry_ref(ref->device, ref->directory, ref->name); 106 if (fDirFd < 0) { 107 status_t error = fDirFd; 108 Unset(); 109 return (fCStatus = error); 110 } 111 112 // set close on exec flag on dir FD 113 fcntl(fDirFd, F_SETFD, FD_CLOEXEC); 114 115 return B_OK; 116 } 117 118 119 status_t 120 BDirectory::SetTo(const node_ref* nref) 121 { 122 Unset(); 123 status_t error = (nref ? B_OK : B_BAD_VALUE); 124 if (error == B_OK) { 125 entry_ref ref(nref->device, nref->node, "."); 126 error = SetTo(&ref); 127 } 128 set_status(error); 129 return error; 130 } 131 132 133 status_t 134 BDirectory::SetTo(const BEntry* entry) 135 { 136 if (!entry) { 137 Unset(); 138 return (fCStatus = B_BAD_VALUE); 139 } 140 141 // open node 142 status_t error = _SetTo(entry->fDirFd, entry->fName, true); 143 if (error != B_OK) 144 return error; 145 146 // open dir 147 fDirFd = _kern_open_dir(entry->fDirFd, entry->fName); 148 if (fDirFd < 0) { 149 status_t error = fDirFd; 150 Unset(); 151 return (fCStatus = error); 152 } 153 154 // set close on exec flag on dir FD 155 fcntl(fDirFd, F_SETFD, FD_CLOEXEC); 156 157 return B_OK; 158 } 159 160 161 status_t 162 BDirectory::SetTo(const char* path) 163 { 164 // open node 165 status_t error = _SetTo(-1, path, true); 166 if (error != B_OK) 167 return error; 168 169 // open dir 170 fDirFd = _kern_open_dir(-1, path); 171 if (fDirFd < 0) { 172 status_t error = fDirFd; 173 Unset(); 174 return (fCStatus = error); 175 } 176 177 // set close on exec flag on dir FD 178 fcntl(fDirFd, F_SETFD, FD_CLOEXEC); 179 180 return B_OK; 181 } 182 183 184 status_t 185 BDirectory::SetTo(const BDirectory* dir, const char* path) 186 { 187 if (!dir || !path || BPrivate::Storage::is_absolute_path(path)) { 188 Unset(); 189 return (fCStatus = B_BAD_VALUE); 190 } 191 192 int dirFD = dir->fDirFd; 193 if (dir == this) { 194 // prevent that our file descriptor goes away in _SetTo() 195 fDirFd = -1; 196 } 197 198 // open node 199 status_t error = _SetTo(dirFD, path, true); 200 if (error != B_OK) 201 return error; 202 203 // open dir 204 fDirFd = _kern_open_dir(dirFD, path); 205 if (fDirFd < 0) { 206 status_t error = fDirFd; 207 Unset(); 208 return (fCStatus = error); 209 } 210 211 if (dir == this) { 212 // cleanup after _SetTo() 213 _kern_close(dirFD); 214 } 215 216 // set close on exec flag on dir FD 217 fcntl(fDirFd, F_SETFD, FD_CLOEXEC); 218 219 return B_OK; 220 } 221 222 223 status_t 224 BDirectory::GetEntry(BEntry* entry) const 225 { 226 if (!entry) 227 return B_BAD_VALUE; 228 if (InitCheck() != B_OK) 229 return B_NO_INIT; 230 return entry->SetTo(this, ".", false); 231 } 232 233 234 bool 235 BDirectory::IsRootDirectory() const 236 { 237 // compare the directory's node ID with the ID of the root node of the FS 238 bool result = false; 239 node_ref ref; 240 fs_info info; 241 if (GetNodeRef(&ref) == B_OK && fs_stat_dev(ref.device, &info) == 0) 242 result = (ref.node == info.root); 243 return result; 244 } 245 246 247 status_t 248 BDirectory::FindEntry(const char* path, BEntry* entry, bool traverse) const 249 { 250 if (path == NULL || entry == NULL) 251 return B_BAD_VALUE; 252 253 entry->Unset(); 254 255 // init a potentially abstract entry 256 status_t status; 257 if (InitCheck() == B_OK) 258 status = entry->SetTo(this, path, traverse); 259 else 260 status = entry->SetTo(path, traverse); 261 262 // fail, if entry is abstract 263 if (status == B_OK && !entry->Exists()) { 264 status = B_ENTRY_NOT_FOUND; 265 entry->Unset(); 266 } 267 268 return status; 269 } 270 271 272 bool 273 BDirectory::Contains(const char* path, int32 nodeFlags) const 274 { 275 // check initialization and parameters 276 if (InitCheck() != B_OK) 277 return false; 278 if (!path) 279 return true; // mimic R5 behavior 280 281 // turn the path into a BEntry and let the other version do the work 282 BEntry entry; 283 if (BPrivate::Storage::is_absolute_path(path)) 284 entry.SetTo(path); 285 else 286 entry.SetTo(this, path); 287 288 return Contains(&entry, nodeFlags); 289 } 290 291 292 bool 293 BDirectory::Contains(const BEntry* entry, int32 nodeFlags) const 294 { 295 // check, if the entry exists at all 296 if (entry == NULL || !entry->Exists() || InitCheck() != B_OK) 297 return false; 298 299 if (nodeFlags != B_ANY_NODE) { 300 // test the node kind 301 bool result = false; 302 if ((nodeFlags & B_FILE_NODE) != 0) 303 result = entry->IsFile(); 304 if (!result && (nodeFlags & B_DIRECTORY_NODE) != 0) 305 result = entry->IsDirectory(); 306 if (!result && (nodeFlags & B_SYMLINK_NODE) != 0) 307 result = entry->IsSymLink(); 308 if (!result) 309 return false; 310 } 311 312 // If the directory is initialized, get the canonical paths of the dir and 313 // the entry and check, if the latter is a prefix of the first one. 314 BPath dirPath(this, ".", true); 315 BPath entryPath(entry); 316 if (dirPath.InitCheck() != B_OK || entryPath.InitCheck() != B_OK) 317 return false; 318 319 uint32 dirLen = strlen(dirPath.Path()); 320 321 if (!strncmp(dirPath.Path(), entryPath.Path(), dirLen)) { 322 // if the paths are identical, return a match to stay consistent with 323 // BeOS behavior. 324 if (entryPath.Path()[dirLen] == '\0' || entryPath.Path()[dirLen] == '/') 325 return true; 326 } 327 return false; 328 } 329 330 331 status_t 332 BDirectory::GetNextEntry(BEntry* entry, bool traverse) 333 { 334 status_t error = (entry ? B_OK : B_BAD_VALUE); 335 if (error == B_OK) { 336 entry_ref ref; 337 error = GetNextRef(&ref); 338 if (error == B_OK) 339 error = entry->SetTo(&ref, traverse); 340 } 341 return error; 342 } 343 344 345 status_t 346 BDirectory::GetNextRef(entry_ref* ref) 347 { 348 status_t error = (ref ? B_OK : B_BAD_VALUE); 349 if (error == B_OK && InitCheck() != B_OK) 350 error = B_FILE_ERROR; 351 if (error == B_OK) { 352 BPrivate::Storage::LongDirEntry entry; 353 bool next = true; 354 while (error == B_OK && next) { 355 if (GetNextDirents(&entry, sizeof(entry), 1) != 1) { 356 error = B_ENTRY_NOT_FOUND; 357 } else { 358 next = (!strcmp(entry.d_name, ".") 359 || !strcmp(entry.d_name, "..")); 360 } 361 } 362 if (error == B_OK) { 363 ref->device = entry.d_pdev; 364 ref->directory = entry.d_pino; 365 error = ref->set_name(entry.d_name); 366 } 367 } 368 return error; 369 } 370 371 372 int32 373 BDirectory::GetNextDirents(dirent* buf, size_t bufSize, int32 count) 374 { 375 if (!buf) 376 return B_BAD_VALUE; 377 if (InitCheck() != B_OK) 378 return B_FILE_ERROR; 379 return _kern_read_dir(fDirFd, buf, bufSize, count); 380 } 381 382 383 status_t 384 BDirectory::Rewind() 385 { 386 if (InitCheck() != B_OK) 387 return B_FILE_ERROR; 388 return _kern_rewind_dir(fDirFd); 389 } 390 391 392 int32 393 BDirectory::CountEntries() 394 { 395 status_t error = Rewind(); 396 if (error != B_OK) 397 return error; 398 int32 count = 0; 399 BPrivate::Storage::LongDirEntry entry; 400 while (error == B_OK) { 401 if (GetNextDirents(&entry, sizeof(entry), 1) != 1) 402 break; 403 if (strcmp(entry.d_name, ".") != 0 && strcmp(entry.d_name, "..") != 0) 404 count++; 405 } 406 Rewind(); 407 return (error == B_OK ? count : error); 408 } 409 410 411 status_t 412 BDirectory::CreateDirectory(const char* path, BDirectory* dir) 413 { 414 if (!path) 415 return B_BAD_VALUE; 416 417 // create the dir 418 status_t error = _kern_create_dir(fDirFd, path, 419 (S_IRWXU | S_IRWXG | S_IRWXO) & ~__gUmask); 420 if (error != B_OK) 421 return error; 422 423 if (dir == NULL) 424 return B_OK; 425 426 // init the supplied BDirectory 427 if (InitCheck() != B_OK || BPrivate::Storage::is_absolute_path(path)) 428 return dir->SetTo(path); 429 430 return dir->SetTo(this, path); 431 } 432 433 434 status_t 435 BDirectory::CreateFile(const char* path, BFile* file, bool failIfExists) 436 { 437 if (!path) 438 return B_BAD_VALUE; 439 440 // Let BFile do the dirty job. 441 uint32 openMode = B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE 442 | (failIfExists ? B_FAIL_IF_EXISTS : 0); 443 BFile tmpFile; 444 BFile* realFile = file ? file : &tmpFile; 445 status_t error = B_OK; 446 if (InitCheck() == B_OK && !BPrivate::Storage::is_absolute_path(path)) 447 error = realFile->SetTo(this, path, openMode); 448 else 449 error = realFile->SetTo(path, openMode); 450 if (error != B_OK && file) // mimic R5 behavior 451 file->Unset(); 452 return error; 453 } 454 455 456 status_t 457 BDirectory::CreateSymLink(const char* path, const char* linkToPath, 458 BSymLink* link) 459 { 460 if (!path || !linkToPath) 461 return B_BAD_VALUE; 462 463 // create the symlink 464 status_t error = _kern_create_symlink(fDirFd, path, linkToPath, 465 (S_IRWXU | S_IRWXG | S_IRWXO) & ~__gUmask); 466 if (error != B_OK) 467 return error; 468 469 if (link == NULL) 470 return B_OK; 471 472 // init the supplied BSymLink 473 if (InitCheck() != B_OK || BPrivate::Storage::is_absolute_path(path)) 474 return link->SetTo(path); 475 476 return link->SetTo(this, path); 477 } 478 479 480 BDirectory& 481 BDirectory::operator=(const BDirectory& dir) 482 { 483 if (&dir != this) { // no need to assign us to ourselves 484 Unset(); 485 if (dir.InitCheck() == B_OK) 486 SetTo(&dir, "."); 487 } 488 return *this; 489 } 490 491 492 status_t 493 BDirectory::_GetStatFor(const char* path, struct stat* st) const 494 { 495 if (!st) 496 return B_BAD_VALUE; 497 if (InitCheck() != B_OK) 498 return B_NO_INIT; 499 500 if (path != NULL) { 501 if (path[0] == '\0') 502 return B_ENTRY_NOT_FOUND; 503 return _kern_read_stat(fDirFd, path, false, st, sizeof(struct stat)); 504 } 505 return GetStat(st); 506 } 507 508 509 status_t 510 BDirectory::_GetStatFor(const char* path, struct stat_beos* st) const 511 { 512 struct stat newStat; 513 status_t error = _GetStatFor(path, &newStat); 514 if (error != B_OK) 515 return error; 516 517 convert_to_stat_beos(&newStat, st); 518 return B_OK; 519 } 520 521 522 // FBC 523 void BDirectory::_ErectorDirectory1() {} 524 void BDirectory::_ErectorDirectory2() {} 525 void BDirectory::_ErectorDirectory3() {} 526 void BDirectory::_ErectorDirectory4() {} 527 void BDirectory::_ErectorDirectory5() {} 528 void BDirectory::_ErectorDirectory6() {} 529 530 531 //! Closes the BDirectory's file descriptor. 532 void 533 BDirectory::close_fd() 534 { 535 if (fDirFd >= 0) { 536 _kern_close(fDirFd); 537 fDirFd = -1; 538 } 539 BNode::close_fd(); 540 } 541 542 543 int 544 BDirectory::get_fd() const 545 { 546 return fDirFd; 547 } 548 549 550 // #pragma mark - C functions 551 552 553 // TODO: Check this method for efficiency. 554 status_t 555 create_directory(const char* path, mode_t mode) 556 { 557 if (!path) 558 return B_BAD_VALUE; 559 560 // That's the strategy: We start with the first component of the supplied 561 // path, create a BPath object from it and successively add the following 562 // components. Each time we get a new path, we check, if the entry it 563 // refers to exists and is a directory. If it doesn't exist, we try 564 // to create it. This goes on, until we're done with the input path or 565 // an error occurs. 566 BPath dirPath; 567 char* component; 568 int32 nextComponent; 569 do { 570 // get the next path component 571 status_t error = BPrivate::Storage::parse_first_path_component(path, 572 component, nextComponent); 573 if (error != B_OK) 574 return error; 575 576 // append it to the BPath 577 if (dirPath.InitCheck() == B_NO_INIT) // first component 578 error = dirPath.SetTo(component); 579 else 580 error = dirPath.Append(component); 581 delete[] component; 582 if (error != B_OK) 583 return error; 584 path += nextComponent; 585 586 // create a BEntry from the BPath 587 BEntry entry; 588 error = entry.SetTo(dirPath.Path(), true); 589 if (error != B_OK) 590 return error; 591 592 // check, if it exists 593 if (entry.Exists()) { 594 // yep, it exists 595 if (!entry.IsDirectory()) // but is no directory 596 return B_NOT_A_DIRECTORY; 597 } else { 598 // it doesn't exist -- create it 599 error = _kern_create_dir(-1, dirPath.Path(), mode & ~__gUmask); 600 if (error != B_OK) 601 return error; 602 } 603 } while (nextComponent != 0); 604 return B_OK; 605 } 606 607 608 // #pragma mark - symbol versions 609 610 611 #ifdef HAIKU_TARGET_PLATFORM_LIBBE_TEST 612 # if __GNUC__ == 2 // gcc 2 613 614 B_DEFINE_SYMBOL_VERSION("_GetStatFor__C10BDirectoryPCcP4stat", 615 "GetStatFor__C10BDirectoryPCcP4stat@@LIBBE_TEST"); 616 617 # else // gcc 4 618 619 B_DEFINE_SYMBOL_VERSION("_ZNK10BDirectory11_GetStatForEPKcP4stat", 620 "_ZNK10BDirectory10GetStatForEPKcP4stat@@LIBBE_TEST"); 621 622 # endif // gcc 4 623 #else // !HAIKU_TARGET_PLATFORM_LIBBE_TEST 624 # if __GNUC__ == 2 // gcc 2 625 626 // BeOS compatible GetStatFor() 627 B_DEFINE_SYMBOL_VERSION("_GetStatFor__C10BDirectoryPCcP9stat_beos", 628 "GetStatFor__C10BDirectoryPCcP4stat@LIBBE_BASE"); 629 630 // Haiku GetStatFor() 631 B_DEFINE_SYMBOL_VERSION("_GetStatFor__C10BDirectoryPCcP4stat", 632 "GetStatFor__C10BDirectoryPCcP4stat@@LIBBE_1_ALPHA1"); 633 634 # else // gcc 4 635 636 // BeOS compatible GetStatFor() 637 B_DEFINE_SYMBOL_VERSION("_ZNK10BDirectory11_GetStatForEPKcP9stat_beos", 638 "_ZNK10BDirectory10GetStatForEPKcP4stat@LIBBE_BASE"); 639 640 // Haiku GetStatFor() 641 B_DEFINE_SYMBOL_VERSION("_ZNK10BDirectory11_GetStatForEPKcP4stat", 642 "_ZNK10BDirectory10GetStatForEPKcP4stat@@LIBBE_1_ALPHA1"); 643 644 # endif // gcc 4 645 #endif // !HAIKU_TARGET_PLATFORM_LIBBE_TEST 646