1// StatCacheServer.cpp 2 3#include <dirent.h> 4#include <errno.h> 5#include <stdio.h> 6#include <string.h> 7#include <sys/stat.h> 8#include <unistd.h> 9 10#include <Application.h> 11#include <Autolock.h> 12#include <Message.h> 13#include <NodeMonitor.h> 14#include <Path.h> 15 16#include "StatCacheServer.h" 17#include "StatCacheServerImpl.h" 18 19//#define DBG(x) { x; } 20#define DBG(x) 21#define OUT(format...) {printf(format); fflush(stdout);} 22 23static const int32 kMaxSymlinks = 32; 24 25// node monitor constants 26static const int32 kDefaultNodeMonitorLimit = 4096; 27static const int32 kNodeMonitorLimitIncrement = 512; 28 29// private BeOS syscall to set the node monitor slot limits 30extern "C" int _kset_mon_limit_(int num); 31 32static inline bool 33is_dot_or_dotdot(const char* name) 34{ 35 if (name && name[0] == '.') 36 return (name[1] == '\0' || name[1] == '.' && name[2] == '\0'); 37 return false; 38} 39 40// get_dirent_size 41static inline 42int32 43get_dirent_size(const char *name) 44{ 45 dirent *dummy = NULL; 46 int32 entrySize = (dummy->d_name + strlen(name) + 1) - (char*)dummy; 47 return (entrySize + 3) & ~0x3; 48} 49 50// node_ref_hash 51static inline 52uint32 53node_ref_hash(dev_t device, ino_t node) 54{ 55 uint32 hash = device; 56 hash = hash * 17 + (uint32)node; 57 hash = hash * 17 + (uint32)(node >> 32); 58 return hash; 59} 60 61// string_hash 62// 63// from the Dragon Book: a slightly modified hashpjw() 64static inline 65uint32 66string_hash(const char *name) 67{ 68 uint32 h = 0; 69 if (name) { 70 for (; *name; name++) { 71 uint32 g = h & 0xf0000000; 72 if (g) 73 h ^= g >> 24; 74 h = (h << 4) + *name; 75 } 76 } 77 return h; 78} 79 80// NodeRefHash 81size_t 82NodeRefHash::operator()(const node_ref &nodeRef) const 83{ 84 return node_ref_hash(nodeRef.device, nodeRef.node); 85} 86 87// EntryRefHash 88size_t 89EntryRefHash::operator()(const entry_ref &entryRef) const 90{ 91 uint32 hash = node_ref_hash(entryRef.device, entryRef.directory); 92 hash = hash * 17 + string_hash(entryRef.name); 93 return hash; 94} 95 96 97// #pragma mark - 98 99// constructor 100Entry::Entry() 101 : Referencable(), 102 fParent(NULL), 103 fName(), 104 fNode(NULL), 105 fPrevious(NULL), 106 fNext(NULL) 107{ 108} 109 110// destructor 111Entry::~Entry() 112{ 113 SetNode(NULL); 114} 115 116// SetTo 117status_t 118Entry::SetTo(Directory *parent, const char *name) 119{ 120 fParent = parent; 121 fName = name; 122 return B_OK; 123} 124 125// GetParent 126Directory * 127Entry::GetParent() const 128{ 129 return fParent; 130} 131 132// GetName 133const char * 134Entry::GetName() const 135{ 136 return fName.c_str(); 137} 138 139// SetNode 140void 141Entry::SetNode(Node *node) 142{ 143 if (fNode != node) { 144 if (fNode) 145 fNode->RemoveReference(); 146 fNode = node; 147 if (fNode) { 148 fNode->AddReference(); 149 if (!fNode->GetEntry() && !is_dot_or_dotdot(fName.c_str())) 150 fNode->SetEntry(this); 151 } 152 } 153} 154 155// GetNode 156Node * 157Entry::GetNode() const 158{ 159 return fNode; 160} 161 162// SetPrevious 163void 164Entry::SetPrevious(Entry *entry) 165{ 166 fPrevious = entry; 167} 168 169// GetPrevious 170Entry * 171Entry::GetPrevious() const 172{ 173 return fPrevious; 174} 175 176// SetNext 177void 178Entry::SetNext(Entry *entry) 179{ 180 fNext = entry; 181} 182 183// GetNext 184Entry * 185Entry::GetNext() const 186{ 187 return fNext; 188} 189 190// GetEntryRef 191entry_ref 192Entry::GetEntryRef() const 193{ 194 node_ref dirRef(fParent->GetNodeRef()); 195 return entry_ref(dirRef.device, dirRef.node, fName.c_str()); 196} 197 198// GetPath 199status_t 200Entry::GetPath(string& path) 201{ 202 if (!fParent) 203 return B_ERROR; 204 205 // get directory path 206 status_t error = fParent->GetPath(path); 207 if (error != B_OK) 208 return error; 209 210 // append the entry name 211 if (path[path.length() - 1] != '/') 212 path += '/'; 213 path += fName; 214 215 return B_OK; 216} 217 218// Unreferenced 219void 220Entry::Unreferenced() 221{ 222 NodeManager::GetDefault()->EntryUnreferenced(this); 223} 224 225 226// #pragma mark - 227 228// constructor 229Node::Node(const struct stat &st) 230 : Referencable(), 231 fEntry(NULL), 232 fStat(st), 233 fStatValid(false) 234{ 235} 236 237// destructor 238Node::~Node() 239{ 240 // stop watching the node 241 NodeManager::GetDefault()->StopWatching(this); 242 243 SetEntry(NULL); 244} 245 246// SetTo 247status_t 248Node::SetTo(Entry *entry) 249{ 250 // start watching the node 251 status_t error = NodeManager::GetDefault()->StartWatching(this); 252 if (error != B_OK) 253 return error; 254 255 SetEntry(entry); 256 257 // update the stat 258 return UpdateStat(); 259} 260 261// GetPath 262status_t 263Node::GetPath(string& path) 264{ 265 if (this == NodeManager::GetDefault()->GetRootDirectory()) { 266 path = "/"; 267 return B_OK; 268 } 269 270 if (!fEntry) 271 return B_ERROR; 272 return fEntry->GetPath(path); 273} 274 275// GetStat 276const struct stat & 277Node::GetStat() const 278{ 279 return fStat; 280} 281 282// GetStat 283status_t 284Node::GetStat(struct stat *st) 285{ 286 if (!fStatValid) { 287 status_t error = UpdateStat(); 288 if (error != B_OK) 289 return error; 290 } 291 *st = fStat; 292 return B_OK; 293} 294 295// UpdateStat 296status_t 297Node::UpdateStat() 298{ 299 // get path 300 string path; 301 status_t error = GetPath(path); 302 if (error != B_OK) 303 return error; 304 305DBG(OUT("disk access: lstat(): %s\n", path.c_str())); 306 307 // read stat 308 if (lstat(path.c_str(), &fStat) < 0) 309 return errno; 310 fStatValid = true; 311 return B_OK; 312} 313 314// MarkStatInvalid 315void 316Node::MarkStatInvalid() 317{ 318 fStatValid = false; 319} 320 321// SetEntry 322void 323Node::SetEntry(Entry *entry) 324{ 325 if (entry != fEntry) { 326 if (fEntry) 327 fEntry->RemoveReference(); 328 fEntry = entry; 329 if (fEntry) 330 fEntry->AddReference(); 331 } 332} 333 334// GetEntry 335Entry * 336Node::GetEntry() const 337{ 338 return fEntry; 339} 340 341// GetNodeRef 342node_ref 343Node::GetNodeRef() const 344{ 345 node_ref nodeRef; 346 nodeRef.device = fStat.st_dev; 347 nodeRef.node = fStat.st_ino; 348 return nodeRef; 349} 350 351// Unreferenced 352void 353Node::Unreferenced() 354{ 355 NodeManager::GetDefault()->NodeUnreferenced(this); 356} 357 358 359// #pragma mark - 360 361// constructor 362Directory::Directory(const struct stat &st) 363 : Node(st), 364 fFirstEntry(NULL), 365 fLastEntry(NULL), 366 fIsComplete(false) 367{ 368} 369 370// destructor 371Directory::~Directory() 372{ 373 while (Entry *entry = GetFirstEntry()) 374 RemoveEntry(entry); 375} 376 377// SetTo 378status_t 379Directory::SetTo(Entry *entry) 380{ 381 return Node::SetTo(entry); 382} 383 384// FindEntry 385status_t 386Directory::FindEntry(const char *name, Entry **entry) 387{ 388 entry_ref ref(fStat.st_dev, fStat.st_ino, name); 389 if (!fIsComplete) 390 return NodeManager::GetDefault()->CreateEntry(ref, NULL, entry); 391 *entry = NodeManager::GetDefault()->GetEntry(ref); 392 return (*entry ? B_OK : B_ENTRY_NOT_FOUND); 393} 394 395// GetFirstEntry 396Entry * 397Directory::GetFirstEntry() const 398{ 399 return fFirstEntry; 400} 401 402// GetNextEntry 403Entry * 404Directory::GetNextEntry(Entry *entry) const 405{ 406 return (entry ? entry->GetNext() : NULL); 407} 408 409// ReadAllEntries 410status_t 411Directory::ReadAllEntries() 412{ 413 if (fIsComplete) 414 return B_OK; 415 416 // get the path 417 string path; 418 status_t error = GetPath(path); 419 if (error != B_OK) 420 return error; 421 422DBG(OUT("disk access: opendir(): %s\n", path.c_str())); 423 424 // open the directory 425 DIR *dir = opendir(path.c_str()); 426 if (!dir) 427 return errno; 428 429 // read the directory 430 while (dirent *entry = readdir(dir)) { 431 Entry *dummy; 432 FindEntry(entry->d_name, &dummy); 433 } 434 closedir(dir); 435 436 fIsComplete = true; 437 438 return B_OK; 439} 440 441// IsComplete 442bool 443Directory::IsComplete() const 444{ 445 return fIsComplete; 446} 447 448// AddEntry 449void 450Directory::AddEntry(Entry *entry) 451{ 452 if (fLastEntry) { 453 entry->SetPrevious(fLastEntry); 454 entry->SetNext(NULL); 455 fLastEntry->SetNext(entry); 456 fLastEntry = entry; 457 } else { 458 entry->SetPrevious(NULL); 459 entry->SetNext(NULL); 460 fFirstEntry = fLastEntry = entry; 461 } 462 entry->AddReference(); 463 464 // the reference the "." entry has, shall be ignored 465 if (strcmp(entry->GetName(), ".") == 0) 466 fReferenceBaseCount++; 467} 468 469// RemoveEntry 470void 471Directory::RemoveEntry(Entry *entry) 472{ 473 if (entry->GetParent() != this) 474 return; 475 476 // the reference the "." entry has, shall be ignored 477 if (strcmp(entry->GetName(), ".") == 0) 478 fReferenceBaseCount--; 479 480 if (entry->GetPrevious()) 481 entry->GetPrevious()->SetNext(entry->GetNext()); 482 else 483 fFirstEntry = entry->GetNext(); 484 if (entry->GetNext()) 485 entry->GetNext()->SetPrevious(entry->GetPrevious()); 486 else 487 fLastEntry = entry->GetPrevious(); 488 entry->SetPrevious(NULL); 489 entry->SetNext(NULL); 490 entry->RemoveReference(); 491} 492 493 494// #pragma mark - 495 496// constructor 497SymLink::SymLink(const struct stat &st) 498 : Node(st), 499 fTarget() 500{ 501} 502 503// destructor 504SymLink::~SymLink() 505{ 506} 507 508// SetTo 509status_t 510SymLink::SetTo(Entry *entry) 511{ 512 // node initialization 513 status_t error = Node::SetTo(entry); 514 if (error != B_OK) 515 return error; 516 517 // get the entry path 518 string path; 519 error = entry->GetPath(path); 520 if (error != B_OK) 521 return error; 522 523 // read the link 524 char target[B_PATH_NAME_LENGTH + 1]; 525 ssize_t bytesRead = readlink(path.c_str(), target, B_PATH_NAME_LENGTH); 526 if (bytesRead < 0) 527 return errno; 528 target[bytesRead] = '\0'; 529 fTarget = target; 530 return B_OK; 531} 532 533// GetTarget 534const char * 535SymLink::GetTarget() const 536{ 537 return fTarget.c_str(); 538} 539 540 541// #pragma mark - 542 543// destructor 544NodeMonitor::NodeMonitor() 545 // higher priority and larger queue, since we must not miss update events 546 : BLooper("node monitor", B_DISPLAY_PRIORITY, 1000), 547 fCurrentNodeMonitorLimit(kDefaultNodeMonitorLimit), 548 fMessageCountSem(-1) 549{ 550} 551 552// destructor 553NodeMonitor::~NodeMonitor() 554{ 555 delete_sem(fMessageCountSem); 556} 557 558// Init 559status_t 560NodeMonitor::Init() 561{ 562 fMessageCountSem = create_sem(0, "nm message count"); 563 if (fMessageCountSem < 0) 564 return fMessageCountSem; 565 return B_OK; 566} 567 568// MessageReceived 569void 570NodeMonitor::MessageReceived(BMessage *message) 571{ 572 switch (message->what) { 573 case B_NODE_MONITOR: 574 DetachCurrentMessage(); 575 fMessageQueue.AddMessage(message); 576 release_sem(fMessageCountSem); 577 break; 578 default: 579 BLooper::MessageReceived(message); 580 } 581} 582 583// StartWatching 584status_t 585NodeMonitor::StartWatching(Node *node) 586{ 587 if (!node) 588 return B_BAD_VALUE; 589 uint32 flags = B_WATCH_STAT; 590 if (S_ISDIR(node->GetStat().st_mode)) 591 flags |= B_WATCH_DIRECTORY; 592 node_ref ref = node->GetNodeRef(); 593 status_t error = watch_node(&ref, flags, this); 594 // If starting to watch the node fail, we allocate more node 595 // monitoring slots and try again. 596 if (error != B_OK) { 597 fCurrentNodeMonitorLimit += kNodeMonitorLimitIncrement; 598 error = _kset_mon_limit_(fCurrentNodeMonitorLimit); 599 if (error == B_OK) 600 error = watch_node(&ref, flags, this); 601 } 602 return error; 603} 604 605// StopWatching 606status_t 607NodeMonitor::StopWatching(Node *node) 608{ 609 if (!node) 610 return B_BAD_VALUE; 611 node_ref ref = node->GetNodeRef(); 612 return watch_node(&ref, B_STOP_WATCHING, this); 613} 614 615// GetNextMonitoringMessage 616status_t 617NodeMonitor::GetNextMonitoringMessage(BMessage **_message) 618{ 619 // acquire the semaphore 620 status_t error = B_OK; 621 do { 622 error = acquire_sem(fMessageCountSem); 623 } while (error == B_INTERRUPTED); 624 if (error != B_OK) 625 return error; 626 627 // get the message 628 BMessage *message = fMessageQueue.NextMessage(); 629 if (!message) 630 return B_ERROR; 631 *_message = message; 632 return B_OK; 633} 634 635 636// #pragma mark - 637 638// constructor 639PathResolver::PathResolver() 640 : fSymLinkCounter(0) 641{ 642} 643 644// FindEntry 645status_t 646PathResolver::FindEntry(const char *path, bool traverse, Entry **_entry) 647{ 648 return FindEntry(NULL, path, traverse, _entry); 649} 650 651// FindEntry 652status_t 653PathResolver::FindEntry(Entry *entry, const char *path, bool traverse, 654 Entry **_entry) 655{ 656 // we accept only absolute paths, if no entry was given 657 if (!path || (!entry && *path != '/')) 658 return B_BAD_VALUE; 659 660 // get the root directory for absolute paths 661 if (*path == '/') { 662 entry = NodeManager::GetDefault()->GetRootDirectory()->GetEntry(); 663 // skip '/' 664 while (*path == '/') 665 path++; 666 } 667 668 while (*path != '\0') { 669 // get path component 670 int componentLen; 671 if (char *nextSlash = strchr(path, '/')) 672 componentLen = nextSlash - path; 673 else 674 componentLen = strlen(path); 675 string component(path, componentLen); 676 path += componentLen; 677 678 // resolve symlink 679 Node *node = entry->GetNode(); 680 if (SymLink *symlink = dynamic_cast<SymLink*>(node)) { 681 status_t error = ResolveSymlink(symlink, &node); 682 if (error != B_OK) 683 return error; 684 } 685 686 // find the entry 687 if (Directory *dir = dynamic_cast<Directory*>(node)) { 688 status_t error = dir->FindEntry(component.c_str(), &entry); 689 if (error != B_OK) 690 return error; 691 } else 692 return B_ENTRY_NOT_FOUND; 693 694 // skip '/' 695 while (*path == '/') 696 path++; 697 } 698 699 // traverse leaf symlink, if requested 700 if (traverse) { 701 status_t error = ResolveSymlink(entry, &entry); 702 if (error != B_OK) 703 return error; 704 } 705 706 *_entry = entry; 707 return B_OK; 708} 709 710// FindNode 711status_t 712PathResolver::FindNode(const char *path, bool traverse, Node **node) 713{ 714 Entry *entry; 715 status_t error = FindEntry(path, traverse, &entry); 716 if (error != B_OK) 717 return error; 718 if (!entry->GetNode()) 719 return B_ENTRY_NOT_FOUND; 720 *node = entry->GetNode(); 721 return B_OK; 722} 723 724// ResolveSymlink 725status_t 726PathResolver::ResolveSymlink(Node *node, Node **_node) 727{ 728 Entry *entry; 729 status_t error = ResolveSymlink(node, &entry); 730 if (error != B_OK) 731 return error; 732 if (!entry->GetNode()) 733 return B_ENTRY_NOT_FOUND; 734 *_node = entry->GetNode(); 735 return B_OK; 736} 737 738// ResolveSymlink 739status_t 740PathResolver::ResolveSymlink(Node *node, Entry **entry) 741{ 742 return ResolveSymlink(node->GetEntry(), entry); 743} 744 745// ResolveSymlink 746status_t 747PathResolver::ResolveSymlink(Entry *entry, Entry **_entry) 748{ 749 if (!entry->GetNode()) 750 return B_ENTRY_NOT_FOUND; 751 752 SymLink *symlink = dynamic_cast<SymLink*>(entry->GetNode()); 753 if (!symlink) { 754 *_entry = entry; 755 return B_OK; 756 } 757 758 if (fSymLinkCounter > kMaxSymlinks) 759 return B_LINK_LIMIT; 760 761 const char *target = symlink->GetTarget(); 762 if (!target || !symlink->GetEntry() || !symlink->GetEntry()->GetParent() 763 || !symlink->GetEntry()->GetParent()->GetEntry()) 764 return B_ENTRY_NOT_FOUND; 765 766 fSymLinkCounter++; 767 status_t error = FindEntry(symlink->GetEntry()->GetParent()->GetEntry(), 768 target, true, _entry); 769 fSymLinkCounter--; 770 return error; 771} 772 773 774// #pragma mark - 775 776// constructor 777NodeManager::NodeManager() 778 : BLocker("node manager"), 779 fRootDirectory(NULL), 780 fNodeMonitor(NULL), 781 fNodeMonitoringProcessor(-1) 782{ 783} 784 785// destructor 786NodeManager::~NodeManager() 787{ 788 if (fNodeMonitor) { 789 fNodeMonitor->Lock(); 790 fNodeMonitor->Quit(); 791 } 792 793 if (fNodeMonitoringProcessor >= 0) { 794 status_t status; 795 wait_for_thread(fNodeMonitoringProcessor, &status); 796 } 797} 798 799// GetDefault 800NodeManager * 801NodeManager::GetDefault() 802{ 803 return &sManager; 804} 805 806// Init 807status_t 808NodeManager::Init() 809{ 810 // create the node monitor 811 fNodeMonitor = new NodeMonitor; 812 status_t error = fNodeMonitor->Init(); 813 if (error != B_OK) 814 return error; 815 fNodeMonitor->Run(); 816 817 // spawn the node monitoring processor 818 fNodeMonitoringProcessor = spawn_thread(&_NodeMonitoringProcessorEntry, 819 "node monitoring processor", B_NORMAL_PRIORITY, this); 820 if (fNodeMonitoringProcessor < 0) 821 return fNodeMonitoringProcessor; 822 resume_thread(fNodeMonitoringProcessor); 823 824 // get root dir stat 825 struct stat st; 826 if (lstat("/", &st) < 0) 827 return errno; 828 829 // create the root node 830 node_ref nodeRef; 831 nodeRef.device = st.st_dev; 832 nodeRef.node = st.st_ino; 833 fRootDirectory = new Directory(st); 834 fNodes[nodeRef] = fRootDirectory; 835 836 // create an entry pointing to the root node 837 entry_ref entryRef(st.st_dev, st.st_ino, "."); 838 Entry *entry = new Entry; 839 error = entry->SetTo(fRootDirectory, "."); 840 if (error != B_OK) 841 return error; 842 entry->SetNode(fRootDirectory); 843 fEntries[entryRef] = entry; 844 845 // now we can initialize the root directory 846 error = fRootDirectory->SetTo(entry); 847 848 return error; 849} 850 851// GetRootDirectory 852Directory * 853NodeManager::GetRootDirectory() const 854{ 855 return fRootDirectory; 856} 857 858// GetNode 859Node * 860NodeManager::GetNode(const node_ref &nodeRef) 861{ 862 NodeMap::iterator it = fNodes.find(nodeRef); 863 if (it == fNodes.end()) 864 return NULL; 865 return it->second; 866} 867 868// GetEntry 869Entry * 870NodeManager::GetEntry(const entry_ref &entryRef) 871{ 872 EntryMap::iterator it = fEntries.find(entryRef); 873 if (it == fEntries.end()) 874 return NULL; 875 return it->second; 876} 877 878// CreateEntry 879status_t 880NodeManager::CreateEntry(const entry_ref &entryRef, const node_ref *nodeRef, 881 Entry **_entry) 882{ 883 Entry *entry = GetEntry(entryRef); 884 885 // If the entry is known, but its node is not the one it should be, we 886 // remove the entry. 887 if (nodeRef && entry && entry->GetNode() 888 && entry->GetNode()->GetNodeRef() != *nodeRef) { 889 RemoveEntry(entry); 890 entry = NULL; 891 } 892 893 if (!entry) { 894 // entry does not yet exist -- create it 895 896 // get the parent directory 897 node_ref parentDirRef; 898 parentDirRef.device = entryRef.device; 899 parentDirRef.node = entryRef.directory; 900 Directory *dir; 901 status_t error = CreateDirectory(parentDirRef, &dir); 902 if (error != B_OK) 903 return error; 904 905 // if the directory hasn't created it, we need to do that now 906 entry = GetEntry(entryRef); 907 if (!entry) { 908 entry = new Entry; 909 error = entry->SetTo(dir, entryRef.name); 910 if (error != B_OK) { 911 delete entry; 912 return error; 913 } 914 915 // get the entry's node 916 Node *node; 917 error = NodeManager::GetDefault()->_CreateNode(entry, &node); 918 if (error != B_OK) { 919 delete entry; 920 return error; 921 } 922 923 // If the node already existed, but points to another entry, we 924 // remove that entry now. We probably missed the respective 925 // B_ENTRY_REMOVED notification. 926 if (node->GetEntry() && node->GetEntry() != entry 927 && !is_dot_or_dotdot(entry->GetName())) { 928 RemoveEntry(node->GetEntry()); 929 930 // reinit node watching 931 StopWatching(node); 932 StartWatching(node); 933 } 934 935 entry->SetNode(node); 936 node->RemoveReference(); 937 // reference acquired by _CreateNode() 938 939 // initialization successful: add the entry to the dir and to the 940 // entry map 941 dir->AddEntry(entry); 942 fEntries[entryRef] = entry; 943 entry->RemoveReference(); 944DBG( 945string path; 946entry->GetPath(path); 947OUT("entry created: `%s'\n", path.c_str()); 948) 949 } 950 } 951 952 *_entry = entry; 953 return B_OK; 954} 955 956// CreateDirectory 957status_t 958NodeManager::CreateDirectory(const node_ref &nodeRef, Directory **_dir) 959{ 960 Node *node = GetNode(nodeRef); 961 if (!node) { 962 // node not yet known -- load the directory 963 // get the full path 964 entry_ref entryRef(nodeRef.device, nodeRef.node, "."); 965 BPath path; 966 status_t error = path.SetTo(&entryRef); 967 if (error != B_OK) 968 return error; 969 970 // find the node 971 error = PathResolver().FindNode(path.Path(), false, &node); 972 if (error != B_OK) 973 return error; 974 } 975 976 // node found -- check, if it is a directory 977 Directory *dir = dynamic_cast<Directory*>(node); 978 if (!dir) 979 return B_NOT_A_DIRECTORY; 980 981 *_dir = dir; 982 return B_OK; 983} 984 985// RemoveEntry 986void 987NodeManager::RemoveEntry(Entry *entry) 988{ 989 if (!entry) 990 return; 991 992DBG( 993string path; 994entry->GetPath(path); 995OUT("entry removed: `%s'\n", path.c_str()); 996) 997 998 // get a temporary reference, so that the entry will not be deleted when 999 // we unset the node 1000 entry->AddReference(); 1001 1002 // remove from directory and node 1003 if (entry->GetParent()) 1004 entry->GetParent()->RemoveEntry(entry); 1005 1006 // detach from node 1007 Node *node = entry->GetNode(); 1008 if (node) { 1009 if (node->GetEntry() == entry) 1010 node->SetEntry(NULL); 1011 entry->SetNode(NULL); 1012 } 1013 1014 // surrender our temporary reference: now the entry should be unreference 1015 entry->RemoveReference(); 1016} 1017 1018// MoveEntry 1019void 1020NodeManager::MoveEntry(Entry *entry, const entry_ref &newRef, 1021 const node_ref &nodeRef) 1022{ 1023 // get the target directory 1024 node_ref newDirRef; 1025 newDirRef.device = newRef.device; 1026 newDirRef.node = newRef.directory; 1027 Directory *newDir = dynamic_cast<Directory*>(GetNode(newDirRef)); 1028 if (!newDir) { 1029 // target directory unknown -- simply remove the entry 1030 RemoveEntry(entry); 1031 return; 1032 } 1033 1034 // If the directory, the name, or the node (missed B_ENTRY_REMOVED and 1035 // B_ENTRY_CREATED) changed, we remove the old entry and create a new one. 1036 Node *node = entry->GetNode(); 1037 if (newDir != entry->GetParent() 1038 || strcmp(newRef.name, entry->GetName()) != 0 1039 || (node && node->GetNodeRef() != nodeRef)) { 1040 // get a temporary reference to the node, so it won't be unnecessarily 1041 // deleted 1042 if (node) 1043 node->AddReference(); 1044 1045 RemoveEntry(entry); 1046 CreateEntry(newRef, &nodeRef, &entry); 1047 1048 if (node) 1049 node->RemoveReference(); 1050 } 1051} 1052 1053// EntryUnreferenced 1054void 1055NodeManager::EntryUnreferenced(Entry *entry) 1056{ 1057DBG(OUT("NodeManager::EntryUnreferenced(%p): (%p, `%s')\n", entry, entry->GetParent(), entry->GetName())); 1058 // remove entry from the map and delete it 1059 if (fEntries.erase(entry->GetEntryRef()) > 0) 1060 delete entry; 1061} 1062 1063// NodeUnreferenced 1064void 1065NodeManager::NodeUnreferenced(Node *node) 1066{ 1067DBG(OUT("NodeManager::NodeUnreferenced(%p): entry: %p\n", node, node->GetEntry())); 1068 // remove node from the map and delete it 1069 if (fNodes.erase(node->GetNodeRef()) > 0) 1070 delete node; 1071} 1072 1073// StartWatching 1074status_t 1075NodeManager::StartWatching(Node *node) 1076{ 1077 return fNodeMonitor->StartWatching(node); 1078} 1079 1080// StopWatching 1081status_t 1082NodeManager::StopWatching(Node *node) 1083{ 1084 return fNodeMonitor->StopWatching(node); 1085} 1086 1087// _NodeMonitoringProcessorEntry 1088int32 1089NodeManager::_NodeMonitoringProcessorEntry(void *data) 1090{ 1091 return ((NodeManager*)data)->_NodeMonitoringProcessor(); 1092} 1093 1094// _NodeMonitoringProcessor 1095int32 1096NodeManager::_NodeMonitoringProcessor() 1097{ 1098 BMessage *message; 1099 while (fNodeMonitor->GetNextMonitoringMessage(&message) == B_OK) { 1100 int32 opcode; 1101 if (message->FindInt32("opcode", &opcode) == B_OK) { 1102 BAutolock _(this); 1103 switch (opcode) { 1104 case B_ENTRY_CREATED: 1105 _EntryCreated(message); 1106 break; 1107 case B_ENTRY_REMOVED: 1108 _EntryRemoved(message); 1109 break; 1110 case B_ENTRY_MOVED: 1111 _EntryMoved(message); 1112 break; 1113 case B_STAT_CHANGED: 1114 _StatChanged(message); 1115 break; 1116 } 1117 } 1118 delete message; 1119 } 1120} 1121 1122// _CreateNode 1123// 1124// On success the caller gets a reference to the node, they are required to 1125// surrender, if done with the node. 1126status_t 1127NodeManager::_CreateNode(Entry *entry, Node **_node) 1128{ 1129 // get the path 1130 string path; 1131 status_t error = entry->GetPath(path); 1132 if (error != B_OK) 1133 return error; 1134 1135DBG(OUT("disk access: lstat(): %s\n", path.c_str())); 1136 1137 // read the stat 1138 struct stat st; 1139 if (lstat(path.c_str(), &st) < 0) 1140 return errno; 1141 1142 // check, if the node does already exist 1143 node_ref nodeRef; 1144 nodeRef.device = st.st_dev; 1145 nodeRef.node = st.st_ino; 1146 Node *node = GetNode(nodeRef); 1147 1148 if (node) { 1149 node->AddReference(); 1150 } else { 1151 // node does not yet exist -- create it 1152 if (S_ISLNK(st.st_mode)) 1153 node = new SymLink(st); 1154 else if (S_ISDIR(st.st_mode)) 1155 node = new Directory(st); 1156 else 1157 node = new Node(st); 1158 1159 error = node->SetTo(entry); 1160 if (error != B_OK) { 1161 delete node; 1162 return error; 1163 } 1164 1165 fNodes[nodeRef] = node; 1166 } 1167 1168 *_node = node; 1169 return B_OK; 1170} 1171 1172// _EntryCreated 1173void 1174NodeManager::_EntryCreated(BMessage *message) 1175{ 1176 // get the info 1177 node_ref dirNodeRef; 1178 node_ref nodeRef; 1179 const char* name; 1180 if (message->FindInt32("device", &dirNodeRef.device) != B_OK 1181 || message->FindInt64("directory", &dirNodeRef.node) != B_OK 1182 || message->FindInt64("node", &nodeRef.node) != B_OK 1183 || message->FindString("name", &name) != B_OK) { 1184 return; 1185 } 1186 nodeRef.device = dirNodeRef.device; 1187 1188 // get the directory 1189 Node *node = NodeManager::GetDefault()->GetNode(dirNodeRef); 1190 Directory *dir = dynamic_cast<Directory*>(node); 1191 if (!dir) 1192 return; 1193 1194 // add the entry, if the directory is complete 1195 if (dir->IsComplete()) { 1196 Entry *entry; 1197 if (dir->FindEntry(name, &entry) != B_OK) { 1198 entry_ref ref(dirNodeRef.device, dirNodeRef.node, name); 1199 NodeManager::GetDefault()->CreateEntry(ref, &nodeRef, &entry); 1200 } 1201 } 1202} 1203 1204// _EntryRemoved 1205void 1206NodeManager::_EntryRemoved(BMessage *message) 1207{ 1208 // get the info 1209 node_ref nodeRef; 1210 const char* name; 1211 if (message->FindInt32("device", &nodeRef.device) != B_OK 1212// || message->FindInt64("directory", &nodeRef.node) != B_OK 1213 || message->FindInt64("node", &nodeRef.node) != B_OK) { 1214 return; 1215 } 1216 1217 // get the node 1218 Node *node = NodeManager::GetDefault()->GetNode(nodeRef); 1219 if (!node) 1220 return; 1221 1222 // remove it 1223 NodeManager::GetDefault()->RemoveEntry(node->GetEntry()); 1224} 1225 1226// _EntryMoved 1227void 1228NodeManager::_EntryMoved(BMessage *message) 1229{ 1230 // get the info 1231 node_ref nodeRef; 1232 ino_t newDirID; 1233 const char* name; 1234 if (message->FindInt32("device", &nodeRef.device) != B_OK 1235// || message->FindInt64("from directory", &fromDirectoryID) != B_OK 1236 || message->FindInt64("to directory", &newDirID) != B_OK 1237 || message->FindInt64("node", &nodeRef.node) != B_OK 1238 || message->FindString("name", &name) != B_OK) { 1239 return; 1240 } 1241 1242 // get the node 1243 Node *node = NodeManager::GetDefault()->GetNode(nodeRef); 1244 if (!node) { 1245 // create it if not present 1246 Entry *entry; 1247 entry_ref newRef(nodeRef.device, newDirID, name); 1248 NodeManager::GetDefault()->CreateEntry(newRef, &nodeRef, &entry); 1249 return; 1250 } 1251 1252 // move it 1253 entry_ref newRef(nodeRef.device, newDirID, name); 1254 NodeManager::GetDefault()->MoveEntry(node->GetEntry(), newRef, nodeRef); 1255} 1256 1257// _StatChanged 1258void 1259NodeManager::_StatChanged(BMessage *message) 1260{ 1261 // get the node ref 1262 node_ref nodeRef; 1263 if (message->FindInt32("device", &nodeRef.device) != B_OK 1264 || message->FindInt64("node", &nodeRef.node)) { 1265 return; 1266 } 1267 1268 // get the node 1269 Node *node = GetNode(nodeRef); 1270 if (!node) 1271 return; 1272 1273 node->MarkStatInvalid(); 1274} 1275 1276// sManager 1277NodeManager NodeManager::sManager; 1278 1279 1280// #pragma mark - 1281 1282// read_request 1283static 1284status_t 1285read_request(port_id port, stat_cache_request &request) 1286{ 1287 status_t error = B_OK; 1288 bool done = false; 1289 do { 1290 int32 code; 1291 ssize_t bytesRead = read_port(port, &code, &request, 1292 sizeof(stat_cache_request)); 1293 if (bytesRead < 0) { 1294 error = bytesRead; 1295 done = (error != B_INTERRUPTED); 1296 } else if (bytesRead 1297 < ((stat_cache_request*)NULL)->path + 2 - (char*)NULL) { 1298DBG(OUT("request too short: %ld\n", bytesRead)); 1299 error = B_ERROR; 1300 } else { 1301 done = true; 1302 error = B_OK; 1303 } 1304 } while (!done); 1305 return error; 1306} 1307 1308// handle_stat_request 1309static 1310status_t 1311handle_stat_request(stat_cache_request &request) 1312{ 1313DBG(OUT("handle_stat_request(): `%s'\n", request.path)); 1314 stat_cache_stat_reply reply; 1315 1316 // get the node 1317 PathResolver resolver; 1318 Node *node; 1319 reply.error = resolver.FindNode(request.path, true, &node); 1320 1321 // get the stat 1322 if (reply.error == B_OK) 1323 reply.error = node->GetStat(&reply.st); 1324DBG(OUT(" -> `%s'\n", strerror(reply.error))); 1325 1326 // send the reply 1327 return write_port(request.replyPort, 0, &reply, sizeof(reply)); 1328} 1329 1330// handle_read_dir_request 1331static 1332status_t 1333handle_read_dir_request(stat_cache_request &request) 1334{ 1335DBG(OUT("handle_read_dir_request(): `%s'\n", request.path)); 1336 // get the directory 1337 PathResolver resolver; 1338 Node *node = NULL; 1339 status_t error = resolver.FindNode(request.path, true, &node); 1340 Directory *dir = dynamic_cast<Directory*>(node); 1341 if (error == B_OK && !dir) 1342 error = B_NOT_A_DIRECTORY; 1343 1344 // read all entries 1345 if (error == B_OK) 1346 error = dir->ReadAllEntries(); 1347 1348 // compute the reply size 1349 int32 replySize = sizeof(stat_cache_readdir_reply); 1350 int32 entryCount = 0; 1351 if (error == B_OK) { 1352 for (Entry *entry = dir->GetFirstEntry(); 1353 entry; 1354 entry = dir->GetNextEntry(entry)) { 1355 replySize += get_dirent_size(entry->GetName()); 1356 entryCount++; 1357 } 1358 } 1359 1360 // allocate a reply 1361 stat_cache_readdir_reply *reply 1362 = (stat_cache_readdir_reply*)new uint8[replySize]; 1363 reply->error = error; 1364 reply->entryCount = entryCount; 1365 1366 // copy the entries into the reply 1367 if (error == B_OK) { 1368 uint8 *buffer = reply->buffer; 1369 for (Entry *entry = dir->GetFirstEntry(); 1370 entry; 1371 entry = dir->GetNextEntry(entry)) { 1372 // get the required info 1373 int32 entrySize = get_dirent_size(entry->GetName()); 1374 node_ref parentNodeRef(entry->GetParent()->GetNodeRef()); 1375 node_ref nodeRef(entry->GetNode()->GetNodeRef()); 1376 1377 // fill in the dirent 1378 dirent *ent = (dirent*)buffer; 1379 ent->d_pdev = parentNodeRef.device; 1380 ent->d_pino = parentNodeRef.node; 1381 ent->d_dev = nodeRef.device; 1382 ent->d_ino = nodeRef.node; 1383 ent->d_reclen = entrySize; 1384 strcpy(ent->d_name, entry->GetName()); 1385 1386 buffer += entrySize; 1387 } 1388 } 1389 1390DBG(OUT(" -> entryCount: %ld, error: `%s'\n", reply->entryCount, strerror(reply->error))); 1391 // send the reply 1392 error = write_port(request.replyPort, 0, reply, replySize); 1393 delete[] (uint8*)reply; 1394 return error; 1395} 1396 1397// request_loop 1398int32 1399request_loop(void *data) 1400{ 1401 port_id requestPort = *(port_id*)data; 1402 1403 // init node manager 1404 status_t error = NodeManager::GetDefault()->Init(); 1405 if (error != B_OK) { 1406 fprintf(stderr, "Failed to init node manager: %s\n", strerror(error)); 1407 return 1; 1408 } 1409 1410 // handle requests until our port goes away 1411 stat_cache_request request; 1412 while (read_request(requestPort, request) == B_OK) { 1413 BAutolock _(NodeManager::GetDefault()); 1414 switch (request.command) { 1415 case STAT_CACHE_COMMAND_STAT: 1416 handle_stat_request(request); 1417 break; 1418 case STAT_CACHE_COMMAND_READDIR: 1419 handle_read_dir_request(request); 1420 break; 1421 default: 1422 fprintf(stderr, "Unknown command: %ld\n", request.command); 1423 break; 1424 } 1425 } 1426 1427 // delete the request port 1428 if (delete_port(requestPort) == B_OK) 1429 be_app->PostMessage(B_QUIT_REQUESTED); 1430 return 0; 1431} 1432 1433// main 1434int 1435main() 1436{ 1437 // create the request port 1438 port_id requestPort = create_port(1, STAT_CACHE_SERVER_PORT_NAME); 1439 if (requestPort < 0) { 1440 fprintf(stderr, "Failed to create request port: %s\n", 1441 strerror(requestPort)); 1442 return 1; 1443 } 1444 1445 // start the request handling loop 1446 thread_id requestLoop = spawn_thread(request_loop, "request loop", 1447 B_NORMAL_PRIORITY, &requestPort); 1448 if (resume_thread(requestLoop) < B_OK) 1449 return -1; 1450 1451 // the BApplication is only needed for a smooth server shutdown 1452 BApplication app("application/x-vnd.haiku.jam-cacheserver"); 1453 app.Run(); 1454 1455 // delete the request port 1456 delete_port(requestPort); 1457 1458 // wait for the request handling loop to quit 1459 status_t result; 1460 wait_for_thread(requestLoop, &result); 1461 1462 return 0; 1463} 1464