• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdelibs-4.10.0 API Reference
  • KDE Home
  • Contact Us
 

KIOSlave

  • kioslave
  • file
file.cpp
Go to the documentation of this file.
1 /*
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>
7 
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
12  version.
13 
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.
18 
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.
23 */
24 
25 #define QT_NO_CAST_FROM_ASCII
26 
27 #include "file.h"
28 #include <QDirIterator>
29 
30 #include <config.h>
31 #include <config-kioslave-file.h>
32 
33 
34 #include <sys/types.h>
35 #include <sys/wait.h>
36 #include <sys/stat.h>
37 #include <sys/socket.h>
38 #ifdef HAVE_SYS_TIME_H
39 #include <sys/time.h>
40 #endif
41 
42 #include <assert.h>
43 #include <dirent.h>
44 #include <errno.h>
45 #include <fcntl.h>
46 #include <grp.h>
47 #include <pwd.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <signal.h>
51 #include <time.h>
52 #include <utime.h>
53 #include <unistd.h>
54 #ifdef HAVE_STRING_H
55 #include <string.h>
56 #endif
57 
58 #include <QtCore/QByteRef>
59 #include <QtCore/QDate>
60 #include <QtCore/QVarLengthArray>
61 #include <QtCore/QCoreApplication>
62 #include <QtCore/QRegExp>
63 #include <QtCore/QFile>
64 #ifdef Q_WS_WIN
65 #include <QtCore/QDir>
66 #include <QtCore/QFileInfo>
67 #endif
68 
69 #include <kdebug.h>
70 #include <kurl.h>
71 #include <kcomponentdata.h>
72 #include <kconfig.h>
73 #include <kconfiggroup.h>
74 #include <ktemporaryfile.h>
75 #include <klocale.h>
76 #include <limits.h>
77 #include <kshell.h>
78 #include <kmountpoint.h>
79 #include <kstandarddirs.h>
80 
81 #ifdef HAVE_VOLMGT
82 #include <volmgt.h>
83 #include <sys/mnttab.h>
84 #endif
85 
86 #include <kdirnotify.h>
87 #include <kio/ioslave_defaults.h>
88 #include <kde_file.h>
89 #include <kglobal.h>
90 #include <kmimetype.h>
91 
92 using namespace KIO;
93 
94 #define MAX_IPC_SIZE (1024*32)
95 
96 static QString readLogFile( const QByteArray&_filename );
97 #ifdef HAVE_POSIX_ACL
98 static void appendACLAtoms( const QByteArray & path, UDSEntry& entry,
99  mode_t type, bool withACL );
100 #endif
101 
102 extern "C" int KDE_EXPORT kdemain( int argc, char **argv )
103 {
104  QCoreApplication app( argc, argv ); // needed for QSocketNotifier
105  KComponentData componentData( "kio_file", "kdelibs4" );
106  ( void ) KGlobal::locale();
107 
108  kDebug(7101) << "Starting" << getpid();
109 
110  if (argc != 4)
111  {
112  fprintf(stderr, "Usage: kio_file protocol domain-socket1 domain-socket2\n");
113  exit(-1);
114  }
115 
116  FileProtocol slave(argv[2], argv[3]);
117  slave.dispatchLoop();
118 
119  kDebug(7101) << "Done";
120  return 0;
121 }
122 
123 FileProtocol::FileProtocol( const QByteArray &pool, const QByteArray &app )
124  : SlaveBase( "file", pool, app ), openFd(-1)
125 {
126 }
127 
128 FileProtocol::~FileProtocol()
129 {
130 }
131 
132 #ifdef HAVE_POSIX_ACL
133 static QString aclToText(acl_t acl) {
134  ssize_t size = 0;
135  char* txt = acl_to_text(acl, &size);
136  const QString ret = QString::fromLatin1(txt, size);
137  acl_free(txt);
138  return ret;
139 }
140 #endif
141 
142 int FileProtocol::setACL( const char *path, mode_t perm, bool directoryDefault )
143 {
144  int ret = 0;
145 #ifdef HAVE_POSIX_ACL
146 
147  const QString ACLString = metaData(QLatin1String("ACL_STRING"));
148  const QString defaultACLString = metaData(QLatin1String("DEFAULT_ACL_STRING"));
149  // Empty strings mean leave as is
150  if ( !ACLString.isEmpty() ) {
151  acl_t acl = 0;
152  if (ACLString == QLatin1String("ACL_DELETE")) {
153  // user told us to delete the extended ACL, so let's write only
154  // the minimal (UNIX permission bits) part
155  acl = acl_from_mode( perm );
156  }
157  acl = acl_from_text( ACLString.toLatin1() );
158  if ( acl_valid( acl ) == 0 ) { // let's be safe
159  ret = acl_set_file( path, ACL_TYPE_ACCESS, acl );
160  kDebug(7101) << "Set ACL on:" << path << "to:" << aclToText(acl);
161  }
162  acl_free( acl );
163  if ( ret != 0 ) return ret; // better stop trying right away
164  }
165 
166  if ( directoryDefault && !defaultACLString.isEmpty() ) {
167  if ( defaultACLString == QLatin1String("ACL_DELETE") ) {
168  // user told us to delete the default ACL, do so
169  ret += acl_delete_def_file( path );
170  } else {
171  acl_t acl = acl_from_text( defaultACLString.toLatin1() );
172  if ( acl_valid( acl ) == 0 ) { // let's be safe
173  ret += acl_set_file( path, ACL_TYPE_DEFAULT, acl );
174  kDebug(7101) << "Set Default ACL on:" << path << "to:" << aclToText(acl);
175  }
176  acl_free( acl );
177  }
178  }
179 #else
180  Q_UNUSED(path);
181  Q_UNUSED(perm);
182  Q_UNUSED(directoryDefault);
183 #endif
184  return ret;
185 }
186 
187 void FileProtocol::chmod( const KUrl& url, int permissions )
188 {
189  const QString path(url.toLocalFile());
190  const QByteArray _path( QFile::encodeName(path) );
191  /* FIXME: Should be atomic */
192  if ( KDE::chmod( path, permissions ) == -1 ||
193  ( setACL( _path.data(), permissions, false ) == -1 ) ||
194  /* if not a directory, cannot set default ACLs */
195  ( setACL( _path.data(), permissions, true ) == -1 && errno != ENOTDIR ) ) {
196 
197  switch (errno) {
198  case EPERM:
199  case EACCES:
200  error(KIO::ERR_ACCESS_DENIED, path);
201  break;
202 #if defined(ENOTSUP)
203  case ENOTSUP: // from setACL since chmod can't return ENOTSUP
204  error(KIO::ERR_UNSUPPORTED_ACTION, i18n("Setting ACL for %1", path));
205  break;
206 #endif
207  case ENOSPC:
208  error(KIO::ERR_DISK_FULL, path);
209  break;
210  default:
211  error(KIO::ERR_CANNOT_CHMOD, path);
212  }
213  } else
214  finished();
215 }
216 
217 void FileProtocol::setModificationTime( const KUrl& url, const QDateTime& mtime )
218 {
219  const QString path(url.toLocalFile());
220  KDE_struct_stat statbuf;
221  if (KDE::lstat(path, &statbuf) == 0) {
222  struct utimbuf utbuf;
223  utbuf.actime = statbuf.st_atime; // access time, unchanged
224  utbuf.modtime = mtime.toTime_t(); // modification time
225  if (KDE::utime(path, &utbuf) != 0) {
226  // TODO: errno could be EACCES, EPERM, EROFS
227  error(KIO::ERR_CANNOT_SETTIME, path);
228  } else {
229  finished();
230  }
231  } else {
232  error(KIO::ERR_DOES_NOT_EXIST, path);
233  }
234 }
235 
236 void FileProtocol::mkdir( const KUrl& url, int permissions )
237 {
238  const QString path(url.toLocalFile());
239 
240  kDebug(7101) << path << "permission=" << permissions;
241 
242  // Remove existing file or symlink, if requested (#151851)
243  if (metaData(QLatin1String("overwrite")) == QLatin1String("true"))
244  QFile::remove(path);
245 
246  KDE_struct_stat buff;
247  if ( KDE::lstat( path, &buff ) == -1 ) {
248  if ( KDE::mkdir( path, 0777 /*umask will be applied*/ ) != 0 ) {
249  if ( errno == EACCES ) {
250  error(KIO::ERR_ACCESS_DENIED, path);
251  return;
252  } else if ( errno == ENOSPC ) {
253  error(KIO::ERR_DISK_FULL, path);
254  return;
255  } else {
256  error(KIO::ERR_COULD_NOT_MKDIR, path);
257  return;
258  }
259  } else {
260  if ( permissions != -1 )
261  chmod( url, permissions );
262  else
263  finished();
264  return;
265  }
266  }
267 
268  if ( S_ISDIR( buff.st_mode ) ) {
269  kDebug(7101) << "ERR_DIR_ALREADY_EXIST";
270  error(KIO::ERR_DIR_ALREADY_EXIST, path);
271  return;
272  }
273  error(KIO::ERR_FILE_ALREADY_EXIST, path);
274  return;
275 }
276 
277 void FileProtocol::get( const KUrl& url )
278 {
279  if (!url.isLocalFile()) {
280  KUrl redir(url);
281  redir.setProtocol(config()->readEntry("DefaultRemoteProtocol", "smb"));
282  redirection(redir);
283  finished();
284  return;
285  }
286 
287  const QString path(url.toLocalFile());
288  KDE_struct_stat buff;
289  if ( KDE::stat( path, &buff ) == -1 ) {
290  if ( errno == EACCES )
291  error(KIO::ERR_ACCESS_DENIED, path);
292  else
293  error(KIO::ERR_DOES_NOT_EXIST, path);
294  return;
295  }
296 
297  if ( S_ISDIR( buff.st_mode ) ) {
298  error(KIO::ERR_IS_DIRECTORY, path);
299  return;
300  }
301  if ( !S_ISREG( buff.st_mode ) ) {
302  error(KIO::ERR_CANNOT_OPEN_FOR_READING, path);
303  return;
304  }
305 
306  int fd = KDE::open( path, O_RDONLY);
307  if ( fd < 0 ) {
308  error(KIO::ERR_CANNOT_OPEN_FOR_READING, path);
309  return;
310  }
311 
312 #if HAVE_FADVISE
313  posix_fadvise( fd, 0, 0, POSIX_FADV_SEQUENTIAL);
314 #endif
315 
316  // Determine the mimetype of the file to be retrieved, and emit it.
317  // This is mandatory in all slaves (for KRun/BrowserRun to work)
318  // In real "remote" slaves, this is usually done using findByNameAndContent
319  // after receiving some data. But we don't know how much data the mimemagic rules
320  // need, so for local files, better use findByUrl with localUrl=true.
321  KMimeType::Ptr mt = KMimeType::findByUrl( url, buff.st_mode, true /* local URL */ );
322  emit mimeType( mt->name() );
323  // Emit total size AFTER mimetype
324  totalSize( buff.st_size );
325 
326  KIO::filesize_t processed_size = 0;
327 
328  const QString resumeOffset = metaData(QLatin1String("resume"));
329  if ( !resumeOffset.isEmpty() )
330  {
331  bool ok;
332  KIO::fileoffset_t offset = resumeOffset.toLongLong(&ok);
333  if (ok && (offset > 0) && (offset < buff.st_size))
334  {
335  if (KDE_lseek(fd, offset, SEEK_SET) == offset)
336  {
337  canResume ();
338  processed_size = offset;
339  kDebug(7101) << "Resume offset:" << KIO::number(offset);
340  }
341  }
342  }
343 
344  char buffer[ MAX_IPC_SIZE ];
345  QByteArray array;
346 
347  while( 1 )
348  {
349  int n = ::read( fd, buffer, MAX_IPC_SIZE );
350  if (n == -1)
351  {
352  if (errno == EINTR)
353  continue;
354  error(KIO::ERR_COULD_NOT_READ, path);
355  ::close(fd);
356  return;
357  }
358  if (n == 0)
359  break; // Finished
360 
361  array = QByteArray::fromRawData(buffer, n);
362  data( array );
363  array.clear();
364 
365  processed_size += n;
366  processedSize( processed_size );
367 
368  //kDebug(7101) << "Processed: " << KIO::number (processed_size);
369  }
370 
371  data( QByteArray() );
372 
373  ::close( fd );
374 
375  processedSize( buff.st_size );
376  finished();
377 }
378 
379 int write_all(int fd, const char *buf, size_t len)
380 {
381  while (len > 0)
382  {
383  ssize_t written = write(fd, buf, len);
384  if (written < 0)
385  {
386  if (errno == EINTR)
387  continue;
388  return -1;
389  }
390  buf += written;
391  len -= written;
392  }
393  return 0;
394 }
395 
396 void FileProtocol::open(const KUrl &url, QIODevice::OpenMode mode)
397 {
398  kDebug(7101) << url;
399 
400  openPath = url.toLocalFile();
401  KDE_struct_stat buff;
402  if (KDE::stat(openPath, &buff) == -1) {
403  if ( errno == EACCES )
404  error(KIO::ERR_ACCESS_DENIED, openPath);
405  else
406  error(KIO::ERR_DOES_NOT_EXIST, openPath);
407  return;
408  }
409 
410  if ( S_ISDIR( buff.st_mode ) ) {
411  error(KIO::ERR_IS_DIRECTORY, openPath);
412  return;
413  }
414  if ( !S_ISREG( buff.st_mode ) ) {
415  error(KIO::ERR_CANNOT_OPEN_FOR_READING, openPath);
416  return;
417  }
418 
419  int flags = 0;
420  if (mode & QIODevice::ReadOnly) {
421  if (mode & QIODevice::WriteOnly) {
422  flags = O_RDWR | O_CREAT;
423  } else {
424  flags = O_RDONLY;
425  }
426  } else if (mode & QIODevice::WriteOnly) {
427  flags = O_WRONLY | O_CREAT;
428  }
429 
430  if (mode & QIODevice::Append) {
431  flags |= O_APPEND;
432  } else if (mode & QIODevice::Truncate) {
433  flags |= O_TRUNC;
434  }
435 
436  int fd = -1;
437  if ( flags & O_CREAT)
438  fd = KDE::open( openPath, flags, 0666);
439  else
440  fd = KDE::open( openPath, flags);
441  if ( fd < 0 ) {
442  error(KIO::ERR_CANNOT_OPEN_FOR_READING, openPath);
443  return;
444  }
445  // Determine the mimetype of the file to be retrieved, and emit it.
446  // This is mandatory in all slaves (for KRun/BrowserRun to work).
447  // If we're not opening the file ReadOnly or ReadWrite, don't attempt to
448  // read the file and send the mimetype.
449  if (mode & QIODevice::ReadOnly){
450  KMimeType::Ptr mt = KMimeType::findByUrl( url, buff.st_mode, true /* local URL */ );
451  emit mimeType( mt->name() );
452  }
453 
454  totalSize( buff.st_size );
455  position( 0 );
456 
457  emit opened();
458  openFd = fd;
459 }
460 
461 void FileProtocol::read(KIO::filesize_t bytes)
462 {
463  kDebug(7101) << "File::open -- read";
464  Q_ASSERT(openFd != -1);
465 
466  QVarLengthArray<char> buffer(bytes);
467  while (true) {
468  int res;
469  do {
470  res = ::read(openFd, buffer.data(), bytes);
471  } while (res == -1 && errno == EINTR);
472 
473  if (res > 0) {
474  QByteArray array = QByteArray::fromRawData(buffer.data(), res);
475  data( array );
476  bytes -= res;
477  } else {
478  // empty array designates eof
479  data(QByteArray());
480  if (res != 0) {
481  error(KIO::ERR_COULD_NOT_READ, openPath);
482  close();
483  }
484  break;
485  }
486  if (bytes <= 0) break;
487  }
488 }
489 
490 void FileProtocol::write(const QByteArray &data)
491 {
492  kDebug(7101) << "File::open -- write";
493  Q_ASSERT(openFd != -1);
494 
495  if (write_all(openFd, data.constData(), data.size())) {
496  if (errno == ENOSPC) { // disk full
497  error(KIO::ERR_DISK_FULL, openPath);
498  close();
499  } else {
500  kWarning(7101) << "Couldn't write. Error:" << strerror(errno);
501  error(KIO::ERR_COULD_NOT_WRITE, openPath);
502  close();
503  }
504  } else {
505  written(data.size());
506  }
507 }
508 
509 void FileProtocol::seek(KIO::filesize_t offset)
510 {
511  kDebug(7101) << "File::open -- seek";
512  Q_ASSERT(openFd != -1);
513 
514  int res = KDE_lseek(openFd, offset, SEEK_SET);
515  if (res != -1) {
516  position( offset );
517  } else {
518  error(KIO::ERR_COULD_NOT_SEEK, openPath);
519  close();
520  }
521 }
522 
523 void FileProtocol::close()
524 {
525  kDebug(7101) << "File::open -- close ";
526  Q_ASSERT(openFd != -1);
527 
528  ::close( openFd );
529  openFd = -1;
530  openPath.clear();
531 
532  finished();
533 }
534 
535 void FileProtocol::put( const KUrl& url, int _mode, KIO::JobFlags _flags )
536 {
537  const QString dest_orig = url.toLocalFile();
538 
539  kDebug(7101) << dest_orig << "mode=" << _mode;
540 
541  QString dest_part(dest_orig + QLatin1String(".part"));
542 
543  KDE_struct_stat buff_orig;
544  const bool bOrigExists = (KDE::lstat(dest_orig, &buff_orig) != -1);
545  bool bPartExists = false;
546  const bool bMarkPartial = config()->readEntry("MarkPartial", true);
547 
548  if (bMarkPartial)
549  {
550  KDE_struct_stat buff_part;
551  bPartExists = (KDE::stat( dest_part, &buff_part ) != -1);
552 
553  if (bPartExists && !(_flags & KIO::Resume) && !(_flags & KIO::Overwrite) && buff_part.st_size > 0 && S_ISREG(buff_part.st_mode))
554  {
555  kDebug(7101) << "calling canResume with" << KIO::number(buff_part.st_size);
556 
557  // Maybe we can use this partial file for resuming
558  // Tell about the size we have, and the app will tell us
559  // if it's ok to resume or not.
560  _flags |= canResume( buff_part.st_size ) ? KIO::Resume : KIO::DefaultFlags;
561 
562  kDebug(7101) << "got answer" << (_flags & KIO::Resume);
563  }
564  }
565 
566  if ( bOrigExists && !(_flags & KIO::Overwrite) && !(_flags & KIO::Resume))
567  {
568  if (S_ISDIR(buff_orig.st_mode))
569  error( KIO::ERR_DIR_ALREADY_EXIST, dest_orig );
570  else
571  error( KIO::ERR_FILE_ALREADY_EXIST, dest_orig );
572  return;
573  }
574 
575  int result;
576  QString dest;
577  QByteArray _dest;
578 
579  int fd = -1;
580 
581  // Loop until we got 0 (end of data)
582  do
583  {
584  QByteArray buffer;
585  dataReq(); // Request for data
586  result = readData( buffer );
587 
588  if (result >= 0)
589  {
590  if (dest.isEmpty())
591  {
592  if (bMarkPartial)
593  {
594  kDebug(7101) << "Appending .part extension to" << dest_orig;
595  dest = dest_part;
596  if ( bPartExists && !(_flags & KIO::Resume) )
597  {
598  kDebug(7101) << "Deleting partial file" << dest_part;
599  QFile::remove( dest_part );
600  // Catch errors when we try to open the file.
601  }
602  }
603  else
604  {
605  dest = dest_orig;
606  if ( bOrigExists && !(_flags & KIO::Resume) )
607  {
608  kDebug(7101) << "Deleting destination file" << dest_orig;
609  QFile::remove( dest_orig );
610  // Catch errors when we try to open the file.
611  }
612  }
613 
614  if ( (_flags & KIO::Resume) )
615  {
616  fd = KDE::open( dest, O_RDWR ); // append if resuming
617  KDE_lseek(fd, 0, SEEK_END); // Seek to end
618  }
619  else
620  {
621  // WABA: Make sure that we keep writing permissions ourselves,
622  // otherwise we can be in for a surprise on NFS.
623  mode_t initialMode;
624  if (_mode != -1)
625  initialMode = _mode | S_IWUSR | S_IRUSR;
626  else
627  initialMode = 0666;
628 
629  fd = KDE::open(dest, O_CREAT | O_TRUNC | O_WRONLY, initialMode);
630  }
631 
632  if ( fd < 0 )
633  {
634  kDebug(7101) << "####################### COULD NOT WRITE" << dest << "_mode=" << _mode;
635  kDebug(7101) << "errno==" << errno << "(" << strerror(errno) << ")";
636  if ( errno == EACCES )
637  error(KIO::ERR_WRITE_ACCESS_DENIED, dest);
638  else
639  error(KIO::ERR_CANNOT_OPEN_FOR_WRITING, dest);
640  return;
641  }
642  }
643 
644  if (write_all( fd, buffer.data(), buffer.size()))
645  {
646  if ( errno == ENOSPC ) // disk full
647  {
648  error(KIO::ERR_DISK_FULL, dest_orig);
649  result = -2; // means: remove dest file
650  }
651  else
652  {
653  kWarning(7101) << "Couldn't write. Error:" << strerror(errno);
654  error(KIO::ERR_COULD_NOT_WRITE, dest_orig);
655  result = -1;
656  }
657  }
658  }
659  }
660  while ( result > 0 );
661 
662  // An error occurred deal with it.
663  if (result < 0)
664  {
665  kDebug(7101) << "Error during 'put'. Aborting.";
666 
667  if (fd != -1)
668  {
669  ::close(fd);
670 
671  KDE_struct_stat buff;
672  if (bMarkPartial && KDE::stat( dest, &buff ) == 0)
673  {
674  int size = config()->readEntry("MinimumKeepSize", DEFAULT_MINIMUM_KEEP_SIZE);
675  if (buff.st_size < size)
676  remove(_dest.data());
677  }
678  }
679 
680  ::exit(255);
681  }
682 
683  if ( fd == -1 ) // we got nothing to write out, so we never opened the file
684  {
685  finished();
686  return;
687  }
688 
689  if ( ::close(fd) )
690  {
691  kWarning(7101) << "Error when closing file descriptor:" << strerror(errno);
692  error(KIO::ERR_COULD_NOT_WRITE, dest_orig);
693  return;
694  }
695 
696  // after full download rename the file back to original name
697  if ( bMarkPartial )
698  {
699  // If the original URL is a symlink and we were asked to overwrite it,
700  // remove the symlink first. This ensures that we do not overwrite the
701  // current source if the symlink points to it.
702  if( (_flags & KIO::Overwrite) && S_ISLNK( buff_orig.st_mode ) )
703  QFile::remove( dest_orig );
704  if ( KDE::rename( dest, dest_orig ) )
705  {
706  kWarning(7101) << " Couldn't rename " << _dest << " to " << dest_orig;
707  error(KIO::ERR_CANNOT_RENAME_PARTIAL, dest_orig);
708  return;
709  }
710  org::kde::KDirNotify::emitFileRenamed(dest, dest_orig);
711  }
712 
713  // set final permissions
714  if ( _mode != -1 && !(_flags & KIO::Resume) )
715  {
716  if (KDE::chmod(dest_orig, _mode) != 0)
717  {
718  // couldn't chmod. Eat the error if the filesystem apparently doesn't support it.
719  KMountPoint::Ptr mp = KMountPoint::currentMountPoints().findByPath(dest_orig);
720  if (mp && mp->testFileSystemFlag(KMountPoint::SupportsChmod))
721  warning( i18n( "Could not change permissions for\n%1" , dest_orig ) );
722  }
723  }
724 
725  // set modification time
726  const QString mtimeStr = metaData(QLatin1String("modified"));
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];
733  // access time
734  utbuf[0].tv_sec = dest_statbuf.st_atime; // access time, unchanged ## TODO preserve msec
735  utbuf[0].tv_usec = 0;
736  // modification time
737  utbuf[1].tv_sec = dt.toTime_t();
738  utbuf[1].tv_usec = dt.time().msec() * 1000;
739  utimes( QFile::encodeName(dest_orig), utbuf );
740  }
741  }
742 
743  }
744 
745  // We have done our job => finish
746  finished();
747 }
748 
749 QString FileProtocol::getUserName( uid_t uid ) const
750 {
751  if ( !mUsercache.contains( uid ) ) {
752  struct passwd *user = getpwuid( uid );
753  if ( user ) {
754  mUsercache.insert( uid, QString::fromLatin1(user->pw_name) );
755  }
756  else
757  return QString::number( uid );
758  }
759  return mUsercache[uid];
760 }
761 
762 QString FileProtocol::getGroupName( gid_t gid ) const
763 {
764  if ( !mGroupcache.contains( gid ) ) {
765  struct group *grp = getgrgid( gid );
766  if ( grp ) {
767  mGroupcache.insert( gid, QString::fromLatin1(grp->gr_name) );
768  }
769  else
770  return QString::number( gid );
771  }
772  return mGroupcache[gid];
773 }
774 
775 bool FileProtocol::createUDSEntry( const QString & filename, const QByteArray & path, UDSEntry & entry,
776  short int details, bool withACL )
777 {
778 #ifndef HAVE_POSIX_ACL
779  Q_UNUSED(withACL);
780 #endif
781  assert(entry.count() == 0); // by contract :-)
782  // entry.reserve( 8 ); // speed up QHash insertion
783 
784  entry.insert( KIO::UDSEntry::UDS_NAME, filename );
785 
786  mode_t type;
787  mode_t access;
788  KDE_struct_stat buff;
789 
790  if ( KDE_lstat( path.data(), &buff ) == 0 ) {
791 
792  if (details > 2) {
793  entry.insert( KIO::UDSEntry::UDS_DEVICE_ID, buff.st_dev );
794  entry.insert( KIO::UDSEntry::UDS_INODE, buff.st_ino );
795  }
796 
797  if (S_ISLNK(buff.st_mode)) {
798 
799  char buffer2[ 1000 ];
800  int n = readlink( path.data(), buffer2, 999 );
801  if ( n != -1 ) {
802  buffer2[ n ] = 0;
803  }
804 
805  entry.insert( KIO::UDSEntry::UDS_LINK_DEST, QFile::decodeName( buffer2 ) );
806 
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
810  type = S_IFMT - 1;
811  access = S_IRWXU | S_IRWXG | S_IRWXO;
812 
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 );
816  goto notype;
817 
818  }
819  }
820  } else {
821  // kWarning() << "lstat didn't work on " << path.data();
822  return false;
823  }
824 
825  type = buff.st_mode & S_IFMT; // extract file type
826  access = buff.st_mode & 07777; // extract permissions
827 
828  entry.insert( KIO::UDSEntry::UDS_FILE_TYPE, type );
829  entry.insert( KIO::UDSEntry::UDS_ACCESS, access );
830 
831  entry.insert( KIO::UDSEntry::UDS_SIZE, buff.st_size );
832 
833 #ifdef HAVE_POSIX_ACL
834  if (details > 0) {
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 );
839  }
840 #endif
841 
842  notype:
843  if (details > 0) {
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 );
848  }
849 
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.
853 
854  return true;
855 }
856 
857 void FileProtocol::special( const QByteArray &data)
858 {
859  int tmp;
860  QDataStream stream(data);
861 
862  stream >> tmp;
863  switch (tmp) {
864  case 1:
865  {
866  QString fstype, dev, point;
867  qint8 iRo;
868 
869  stream >> iRo >> fstype >> dev >> point;
870 
871  bool ro = ( iRo != 0 );
872 
873  kDebug(7101) << "MOUNTING fstype=" << fstype << " dev=" << dev << " point=" << point << " ro=" << ro;
874  bool ok = pmount( dev );
875  if (ok)
876  finished();
877  else
878  mount( ro, fstype.toLatin1(), dev, point );
879 
880  }
881  break;
882  case 2:
883  {
884  QString point;
885  stream >> point;
886  bool ok = pumount( point );
887  if (ok)
888  finished();
889  else
890  unmount( point );
891  }
892  break;
893 
894  default:
895  break;
896  }
897 }
898 
899 void FileProtocol::mount( bool _ro, const char *_fstype, const QString& _dev, const QString& _point )
900 {
901  kDebug(7101) << "fstype=" << _fstype;
902 
903 #ifndef _WIN32_WCE
904 #ifdef HAVE_VOLMGT
905  /*
906  * support for Solaris volume management
907  */
908  QString err;
909  QByteArray devname = QFile::encodeName( _dev );
910 
911  if( volmgt_running() ) {
912 // kDebug(7101) << "VOLMGT: vold ok.";
913  if( volmgt_check( devname.data() ) == 0 ) {
914  kDebug(7101) << "VOLMGT: no media in "
915  << devname.data();
916  err = i18n("No Media inserted or Media not recognized.");
917  error( KIO::ERR_COULD_NOT_MOUNT, err );
918  return;
919  } else {
920  kDebug(7101) << "VOLMGT: " << devname.data()
921  << ": media ok";
922  finished();
923  return;
924  }
925  } else {
926  err = i18n("\"vold\" is not running.");
927  kDebug(7101) << "VOLMGT: " << err;
928  error( KIO::ERR_COULD_NOT_MOUNT, err );
929  return;
930  }
931 #else
932 
933 
934  KTemporaryFile tmpFile;
935  tmpFile.setAutoRemove(false);
936  tmpFile.open();
937  QByteArray tmpFileName = QFile::encodeName(tmpFile.fileName());
938  QByteArray dev;
939  if (_dev.startsWith(QLatin1String("LABEL="))) { // turn LABEL=foo into -L foo (#71430)
940  QString labelName = _dev.mid( 6 );
941  dev = "-L ";
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 );
945  dev = "-U ";
946  dev += QFile::encodeName( KShell::quoteArg( uuidName ) );
947  }
948  else
949  dev = QFile::encodeName( KShell::quoteArg(_dev) ); // get those ready to be given to a shell
950 
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");
957  if(!epath.isEmpty())
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\""));
962  return;
963  }
964 
965  // Two steps, in case mount doesn't like it when we pass all options
966  for ( int step = 0 ; step <= 1 ; step++ )
967  {
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 )
971  buffer += dev;
972  else
973  // Mount using the mountpoint, if no fstype nor device (impossible in first step)
974  if ( !_point.isEmpty() && dev.isEmpty() && fstype_empty )
975  buffer += point;
976  else
977  // mount giving device + mountpoint but no fstype
978  if ( !_point.isEmpty() && !dev.isEmpty() && fstype_empty )
979  buffer += readonly + ' ' + dev + ' ' + point;
980  else
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;
985 #else
986  buffer += readonly + " -t " + fstype + ' ' + dev + ' ' + point;
987 #endif
988  buffer += " 2>" + tmpFileName;
989  kDebug(7101) << buffer;
990 
991  int mount_ret = system( buffer.constData() );
992 
993  QString err = readLogFile( tmpFileName );
994  if ( err.isEmpty() && mount_ret == 0)
995  {
996  finished();
997  return;
998  }
999  else
1000  {
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)
1005  {
1006  kDebug(7101) << "mount got a warning:" << err;
1007  warning( err );
1008  finished();
1009  return;
1010  }
1011  else
1012  {
1013  if ( (step == 0) && !_point.isEmpty())
1014  {
1015  kDebug(7101) << err;
1016  kDebug(7101) << "Mounting with those options didn't work, trying with only mountpoint";
1017  fstype = "";
1018  fstype_empty = true;
1019  dev = "";
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.
1028  }
1029  else
1030  {
1031  error( KIO::ERR_COULD_NOT_MOUNT, err );
1032  return;
1033  }
1034  }
1035  }
1036  }
1037 #endif /* ! HAVE_VOLMGT */
1038 #else
1039  QString err;
1040  err = i18n("mounting is not supported by wince.");
1041  error( KIO::ERR_COULD_NOT_MOUNT, err );
1042 #endif
1043 
1044 }
1045 
1046 
1047 void FileProtocol::unmount( const QString& _point )
1048 {
1049 #ifndef _WIN32_WCE
1050  QByteArray buffer;
1051 
1052  KTemporaryFile tmpFile;
1053  tmpFile.setAutoRemove(false);
1054  tmpFile.open();
1055  QByteArray tmpFileName = QFile::encodeName(tmpFile.fileName());
1056  QString err;
1057 
1058 #ifdef HAVE_VOLMGT
1059  /*
1060  * support for Solaris volume management
1061  */
1062  char *devname;
1063  char *ptr;
1064  FILE *mnttab;
1065  struct mnttab mnt;
1066 
1067  if( volmgt_running() ) {
1068  kDebug(7101) << "VOLMGT: looking for "
1069  << _point.toLocal8Bit();
1070 
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 );
1075  return;
1076  }
1077 
1078  /*
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
1082  * look ourselves...
1083  */
1084  devname = NULL;
1085  rewind( mnttab );
1086  while( getmntent( mnttab, &mnt ) == 0 ) {
1087  if( strcmp( _point.toLocal8Bit(), mnt.mnt_mountp ) == 0 ){
1088  devname = mnt.mnt_special;
1089  break;
1090  }
1091  }
1092  fclose( mnttab );
1093 
1094  if( devname == NULL ) {
1095  err = QLatin1String("not in mnttab");
1096  kDebug(7101) << "VOLMGT: "
1097  << QFile::encodeName(_point).data()
1098  << ": " << err;
1099  error( KIO::ERR_COULD_NOT_UNMOUNT, err );
1100  return;
1101  }
1102 
1103  /*
1104  * strip off the directory name (volume name)
1105  * the eject(1) command will handle unmounting and
1106  * physically eject the media (if possible)
1107  */
1108  ptr = strrchr( devname, '/' );
1109  *ptr = '\0';
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;
1113 
1114  /*
1115  * from eject(1): exit status == 0 => need to manually eject
1116  * exit status == 4 => media was ejected
1117  */
1118  if( WEXITSTATUS( system( buffer.constData() )) == 4 ) {
1119  /*
1120  * this is not an error, so skip "readLogFile()"
1121  * to avoid wrong/confusing error popup. The
1122  * temporary file is removed by KTemporaryFile's
1123  * destructor, so don't do that manually.
1124  */
1125  finished();
1126  return;
1127  }
1128  } else {
1129  /*
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...
1134  */
1135  err = i18n("\"vold\" is not running.");
1136  kDebug(7101) << "VOLMGT: " << err;
1137  error( KIO::ERR_COULD_NOT_UNMOUNT, err );
1138  return;
1139  }
1140 #else
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();
1146 
1147  if (umountProg.isEmpty()) {
1148  error( KIO::ERR_COULD_NOT_UNMOUNT, i18n("Could not find program \"umount\""));
1149  return;
1150  }
1151  buffer = umountProg + ' ' + QFile::encodeName(KShell::quoteArg(_point)) + " 2>" + tmpFileName;
1152  system( buffer.constData() );
1153 #endif /* HAVE_VOLMGT */
1154 
1155  err = readLogFile( tmpFileName );
1156  if ( err.isEmpty() )
1157  finished();
1158  else
1159  error( KIO::ERR_COULD_NOT_UNMOUNT, err );
1160 #else
1161  QString err;
1162  err = i18n("unmounting is not supported by wince.");
1163  error( KIO::ERR_COULD_NOT_MOUNT, err );
1164 #endif
1165 }
1166 
1167 /*************************************
1168  *
1169  * pmount handling
1170  *
1171  *************************************/
1172 
1173 bool FileProtocol::pmount(const QString &dev)
1174 {
1175 #ifndef _WIN32_WCE
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);
1181 
1182  if (pmountProg.isEmpty())
1183  return false;
1184 
1185  QByteArray buffer = QFile::encodeName(pmountProg) + ' ' +
1186  QFile::encodeName(KShell::quoteArg(dev));
1187 
1188  int res = system( buffer.constData() );
1189 
1190  return res==0;
1191 #else
1192  return false;
1193 #endif
1194 }
1195 
1196 bool FileProtocol::pumount(const QString &point)
1197 {
1198 #ifndef _WIN32_WCE
1199  KMountPoint::Ptr mp = KMountPoint::currentMountPoints(KMountPoint::NeedRealDeviceName).findByPath(point);
1200  if (!mp)
1201  return false;
1202  QString dev = mp->realDeviceName();
1203  if (dev.isEmpty()) return false;
1204 
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);
1210 
1211  if (pumountProg.isEmpty())
1212  return false;
1213 
1214  QByteArray buffer = QFile::encodeName(pumountProg);
1215  buffer += ' ';
1216  buffer += QFile::encodeName(KShell::quoteArg(dev));
1217 
1218  int res = system( buffer.data() );
1219 
1220  return res==0;
1221 #else
1222  return false;
1223 #endif
1224 }
1225 
1226 /*************************************
1227  *
1228  * Utilities
1229  *
1230  *************************************/
1231 
1232 static QString readLogFile( const QByteArray& _filename )
1233 {
1234  QString result;
1235  QFile file(QFile::decodeName(_filename));
1236  if (file.open(QIODevice::ReadOnly)) {
1237  result = QString::fromLocal8Bit(file.readAll());
1238  }
1239  (void)file.remove();
1240  return result;
1241 }
1242 
1243 /*************************************
1244  *
1245  * ACL handling helpers
1246  *
1247  *************************************/
1248 #ifdef HAVE_POSIX_ACL
1249 
1250 bool FileProtocol::isExtendedACL( acl_t acl )
1251 {
1252  return ( acl_equiv_mode( acl, 0 ) != 0 );
1253 }
1254 
1255 static void appendACLAtoms( const QByteArray & path, UDSEntry& entry, mode_t type, bool withACL )
1256 {
1257  // first check for a noop
1258  if ( acl_extended_file( path.data() ) == 0 ) return;
1259 
1260  acl_t acl = 0;
1261  acl_t defaultAcl = 0;
1262  bool isDir = S_ISDIR( type );
1263  // do we have an acl for the file, and/or a default acl for the dir, if it is one?
1264  acl = acl_get_file( path.data(), ACL_TYPE_ACCESS );
1265  /* Sadly libacl does not provided a means of checking for extended ACL and default
1266  * ACL separately. Since a directory can have both, we need to check again. */
1267  if ( isDir ) {
1268  if ( acl ) {
1269  if ( !FileProtocol::isExtendedACL( acl ) ) {
1270  acl_free( acl );
1271  acl = 0;
1272  }
1273  }
1274  defaultAcl = acl_get_file( path.data(), ACL_TYPE_DEFAULT );
1275  }
1276  if ( acl || defaultAcl ) {
1277  kDebug(7101) << path.constData() << "has extended ACL entries";
1278  entry.insert( KIO::UDSEntry::UDS_EXTENDED_ACL, 1 );
1279  }
1280  if ( withACL ) {
1281  if ( acl ) {
1282  const QString str = aclToText(acl);
1283  entry.insert( KIO::UDSEntry::UDS_ACL_STRING, str );
1284  kDebug(7101) << path.constData() << "ACL:" << str;
1285  }
1286  if ( defaultAcl ) {
1287  const QString str = aclToText(defaultAcl);
1288  entry.insert( KIO::UDSEntry::UDS_DEFAULT_ACL_STRING, str );
1289  kDebug(7101) << path.constData() << "DEFAULT ACL:" << str;
1290  }
1291  }
1292  if ( acl ) acl_free( acl );
1293  if ( defaultAcl ) acl_free( defaultAcl );
1294 }
1295 #endif
1296 
1297 // We could port this to KTempDir::removeDir but then we wouldn't be able to tell the user
1298 // where exactly the deletion failed, in case of errors.
1299 bool FileProtocol::deleteRecursive(const QString& path)
1300 {
1301  //kDebug() << path;
1302  QDirIterator it(path, QDir::AllEntries | QDir::NoDotAndDotDot | QDir::System | QDir::Hidden,
1303  QDirIterator::Subdirectories);
1304  QStringList dirsToDelete;
1305  while ( it.hasNext() ) {
1306  const QString itemPath = it.next();
1307  //kDebug() << "itemPath=" << itemPath;
1308  const QFileInfo info = it.fileInfo();
1309  if (info.isDir() && !info.isSymLink())
1310  dirsToDelete.prepend(itemPath);
1311  else {
1312  //kDebug() << "QFile::remove" << itemPath;
1313  if (!QFile::remove(itemPath)) {
1314  error(KIO::ERR_CANNOT_DELETE, itemPath);
1315  return false;
1316  }
1317  }
1318  }
1319  QDir dir;
1320  Q_FOREACH(const QString& itemPath, dirsToDelete) {
1321  //kDebug() << "QDir::rmdir" << itemPath;
1322  if (!dir.rmdir(itemPath)) {
1323  error(KIO::ERR_CANNOT_DELETE, itemPath);
1324  return false;
1325  }
1326  }
1327  return true;
1328 }
1329 
1330 #include "file.moc"
This file is part of the KDE documentation.
Documentation copyright © 1996-2013 The KDE developers.
Generated on Sat Feb 9 2013 12:24:03 by doxygen 1.8.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KIOSlave

Skip menu "KIOSlave"
  • Main Page
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members
  • Related Pages

kdelibs-4.10.0 API Reference

Skip menu "kdelibs-4.10.0 API Reference"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal