summaryrefslogtreecommitdiffstats
path: root/src/part/localLister.cpp
blob: 7e0fb8a9327c46142f5e2b7d238e3c1420f9cf4e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
//Author:    Max Howell <max.howell@methylblue.com>, (C) 2003-4
//Copyright: See COPYING file that comes with this distribution

#include "../../config.h"
#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 *parent )
      : TQThread()
      , m_path( path )
      , m_trees( cachedTrees )
      , m_parent( parent )
   {
      //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->children();
                  //**** 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;
   }
}