示例#1
0
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;
	}
    }
}
示例#2
0
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!
}