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 // all the following calls are pure viruals, should not get called 52 status_t 53 TWalker::GetNextEntry(BEntry*, bool ) 54 { 55 TRESPASS(); 56 return B_ERROR; 57 } 58 59 status_t 60 TWalker::GetNextRef(entry_ref*) 61 { 62 TRESPASS(); 63 return B_ERROR; 64 } 65 66 int32 67 TWalker::GetNextDirents(struct dirent*, size_t, int32) 68 { 69 TRESPASS(); 70 return 0; 71 } 72 73 74 status_t 75 TWalker::Rewind() 76 { 77 TRESPASS(); 78 return B_ERROR; 79 } 80 81 int32 82 TWalker::CountEntries() 83 { 84 TRESPASS(); 85 return -1; 86 } 87 88 89 TNodeWalker::TNodeWalker(bool includeTopDirectory) 90 : fDirs(20), 91 fTopIndex(-1), 92 fTopDir(0), 93 fIncludeTopDir(includeTopDirectory), 94 fOriginalIncludeTopDir(includeTopDirectory), 95 fJustFile(0), 96 fOriginalJustFile(0) 97 { 98 } 99 100 101 TNodeWalker::TNodeWalker(const char* path, bool includeTopDirectory) 102 : fDirs(20), 103 fTopIndex(-1), 104 fTopDir(0), 105 fIncludeTopDir(includeTopDirectory), 106 fOriginalIncludeTopDir(includeTopDirectory), 107 fJustFile(0), 108 fOriginalDirCopy(path), 109 fOriginalJustFile(0) 110 { 111 if (fOriginalDirCopy.InitCheck() != B_OK) { 112 // not a directory, set up walking a single file 113 fJustFile = new BEntry(path); 114 if (fJustFile->InitCheck() != B_OK) { 115 delete fJustFile; 116 fJustFile = NULL; 117 } 118 fOriginalJustFile = fJustFile; 119 } else { 120 fTopDir = new BDirectory(fOriginalDirCopy); 121 fTopIndex++; 122 fDirs.AddItem(fTopDir); 123 } 124 } 125 126 127 TNodeWalker::TNodeWalker(const entry_ref* ref, bool includeTopDirectory) 128 : fDirs(20), 129 fTopIndex(-1), 130 fTopDir(0), 131 fIncludeTopDir(includeTopDirectory), 132 fOriginalIncludeTopDir(includeTopDirectory), 133 fJustFile(0), 134 fOriginalDirCopy(ref), 135 fOriginalJustFile(0) 136 { 137 if (fOriginalDirCopy.InitCheck() != B_OK) { 138 // not a directory, set up walking a single file 139 fJustFile = new BEntry(ref); 140 if (fJustFile->InitCheck() != B_OK) { 141 delete fJustFile; 142 fJustFile = NULL; 143 } 144 fOriginalJustFile = fJustFile; 145 } else { 146 fTopDir = new BDirectory(fOriginalDirCopy); 147 fTopIndex++; 148 fDirs.AddItem(fTopDir); 149 } 150 } 151 152 153 TNodeWalker::TNodeWalker(const BDirectory* dir, bool includeTopDirectory) 154 : fDirs(20), 155 fTopIndex(-1), 156 fTopDir(0), 157 fIncludeTopDir(includeTopDirectory), 158 fOriginalIncludeTopDir(includeTopDirectory), 159 fJustFile(0), 160 fOriginalDirCopy(*dir), 161 fOriginalJustFile(0) 162 { 163 fTopDir = new BDirectory(*dir); 164 fTopIndex++; 165 fDirs.AddItem(fTopDir); 166 } 167 168 169 TNodeWalker::TNodeWalker() 170 : fDirs(20), 171 fTopIndex(-1), 172 fTopDir(0), 173 fIncludeTopDir(false), 174 fOriginalIncludeTopDir(false), 175 fJustFile(0), 176 fOriginalJustFile(0) 177 { 178 } 179 180 TNodeWalker::TNodeWalker(const char* path) 181 : fDirs(20), 182 fTopIndex(-1), 183 fTopDir(0), 184 fIncludeTopDir(false), 185 fOriginalIncludeTopDir(false), 186 fJustFile(0), 187 fOriginalDirCopy(path), 188 fOriginalJustFile(0) 189 { 190 if (fOriginalDirCopy.InitCheck() != B_OK) { 191 // not a directory, set up walking a single file 192 fJustFile = new BEntry(path); 193 if (fJustFile->InitCheck() != B_OK) { 194 delete fJustFile; 195 fJustFile = NULL; 196 } 197 fOriginalJustFile = fJustFile; 198 } else { 199 fTopDir = new BDirectory(fOriginalDirCopy); 200 fTopIndex++; 201 fDirs.AddItem(fTopDir); 202 } 203 } 204 205 TNodeWalker::TNodeWalker(const entry_ref* ref) 206 : fDirs(20), 207 fTopIndex(-1), 208 fTopDir(0), 209 fIncludeTopDir(false), 210 fOriginalIncludeTopDir(false), 211 fJustFile(0), 212 fOriginalDirCopy(ref), 213 fOriginalJustFile(0) 214 { 215 if (fOriginalDirCopy.InitCheck() != B_OK) { 216 // not a directory, set up walking a single file 217 fJustFile = new BEntry(ref); 218 if (fJustFile->InitCheck() != B_OK) { 219 delete fJustFile; 220 fJustFile = NULL; 221 } 222 fOriginalJustFile = fJustFile; 223 } else { 224 fTopDir = new BDirectory(fOriginalDirCopy); 225 fTopIndex++; 226 fDirs.AddItem(fTopDir); 227 } 228 } 229 230 TNodeWalker::TNodeWalker(const BDirectory* dir) 231 : fDirs(20), 232 fTopIndex(-1), 233 fTopDir(0), 234 fIncludeTopDir(false), 235 fOriginalIncludeTopDir(false), 236 fJustFile(0), 237 fOriginalDirCopy(*dir), 238 fOriginalJustFile(0) 239 { 240 fTopDir = new BDirectory(*dir); 241 fTopIndex++; 242 fDirs.AddItem(fTopDir); 243 } 244 245 TNodeWalker::~TNodeWalker() 246 { 247 delete fOriginalJustFile; 248 249 for (;;) { 250 BDirectory* directory = fDirs.RemoveItemAt(fTopIndex--); 251 if (directory == NULL) 252 break; 253 delete directory; 254 } 255 } 256 257 status_t 258 TNodeWalker::PopDirCommon() 259 { 260 ASSERT(fTopIndex >= 0); 261 262 // done with the old dir, pop it 263 fDirs.RemoveItemAt(fTopIndex); 264 fTopIndex--; 265 delete fTopDir; 266 fTopDir = NULL; 267 268 if (fTopIndex == -1) 269 // done 270 return B_ENTRY_NOT_FOUND; 271 272 // point to the new top dir 273 fTopDir = fDirs.ItemAt(fTopIndex); 274 275 return B_OK; 276 } 277 278 void 279 TNodeWalker::PushDirCommon(const entry_ref* ref) 280 { 281 fTopDir = new BDirectory(ref); 282 // OK to ignore error here. Will 283 // catch at next call to GetNextEntry 284 fTopIndex++; 285 fDirs.AddItem(fTopDir); 286 } 287 288 status_t 289 TNodeWalker::GetNextEntry(BEntry* entry, bool traverse) 290 { 291 if (fJustFile) { 292 *entry = *fJustFile; 293 fJustFile = 0; 294 return B_OK; 295 } 296 297 if (!fTopDir) 298 // done 299 return B_ENTRY_NOT_FOUND; 300 301 // If requested to include the top directory, return that first. 302 if (fIncludeTopDir) { 303 fIncludeTopDir = false; 304 return fTopDir->GetEntry(entry); 305 } 306 307 // Get the next entry. 308 status_t err = fTopDir->GetNextEntry(entry, traverse); 309 310 if (err != B_OK) { 311 err = PopDirCommon(); 312 if (err != B_OK) 313 return err; 314 return GetNextEntry(entry, traverse); 315 } 316 // See if this entry is a directory. If it is then push it onto the 317 // stack 318 entry_ref ref; 319 err = entry->GetRef(&ref); 320 321 if (err == B_OK && fTopDir->Contains(ref.name, B_DIRECTORY_NODE)) 322 PushDirCommon(&ref); 323 324 return err; 325 } 326 327 status_t 328 TNodeWalker::GetNextRef(entry_ref* ref) 329 { 330 if (fJustFile) { 331 fJustFile->GetRef(ref); 332 fJustFile = 0; 333 return B_OK; 334 } 335 336 if (!fTopDir) 337 // done 338 return B_ENTRY_NOT_FOUND; 339 340 // If requested to include the top directory, return that first. 341 if (fIncludeTopDir) { 342 fIncludeTopDir = false; 343 BEntry entry; 344 status_t err = fTopDir->GetEntry(&entry); 345 if (err == B_OK) 346 err = entry.GetRef(ref); 347 return err; 348 } 349 350 // Get the next entry. 351 status_t err = fTopDir->GetNextRef(ref); 352 if (err != B_OK) { 353 err = PopDirCommon(); 354 if (err != B_OK) 355 return err; 356 return GetNextRef(ref); 357 } 358 // See if this entry is a directory. If it is then push it onto the 359 // stack 360 361 if (fTopDir->Contains(ref->name, B_DIRECTORY_NODE)) 362 PushDirCommon(ref); 363 364 return B_OK; 365 } 366 367 static int32 368 build_dirent(const BEntry* source, struct dirent* ent, 369 size_t size, int32 count) 370 { 371 entry_ref ref; 372 source->GetRef(&ref); 373 374 size_t recordLength = strlen(ref.name) + sizeof(dirent); 375 if (recordLength > size || count <= 0) 376 // can't fit in buffer, bail 377 return 0; 378 379 // info about this node 380 ent->d_reclen = static_cast<ushort>(recordLength); 381 strcpy(ent->d_name, ref.name); 382 ent->d_dev = ref.device; 383 ent->d_ino = ref.directory; 384 385 // info about the parent 386 BEntry parent; 387 source->GetParent(&parent); 388 if (parent.InitCheck() == B_OK) { 389 entry_ref parentRef; 390 parent.GetRef(&parentRef); 391 ent->d_pdev = parentRef.device; 392 ent->d_pino = parentRef.directory; 393 } else { 394 ent->d_pdev = 0; 395 ent->d_pino = 0; 396 } 397 398 return 1; 399 } 400 401 int32 402 TNodeWalker::GetNextDirents(struct dirent* ent, size_t size, int32 count) 403 { 404 if (fJustFile) { 405 if (!count) 406 return 0; 407 408 // simulate GetNextDirents by building a single dirent structure 409 int32 result = build_dirent(fJustFile, ent, size, count); 410 fJustFile = 0; 411 return result; 412 } 413 414 if (!fTopDir) 415 // done 416 return 0; 417 418 // If requested to include the top directory, return that first. 419 if (fIncludeTopDir) { 420 fIncludeTopDir = false; 421 BEntry entry; 422 if (fTopDir->GetEntry(&entry) < B_OK) 423 return 0; 424 425 return build_dirent(fJustFile, ent, size, count); 426 } 427 428 // Get the next entry. 429 int32 result = fTopDir->GetNextDirents(ent, size, count); 430 431 if (!result) { 432 status_t err = PopDirCommon(); 433 if (err != B_OK) 434 return 0; 435 436 return GetNextDirents(ent, size, count); 437 } 438 439 // push any directories in the returned entries onto the stack 440 for (int32 i = 0; i < result; i++) { 441 if (fTopDir->Contains(ent->d_name, B_DIRECTORY_NODE)) { 442 entry_ref ref(ent->d_dev, ent->d_ino, ent->d_name); 443 PushDirCommon(&ref); 444 } 445 ent = (dirent*)((char*)ent + ent->d_reclen); 446 } 447 448 return result; 449 } 450 451 status_t 452 TNodeWalker::Rewind() 453 { 454 if (fOriginalJustFile) { 455 // single file mode, rewind by pointing to the original file 456 fJustFile = fOriginalJustFile; 457 return B_OK; 458 } 459 460 // pop all the directories and point to the initial one 461 for (;;) { 462 BDirectory* directory = fDirs.RemoveItemAt(fTopIndex--); 463 if (!directory) 464 break; 465 delete directory; 466 } 467 468 fTopDir = new BDirectory(fOriginalDirCopy); 469 fTopIndex = 0; 470 fIncludeTopDir = fOriginalIncludeTopDir; 471 fDirs.AddItem(fTopDir); 472 // rewind the directory 473 return fTopDir->Rewind(); 474 } 475 476 int32 477 TNodeWalker::CountEntries() 478 { 479 // should not be calling this 480 TRESPASS(); 481 return -1; 482 } 483 484 TVolWalker::TVolWalker(bool knowsAttributes, bool writable, bool includeTopDirectory) 485 : TNodeWalker(includeTopDirectory), 486 fKnowsAttr(knowsAttributes), 487 fWritable(writable) 488 { 489 // Get things initialized. Find first volume, or find the first volume 490 // that supports attributes. 491 NextVolume(); 492 } 493 494 TVolWalker::~TVolWalker() 495 { 496 } 497 498 status_t 499 TVolWalker::NextVolume() 500 { 501 status_t err; 502 503 // The stack of directoies should be empty. 504 ASSERT(fTopIndex == -1); 505 ASSERT(fTopDir == NULL); 506 507 do { 508 err = fVolRoster.GetNextVolume(&fVol); 509 if (err != B_OK) 510 break; 511 } while ((fKnowsAttr && !fVol.KnowsAttr()) || (fWritable && fVol.IsReadOnly())); 512 513 if (err == B_OK) { 514 // Get the root directory to get things started. There's always 515 // a root directory for a volume. So if there is an error then it 516 // means that something is really bad, like the system is out of 517 // memory. In that case don't worry about truying to skip to the 518 // next volume. 519 fTopDir = new BDirectory(); 520 err = fVol.GetRootDirectory(fTopDir); 521 fIncludeTopDir = fOriginalIncludeTopDir; 522 fTopIndex = 0; 523 fDirs.AddItem(fTopDir); 524 } 525 526 return err; 527 } 528 529 status_t 530 TVolWalker::GetNextEntry(BEntry* entry, bool traverse) 531 { 532 if (!fTopDir) 533 return B_ENTRY_NOT_FOUND; 534 535 // Get the next entry. 536 status_t err = _inherited::GetNextEntry(entry, traverse); 537 538 while (err != B_OK) { 539 // We're done with the current volume. Go to the next one 540 err = NextVolume(); 541 if (err != B_OK) 542 break; 543 err = GetNextEntry(entry, traverse); 544 } 545 546 return err; 547 } 548 549 status_t 550 TVolWalker::GetNextRef(entry_ref* ref) 551 { 552 if (!fTopDir) 553 return B_ENTRY_NOT_FOUND; 554 555 // Get the next ref. 556 status_t err = _inherited::GetNextRef(ref); 557 558 while (err != B_OK) { 559 // We're done with the current volume. Go to the next one 560 err = NextVolume(); 561 if (err != B_OK) 562 break; 563 err = GetNextRef(ref); 564 } 565 566 return err; 567 } 568 569 int32 570 TVolWalker::GetNextDirents(struct dirent* ent, size_t size, int32 count) 571 { 572 if (!fTopDir) 573 return B_ENTRY_NOT_FOUND; 574 575 // Get the next dirent. 576 status_t err = _inherited::GetNextDirents(ent, size, count); 577 578 while (err != B_OK) { 579 // We're done with the current volume. Go to the next one 580 err = NextVolume(); 581 if (err != B_OK) 582 break; 583 err = GetNextDirents(ent, size, count); 584 } 585 586 return err; 587 } 588 589 status_t 590 TVolWalker::Rewind() 591 { 592 fVolRoster.Rewind(); 593 return NextVolume(); 594 } 595 596 TQueryWalker::TQueryWalker(const char* predicate) 597 : TWalker(), fQuery(), fVolRoster(), fVol() 598 { 599 fPredicate = strdup(predicate); 600 NextVolume(); 601 } 602 603 TQueryWalker::~TQueryWalker() 604 { 605 free((char*) fPredicate); 606 fPredicate = NULL; 607 } 608 609 status_t 610 TQueryWalker::GetNextEntry(BEntry* entry, bool traverse) 611 { 612 status_t err; 613 614 do { 615 err = fQuery.GetNextEntry(entry, traverse); 616 if (err == B_ENTRY_NOT_FOUND) { 617 if (NextVolume() != B_OK) 618 break; 619 } 620 } while (err == B_ENTRY_NOT_FOUND); 621 622 return err; 623 } 624 625 status_t 626 TQueryWalker::GetNextRef(entry_ref* ref) 627 { 628 status_t err; 629 630 for (;;) { 631 err = fQuery.GetNextRef(ref); 632 if (err != B_ENTRY_NOT_FOUND) 633 break; 634 635 err = NextVolume(); 636 if (err != B_OK) 637 break; 638 } 639 640 return err; 641 } 642 643 int32 644 TQueryWalker::GetNextDirents(struct dirent* ent, size_t size, int32 count) 645 { 646 int32 result; 647 648 for (;;) { 649 result = fQuery.GetNextDirents(ent, size, count); 650 if (result != 0) 651 return result; 652 653 if (NextVolume() != B_OK) 654 return 0; 655 } 656 657 return result; 658 } 659 660 status_t 661 TQueryWalker::NextVolume() 662 { 663 status_t err; 664 do { 665 err = fVolRoster.GetNextVolume(&fVol); 666 if (err) 667 break; 668 } while (!fVol.KnowsQuery()); 669 670 671 if (err == B_OK) { 672 err = fQuery.Clear(); 673 err = fQuery.SetVolume(&fVol); 674 err = fQuery.SetPredicate(fPredicate); 675 err = fQuery.Fetch(); 676 } 677 678 return err; 679 } 680 681 int32 682 TQueryWalker::CountEntries() 683 { 684 // should not be calling this 685 TRESPASS(); 686 return -1; 687 } 688 689 status_t 690 TQueryWalker::Rewind() 691 { 692 fVolRoster.Rewind(); 693 return NextVolume(); 694 } 695 696 } // namespace BTrackerPrivate 697