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