2
Copyright (C) 2000-2002 Stephan Kulow <coolo@kde.org>
3
Copyright (C) 2000-2002 David Faure <faure@kde.org>
4
Copyright (C) 2000-2002 Waldo Bastian <bastian@kde.org>
5
Copyright (C) 2006 Allan Sandfeld Jensen <sandfeld@kde.org>
6
Copyright (C) 2007 Thiago Macieira <thiago@kde.org>
8
This library is free software; you can redistribute it and/or
9
modify it under the terms of the GNU Library General Public
10
License (LGPL) as published by the Free Software Foundation;
11
either version 2 of the License, or (at your option) any later
14
This library is distributed in the hope that it will be useful,
15
but WITHOUT ANY WARRANTY; without even the implied warranty of
16
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17
Library General Public License for more details.
19
You should have received a copy of the GNU Library General Public License
20
along with this library; see the file COPYING.LIB. If not, write to
21
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22
Boston, MA 02110-1301, USA.
25
#define QT_NO_CAST_FROM_ASCII
28
#include <QDirIterator>
31
#include <config-acl.h>
33
#include <sys/types.h>
36
#include <sys/socket.h>
37
#ifdef HAVE_SYS_TIME_H
43
#include <acl/libacl.h>
62
#include <QtCore/QByteRef>
63
#include <QtCore/QDate>
64
#include <QtCore/QVarLengthArray>
65
#include <QtCore/QCoreApplication>
66
#include <QtCore/QRegExp>
67
#include <QtCore/QFile>
69
#include <QtCore/QDir>
70
#include <QtCore/QFileInfo>
75
#include <kcomponentdata.h>
77
#include <kconfiggroup.h>
78
#include <ktemporaryfile.h>
82
#include <kmountpoint.h>
83
#include <kstandarddirs.h>
87
#include <sys/mnttab.h>
90
#include <kdirnotify.h>
91
#include <kio/ioslave_defaults.h>
94
#include <kmimetype.h>
98
#define MAX_IPC_SIZE (1024*32)
100
static QString testLogFile( const QByteArray&_filename );
101
#ifdef HAVE_POSIX_ACL
102
static bool isExtendedACL( acl_t p_acl );
103
static void appendACLAtoms( const QByteArray & path, UDSEntry& entry,
104
mode_t type, bool withACL );
107
extern "C" int KDE_EXPORT kdemain( int argc, char **argv )
109
QCoreApplication app( argc, argv ); // needed for QSocketNotifier
110
KComponentData componentData( "kio_file", "kdelibs4" );
111
( void ) KGlobal::locale();
113
kDebug(7101) << "Starting" << getpid();
117
fprintf(stderr, "Usage: kio_file protocol domain-socket1 domain-socket2\n");
121
FileProtocol slave(argv[2], argv[3]);
122
slave.dispatchLoop();
124
kDebug(7101) << "Done";
128
FileProtocol::FileProtocol( const QByteArray &pool, const QByteArray &app )
129
: SlaveBase( "file", pool, app ), openFd(-1)
133
FileProtocol::~FileProtocol()
137
#ifdef HAVE_POSIX_ACL
138
static QString aclToText(acl_t acl) {
140
char* txt = acl_to_text(acl, &size);
141
const QString ret = QString::fromLatin1(txt, size);
147
int FileProtocol::setACL( const char *path, mode_t perm, bool directoryDefault )
150
#ifdef HAVE_POSIX_ACL
152
const QString ACLString = metaData(QLatin1String("ACL_STRING"));
153
const QString defaultACLString = metaData(QLatin1String("DEFAULT_ACL_STRING"));
154
// Empty strings mean leave as is
155
if ( !ACLString.isEmpty() ) {
157
if (ACLString == QLatin1String("ACL_DELETE")) {
158
// user told us to delete the extended ACL, so let's write only
159
// the minimal (UNIX permission bits) part
160
acl = acl_from_mode( perm );
162
acl = acl_from_text( ACLString.toLatin1() );
163
if ( acl_valid( acl ) == 0 ) { // let's be safe
164
ret = acl_set_file( path, ACL_TYPE_ACCESS, acl );
165
kDebug(7101) << "Set ACL on:" << path << "to:" << aclToText(acl);
168
if ( ret != 0 ) return ret; // better stop trying right away
171
if ( directoryDefault && !defaultACLString.isEmpty() ) {
172
if ( defaultACLString == QLatin1String("ACL_DELETE") ) {
173
// user told us to delete the default ACL, do so
174
ret += acl_delete_def_file( path );
176
acl_t acl = acl_from_text( defaultACLString.toLatin1() );
177
if ( acl_valid( acl ) == 0 ) { // let's be safe
178
ret += acl_set_file( path, ACL_TYPE_DEFAULT, acl );
179
kDebug(7101) << "Set Default ACL on:" << path << "to:" << aclToText(acl);
187
Q_UNUSED(directoryDefault);
192
void FileProtocol::chmod( const KUrl& url, int permissions )
194
const QString path(url.toLocalFile());
195
const QByteArray _path( QFile::encodeName(path) );
196
/* FIXME: Should be atomic */
197
if ( KDE::chmod( path, permissions ) == -1 ||
198
( setACL( _path.data(), permissions, false ) == -1 ) ||
199
/* if not a directory, cannot set default ACLs */
200
( setACL( _path.data(), permissions, true ) == -1 && errno != ENOTDIR ) ) {
205
error(KIO::ERR_ACCESS_DENIED, path);
208
case ENOTSUP: // from setACL since chmod can't return ENOTSUP
209
error(KIO::ERR_UNSUPPORTED_ACTION, i18n("Setting ACL for %1", path));
213
error(KIO::ERR_DISK_FULL, path);
216
error(KIO::ERR_CANNOT_CHMOD, path);
222
void FileProtocol::setModificationTime( const KUrl& url, const QDateTime& mtime )
224
const QString path(url.toLocalFile());
225
KDE_struct_stat statbuf;
226
if (KDE::lstat(path, &statbuf) == 0) {
227
struct utimbuf utbuf;
228
utbuf.actime = statbuf.st_atime; // access time, unchanged
229
utbuf.modtime = mtime.toTime_t(); // modification time
230
if (KDE::utime(path, &utbuf) != 0) {
231
// TODO: errno could be EACCES, EPERM, EROFS
232
error(KIO::ERR_CANNOT_SETTIME, path);
237
error(KIO::ERR_DOES_NOT_EXIST, path);
241
void FileProtocol::mkdir( const KUrl& url, int permissions )
243
const QString path(url.toLocalFile());
245
kDebug(7101) << path << "permission=" << permissions;
247
// Remove existing file or symlink, if requested (#151851)
248
if (metaData(QLatin1String("overwrite")) == QLatin1String("true"))
251
KDE_struct_stat buff;
252
if ( KDE::lstat( path, &buff ) == -1 ) {
253
if ( KDE::mkdir( path, 0777 /*umask will be applied*/ ) != 0 ) {
254
if ( errno == EACCES ) {
255
error(KIO::ERR_ACCESS_DENIED, path);
257
} else if ( errno == ENOSPC ) {
258
error(KIO::ERR_DISK_FULL, path);
261
error(KIO::ERR_COULD_NOT_MKDIR, path);
265
if ( permissions != -1 )
266
chmod( url, permissions );
273
if ( S_ISDIR( buff.st_mode ) ) {
274
kDebug(7101) << "ERR_DIR_ALREADY_EXIST";
275
error(KIO::ERR_DIR_ALREADY_EXIST, path);
278
error(KIO::ERR_FILE_ALREADY_EXIST, path);
282
void FileProtocol::get( const KUrl& url )
284
if (!url.isLocalFile()) {
286
redir.setProtocol(config()->readEntry("DefaultRemoteProtocol", "smb"));
292
const QString path(url.toLocalFile());
293
KDE_struct_stat buff;
294
if ( KDE::stat( path, &buff ) == -1 ) {
295
if ( errno == EACCES )
296
error(KIO::ERR_ACCESS_DENIED, path);
298
error(KIO::ERR_DOES_NOT_EXIST, path);
302
if ( S_ISDIR( buff.st_mode ) ) {
303
error(KIO::ERR_IS_DIRECTORY, path);
306
if ( !S_ISREG( buff.st_mode ) ) {
307
error(KIO::ERR_CANNOT_OPEN_FOR_READING, path);
311
int fd = KDE::open( path, O_RDONLY);
313
error(KIO::ERR_CANNOT_OPEN_FOR_READING, path);
318
posix_fadvise( fd, 0, 0, POSIX_FADV_SEQUENTIAL);
321
// Determine the mimetype of the file to be retrieved, and emit it.
322
// This is mandatory in all slaves (for KRun/BrowserRun to work)
323
// In real "remote" slaves, this is usually done using findByNameAndContent
324
// after receiving some data. But we don't know how much data the mimemagic rules
325
// need, so for local files, better use findByUrl with localUrl=true.
326
KMimeType::Ptr mt = KMimeType::findByUrl( url, buff.st_mode, true /* local URL */ );
327
emit mimeType( mt->name() );
328
// Emit total size AFTER mimetype
329
totalSize( buff.st_size );
331
KIO::filesize_t processed_size = 0;
333
const QString resumeOffset = metaData(QLatin1String("resume"));
334
if ( !resumeOffset.isEmpty() )
337
KIO::fileoffset_t offset = resumeOffset.toLongLong(&ok);
338
if (ok && (offset > 0) && (offset < buff.st_size))
340
if (KDE_lseek(fd, offset, SEEK_SET) == offset)
343
processed_size = offset;
344
kDebug(7101) << "Resume offset:" << KIO::number(offset);
349
char buffer[ MAX_IPC_SIZE ];
354
int n = ::read( fd, buffer, MAX_IPC_SIZE );
359
error(KIO::ERR_COULD_NOT_READ, path);
366
array = QByteArray::fromRawData(buffer, n);
371
processedSize( processed_size );
373
//kDebug(7101) << "Processed: " << KIO::number (processed_size);
376
data( QByteArray() );
380
processedSize( buff.st_size );
384
int write_all(int fd, const char *buf, size_t len)
388
ssize_t written = write(fd, buf, len);
401
void FileProtocol::open(const KUrl &url, QIODevice::OpenMode mode)
405
openPath = url.toLocalFile();
406
KDE_struct_stat buff;
407
if (KDE::stat(openPath, &buff) == -1) {
408
if ( errno == EACCES )
409
error(KIO::ERR_ACCESS_DENIED, openPath);
411
error(KIO::ERR_DOES_NOT_EXIST, openPath);
415
if ( S_ISDIR( buff.st_mode ) ) {
416
error(KIO::ERR_IS_DIRECTORY, openPath);
419
if ( !S_ISREG( buff.st_mode ) ) {
420
error(KIO::ERR_CANNOT_OPEN_FOR_READING, openPath);
425
if (mode & QIODevice::ReadOnly) {
426
if (mode & QIODevice::WriteOnly) {
427
flags = O_RDWR | O_CREAT;
431
} else if (mode & QIODevice::WriteOnly) {
432
flags = O_WRONLY | O_CREAT;
435
if (mode & QIODevice::Append) {
437
} else if (mode & QIODevice::Truncate) {
442
if ( flags & O_CREAT)
443
fd = KDE::open( openPath, flags, 0666);
445
fd = KDE::open( openPath, flags);
447
error(KIO::ERR_CANNOT_OPEN_FOR_READING, openPath);
450
// Determine the mimetype of the file to be retrieved, and emit it.
451
// This is mandatory in all slaves (for KRun/BrowserRun to work).
452
// If we're not opening the file ReadOnly or ReadWrite, don't attempt to
453
// read the file and send the mimetype.
454
if (mode & QIODevice::ReadOnly){
455
KMimeType::Ptr mt = KMimeType::findByUrl( url, buff.st_mode, true /* local URL */ );
456
emit mimeType( mt->name() );
459
totalSize( buff.st_size );
466
void FileProtocol::read(KIO::filesize_t bytes)
468
kDebug(7101) << "File::open -- read";
469
Q_ASSERT(openFd != -1);
471
QVarLengthArray<char> buffer(bytes);
475
res = ::read(openFd, buffer.data(), bytes);
476
} while (res == -1 && errno == EINTR);
479
QByteArray array = QByteArray::fromRawData(buffer.data(), res);
483
// empty array designates eof
486
error(KIO::ERR_COULD_NOT_READ, openPath);
491
if (bytes <= 0) break;
495
void FileProtocol::write(const QByteArray &data)
497
kDebug(7101) << "File::open -- write";
498
Q_ASSERT(openFd != -1);
500
if (write_all(openFd, data.constData(), data.size())) {
501
if (errno == ENOSPC) { // disk full
502
error(KIO::ERR_DISK_FULL, openPath);
505
kWarning(7101) << "Couldn't write. Error:" << strerror(errno);
506
error(KIO::ERR_COULD_NOT_WRITE, openPath);
510
written(data.size());
514
void FileProtocol::seek(KIO::filesize_t offset)
516
kDebug(7101) << "File::open -- seek";
517
Q_ASSERT(openFd != -1);
519
int res = KDE_lseek(openFd, offset, SEEK_SET);
523
error(KIO::ERR_COULD_NOT_SEEK, openPath);
528
void FileProtocol::close()
530
kDebug(7101) << "File::open -- close ";
531
Q_ASSERT(openFd != -1);
540
void FileProtocol::put( const KUrl& url, int _mode, KIO::JobFlags _flags )
542
const QString dest_orig = url.toLocalFile();
544
kDebug(7101) << dest_orig << "mode=" << _mode;
546
QString dest_part(dest_orig + QLatin1String(".part"));
548
KDE_struct_stat buff_orig;
549
const bool bOrigExists = (KDE::lstat(dest_orig, &buff_orig) != -1);
550
bool bPartExists = false;
551
const bool bMarkPartial = config()->readEntry("MarkPartial", true);
555
KDE_struct_stat buff_part;
556
bPartExists = (KDE::stat( dest_part, &buff_part ) != -1);
558
if (bPartExists && !(_flags & KIO::Resume) && !(_flags & KIO::Overwrite) && buff_part.st_size > 0 && S_ISREG(buff_part.st_mode))
560
kDebug(7101) << "calling canResume with" << KIO::number(buff_part.st_size);
562
// Maybe we can use this partial file for resuming
563
// Tell about the size we have, and the app will tell us
564
// if it's ok to resume or not.
565
_flags |= canResume( buff_part.st_size ) ? KIO::Resume : KIO::DefaultFlags;
567
kDebug(7101) << "got answer" << (_flags & KIO::Resume);
571
if ( bOrigExists && !(_flags & KIO::Overwrite) && !(_flags & KIO::Resume))
573
if (S_ISDIR(buff_orig.st_mode))
574
error( KIO::ERR_DIR_ALREADY_EXIST, dest_orig );
576
error( KIO::ERR_FILE_ALREADY_EXIST, dest_orig );
586
// Loop until we got 0 (end of data)
590
dataReq(); // Request for data
591
result = readData( buffer );
599
kDebug(7101) << "Appending .part extension to" << dest_orig;
601
if ( bPartExists && !(_flags & KIO::Resume) )
603
kDebug(7101) << "Deleting partial file" << dest_part;
604
QFile::remove( dest_part );
605
// Catch errors when we try to open the file.
611
if ( bOrigExists && !(_flags & KIO::Resume) )
613
kDebug(7101) << "Deleting destination file" << dest_orig;
614
QFile::remove( dest_orig );
615
// Catch errors when we try to open the file.
619
if ( (_flags & KIO::Resume) )
621
fd = KDE::open( dest, O_RDWR ); // append if resuming
622
KDE_lseek(fd, 0, SEEK_END); // Seek to end
626
// WABA: Make sure that we keep writing permissions ourselves,
627
// otherwise we can be in for a surprise on NFS.
630
initialMode = _mode | S_IWUSR | S_IRUSR;
634
fd = KDE::open(dest, O_CREAT | O_TRUNC | O_WRONLY, initialMode);
639
kDebug(7101) << "####################### COULD NOT WRITE" << dest << "_mode=" << _mode;
640
kDebug(7101) << "errno==" << errno << "(" << strerror(errno) << ")";
641
if ( errno == EACCES )
642
error(KIO::ERR_WRITE_ACCESS_DENIED, dest);
644
error(KIO::ERR_CANNOT_OPEN_FOR_WRITING, dest);
649
if (write_all( fd, buffer.data(), buffer.size()))
651
if ( errno == ENOSPC ) // disk full
653
error(KIO::ERR_DISK_FULL, dest_orig);
654
result = -2; // means: remove dest file
658
kWarning(7101) << "Couldn't write. Error:" << strerror(errno);
659
error(KIO::ERR_COULD_NOT_WRITE, dest_orig);
665
while ( result > 0 );
667
// An error occurred deal with it.
670
kDebug(7101) << "Error during 'put'. Aborting.";
676
KDE_struct_stat buff;
677
if (bMarkPartial && KDE::stat( dest, &buff ) == 0)
679
int size = config()->readEntry("MinimumKeepSize", DEFAULT_MINIMUM_KEEP_SIZE);
680
if (buff.st_size < size)
681
remove(_dest.data());
688
if ( fd == -1 ) // we got nothing to write out, so we never opened the file
696
kWarning(7101) << "Error when closing file descriptor:" << strerror(errno);
697
error(KIO::ERR_COULD_NOT_WRITE, dest_orig);
701
// after full download rename the file back to original name
704
// If the original URL is a symlink and we were asked to overwrite it,
705
// remove the symlink first. This ensures that we do not overwrite the
706
// current source if the symlink points to it.
707
if( (_flags & KIO::Overwrite) && S_ISLNK( buff_orig.st_mode ) )
708
QFile::remove( dest_orig );
709
if ( KDE::rename( dest, dest_orig ) )
711
kWarning(7101) << " Couldn't rename " << _dest << " to " << dest_orig;
712
error(KIO::ERR_CANNOT_RENAME_PARTIAL, dest_orig);
715
org::kde::KDirNotify::emitFileRenamed(dest, dest_orig);
718
// set final permissions
719
if ( _mode != -1 && !(_flags & KIO::Resume) )
721
if (KDE::chmod(dest_orig, _mode) != 0)
723
// couldn't chmod. Eat the error if the filesystem apparently doesn't support it.
724
KMountPoint::Ptr mp = KMountPoint::currentMountPoints().findByPath(dest_orig);
725
if (mp && mp->testFileSystemFlag(KMountPoint::SupportsChmod))
726
warning( i18n( "Could not change permissions for\n%1" , dest_orig ) );
730
// set modification time
731
const QString mtimeStr = metaData(QLatin1String("modified"));
732
if ( !mtimeStr.isEmpty() ) {
733
QDateTime dt = QDateTime::fromString( mtimeStr, Qt::ISODate );
734
if ( dt.isValid() ) {
735
KDE_struct_stat dest_statbuf;
736
if (KDE::stat( dest_orig, &dest_statbuf ) == 0) {
737
struct timeval utbuf[2];
739
utbuf[0].tv_sec = dest_statbuf.st_atime; // access time, unchanged ## TODO preserve msec
740
utbuf[0].tv_usec = 0;
742
utbuf[1].tv_sec = dt.toTime_t();
743
utbuf[1].tv_usec = dt.time().msec() * 1000;
744
utimes( QFile::encodeName(dest_orig), utbuf );
750
// We have done our job => finish
754
QString FileProtocol::getUserName( uid_t uid ) const
756
if ( !mUsercache.contains( uid ) ) {
757
struct passwd *user = getpwuid( uid );
759
mUsercache.insert( uid, QString::fromLatin1(user->pw_name) );
762
return QString::number( uid );
764
return mUsercache[uid];
767
QString FileProtocol::getGroupName( gid_t gid ) const
769
if ( !mGroupcache.contains( gid ) ) {
770
struct group *grp = getgrgid( gid );
772
mGroupcache.insert( gid, QString::fromLatin1(grp->gr_name) );
775
return QString::number( gid );
777
return mGroupcache[gid];
780
bool FileProtocol::createUDSEntry( const QString & filename, const QByteArray & path, UDSEntry & entry,
781
short int details, bool withACL )
783
#ifndef HAVE_POSIX_ACL
786
assert(entry.count() == 0); // by contract :-)
787
// entry.reserve( 8 ); // speed up QHash insertion
789
entry.insert( KIO::UDSEntry::UDS_NAME, filename );
793
KDE_struct_stat buff;
795
if ( KDE_lstat( path.data(), &buff ) == 0 ) {
797
if (S_ISLNK(buff.st_mode)) {
799
char buffer2[ 1000 ];
800
int n = readlink( path.data(), buffer2, 1000 );
805
entry.insert( KIO::UDSEntry::UDS_LINK_DEST, QFile::decodeName( buffer2 ) );
807
// A symlink -> follow it only if details>1
808
if ( details > 1 && KDE_stat( path.data(), &buff ) == -1 ) {
809
// It is a link pointing to nowhere
811
access = S_IRWXU | S_IRWXG | S_IRWXO;
813
entry.insert( KIO::UDSEntry::UDS_FILE_TYPE, type );
814
entry.insert( KIO::UDSEntry::UDS_ACCESS, access );
815
entry.insert( KIO::UDSEntry::UDS_SIZE, 0LL );
821
// kWarning() << "lstat didn't work on " << path.data();
825
type = buff.st_mode & S_IFMT; // extract file type
826
access = buff.st_mode & 07777; // extract permissions
828
entry.insert( KIO::UDSEntry::UDS_FILE_TYPE, type );
829
entry.insert( KIO::UDSEntry::UDS_ACCESS, access );
831
entry.insert( KIO::UDSEntry::UDS_SIZE, buff.st_size );
833
#ifdef HAVE_POSIX_ACL
835
/* Append an atom indicating whether the file has extended acl information
836
* and if withACL is specified also one with the acl itself. If it's a directory
837
* and it has a default ACL, also append that. */
838
appendACLAtoms( path, entry, type, withACL );
844
entry.insert( KIO::UDSEntry::UDS_MODIFICATION_TIME, buff.st_mtime );
845
entry.insert( KIO::UDSEntry::UDS_USER, getUserName( buff.st_uid ) );
846
entry.insert( KIO::UDSEntry::UDS_GROUP, getGroupName( buff.st_gid ) );
847
entry.insert( KIO::UDSEntry::UDS_ACCESS_TIME, buff.st_atime );
850
// Note: buff.st_ctime isn't the creation time !
851
// We made that mistake for KDE 2.0, but it's in fact the
852
// "file status" change time, which we don't care about.
857
void FileProtocol::special( const QByteArray &data)
860
QDataStream stream(data);
866
QString fstype, dev, point;
869
stream >> iRo >> fstype >> dev >> point;
871
bool ro = ( iRo != 0 );
873
kDebug(7101) << "MOUNTING fstype=" << fstype << " dev=" << dev << " point=" << point << " ro=" << ro;
874
bool ok = pmount( dev );
878
mount( ro, fstype.toAscii(), dev, point );
886
bool ok = pumount( point );
899
void FileProtocol::mount( bool _ro, const char *_fstype, const QString& _dev, const QString& _point )
901
kDebug(7101) << "fstype=" << _fstype;
906
* support for Solaris volume management
909
QByteArray devname = QFile::encodeName( _dev );
911
if( volmgt_running() ) {
912
// kDebug(7101) << "VOLMGT: vold ok.";
913
if( volmgt_check( devname.data() ) == 0 ) {
914
kDebug(7101) << "VOLMGT: no media in "
916
err = i18n("No Media inserted or Media not recognized.");
917
error( KIO::ERR_COULD_NOT_MOUNT, err );
920
kDebug(7101) << "VOLMGT: " << devname.data()
926
err = i18n("\"vold\" is not running.");
927
kDebug(7101) << "VOLMGT: " << err;
928
error( KIO::ERR_COULD_NOT_MOUNT, err );
934
KTemporaryFile tmpFile;
935
tmpFile.setAutoRemove(false);
937
QByteArray tmpFileName = QFile::encodeName(tmpFile.fileName());
939
if (_dev.startsWith(QLatin1String("LABEL="))) { // turn LABEL=foo into -L foo (#71430)
940
QString labelName = _dev.mid( 6 );
942
dev += QFile::encodeName( KShell::quoteArg( labelName ) ); // is it correct to assume same encoding as filesystem?
943
} else if (_dev.startsWith(QLatin1String("UUID="))) { // and UUID=bar into -U bar
944
QString uuidName = _dev.mid( 5 );
946
dev += QFile::encodeName( KShell::quoteArg( uuidName ) );
949
dev = QFile::encodeName( KShell::quoteArg(_dev) ); // get those ready to be given to a shell
951
QByteArray point = QFile::encodeName( KShell::quoteArg(_point) );
952
bool fstype_empty = !_fstype || !*_fstype;
953
QByteArray fstype = KShell::quoteArg(QString::fromLatin1(_fstype)).toLatin1(); // good guess
954
QByteArray readonly = _ro ? "-r" : "";
955
QString epath = QString::fromLocal8Bit(qgetenv("PATH"));
956
QString path = QLatin1String("/sbin:/bin");
958
path += QLatin1String(":") + epath;
959
QByteArray mountProg = KGlobal::dirs()->findExe(QLatin1String("mount"), path).toLocal8Bit();
960
if (mountProg.isEmpty()){
961
error( KIO::ERR_COULD_NOT_MOUNT, i18n("Could not find program \"mount\""));
965
// Two steps, in case mount doesn't like it when we pass all options
966
for ( int step = 0 ; step <= 1 ; step++ )
968
QByteArray buffer = mountProg + ' ';
969
// Mount using device only if no fstype nor mountpoint (KDE-1.x like)
970
if ( !dev.isEmpty() && _point.isEmpty() && fstype_empty )
973
// Mount using the mountpoint, if no fstype nor device (impossible in first step)
974
if ( !_point.isEmpty() && dev.isEmpty() && fstype_empty )
977
// mount giving device + mountpoint but no fstype
978
if ( !_point.isEmpty() && !dev.isEmpty() && fstype_empty )
979
buffer += readonly + ' ' + dev + ' ' + point;
981
// mount giving device + mountpoint + fstype
982
#if defined(__svr4__) && defined(Q_OS_SOLARIS) // MARCO for Solaris 8 and I
983
// believe this is true for SVR4 in general
984
buffer += "-F " + fstype + ' ' + (_ro ? "-oro" : "") + ' ' + dev + ' ' + point;
986
buffer += readonly + " -t " + fstype + ' ' + dev + ' ' + point;
988
buffer += " 2>" + tmpFileName;
989
kDebug(7101) << buffer;
991
int mount_ret = system( buffer.constData() );
993
QString err = testLogFile( tmpFileName );
994
if ( err.isEmpty() && mount_ret == 0)
1001
// Didn't work - or maybe we just got a warning
1002
KMountPoint::Ptr mp = KMountPoint::currentMountPoints().findByDevice( _dev );
1003
// Is the device mounted ?
1004
if ( mp && mount_ret == 0)
1006
kDebug(7101) << "mount got a warning:" << err;
1013
if ( (step == 0) && !_point.isEmpty())
1015
kDebug(7101) << err;
1016
kDebug(7101) << "Mounting with those options didn't work, trying with only mountpoint";
1018
fstype_empty = true;
1020
// The reason for trying with only mountpoint (instead of
1021
// only device) is that some people (hi Malte!) have the
1022
// same device associated with two mountpoints
1023
// for different fstypes, like /dev/fd0 /mnt/e2floppy and
1024
// /dev/fd0 /mnt/dosfloppy.
1025
// If the user has the same mountpoint associated with two
1026
// different devices, well they shouldn't specify the
1027
// mountpoint but just the device.
1031
error( KIO::ERR_COULD_NOT_MOUNT, err );
1037
#endif /* ! HAVE_VOLMGT */
1040
err = i18n("mounting is not supported by wince.");
1041
error( KIO::ERR_COULD_NOT_MOUNT, err );
1047
void FileProtocol::unmount( const QString& _point )
1052
KTemporaryFile tmpFile;
1053
tmpFile.setAutoRemove(false);
1055
QByteArray tmpFileName = QFile::encodeName(tmpFile.fileName());
1060
* support for Solaris volume management
1067
if( volmgt_running() ) {
1068
kDebug(7101) << "VOLMGT: looking for "
1069
<< _point.toLocal8Bit();
1071
if( (mnttab = KDE_fopen( MNTTAB, "r" )) == NULL ) {
1072
err = QLatin1String("could not open mnttab");
1073
kDebug(7101) << "VOLMGT: " << err;
1074
error( KIO::ERR_COULD_NOT_UNMOUNT, err );
1079
* since there's no way to derive the device name from
1080
* the mount point through the volmgt library (and
1081
* media_findname() won't work in this case), we have to
1086
while( getmntent( mnttab, &mnt ) == 0 ) {
1087
if( strcmp( _point.toLocal8Bit(), mnt.mnt_mountp ) == 0 ){
1088
devname = mnt.mnt_special;
1094
if( devname == NULL ) {
1095
err = QLatin1String("not in mnttab");
1096
kDebug(7101) << "VOLMGT: "
1097
<< QFile::encodeName(_point).data()
1099
error( KIO::ERR_COULD_NOT_UNMOUNT, err );
1104
* strip off the directory name (volume name)
1105
* the eject(1) command will handle unmounting and
1106
* physically eject the media (if possible)
1108
ptr = strrchr( devname, '/' );
1110
QByteArray qdevname(QFile::encodeName(KShell::quoteArg(QFile::decodeName(QByteArray(devname)))).data());
1111
buffer = "/usr/bin/eject " + qdevname + " 2>" + tmpFileName;
1112
kDebug(7101) << "VOLMGT: eject " << qdevname;
1115
* from eject(1): exit status == 0 => need to manually eject
1116
* exit status == 4 => media was ejected
1118
if( WEXITSTATUS( system( buffer.constData() )) == 4 ) {
1120
* this is not an error, so skip "testLogFile()"
1121
* to avoid wrong/confusing error popup. The
1122
* temporary file is removed by KTemporaryFile's
1123
* destructor, so don't do that manually.
1130
* eject(1) should do its job without vold(1M) running,
1131
* so we probably could call eject anyway, but since the
1132
* media is mounted now, vold must've died for some reason
1133
* during the user's session, so it should be restarted...
1135
err = i18n("\"vold\" is not running.");
1136
kDebug(7101) << "VOLMGT: " << err;
1137
error( KIO::ERR_COULD_NOT_UNMOUNT, err );
1141
QString epath = QString::fromLocal8Bit(qgetenv("PATH"));
1142
QString path = QLatin1String("/sbin:/bin");
1143
if (!epath.isEmpty())
1144
path += QLatin1Char(':') + epath;
1145
QByteArray umountProg = KGlobal::dirs()->findExe(QLatin1String("umount"), path).toLocal8Bit();
1147
if (umountProg.isEmpty()) {
1148
error( KIO::ERR_COULD_NOT_UNMOUNT, i18n("Could not find program \"umount\""));
1151
buffer = umountProg + ' ' + QFile::encodeName(KShell::quoteArg(_point)) + " 2>" + tmpFileName;
1152
system( buffer.constData() );
1153
#endif /* HAVE_VOLMGT */
1155
err = testLogFile( tmpFileName );
1156
if ( err.isEmpty() )
1159
error( KIO::ERR_COULD_NOT_UNMOUNT, err );
1162
err = i18n("unmounting is not supported by wince.");
1163
error( KIO::ERR_COULD_NOT_MOUNT, err );
1167
/*************************************
1171
*************************************/
1173
bool FileProtocol::pmount(const QString &dev)
1176
QString epath = QString::fromLocal8Bit(qgetenv("PATH"));
1177
QString path = QLatin1String("/sbin:/bin");
1178
if (!epath.isEmpty())
1179
path += QLatin1Char(':') + epath;
1180
QString pmountProg = KGlobal::dirs()->findExe(QLatin1String("pmount"), path);
1182
if (pmountProg.isEmpty())
1185
QByteArray buffer = QFile::encodeName(pmountProg) + ' ' +
1186
QFile::encodeName(KShell::quoteArg(dev));
1188
int res = system( buffer.constData() );
1196
bool FileProtocol::pumount(const QString &point)
1199
KMountPoint::Ptr mp = KMountPoint::currentMountPoints(KMountPoint::NeedRealDeviceName).findByPath(point);
1202
QString dev = mp->realDeviceName();
1203
if (dev.isEmpty()) return false;
1205
QString epath = QString::fromLocal8Bit(qgetenv("PATH"));
1206
QString path = QLatin1String("/sbin:/bin");
1207
if (!epath.isEmpty())
1208
path += QLatin1Char(':') + epath;
1209
QString pumountProg = KGlobal::dirs()->findExe(QLatin1String("pumount"), path);
1211
if (pumountProg.isEmpty())
1214
QByteArray buffer = QFile::encodeName(pumountProg);
1216
buffer += QFile::encodeName(KShell::quoteArg(dev));
1218
int res = system( buffer.data() );
1226
/*************************************
1230
*************************************/
1232
static QString testLogFile( const QByteArray& _filename )
1234
char buffer[ 1024 ];
1235
KDE_struct_stat buff;
1239
KDE_stat( _filename, &buff );
1240
int size = buff.st_size;
1242
unlink( _filename );
1246
FILE * f = KDE_fopen( _filename, "rb" );
1248
unlink( _filename );
1249
result = i18n("Could not read %1", QFile::decodeName(_filename));
1256
p = fgets( buffer, sizeof(buffer)-1, f );
1258
result += QString::fromLocal8Bit(buffer);
1263
unlink( _filename );
1268
/*************************************
1270
* ACL handling helpers
1272
*************************************/
1273
#ifdef HAVE_POSIX_ACL
1275
static bool isExtendedACL( acl_t acl )
1277
return ( acl_equiv_mode( acl, 0 ) != 0 );
1280
static void appendACLAtoms( const QByteArray & path, UDSEntry& entry, mode_t type, bool withACL )
1282
// first check for a noop
1283
if ( acl_extended_file( path.data() ) == 0 ) return;
1286
acl_t defaultAcl = 0;
1287
bool isDir = S_ISDIR( type );
1288
// do we have an acl for the file, and/or a default acl for the dir, if it is one?
1289
acl = acl_get_file( path.data(), ACL_TYPE_ACCESS );
1290
/* Sadly libacl does not provided a means of checking for extended ACL and default
1291
* ACL separately. Since a directory can have both, we need to check again. */
1294
if ( !isExtendedACL( acl ) ) {
1299
defaultAcl = acl_get_file( path.data(), ACL_TYPE_DEFAULT );
1301
if ( acl || defaultAcl ) {
1302
kDebug(7101) << path.constData() << "has extended ACL entries";
1303
entry.insert( KIO::UDSEntry::UDS_EXTENDED_ACL, 1 );
1307
const QString str = aclToText(acl);
1308
entry.insert( KIO::UDSEntry::UDS_ACL_STRING, str );
1309
kDebug(7101) << path.constData() << "ACL:" << str;
1312
const QString str = aclToText(defaultAcl);
1313
entry.insert( KIO::UDSEntry::UDS_DEFAULT_ACL_STRING, str );
1314
kDebug(7101) << path.constData() << "DEFAULT ACL:" << str;
1317
if ( acl ) acl_free( acl );
1318
if ( defaultAcl ) acl_free( defaultAcl );
1322
// We could port this to KTempDir::removeDir but then we wouldn't be able to tell the user
1323
// where exactly the deletion failed, in case of errors.
1324
bool FileProtocol::deleteRecursive(const QString& path)
1327
QDirIterator it(path, QDir::AllEntries | QDir::NoDotAndDotDot | QDir::System | QDir::Hidden,
1328
QDirIterator::Subdirectories);
1329
QStringList dirsToDelete;
1330
while ( it.hasNext() ) {
1331
const QString itemPath = it.next();
1332
//kDebug() << "itemPath=" << itemPath;
1333
const QFileInfo info = it.fileInfo();
1334
if (info.isDir() && !info.isSymLink())
1335
dirsToDelete.prepend(itemPath);
1337
//kDebug() << "QFile::remove" << itemPath;
1338
if (!QFile::remove(itemPath)) {
1339
error(KIO::ERR_CANNOT_DELETE, itemPath);
1345
Q_FOREACH(const QString& itemPath, dirsToDelete) {
1346
//kDebug() << "QDir::rmdir" << itemPath;
1347
if (!dir.rmdir(itemPath)) {
1348
error(KIO::ERR_CANNOT_DELETE, itemPath);