File indexing completed on 2024-05-05 04:51:37
0001 /* 0002 SPDX-FileCopyrightText: 2003-2009 Sebastian Trueg <trueg@k3b.org> 0003 SPDX-FileCopyrightText: 2009 Michal Malek <michalm@jabster.pl> 0004 SPDX-FileCopyrightText: 1998-2009 Sebastian Trueg <trueg@k3b.org> 0005 SPDX-FileCopyrightText: 2009 Michal Malek <michalm@jabster.pl> 0006 0007 SPDX-License-Identifier: GPL-2.0-or-later 0008 */ 0009 0010 #include "k3bexternalbinpermissionmodel.h" 0011 #include "k3bexternalbinmanager.h" 0012 #include "k3bdefaultexternalprograms.h" 0013 #include "k3bglobals.h" 0014 0015 #include <KLocalizedString> 0016 0017 #include <QDebug> 0018 #include <QFile> 0019 #include <QFileInfo> 0020 #include <QList> 0021 #include <QSet> 0022 0023 #include <sys/stat.h> 0024 0025 0026 namespace { 0027 0028 bool shouldRunSuidRoot( const K3b::ExternalBin* bin ) 0029 { 0030 // 0031 // Since kernel 2.6.8 older cdrecord versions are not able to use the SCSI subsystem when running suid root anymore 0032 // So for we ignore the suid root issue with kernel >= 2.6.8 and cdrecord < 2.01.01a02 0033 // 0034 // Some kernel version 2.6.16.something again introduced a problem here. Since I do not know the exact version 0035 // and a workaround was introduced in cdrecord 2.01.01a05 just use that version as the first for suid root. 0036 // 0037 // Seems as if cdrdao never had problems with suid root... 0038 // 0039 0040 if( bin->name() == "cdrecord" ) { 0041 return ( K3b::simpleKernelVersion() < K3b::Version( 2, 6, 8 ) || 0042 bin->version() >= K3b::Version( 2, 1, 1, "a05" ) || 0043 bin->hasFeature( "wodim" ) ); 0044 } 0045 else if( bin->name() == "cdrdao" ) { 0046 return true; 0047 } 0048 else if( bin->name() == "growisofs" ) { 0049 // 0050 // starting with 6.0 growiofs raises it's priority using nice(-20) 0051 // BUT: newer kernels have ridiculously low default memorylocked resource limit, which prevents privileged 0052 // users from starting growisofs 6.0 with "unable to anonymously mmap 33554432: Resource temporarily unavailable" 0053 // error message. Until Andy releases a version including a workaround we simply never configure growisofs suid root 0054 return false; // bin->version >= K3b::Version( 6, 0 ); 0055 } 0056 else 0057 return false; 0058 } 0059 0060 } // namespace 0061 0062 namespace K3b { 0063 0064 class ExternalBinPermissionModel::Private 0065 { 0066 public: 0067 explicit Private(ExternalBinManager const& ebm) : externalBinManager(ebm) {} 0068 ExternalBinManager const& externalBinManager; 0069 QString burningGroup; 0070 QList<const ExternalBin*> programs; 0071 QSet<const ExternalBin*> selectedPrograms; 0072 0073 void buildProgramList(); 0074 bool getProgramInfo( const ExternalBin* program, 0075 QString& owner, QString& group, QString& wantedGroup, 0076 int& perm, int& wantedPerm ) const; 0077 bool needChangePermissions( const ExternalBin* program ) const; 0078 }; 0079 0080 0081 void ExternalBinPermissionModel::Private::buildProgramList() 0082 { 0083 programs.clear(); 0084 const QMap<QString, ExternalProgram*>& map = externalBinManager.programs(); 0085 for( QMap<QString, ExternalProgram*>::const_iterator it = map.constBegin(); it != map.constEnd(); ++it ) { 0086 if (it.key() == "cdrecord" || 0087 it.key() == "cdrdao" || 0088 it.key() == "growisofs") { 0089 programs += it.value()->bins(); 0090 } 0091 } 0092 selectedPrograms = QSet<const ExternalBin *>(programs.begin(), programs.end()); 0093 } 0094 0095 0096 bool ExternalBinPermissionModel::Private::getProgramInfo( const ExternalBin* program, 0097 QString& owner, QString& group, QString& wantedGroup, 0098 int& perm, int& wantedPerm ) const 0099 { 0100 // we need the uid bit which is not supported by QFileInfo 0101 struct stat s; 0102 if( ::stat( QFile::encodeName(program->path()), &s ) == 0 ) { 0103 0104 QFileInfo fi( program->path() ); 0105 owner = fi.owner(); 0106 group = fi.group(); 0107 perm = s.st_mode & 0007777; 0108 0109 if( !burningGroup.isEmpty() && burningGroup != "root" ) 0110 wantedGroup = burningGroup; 0111 else 0112 wantedGroup = "root"; 0113 0114 if( shouldRunSuidRoot( program ) ) { 0115 if( wantedGroup != "root" ) 0116 wantedPerm = 0004710; 0117 else 0118 wantedPerm = 0004711; 0119 } 0120 else { 0121 if( wantedGroup != "root" ) 0122 wantedPerm = 0000750; 0123 else 0124 wantedPerm = 0000755; 0125 } 0126 0127 return true; 0128 } 0129 else { 0130 qDebug() << "(ExternalBinPermissionModel) unable to stat " << program->path(); 0131 return false; 0132 } 0133 } 0134 0135 0136 bool ExternalBinPermissionModel::Private::needChangePermissions( const ExternalBin* program ) const 0137 { 0138 QString owner, group, wantedGroup; 0139 int perm, wantedPerm; 0140 0141 if( getProgramInfo( program, owner, group, wantedGroup, perm, wantedPerm ) ) 0142 return( perm != wantedPerm || owner != "root" || group != wantedGroup ); 0143 else 0144 return false; 0145 } 0146 0147 0148 ExternalBinPermissionModel::ExternalBinPermissionModel(ExternalBinManager const& externalBinManager, QObject* parent) 0149 : 0150 QAbstractItemModel( parent ), 0151 d( new Private( externalBinManager ) ) 0152 { 0153 d->buildProgramList(); 0154 } 0155 0156 0157 ExternalBinPermissionModel::~ExternalBinPermissionModel() 0158 { 0159 delete d; 0160 } 0161 0162 0163 QList<HelperProgramItem> ExternalBinPermissionModel::selectedPrograms() const 0164 { 0165 QList<HelperProgramItem> selectedPrograms; 0166 Q_FOREACH( const ExternalBin* program, d->selectedPrograms ) 0167 { 0168 if( d->needChangePermissions( program ) ) 0169 selectedPrograms << HelperProgramItem( program->path(), shouldRunSuidRoot( program ) ); 0170 } 0171 return selectedPrograms; 0172 } 0173 0174 0175 bool ExternalBinPermissionModel::changesNeeded() const 0176 { 0177 return !selectedPrograms().isEmpty(); 0178 } 0179 0180 0181 QStringList ExternalBinPermissionModel::searchPaths() const 0182 { 0183 return d->externalBinManager.searchPath(); 0184 } 0185 0186 0187 const QString& ExternalBinPermissionModel::burningGroup() const 0188 { 0189 return d->burningGroup; 0190 } 0191 0192 0193 const ExternalBin* ExternalBinPermissionModel::programForIndex( const QModelIndex& index ) const 0194 { 0195 if( index.isValid() ) 0196 return static_cast<const ExternalBin*>( index.internalPointer() ); 0197 else 0198 return 0; 0199 } 0200 0201 0202 QModelIndex ExternalBinPermissionModel::indexForProgram( const ExternalBin* program ) const 0203 { 0204 if( program != 0 && !d->programs.isEmpty() ) { 0205 int row = d->programs.indexOf( program ); 0206 return createIndex( row, 0, const_cast<ExternalBin*>( program ) ); 0207 } 0208 else 0209 return QModelIndex(); 0210 } 0211 0212 0213 QVariant ExternalBinPermissionModel::data( const QModelIndex& index, int role ) const 0214 { 0215 if( const ExternalBin* program = programForIndex( index ) ) { 0216 if( role == Qt::DisplayRole ) { 0217 if( index.column() == ProgramColumn ) { 0218 return program->path(); 0219 } else { 0220 QString owner, group, wantedGroup; 0221 int perm, wantedPerm; 0222 0223 if( d->getProgramInfo( program, owner, group, wantedGroup, perm, wantedPerm ) ) { 0224 0225 if( index.column() == PermissionsColumn ) { 0226 return QString(QString::number( perm, 8 ).rightJustified( 4, '0' ) + ' ' + owner + '.' + group); 0227 } else if ( index.column() == NewPermissionsColumn ) { 0228 if( perm != wantedPerm || owner != "root" || group != wantedGroup ) 0229 return QString("%1 root.%2").arg(wantedPerm,0,8).arg(wantedGroup); 0230 else 0231 return i18n("no change"); 0232 } 0233 } 0234 } 0235 } 0236 else if( role == Qt::CheckStateRole && index.column() == ProgramColumn && d->needChangePermissions( program ) ) { 0237 if( d->selectedPrograms.contains( program ) ) 0238 return Qt::Checked; 0239 else 0240 return Qt::Unchecked; 0241 } 0242 } 0243 return QVariant(); 0244 } 0245 0246 0247 bool ExternalBinPermissionModel::setData( const QModelIndex& index, const QVariant& value, int role ) 0248 { 0249 if( role == Qt::CheckStateRole ) { 0250 if( const ExternalBin* program = programForIndex( index ) ) { 0251 if( value.toInt() == Qt::Unchecked && d->selectedPrograms.contains( program ) ) { 0252 d->selectedPrograms.remove( program ); 0253 emit dataChanged( index, index ); 0254 return true; 0255 } 0256 else if( value.toInt() == Qt::Checked && !d->selectedPrograms.contains( program ) ) { 0257 d->selectedPrograms.insert( program ); 0258 emit dataChanged( index, index ); 0259 return true; 0260 } 0261 } 0262 } 0263 return false; 0264 } 0265 0266 0267 Qt::ItemFlags ExternalBinPermissionModel::flags( const QModelIndex& index ) const 0268 { 0269 if( const ExternalBin* program = programForIndex( index ) ) 0270 { 0271 if( index.column() == ProgramColumn && d->needChangePermissions( program ) ) 0272 return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable; 0273 else 0274 return Qt::ItemIsEnabled | Qt::ItemIsSelectable; 0275 } 0276 else 0277 return Qt::ItemFlags(); 0278 } 0279 0280 0281 QVariant ExternalBinPermissionModel::headerData( int section, Qt::Orientation orientation, int role ) const 0282 { 0283 if( orientation == Qt::Horizontal && role == Qt::DisplayRole ) { 0284 switch( section ) 0285 { 0286 case ProgramColumn: return i18n( "Program" ); 0287 case PermissionsColumn: return i18n( "Permissions" ); 0288 case NewPermissionsColumn: return i18n( "New permissions" ); 0289 default: return QVariant(); 0290 } 0291 } 0292 else 0293 return QVariant(); 0294 } 0295 0296 0297 QModelIndex ExternalBinPermissionModel::index( int row, int column, const QModelIndex& parent ) const 0298 { 0299 if( hasIndex(row, column, parent) && !parent.isValid() ) { 0300 const ExternalBin* program = d->programs.at( row ); 0301 if( program != 0 ) 0302 return createIndex( row, column, const_cast<ExternalBin*>( program ) ); 0303 else 0304 return QModelIndex(); 0305 } 0306 else 0307 return QModelIndex(); 0308 } 0309 0310 0311 QModelIndex ExternalBinPermissionModel::parent( const QModelIndex& index ) const 0312 { 0313 Q_UNUSED( index ); 0314 return QModelIndex(); 0315 } 0316 0317 0318 int ExternalBinPermissionModel::rowCount( const QModelIndex& parent ) const 0319 { 0320 if( !parent.isValid() ) 0321 return d->programs.size(); 0322 else 0323 return 0; 0324 } 0325 0326 0327 int ExternalBinPermissionModel::columnCount( const QModelIndex& parent ) const 0328 { 0329 Q_UNUSED( parent ); 0330 return NumColumns; 0331 } 0332 0333 0334 QModelIndex ExternalBinPermissionModel::buddy( const QModelIndex& index ) const 0335 { 0336 if( programForIndex( index ) != 0 ) 0337 return ExternalBinPermissionModel::index( index.row(), ProgramColumn, index.parent() ); 0338 else 0339 return index; 0340 } 0341 0342 void ExternalBinPermissionModel::setBurningGroup( const QString& burningGroup ) 0343 { 0344 if( burningGroup != d->burningGroup ) { 0345 beginResetModel(); 0346 d->burningGroup = burningGroup; 0347 0348 // Remove from the selected list all programs 0349 // whose permissions don't need to be changed anymore 0350 for( QSet<const ExternalBin*>::iterator program = d->selectedPrograms.begin(); 0351 program != d->selectedPrograms.end(); ) 0352 { 0353 if( !d->needChangePermissions( *program ) ) 0354 program = d->selectedPrograms.erase( program ); 0355 else 0356 ++program; 0357 } 0358 endResetModel(); 0359 } 0360 } 0361 0362 void ExternalBinPermissionModel::update() 0363 { 0364 beginResetModel(); 0365 d->buildProgramList(); 0366 d->selectedPrograms.intersect( QSet<const ExternalBin *>(d->programs.begin(), d->programs.end() )); 0367 0368 endResetModel(); 0369 } 0370 0371 } // namespace K3b 0372 0373 #include "moc_k3bexternalbinpermissionmodel.cpp"