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 27BDirectory::BDirectory() 28 : 29 fDirFd(-1) 30{ 31} 32 33 34BDirectory::BDirectory(const BDirectory& dir) 35 : 36 fDirFd(-1) 37{ 38 *this = dir; 39} 40 41 42BDirectory::BDirectory(const entry_ref* ref) 43 : 44 fDirFd(-1) 45{ 46 SetTo(ref); 47} 48 49 50BDirectory::BDirectory(const node_ref* nref) 51 : 52 fDirFd(-1) 53{ 54 SetTo(nref); 55} 56 57 58BDirectory::BDirectory(const BEntry* entry) 59 : 60 fDirFd(-1) 61{ 62 SetTo(entry); 63} 64 65 66BDirectory::BDirectory(const char* path) 67 : 68 fDirFd(-1) 69{ 70 SetTo(path); 71} 72 73 74BDirectory::BDirectory(const BDirectory* dir, const char* path) 75 : 76 fDirFd(-1) 77{ 78 SetTo(dir, path); 79} 80 81 82BDirectory::~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 93status_t 94BDirectory::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 115status_t 116BDirectory::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 129status_t 130BDirectory::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 156status_t 157BDirectory::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 178status_t 179BDirectory::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 216status_t 217BDirectory::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 227status_t 228BDirectory::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 252bool 253BDirectory::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 272bool 273BDirectory::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 311status_t 312BDirectory::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 327status_t 328BDirectory::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 352int32 353BDirectory::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 363status_t 364BDirectory::Rewind() 365{ 366 if (InitCheck() != B_OK) 367 return B_FILE_ERROR; 368 return _kern_rewind_dir(fDirFd); 369} 370 371 372int32 373BDirectory::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 392status_t 393BDirectory::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 415status_t 416BDirectory::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 437status_t 438BDirectory::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 461BDirectory& 462BDirectory::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 473status_t 474BDirectory::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 491void BDirectory::_ErectorDirectory1() {} 492void BDirectory::_ErectorDirectory2() {} 493void BDirectory::_ErectorDirectory3() {} 494void BDirectory::_ErectorDirectory4() {} 495void BDirectory::_ErectorDirectory5() {} 496void BDirectory::_ErectorDirectory6() {} 497 498 499//! Closes the BDirectory's file descriptor. 500void 501BDirectory::close_fd() 502{ 503 if (fDirFd >= 0) { 504 _kern_close(fDirFd); 505 fDirFd = -1; 506 } 507 BNode::close_fd(); 508} 509 510 511int 512BDirectory::get_fd() const 513{ 514 return fDirFd; 515} 516 517 518status_t 519BDirectory::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. 538status_t 539create_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