Changeset View
Changeset View
Standalone View
Standalone View
src/ioslaves/file/file_unix.cpp
Show First 20 Lines • Show All 44 Lines • ▼ Show 20 Line(s) | |||||
45 | #include <utime.h> | 45 | #include <utime.h> | ||
46 | 46 | | |||
47 | #include <KAuth> | 47 | #include <KAuth> | ||
48 | #include <KRandom> | 48 | #include <KRandom> | ||
49 | 49 | | |||
50 | #include "fdreceiver.h" | 50 | #include "fdreceiver.h" | ||
51 | #include "statjob.h" | 51 | #include "statjob.h" | ||
52 | 52 | | |||
53 | #if HAVE_STATX | ||||
54 | #include <sys/stat.h> | ||||
55 | #endif | ||||
56 | | ||||
53 | //sendfile has different semantics in different platforms | 57 | //sendfile has different semantics in different platforms | ||
54 | #if HAVE_SENDFILE && defined Q_OS_LINUX | 58 | #if HAVE_SENDFILE && defined Q_OS_LINUX | ||
55 | #define USE_SENDFILE 1 | 59 | #define USE_SENDFILE 1 | ||
56 | #endif | 60 | #endif | ||
57 | 61 | | |||
58 | #ifdef USE_SENDFILE | 62 | #ifdef USE_SENDFILE | ||
59 | #include <sys/sendfile.h> | 63 | #include <sys/sendfile.h> | ||
60 | #endif | 64 | #endif | ||
▲ Show 20 Lines • Show All 70 Lines • ▼ Show 20 Line(s) | |||||
131 | } | 135 | } | ||
132 | 136 | | |||
133 | bool FileProtocol::privilegeOperationUnitTestMode() | 137 | bool FileProtocol::privilegeOperationUnitTestMode() | ||
134 | { | 138 | { | ||
135 | return (metaData(QStringLiteral("UnitTesting")) == QLatin1String("true")) | 139 | return (metaData(QStringLiteral("UnitTesting")) == QLatin1String("true")) | ||
136 | && (requestPrivilegeOperation(QStringLiteral("Test Call")) == KIO::OperationAllowed); | 140 | && (requestPrivilegeOperation(QStringLiteral("Test Call")) == KIO::OperationAllowed); | ||
137 | } | 141 | } | ||
138 | 142 | | |||
143 | | ||||
144 | static QHash<KUserId, QString> staticUserCache; | ||||
145 | static QHash<KGroupId, QString> staticGroupCache; | ||||
146 | | ||||
147 | static QString getUserName(KUserId uid) | ||||
dfaure: static | |||||
148 | { | ||||
149 | if (Q_UNLIKELY(!uid.isValid())) { | ||||
150 | return QString(); | ||||
151 | } | ||||
152 | auto it = staticUserCache.find(uid); | ||||
153 | if (it == staticUserCache.end()) { | ||||
154 | KUser user(uid); | ||||
155 | QString name = user.loginName(); | ||||
156 | if (name.isEmpty()) { | ||||
157 | name = uid.toString(); | ||||
158 | } | ||||
159 | it = staticUserCache.insert(uid, name); | ||||
160 | } | ||||
161 | return *it; | ||||
162 | } | ||||
163 | | ||||
164 | static QString getGroupName(KGroupId gid) | ||||
dfaure: static | |||||
165 | { | ||||
166 | if (Q_UNLIKELY(!gid.isValid())) { | ||||
167 | return QString(); | ||||
168 | } | ||||
169 | auto it = staticGroupCache.find(gid); | ||||
170 | if (it == staticGroupCache.end()) { | ||||
171 | KUserGroup group(gid); | ||||
172 | QString name = group.name(); | ||||
173 | if (name.isEmpty()) { | ||||
174 | name = gid.toString(); | ||||
175 | } | ||||
176 | it = staticGroupCache.insert(gid, name); | ||||
177 | } | ||||
178 | return *it; | ||||
179 | } | ||||
180 | | ||||
181 | #if HAVE_STATX | ||||
182 | // statx syscall is available | ||||
183 | inline int LSTAT(const char* path, struct statx * buff, KIO::StatDetails details) { | ||||
184 | uint32_t mask = 0; | ||||
185 | if (details & KIO::Basic) { | ||||
186 | // filename, access, type, size, linkdest | ||||
187 | mask |= STATX_SIZE | STATX_TYPE; | ||||
188 | } | ||||
189 | if (details & KIO::User) { | ||||
190 | // uid, gid | ||||
191 | mask |= STATX_UID | STATX_GID; | ||||
192 | } | ||||
193 | if (details & KIO::Time) { | ||||
194 | // atime, mtime, btime | ||||
195 | mask |= STATX_ATIME | STATX_MTIME | STATX_BTIME; | ||||
196 | } | ||||
197 | if (details & KIO::Inode) { | ||||
198 | // dev, inode | ||||
199 | mask |= STATX_INO; | ||||
200 | } | ||||
201 | return statx(AT_FDCWD, path, AT_SYMLINK_NOFOLLOW, mask, buff); | ||||
202 | } | ||||
203 | inline int STAT(const char* path, struct statx * buff, KIO::StatDetails details) { | ||||
204 | uint32_t mask = 0; | ||||
205 | if (details & KIO::Basic) { | ||||
206 | // filename, access, type, size, linkdest | ||||
207 | mask |= STATX_SIZE | STATX_TYPE; | ||||
208 | } | ||||
209 | if (details & KIO::User) { | ||||
210 | // uid, gid | ||||
211 | mask |= STATX_UID | STATX_GID; | ||||
212 | } | ||||
213 | if (details & KIO::Time) { | ||||
214 | // atime, mtime, btime | ||||
215 | mask |= STATX_ATIME | STATX_MTIME | STATX_BTIME; | ||||
216 | } | ||||
217 | // KIO::Inode is ignored as when STAT is called, the entry inode field has already been filled | ||||
218 | return statx(AT_FDCWD, path, AT_STATX_SYNC_AS_STAT, mask, buff); | ||||
219 | } | ||||
220 | inline static uint16_t stat_mode(struct statx &buf) { return buf.stx_mode; } | ||||
221 | inline static uint32_t stat_dev(struct statx &buf) { return buf.stx_dev_major; } | ||||
222 | inline static uint64_t stat_ino(struct statx &buf) { return buf.stx_ino; } | ||||
223 | inline static uint64_t stat_size(struct statx &buf) { return buf.stx_size; } | ||||
224 | inline static uint32_t stat_uid(struct statx &buf) { return buf.stx_uid; } | ||||
225 | inline static uint32_t stat_gid(struct statx &buf) { return buf.stx_gid; } | ||||
226 | inline static int64_t stat_atime(struct statx &buf) { return buf.stx_atime.tv_sec; } | ||||
227 | inline static int64_t stat_mtime(struct statx &buf) { return buf.stx_mtime.tv_sec; } | ||||
228 | #else | ||||
229 | // regular stat struct | ||||
230 | inline int LSTAT(const char* path, QT_STATBUF * buff, KIO::StatDetails details) { | ||||
231 | Q_UNUSED(details) | ||||
232 | return QT_LSTAT(path, buff); | ||||
233 | } | ||||
234 | inline int STAT(const char* path, QT_STATBUF * buff, KIO::StatDetails details) { | ||||
235 | Q_UNUSED(details) | ||||
236 | return QT_STAT(path, buff); | ||||
237 | } | ||||
238 | inline static mode_t stat_mode(QT_STATBUF &buf) { return buf.st_mode; } | ||||
239 | inline static dev_t stat_dev(QT_STATBUF &buf) { return buf.st_dev; } | ||||
240 | inline static ino_t stat_ino(QT_STATBUF &buf) { return buf.st_ino; } | ||||
241 | inline static off_t stat_size(QT_STATBUF &buf) { return buf.st_size; } | ||||
242 | inline static uid_t stat_uid(QT_STATBUF &buf) { return buf.st_uid; } | ||||
243 | inline static gid_t stat_gid(QT_STATBUF &buf) { return buf.st_gid; } | ||||
244 | inline static time_t stat_atime(QT_STATBUF &buf) { return buf.st_atime; } | ||||
245 | inline static time_t stat_mtime(QT_STATBUF &buf) { return buf.st_mtime; } | ||||
246 | #endif | ||||
247 | | ||||
248 | static bool createUDSEntry(const QString &filename, const QByteArray &path, UDSEntry &entry, | ||||
dfaure: static | |||||
249 | KIO::StatDetails details) | ||||
250 | { | ||||
251 | assert(entry.count() == 0); // by contract :-) | ||||
252 | int entries = 0; | ||||
253 | if (details & KIO::Basic) { | ||||
254 | // filename, access, type, size, linkdest | ||||
255 | entries += 5; | ||||
256 | } | ||||
257 | if (details & KIO::User) { | ||||
258 | // uid, gid | ||||
259 | entries += 2; | ||||
260 | } | ||||
261 | if (details & KIO::Time) { | ||||
262 | // atime, mtime, btime | ||||
263 | entries += 3; | ||||
264 | } | ||||
265 | if (details & KIO::Acl) { | ||||
266 | // acl data | ||||
267 | entries += 3; | ||||
268 | } | ||||
269 | if (details & KIO::Inode) { | ||||
270 | // dev, inode | ||||
271 | entries += 2; | ||||
272 | } | ||||
273 | entry.reserve(entries); | ||||
274 | | ||||
275 | if (details & KIO::Basic) { | ||||
276 | entry.fastInsert(KIO::UDSEntry::UDS_NAME, filename); | ||||
277 | } | ||||
278 | | ||||
279 | mode_t type; | ||||
280 | mode_t access; | ||||
281 | bool isBrokenSymLink = false; | ||||
282 | signed long long size = 0LL; | ||||
283 | #if HAVE_POSIX_ACL | ||||
284 | QByteArray targetPath = path; | ||||
285 | #endif | ||||
286 | | ||||
287 | #if HAVE_STATX | ||||
288 | // statx syscall is available | ||||
289 | struct statx buff; | ||||
290 | #else | ||||
291 | QT_STATBUF buff; | ||||
292 | #endif | ||||
293 | | ||||
294 | if (LSTAT(path.data(), &buff, details) == 0) { | ||||
295 | | ||||
296 | if (details & KIO::Inode) { | ||||
297 | entry.fastInsert(KIO::UDSEntry::UDS_DEVICE_ID, stat_dev(buff)); | ||||
298 | entry.fastInsert(KIO::UDSEntry::UDS_INODE, stat_ino(buff)); | ||||
299 | } | ||||
300 | | ||||
301 | if ((stat_mode(buff) & QT_STAT_MASK) == QT_STAT_LNK) { | ||||
302 | | ||||
303 | QByteArray linkTargetBuffer; | ||||
304 | if (details & (KIO::Basic|KIO::ResolveSymlink)) { | ||||
305 | | ||||
306 | // Use readlink on Unix because symLinkTarget turns relative targets into absolute (#352927) | ||||
307 | #if HAVE_STATX | ||||
308 | size_t lowerBound = 256; | ||||
309 | size_t higherBound = 1024; | ||||
310 | uint64_t s = stat_size(buff); | ||||
311 | if (s > SIZE_MAX) { | ||||
312 | qCWarning(KIO_FILE) << "file size bigger than SIZE_MAX, too big for readlink use!" << path; | ||||
313 | return false; | ||||
314 | } | ||||
315 | size_t size = static_cast<size_t>(s); | ||||
316 | using SizeType = size_t; | ||||
317 | #else | ||||
318 | off_t lowerBound = 256; | ||||
319 | off_t higherBound = 1024; | ||||
320 | off_t size = stat_size(buff); | ||||
321 | using SizeType = off_t; | ||||
322 | #endif | ||||
323 | SizeType bufferSize = qBound(lowerBound, size +1, higherBound); | ||||
324 | linkTargetBuffer.resize(bufferSize); | ||||
325 | while (true) { | ||||
326 | ssize_t n = readlink(path.constData(), linkTargetBuffer.data(), bufferSize); | ||||
327 | if (n < 0 && errno != ERANGE) { | ||||
328 | qCWarning(KIO_FILE) << "readlink failed!" << path; | ||||
329 | return false; | ||||
330 | } else if (n > 0 && static_cast<SizeType>(n) != bufferSize) { | ||||
331 | // the buffer was not filled in the last iteration | ||||
332 | // we are finished reading, break the loop | ||||
333 | linkTargetBuffer.truncate(n); | ||||
334 | break; | ||||
335 | } | ||||
336 | bufferSize *= 2; | ||||
337 | linkTargetBuffer.resize(bufferSize); | ||||
338 | } | ||||
339 | const QString linkTarget = QFile::decodeName(linkTargetBuffer); | ||||
340 | entry.fastInsert(KIO::UDSEntry::UDS_LINK_DEST, linkTarget); | ||||
341 | } | ||||
342 | | ||||
343 | // A symlink | ||||
344 | if (details & KIO::ResolveSymlink) { | ||||
345 | if (STAT(path.constData(), &buff, details) == -1) { | ||||
346 | isBrokenSymLink = true; | ||||
347 | } else { | ||||
348 | #if HAVE_POSIX_ACL | ||||
349 | if (details & KIO::Acl) { | ||||
350 | // valid symlink, will get the ACLs of the destination | ||||
351 | targetPath = linkTargetBuffer; | ||||
352 | } | ||||
353 | #endif | ||||
354 | } | ||||
355 | } | ||||
356 | } | ||||
357 | } else { | ||||
358 | // qCWarning(KIO_FILE) << "lstat didn't work on " << path.data(); | ||||
359 | return false; | ||||
360 | } | ||||
361 | | ||||
362 | if (details & KIO::Basic) { | ||||
363 | if (isBrokenSymLink) { | ||||
364 | // It is a link pointing to nowhere | ||||
365 | type = S_IFMT - 1; | ||||
366 | access = S_IRWXU | S_IRWXG | S_IRWXO; | ||||
367 | size = 0LL; | ||||
368 | } else { | ||||
369 | type = stat_mode(buff) & S_IFMT; // extract file type | ||||
370 | access = stat_mode(buff) & 07777; // extract permissions | ||||
371 | size = stat_size(buff); | ||||
372 | } | ||||
373 | | ||||
374 | entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, type); | ||||
375 | entry.fastInsert(KIO::UDSEntry::UDS_ACCESS, access); | ||||
376 | entry.fastInsert(KIO::UDSEntry::UDS_SIZE, size); | ||||
377 | } | ||||
378 | | ||||
379 | #if HAVE_POSIX_ACL | ||||
380 | if (details & KIO::Acl) { | ||||
381 | /* Append an atom indicating whether the file has extended acl information | ||||
382 | * and if withACL is specified also one with the acl itself. If it's a directory | ||||
383 | * and it has a default ACL, also append that. */ | ||||
384 | appendACLAtoms(targetPath, entry, type); | ||||
385 | } | ||||
386 | #endif | ||||
387 | | ||||
388 | if (details & KIO::User) { | ||||
389 | entry.fastInsert(KIO::UDSEntry::UDS_USER, getUserName(KUserId(stat_uid(buff)))); | ||||
390 | entry.fastInsert(KIO::UDSEntry::UDS_GROUP, getGroupName(KGroupId(stat_gid(buff)))); | ||||
391 | } | ||||
392 | | ||||
393 | if (details & KIO::Time) { | ||||
394 | entry.fastInsert(KIO::UDSEntry::UDS_MODIFICATION_TIME, stat_mtime(buff)); | ||||
395 | entry.fastInsert(KIO::UDSEntry::UDS_ACCESS_TIME, stat_atime(buff)); | ||||
396 | | ||||
397 | #ifdef st_birthtime | ||||
398 | /* For example FreeBSD's and NetBSD's stat contains a field for | ||||
399 | * the inode birth time: st_birthtime | ||||
400 | * This however only works on UFS and ZFS, and not, on say, NFS. | ||||
401 | * Instead of setting a bogus fallback like st_mtime, only use | ||||
402 | * it if it is greater than 0. */ | ||||
403 | if (buff.st_birthtime > 0) { | ||||
404 | entry.fastInsert(KIO::UDSEntry::UDS_CREATION_TIME, buff.st_birthtime); | ||||
405 | } | ||||
406 | #elif defined __st_birthtime | ||||
407 | /* As above, but OpenBSD calls it slightly differently. */ | ||||
408 | if (buff.__st_birthtime > 0) { | ||||
409 | entry.fastInsert(KIO::UDSEntry::UDS_CREATION_TIME, buff.__st_birthtime); | ||||
410 | } | ||||
411 | #elif HAVE_STATX | ||||
412 | /* And linux version using statx syscall */ | ||||
413 | if (buff.stx_mask & STATX_BTIME) { | ||||
414 | entry.fastInsert(KIO::UDSEntry::UDS_CREATION_TIME, buff.stx_btime.tv_sec); | ||||
415 | } | ||||
416 | #endif | ||||
417 | } | ||||
418 | | ||||
419 | return true; | ||||
420 | } | ||||
421 | | ||||
139 | PrivilegeOperationReturnValue FileProtocol::tryOpen(QFile &f, const QByteArray &path, int flags, int mode, int errcode) | 422 | PrivilegeOperationReturnValue FileProtocol::tryOpen(QFile &f, const QByteArray &path, int flags, int mode, int errcode) | ||
140 | { | 423 | { | ||
141 | const QString sockPath = socketPath(); | 424 | const QString sockPath = socketPath(); | ||
142 | FdReceiver fdRecv(QFile::encodeName(sockPath).toStdString()); | 425 | FdReceiver fdRecv(QFile::encodeName(sockPath).toStdString()); | ||
143 | if (!fdRecv.isListening()) { | 426 | if (!fdRecv.isListening()) { | ||
144 | return PrivilegeOperationReturnValue::failure(errcode); | 427 | return PrivilegeOperationReturnValue::failure(errcode); | ||
145 | } | 428 | } | ||
146 | 429 | | |||
▲ Show 20 Lines • Show All 857 Lines • ▼ Show 20 Line(s) | 1235 | { | |||
1004 | auto reply = execAction.execute(); | 1287 | auto reply = execAction.execute(); | ||
1005 | if (reply->exec()) { | 1288 | if (reply->exec()) { | ||
1006 | addTemporaryAuthorization(actionId); | 1289 | addTemporaryAuthorization(actionId); | ||
1007 | return PrivilegeOperationReturnValue::success(); | 1290 | return PrivilegeOperationReturnValue::success(); | ||
1008 | } | 1291 | } | ||
1009 | 1292 | | |||
1010 | return PrivilegeOperationReturnValue::failure(KIO::ERR_ACCESS_DENIED); | 1293 | return PrivilegeOperationReturnValue::failure(KIO::ERR_ACCESS_DENIED); | ||
1011 | } | 1294 | } | ||
1295 | | ||||
1296 | |
static