Changeset View
Changeset View
Standalone View
Standalone View
src/ioslaves/file/file_unix.cpp
Show All 33 Lines | |||||
34 | #include <QDebug> | 34 | #include <QDebug> | ||
35 | #include <kconfiggroup.h> | 35 | #include <kconfiggroup.h> | ||
36 | #include <klocalizedstring.h> | 36 | #include <klocalizedstring.h> | ||
37 | #include <kmountpoint.h> | 37 | #include <kmountpoint.h> | ||
38 | 38 | | |||
39 | #include <errno.h> | 39 | #include <errno.h> | ||
40 | #include <utime.h> | 40 | #include <utime.h> | ||
41 | 41 | | |||
42 | #include <KAuth> | ||||
43 | | ||||
44 | #include "fdreceiver.h" | ||||
45 | | ||||
42 | //sendfile has different semantics in different platforms | 46 | //sendfile has different semantics in different platforms | ||
43 | #if defined HAVE_SENDFILE && defined Q_OS_LINUX | 47 | #if defined HAVE_SENDFILE && defined Q_OS_LINUX | ||
44 | #define USE_SENDFILE 1 | 48 | #define USE_SENDFILE 1 | ||
45 | #endif | 49 | #endif | ||
46 | 50 | | |||
47 | #ifdef USE_SENDFILE | 51 | #ifdef USE_SENDFILE | ||
48 | #include <sys/sendfile.h> | 52 | #include <sys/sendfile.h> | ||
49 | #endif | 53 | #endif | ||
50 | 54 | | |||
51 | using namespace KIO; | 55 | using namespace KIO; | ||
52 | 56 | | |||
53 | #define MAX_IPC_SIZE (1024*32) | 57 | #define MAX_IPC_SIZE (1024*32) | ||
54 | 58 | | |||
55 | static bool | 59 | static bool | ||
56 | same_inode(const QT_STATBUF &src, const QT_STATBUF &dest) | 60 | same_inode(const QT_STATBUF &src, const QT_STATBUF &dest) | ||
57 | { | 61 | { | ||
58 | if (src.st_ino == dest.st_ino && | 62 | if (src.st_ino == dest.st_ino && | ||
59 | src.st_dev == dest.st_dev) { | 63 | src.st_dev == dest.st_dev) { | ||
60 | return true; | 64 | return true; | ||
61 | } | 65 | } | ||
62 | 66 | | |||
63 | return false; | 67 | return false; | ||
64 | } | 68 | } | ||
65 | 69 | | |||
70 | static const QString socketPath() | ||||
71 | { | ||||
72 | return QStringLiteral("org_kde_kio_file_helper_%1").arg(getpid()); | ||||
73 | } | ||||
74 | | ||||
75 | bool FileProtocol::privilegeOperationUnitTestMode() | ||||
76 | { | ||||
77 | return (metaData(QStringLiteral("UnitTesting")) == QLatin1String("true")) | ||||
78 | && (requestPrivilegeOperation() == KIO::OperationAllowed); | ||||
79 | } | ||||
80 | | ||||
81 | PrivilegeOperationReturnValue FileProtocol::tryOpen(QFile &f, const QByteArray &path, int flags, int mode) | ||||
82 | { | ||||
83 | FdReceiver fdRecv(socketPath()); | ||||
84 | if (!fdRecv.isListening()) { | ||||
85 | return PrivilegeOperationReturnValue::failure(); | ||||
86 | } | ||||
87 | | ||||
88 | QIODevice::OpenMode openMode; | ||||
89 | if (flags & O_RDONLY) { | ||||
90 | openMode |= QIODevice::ReadOnly; | ||||
91 | } | ||||
92 | if (flags & O_WRONLY || flags & O_CREAT) { | ||||
93 | openMode |= QIODevice::WriteOnly; | ||||
94 | } | ||||
95 | if (flags & O_RDWR) { | ||||
96 | openMode |= QIODevice::ReadWrite; | ||||
97 | } | ||||
98 | if (flags & O_TRUNC) { | ||||
99 | openMode |= QIODevice::Truncate; | ||||
100 | } | ||||
101 | if (flags & O_APPEND) { | ||||
102 | openMode |= QIODevice::Append; | ||||
103 | } | ||||
104 | | ||||
105 | if (auto err = execWithElevatedPrivilege(OPEN, path, flags, mode)) { | ||||
106 | return err; | ||||
107 | } else { | ||||
108 | int fd = fdRecv.fileDescriptor(); | ||||
109 | if (fd < 3 || !f.open(fd, openMode, QFileDevice::AutoCloseHandle)) { | ||||
110 | return PrivilegeOperationReturnValue::failure(); | ||||
111 | } | ||||
112 | } | ||||
113 | return PrivilegeOperationReturnValue::success(); | ||||
114 | } | ||||
115 | | ||||
116 | PrivilegeOperationReturnValue FileProtocol::tryChangeFileAttr(ActionType action, const QVariant &arg1, | ||||
117 | const QVariant &arg2, const QVariant &arg3) | ||||
118 | { | ||||
119 | KAuth::Action execAction(QStringLiteral("org.kde.kio.file.exec")); | ||||
120 | execAction.setHelperId(QStringLiteral("org.kde.kio.file")); | ||||
121 | if (execAction.status() == KAuth::Action::AuthorizedStatus) { | ||||
122 | return execWithElevatedPrivilege(action, arg1, arg2, arg3); | ||||
123 | } | ||||
124 | return PrivilegeOperationReturnValue::failure(); | ||||
125 | } | ||||
126 | | ||||
66 | void FileProtocol::copy(const QUrl &srcUrl, const QUrl &destUrl, | 127 | void FileProtocol::copy(const QUrl &srcUrl, const QUrl &destUrl, | ||
67 | int _mode, JobFlags _flags) | 128 | int _mode, JobFlags _flags) | ||
68 | { | 129 | { | ||
130 | if (privilegeOperationUnitTestMode()) { | ||||
131 | finished(); | ||||
132 | return; | ||||
133 | } | ||||
134 | | ||||
69 | // qDebug() << "copy(): " << srcUrl << " -> " << destUrl << ", mode=" << _mode; | 135 | // qDebug() << "copy(): " << srcUrl << " -> " << destUrl << ", mode=" << _mode; | ||
70 | 136 | | |||
71 | const QString src = srcUrl.toLocalFile(); | 137 | const QString src = srcUrl.toLocalFile(); | ||
72 | const QString dest = destUrl.toLocalFile(); | 138 | const QString dest = destUrl.toLocalFile(); | ||
73 | QByteArray _src(QFile::encodeName(src)); | 139 | QByteArray _src(QFile::encodeName(src)); | ||
74 | QByteArray _dest(QFile::encodeName(dest)); | 140 | QByteArray _dest(QFile::encodeName(dest)); | ||
75 | 141 | | |||
76 | QT_STATBUF buff_src; | 142 | QT_STATBUF buff_src; | ||
Show All 36 Lines | 177 | if (!(_flags & KIO::Overwrite)) { | |||
113 | return; | 179 | return; | ||
114 | } | 180 | } | ||
115 | 181 | | |||
116 | // If the destination is a symlink and overwrite is TRUE, | 182 | // If the destination is a symlink and overwrite is TRUE, | ||
117 | // remove the symlink first to prevent the scenario where | 183 | // remove the symlink first to prevent the scenario where | ||
118 | // the symlink actually points to current source! | 184 | // the symlink actually points to current source! | ||
119 | if ((_flags & KIO::Overwrite) && ((buff_dest.st_mode & QT_STAT_MASK) == QT_STAT_LNK)) { | 185 | if ((_flags & KIO::Overwrite) && ((buff_dest.st_mode & QT_STAT_MASK) == QT_STAT_LNK)) { | ||
120 | //qDebug() << "copy(): LINK DESTINATION"; | 186 | //qDebug() << "copy(): LINK DESTINATION"; | ||
121 | QFile::remove(dest); | 187 | if (!QFile::remove(dest)) { | ||
188 | if (auto err = execWithElevatedPrivilege(DEL, _dest)) { | ||||
189 | if (!err.wasCanceled()) { | ||||
190 | error(KIO::ERR_CANNOT_DELETE_ORIGINAL, dest); | ||||
191 | } | ||||
192 | return; | ||||
193 | } | ||||
194 | } | ||||
122 | } | 195 | } | ||
123 | } | 196 | } | ||
124 | 197 | | |||
125 | QFile src_file(src); | 198 | QFile src_file(src); | ||
126 | if (!src_file.open(QIODevice::ReadOnly)) { | 199 | if (!src_file.open(QIODevice::ReadOnly)) { | ||
200 | if (auto err = tryOpen(src_file, _src, O_RDONLY, S_IRUSR)) { | ||||
201 | if (!err.wasCanceled()) { | ||||
127 | error(KIO::ERR_CANNOT_OPEN_FOR_READING, src); | 202 | error(KIO::ERR_CANNOT_OPEN_FOR_READING, src); | ||
203 | } | ||||
128 | return; | 204 | return; | ||
129 | } | 205 | } | ||
206 | } | ||||
130 | 207 | | |||
131 | #if HAVE_FADVISE | 208 | #if HAVE_FADVISE | ||
132 | posix_fadvise(src_file.handle(), 0, 0, POSIX_FADV_SEQUENTIAL); | 209 | posix_fadvise(src_file.handle(), 0, 0, POSIX_FADV_SEQUENTIAL); | ||
133 | #endif | 210 | #endif | ||
134 | 211 | | |||
135 | QFile dest_file(dest); | 212 | QFile dest_file(dest); | ||
136 | if (!dest_file.open(QIODevice::Truncate | QIODevice::WriteOnly)) { | 213 | if (!dest_file.open(QIODevice::Truncate | QIODevice::WriteOnly)) { | ||
214 | if (auto err = tryOpen(dest_file, _dest, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR)) { | ||||
215 | if (!err.wasCanceled()) { | ||||
137 | // qDebug() << "###### COULD NOT WRITE " << dest; | 216 | // qDebug() << "###### COULD NOT WRITE " << dest; | ||
138 | if (errno == EACCES) { | 217 | if (errno == EACCES) { | ||
139 | error(KIO::ERR_WRITE_ACCESS_DENIED, dest); | 218 | error(KIO::ERR_WRITE_ACCESS_DENIED, dest); | ||
140 | } else { | 219 | } else { | ||
141 | error(KIO::ERR_CANNOT_OPEN_FOR_WRITING, dest); | 220 | error(KIO::ERR_CANNOT_OPEN_FOR_WRITING, dest); | ||
142 | } | 221 | } | ||
222 | } | ||||
143 | src_file.close(); | 223 | src_file.close(); | ||
144 | return; | 224 | return; | ||
145 | } | 225 | } | ||
226 | } | ||||
146 | 227 | | |||
147 | // nobody shall be allowed to peek into the file during creation | 228 | // nobody shall be allowed to peek into the file during creation | ||
148 | dest_file.setPermissions(QFileDevice::ReadOwner | QFileDevice::WriteOwner); | 229 | if (!QFile::setPermissions(dest, QFileDevice::ReadOwner | QFileDevice::WriteOwner)) { | ||
230 | if (auto err = execWithElevatedPrivilege(CHOWN, _dest, getuid(), getgid())) { | ||||
231 | dest_file.close(); | ||||
232 | execWithElevatedPrivilege(DEL, _dest); | ||||
233 | if (!err.wasCanceled()) { | ||||
234 | error(KIO::ERR_CANNOT_CHOWN, dest); | ||||
235 | } | ||||
236 | return; | ||||
237 | } else { | ||||
238 | QFile::setPermissions(dest, QFileDevice::ReadOwner | QFileDevice::WriteOwner); | ||||
239 | } | ||||
240 | } | ||||
149 | 241 | | |||
150 | #if HAVE_FADVISE | 242 | #if HAVE_FADVISE | ||
151 | posix_fadvise(dest_file.handle(), 0, 0, POSIX_FADV_SEQUENTIAL); | 243 | posix_fadvise(dest_file.handle(), 0, 0, POSIX_FADV_SEQUENTIAL); | ||
152 | #endif | 244 | #endif | ||
153 | 245 | | |||
154 | #if HAVE_POSIX_ACL | 246 | #if HAVE_POSIX_ACL | ||
155 | acl = acl_get_fd(src_file.handle()); | 247 | acl = acl_get_fd(src_file.handle()); | ||
156 | if (acl && !isExtendedACL(acl)) { | 248 | if (acl && !isExtendedACL(acl)) { | ||
▲ Show 20 Lines • Show All 44 Lines • ▼ Show 20 Line(s) | 292 | #endif | |||
201 | error(KIO::ERR_CANNOT_READ, src); | 293 | error(KIO::ERR_CANNOT_READ, src); | ||
202 | src_file.close(); | 294 | src_file.close(); | ||
203 | dest_file.close(); | 295 | dest_file.close(); | ||
204 | #if HAVE_POSIX_ACL | 296 | #if HAVE_POSIX_ACL | ||
205 | if (acl) { | 297 | if (acl) { | ||
206 | acl_free(acl); | 298 | acl_free(acl); | ||
207 | } | 299 | } | ||
208 | #endif | 300 | #endif | ||
209 | dest_file.remove(); // don't keep partly copied file | 301 | if (!QFile::remove(dest)) { // don't keep partly copied file | ||
302 | execWithElevatedPrivilege(DEL, _dest); | ||||
303 | } | ||||
210 | return; | 304 | return; | ||
211 | } | 305 | } | ||
212 | if (n == 0) { | 306 | if (n == 0) { | ||
213 | break; // Finished | 307 | break; // Finished | ||
214 | } | 308 | } | ||
215 | #ifdef USE_SENDFILE | 309 | #ifdef USE_SENDFILE | ||
216 | if (!use_sendfile) { | 310 | if (!use_sendfile) { | ||
217 | #endif | 311 | #endif | ||
218 | if (dest_file.write(buffer, n) != n) { | 312 | if (dest_file.write(buffer, n) != n) { | ||
219 | if (dest_file.error() == QFileDevice::ResourceError) { // disk full | 313 | if (dest_file.error() == QFileDevice::ResourceError) { // disk full | ||
220 | error(KIO::ERR_DISK_FULL, dest); | 314 | error(KIO::ERR_DISK_FULL, dest); | ||
221 | } else { | 315 | } else { | ||
222 | qCWarning(KIO_FILE) << "Couldn't write[2]. Error:" << dest_file.errorString(); | 316 | qCWarning(KIO_FILE) << "Couldn't write[2]. Error:" << dest_file.errorString(); | ||
223 | error(KIO::ERR_CANNOT_WRITE, dest); | 317 | error(KIO::ERR_CANNOT_WRITE, dest); | ||
224 | } | 318 | } | ||
225 | #if HAVE_POSIX_ACL | 319 | #if HAVE_POSIX_ACL | ||
226 | if (acl) { | 320 | if (acl) { | ||
227 | acl_free(acl); | 321 | acl_free(acl); | ||
228 | } | 322 | } | ||
229 | #endif | 323 | #endif | ||
230 | dest_file.remove(); // don't keep partly copied file | 324 | if (!QFile::remove(dest)) { // don't keep partly copied file | ||
325 | execWithElevatedPrivilege(DEL, _dest); | ||||
326 | } | ||||
231 | return; | 327 | return; | ||
232 | } | 328 | } | ||
233 | processed_size += n; | 329 | processed_size += n; | ||
234 | #ifdef USE_SENDFILE | 330 | #ifdef USE_SENDFILE | ||
235 | } | 331 | } | ||
236 | #endif | 332 | #endif | ||
237 | processedSize(processed_size); | 333 | processedSize(processed_size); | ||
238 | } | 334 | } | ||
239 | 335 | | |||
240 | src_file.close(); | 336 | src_file.close(); | ||
241 | dest_file.close(); | 337 | dest_file.close(); | ||
242 | 338 | | |||
243 | if (dest_file.error() != QFile::NoError) { | 339 | if (dest_file.error() != QFile::NoError) { | ||
244 | qCWarning(KIO_FILE) << "Error when closing file descriptor[2]:" << dest_file.errorString(); | 340 | qCWarning(KIO_FILE) << "Error when closing file descriptor[2]:" << dest_file.errorString(); | ||
245 | error(KIO::ERR_CANNOT_WRITE, dest); | 341 | error(KIO::ERR_CANNOT_WRITE, dest); | ||
246 | #if HAVE_POSIX_ACL | 342 | #if HAVE_POSIX_ACL | ||
247 | if (acl) { | 343 | if (acl) { | ||
248 | acl_free(acl); | 344 | acl_free(acl); | ||
249 | } | 345 | } | ||
250 | #endif | 346 | #endif | ||
251 | dest_file.remove(); // don't keep partly copied file | 347 | if (!QFile::remove(dest)) { // don't keep partly copied file | ||
348 | execWithElevatedPrivilege(DEL, _dest); | ||||
349 | } | ||||
252 | return; | 350 | return; | ||
253 | } | 351 | } | ||
254 | 352 | | |||
255 | // set final permissions | 353 | // set final permissions | ||
256 | // if no special mode given, preserve the mode from the sourcefile | 354 | // if no special mode given, preserve the mode from the sourcefile | ||
257 | if (_mode == -1) { | 355 | if (_mode == -1) { | ||
258 | _mode = buff_src.st_mode; | 356 | _mode = buff_src.st_mode; | ||
259 | } | 357 | } | ||
260 | 358 | | |||
261 | if ((::chmod(_dest.data(), _mode) != 0) | 359 | if ((::chmod(_dest.data(), _mode) != 0) | ||
262 | #if HAVE_POSIX_ACL | 360 | #if HAVE_POSIX_ACL | ||
263 | || (acl && acl_set_file(_dest.data(), ACL_TYPE_ACCESS, acl) != 0) | 361 | || (acl && acl_set_file(_dest.data(), ACL_TYPE_ACCESS, acl) != 0) | ||
264 | #endif | 362 | #endif | ||
265 | ) { | 363 | ) { | ||
266 | KMountPoint::Ptr mp = KMountPoint::currentMountPoints().findByPath(dest); | 364 | if (tryChangeFileAttr(CHMOD, _dest, _mode)) { | ||
267 | // Eat the error if the filesystem apparently doesn't support chmod. | | |||
268 | if (mp && mp->testFileSystemFlag(KMountPoint::SupportsChmod)) { | | |||
269 | warning(i18n("Could not change permissions for '%1'", dest)); | 365 | warning(i18n("Could not change permissions for '%1'", dest)); | ||
270 | } | 366 | } | ||
271 | } | 367 | } | ||
272 | #if HAVE_POSIX_ACL | 368 | #if HAVE_POSIX_ACL | ||
273 | if (acl) { | 369 | if (acl) { | ||
274 | acl_free(acl); | 370 | acl_free(acl); | ||
275 | } | 371 | } | ||
276 | #endif | 372 | #endif | ||
277 | 373 | | |||
278 | // preserve ownership | 374 | // preserve ownership | ||
279 | if (::chown(_dest.data(), -1 /*keep user*/, buff_src.st_gid) == 0) { | 375 | if (::chown(_dest.data(), -1 /*keep user*/, buff_src.st_gid) == 0) { | ||
280 | // as we are the owner of the new file, we can always change the group, but | 376 | // as we are the owner of the new file, we can always change the group, but | ||
281 | // we might not be allowed to change the owner | 377 | // we might not be allowed to change the owner | ||
282 | (void)::chown(_dest.data(), buff_src.st_uid, -1 /*keep group*/); | 378 | (void)::chown(_dest.data(), buff_src.st_uid, -1 /*keep group*/); | ||
283 | } else { | 379 | } else { | ||
380 | if (tryChangeFileAttr(CHOWN, _dest, buff_src.st_uid, buff_src.st_gid)) { | ||||
284 | qCWarning(KIO_FILE) << QStringLiteral("Couldn't preserve group for '%1'").arg(dest); | 381 | qCWarning(KIO_FILE) << QStringLiteral("Couldn't preserve group for '%1'").arg(dest); | ||
285 | } | 382 | } | ||
383 | } | ||||
286 | 384 | | |||
287 | // copy access and modification time | 385 | // copy access and modification time | ||
288 | struct utimbuf ut; | 386 | struct utimbuf ut; | ||
289 | ut.actime = buff_src.st_atime; | 387 | ut.actime = buff_src.st_atime; | ||
290 | ut.modtime = buff_src.st_mtime; | 388 | ut.modtime = buff_src.st_mtime; | ||
291 | if (::utime(_dest.data(), &ut) != 0) { | 389 | if (::utime(_dest.data(), &ut) != 0) { | ||
390 | if (tryChangeFileAttr(UTIME, _dest, qint64(ut.actime), qint64(ut.modtime))) { | ||||
292 | qCWarning(KIO_FILE) << QStringLiteral("Couldn't preserve access and modification time for '%1'").arg(dest); | 391 | qCWarning(KIO_FILE) << QStringLiteral("Couldn't preserve access and modification time for '%1'").arg(dest); | ||
293 | } | 392 | } | ||
393 | } | ||||
294 | 394 | | |||
295 | processedSize(buff_src.st_size); | 395 | processedSize(buff_src.st_size); | ||
296 | finished(); | 396 | finished(); | ||
297 | } | 397 | } | ||
298 | 398 | | |||
299 | static bool isLocalFileSameHost(const QUrl &url) | 399 | static bool isLocalFileSameHost(const QUrl &url) | ||
300 | { | 400 | { | ||
301 | if (!url.isLocalFile()) { | 401 | if (!url.isLocalFile()) { | ||
▲ Show 20 Lines • Show All 157 Lines • ▼ Show 20 Line(s) | 549 | if (dest_exists) { | |||
459 | 559 | | |||
460 | if (!(_flags & KIO::Overwrite)) { | 560 | if (!(_flags & KIO::Overwrite)) { | ||
461 | error(KIO::ERR_FILE_ALREADY_EXIST, dest); | 561 | error(KIO::ERR_FILE_ALREADY_EXIST, dest); | ||
462 | return; | 562 | return; | ||
463 | } | 563 | } | ||
464 | } | 564 | } | ||
465 | 565 | | |||
466 | if (::rename(_src.data(), _dest.data())) { | 566 | if (::rename(_src.data(), _dest.data())) { | ||
567 | if (auto err = execWithElevatedPrivilege(RENAME, _src, _dest)) { | ||||
568 | if (!err.wasCanceled()) { | ||||
467 | if ((errno == EACCES) || (errno == EPERM)) { | 569 | if ((errno == EACCES) || (errno == EPERM)) { | ||
468 | error(KIO::ERR_ACCESS_DENIED, dest); | 570 | error(KIO::ERR_ACCESS_DENIED, dest); | ||
469 | } else if (errno == EXDEV) { | 571 | } else if (errno == EXDEV) { | ||
470 | error(KIO::ERR_UNSUPPORTED_ACTION, QStringLiteral("rename")); | 572 | error(KIO::ERR_UNSUPPORTED_ACTION, QStringLiteral("rename")); | ||
471 | } else if (errno == EROFS) { // The file is on a read-only filesystem | 573 | } else if (errno == EROFS) { // The file is on a read-only filesystem | ||
472 | error(KIO::ERR_CANNOT_DELETE, src); | 574 | error(KIO::ERR_CANNOT_DELETE, src); | ||
473 | } else { | 575 | } else { | ||
474 | error(KIO::ERR_CANNOT_RENAME, src); | 576 | error(KIO::ERR_CANNOT_RENAME, src); | ||
475 | } | 577 | } | ||
578 | } | ||||
476 | return; | 579 | return; | ||
477 | } | 580 | } | ||
581 | } | ||||
478 | 582 | | |||
479 | finished(); | 583 | finished(); | ||
480 | } | 584 | } | ||
481 | 585 | | |||
482 | void FileProtocol::symlink(const QString &target, const QUrl &destUrl, KIO::JobFlags flags) | 586 | void FileProtocol::symlink(const QString &target, const QUrl &destUrl, KIO::JobFlags flags) | ||
483 | { | 587 | { | ||
484 | const QString dest = destUrl.toLocalFile(); | 588 | const QString dest = destUrl.toLocalFile(); | ||
485 | // Assume dest is local too (wouldn't be here otherwise) | 589 | // Assume dest is local too (wouldn't be here otherwise) | ||
486 | if (::symlink(QFile::encodeName(target).constData(), QFile::encodeName(dest).constData()) == -1) { | 590 | if (::symlink(QFile::encodeName(target).constData(), QFile::encodeName(dest).constData()) == -1) { | ||
487 | // Does the destination already exist ? | 591 | // Does the destination already exist ? | ||
488 | if (errno == EEXIST) { | 592 | if (errno == EEXIST) { | ||
489 | if ((flags & KIO::Overwrite)) { | 593 | if ((flags & KIO::Overwrite)) { | ||
490 | // Try to delete the destination | 594 | // Try to delete the destination | ||
491 | if (unlink(QFile::encodeName(dest).constData()) != 0) { | 595 | if (unlink(QFile::encodeName(dest).constData()) != 0) { | ||
596 | if (auto err = execWithElevatedPrivilege(DEL, dest)) { | ||||
597 | if (!err.wasCanceled()) { | ||||
492 | error(KIO::ERR_CANNOT_DELETE, dest); | 598 | error(KIO::ERR_CANNOT_DELETE, dest); | ||
599 | } | ||||
493 | return; | 600 | return; | ||
494 | } | 601 | } | ||
602 | } | ||||
495 | // Try again - this won't loop forever since unlink succeeded | 603 | // Try again - this won't loop forever since unlink succeeded | ||
496 | symlink(target, destUrl, flags); | 604 | symlink(target, destUrl, flags); | ||
497 | return; | 605 | return; | ||
498 | } else { | 606 | } else { | ||
499 | QT_STATBUF buff_dest; | 607 | QT_STATBUF buff_dest; | ||
500 | if (QT_LSTAT(QFile::encodeName(dest).constData(), &buff_dest) == 0 && ((buff_dest.st_mode & QT_STAT_MASK) == QT_STAT_DIR)) { | 608 | if (QT_LSTAT(QFile::encodeName(dest).constData(), &buff_dest) == 0 && ((buff_dest.st_mode & QT_STAT_MASK) == QT_STAT_DIR)) { | ||
501 | error(KIO::ERR_DIR_ALREADY_EXIST, dest); | 609 | error(KIO::ERR_DIR_ALREADY_EXIST, dest); | ||
502 | } else { | 610 | } else { | ||
503 | error(KIO::ERR_FILE_ALREADY_EXIST, dest); | 611 | error(KIO::ERR_FILE_ALREADY_EXIST, dest); | ||
504 | } | 612 | } | ||
505 | return; | 613 | return; | ||
506 | } | 614 | } | ||
507 | } else { | 615 | } else { | ||
616 | if (auto err = execWithElevatedPrivilege(SYMLINK, dest, target)) { | ||||
617 | if (!err.wasCanceled()) { | ||||
508 | // Some error occurred while we tried to symlink | 618 | // Some error occurred while we tried to symlink | ||
509 | error(KIO::ERR_CANNOT_SYMLINK, dest); | 619 | error(KIO::ERR_CANNOT_SYMLINK, dest); | ||
620 | } | ||||
510 | return; | 621 | return; | ||
511 | } | 622 | } | ||
512 | } | 623 | } | ||
624 | } | ||||
513 | finished(); | 625 | finished(); | ||
514 | } | 626 | } | ||
515 | 627 | | |||
516 | void FileProtocol::del(const QUrl &url, bool isfile) | 628 | void FileProtocol::del(const QUrl &url, bool isfile) | ||
517 | { | 629 | { | ||
518 | const QString path = url.toLocalFile(); | 630 | const QString path = url.toLocalFile(); | ||
519 | const QByteArray _path(QFile::encodeName(path)); | 631 | const QByteArray _path(QFile::encodeName(path)); | ||
520 | /***** | 632 | /***** | ||
521 | * Delete files | 633 | * Delete files | ||
522 | *****/ | 634 | *****/ | ||
523 | 635 | | |||
524 | if (isfile) { | 636 | if (isfile) { | ||
525 | // qDebug() << "Deleting file "<< url; | 637 | // qDebug() << "Deleting file "<< url; | ||
526 | 638 | | |||
527 | if (unlink(_path.data()) == -1) { | 639 | if (unlink(_path.data()) == -1) { | ||
640 | if (auto err = execWithElevatedPrivilege(DEL, _path)) { | ||||
641 | if (!err.wasCanceled()) { | ||||
528 | if ((errno == EACCES) || (errno == EPERM)) { | 642 | if ((errno == EACCES) || (errno == EPERM)) { | ||
529 | error(KIO::ERR_ACCESS_DENIED, path); | 643 | error(KIO::ERR_ACCESS_DENIED, path); | ||
530 | } else if (errno == EISDIR) { | 644 | } else if (errno == EISDIR) { | ||
531 | error(KIO::ERR_IS_DIRECTORY, path); | 645 | error(KIO::ERR_IS_DIRECTORY, path); | ||
532 | } else { | 646 | } else { | ||
533 | error(KIO::ERR_CANNOT_DELETE, path); | 647 | error(KIO::ERR_CANNOT_DELETE, path); | ||
534 | } | 648 | } | ||
649 | } | ||||
535 | return; | 650 | return; | ||
536 | } | 651 | } | ||
652 | } | ||||
537 | } else { | 653 | } else { | ||
538 | 654 | | |||
539 | /***** | 655 | /***** | ||
540 | * Delete empty directory | 656 | * Delete empty directory | ||
541 | *****/ | 657 | *****/ | ||
542 | 658 | | |||
543 | // qDebug() << "Deleting directory " << url; | 659 | // qDebug() << "Deleting directory " << url; | ||
544 | if (metaData(QStringLiteral("recurse")) == QLatin1String("true")) { | 660 | if (metaData(QStringLiteral("recurse")) == QLatin1String("true")) { | ||
545 | if (!deleteRecursive(path)) { | 661 | if (!deleteRecursive(path)) { | ||
546 | return; | 662 | return; | ||
547 | } | 663 | } | ||
548 | } | 664 | } | ||
549 | if (QT_RMDIR(_path.data()) == -1) { | 665 | if (QT_RMDIR(_path.data()) == -1) { | ||
666 | if (auto err = execWithElevatedPrivilege(RMDIR, _path)) { | ||||
667 | if (!err.wasCanceled()) { | ||||
550 | if ((errno == EACCES) || (errno == EPERM)) { | 668 | if ((errno == EACCES) || (errno == EPERM)) { | ||
551 | error(KIO::ERR_ACCESS_DENIED, path); | 669 | error(KIO::ERR_ACCESS_DENIED, path); | ||
552 | } else { | 670 | } else { | ||
553 | // qDebug() << "could not rmdir " << perror; | 671 | // qDebug() << "could not rmdir " << perror; | ||
554 | error(KIO::ERR_CANNOT_RMDIR, path); | 672 | error(KIO::ERR_CANNOT_RMDIR, path); | ||
673 | } | ||||
674 | } | ||||
555 | return; | 675 | return; | ||
556 | } | 676 | } | ||
557 | } | 677 | } | ||
558 | } | 678 | } | ||
559 | 679 | | |||
560 | finished(); | 680 | finished(); | ||
561 | } | 681 | } | ||
562 | 682 | | |||
Show All 26 Lines | 708 | error(KIO::ERR_SLAVE_DEFINED, | |||
589 | i18n("Could not get group id for given group name %1", group)); | 709 | i18n("Could not get group id for given group name %1", group)); | ||
590 | return; | 710 | return; | ||
591 | } | 711 | } | ||
592 | 712 | | |||
593 | gid = p->gr_gid; | 713 | gid = p->gr_gid; | ||
594 | } | 714 | } | ||
595 | 715 | | |||
596 | if (::chown(_path.constData(), uid, gid) == -1) { | 716 | if (::chown(_path.constData(), uid, gid) == -1) { | ||
717 | if (auto err = execWithElevatedPrivilege(CHOWN, _path, uid, gid)) { | ||||
718 | if (!err.wasCanceled()) { | ||||
597 | switch (errno) { | 719 | switch (errno) { | ||
598 | case EPERM: | 720 | case EPERM: | ||
599 | case EACCES: | 721 | case EACCES: | ||
600 | error(KIO::ERR_ACCESS_DENIED, path); | 722 | error(KIO::ERR_ACCESS_DENIED, path); | ||
601 | break; | 723 | break; | ||
602 | case ENOSPC: | 724 | case ENOSPC: | ||
603 | error(KIO::ERR_DISK_FULL, path); | 725 | error(KIO::ERR_DISK_FULL, path); | ||
604 | break; | 726 | break; | ||
605 | default: | 727 | default: | ||
606 | error(KIO::ERR_CANNOT_CHOWN, path); | 728 | error(KIO::ERR_CANNOT_CHOWN, path); | ||
607 | } | 729 | } | ||
730 | } | ||||
731 | } | ||||
608 | } else { | 732 | } else { | ||
609 | finished(); | 733 | finished(); | ||
610 | } | 734 | } | ||
611 | } | 735 | } | ||
612 | 736 | | |||
613 | void FileProtocol::stat(const QUrl &url) | 737 | void FileProtocol::stat(const QUrl &url) | ||
614 | { | 738 | { | ||
615 | if (!isLocalFileSameHost(url)) { | 739 | if (!isLocalFileSameHost(url)) { | ||
Show All 29 Lines | 768 | for (; it1 != mOutgoingMetaData.end(); it1++) { | |||
645 | // qDebug() << it1.key() << " = " << it1.data(); | 769 | // qDebug() << it1.key() << " = " << it1.data(); | ||
646 | } | 770 | } | ||
647 | ///////// | 771 | ///////// | ||
648 | #endif | 772 | #endif | ||
649 | statEntry(entry); | 773 | statEntry(entry); | ||
650 | 774 | | |||
651 | finished(); | 775 | finished(); | ||
652 | } | 776 | } | ||
777 | | ||||
778 | PrivilegeOperationReturnValue FileProtocol::execWithElevatedPrivilege(ActionType action, const QVariant &arg1, | ||||
779 | const QVariant &arg2, const QVariant &arg3) | ||||
780 | { | ||||
781 | if (privilegeOperationUnitTestMode()) { | ||||
782 | return PrivilegeOperationReturnValue::success(); | ||||
783 | } | ||||
784 | | ||||
785 | if (!(errno == EACCES || errno == EPERM)) { | ||||
786 | return PrivilegeOperationReturnValue::failure(); | ||||
787 | } | ||||
788 | | ||||
789 | KIO::PrivilegeOperationStatus opStatus = requestPrivilegeOperation(); | ||||
790 | if (opStatus != KIO::OperationAllowed) { | ||||
791 | if (opStatus == KIO::OperationCanceled) { | ||||
792 | error(KIO::ERR_USER_CANCELED, QString()); | ||||
793 | return PrivilegeOperationReturnValue::canceled(); | ||||
794 | } | ||||
795 | return PrivilegeOperationReturnValue::failure(); | ||||
796 | } | ||||
797 | | ||||
798 | if (action == CHMOD || action == CHOWN || action == UTIME) { | ||||
799 | KMountPoint::Ptr mp = KMountPoint::currentMountPoints().findByPath(arg1.toString()); | ||||
800 | // Test for chmod and utime will return the same result as test for chown. | ||||
801 | if (mp && !mp->testFileSystemFlag(KMountPoint::SupportsChown)) { | ||||
802 | return PrivilegeOperationReturnValue::failure(); | ||||
803 | } | ||||
804 | } | ||||
805 | | ||||
806 | QByteArray helperArgs; | ||||
807 | QDataStream out(&helperArgs, QIODevice::WriteOnly); | ||||
808 | out << action << arg1 << arg2 << arg3; | ||||
809 | | ||||
810 | if (action == OPEN || action == OPENDIR) { | ||||
811 | out << QVariant::fromValue(socketPath()); | ||||
812 | } | ||||
813 | | ||||
814 | KAuth::Action execAction(QStringLiteral("org.kde.kio.file.exec")); | ||||
815 | execAction.setHelperId(QStringLiteral("org.kde.kio.file")); | ||||
816 | | ||||
817 | QVariantMap argv; | ||||
818 | argv.insert(QStringLiteral("arguments"), helperArgs); | ||||
819 | execAction.setArguments(argv); | ||||
820 | | ||||
821 | auto reply = execAction.execute(); | ||||
822 | if (reply->exec()) { | ||||
823 | return PrivilegeOperationReturnValue::success(); | ||||
824 | } | ||||
825 | | ||||
826 | return PrivilegeOperationReturnValue::failure(); | ||||
827 | } |