You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
334 lines
11 KiB
334 lines
11 KiB
//Author: Max Howell <max.howell@methylblue.com>, (C) 2003-4
|
|
//Copyright: See COPYING file that comes with this distribution
|
|
|
|
#include "Config.h"
|
|
#include "debug.h"
|
|
#include <dirent.h>
|
|
#include "fileTree.h"
|
|
#include <fstab.h>
|
|
#include "localLister.h"
|
|
#ifdef HAVE_MNTENT_H
|
|
#include <mntent.h>
|
|
#endif
|
|
#include <tqapplication.h> //postEvent()
|
|
#include <tqfile.h>
|
|
#include "scan.h"
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
|
|
namespace Filelight
|
|
{
|
|
TQStringList LocalLister::s_remoteMounts;
|
|
TQStringList LocalLister::s_localMounts;
|
|
|
|
LocalLister::LocalLister( const TQString &path, Chain<Directory> *cachedTrees, TQObject *tqparent )
|
|
: TQThread()
|
|
, m_path( path )
|
|
, m_trees( cachedTrees )
|
|
, m_parent( tqparent )
|
|
{
|
|
//add empty directories for any mount points that are in the path
|
|
//TODO empty directories is not ideal as adds to fileCount incorrectly
|
|
|
|
TQStringList list( Config::skipList );
|
|
if( !Config::scanAcrossMounts ) list += s_localMounts;
|
|
if( !Config::scanRemoteMounts ) list += s_remoteMounts;
|
|
|
|
for( TQStringList::ConstIterator it = list.constBegin(); it != list.constEnd(); ++it )
|
|
if( (*it).startsWith( path ) )
|
|
//prevent scanning of these directories
|
|
m_trees->append( new Directory( (*it).local8Bit() ) );
|
|
|
|
start();
|
|
}
|
|
|
|
void
|
|
LocalLister::run()
|
|
{
|
|
//recursively scan the requested path
|
|
const TQCString path = TQFile::encodeName( m_path );
|
|
Directory *tree = scan( path, path );
|
|
|
|
//delete the list of trees useful for this scan,
|
|
//in a sucessful scan the contents would now be transfered to 'tree'
|
|
delete m_trees;
|
|
|
|
if( ScanManager::s_abort ) //scan was cancelled
|
|
{
|
|
debug() << "Scan succesfully aborted\n";
|
|
delete tree;
|
|
tree = 0;
|
|
}
|
|
|
|
TQCustomEvent *e = new TQCustomEvent( 1000 );
|
|
e->setData( tree );
|
|
TQApplication::postEvent( m_parent, e );
|
|
}
|
|
|
|
// from system.h in GNU coreutils package
|
|
/* Extract or fake data from a `struct stat'.
|
|
ST_BLKSIZE: Preferred I/O blocksize for the file, in bytes.
|
|
ST_NBLOCKS: Number of blocks in the file, including indirect blocks.
|
|
ST_NBLOCKSIZE: Size of blocks used when calculating ST_NBLOCKS. */
|
|
#ifndef HAVE_STRUCT_STAT_ST_BLOCKS
|
|
#define ST_BLKSIZE(statbuf) DEV_BSIZE
|
|
#if defined _POSIX_SOURCE || !defined BSIZE /* fileblocks.c uses BSIZE. */
|
|
#define ST_NBLOCKS(statbuf) ((statbuf).st_size / ST_NBLOCKSIZE + ((statbuf).st_size % ST_NBLOCKSIZE != 0))
|
|
#else /* !_POSIX_SOURCE && BSIZE */
|
|
#define ST_NBLOCKS(statbuf) (S_ISREG ((statbuf).st_mode) || S_ISDIR ((statbuf).st_mode) ? st_blocks ((statbuf).st_size) : 0)
|
|
#endif /* !_POSIX_SOURCE && BSIZE */
|
|
#else /* HAVE_STRUCT_STAT_ST_BLOCKS */
|
|
/* Some systems, like Sequents, return st_blksize of 0 on pipes.
|
|
Also, when running `rsh hpux11-system cat any-file', cat would
|
|
determine that the output stream had an st_blksize of 2147421096.
|
|
So here we arbitrarily limit the `optimal' block size to 4MB.
|
|
If anyone knows of a system for which the legitimate value for
|
|
st_blksize can exceed 4MB, please report it as a bug in this code. */
|
|
#define ST_BLKSIZE(statbuf) ((0 < (statbuf).st_blksize && (statbuf).st_blksize <= (1 << 22)) /* 4MiB */ ? (statbuf).st_blksize : DEV_BSIZE)
|
|
#if defined hpux || defined __hpux__ || defined __hpux
|
|
/* HP-UX counts st_blocks in 1024-byte units.
|
|
This loses when mixing HP-UX and BSD filesystems with NFS. */
|
|
#define ST_NBLOCKSIZE 1024
|
|
#else /* !hpux */
|
|
#if defined _AIX && defined _I386
|
|
/* AIX PS/2 counts st_blocks in 4K units. */
|
|
#define ST_NBLOCKSIZE (4 * 1024)
|
|
#else /* not AIX PS/2 */
|
|
#if defined _CRAY
|
|
#define ST_NBLOCKS(statbuf) (S_ISREG ((statbuf).st_mode) || S_ISDIR ((statbuf).st_mode) ? (statbuf).st_blocks * ST_BLKSIZE(statbuf)/ST_NBLOCKSIZE : 0)
|
|
#endif /* _CRAY */
|
|
#endif /* not AIX PS/2 */
|
|
#endif /* !hpux */
|
|
#endif /* HAVE_STRUCT_STAT_ST_BLOCKS */
|
|
|
|
#ifndef ST_NBLOCKS
|
|
#define ST_NBLOCKS(statbuf) ((statbuf).st_blocks)
|
|
#endif
|
|
|
|
#ifndef ST_NBLOCKSIZE
|
|
#define ST_NBLOCKSIZE 512
|
|
#endif
|
|
|
|
//some GNU systems don't support big files for some reason
|
|
#ifdef __USE_LARGEFILE64 //see dirent.h
|
|
#define dirent dirent64
|
|
#define scandir scandir64
|
|
#define stat stat64
|
|
#define statstruct stat64
|
|
#define lstat lstat64
|
|
#define readdir readdir64
|
|
#endif
|
|
|
|
#ifndef NULL
|
|
#define NULL 0
|
|
#endif
|
|
|
|
|
|
#include <errno.h>
|
|
static void
|
|
outputError( TQCString path )
|
|
{
|
|
///show error message that stat or opendir may give
|
|
|
|
#define out( s ) error() << s ": " << path << endl; break
|
|
|
|
switch( errno ) {
|
|
case EACCES:
|
|
out( "Inadequate access permisions" );
|
|
case EMFILE:
|
|
out( "Too many file descriptors in use by Filelight" );
|
|
case ENFILE:
|
|
out( "Too many files are currently open in the system" );
|
|
case ENOENT:
|
|
out( "A component of the path does not exist, or the path is an empty string" );
|
|
case ENOMEM:
|
|
out( "Insufficient memory to complete the operation" );
|
|
case ENOTDIR:
|
|
out( "A component of the path is not a directory" );
|
|
case EBADF:
|
|
out( "Bad file descriptor" );
|
|
case EFAULT:
|
|
out( "Bad address" );
|
|
case ELOOP: //NOTE shouldn't ever happen
|
|
out( "Too many symbolic links encountered while traversing the path" );
|
|
case ENAMETOOLONG:
|
|
out( "File name too long" );
|
|
}
|
|
|
|
#undef out
|
|
}
|
|
|
|
Directory*
|
|
LocalLister::scan( const TQCString &path, const TQCString &dirname )
|
|
{
|
|
Directory *cwd = new Directory( dirname );
|
|
DIR *dir = opendir( path );
|
|
|
|
if( !dir ) {
|
|
outputError( path );
|
|
return cwd;
|
|
}
|
|
|
|
struct stat statbuf;
|
|
dirent *ent;
|
|
while ((ent = readdir( dir )))
|
|
{
|
|
if( ScanManager::s_abort )
|
|
return cwd;
|
|
|
|
if( qstrcmp( ent->d_name, "." ) == 0 || qstrcmp( ent->d_name, ".." ) == 0 )
|
|
continue;
|
|
|
|
TQCString new_path = path; new_path += ent->d_name;
|
|
|
|
//get file information
|
|
if( lstat( new_path, &statbuf ) == -1 ) {
|
|
outputError( new_path );
|
|
continue;
|
|
}
|
|
|
|
if( S_ISLNK( statbuf.st_mode ) ||
|
|
S_ISCHR( statbuf.st_mode ) ||
|
|
S_ISBLK( statbuf.st_mode ) ||
|
|
S_ISFIFO( statbuf.st_mode ) ||
|
|
S_ISSOCK( statbuf.st_mode ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if( S_ISREG( statbuf.st_mode ) ) //file
|
|
//using units of KiB as 32bit max is 4GiB and 64bit ints are expensive
|
|
cwd->append( ent->d_name, (ST_NBLOCKS( statbuf ) * ST_NBLOCKSIZE) / 1024 );
|
|
|
|
else if( S_ISDIR( statbuf.st_mode ) ) //directory
|
|
{
|
|
Directory *d = 0;
|
|
TQCString new_dirname = ent->d_name;
|
|
new_dirname += '/';
|
|
new_path += '/';
|
|
|
|
//check to see if we've scanned this section already
|
|
|
|
for( Iterator<Directory> it = m_trees->iterator(); it != m_trees->end(); ++it )
|
|
{
|
|
if( new_path == (*it)->name8Bit() )
|
|
{
|
|
debug() << "Tree pre-completed: " << (*it)->name() << "\n";
|
|
d = it.remove();
|
|
ScanManager::s_files += d->tqchildren();
|
|
//**** ideally don't have this redundant extra somehow
|
|
cwd->append( d, new_dirname );
|
|
}
|
|
}
|
|
|
|
if( !d ) //then scan
|
|
if ((d = scan( new_path, new_dirname ))) //then scan was successful
|
|
cwd->append( d );
|
|
}
|
|
|
|
++ScanManager::s_files;
|
|
}
|
|
|
|
closedir( dir );
|
|
|
|
return cwd;
|
|
}
|
|
|
|
bool
|
|
LocalLister::readMounts()
|
|
{
|
|
#define INFO_PARTITIONS "/proc/partitions"
|
|
#define INFO_MOUNTED_PARTITIONS "/etc/mtab" /* on Linux... */
|
|
|
|
//**** SHAMBLES
|
|
// ** mtab should have priority as mount points don't have to follow fstab
|
|
// ** no removable media detection
|
|
// ** no updates if mounts change
|
|
// ** you want a KDE extension that handles this for you really
|
|
|
|
struct fstab *fstab_ent;
|
|
#ifdef HAVE_MNTENT_H
|
|
struct mntent *mnt_ent;
|
|
#endif
|
|
TQString str;
|
|
|
|
|
|
#ifdef HAVE_MNTENT_H
|
|
FILE *fp;
|
|
if( setfsent() == 0 || !( fp = setmntent( INFO_MOUNTED_PARTITIONS, "r" ) ) )
|
|
#else
|
|
if( setfsent() == 0 )
|
|
#endif
|
|
return false;
|
|
|
|
#define FS_NAME fstab_ent->fs_spec // device-name
|
|
#define FS_FILE fstab_ent->fs_file // mount-point
|
|
#define FS_TYPE fstab_ent->fs_vfstype // fs-type
|
|
#define FS_MNTOPS fstab_ent->fs_mntops // mount-options
|
|
|
|
TQStringList remoteFsTypes;
|
|
remoteFsTypes << "smbfs" ;
|
|
#ifdef MNTTYPE_NFS
|
|
remoteFsTypes << MNTTYPE_NFS;
|
|
#else
|
|
remoteFsTypes << "nfs";
|
|
#endif
|
|
// What about afs?
|
|
|
|
while( (fstab_ent = getfsent()) != NULL )
|
|
{
|
|
str = TQString( FS_FILE );
|
|
if( str == "/" ) continue;
|
|
str += '/';
|
|
|
|
if( remoteFsTypes.contains( FS_TYPE ) )
|
|
s_remoteMounts.append( str ); //**** NO! can't be sure won't have trailing slash, need to do a check first dummy!!
|
|
|
|
else
|
|
s_localMounts.append( str ); //**** NO! can't be sure won't have trailing slash, need to do a check first dummy!!
|
|
|
|
kdDebug() << "FSTAB: " << FS_TYPE << "\n";
|
|
}
|
|
|
|
endfsent(); /* close fstab.. */
|
|
|
|
#undef FS_NAME
|
|
#undef FS_FILE
|
|
#undef FS_TYPE
|
|
#undef FS_MNTOPS
|
|
|
|
#define FS_NAME mnt_ent->mnt_fsname // device-name
|
|
#define FS_FILE mnt_ent->mnt_dir // mount-point
|
|
#define FS_TYPE mnt_ent->mnt_type // fs-type
|
|
#define FS_MNTOPS mnt_ent->mnt_opts // mount-options
|
|
|
|
//scan mtab, **** mtab should take priority, but currently it isn't
|
|
|
|
#ifdef HAVE_MNTENT_H
|
|
while( ( mnt_ent = getmntent( fp ) ) != NULL )
|
|
{
|
|
bool b = false;
|
|
|
|
str = TQString( FS_FILE );
|
|
if( str == "/" ) continue;
|
|
str += "/";
|
|
|
|
if( remoteFsTypes.contains( FS_TYPE ) )
|
|
if( b = !s_remoteMounts.contains( str ) )
|
|
s_remoteMounts.append( str ); //**** NO! can't be sure won't have trailing slash, need to do a check first dummy!!
|
|
|
|
else if( b = !s_localMounts.contains( str ) )
|
|
s_localMounts.append( str ); //**** NO! can't be sure won't have trailing slash, need to do a check first dummy!!
|
|
|
|
if( b ) kdDebug() << "MTAB: " << FS_TYPE << "\n";
|
|
}
|
|
|
|
endmntent( fp ); /* close mtab.. */
|
|
#endif
|
|
|
|
|
|
return true;
|
|
}
|
|
}
|