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 if (source == NULL) 395 return 0; 396 397 entry_ref ref; 398 source->GetRef(&ref); 399 400 size_t recordLength = offsetof(struct dirent, d_name) + strlen(ref.name) + 1; 401 if (recordLength > size || count <= 0) { 402 // can't fit in buffer, bail 403 return 0; 404 } 405 406 // info about this node 407 ent->d_reclen = static_cast<ushort>(recordLength); 408 strcpy(ent->d_name, ref.name); 409 ent->d_dev = ref.device; 410 ent->d_ino = ref.directory; 411 412 // info about the parent 413 BEntry parent; 414 source->GetParent(&parent); 415 if (parent.InitCheck() == B_OK) { 416 entry_ref parentRef; 417 parent.GetRef(&parentRef); 418 ent->d_pdev = parentRef.device; 419 ent->d_pino = parentRef.directory; 420 } else { 421 ent->d_pdev = 0; 422 ent->d_pino = 0; 423 } 424 425 return 1; 426 } 427 428 429 int32 430 TNodeWalker::GetNextDirents(struct dirent* ent, size_t size, int32 count) 431 { 432 if (fJustFile != NULL) { 433 if (count == 0) 434 return 0; 435 436 // simulate GetNextDirents by building a single dirent structure 437 int32 result = build_dirent(fJustFile, ent, size, count); 438 fJustFile = 0; 439 return result; 440 } 441 442 if (fTopDir == NULL) { 443 // done 444 return 0; 445 } 446 447 // If requested to include the top directory, return that first. 448 if (fIncludeTopDir) { 449 fIncludeTopDir = false; 450 BEntry entry; 451 if (fTopDir->GetEntry(&entry) < B_OK) 452 return 0; 453 454 return build_dirent(fJustFile, ent, size, count); 455 } 456 457 // get the next entry 458 int32 nextDirent = fTopDir->GetNextDirents(ent, size, count); 459 if (nextDirent == 0) { 460 status_t result = PopDirCommon(); 461 if (result != B_OK) 462 return 0; 463 464 return GetNextDirents(ent, size, count); 465 } 466 467 // push any directories in the returned entries onto the stack 468 for (int32 i = 0; i < nextDirent; i++) { 469 if (fTopDir->Contains(ent->d_name, B_DIRECTORY_NODE)) { 470 entry_ref ref(ent->d_dev, ent->d_ino, ent->d_name); 471 PushDirCommon(&ref); 472 } 473 ent = (dirent*)((char*)ent + ent->d_reclen); 474 } 475 476 return nextDirent; 477 } 478 479 480 status_t 481 TNodeWalker::Rewind() 482 { 483 if (fOriginalJustFile != NULL) { 484 // single file mode, rewind by pointing to the original file 485 fJustFile = fOriginalJustFile; 486 return B_OK; 487 } 488 489 // pop all the directories and point to the initial one 490 for (;;) { 491 BDirectory* directory = fDirs.RemoveItemAt(fTopIndex--); 492 if (directory == NULL) 493 break; 494 495 delete directory; 496 } 497 498 fTopDir = new BDirectory(fOriginalDirCopy); 499 fTopIndex = 0; 500 fIncludeTopDir = fOriginalIncludeTopDir; 501 fDirs.AddItem(fTopDir); 502 503 return fTopDir->Rewind(); 504 // rewind the directory 505 } 506 507 int32 508 TNodeWalker::CountEntries() 509 { 510 // should not be calling this 511 TRESPASS(); 512 return -1; 513 } 514 515 516 TVolWalker::TVolWalker(bool knowsAttributes, bool writable, 517 bool includeTopDirectory) 518 : 519 TNodeWalker(includeTopDirectory), 520 fKnowsAttr(knowsAttributes), 521 fWritable(writable) 522 { 523 // Get things initialized. Find first volume, or find the first volume 524 // that supports attributes. 525 NextVolume(); 526 } 527 528 529 TVolWalker::~TVolWalker() 530 { 531 } 532 533 534 status_t 535 TVolWalker::NextVolume() 536 { 537 // The stack of directoies should be empty. 538 ASSERT(fTopIndex == -1); 539 ASSERT(fTopDir == NULL); 540 541 status_t result; 542 do { 543 result = fVolRoster.GetNextVolume(&fVol); 544 if (result != B_OK) 545 break; 546 } while ((fKnowsAttr && !fVol.KnowsAttr()) 547 || (fWritable && fVol.IsReadOnly())); 548 549 if (result == B_OK) { 550 // Get the root directory to get things started. There's always 551 // a root directory for a volume. So if there is an error then it 552 // means that something is really bad, like the system is out of 553 // memory. In that case don't worry about truying to skip to the 554 // next volume. 555 fTopDir = new BDirectory(); 556 result = fVol.GetRootDirectory(fTopDir); 557 fIncludeTopDir = fOriginalIncludeTopDir; 558 fTopIndex = 0; 559 fDirs.AddItem(fTopDir); 560 } 561 562 return result; 563 } 564 565 status_t 566 TVolWalker::GetNextEntry(BEntry* entry, bool traverse) 567 { 568 if (fTopDir == NULL) 569 return B_ENTRY_NOT_FOUND; 570 571 // get the next entry 572 status_t result = _inherited::GetNextEntry(entry, traverse); 573 while (result != B_OK) { 574 // we're done with the current volume, go to the next one 575 result = NextVolume(); 576 if (result != B_OK) 577 break; 578 579 result = GetNextEntry(entry, traverse); 580 } 581 582 return result; 583 } 584 585 586 status_t 587 TVolWalker::GetNextRef(entry_ref* ref) 588 { 589 if (fTopDir == NULL) 590 return B_ENTRY_NOT_FOUND; 591 592 // Get the next ref. 593 status_t result = _inherited::GetNextRef(ref); 594 595 while (result != B_OK) { 596 // we're done with the current volume, go to the next one 597 result = NextVolume(); 598 if (result != B_OK) 599 break; 600 result = GetNextRef(ref); 601 } 602 603 return result; 604 } 605 606 607 int32 608 TVolWalker::GetNextDirents(struct dirent* ent, size_t size, int32 count) 609 { 610 if (fTopDir == NULL) 611 return B_ENTRY_NOT_FOUND; 612 613 // get the next dirent 614 status_t result = _inherited::GetNextDirents(ent, size, count); 615 while (result != B_OK) { 616 // we're done with the current volume, go to the next one 617 result = NextVolume(); 618 if (result != B_OK) 619 break; 620 621 result = GetNextDirents(ent, size, count); 622 } 623 624 return result; 625 } 626 627 628 status_t 629 TVolWalker::Rewind() 630 { 631 fVolRoster.Rewind(); 632 return NextVolume(); 633 } 634 635 636 TQueryWalker::TQueryWalker(const char* predicate) 637 : 638 TWalker(), 639 fTime(0) 640 { 641 fPredicate = strdup(predicate); 642 NextVolume(); 643 } 644 645 646 TQueryWalker::~TQueryWalker() 647 { 648 free((char*)fPredicate); 649 fPredicate = NULL; 650 } 651 652 653 status_t 654 TQueryWalker::GetNextEntry(BEntry* entry, bool traverse) 655 { 656 status_t result; 657 do { 658 result = fQuery.GetNextEntry(entry, traverse); 659 if (result == B_ENTRY_NOT_FOUND) { 660 if (NextVolume() != B_OK) 661 break; 662 } 663 } while (result == B_ENTRY_NOT_FOUND); 664 665 return result; 666 } 667 668 669 status_t 670 TQueryWalker::GetNextRef(entry_ref* ref) 671 { 672 status_t result; 673 674 for (;;) { 675 result = fQuery.GetNextRef(ref); 676 if (result != B_ENTRY_NOT_FOUND) 677 break; 678 679 result = NextVolume(); 680 if (result != B_OK) 681 break; 682 } 683 684 return result; 685 } 686 687 688 int32 689 TQueryWalker::GetNextDirents(struct dirent* ent, size_t size, int32 count) 690 { 691 int32 result; 692 693 for (;;) { 694 result = fQuery.GetNextDirents(ent, size, count); 695 if (result != 0) 696 return result; 697 698 if (NextVolume() != B_OK) 699 return 0; 700 } 701 702 return result; 703 } 704 705 706 status_t 707 TQueryWalker::NextVolume() 708 { 709 status_t result; 710 do { 711 result = fVolRoster.GetNextVolume(&fVol); 712 if (result != B_OK) 713 break; 714 } while (!fVol.KnowsQuery()); 715 716 if (result == B_OK) { 717 result = fQuery.Clear(); 718 result = fQuery.SetVolume(&fVol); 719 result = fQuery.SetPredicate(fPredicate); 720 result = fQuery.Fetch(); 721 } 722 723 return result; 724 } 725 726 727 int32 728 TQueryWalker::CountEntries() 729 { 730 // should not be calling this 731 TRESPASS(); 732 return -1; 733 } 734 735 736 status_t 737 TQueryWalker::Rewind() 738 { 739 fVolRoster.Rewind(); 740 return NextVolume(); 741 } 742 743 } // namespace BTrackerPrivate 744