KDECore
kmountpoint.cpp
Go to the documentation of this file.
00001 /* 00002 * This file is part of the KDE libraries 00003 * Copyright (c) 2003 Waldo Bastian <bastian@kde.org> 00004 * 2007 David Faure <faure@kde.org> 00005 * 00006 * This library is free software; you can redistribute it and/or 00007 * modify it under the terms of the GNU Library General Public 00008 * License version 2 as published by the Free Software Foundation. 00009 * 00010 * This library is distributed in the hope that it will be useful, 00011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00013 * Library General Public License for more details. 00014 * 00015 * You should have received a copy of the GNU Library General Public License 00016 * along with this library; see the file COPYING.LIB. If not, write to 00017 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00018 * Boston, MA 02110-1301, USA. 00019 */ 00020 00021 #include "kmountpoint.h" 00022 00023 #include <config.h> 00024 #include <stdlib.h> 00025 00026 #include <QtCore/QFile> 00027 #include <QtCore/QTextIStream> 00028 00029 #include "kstandarddirs.h" 00030 00031 #ifdef Q_WS_WIN 00032 #include <windows.h> 00033 #include <QDir> 00034 #endif 00035 00036 #ifdef HAVE_VOLMGT 00037 #include <volmgt.h> 00038 #endif 00039 #ifdef HAVE_SYS_MNTTAB_H 00040 #include <sys/mnttab.h> 00041 #endif 00042 #ifdef HAVE_MNTENT_H 00043 #include <mntent.h> 00044 #elif defined(HAVE_SYS_MNTENT_H) 00045 #include <sys/mntent.h> 00046 #endif 00047 00048 // This is the *BSD branch 00049 #ifdef HAVE_SYS_MOUNT_H 00050 #ifdef HAVE_SYS_TYPES_H 00051 #include <sys/types.h> 00052 #endif 00053 #ifdef HAVE_SYS_PARAM_H 00054 #include <sys/param.h> 00055 #endif 00056 #include <sys/mount.h> 00057 #endif 00058 00059 #ifdef HAVE_FSTAB_H 00060 #include <fstab.h> 00061 #endif 00062 #if defined(_AIX) 00063 #include <sys/mntctl.h> 00064 #include <sys/vmount.h> 00065 #include <sys/vfs.h> 00066 /* AIX does not prototype mntctl anywhere that I can find */ 00067 #ifndef mntctl 00068 extern "C" int mntctl(int command, int size, void* buffer); 00069 #endif 00070 extern "C" struct vfs_ent *getvfsbytype(int vfsType); 00071 extern "C" void endvfsent( ); 00072 #endif 00073 00074 00075 #ifndef HAVE_GETMNTINFO 00076 # ifdef _PATH_MOUNTED 00077 // On some Linux, MNTTAB points to /etc/fstab ! 00078 # undef MNTTAB 00079 # define MNTTAB _PATH_MOUNTED 00080 # else 00081 # ifndef MNTTAB 00082 # ifdef MTAB_FILE 00083 # define MNTTAB MTAB_FILE 00084 # else 00085 # define MNTTAB "/etc/mnttab" 00086 # endif 00087 # endif 00088 # endif 00089 #endif 00090 00091 #include "kdebug.h" 00092 00093 00094 #ifdef _OS_SOLARIS_ 00095 #define FSTAB "/etc/vfstab" 00096 #else 00097 #define FSTAB "/etc/fstab" 00098 #endif 00099 00100 class KMountPoint::Private { 00101 public: 00102 void finalizePossibleMountPoint(DetailsNeededFlags infoNeeded); 00103 void finalizeCurrentMountPoint(DetailsNeededFlags infoNeeded); 00104 00105 QString mountedFrom; 00106 QString device; // Only available when the NeedRealDeviceName flag was set. 00107 QString mountPoint; 00108 QString mountType; 00109 QStringList mountOptions; 00110 }; 00111 00112 KMountPoint::KMountPoint() 00113 :d( new Private ) 00114 { 00115 } 00116 00117 KMountPoint::~KMountPoint() 00118 { 00119 delete d; 00120 } 00121 00122 // There are (at least) four kind of APIs: 00123 // setmntent + getmntent + struct mntent (linux...) 00124 // getmntent + struct mnttab 00125 // mntctl + struct vmount (AIX) 00126 // getmntinfo + struct statfs&flags (BSD 4.4 and friends) 00127 // getfsent + char* (BSD 4.3 and friends) 00128 00129 #ifdef HAVE_SETMNTENT 00130 #define SETMNTENT setmntent 00131 #define ENDMNTENT endmntent 00132 #define STRUCT_MNTENT struct mntent * 00133 #define STRUCT_SETMNTENT FILE * 00134 #define GETMNTENT(file, var) ((var = getmntent(file)) != 0) 00135 #define MOUNTPOINT(var) var->mnt_dir 00136 #define MOUNTTYPE(var) var->mnt_type 00137 #define MOUNTOPTIONS(var) var->mnt_opts 00138 #define FSNAME(var) var->mnt_fsname 00139 #else 00140 #define SETMNTENT fopen 00141 #define ENDMNTENT fclose 00142 #define STRUCT_MNTENT struct mnttab 00143 #define STRUCT_SETMNTENT FILE * 00144 #define GETMNTENT(file, var) (getmntent(file, &var) == 0) 00145 #define MOUNTPOINT(var) var.mnt_mountp 00146 #define MOUNTTYPE(var) var.mnt_fstype 00147 #define MOUNTOPTIONS(var) var.mnt_mntopts 00148 #define FSNAME(var) var.mnt_special 00149 #endif 00150 00155 static QString devNameFromOptions(const QStringList &options) 00156 { 00157 // Search options to find the device name 00158 for ( QStringList::ConstIterator it = options.begin(); it != options.end(); ++it) 00159 { 00160 if( (*it).startsWith(QLatin1String("dev="))) 00161 return (*it).mid(4); 00162 } 00163 return QString::fromLatin1("none"); 00164 } 00165 00166 void KMountPoint::Private::finalizePossibleMountPoint(DetailsNeededFlags infoNeeded) 00167 { 00168 if (mountType == QLatin1String("supermount")) { 00169 mountedFrom = devNameFromOptions(mountOptions); 00170 } 00171 00172 if (mountedFrom.startsWith(QLatin1String("UUID="))) { 00173 const QString uuid = mountedFrom.mid(5); 00174 const QString potentialDevice = QFile::symLinkTarget(QString::fromLatin1("/dev/disk/by-uuid/") + uuid); 00175 if (QFile::exists(potentialDevice)) { 00176 mountedFrom = potentialDevice; 00177 } 00178 } 00179 if (mountedFrom.startsWith(QLatin1String("LABEL="))) { 00180 const QString label = mountedFrom.mid(6); 00181 const QString potentialDevice = QFile::symLinkTarget(QString::fromLatin1("/dev/disk/by-label/") + label); 00182 if (QFile::exists(potentialDevice)) { 00183 mountedFrom = potentialDevice; 00184 } 00185 } 00186 00187 if (infoNeeded & NeedRealDeviceName) { 00188 if (mountedFrom.startsWith(QLatin1Char('/'))) 00189 device = KStandardDirs::realFilePath(mountedFrom); 00190 } 00191 // TODO: Strip trailing '/' ? 00192 } 00193 00194 void KMountPoint::Private::finalizeCurrentMountPoint(DetailsNeededFlags infoNeeded) 00195 { 00196 if (infoNeeded & NeedRealDeviceName) { 00197 if (mountedFrom.startsWith(QLatin1Char('/'))) 00198 device = KStandardDirs::realFilePath(mountedFrom); 00199 } 00200 } 00201 00202 KMountPoint::List KMountPoint::possibleMountPoints(DetailsNeededFlags infoNeeded) 00203 { 00204 #ifdef Q_WS_WIN 00205 return KMountPoint::currentMountPoints(infoNeeded); 00206 #endif 00207 00208 KMountPoint::List result; 00209 00210 #ifdef HAVE_SETMNTENT 00211 STRUCT_SETMNTENT fstab; 00212 if ((fstab = SETMNTENT(FSTAB, "r")) == 0) 00213 return result; 00214 00215 STRUCT_MNTENT fe; 00216 while (GETMNTENT(fstab, fe)) 00217 { 00218 Ptr mp(new KMountPoint); 00219 mp->d->mountedFrom = QFile::decodeName(FSNAME(fe)); 00220 00221 mp->d->mountPoint = QFile::decodeName(MOUNTPOINT(fe)); 00222 mp->d->mountType = QFile::decodeName(MOUNTTYPE(fe)); 00223 00224 //Devices using supermount have their device names in the mount options 00225 //instead of the device field. That's why we need to read the mount options 00226 if (infoNeeded & NeedMountOptions || (mp->d->mountType == QLatin1String("supermount"))) 00227 { 00228 QString options = QFile::decodeName(MOUNTOPTIONS(fe)); 00229 mp->d->mountOptions = options.split( QLatin1Char(',') ); 00230 } 00231 00232 mp->d->finalizePossibleMountPoint(infoNeeded); 00233 00234 result.append(mp); 00235 } 00236 ENDMNTENT(fstab); 00237 #else 00238 QFile f(QLatin1String(FSTAB)); 00239 if ( !f.open(QIODevice::ReadOnly) ) 00240 return result; 00241 00242 QTextStream t (&f); 00243 QString s; 00244 00245 while (! t.atEnd()) 00246 { 00247 s=t.readLine().simplified(); 00248 if ( s.isEmpty() || (s[0] == QLatin1Char('#'))) 00249 continue; 00250 00251 // not empty or commented out by '#' 00252 const QStringList item = s.split(QLatin1Char(' ')); 00253 00254 #ifdef _OS_SOLARIS_ 00255 if (item.count() < 5) 00256 continue; 00257 #else 00258 if (item.count() < 4) 00259 continue; 00260 #endif 00261 00262 Ptr mp(new KMountPoint); 00263 00264 int i = 0; 00265 mp->d->mountedFrom = item[i++]; 00266 #ifdef _OS_SOLARIS_ 00267 //device to fsck 00268 i++; 00269 #endif 00270 mp->d->mountPoint = item[i++]; 00271 mp->d->mountType = item[i++]; 00272 QString options = item[i++]; 00273 00274 if (infoNeeded & NeedMountOptions) 00275 { 00276 mp->d->mountOptions = options.split(QLatin1Char(',')); 00277 } 00278 00279 mp->d->finalizePossibleMountPoint(infoNeeded); 00280 00281 result.append(mp); 00282 } //while 00283 00284 f.close(); 00285 #endif 00286 return result; 00287 } 00288 00289 KMountPoint::List KMountPoint::currentMountPoints(DetailsNeededFlags infoNeeded) 00290 { 00291 KMountPoint::List result; 00292 00293 #ifdef HAVE_GETMNTINFO 00294 00295 #ifdef GETMNTINFO_USES_STATVFS 00296 struct statvfs *mounted; 00297 #else 00298 struct statfs *mounted; 00299 #endif 00300 00301 int num_fs = getmntinfo(&mounted, MNT_NOWAIT); 00302 00303 for (int i=0;i< num_fs;i++) 00304 { 00305 Ptr mp(new KMountPoint); 00306 mp->d->mountedFrom = QFile::decodeName(mounted[i].f_mntfromname); 00307 mp->d->mountPoint = QFile::decodeName(mounted[i].f_mntonname); 00308 00309 #ifdef __osf__ 00310 mp->d->mountType = QFile::decodeName(mnt_names[mounted[i].f_type]); 00311 #else 00312 mp->d->mountType = QFile::decodeName(mounted[i].f_fstypename); 00313 #endif 00314 00315 if (infoNeeded & NeedMountOptions) 00316 { 00317 struct fstab *ft = getfsfile(mounted[i].f_mntonname); 00318 if (ft != 0) { 00319 QString options = QFile::decodeName(ft->fs_mntops); 00320 mp->d->mountOptions = options.split(QLatin1Char(',')); 00321 } else { 00322 // TODO: get mount options if not mounted via fstab, see mounted[i].f_flags 00323 } 00324 } 00325 00326 mp->d->finalizeCurrentMountPoint(infoNeeded); 00327 // TODO: Strip trailing '/' ? 00328 result.append(mp); 00329 } 00330 00331 #elif defined(_AIX) 00332 00333 struct vmount *mntctl_buffer; 00334 struct vmount *vm; 00335 char *mountedfrom; 00336 char *mountedto; 00337 int fsname_len, num; 00338 int buf_sz = 4096; 00339 00340 mntctl_buffer = (struct vmount*)malloc(buf_sz); 00341 num = mntctl(MCTL_QUERY, buf_sz, mntctl_buffer); 00342 if (num == 0) 00343 { 00344 buf_sz = *(int*)mntctl_buffer; 00345 free(mntctl_buffer); 00346 mntctl_buffer = (struct vmount*)malloc(buf_sz); 00347 num = mntctl(MCTL_QUERY, buf_sz, mntctl_buffer); 00348 } 00349 00350 if (num > 0) 00351 { 00352 /* iterate through items in the vmount structure: */ 00353 vm = (struct vmount *)mntctl_buffer; 00354 for ( ; num > 0; --num ) 00355 { 00356 /* get the name of the mounted file systems: */ 00357 fsname_len = vmt2datasize(vm, VMT_STUB); 00358 mountedto = (char*)malloc(fsname_len + 1); 00359 mountedto[fsname_len] = '\0'; 00360 strncpy(mountedto, (char *)vmt2dataptr(vm, VMT_STUB), fsname_len); 00361 00362 fsname_len = vmt2datasize(vm, VMT_OBJECT); 00363 mountedfrom = (char*)malloc(fsname_len + 1); 00364 mountedfrom[fsname_len] = '\0'; 00365 strncpy(mountedfrom, (char *)vmt2dataptr(vm, VMT_OBJECT), fsname_len); 00366 00367 /* Look up the string for the file system type, 00368 * as listed in /etc/vfs. 00369 * ex.: nfs,jfs,afs,cdrfs,sfs,cachefs,nfs3,autofs 00370 */ 00371 struct vfs_ent* ent = getvfsbytype(vm->vmt_gfstype); 00372 00373 KMountPoint *mp = new KMountPoint; 00374 mp->d->mountedFrom = QFile::decodeName(mountedfrom); 00375 mp->d->mountPoint = QFile::decodeName(mountedto); 00376 mp->d->mountType = QFile::decodeName(ent->vfsent_name); 00377 00378 free(mountedfrom); 00379 free(mountedto); 00380 00381 if (infoNeeded & NeedMountOptions) 00382 { 00383 // TODO 00384 } 00385 00386 mp->d->finalizeCurrentMountPoint(infoNeeded); 00387 result.append(mp); 00388 00389 /* goto the next vmount structure: */ 00390 vm = (struct vmount *)((char *)vm + vm->vmt_length); 00391 } 00392 00393 endvfsent( ); 00394 } 00395 00396 free( mntctl_buffer ); 00397 #elif defined(Q_WS_WIN) && !defined(_WIN32_WCE) 00398 //nothing fancy with infoNeeded but it gets the job done 00399 DWORD bits = GetLogicalDrives(); 00400 if(!bits) 00401 return result; 00402 00403 for(int i = 0; i < 26; i++) 00404 { 00405 if(bits & (1 << i)) 00406 { 00407 Ptr mp(new KMountPoint); 00408 mp->d->mountPoint = QString(QLatin1Char('A' + i) + QLatin1String(":/")); 00409 result.append(mp); 00410 } 00411 } 00412 00413 #elif defined(_WIN32_WCE) 00414 Ptr mp(new KMountPoint); 00415 mp->d->mountPoint = QString("/"); 00416 result.append(mp); 00417 00418 #else 00419 STRUCT_SETMNTENT mnttab; 00420 if ((mnttab = SETMNTENT(MNTTAB, "r")) == 0) 00421 return result; 00422 00423 STRUCT_MNTENT fe; 00424 while (GETMNTENT(mnttab, fe)) 00425 { 00426 Ptr mp(new KMountPoint); 00427 mp->d->mountedFrom = QFile::decodeName(FSNAME(fe)); 00428 00429 mp->d->mountPoint = QFile::decodeName(MOUNTPOINT(fe)); 00430 mp->d->mountType = QFile::decodeName(MOUNTTYPE(fe)); 00431 00432 //Devices using supermount have their device names in the mount options 00433 //instead of the device field. That's why we need to read the mount options 00434 if (infoNeeded & NeedMountOptions || (mp->d->mountType == QLatin1String("supermount"))) 00435 { 00436 QString options = QFile::decodeName(MOUNTOPTIONS(fe)); 00437 mp->d->mountOptions = options.split( QLatin1Char(',') ); 00438 } 00439 mp->d->finalizeCurrentMountPoint(infoNeeded); 00440 00441 result.append(mp); 00442 } 00443 ENDMNTENT(mnttab); 00444 #endif 00445 return result; 00446 } 00447 00448 QString KMountPoint::mountedFrom() const 00449 { 00450 return d->mountedFrom; 00451 } 00452 00453 QString KMountPoint::realDeviceName() const 00454 { 00455 return d->device; 00456 } 00457 00458 QString KMountPoint::mountPoint() const 00459 { 00460 return d->mountPoint; 00461 } 00462 00463 QString KMountPoint::mountType() const 00464 { 00465 return d->mountType; 00466 } 00467 00468 QStringList KMountPoint::mountOptions() const 00469 { 00470 return d->mountOptions; 00471 } 00472 00473 KMountPoint::List::List() 00474 : QList<Ptr>() 00475 { 00476 } 00477 00478 KMountPoint::Ptr KMountPoint::List::findByPath(const QString& path) const 00479 { 00480 #ifndef Q_WS_WIN 00481 /* If the path contains symlinks, get the real name */ 00482 const QString realname = KStandardDirs::realFilePath(path); 00483 #else 00484 const QString realname = QDir::fromNativeSeparators(QDir(path).absolutePath()); 00485 #endif 00486 00487 int max = 0; 00488 KMountPoint::Ptr result; 00489 for (const_iterator it = begin(); it != end(); ++it) { 00490 const QString mountpoint = (*it)->d->mountPoint; 00491 const int length = mountpoint.length(); 00492 if (realname.startsWith(mountpoint) && length > max) { 00493 max = length; 00494 result = *it; 00495 // keep iterating to check for a better match (bigger max) 00496 } 00497 } 00498 return result; 00499 } 00500 00501 KMountPoint::Ptr KMountPoint::List::findByDevice(const QString& device) const 00502 { 00503 const QString realDevice = KStandardDirs::realFilePath(device); 00504 if (realDevice.isEmpty()) // d->device can be empty in the loop below, don't match empty with it 00505 return Ptr(); 00506 for (const_iterator it = begin(); it != end(); ++it) { 00507 if ((*it)->d->device == realDevice || 00508 (*it)->d->mountedFrom == realDevice) 00509 return *it; 00510 } 00511 return Ptr(); 00512 } 00513 00514 bool KMountPoint::probablySlow() const 00515 { 00516 bool nfs = d->mountType == QLatin1String("nfs"); 00517 bool autofs = d->mountType == QLatin1String("autofs") || d->mountType == QLatin1String("subfs"); 00518 //bool pid = d->mountPoint.contains(":(pid"); 00519 // The "pid" thing was in kde3's KIO::probably_slow_mounted, with obscure logic 00520 // (looks like it used state from the previous line or something...) 00521 // This needs to be revised once we have a testcase or explanation about it. 00522 // But autofs works already, it shows nfs as mountType in mtab. 00523 if (nfs || autofs) { 00524 return true; 00525 } 00526 return false; 00527 } 00528 00529 bool KMountPoint::testFileSystemFlag(FileSystemFlag flag) const 00530 { 00531 const bool isMsDos = ( d->mountType == QLatin1String("msdos") || d->mountType == QLatin1String("fat") || d->mountType == QLatin1String("vfat") ); 00532 switch (flag) { 00533 case SupportsChmod: 00534 case SupportsChown: 00535 case SupportsUTime: 00536 case SupportsSymlinks: 00537 return !isMsDos; // it's amazing the number of things FAT doesn't support :) 00538 case CaseInsensitive: 00539 return isMsDos; 00540 } 00541 return false; 00542 } 00543
KDE 4.6 API Reference