1 /* nwc_fsentry.cc
2  * This file belongs to Worker, a file manager for UN*X/X11.
3  * Copyright (C) 2006-2019 Ralf Hoffmann.
4  * You can contact me at: ralf@boomerangsworld.de
5  *   or http://www.boomerangsworld.de/worker
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20  */
21 
22 #include "nwc_fsentry.hh"
23 #include "grouphash.h"
24 #include "aguix/util.h"
25 #include <string.h>  //memcpy
26 #include "nwc_path.hh"
27 #include "nwc_os.hh"
28 #include "aguix/lowlevelfunc.h"
29 
30 namespace NWC
31 {
32 
FSEntry(const std::string & fullname)33   FSEntry::FSEntry( const std::string &fullname )
34   {
35     _fullname = fullname;
36     m_basename_calculated = false;
37     _exists = false;
38 
39     memset( &_statbuf, 0, sizeof( _statbuf ) );
40     memset( &_dest_statbuf, 0, sizeof( _dest_statbuf ) );
41     _is_link = false;
42     _is_corrupt = false;
43 
44     if ( _fullname[0] == '/' ) {
45       worker_struct_stat stbuf;
46 
47       if ( worker_lstat( _fullname.c_str(), &stbuf ) == 0 ) {
48         _statbuf.size = stbuf.st_size;
49         _statbuf.lastaccess = stbuf.st_atime;
50         _statbuf.lastmod = stbuf.st_mtime;
51         _statbuf.lastchange = stbuf.st_ctime;
52         _statbuf.mode = stbuf.st_mode;
53         _statbuf.userid = stbuf.st_uid;
54         _statbuf.groupid = stbuf.st_gid;
55         _statbuf.inode = stbuf.st_ino;
56         _statbuf.nlink = stbuf.st_nlink;
57         _statbuf.blocks = stbuf.st_blocks;
58         _statbuf.rdev = stbuf.st_rdev;
59         _statbuf.dev = stbuf.st_dev;
60 
61         _exists = true;
62       }
63 
64       if ( S_ISLNK( _statbuf.mode ) ) {
65         _is_link = true;
66 
67         if ( worker_stat( _fullname.c_str(), &stbuf ) == 0 ) {
68           _dest_statbuf.size = stbuf.st_size;
69           _dest_statbuf.lastaccess = stbuf.st_atime;
70           _dest_statbuf.lastmod = stbuf.st_mtime;
71           _dest_statbuf.lastchange = stbuf.st_ctime;
72           _dest_statbuf.mode = stbuf.st_mode;
73           _dest_statbuf.userid = stbuf.st_uid;
74           _dest_statbuf.groupid = stbuf.st_gid;
75           _dest_statbuf.inode = stbuf.st_ino;
76           _dest_statbuf.nlink = stbuf.st_nlink;
77           _dest_statbuf.blocks = stbuf.st_blocks;
78           _dest_statbuf.rdev = stbuf.st_rdev;
79           _dest_statbuf.dev = stbuf.st_dev;
80         } else {
81           /* corrupt */
82           _is_corrupt = true;
83           _dest_statbuf.mode = 0;  // to prevent corrupted links treated as dirs
84           if ( errno == ENOENT ) {
85             // Eventl. fuer spaetere Betrachtungen
86           }
87         }
88       }
89     }
90   }
91 
FSEntry(const FSEntry & other)92   FSEntry::FSEntry( const FSEntry &other )
93   {
94     _fullname = other._fullname;
95     m_basename = other.m_basename;
96     m_basename_calculated = other.m_basename_calculated;
97     _exists = other._exists;
98     memcpy( &_statbuf, &other._statbuf, sizeof( other._statbuf ) );
99     memcpy( &_dest_statbuf, &other._dest_statbuf, sizeof( other._dest_statbuf ) );
100     _is_link = other._is_link;
101     _is_corrupt = other._is_corrupt;
102   }
103 
~FSEntry()104   FSEntry::~FSEntry()
105   {
106   }
107 
isDir(bool follow_symlinks) const108   bool FSEntry::isDir( bool follow_symlinks ) const
109   {
110     bool is_dir = false;
111 
112     if ( follow_symlinks == true && _is_link == true && _is_corrupt == false ) {
113         if ( S_ISDIR( _dest_statbuf.mode ) ) is_dir = true;
114     } else if ( S_ISDIR( _statbuf.mode ) ) is_dir = true;
115 
116     return is_dir;
117   }
118 
getFullname() const119   const std::string &FSEntry::getFullname() const
120   {
121     return _fullname;
122   }
123 
entryExists() const124   bool FSEntry::entryExists() const
125   {
126     return _exists;
127   }
128 
clone() const129   FSEntry *FSEntry::clone() const
130   {
131     return new FSEntry( *this );
132   }
133 
getBasename() const134   const std::string &FSEntry::getBasename() const
135   {
136       if ( m_basename_calculated ) return m_basename;
137 
138       const std::string &fn = getFullname();
139 
140       m_basename_calculated = true;
141 
142       std::string::size_type pos = fn.rfind( "/" );
143       if ( pos == std::string::npos ) {
144           m_basename = fn;
145           return fn;
146       }
147 
148       m_basename = std::string( fn, pos + 1 );
149 
150       return m_basename;
151   }
152 
getDirname() const153   std::string FSEntry::getDirname() const
154   {
155       const std::string &fn = getFullname();
156 
157       return NWC::Path::dirname( fn );
158   }
159 
getExtension() const160   std::string FSEntry::getExtension() const
161   {
162       const std::string &bn = getBasename();
163 
164       std::string::size_type pos = bn.rfind( "." );
165       if ( pos == std::string::npos ) {
166           return std::string( "" );
167       }
168 
169       return std::string( bn, pos + 1, std::string::npos );
170   }
171 
isReg(bool follow_symlinks) const172   bool FSEntry::isReg( bool follow_symlinks ) const
173   {
174     bool is_reg = false;
175 
176     if ( _exists == true ) {
177       if ( follow_symlinks == true && _is_link == true && _is_corrupt == false ) {
178         if ( S_ISREG( _dest_statbuf.mode ) ) is_reg = true;
179       } else if ( S_ISREG( _statbuf.mode ) ) is_reg = true;
180     }
181     return is_reg;
182   }
183 
isLink() const184     bool FSEntry::isLink() const
185     {
186         return _is_link;
187     }
188 
stat_size() const189     loff_t FSEntry::stat_size() const
190     {
191         return _statbuf.size;
192     }
193 
stat_dest_size() const194     loff_t FSEntry::stat_dest_size() const
195     {
196         return _dest_statbuf.size;
197     }
198 
stat_dev() const199     dev_t FSEntry::stat_dev() const
200     {
201         return _statbuf.dev;
202     }
203 
stat_dest_dev() const204     dev_t FSEntry::stat_dest_dev() const
205     {
206         return _dest_statbuf.dev;
207     }
208 
stat_inode() const209     ino_t FSEntry::stat_inode() const
210     {
211         return _statbuf.inode;
212     }
213 
stat_dest_inode() const214     ino_t FSEntry::stat_dest_inode() const
215     {
216         return _dest_statbuf.inode;
217     }
218 
stat_lastmod() const219     time_t FSEntry::stat_lastmod() const
220     {
221         return _statbuf.lastmod;
222     }
223 
stat_dest_lastmod() const224     time_t FSEntry::stat_dest_lastmod() const
225     {
226         return _dest_statbuf.lastmod;
227     }
228 
stat_rdev() const229     dev_t FSEntry::stat_rdev() const
230     {
231         return _statbuf.rdev;
232     }
233 
stat_dest_rdev() const234     dev_t FSEntry::stat_dest_rdev() const
235     {
236         return _dest_statbuf.rdev;
237     }
238 
stat_mode() const239     mode_t FSEntry::stat_mode() const
240     {
241         return _statbuf.mode;
242     }
243 
stat_dest_mode() const244     mode_t FSEntry::stat_dest_mode() const
245     {
246         return _dest_statbuf.mode;
247     }
248 
stat_lastaccess() const249     time_t FSEntry::stat_lastaccess() const
250     {
251         return _statbuf.lastaccess;
252     }
253 
stat_dest_lastaccess() const254     time_t FSEntry::stat_dest_lastaccess() const
255     {
256         return _dest_statbuf.lastaccess;
257     }
258 
stat_lastchange() const259     time_t FSEntry::stat_lastchange() const
260     {
261         return _statbuf.lastchange;
262     }
263 
stat_dest_lastchange() const264     time_t FSEntry::stat_dest_lastchange() const
265     {
266         return _dest_statbuf.lastchange;
267     }
268 
stat_nlink() const269     nlink_t FSEntry::stat_nlink() const
270     {
271         return _statbuf.nlink;
272     }
273 
stat_dest_nlink() const274     nlink_t FSEntry::stat_dest_nlink() const
275     {
276         return _dest_statbuf.nlink;
277     }
278 
stat_userid() const279     uid_t FSEntry::stat_userid() const
280     {
281         return _statbuf.userid;
282     }
283 
stat_dest_userid() const284     uid_t FSEntry::stat_dest_userid() const
285     {
286         return _dest_statbuf.userid;
287     }
288 
stat_groupid() const289     gid_t FSEntry::stat_groupid() const
290     {
291         return _statbuf.groupid;
292     }
293 
stat_dest_groupid() const294     gid_t FSEntry::stat_dest_groupid() const
295     {
296         return _dest_statbuf.groupid;
297     }
298 
stat_blocks() const299     loff_t FSEntry::stat_blocks() const
300     {
301         return _statbuf.blocks;
302     }
303 
stat_dest_blocks() const304     loff_t FSEntry::stat_dest_blocks() const
305     {
306         return _dest_statbuf.blocks;
307     }
308 
isHiddenEntry() const309     bool FSEntry::isHiddenEntry() const
310     {
311         if ( AGUIXUtils::starts_with( getBasename(), "." ) ) return true;
312 
313         return false;
314     }
315 
isBrokenLink() const316     bool FSEntry::isBrokenLink() const
317     {
318         return _is_link && _is_corrupt;
319     }
320 
isExe(bool fastTest) const321     bool FSEntry::isExe( bool fastTest ) const
322     {
323         if ( fastTest == true ) {
324             if ( ( _statbuf.mode & S_IXUSR ) == S_IXUSR ) return true;
325             if ( ( _statbuf.mode & S_IXGRP ) == S_IXGRP ) return true;
326             if ( ( _statbuf.mode & S_IXOTH ) == S_IXOTH ) return true;
327         } else {
328             uid_t uid;
329             gid_t gid;
330             mode_t m;
331             bool e = false;
332 
333             if ( isLink() == false ) {
334                 uid = stat_userid();
335                 gid = stat_groupid();
336                 m = stat_mode();
337             } else if ( ! isBrokenLink() ) {
338                 uid = stat_dest_userid();
339                 gid = stat_dest_groupid();
340                 m = stat_dest_mode();
341             } else {
342                 // corrupt symlink is not executable
343                 return false;
344             }
345 
346             if ( uid == geteuid() ) {
347                 e = ( ( m & S_IXUSR ) != 0 ) ? true : false;
348             } else if ( ugdb->isInGroup( gid ) == true ) {
349                 e = ( ( m & S_IXGRP ) != 0 ) ? true : false;
350             } else {
351                 e = ( ( m & S_IXOTH ) != 0 ) ? true : false;
352             }
353             return e;
354         }
355         return false;
356     }
357 
getPermissionString(std::string & res) const358     bool FSEntry::getPermissionString( std::string &res ) const
359     {
360         char str[11];
361 
362         if ( isLink() == true ) str[0] = 'l';
363         else {
364             if ( isBrokenLink() ) str[0] = '?';
365             else if ( S_ISDIR( _statbuf.mode ) ) str[0] = 'd';
366             else if ( S_ISFIFO( _statbuf.mode ) ) str[0] = 'p';
367             else if ( S_ISSOCK( _statbuf.mode ) ) str[0] = 's';
368             else if ( S_ISCHR( _statbuf.mode ) ) str[0] = 'c';
369             else if ( S_ISBLK( _statbuf.mode ) ) str[0] = 'b';
370             else str[0] = '-';
371         }
372         if ( ( _statbuf.mode & S_IRUSR ) == S_IRUSR ) str[1] = 'r'; else str[1] = '-';
373         if ( ( _statbuf.mode & S_IWUSR ) == S_IWUSR ) str[2] = 'w'; else str[2] = '-';
374         if ( ( _statbuf.mode & S_ISUID ) == S_ISUID ) {
375             if ( ( _statbuf.mode & S_IXUSR ) == S_IXUSR ) str[3] = 's'; else str[3] = 'S';
376         } else {
377             if ( ( _statbuf.mode & S_IXUSR ) == S_IXUSR ) str[3] = 'x'; else str[3] = '-';
378         }
379         if ( ( _statbuf.mode & S_IRGRP ) == S_IRGRP ) str[4] = 'r'; else str[4] = '-';
380         if ( ( _statbuf.mode & S_IWGRP ) == S_IWGRP ) str[5] = 'w'; else str[5] = '-';
381         if ( ( _statbuf.mode & S_ISGID ) == S_ISGID ) {
382             if ( ( _statbuf.mode & S_IXGRP ) == S_IXGRP ) str[6] = 's'; else str[6] = 'S';
383         } else {
384             if ( ( _statbuf.mode & S_IXGRP ) == S_IXGRP ) str[6] = 'x'; else str[6] = '-';
385         }
386         if ( ( _statbuf.mode & S_IROTH ) == S_IROTH ) str[7] = 'r'; else str[7] = '-';
387         if ( ( _statbuf.mode & S_IWOTH ) == S_IWOTH ) str[8] = 'w'; else str[8] = '-';
388         if ( ( _statbuf.mode & S_ISVTX ) == S_ISVTX ) {
389             if ( ( _statbuf.mode & S_IXOTH ) == S_IXOTH ) str[9] = 't'; else str[9] = 'T';
390         } else {
391             if ( ( _statbuf.mode & S_IXOTH ) == S_IXOTH ) str[9] = 'x'; else str[9] = '-';
392         }
393         str[10] = 0;
394 
395         res += str;
396 
397         return true;
398     }
399 
getDestination(std::string & res) const400     bool FSEntry::getDestination( std::string &res ) const
401     {
402         char *buf;
403 
404         if ( ! isLink() ) return false;
405 
406         buf = NWC::OS::getLinkTarget( _fullname.c_str() );
407 
408         if ( buf ) {
409             res += buf;
410             _freesafe( buf );
411 
412             return true;
413         }
414 
415         return false;
416     }
417 
changeBasename(const std::string & newbasename)418     int FSEntry::changeBasename( const std::string &newbasename )
419     {
420         std::string newfullname = Path::join( Path::dirname( _fullname ),
421                                               Path::basename( newbasename ) );
422 
423 
424         m_basename = Path::basename( newbasename );
425         _fullname = newfullname;
426         m_basename_calculated = true;
427 
428         return 0;
429     }
430 
updateStats()431     int FSEntry::updateStats()
432     {
433         _exists = false;
434 
435         _is_link = false;
436         _is_corrupt = false;
437 
438         if ( _fullname[0] == '/' ) {
439             worker_struct_stat stbuf;
440 
441             if ( worker_lstat( _fullname.c_str(), &stbuf ) == 0 ) {
442                 _statbuf.size = stbuf.st_size;
443                 _statbuf.lastaccess = stbuf.st_atime;
444                 _statbuf.lastmod = stbuf.st_mtime;
445                 _statbuf.lastchange = stbuf.st_ctime;
446                 _statbuf.userid = stbuf.st_uid;
447                 _statbuf.groupid = stbuf.st_gid;
448                 _statbuf.inode = stbuf.st_ino;
449                 _statbuf.nlink = stbuf.st_nlink;
450                 _statbuf.blocks = stbuf.st_blocks;
451                 _statbuf.rdev = stbuf.st_rdev;
452                 _statbuf.dev = stbuf.st_dev;
453 
454                 // only apply access mode changes to keep type of entry
455                 _statbuf.mode &= ~(S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO);
456                 _statbuf.mode |= stbuf.st_mode & (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO);
457 
458                 _exists = true;
459             }
460 
461             if ( S_ISLNK( _statbuf.mode ) ) {
462                 _is_link = true;
463 
464                 if ( worker_stat( _fullname.c_str(), &stbuf ) == 0 ) {
465                     _dest_statbuf.size = stbuf.st_size;
466                     _dest_statbuf.lastaccess = stbuf.st_atime;
467                     _dest_statbuf.lastmod = stbuf.st_mtime;
468                     _dest_statbuf.lastchange = stbuf.st_ctime;
469                     _dest_statbuf.userid = stbuf.st_uid;
470                     _dest_statbuf.groupid = stbuf.st_gid;
471                     _dest_statbuf.inode = stbuf.st_ino;
472                     _dest_statbuf.nlink = stbuf.st_nlink;
473                     _dest_statbuf.blocks = stbuf.st_blocks;
474                     _dest_statbuf.rdev = stbuf.st_rdev;
475                     _dest_statbuf.dev = stbuf.st_dev;
476 
477                     _dest_statbuf.mode &= ~(S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO);
478                     _dest_statbuf.mode |= stbuf.st_mode & (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO);
479                 } else {
480                     /* corrupt */
481                     _is_corrupt = true;
482                     _dest_statbuf.mode = 0;  // to prevent corrupted links treated as dirs
483                     if ( errno == ENOENT ) {
484                         // Eventl. fuer spaetere Betrachtungen
485                     }
486                 }
487             }
488         }
489 
490         return 0;
491     }
492 
resetToDummy()493     void FSEntry::resetToDummy()
494     {
495         memset( &_dest_statbuf, 0, sizeof( _dest_statbuf ) );
496 
497         _statbuf.lastaccess = 0;
498         _statbuf.lastmod = 0;
499         _statbuf.lastchange = 0;
500         _statbuf.userid = 0;
501         _statbuf.groupid = 0;
502         _statbuf.inode = 0;
503         _statbuf.nlink = 2;
504         _statbuf.rdev = 0;
505         _statbuf.dev = 0;
506 
507         _is_link = false;
508         _is_corrupt = false;
509         _exists = true;
510     }
511 }
512