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