#include "Directory.h" #include #include #include #include #include #include #include #include #include "Client.h" #include "DirEntry.h" #include "DirectoryScanner.h" #include "Event.h" #include "FileSystem.h" #include "Log.h" #include "Scheduler.h" Directory *Directory::current_dir; Directory::Directory(const char *name, Client *c, Request r, const Cred& cr) : ClientInterest(name, c, r, cr, DIRECTORY), entries(NULL), unhangPid(-1) { dir_bits() = 0; dir_bits() |= SCANNING; DirectoryScanner *scanner = new DirectoryScanner(*this, Event::Exists, false, new_done_handler, this); if (!scanner->done()) client()->enqueue_scanner(scanner); } void Directory::new_done_handler(DirectoryScanner *scanner, void *closure) { Directory *dir = (Directory *) closure; dir->dir_bits() &= ~SCANNING; dir->post_event(Event::EndExist); delete scanner; } Directory::~Directory() { assert(!(dir_bits() & SCANNING)); if (dir_bits() & RESCAN_SCHEDULED) Scheduler::remove_onetime_task(scan_task, this); DirEntry *q, *p = entries; if (p) { (void) chdir(); while (p) { q = p->next; delete p; p = q; } } if (current_dir == this) chdir_task(NULL); } ClientInterest::Type Directory::type() const { assert(!(dir_bits() & SCANNING)); return DIRECTORY; } void Directory::resume() { assert(!(dir_bits() & SCANNING)); ClientInterest::resume(); for (DirEntry *ep = entries; ep; ep = ep->next) if (ep->needs_scan()) ep->scan(); } Interest * Directory::find_name(const char *name) { assert(!(dir_bits() & SCANNING)); if (name[0] == '/') return this; else for (DirEntry *ep = entries; ep; ep = ep->next) if (!strcmp(name, ep->name())) return ep; return NULL; } // Directory::do_scan() scans a Directory. There are several cases. // // If monitoring is suspended, do nothing. // // If the Directory is actually not a directory (e.g., a file, a // device or nonexistent), then it is lstat'd once, and a Changed, // Deleted or Created event is written if appropriate. // // If a Directory changes from a directory to something else, then // all its entries are deleted and Deleted events are sent. // // If it is a real directory, then it is lstat'd repeatedly and read // repeatedly until it stops changing. This prevents fam from // missing files in race conditions (I hope). void Directory::do_scan() { if (!active() || !needs_scan() || (dir_bits() & SCANNING)) return; become_user(); ChangeFlags changes = do_stat(); if (changes && !isdir()) post_event(Event(changes)); Boolean scan_entries = filesystem()->dir_entries_scanned(); dir_bits() |= SCANNING; DirectoryScanner *scanner = new DirectoryScanner(*this, Event::Created, scan_entries, scan_done_handler, this); if (!scanner->done()) client()->enqueue_scanner(scanner); } void Directory::scan_task(void *closure) { Directory *dir = (Directory *) closure; dir->dir_bits() &= ~RESCAN_SCHEDULED; dir->scan(); } void Directory::scan_done_handler(DirectoryScanner *scanner, void *closure) { Directory *dir = (Directory *) closure; dir->dir_bits() &= ~SCANNING; delete scanner; } Boolean Directory::chdir() { if (current_dir == this) return true; int rc = ::chdir(name()); if (rc < 0) { Log::info("can't chdir(\"%s\"): %m", name()); return false; } if (!current_dir) Scheduler::install_onetime_task(Scheduler::ASAP, chdir_task, NULL); current_dir = this; return true; } void Directory::chdir_task(void *) { if (current_dir) { (void) ::chdir("/"); current_dir = NULL; } } // // void // Directory::unhang() // // Description: // This gets called after a system call has failed with oserror() // set to ETIMEDOUT. This means that we can't contact the nfs // server. In order to reconnect when the server becomes visible // again, a process has to acually hang on the mount. We fork and // exec nfsunhang to do this for us. We don't care about // nfsunhang's exit status because we poll nfs mounts anyway. // void Directory::unhang() { int status; if (unhangPid == -1 || waitpid(unhangPid, &status, WNOHANG) != 0) { if (access("/usr/lib/nfsunhang", X_OK) == -1) { return; } unhangPid = fork(); if (unhangPid == 0) { for (int fd = getdtablehi() - 1; fd > 2; fd--) { close(fd); } execl("/usr/lib/nfsunhang", "nfsunhang", name(), NULL); exit(1); } Log::debug("unhangPid: %d", unhangPid); } }