Changeset View
Changeset View
Standalone View
Standalone View
src/ioslaves/file/file.cpp
Show First 20 Lines • Show All 61 Lines • ▼ Show 20 Line(s) | |||||
62 | #include <kconfiggroup.h> | 62 | #include <kconfiggroup.h> | ||
63 | #include <kshell.h> | 63 | #include <kshell.h> | ||
64 | #include <kmountpoint.h> | 64 | #include <kmountpoint.h> | ||
65 | #include <klocalizedstring.h> | 65 | #include <klocalizedstring.h> | ||
66 | #include <QMimeDatabase> | 66 | #include <QMimeDatabase> | ||
67 | #include <QStandardPaths> | 67 | #include <QStandardPaths> | ||
68 | #include <QDataStream> | 68 | #include <QDataStream> | ||
69 | 69 | | |||
70 | #if HAVE_STATX | | |||
71 | #include <sys/stat.h> | | |||
72 | #endif | | |||
73 | | ||||
74 | #if HAVE_VOLMGT | 70 | #if HAVE_VOLMGT | ||
75 | #include <volmgt.h> | 71 | #include <volmgt.h> | ||
76 | #include <sys/mnttab.h> | 72 | #include <sys/mnttab.h> | ||
77 | #endif | 73 | #endif | ||
78 | 74 | | |||
79 | #include <kdirnotify.h> | 75 | #include <kdirnotify.h> | ||
80 | #include <ioslave_defaults.h> | 76 | #include <ioslave_defaults.h> | ||
81 | 77 | | |||
▲ Show 20 Lines • Show All 742 Lines • ▼ Show 20 Line(s) | 818 | #endif | |||
824 | } | 820 | } | ||
825 | 821 | | |||
826 | } | 822 | } | ||
827 | 823 | | |||
828 | // We have done our job => finish | 824 | // We have done our job => finish | ||
829 | finished(); | 825 | finished(); | ||
830 | } | 826 | } | ||
831 | 827 | | |||
832 | QString FileProtocol::getUserName(KUserId uid) const | | |||
833 | { | | |||
834 | if (Q_UNLIKELY(!uid.isValid())) { | | |||
835 | return QString(); | | |||
836 | } | | |||
837 | auto it = mUsercache.find(uid); | | |||
838 | if (it == mUsercache.end()) { | | |||
839 | KUser user(uid); | | |||
840 | QString name = user.loginName(); | | |||
841 | if (name.isEmpty()) { | | |||
842 | name = uid.toString(); | | |||
843 | } | | |||
844 | it = mUsercache.insert(uid, name); | | |||
845 | } | | |||
846 | return *it; | | |||
847 | } | | |||
848 | | ||||
849 | QString FileProtocol::getGroupName(KGroupId gid) const | | |||
850 | { | | |||
851 | if (Q_UNLIKELY(!gid.isValid())) { | | |||
852 | return QString(); | | |||
853 | } | | |||
854 | auto it = mGroupcache.find(gid); | | |||
855 | if (it == mGroupcache.end()) { | | |||
856 | KUserGroup group(gid); | | |||
857 | QString name = group.name(); | | |||
858 | if (name.isEmpty()) { | | |||
859 | name = gid.toString(); | | |||
860 | } | | |||
861 | it = mGroupcache.insert(gid, name); | | |||
862 | } | | |||
863 | return *it; | | |||
864 | } | | |||
865 | | ||||
866 | #if HAVE_STATX | | |||
867 | // statx syscall is available | | |||
868 | inline int LSTAT(const char* path, struct statx * buff, KIO::StatDetails details) { | | |||
869 | uint32_t mask = 0; | | |||
870 | if (details & KIO::Basic) { | | |||
871 | // filename, access, type, size, linkdest | | |||
872 | mask |= STATX_SIZE | STATX_TYPE; | | |||
873 | } | | |||
874 | if (details & KIO::User) { | | |||
875 | // uid, gid | | |||
876 | mask |= STATX_UID | STATX_GID; | | |||
877 | } | | |||
878 | if (details & KIO::Time) { | | |||
879 | // atime, mtime, btime | | |||
880 | mask |= STATX_ATIME | STATX_MTIME | STATX_BTIME; | | |||
881 | } | | |||
882 | if (details & KIO::Inode) { | | |||
883 | // dev, inode | | |||
884 | mask |= STATX_INO; | | |||
885 | } | | |||
886 | return statx(AT_FDCWD, path, AT_SYMLINK_NOFOLLOW, mask, buff); | | |||
887 | } | | |||
888 | inline int STAT(const char* path, struct statx * buff, KIO::StatDetails details) { | | |||
889 | uint32_t mask = 0; | | |||
890 | if (details & KIO::Basic) { | | |||
891 | // filename, access, type, size, linkdest | | |||
892 | mask |= STATX_SIZE | STATX_TYPE; | | |||
893 | } | | |||
894 | if (details & KIO::User) { | | |||
895 | // uid, gid | | |||
896 | mask |= STATX_UID | STATX_GID; | | |||
897 | } | | |||
898 | if (details & KIO::Time) { | | |||
899 | // atime, mtime, btime | | |||
900 | mask |= STATX_ATIME | STATX_MTIME | STATX_BTIME; | | |||
901 | } | | |||
902 | // KIO::Inode is ignored as when STAT is called, the entry inode field has already been filled | | |||
903 | return statx(AT_FDCWD, path, AT_STATX_SYNC_AS_STAT, mask, buff); | | |||
904 | } | | |||
905 | inline static uint16_t stat_mode(struct statx &buf) { return buf.stx_mode; } | | |||
906 | inline static uint32_t stat_dev(struct statx &buf) { return buf.stx_dev_major; } | | |||
907 | inline static uint64_t stat_ino(struct statx &buf) { return buf.stx_ino; } | | |||
908 | inline static uint64_t stat_size(struct statx &buf) { return buf.stx_size; } | | |||
909 | inline static uint32_t stat_uid(struct statx &buf) { return buf.stx_uid; } | | |||
910 | inline static uint32_t stat_gid(struct statx &buf) { return buf.stx_gid; } | | |||
911 | inline static int64_t stat_atime(struct statx &buf) { return buf.stx_atime.tv_sec; } | | |||
912 | inline static int64_t stat_mtime(struct statx &buf) { return buf.stx_mtime.tv_sec; } | | |||
913 | #else | | |||
914 | // regular stat struct | | |||
915 | inline int LSTAT(const char* path, QT_STATBUF * buff, KIO::StatDetails details) { | | |||
916 | Q_UNUSED(details) | | |||
917 | return QT_LSTAT(path, buff); | | |||
918 | } | | |||
919 | inline int STAT(const char* path, QT_STATBUF * buff, KIO::StatDetails details) { | | |||
920 | Q_UNUSED(details) | | |||
921 | return QT_STAT(path, buff); | | |||
922 | } | | |||
923 | inline static mode_t stat_mode(QT_STATBUF &buf) { return buf.st_mode; } | | |||
924 | inline static dev_t stat_dev(QT_STATBUF &buf) { return buf.st_dev; } | | |||
925 | inline static ino_t stat_ino(QT_STATBUF &buf) { return buf.st_ino; } | | |||
926 | inline static off_t stat_size(QT_STATBUF &buf) { return buf.st_size; } | | |||
927 | #ifndef Q_OS_WIN | | |||
928 | inline static uid_t stat_uid(QT_STATBUF &buf) { return buf.st_uid; } | | |||
929 | inline static gid_t stat_gid(QT_STATBUF &buf) { return buf.st_gid; } | | |||
930 | #endif | | |||
931 | inline static time_t stat_atime(QT_STATBUF &buf) { return buf.st_atime; } | | |||
932 | inline static time_t stat_mtime(QT_STATBUF &buf) { return buf.st_mtime; } | | |||
933 | #endif | | |||
934 | | ||||
935 | bool FileProtocol::createUDSEntry(const QString &filename, const QByteArray &path, UDSEntry &entry, | | |||
936 | KIO::StatDetails details) | | |||
937 | { | | |||
938 | assert(entry.count() == 0); // by contract :-) | | |||
939 | int entries = 0; | | |||
940 | if (details & KIO::Basic) { | | |||
941 | // filename, access, type, size, linkdest | | |||
942 | entries += 5; | | |||
943 | } | | |||
944 | if (details & KIO::User) { | | |||
945 | // uid, gid | | |||
946 | entries += 2; | | |||
947 | } | | |||
948 | if (details & KIO::Time) { | | |||
949 | // atime, mtime, btime | | |||
950 | entries += 3; | | |||
951 | } | | |||
952 | if (details & KIO::Acl) { | | |||
953 | // acl data | | |||
954 | entries += 3; | | |||
955 | } | | |||
956 | if (details & KIO::Inode) { | | |||
957 | // dev, inode | | |||
958 | entries += 2; | | |||
959 | } | | |||
960 | entry.reserve(entries); | | |||
961 | | ||||
962 | if (details & KIO::Basic) { | | |||
963 | entry.fastInsert(KIO::UDSEntry::UDS_NAME, filename); | | |||
964 | } | | |||
965 | | ||||
966 | mode_t type; | | |||
967 | mode_t access; | | |||
968 | bool isBrokenSymLink = false; | | |||
969 | signed long long size = 0LL; | | |||
970 | #if HAVE_POSIX_ACL | | |||
971 | QByteArray targetPath = path; | | |||
972 | #endif | | |||
973 | | ||||
974 | #if HAVE_STATX | | |||
975 | // statx syscall is available | | |||
976 | struct statx buff; | | |||
977 | #else | | |||
978 | QT_STATBUF buff; | | |||
979 | #endif | | |||
980 | | ||||
981 | if (LSTAT(path.data(), &buff, details) == 0) { | | |||
982 | | ||||
983 | if (details & KIO::Inode) { | | |||
984 | entry.fastInsert(KIO::UDSEntry::UDS_DEVICE_ID, stat_dev(buff)); | | |||
985 | entry.fastInsert(KIO::UDSEntry::UDS_INODE, stat_ino(buff)); | | |||
986 | } | | |||
987 | | ||||
988 | if ((stat_mode(buff) & QT_STAT_MASK) == QT_STAT_LNK) { | | |||
989 | | ||||
990 | QByteArray linkTargetBuffer; | | |||
991 | if (details & (KIO::Basic|KIO::ResolveSymlink)) { | | |||
992 | #ifdef Q_OS_WIN | | |||
993 | const QString linkTarget = QFile::symLinkTarget(QFile::decodeName(path)); | | |||
994 | #else | | |||
995 | // Use readlink on Unix because symLinkTarget turns relative targets into absolute (#352927) | | |||
996 | #if HAVE_STATX | | |||
997 | size_t lowerBound = 256; | | |||
998 | size_t higherBound = 1024; | | |||
999 | uint64_t s = stat_size(buff); | | |||
1000 | if (s > SIZE_MAX) { | | |||
1001 | qCWarning(KIO_FILE) << "file size bigger than SIZE_MAX, too big for readlink use!" << path; | | |||
1002 | return false; | | |||
1003 | } | | |||
1004 | size_t size = static_cast<size_t>(s); | | |||
1005 | using SizeType = size_t; | | |||
1006 | #else | | |||
1007 | off_t lowerBound = 256; | | |||
1008 | off_t higherBound = 1024; | | |||
1009 | off_t size = stat_size(buff); | | |||
1010 | using SizeType = off_t; | | |||
1011 | #endif | | |||
1012 | SizeType bufferSize = qBound(lowerBound, size +1, higherBound); | | |||
1013 | linkTargetBuffer.resize(bufferSize); | | |||
1014 | while (true) { | | |||
1015 | ssize_t n = readlink(path.constData(), linkTargetBuffer.data(), bufferSize); | | |||
1016 | if (n < 0 && errno != ERANGE) { | | |||
1017 | qCWarning(KIO_FILE) << "readlink failed!" << path; | | |||
1018 | return false; | | |||
1019 | } else if (n > 0 && static_cast<SizeType>(n) != bufferSize) { | | |||
1020 | // the buffer was not filled in the last iteration | | |||
1021 | // we are finished reading, break the loop | | |||
1022 | linkTargetBuffer.truncate(n); | | |||
1023 | break; | | |||
1024 | } | | |||
1025 | bufferSize *= 2; | | |||
1026 | linkTargetBuffer.resize(bufferSize); | | |||
1027 | } | | |||
1028 | const QString linkTarget = QFile::decodeName(linkTargetBuffer); | | |||
1029 | #endif | | |||
1030 | entry.fastInsert(KIO::UDSEntry::UDS_LINK_DEST, linkTarget); | | |||
1031 | } | | |||
1032 | | ||||
1033 | // A symlink | | |||
1034 | if (details & KIO::ResolveSymlink) { | | |||
1035 | if (STAT(path.constData(), &buff, details) == -1) { | | |||
1036 | isBrokenSymLink = true; | | |||
1037 | } else { | | |||
1038 | #if HAVE_POSIX_ACL | | |||
1039 | if (details & KIO::Acl) { | | |||
1040 | // valid symlink, will get the ACLs of the destination | | |||
1041 | targetPath = linkTargetBuffer; | | |||
1042 | } | | |||
1043 | #endif | | |||
1044 | } | | |||
1045 | } | | |||
1046 | } | | |||
1047 | } else { | | |||
1048 | // qCWarning(KIO_FILE) << "lstat didn't work on " << path.data(); | | |||
1049 | return false; | | |||
1050 | } | | |||
1051 | | ||||
1052 | if (details & KIO::Basic) { | | |||
1053 | if (isBrokenSymLink) { | | |||
1054 | // It is a link pointing to nowhere | | |||
1055 | type = S_IFMT - 1; | | |||
1056 | access = S_IRWXU | S_IRWXG | S_IRWXO; | | |||
1057 | size = 0LL; | | |||
1058 | } else { | | |||
1059 | type = stat_mode(buff) & S_IFMT; // extract file type | | |||
1060 | access = stat_mode(buff) & 07777; // extract permissions | | |||
1061 | size = stat_size(buff); | | |||
1062 | } | | |||
1063 | | ||||
1064 | entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, type); | | |||
1065 | entry.fastInsert(KIO::UDSEntry::UDS_ACCESS, access); | | |||
1066 | entry.fastInsert(KIO::UDSEntry::UDS_SIZE, size); | | |||
1067 | } | | |||
1068 | | ||||
1069 | #if HAVE_POSIX_ACL | | |||
1070 | if (details & KIO::Acl) { | | |||
1071 | /* Append an atom indicating whether the file has extended acl information | | |||
1072 | * and if withACL is specified also one with the acl itself. If it's a directory | | |||
1073 | * and it has a default ACL, also append that. */ | | |||
1074 | appendACLAtoms(targetPath, entry, type); | | |||
1075 | } | | |||
1076 | #endif | | |||
1077 | | ||||
1078 | if (details & KIO::User) { | | |||
1079 | #ifndef Q_OS_WIN | | |||
1080 | entry.fastInsert(KIO::UDSEntry::UDS_USER, getUserName(KUserId(stat_uid(buff)))); | | |||
1081 | entry.fastInsert(KIO::UDSEntry::UDS_GROUP, getGroupName(KGroupId(stat_gid(buff)))); | | |||
1082 | #else | | |||
1083 | #pragma message("TODO: st_uid and st_gid are always zero, use GetSecurityInfo to find the owner") | | |||
1084 | #endif | | |||
1085 | } | | |||
1086 | | ||||
1087 | if (details & KIO::Time) { | | |||
1088 | entry.fastInsert(KIO::UDSEntry::UDS_MODIFICATION_TIME, stat_mtime(buff)); | | |||
1089 | entry.fastInsert(KIO::UDSEntry::UDS_ACCESS_TIME, stat_atime(buff)); | | |||
1090 | | ||||
1091 | #ifdef st_birthtime | | |||
1092 | /* For example FreeBSD's and NetBSD's stat contains a field for | | |||
1093 | * the inode birth time: st_birthtime | | |||
1094 | * This however only works on UFS and ZFS, and not, on say, NFS. | | |||
1095 | * Instead of setting a bogus fallback like st_mtime, only use | | |||
1096 | * it if it is greater than 0. */ | | |||
1097 | if (buff.st_birthtime > 0) { | | |||
1098 | entry.fastInsert(KIO::UDSEntry::UDS_CREATION_TIME, buff.st_birthtime); | | |||
1099 | } | | |||
1100 | #elif defined __st_birthtime | | |||
1101 | /* As above, but OpenBSD calls it slightly differently. */ | | |||
1102 | if (buff.__st_birthtime > 0) { | | |||
1103 | entry.fastInsert(KIO::UDSEntry::UDS_CREATION_TIME, buff.__st_birthtime); | | |||
1104 | } | | |||
1105 | #elif HAVE_STATX | | |||
1106 | /* And linux version using statx syscall */ | | |||
1107 | if (buff.stx_mask & STATX_BTIME) { | | |||
1108 | entry.fastInsert(KIO::UDSEntry::UDS_CREATION_TIME, buff.stx_btime.tv_sec); | | |||
1109 | } | | |||
1110 | #endif | | |||
1111 | } | | |||
1112 | | ||||
1113 | return true; | | |||
1114 | } | | |||
1115 | | ||||
1116 | void FileProtocol::special(const QByteArray &data) | 828 | void FileProtocol::special(const QByteArray &data) | ||
1117 | { | 829 | { | ||
1118 | int tmp; | 830 | int tmp; | ||
1119 | QDataStream stream(data); | 831 | QDataStream stream(data); | ||
1120 | 832 | | |||
1121 | stream >> tmp; | 833 | stream >> tmp; | ||
1122 | switch (tmp) { | 834 | switch (tmp) { | ||
1123 | case 1: { | 835 | case 1: { | ||
▲ Show 20 Lines • Show All 500 Lines • Show Last 20 Lines |