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 if (entry == NULL) 335 return B_BAD_VALUE; 336 337 entry_ref ref; 338 status_t status = GetNextRef(&ref); 339 if (status != B_OK) { 340 entry->Unset(); 341 return status; 342 } 343 return entry->SetTo(&ref, traverse); 344 } 345 346 347 status_t 348 BDirectory::GetNextRef(entry_ref* ref) 349 { 350 if (ref == NULL) 351 return B_BAD_VALUE; 352 if (InitCheck() != B_OK) 353 return B_FILE_ERROR; 354 355 BPrivate::Storage::LongDirEntry longEntry; 356 struct dirent* entry = &longEntry.dirent; 357 bool next = true; 358 while (next) { 359 if (GetNextDirents(entry, sizeof(longEntry), 1) != 1) 360 return B_ENTRY_NOT_FOUND; 361 362 next = (!strcmp(entry->d_name, ".") 363 || !strcmp(entry->d_name, "..")); 364 } 365 366 ref->device = entry->d_pdev; 367 ref->directory = entry->d_pino; 368 return ref->set_name(entry->d_name); 369 } 370 371 372 int32 373 BDirectory::GetNextDirents(dirent* buf, size_t bufSize, int32 count) 374 { 375 if (buf == NULL) 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 longEntry; 400 struct dirent* entry = &longEntry.dirent; 401 while (error == B_OK) { 402 if (GetNextDirents(entry, sizeof(longEntry), 1) != 1) 403 break; 404 if (strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0) 405 count++; 406 } 407 Rewind(); 408 return (error == B_OK ? count : error); 409 } 410 411 412 status_t 413 BDirectory::CreateDirectory(const char* path, BDirectory* dir) 414 { 415 if (!path) 416 return B_BAD_VALUE; 417 418 // create the dir 419 status_t error = _kern_create_dir(fDirFd, path, 420 (S_IRWXU | S_IRWXG | S_IRWXO) & ~__gUmask); 421 if (error != B_OK) 422 return error; 423 424 if (dir == NULL) 425 return B_OK; 426 427 // init the supplied BDirectory 428 if (InitCheck() != B_OK || BPrivate::Storage::is_absolute_path(path)) 429 return dir->SetTo(path); 430 431 return dir->SetTo(this, path); 432 } 433 434 435 status_t 436 BDirectory::CreateFile(const char* path, BFile* file, bool failIfExists) 437 { 438 if (!path) 439 return B_BAD_VALUE; 440 441 // Let BFile do the dirty job. 442 uint32 openMode = B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE 443 | (failIfExists ? B_FAIL_IF_EXISTS : 0); 444 BFile tmpFile; 445 BFile* realFile = file ? file : &tmpFile; 446 status_t error = B_OK; 447 if (InitCheck() == B_OK && !BPrivate::Storage::is_absolute_path(path)) 448 error = realFile->SetTo(this, path, openMode); 449 else 450 error = realFile->SetTo(path, openMode); 451 if (error != B_OK && file) // mimic R5 behavior 452 file->Unset(); 453 return error; 454 } 455 456 457 status_t 458 BDirectory::CreateSymLink(const char* path, const char* linkToPath, 459 BSymLink* link) 460 { 461 if (!path || !linkToPath) 462 return B_BAD_VALUE; 463 464 // create the symlink 465 status_t error = _kern_create_symlink(fDirFd, path, linkToPath, 466 (S_IRWXU | S_IRWXG | S_IRWXO) & ~__gUmask); 467 if (error != B_OK) 468 return error; 469 470 if (link == NULL) 471 return B_OK; 472 473 // init the supplied BSymLink 474 if (InitCheck() != B_OK || BPrivate::Storage::is_absolute_path(path)) 475 return link->SetTo(path); 476 477 return link->SetTo(this, path); 478 } 479 480 481 BDirectory& 482 BDirectory::operator=(const BDirectory& dir) 483 { 484 if (&dir != this) { // no need to assign us to ourselves 485 Unset(); 486 if (dir.InitCheck() == B_OK) 487 SetTo(&dir, "."); 488 } 489 return *this; 490 } 491 492 493 status_t 494 BDirectory::_GetStatFor(const char* path, struct stat* st) const 495 { 496 if (!st) 497 return B_BAD_VALUE; 498 if (InitCheck() != B_OK) 499 return B_NO_INIT; 500 501 if (path != NULL) { 502 if (path[0] == '\0') 503 return B_ENTRY_NOT_FOUND; 504 return _kern_read_stat(fDirFd, path, false, st, sizeof(struct stat)); 505 } 506 return GetStat(st); 507 } 508 509 510 status_t 511 BDirectory::_GetStatFor(const char* path, struct stat_beos* st) const 512 { 513 struct stat newStat; 514 status_t error = _GetStatFor(path, &newStat); 515 if (error != B_OK) 516 return error; 517 518 convert_to_stat_beos(&newStat, st); 519 return B_OK; 520 } 521 522 523 // FBC 524 void BDirectory::_ErectorDirectory1() {} 525 void BDirectory::_ErectorDirectory2() {} 526 void BDirectory::_ErectorDirectory3() {} 527 void BDirectory::_ErectorDirectory4() {} 528 void BDirectory::_ErectorDirectory5() {} 529 void BDirectory::_ErectorDirectory6() {} 530 531 532 //! Closes the BDirectory's file descriptor. 533 void 534 BDirectory::close_fd() 535 { 536 if (fDirFd >= 0) { 537 _kern_close(fDirFd); 538 fDirFd = -1; 539 } 540 BNode::close_fd(); 541 } 542 543 544 int 545 BDirectory::get_fd() const 546 { 547 return fDirFd; 548 } 549 550 551 // #pragma mark - C functions 552 553 554 // TODO: Check this method for efficiency. 555 status_t 556 create_directory(const char* path, mode_t mode) 557 { 558 if (!path) 559 return B_BAD_VALUE; 560 561 // That's the strategy: We start with the first component of the supplied 562 // path, create a BPath object from it and successively add the following 563 // components. Each time we get a new path, we check, if the entry it 564 // refers to exists and is a directory. If it doesn't exist, we try 565 // to create it. This goes on, until we're done with the input path or 566 // an error occurs. 567 BPath dirPath; 568 char* component; 569 int32 nextComponent; 570 do { 571 // get the next path component 572 status_t error = BPrivate::Storage::parse_first_path_component(path, 573 component, nextComponent); 574 if (error != B_OK) 575 return error; 576 577 // append it to the BPath 578 if (dirPath.InitCheck() == B_NO_INIT) // first component 579 error = dirPath.SetTo(component); 580 else 581 error = dirPath.Append(component); 582 delete[] component; 583 if (error != B_OK) 584 return error; 585 path += nextComponent; 586 587 // create a BEntry from the BPath 588 BEntry entry; 589 error = entry.SetTo(dirPath.Path(), true); 590 if (error != B_OK) 591 return error; 592 593 // check, if it exists 594 if (entry.Exists()) { 595 // yep, it exists 596 if (!entry.IsDirectory()) // but is no directory 597 return B_NOT_A_DIRECTORY; 598 } else { 599 // it doesn't exist -- create it 600 error = _kern_create_dir(-1, dirPath.Path(), mode & ~__gUmask); 601 if (error != B_OK) 602 return error; 603 } 604 } while (nextComponent != 0); 605 return B_OK; 606 } 607 608 609 // #pragma mark - symbol versions 610 611 612 #ifdef HAIKU_TARGET_PLATFORM_LIBBE_TEST 613 # if __GNUC__ == 2 // gcc 2 614 615 B_DEFINE_SYMBOL_VERSION("_GetStatFor__C10BDirectoryPCcP4stat", 616 "GetStatFor__C10BDirectoryPCcP4stat@@LIBBE_TEST"); 617 618 # else // gcc 4 619 620 B_DEFINE_SYMBOL_VERSION("_ZNK10BDirectory11_GetStatForEPKcP4stat", 621 "_ZNK10BDirectory10GetStatForEPKcP4stat@@LIBBE_TEST"); 622 623 # endif // gcc 4 624 #else // !HAIKU_TARGET_PLATFORM_LIBBE_TEST 625 # if __GNUC__ == 2 // gcc 2 626 627 // BeOS compatible GetStatFor() 628 B_DEFINE_SYMBOL_VERSION("_GetStatFor__C10BDirectoryPCcP9stat_beos", 629 "GetStatFor__C10BDirectoryPCcP4stat@LIBBE_BASE"); 630 631 // Haiku GetStatFor() 632 B_DEFINE_SYMBOL_VERSION("_GetStatFor__C10BDirectoryPCcP4stat", 633 "GetStatFor__C10BDirectoryPCcP4stat@@LIBBE_1_ALPHA1"); 634 635 # else // gcc 4 636 637 // BeOS compatible GetStatFor() 638 B_DEFINE_SYMBOL_VERSION("_ZNK10BDirectory11_GetStatForEPKcP9stat_beos", 639 "_ZNK10BDirectory10GetStatForEPKcP4stat@LIBBE_BASE"); 640 641 // Haiku GetStatFor() 642 B_DEFINE_SYMBOL_VERSION("_ZNK10BDirectory11_GetStatForEPKcP4stat", 643 "_ZNK10BDirectory10GetStatForEPKcP4stat@@LIBBE_1_ALPHA1"); 644 645 # endif // gcc 4 646 #endif // !HAIKU_TARGET_PLATFORM_LIBBE_TEST 647