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