void CacheReader::addItem() { if ( fieldsCount() < 4 ) { _ok = false; emit error(); return; } int n = 0; char * type = field( n++ ); char * raw_path = field( n++ ); char * size_str = field( n++ ); char * mtime_str = field( n++ ); char * blocks_str = 0; char * links_str = 0; while ( fieldsCount() > n+1 ) { char * keyword = field( n++ ); char * val_str = field( n++ ); if ( strcasecmp( keyword, "blocks:" ) == 0 ) blocks_str = val_str; if ( strcasecmp( keyword, "links:" ) == 0 ) links_str = val_str; } // Type mode_t mode = S_IFREG; if ( strcasecmp( type, "F" ) == 0 ) mode = S_IFREG; else if ( strcasecmp( type, "D" ) == 0 ) mode = S_IFDIR; else if ( strcasecmp( type, "L" ) == 0 ) mode = S_IFLNK; else if ( strcasecmp( type, "BlockDev" ) == 0 ) mode = S_IFBLK; else if ( strcasecmp( type, "CharDev" ) == 0 ) mode = S_IFCHR; else if ( strcasecmp( type, "FIFO" ) == 0 ) mode = S_IFIFO; else if ( strcasecmp( type, "Socket" ) == 0 ) mode = S_IFSOCK; // Path if ( *raw_path == '/' ) _lastDir = 0; // Size char * end = 0; FileSize size = strtoll( size_str, &end, 10 ); if ( end ) { switch ( *end ) { case 'K': size *= KB; break; case 'M': size *= MB; break; case 'G': size *= GB; break; case 'T': size *= TB; break; default: break; } } // MTime time_t mtime = strtol( mtime_str, 0, 0 ); // Blocks FileSize blocks = blocks_str ? strtoll( blocks_str, 0, 10 ) : -1; // Links int links = links_str ? atoi( links_str ) : 1; // // Create a new item // char * raw_name = raw_path; if ( *raw_path == '/' && _tree->root() ) { // Split raw_path in path + name raw_name = strrchr( raw_path, '/' ); if ( raw_name ) *raw_name++ = 0; // Overwrite the last '/' with 0 byte - split string there else // No '/' found raw_name = raw_path; } // Using a protocol part to avoid directory names with a colon ":" // being cut off because it looks like a URL protocol. QByteArray protocol = "foo:"; QString path = QUrl::fromEncoded( protocol + QByteArray( raw_path ) ).path(); QString name = QUrl::fromEncoded( protocol + QByteArray( raw_name ) ).path(); if ( _lastExcludedDir ) { if ( path.startsWith( _lastExcludedDirUrl ) ) { // logDebug() << "Excluding " << path << "/" << name << endl; return; } } // Find parent in tree DirInfo * parent = _lastDir; if ( ! parent && _tree->root() ) { if ( ! _tree->root()->hasChildren() ) parent = _tree->root(); // Try the easy way first - the starting point of this cache if ( ! parent && _toplevel ) parent = dynamic_cast<DirInfo *> ( _toplevel->locate( path ) ); // Fallback: Search the entire tree if ( ! parent ) parent = dynamic_cast<DirInfo *> ( _tree->locate( path ) ); if ( ! parent ) // Still nothing? { logError() << _fileName << ":" << _lineNo << ": " << "Could not locate parent " << path << endl; return; // Ignore this cache line completely } } if ( strcasecmp( type, "D" ) == 0 ) { QString url = ( parent == _tree->root() ) ? path + "/" + name : name; // logDebug() << "Creating DirInfo for " << url << " with parent " << parent << endl; DirInfo * dir = new DirInfo( _tree, parent, url, mode, size, mtime ); dir->setReadState( DirReading ); _lastDir = dir; if ( parent ) parent->insertChild( dir ); if ( ! _tree->root() ) { _tree->setRoot( dir ); _toplevel = dir; } if ( ! _toplevel ) _toplevel = dir; _tree->childAddedNotify( dir ); if ( dir != _toplevel ) { if ( ExcludeRules::instance()->match( dir->url(), dir->name() ) ) { logDebug() << "Excluding " << name << endl; dir->setExcluded(); dir->setReadState( DirOnRequestOnly ); _tree->sendFinalizeLocal( dir ); dir->finalizeLocal(); _tree->sendReadJobFinished( dir ); _lastExcludedDir = dir; _lastExcludedDirUrl = _lastExcludedDir->url(); _lastDir = 0; } } } else { if ( parent ) { // logDebug() << "Creating FileInfo for " << parent->debugUrl() << "/" << name << endl; FileInfo * item = new FileInfo( _tree, parent, name, mode, size, mtime, blocks, links ); parent->insertChild( item ); _tree->childAddedNotify( item ); } else { logError() << _fileName << ":" << _lineNo << ": " << "No parent for item " << name << endl; } } }
void LocalDirReadJob::startReading() { struct dirent * entry; struct stat statInfo; QString dirName = _dir->url(); QString defaultCacheName = DEFAULT_CACHE_NAME; // logDebug() << _dir << endl; if ( ( _diskDir = opendir( dirName.toUtf8() ) ) ) { _dir->setReadState( DirReading ); while ( ( entry = readdir( _diskDir ) ) ) { QString entryName = entry->d_name; if ( entryName != "." && entryName != ".." ) { QString fullName = dirName + "/" + entryName; if ( lstat( fullName.toUtf8(), &statInfo ) == 0 ) // lstat() OK { if ( S_ISDIR( statInfo.st_mode ) ) // directory child? { DirInfo *subDir = new DirInfo( entryName, &statInfo, _tree, _dir ); CHECK_NEW( subDir ); _dir->insertChild( subDir ); childAdded( subDir ); if ( ExcludeRules::instance()->match( entryName ) ) { subDir->setExcluded(); subDir->setReadState( DirOnRequestOnly ); finishReading( subDir ); } else // No exclude rule matched { if ( _dir->device() == subDir->device() ) // normal case { LocalDirReadJob * job = new LocalDirReadJob( _tree, subDir ); CHECK_NEW( job ); _tree->addJob( job ); } else // The subdirectory we just found is a mount point. { logDebug() << "Found mount point " << subDir << endl; subDir->setMountPoint(); if ( _tree->crossFileSystems() ) { LocalDirReadJob * job = new LocalDirReadJob( _tree, subDir ); CHECK_NEW( job ); _tree->addJob( job ); } else { subDir->setReadState( DirOnRequestOnly ); finishReading( subDir ); } } } } else // non-directory child { if ( entryName == defaultCacheName ) // .qdirstat.cache.gz found? { logDebug() << "Found cache file " << defaultCacheName << endl; // // Read content of this subdirectory from cache file // CacheReadJob * cacheReadJob = new CacheReadJob( _tree, _dir->parent(), fullName ); CHECK_NEW( cacheReadJob ); QString firstDirInCache = cacheReadJob->reader()->firstDir(); if ( firstDirInCache == dirName ) // Does this cache file match this directory? { logDebug() << "Using cache file " << fullName << " for " << dirName << endl; cacheReadJob->reader()->rewind(); // Read offset was moved by firstDir() _tree->addJob( cacheReadJob ); // Job queue will assume ownership of cacheReadJob if ( _dir->parent() ) _dir->parent()->setReadState( DirReading ); // // Clean up partially read directory content // DirTree * tree = _tree; // Copy data members to local variables: DirInfo * dir = _dir; // This object will be deleted soon by killAll() _queue->killAll( _dir, cacheReadJob ); // Will delete this job as well! // All data members of this object are invalid from here on! logDebug() << "Deleting subtree " << dir << endl; tree->deleteSubtree( dir ); return; } else { logWarning() << "NOT using cache file " << fullName << " with dir " << firstDirInCache << " for " << dirName << endl; delete cacheReadJob; } } else { FileInfo *child = new FileInfo( entryName, &statInfo, _tree, _dir ); CHECK_NEW( child ); _dir->insertChild( child ); childAdded( child ); } } } else // lstat() error { logWarning() << "lstat(" << fullName << ") failed: " << strerror( errno ) << endl; /* * Not much we can do when lstat() didn't work; let's at * least create an (almost empty) entry as a placeholder. */ DirInfo *child = new DirInfo( _tree, _dir, entry->d_name ); CHECK_NEW( child ); child->setReadState( DirError ); _dir->insertChild( child ); childAdded( child ); } } } closedir( _diskDir ); _dir->setReadState( DirFinished ); finishReading( _dir ); } else { _dir->setReadState( DirError ); logWarning() << "opendir(" << dirName << ") failed" << endl; // opendir() doesn't set 'errno' according to POSIX :-( finishReading( _dir ); } finished(); // Don't add anything after finished() since this deletes this job! }