1/* 2Open Tracker License 3 4Terms and Conditions 5 6Copyright (c) 1991-2000, Be Incorporated. All rights reserved. 7 8Permission is hereby granted, free of charge, to any person obtaining a copy of 9this software and associated documentation files (the "Software"), to deal in 10the Software without restriction, including without limitation the rights to 11use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 12of the Software, and to permit persons to whom the Software is furnished to do 13so, subject to the following conditions: 14 15The above copyright notice and this permission notice applies to all licensees 16and shall be included in all copies or substantial portions of the Software. 17 18THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY, 20FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 21BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 22AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION 23WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 25Except as contained in this notice, the name of Be Incorporated shall not be 26used in advertising or otherwise to promote the sale, use or other dealings in 27this Software without prior written authorization from Be Incorporated. 28 29Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks 30of Be Incorporated in the United States and other countries. Other brand product 31names are registered trademarks or trademarks of their respective holders. 32All 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 45namespace BTrackerPrivate { 46 47TWalker::~TWalker() 48{ 49} 50 51// all the following calls are pure viruals, should not get called 52status_t 53TWalker::GetNextEntry(BEntry*, bool ) 54{ 55 TRESPASS(); 56 return B_ERROR; 57} 58 59status_t 60TWalker::GetNextRef(entry_ref*) 61{ 62 TRESPASS(); 63 return B_ERROR; 64} 65 66int32 67TWalker::GetNextDirents(struct dirent*, size_t, int32) 68{ 69 TRESPASS(); 70 return 0; 71} 72 73 74status_t 75TWalker::Rewind() 76{ 77 TRESPASS(); 78 return B_ERROR; 79} 80 81int32 82TWalker::CountEntries() 83{ 84 TRESPASS(); 85 return -1; 86} 87 88 89TNodeWalker::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 101TNodeWalker::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 127TNodeWalker::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 153TNodeWalker::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 169TNodeWalker::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 180TNodeWalker::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 205TNodeWalker::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 230TNodeWalker::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 245TNodeWalker::~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 257status_t 258TNodeWalker::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 278void 279TNodeWalker::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 288status_t 289TNodeWalker::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 327status_t 328TNodeWalker::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 367static int32 368build_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 401int32 402TNodeWalker::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 451status_t 452TNodeWalker::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 476int32 477TNodeWalker::CountEntries() 478{ 479 // should not be calling this 480 TRESPASS(); 481 return -1; 482} 483 484TVolWalker::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 494TVolWalker::~TVolWalker() 495{ 496} 497 498status_t 499TVolWalker::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 529status_t 530TVolWalker::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 549status_t 550TVolWalker::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 569int32 570TVolWalker::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 589status_t 590TVolWalker::Rewind() 591{ 592 fVolRoster.Rewind(); 593 return NextVolume(); 594} 595 596TQueryWalker::TQueryWalker(const char* predicate) 597 : TWalker(), fQuery(), fVolRoster(), fVol() 598{ 599 fPredicate = strdup(predicate); 600 NextVolume(); 601} 602 603TQueryWalker::~TQueryWalker() 604{ 605 free((char*) fPredicate); 606 fPredicate = NULL; 607} 608 609status_t 610TQueryWalker::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 625status_t 626TQueryWalker::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 643int32 644TQueryWalker::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 660status_t 661TQueryWalker::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 681int32 682TQueryWalker::CountEntries() 683{ 684 // should not be calling this 685 TRESPASS(); 686 return -1; 687} 688 689status_t 690TQueryWalker::Rewind() 691{ 692 fVolRoster.Rewind(); 693 return NextVolume(); 694} 695 696} // namespace BTrackerPrivate 697