25 #define QT_NO_CAST_FROM_ASCII
28 #include <QDirIterator>
31 #include <config-acl.h>
32 #include <config-kioslave-file.h>
35 #include <sys/types.h>
38 #include <sys/socket.h>
39 #ifdef HAVE_SYS_TIME_H
45 #include <acl/libacl.h>
64 #include <QtCore/QByteRef>
65 #include <QtCore/QDate>
66 #include <QtCore/QVarLengthArray>
67 #include <QtCore/QCoreApplication>
68 #include <QtCore/QRegExp>
69 #include <QtCore/QFile>
71 #include <QtCore/QDir>
72 #include <QtCore/QFileInfo>
89 #include <sys/mnttab.h>
100 #define MAX_IPC_SIZE (1024*32)
103 #ifdef HAVE_POSIX_ACL
104 static bool isExtendedACL( acl_t p_acl );
105 static void appendACLAtoms(
const QByteArray & path,
UDSEntry& entry,
106 mode_t type,
bool withACL );
111 QCoreApplication app( argc, argv );
115 kDebug(7101) <<
"Starting" << getpid();
119 fprintf(stderr,
"Usage: kio_file protocol domain-socket1 domain-socket2\n");
131 :
SlaveBase(
"file", pool, app ), openFd(-1)
139 #ifdef HAVE_POSIX_ACL
140 static QString aclToText(acl_t acl) {
142 char* txt = acl_to_text(acl, &size);
143 const QString ret = QString::fromLatin1(txt, size);
149 int FileProtocol::setACL(
const char *path, mode_t perm,
bool directoryDefault )
152 #ifdef HAVE_POSIX_ACL
155 const QString defaultACLString =
metaData(QLatin1String(
"DEFAULT_ACL_STRING"));
157 if ( !ACLString.isEmpty() ) {
159 if (ACLString == QLatin1String(
"ACL_DELETE")) {
162 acl = acl_from_mode( perm );
164 acl = acl_from_text( ACLString.toLatin1() );
165 if ( acl_valid( acl ) == 0 ) {
166 ret = acl_set_file( path, ACL_TYPE_ACCESS, acl );
167 kDebug(7101) <<
"Set ACL on:" << path <<
"to:" << aclToText(acl);
170 if ( ret != 0 )
return ret;
173 if ( directoryDefault && !defaultACLString.isEmpty() ) {
174 if ( defaultACLString == QLatin1String(
"ACL_DELETE") ) {
176 ret += acl_delete_def_file( path );
178 acl_t acl = acl_from_text( defaultACLString.toLatin1() );
179 if ( acl_valid( acl ) == 0 ) {
180 ret += acl_set_file( path, ACL_TYPE_DEFAULT, acl );
181 kDebug(7101) <<
"Set Default ACL on:" << path <<
"to:" << aclToText(acl);
189 Q_UNUSED(directoryDefault);
197 const QByteArray _path( QFile::encodeName(path) );
200 ( setACL( _path.data(), permissions, false ) == -1 ) ||
202 ( setACL( _path.data(), permissions, true ) == -1 && errno != ENOTDIR ) ) {
227 KDE_struct_stat statbuf;
229 struct utimbuf utbuf;
230 utbuf.actime = statbuf.st_atime;
231 utbuf.modtime = mtime.toTime_t();
247 kDebug(7101) << path <<
"permission=" << permissions;
250 if (
metaData(QLatin1String(
"overwrite")) == QLatin1String(
"true"))
253 KDE_struct_stat buff;
256 if ( errno == EACCES ) {
259 }
else if ( errno == ENOSPC ) {
267 if ( permissions != -1 )
268 chmod( url, permissions );
275 if ( S_ISDIR( buff.st_mode ) ) {
276 kDebug(7101) <<
"ERR_DIR_ALREADY_EXIST";
295 KDE_struct_stat buff;
297 if ( errno == EACCES )
304 if ( S_ISDIR( buff.st_mode ) ) {
308 if ( !S_ISREG( buff.st_mode ) ) {
320 posix_fadvise( fd, 0, 0, POSIX_FADV_SEQUENTIAL);
336 if ( !resumeOffset.isEmpty() )
340 if (ok && (offset > 0) && (offset < buff.st_size))
342 if (KDE_lseek(fd, offset, SEEK_SET) == offset)
345 processed_size = offset;
368 array = QByteArray::fromRawData(buffer, n);
378 data( QByteArray() );
390 ssize_t written = write(fd, buf, len);
408 KDE_struct_stat buff;
410 if ( errno == EACCES )
417 if ( S_ISDIR( buff.st_mode ) ) {
421 if ( !S_ISREG( buff.st_mode ) ) {
427 if (mode & QIODevice::ReadOnly) {
428 if (mode & QIODevice::WriteOnly) {
429 flags = O_RDWR | O_CREAT;
433 }
else if (mode & QIODevice::WriteOnly) {
434 flags = O_WRONLY | O_CREAT;
437 if (mode & QIODevice::Append) {
439 }
else if (mode & QIODevice::Truncate) {
444 if ( flags & O_CREAT)
456 if (mode & QIODevice::ReadOnly){
470 kDebug(7101) <<
"File::open -- read";
471 Q_ASSERT(openFd != -1);
473 QVarLengthArray<char> buffer(bytes);
477 res =
::read(openFd, buffer.data(), bytes);
478 }
while (res == -1 && errno == EINTR);
481 QByteArray array = QByteArray::fromRawData(buffer.data(), res);
493 if (bytes <= 0)
break;
499 kDebug(7101) <<
"File::open -- write";
500 Q_ASSERT(openFd != -1);
502 if (
write_all(openFd, data.constData(), data.size())) {
503 if (errno == ENOSPC) {
507 kWarning(7101) <<
"Couldn't write. Error:" << strerror(errno);
518 kDebug(7101) <<
"File::open -- seek";
519 Q_ASSERT(openFd != -1);
521 int res = KDE_lseek(openFd, offset, SEEK_SET);
532 kDebug(7101) <<
"File::open -- close ";
533 Q_ASSERT(openFd != -1);
546 kDebug(7101) << dest_orig <<
"mode=" << _mode;
548 QString dest_part(dest_orig + QLatin1String(
".part"));
550 KDE_struct_stat buff_orig;
551 const bool bOrigExists = (
KDE::lstat(dest_orig, &buff_orig) != -1);
552 bool bPartExists =
false;
557 KDE_struct_stat buff_part;
558 bPartExists = (
KDE::stat( dest_part, &buff_part ) != -1);
560 if (bPartExists && !(_flags &
KIO::Resume) && !(_flags &
KIO::Overwrite) && buff_part.st_size > 0 && S_ISREG(buff_part.st_mode))
569 kDebug(7101) <<
"got answer" << (_flags & KIO::Resume);
575 if (S_ISDIR(buff_orig.st_mode))
601 kDebug(7101) <<
"Appending .part extension to" << dest_orig;
605 kDebug(7101) <<
"Deleting partial file" << dest_part;
606 QFile::remove( dest_part );
615 kDebug(7101) <<
"Deleting destination file" << dest_orig;
616 QFile::remove( dest_orig );
624 KDE_lseek(fd, 0, SEEK_END);
632 initialMode = _mode | S_IWUSR | S_IRUSR;
636 fd =
KDE::open(dest, O_CREAT | O_TRUNC | O_WRONLY, initialMode);
641 kDebug(7101) <<
"####################### COULD NOT WRITE" << dest <<
"_mode=" << _mode;
642 kDebug(7101) <<
"errno==" << errno <<
"(" << strerror(errno) <<
")";
643 if ( errno == EACCES )
651 if (
write_all( fd, buffer.data(), buffer.size()))
653 if ( errno == ENOSPC )
660 kWarning(7101) <<
"Couldn't write. Error:" << strerror(errno);
667 while ( result > 0 );
672 kDebug(7101) <<
"Error during 'put'. Aborting.";
678 KDE_struct_stat buff;
679 if (bMarkPartial &&
KDE::stat( dest, &buff ) == 0)
682 if (buff.st_size < size)
683 remove(_dest.data());
698 kWarning(7101) <<
"Error when closing file descriptor:" << strerror(errno);
709 if( (_flags & KIO::Overwrite) && S_ISLNK( buff_orig.st_mode ) )
710 QFile::remove( dest_orig );
713 kWarning(7101) <<
" Couldn't rename " << _dest <<
" to " << dest_orig;
717 org::kde::KDirNotify::emitFileRenamed(dest, dest_orig);
728 warning(
i18n(
"Could not change permissions for\n%1" , dest_orig ) );
734 if ( !mtimeStr.isEmpty() ) {
735 QDateTime dt = QDateTime::fromString( mtimeStr, Qt::ISODate );
736 if ( dt.isValid() ) {
737 KDE_struct_stat dest_statbuf;
738 if (
KDE::stat( dest_orig, &dest_statbuf ) == 0) {
739 struct timeval utbuf[2];
741 utbuf[0].tv_sec = dest_statbuf.st_atime;
742 utbuf[0].tv_usec = 0;
744 utbuf[1].tv_sec = dt.toTime_t();
745 utbuf[1].tv_usec = dt.time().msec() * 1000;
746 utimes( QFile::encodeName(dest_orig), utbuf );
756 QString FileProtocol::getUserName( uid_t uid )
const
758 if ( !mUsercache.contains( uid ) ) {
759 struct passwd *user = getpwuid( uid );
761 mUsercache.insert( uid, QString::fromLatin1(user->pw_name) );
764 return QString::number( uid );
766 return mUsercache[uid];
769 QString FileProtocol::getGroupName( gid_t gid )
const
771 if ( !mGroupcache.contains( gid ) ) {
772 struct group *grp = getgrgid( gid );
774 mGroupcache.insert( gid, QString::fromLatin1(grp->gr_name) );
777 return QString::number( gid );
779 return mGroupcache[gid];
782 bool FileProtocol::createUDSEntry(
const QString & filename,
const QByteArray & path,
UDSEntry & entry,
783 short int details,
bool withACL )
785 #ifndef HAVE_POSIX_ACL
788 assert(entry.
count() == 0);
795 KDE_struct_stat buff;
797 if ( KDE_lstat( path.data(), &buff ) == 0 ) {
804 if (S_ISLNK(buff.st_mode)) {
806 char buffer2[ 1000 ];
807 int n = readlink( path.data(), buffer2, 999 );
815 if ( details > 1 && KDE_stat( path.data(), &buff ) == -1 ) {
818 access = S_IRWXU | S_IRWXG | S_IRWXO;
832 type = buff.st_mode & S_IFMT;
833 access = buff.st_mode & 07777;
840 #ifdef HAVE_POSIX_ACL
845 appendACLAtoms( path, entry, type, withACL );
867 QDataStream stream(data);
876 stream >> iRo >> fstype >> dev >> point;
878 bool ro = ( iRo != 0 );
880 kDebug(7101) <<
"MOUNTING fstype=" << fstype <<
" dev=" << dev <<
" point=" << point <<
" ro=" << ro;
885 mount( ro, fstype.toAscii(), dev, point );
908 kDebug(7101) <<
"fstype=" << _fstype;
916 QByteArray devname = QFile::encodeName( _dev );
918 if( volmgt_running() ) {
920 if( volmgt_check( devname.data() ) == 0 ) {
921 kDebug(7101) <<
"VOLMGT: no media in "
923 err =
i18n(
"No Media inserted or Media not recognized.");
927 kDebug(7101) <<
"VOLMGT: " << devname.data()
933 err =
i18n(
"\"vold\" is not running.");
934 kDebug(7101) <<
"VOLMGT: " << err;
942 tmpFile.setAutoRemove(
false);
944 QByteArray tmpFileName = QFile::encodeName(tmpFile.fileName());
946 if (_dev.startsWith(QLatin1String(
"LABEL="))) {
947 QString labelName = _dev.mid( 6 );
950 }
else if (_dev.startsWith(QLatin1String(
"UUID="))) {
951 QString uuidName = _dev.mid( 5 );
959 bool fstype_empty = !_fstype || !*_fstype;
960 QByteArray fstype =
KShell::quoteArg(QString::fromLatin1(_fstype)).toLatin1();
961 QByteArray readonly = _ro ?
"-r" :
"";
962 QString epath = QString::fromLocal8Bit(qgetenv(
"PATH"));
963 QString path = QLatin1String(
"/sbin:/bin");
965 path += QLatin1String(
":") + epath;
967 if (mountProg.isEmpty()){
973 for (
int step = 0 ; step <= 1 ; step++ )
975 QByteArray buffer = mountProg +
' ';
977 if ( !dev.isEmpty() && _point.isEmpty() && fstype_empty )
981 if ( !_point.isEmpty() && dev.isEmpty() && fstype_empty )
985 if ( !_point.isEmpty() && !dev.isEmpty() && fstype_empty )
986 buffer += readonly +
' ' + dev +
' ' + point;
989 #if defined(__svr4__) && defined(Q_OS_SOLARIS) // MARCO for Solaris 8 and I
991 buffer +=
"-F " + fstype +
' ' + (_ro ?
"-oro" :
"") +
' ' + dev +
' ' + point;
993 buffer += readonly +
" -t " + fstype +
' ' + dev +
' ' + point;
995 buffer +=
" 2>" + tmpFileName;
998 int mount_ret = system( buffer.constData() );
1001 if ( err.isEmpty() && mount_ret == 0)
1011 if ( mp && mount_ret == 0)
1013 kDebug(7101) <<
"mount got a warning:" << err;
1020 if ( (step == 0) && !_point.isEmpty())
1023 kDebug(7101) <<
"Mounting with those options didn't work, trying with only mountpoint";
1025 fstype_empty =
true;
1047 err =
i18n(
"mounting is not supported by wince.");
1060 tmpFile.setAutoRemove(
false);
1062 QByteArray tmpFileName = QFile::encodeName(tmpFile.fileName());
1074 if( volmgt_running() ) {
1075 kDebug(7101) <<
"VOLMGT: looking for "
1076 << _point.toLocal8Bit();
1078 if( (mnttab = KDE_fopen(
MNTTAB,
"r" )) == NULL ) {
1079 err = QLatin1String(
"could not open mnttab");
1080 kDebug(7101) <<
"VOLMGT: " << err;
1093 while( getmntent( mnttab, &mnt ) == 0 ) {
1094 if( strcmp( _point.toLocal8Bit(), mnt.mnt_mountp ) == 0 ){
1095 devname = mnt.mnt_special;
1101 if( devname == NULL ) {
1102 err = QLatin1String(
"not in mnttab");
1103 kDebug(7101) <<
"VOLMGT: "
1104 << QFile::encodeName(_point).data()
1115 ptr = strrchr( devname,
'/' );
1117 QByteArray qdevname(QFile::encodeName(
KShell::quoteArg(QFile::decodeName(QByteArray(devname)))).
data());
1118 buffer =
"/usr/bin/eject " + qdevname +
" 2>" + tmpFileName;
1119 kDebug(7101) <<
"VOLMGT: eject " << qdevname;
1125 if( WEXITSTATUS( system( buffer.constData() )) == 4 ) {
1142 err =
i18n(
"\"vold\" is not running.");
1143 kDebug(7101) <<
"VOLMGT: " << err;
1148 QString epath = QString::fromLocal8Bit(qgetenv(
"PATH"));
1149 QString path = QLatin1String(
"/sbin:/bin");
1150 if (!epath.isEmpty())
1151 path += QLatin1Char(
':') + epath;
1154 if (umountProg.isEmpty()) {
1158 buffer = umountProg +
' ' + QFile::encodeName(
KShell::quoteArg(_point)) +
" 2>" + tmpFileName;
1159 system( buffer.constData() );
1163 if ( err.isEmpty() )
1169 err =
i18n(
"unmounting is not supported by wince.");
1183 QString epath = QString::fromLocal8Bit(qgetenv(
"PATH"));
1184 QString path = QLatin1String(
"/sbin:/bin");
1185 if (!epath.isEmpty())
1186 path += QLatin1Char(
':') + epath;
1189 if (pmountProg.isEmpty())
1192 QByteArray buffer = QFile::encodeName(pmountProg) +
' ' +
1195 int res = system( buffer.constData() );
1209 QString dev = mp->realDeviceName();
1210 if (dev.isEmpty())
return false;
1212 QString epath = QString::fromLocal8Bit(qgetenv(
"PATH"));
1213 QString path = QLatin1String(
"/sbin:/bin");
1214 if (!epath.isEmpty())
1215 path += QLatin1Char(
':') + epath;
1218 if (pumountProg.isEmpty())
1221 QByteArray buffer = QFile::encodeName(pumountProg);
1225 int res = system( buffer.data() );
1241 char buffer[ 1024 ];
1242 KDE_struct_stat buff;
1246 KDE_stat( _filename, &buff );
1247 int size = buff.st_size;
1249 unlink( _filename );
1253 FILE * f = KDE_fopen( _filename,
"rb" );
1255 unlink( _filename );
1256 result =
i18n(
"Could not read %1", QFile::decodeName(_filename));
1263 p = fgets( buffer,
sizeof(buffer)-1, f );
1265 result += QString::fromLocal8Bit(buffer);
1270 unlink( _filename );
1280 #ifdef HAVE_POSIX_ACL
1282 static bool isExtendedACL( acl_t acl )
1284 return ( acl_equiv_mode( acl, 0 ) != 0 );
1287 static void appendACLAtoms(
const QByteArray & path,
UDSEntry& entry, mode_t type,
bool withACL )
1290 if ( acl_extended_file( path.data() ) == 0 )
return;
1293 acl_t defaultAcl = 0;
1294 bool isDir = S_ISDIR( type );
1296 acl = acl_get_file( path.data(), ACL_TYPE_ACCESS );
1301 if ( !isExtendedACL( acl ) ) {
1306 defaultAcl = acl_get_file( path.data(), ACL_TYPE_DEFAULT );
1308 if ( acl || defaultAcl ) {
1309 kDebug(7101) << path.constData() <<
"has extended ACL entries";
1314 const QString str = aclToText(acl);
1316 kDebug(7101) << path.constData() <<
"ACL:" << str;
1319 const QString str = aclToText(defaultAcl);
1321 kDebug(7101) << path.constData() <<
"DEFAULT ACL:" << str;
1324 if ( acl ) acl_free( acl );
1325 if ( defaultAcl ) acl_free( defaultAcl );
1331 bool FileProtocol::deleteRecursive(
const QString& path)
1334 QDirIterator it(path, QDir::AllEntries | QDir::NoDotAndDotDot | QDir::System | QDir::Hidden,
1335 QDirIterator::Subdirectories);
1337 while ( it.hasNext() ) {
1338 const QString itemPath = it.next();
1340 const QFileInfo info = it.fileInfo();
1341 if (info.isDir() && !info.isSymLink())
1342 dirsToDelete.prepend(itemPath);
1345 if (!QFile::remove(itemPath)) {
1352 Q_FOREACH(
const QString& itemPath, dirsToDelete) {
1354 if (!dir.rmdir(itemPath)) {