1 /* 2 Open Tracker License 3 4 Terms and Conditions 5 6 Copyright (c) 1991-2000, Be Incorporated. All rights reserved. 7 8 Permission is hereby granted, free of charge, to any person obtaining a copy of 9 this software and associated documentation files (the "Software"), to deal in 10 the Software without restriction, including without limitation the rights to 11 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 12 of the Software, and to permit persons to whom the Software is furnished to do 13 so, subject to the following conditions: 14 15 The above copyright notice and this permission notice applies to all licensees 16 and shall be included in all copies or substantial portions of the Software. 17 18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY, 20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 21 BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 22 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION 23 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 25 Except as contained in this notice, the name of Be Incorporated shall not be 26 used in advertising or otherwise to promote the sale, use or other dealings in 27 this Software without prior written authorization from Be Incorporated. 28 29 Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks 30 of Be Incorporated in the United States and other countries. Other brand product 31 names are registered trademarks or trademarks of their respective holders. 32 All rights reserved. 33 */ 34 35 36 #include <Debug.h> 37 #include <stdio.h> 38 #include <string.h> 39 #include <errno.h> 40 #include <SupportDefs.h> 41 42 #include "NodeWalker.h" 43 44 45 namespace BTrackerPrivate { 46 47 TWalker::~TWalker() 48 { 49 } 50 51 52 // all the following calls are pure virtuals, should not get called 53 status_t 54 TWalker::GetNextEntry(BEntry*, bool ) 55 { 56 TRESPASS(); 57 return B_ERROR; 58 } 59 60 61 status_t 62 TWalker::GetNextRef(entry_ref*) 63 { 64 TRESPASS(); 65 return B_ERROR; 66 } 67 68 69 int32 70 TWalker::GetNextDirents(struct dirent*, size_t, int32) 71 { 72 TRESPASS(); 73 return 0; 74 } 75 76 77 status_t 78 TWalker::Rewind() 79 { 80 TRESPASS(); 81 return B_ERROR; 82 } 83 84 85 int32 86 TWalker::CountEntries() 87 { 88 TRESPASS(); 89 return -1; 90 } 91 92 93 TNodeWalker::TNodeWalker(bool includeTopDirectory) 94 : 95 fDirs(20), 96 fTopIndex(-1), 97 fTopDir(NULL), 98 fIncludeTopDir(includeTopDirectory), 99 fOriginalIncludeTopDir(includeTopDirectory), 100 fJustFile(NULL), 101 fOriginalJustFile(NULL) 102 { 103 } 104 105 106 TNodeWalker::TNodeWalker(const char* path, bool includeTopDirectory) 107 : 108 fDirs(20), 109 fTopIndex(-1), 110 fTopDir(NULL), 111 fIncludeTopDir(includeTopDirectory), 112 fOriginalIncludeTopDir(includeTopDirectory), 113 fJustFile(NULL), 114 fOriginalDirCopy(path), 115 fOriginalJustFile(NULL) 116 { 117 if (fOriginalDirCopy.InitCheck() != B_OK) { 118 // not a directory, set up walking a single file 119 fJustFile = new BEntry(path); 120 if (fJustFile->InitCheck() != B_OK) { 121 delete fJustFile; 122 fJustFile = NULL; 123 } 124 fOriginalJustFile = fJustFile; 125 } else { 126 fTopDir = new BDirectory(fOriginalDirCopy); 127 fTopIndex++; 128 fDirs.AddItem(fTopDir); 129 } 130 } 131 132 133 TNodeWalker::TNodeWalker(const entry_ref* ref, bool includeTopDirectory) 134 : 135 fDirs(20), 136 fTopIndex(-1), 137 fTopDir(NULL), 138 fIncludeTopDir(includeTopDirectory), 139 fOriginalIncludeTopDir(includeTopDirectory), 140 fJustFile(NULL), 141 fOriginalDirCopy(ref), 142 fOriginalJustFile(NULL) 143 { 144 if (fOriginalDirCopy.InitCheck() != B_OK) { 145 // not a directory, set up walking a single file 146 fJustFile = new BEntry(ref); 147 if (fJustFile->InitCheck() != B_OK) { 148 delete fJustFile; 149 fJustFile = NULL; 150 } 151 fOriginalJustFile = fJustFile; 152 } else { 153 fTopDir = new BDirectory(fOriginalDirCopy); 154 fTopIndex++; 155 fDirs.AddItem(fTopDir); 156 } 157 } 158 159 160 TNodeWalker::TNodeWalker(const BDirectory* dir, bool includeTopDirectory) 161 : 162 fDirs(20), 163 fTopIndex(-1), 164 fTopDir(NULL), 165 fIncludeTopDir(includeTopDirectory), 166 fOriginalIncludeTopDir(includeTopDirectory), 167 fJustFile(NULL), 168 fOriginalDirCopy(*dir), 169 fOriginalJustFile(NULL) 170 { 171 fTopDir = new BDirectory(*dir); 172 fTopIndex++; 173 fDirs.AddItem(fTopDir); 174 } 175 176 177 TNodeWalker::TNodeWalker() 178 : 179 fDirs(20), 180 fTopIndex(-1), 181 fTopDir(NULL), 182 fIncludeTopDir(false), 183 fOriginalIncludeTopDir(false), 184 fJustFile(NULL), 185 fOriginalJustFile(NULL) 186 { 187 } 188 189 190 TNodeWalker::TNodeWalker(const char* path) 191 : 192 fDirs(20), 193 fTopIndex(-1), 194 fTopDir(NULL), 195 fIncludeTopDir(false), 196 fOriginalIncludeTopDir(false), 197 fJustFile(NULL), 198 fOriginalDirCopy(path), 199 fOriginalJustFile(NULL) 200 { 201 if (fOriginalDirCopy.InitCheck() != B_OK) { 202 // not a directory, set up walking a single file 203 fJustFile = new BEntry(path); 204 if (fJustFile->InitCheck() != B_OK) { 205 delete fJustFile; 206 fJustFile = NULL; 207 } 208 fOriginalJustFile = fJustFile; 209 } else { 210 fTopDir = new BDirectory(fOriginalDirCopy); 211 fTopIndex++; 212 fDirs.AddItem(fTopDir); 213 } 214 } 215 216 217 TNodeWalker::TNodeWalker(const entry_ref* ref) 218 : 219 fDirs(20), 220 fTopIndex(-1), 221 fTopDir(NULL), 222 fIncludeTopDir(false), 223 fOriginalIncludeTopDir(false), 224 fJustFile(NULL), 225 fOriginalDirCopy(ref), 226 fOriginalJustFile(NULL) 227 { 228 if (fOriginalDirCopy.InitCheck() != B_OK) { 229 // not a directory, set up walking a single file 230 fJustFile = new BEntry(ref); 231 if (fJustFile->InitCheck() != B_OK) { 232 delete fJustFile; 233 fJustFile = NULL; 234 } 235 fOriginalJustFile = fJustFile; 236 } else { 237 fTopDir = new BDirectory(fOriginalDirCopy); 238 fTopIndex++; 239 fDirs.AddItem(fTopDir); 240 } 241 } 242 243 TNodeWalker::TNodeWalker(const BDirectory* dir) 244 : 245 fDirs(20), 246 fTopIndex(-1), 247 fTopDir(NULL), 248 fIncludeTopDir(false), 249 fOriginalIncludeTopDir(false), 250 fJustFile(NULL), 251 fOriginalDirCopy(*dir), 252 fOriginalJustFile(NULL) 253 { 254 fTopDir = new BDirectory(*dir); 255 fTopIndex++; 256 fDirs.AddItem(fTopDir); 257 } 258 259 260 TNodeWalker::~TNodeWalker() 261 { 262 delete fOriginalJustFile; 263 264 for (;;) { 265 BDirectory* directory = fDirs.RemoveItemAt(fTopIndex--); 266 if (directory == NULL) 267 break; 268 269 delete directory; 270 } 271 } 272 273 274 status_t 275 TNodeWalker::PopDirCommon() 276 { 277 ASSERT(fTopIndex >= 0); 278 279 // done with the old dir, pop it 280 fDirs.RemoveItemAt(fTopIndex); 281 fTopIndex--; 282 delete fTopDir; 283 fTopDir = NULL; 284 285 if (fTopIndex == -1) { 286 // done 287 return B_ENTRY_NOT_FOUND; 288 } 289 290 // point to the new top dir 291 fTopDir = fDirs.ItemAt(fTopIndex); 292 293 return B_OK; 294 } 295 296 297 void 298 TNodeWalker::PushDirCommon(const entry_ref* ref) 299 { 300 fTopDir = new BDirectory(ref); 301 // OK to ignore error here. Will 302 // catch at next call to GetNextEntry 303 fTopIndex++; 304 fDirs.AddItem(fTopDir); 305 } 306 307 308 status_t 309 TNodeWalker::GetNextEntry(BEntry* entry, bool traverse) 310 { 311 if (fJustFile != NULL) { 312 *entry = *fJustFile; 313 fJustFile = 0; 314 return B_OK; 315 } 316 317 if (fTopDir == NULL) { 318 // done 319 return B_ENTRY_NOT_FOUND; 320 } 321 322 // If requested to include the top directory, return that first. 323 if (fIncludeTopDir) { 324 fIncludeTopDir = false; 325 return fTopDir->GetEntry(entry); 326 } 327 328 // Get the next entry. 329 status_t result = fTopDir->GetNextEntry(entry, traverse); 330 if (result != B_OK) { 331 result = PopDirCommon(); 332 if (result != B_OK) 333 return result; 334 335 return GetNextEntry(entry, traverse); 336 } 337 // See if this entry is a directory. If it is then push it onto the 338 // stack 339 entry_ref ref; 340 result = entry->GetRef(&ref); 341 342 if (result == B_OK && fTopDir->Contains(ref.name, B_DIRECTORY_NODE)) 343 PushDirCommon(&ref); 344 345 return result; 346 } 347 348 349 status_t 350 TNodeWalker::GetNextRef(entry_ref* ref) 351 { 352 if (fJustFile != NULL) { 353 fJustFile->GetRef(ref); 354 fJustFile = 0; 355 return B_OK; 356 } 357 358 if (fTopDir == NULL) { 359 // done 360 return B_ENTRY_NOT_FOUND; 361 } 362 363 // If requested to include the top directory, return that first. 364 if (fIncludeTopDir) { 365 fIncludeTopDir = false; 366 BEntry entry; 367 status_t err = fTopDir->GetEntry(&entry); 368 if (err == B_OK) 369 err = entry.GetRef(ref); 370 return err; 371 } 372 373 // get the next entry 374 status_t err = fTopDir->GetNextRef(ref); 375 if (err != B_OK) { 376 err = PopDirCommon(); 377 if (err != B_OK) 378 return err; 379 return GetNextRef(ref); 380 } 381 382 // See if this entry is a directory, if it is then push it onto the stack. 383 if (fTopDir->Contains(ref->name, B_DIRECTORY_NODE)) 384 PushDirCommon(ref); 385 386 return B_OK; 387 } 388 389 390 static int32 391 build_dirent(const BEntry* source, struct dirent* ent, 392 size_t size, int32 count) 393 { 394 entry_ref ref; 395 source->GetRef(&ref); 396 397 size_t recordLength = strlen(ref.name) + sizeof(dirent); 398 if (recordLength > size || count <= 0) { 399 // can't fit in buffer, bail 400 return 0; 401 } 402 403 // info about this node 404 ent->d_reclen = static_cast<ushort>(recordLength); 405 strcpy(ent->d_name, ref.name); 406 ent->d_dev = ref.device; 407 ent->d_ino = ref.directory; 408 409 // info about the parent 410 BEntry parent; 411 source->GetParent(&parent); 412 if (parent.InitCheck() == B_OK) { 413 entry_ref parentRef; 414 parent.GetRef(&parentRef); 415 ent->d_pdev = parentRef.device; 416 ent->d_pino = parentRef.directory; 417 } else { 418 ent->d_pdev = 0; 419 ent->d_pino = 0; 420 } 421 422 return 1; 423 } 424 425 426 int32 427 TNodeWalker::GetNextDirents(struct dirent* ent, size_t size, int32 count) 428 { 429 if (fJustFile != NULL) { 430 if (count == 0) 431 return 0; 432 433 // simulate GetNextDirents by building a single dirent structure 434 int32 result = build_dirent(fJustFile, ent, size, count); 435 fJustFile = 0; 436 return result; 437 } 438 439 if (fTopDir == NULL) { 440 // done 441 return 0; 442 } 443 444 // If requested to include the top directory, return that first. 445 if (fIncludeTopDir) { 446 fIncludeTopDir = false; 447 BEntry entry; 448 if (fTopDir->GetEntry(&entry) < B_OK) 449 return 0; 450 451 return build_dirent(fJustFile, ent, size, count); 452 } 453 454 // get the next entry 455 int32 nextDirent = fTopDir->GetNextDirents(ent, size, count); 456 if (nextDirent == 0) { 457 status_t result = PopDirCommon(); 458 if (result != B_OK) 459 return 0; 460 461 return GetNextDirents(ent, size, count); 462 } 463 464 // push any directories in the returned entries onto the stack 465 for (int32 i = 0; i < nextDirent; i++) { 466 if (fTopDir->Contains(ent->d_name, B_DIRECTORY_NODE)) { 467 entry_ref ref(ent->d_dev, ent->d_ino, ent->d_name); 468 PushDirCommon(&ref); 469 } 470 ent = (dirent*)((char*)ent + ent->d_reclen); 471 } 472 473 return nextDirent; 474 } 475 476 477 status_t 478 TNodeWalker::Rewind() 479 { 480 if (fOriginalJustFile != NULL) { 481 // single file mode, rewind by pointing to the original file 482 fJustFile = fOriginalJustFile; 483 return B_OK; 484 } 485 486 // pop all the directories and point to the initial one 487 for (;;) { 488 BDirectory* directory = fDirs.RemoveItemAt(fTopIndex--); 489 if (directory == NULL) 490 break; 491 492 delete directory; 493 } 494 495 fTopDir = new BDirectory(fOriginalDirCopy); 496 fTopIndex = 0; 497 fIncludeTopDir = fOriginalIncludeTopDir; 498 fDirs.AddItem(fTopDir); 499 500 return fTopDir->Rewind(); 501 // rewind the directory 502 } 503 504 int32 505 TNodeWalker::CountEntries() 506 { 507 // should not be calling this 508 TRESPASS(); 509 return -1; 510 } 511 512 513 TVolWalker::TVolWalker(bool knowsAttributes, bool writable, 514 bool includeTopDirectory) 515 : 516 TNodeWalker(includeTopDirectory), 517 fKnowsAttr(knowsAttributes), 518 fWritable(writable) 519 { 520 // Get things initialized. Find first volume, or find the first volume 521 // that supports attributes. 522 NextVolume(); 523 } 524 525 526 TVolWalker::~TVolWalker() 527 { 528 } 529 530 531 status_t 532 TVolWalker::NextVolume() 533 { 534 // The stack of directoies should be empty. 535 ASSERT(fTopIndex == -1); 536 ASSERT(fTopDir == NULL); 537 538 status_t result; 539 do { 540 result = fVolRoster.GetNextVolume(&fVol); 541 if (result != B_OK) 542 break; 543 } while ((fKnowsAttr && !fVol.KnowsAttr()) 544 || (fWritable && fVol.IsReadOnly())); 545 546 if (result == B_OK) { 547 // Get the root directory to get things started. There's always 548 // a root directory for a volume. So if there is an error then it 549 // means that something is really bad, like the system is out of 550 // memory. In that case don't worry about truying to skip to the 551 // next volume. 552 fTopDir = new BDirectory(); 553 result = fVol.GetRootDirectory(fTopDir); 554 fIncludeTopDir = fOriginalIncludeTopDir; 555 fTopIndex = 0; 556 fDirs.AddItem(fTopDir); 557 } 558 559 return result; 560 } 561 562 status_t 563 TVolWalker::GetNextEntry(BEntry* entry, bool traverse) 564 { 565 if (fTopDir == NULL) 566 return B_ENTRY_NOT_FOUND; 567 568 // get the next entry 569 status_t result = _inherited::GetNextEntry(entry, traverse); 570 while (result != B_OK) { 571 // we're done with the current volume, go to the next one 572 result = NextVolume(); 573 if (result != B_OK) 574 break; 575 576 result = GetNextEntry(entry, traverse); 577 } 578 579 return result; 580 } 581 582 583 status_t 584 TVolWalker::GetNextRef(entry_ref* ref) 585 { 586 if (fTopDir == NULL) 587 return B_ENTRY_NOT_FOUND; 588 589 // Get the next ref. 590 status_t result = _inherited::GetNextRef(ref); 591 592 while (result != B_OK) { 593 // we're done with the current volume, go to the next one 594 result = NextVolume(); 595 if (result != B_OK) 596 break; 597 result = GetNextRef(ref); 598 } 599 600 return result; 601 } 602 603 604 int32 605 TVolWalker::GetNextDirents(struct dirent* ent, size_t size, int32 count) 606 { 607 if (fTopDir == NULL) 608 return B_ENTRY_NOT_FOUND; 609 610 // get the next dirent 611 status_t result = _inherited::GetNextDirents(ent, size, count); 612 while (result != B_OK) { 613 // we're done with the current volume, go to the next one 614 result = NextVolume(); 615 if (result != B_OK) 616 break; 617 618 result = GetNextDirents(ent, size, count); 619 } 620 621 return result; 622 } 623 624 625 status_t 626 TVolWalker::Rewind() 627 { 628 fVolRoster.Rewind(); 629 return NextVolume(); 630 } 631 632 633 TQueryWalker::TQueryWalker(const char* predicate) 634 : 635 TWalker(), 636 fTime(0) 637 { 638 fPredicate = strdup(predicate); 639 NextVolume(); 640 } 641 642 643 TQueryWalker::~TQueryWalker() 644 { 645 free((char*)fPredicate); 646 fPredicate = NULL; 647 } 648 649 650 status_t 651 TQueryWalker::GetNextEntry(BEntry* entry, bool traverse) 652 { 653 status_t result; 654 do { 655 result = fQuery.GetNextEntry(entry, traverse); 656 if (result == B_ENTRY_NOT_FOUND) { 657 if (NextVolume() != B_OK) 658 break; 659 } 660 } while (result == B_ENTRY_NOT_FOUND); 661 662 return result; 663 } 664 665 666 status_t 667 TQueryWalker::GetNextRef(entry_ref* ref) 668 { 669 status_t result; 670 671 for (;;) { 672 result = fQuery.GetNextRef(ref); 673 if (result != B_ENTRY_NOT_FOUND) 674 break; 675 676 result = NextVolume(); 677 if (result != B_OK) 678 break; 679 } 680 681 return result; 682 } 683 684 685 int32 686 TQueryWalker::GetNextDirents(struct dirent* ent, size_t size, int32 count) 687 { 688 int32 result; 689 690 for (;;) { 691 result = fQuery.GetNextDirents(ent, size, count); 692 if (result != 0) 693 return result; 694 695 if (NextVolume() != B_OK) 696 return 0; 697 } 698 699 return result; 700 } 701 702 703 status_t 704 TQueryWalker::NextVolume() 705 { 706 status_t result; 707 do { 708 result = fVolRoster.GetNextVolume(&fVol); 709 if (result != B_OK) 710 break; 711 } while (!fVol.KnowsQuery()); 712 713 if (result == B_OK) { 714 result = fQuery.Clear(); 715 result = fQuery.SetVolume(&fVol); 716 result = fQuery.SetPredicate(fPredicate); 717 result = fQuery.Fetch(); 718 } 719 720 return result; 721 } 722 723 724 int32 725 TQueryWalker::CountEntries() 726 { 727 // should not be calling this 728 TRESPASS(); 729 return -1; 730 } 731 732 733 status_t 734 TQueryWalker::Rewind() 735 { 736 fVolRoster.Rewind(); 737 return NextVolume(); 738 } 739 740 } // namespace BTrackerPrivate 741