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