25 #define QT_NO_CAST_FROM_ASCII
28 #include <QDirIterator>
31 #include <config-kioslave-file.h>
34 #include <sys/types.h>
37 #include <sys/socket.h>
38 #ifdef HAVE_SYS_TIME_H
58 #include <QtCore/QByteRef>
59 #include <QtCore/QDate>
60 #include <QtCore/QVarLengthArray>
61 #include <QtCore/QCoreApplication>
62 #include <QtCore/QRegExp>
63 #include <QtCore/QFile>
65 #include <QtCore/QDir>
66 #include <QtCore/QFileInfo>
83 #include <sys/mnttab.h>
94 #define MAX_IPC_SIZE (1024*32)
98 static void appendACLAtoms(
const QByteArray & path,
UDSEntry& entry,
99 mode_t type,
bool withACL );
104 QCoreApplication app( argc, argv );
108 kDebug(7101) <<
"Starting" << getpid();
112 fprintf(stderr,
"Usage: kio_file protocol domain-socket1 domain-socket2\n");
124 :
SlaveBase(
"file", pool, app ), openFd(-1)
132 #ifdef HAVE_POSIX_ACL
133 static QString aclToText(acl_t acl) {
135 char* txt = acl_to_text(acl, &size);
136 const QString ret = QString::fromLatin1(txt, size);
142 int FileProtocol::setACL(
const char *path, mode_t perm,
bool directoryDefault )
145 #ifdef HAVE_POSIX_ACL
148 const QString defaultACLString =
metaData(QLatin1String(
"DEFAULT_ACL_STRING"));
150 if ( !ACLString.isEmpty() ) {
152 if (ACLString == QLatin1String(
"ACL_DELETE")) {
155 acl = acl_from_mode( perm );
157 acl = acl_from_text( ACLString.toLatin1() );
158 if ( acl_valid( acl ) == 0 ) {
159 ret = acl_set_file( path, ACL_TYPE_ACCESS, acl );
160 kDebug(7101) <<
"Set ACL on:" << path <<
"to:" << aclToText(acl);
163 if ( ret != 0 )
return ret;
166 if ( directoryDefault && !defaultACLString.isEmpty() ) {
167 if ( defaultACLString == QLatin1String(
"ACL_DELETE") ) {
169 ret += acl_delete_def_file( path );
171 acl_t acl = acl_from_text( defaultACLString.toLatin1() );
172 if ( acl_valid( acl ) == 0 ) {
173 ret += acl_set_file( path, ACL_TYPE_DEFAULT, acl );
174 kDebug(7101) <<
"Set Default ACL on:" << path <<
"to:" << aclToText(acl);
182 Q_UNUSED(directoryDefault);
190 const QByteArray _path( QFile::encodeName(path) );
193 ( setACL( _path.data(), permissions, false ) == -1 ) ||
195 ( setACL( _path.data(), permissions, true ) == -1 && errno != ENOTDIR ) ) {
220 KDE_struct_stat statbuf;
222 struct utimbuf utbuf;
223 utbuf.actime = statbuf.st_atime;
224 utbuf.modtime = mtime.toTime_t();
240 kDebug(7101) << path <<
"permission=" << permissions;
243 if (
metaData(QLatin1String(
"overwrite")) == QLatin1String(
"true"))
246 KDE_struct_stat buff;
249 if ( errno == EACCES ) {
252 }
else if ( errno == ENOSPC ) {
260 if ( permissions != -1 )
261 chmod( url, permissions );
268 if ( S_ISDIR( buff.st_mode ) ) {
269 kDebug(7101) <<
"ERR_DIR_ALREADY_EXIST";
288 KDE_struct_stat buff;
290 if ( errno == EACCES )
297 if ( S_ISDIR( buff.st_mode ) ) {
301 if ( !S_ISREG( buff.st_mode ) ) {
313 posix_fadvise( fd, 0, 0, POSIX_FADV_SEQUENTIAL);
329 if ( !resumeOffset.isEmpty() )
333 if (ok && (offset > 0) && (offset < buff.st_size))
335 if (KDE_lseek(fd, offset, SEEK_SET) == offset)
338 processed_size = offset;
361 array = QByteArray::fromRawData(buffer, n);
371 data( QByteArray() );
383 ssize_t written = write(fd, buf, len);
401 KDE_struct_stat buff;
403 if ( errno == EACCES )
410 if ( S_ISDIR( buff.st_mode ) ) {
414 if ( !S_ISREG( buff.st_mode ) ) {
420 if (mode & QIODevice::ReadOnly) {
421 if (mode & QIODevice::WriteOnly) {
422 flags = O_RDWR | O_CREAT;
426 }
else if (mode & QIODevice::WriteOnly) {
427 flags = O_WRONLY | O_CREAT;
430 if (mode & QIODevice::Append) {
432 }
else if (mode & QIODevice::Truncate) {
437 if ( flags & O_CREAT)
449 if (mode & QIODevice::ReadOnly){
463 kDebug(7101) <<
"File::open -- read";
464 Q_ASSERT(openFd != -1);
466 QVarLengthArray<char> buffer(bytes);
470 res =
::read(openFd, buffer.data(), bytes);
471 }
while (res == -1 && errno == EINTR);
474 QByteArray array = QByteArray::fromRawData(buffer.data(), res);
486 if (bytes <= 0)
break;
492 kDebug(7101) <<
"File::open -- write";
493 Q_ASSERT(openFd != -1);
495 if (
write_all(openFd, data.constData(), data.size())) {
496 if (errno == ENOSPC) {
500 kWarning(7101) <<
"Couldn't write. Error:" << strerror(errno);
511 kDebug(7101) <<
"File::open -- seek";
512 Q_ASSERT(openFd != -1);
514 int res = KDE_lseek(openFd, offset, SEEK_SET);
525 kDebug(7101) <<
"File::open -- close ";
526 Q_ASSERT(openFd != -1);
539 kDebug(7101) << dest_orig <<
"mode=" << _mode;
541 QString dest_part(dest_orig + QLatin1String(
".part"));
543 KDE_struct_stat buff_orig;
544 const bool bOrigExists = (
KDE::lstat(dest_orig, &buff_orig) != -1);
545 bool bPartExists =
false;
550 KDE_struct_stat buff_part;
551 bPartExists = (
KDE::stat( dest_part, &buff_part ) != -1);
553 if (bPartExists && !(_flags &
KIO::Resume) && !(_flags &
KIO::Overwrite) && buff_part.st_size > 0 && S_ISREG(buff_part.st_mode))
568 if (S_ISDIR(buff_orig.st_mode))
594 kDebug(7101) <<
"Appending .part extension to" << dest_orig;
598 kDebug(7101) <<
"Deleting partial file" << dest_part;
599 QFile::remove( dest_part );
608 kDebug(7101) <<
"Deleting destination file" << dest_orig;
609 QFile::remove( dest_orig );
617 KDE_lseek(fd, 0, SEEK_END);
625 initialMode = _mode | S_IWUSR | S_IRUSR;
629 fd =
KDE::open(dest, O_CREAT | O_TRUNC | O_WRONLY, initialMode);
634 kDebug(7101) <<
"####################### COULD NOT WRITE" << dest <<
"_mode=" << _mode;
635 kDebug(7101) <<
"errno==" << errno <<
"(" << strerror(errno) <<
")";
636 if ( errno == EACCES )
644 if (
write_all( fd, buffer.data(), buffer.size()))
646 if ( errno == ENOSPC )
653 kWarning(7101) <<
"Couldn't write. Error:" << strerror(errno);
660 while ( result > 0 );
665 kDebug(7101) <<
"Error during 'put'. Aborting.";
671 KDE_struct_stat buff;
672 if (bMarkPartial &&
KDE::stat( dest, &buff ) == 0)
675 if (buff.st_size < size)
676 remove(_dest.data());
691 kWarning(7101) <<
"Error when closing file descriptor:" << strerror(errno);
702 if( (_flags & KIO::Overwrite) && S_ISLNK( buff_orig.st_mode ) )
703 QFile::remove( dest_orig );
706 kWarning(7101) <<
" Couldn't rename " << _dest <<
" to " << dest_orig;
710 org::kde::KDirNotify::emitFileRenamed(dest, dest_orig);
721 warning(
i18n(
"Could not change permissions for\n%1" , dest_orig ) );
727 if ( !mtimeStr.isEmpty() ) {
728 QDateTime dt = QDateTime::fromString( mtimeStr, Qt::ISODate );
729 if ( dt.isValid() ) {
730 KDE_struct_stat dest_statbuf;
731 if (
KDE::stat( dest_orig, &dest_statbuf ) == 0) {
732 struct timeval utbuf[2];
734 utbuf[0].tv_sec = dest_statbuf.st_atime;
735 utbuf[0].tv_usec = 0;
737 utbuf[1].tv_sec = dt.toTime_t();
738 utbuf[1].tv_usec = dt.time().msec() * 1000;
739 utimes( QFile::encodeName(dest_orig), utbuf );
749 QString FileProtocol::getUserName( uid_t uid )
const
751 if ( !mUsercache.contains( uid ) ) {
752 struct passwd *user = getpwuid( uid );
754 mUsercache.insert( uid, QString::fromLatin1(user->pw_name) );
757 return QString::number( uid );
759 return mUsercache[uid];
762 QString FileProtocol::getGroupName( gid_t gid )
const
764 if ( !mGroupcache.contains( gid ) ) {
765 struct group *grp = getgrgid( gid );
767 mGroupcache.insert( gid, QString::fromLatin1(grp->gr_name) );
770 return QString::number( gid );
772 return mGroupcache[gid];
775 bool FileProtocol::createUDSEntry(
const QString & filename,
const QByteArray & path,
UDSEntry & entry,
776 short int details,
bool withACL )
778 #ifndef HAVE_POSIX_ACL
781 assert(entry.
count() == 0);
788 KDE_struct_stat buff;
790 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, 999 );
808 if ( details > 1 && KDE_stat( path.data(), &buff ) == -1 ) {
811 access = S_IRWXU | S_IRWXG | S_IRWXO;
825 type = buff.st_mode & S_IFMT;
826 access = buff.st_mode & 07777;
833 #ifdef HAVE_POSIX_ACL
838 appendACLAtoms( path, entry, type, withACL );
860 QDataStream stream(data);
869 stream >> iRo >> fstype >> dev >> point;
871 bool ro = ( iRo != 0 );
873 kDebug(7101) <<
"MOUNTING fstype=" << fstype <<
" dev=" << dev <<
" point=" << point <<
" ro=" << ro;
878 mount( ro, fstype.toLatin1(), dev, point );
901 kDebug(7101) <<
"fstype=" << _fstype;
909 QByteArray devname = QFile::encodeName( _dev );
911 if( volmgt_running() ) {
913 if( volmgt_check( devname.data() ) == 0 ) {
914 kDebug(7101) <<
"VOLMGT: no media in "
916 err =
i18n(
"No Media inserted or Media not recognized.");
920 kDebug(7101) <<
"VOLMGT: " << devname.data()
926 err =
i18n(
"\"vold\" is not running.");
927 kDebug(7101) <<
"VOLMGT: " << err;
935 tmpFile.setAutoRemove(
false);
937 QByteArray tmpFileName = QFile::encodeName(tmpFile.fileName());
939 if (_dev.startsWith(QLatin1String(
"LABEL="))) {
940 QString labelName = _dev.mid( 6 );
943 }
else if (_dev.startsWith(QLatin1String(
"UUID="))) {
944 QString uuidName = _dev.mid( 5 );
952 bool fstype_empty = !_fstype || !*_fstype;
953 QByteArray fstype =
KShell::quoteArg(QString::fromLatin1(_fstype)).toLatin1();
954 QByteArray readonly = _ro ?
"-r" :
"";
955 QString epath = QString::fromLocal8Bit(qgetenv(
"PATH"));
956 QString path = QLatin1String(
"/sbin:/bin");
958 path += QLatin1String(
":") + epath;
960 if (mountProg.isEmpty()){
966 for (
int step = 0 ; step <= 1 ; step++ )
968 QByteArray buffer = mountProg +
' ';
970 if ( !dev.isEmpty() && _point.isEmpty() && fstype_empty )
974 if ( !_point.isEmpty() && dev.isEmpty() && fstype_empty )
978 if ( !_point.isEmpty() && !dev.isEmpty() && fstype_empty )
979 buffer += readonly +
' ' + dev +
' ' + point;
982 #if defined(__svr4__) && defined(Q_OS_SOLARIS) // MARCO for Solaris 8 and I
984 buffer +=
"-F " + fstype +
' ' + (_ro ?
"-oro" :
"") +
' ' + dev +
' ' + point;
986 buffer += readonly +
" -t " + fstype +
' ' + dev +
' ' + point;
988 buffer +=
" 2>" + tmpFileName;
991 int mount_ret = system( buffer.constData() );
994 if ( err.isEmpty() && mount_ret == 0)
1004 if ( mp && mount_ret == 0)
1006 kDebug(7101) <<
"mount got a warning:" << err;
1013 if ( (step == 0) && !_point.isEmpty())
1016 kDebug(7101) <<
"Mounting with those options didn't work, trying with only mountpoint";
1018 fstype_empty =
true;
1040 err =
i18n(
"mounting is not supported by wince.");
1053 tmpFile.setAutoRemove(
false);
1055 QByteArray tmpFileName = QFile::encodeName(tmpFile.fileName());
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;
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()
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;
1118 if( WEXITSTATUS( system( buffer.constData() )) == 4 ) {
1135 err =
i18n(
"\"vold\" is not running.");
1136 kDebug(7101) <<
"VOLMGT: " << err;
1141 QString epath = QString::fromLocal8Bit(qgetenv(
"PATH"));
1142 QString path = QLatin1String(
"/sbin:/bin");
1143 if (!epath.isEmpty())
1144 path += QLatin1Char(
':') + epath;
1147 if (umountProg.isEmpty()) {
1151 buffer = umountProg +
' ' + QFile::encodeName(
KShell::quoteArg(_point)) +
" 2>" + tmpFileName;
1152 system( buffer.constData() );
1156 if ( err.isEmpty() )
1162 err =
i18n(
"unmounting is not supported by wince.");
1176 QString epath = QString::fromLocal8Bit(qgetenv(
"PATH"));
1177 QString path = QLatin1String(
"/sbin:/bin");
1178 if (!epath.isEmpty())
1179 path += QLatin1Char(
':') + epath;
1182 if (pmountProg.isEmpty())
1185 QByteArray buffer = QFile::encodeName(pmountProg) +
' ' +
1188 int res = system( buffer.constData() );
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;
1211 if (pumountProg.isEmpty())
1214 QByteArray buffer = QFile::encodeName(pumountProg);
1218 int res = system( buffer.data() );
1235 QFile file(QFile::decodeName(_filename));
1236 if (file.open(QIODevice::ReadOnly)) {
1237 result = QString::fromLocal8Bit(file.readAll());
1239 (void)file.remove();
1248 #ifdef HAVE_POSIX_ACL
1250 bool FileProtocol::isExtendedACL( acl_t acl )
1252 return ( acl_equiv_mode( acl, 0 ) != 0 );
1255 static void appendACLAtoms(
const QByteArray & path,
UDSEntry& entry, mode_t type,
bool withACL )
1258 if ( acl_extended_file( path.data() ) == 0 )
return;
1261 acl_t defaultAcl = 0;
1262 bool isDir = S_ISDIR( type );
1264 acl = acl_get_file( path.data(), ACL_TYPE_ACCESS );
1269 if ( !FileProtocol::isExtendedACL( acl ) ) {
1274 defaultAcl = acl_get_file( path.data(), ACL_TYPE_DEFAULT );
1276 if ( acl || defaultAcl ) {
1277 kDebug(7101) << path.constData() <<
"has extended ACL entries";
1282 const QString str = aclToText(acl);
1284 kDebug(7101) << path.constData() <<
"ACL:" << str;
1287 const QString str = aclToText(defaultAcl);
1289 kDebug(7101) << path.constData() <<
"DEFAULT ACL:" << str;
1292 if ( acl ) acl_free( acl );
1293 if ( defaultAcl ) acl_free( defaultAcl );
1299 bool FileProtocol::deleteRecursive(
const QString& path)
1302 QDirIterator it(path, QDir::AllEntries | QDir::NoDotAndDotDot | QDir::System | QDir::Hidden,
1303 QDirIterator::Subdirectories);
1305 while ( it.hasNext() ) {
1306 const QString itemPath = it.next();
1308 const QFileInfo info = it.fileInfo();
1309 if (info.isDir() && !info.isSymLink())
1310 dirsToDelete.prepend(itemPath);
1313 if (!QFile::remove(itemPath)) {
1320 Q_FOREACH(
const QString& itemPath, dirsToDelete) {
1322 if (!dir.rmdir(itemPath)) {