1 | /* |
2 | This file is part of the KDE libraries |
3 | |
4 | SPDX-FileCopyrightText: 2011 David Faure <faure@kde.org> |
5 | |
6 | SPDX-License-Identifier: LGPL-2.1-only |
7 | */ |
8 | |
9 | #include "kfilesystemtype.h" |
10 | #include "knetworkmounts.h" |
11 | |
12 | #include <config-kfilesystemtype.h> |
13 | #if HAVE_UDEV |
14 | #include <libudev.h> |
15 | #endif |
16 | |
17 | #include <QCoreApplication> |
18 | #include <QFile> |
19 | |
20 | #include <array> |
21 | |
22 | struct FsInfo { |
23 | KFileSystemType::Type type = KFileSystemType::Unknown; |
24 | const char *name = nullptr; |
25 | }; |
26 | |
27 | #ifndef Q_OS_WIN |
28 | static const std::array<FsInfo, 19> s_fsMap = {._M_elems: { |
29 | {.type: KFileSystemType::Nfs, .name: "nfs" }, |
30 | {.type: KFileSystemType::Smb, .name: "smb" }, |
31 | {.type: KFileSystemType::Fat, .name: "fat" }, |
32 | {.type: KFileSystemType::Ramfs, .name: "ramfs" }, |
33 | {.type: KFileSystemType::Other, .name: "other" }, |
34 | {.type: KFileSystemType::Ntfs, .name: "ntfs" }, |
35 | {.type: KFileSystemType::Ntfs, .name: "ntfs3" }, |
36 | {.type: KFileSystemType::Exfat, .name: "exfat" }, |
37 | {.type: KFileSystemType::Unknown, .name: "unknown" }, |
38 | {.type: KFileSystemType::Nfs, .name: "autofs" }, |
39 | {.type: KFileSystemType::Nfs, .name: "cachefs" }, |
40 | {.type: KFileSystemType::Nfs, .name: "fuse.sshfs" }, |
41 | {.type: KFileSystemType::Nfs, .name: "xtreemfs@" }, // #178678 |
42 | {.type: KFileSystemType::Smb, .name: "smbfs" }, |
43 | {.type: KFileSystemType::Smb, .name: "cifs" }, |
44 | {.type: KFileSystemType::Fat, .name: "vfat" }, |
45 | {.type: KFileSystemType::Fat, .name: "msdos" }, |
46 | {.type: KFileSystemType::Fuse, .name: "fuseblk" }, |
47 | }}; |
48 | |
49 | inline KFileSystemType::Type kde_typeFromName(const QLatin1String name) |
50 | { |
51 | auto it = std::find_if(first: s_fsMap.cbegin(), last: s_fsMap.cend(), pred: [name](const auto &fsInfo) { |
52 | return QLatin1String(fsInfo.name) == name; |
53 | }); |
54 | return it != s_fsMap.cend() ? it->type : KFileSystemType::Other; |
55 | } |
56 | |
57 | inline KFileSystemType::Type kde_typeFromName(const char *c) |
58 | { |
59 | return kde_typeFromName(name: QLatin1String(c)); |
60 | } |
61 | |
62 | #if defined(Q_OS_BSD4) && !defined(Q_OS_NETBSD) |
63 | #include <sys/mount.h> |
64 | #include <sys/param.h> |
65 | |
66 | KFileSystemType::Type determineFileSystemTypeImpl(const QByteArray &path) |
67 | { |
68 | struct statfs buf; |
69 | if (statfs(path.constData(), &buf) != 0) { |
70 | return KFileSystemType::Unknown; |
71 | } |
72 | return kde_typeFromName(buf.f_fstypename); |
73 | } |
74 | |
75 | #elif defined(Q_OS_LINUX) || defined(Q_OS_HURD) |
76 | #include <sys/statfs.h> |
77 | |
78 | #ifdef Q_OS_LINUX |
79 | #include <linux/magic.h> // A lot of the filesystem superblock MAGIC numbers |
80 | #include <sys/stat.h> |
81 | #endif |
82 | |
83 | // Add known missig magics |
84 | // Can use https://github.com/systemd/systemd/blob/main/src/basic/missing_magic.h |
85 | // as reference |
86 | |
87 | // From linux/fs/ntfs/ntfs.h |
88 | #ifndef NTFS_SB_MAGIC |
89 | #define NTFS_SB_MAGIC 0x5346544e |
90 | #endif |
91 | |
92 | // From linux/fs/ntfs3/super.c |
93 | #ifndef NTFS3_MAGIC |
94 | #define NTFS3_MAGIC 0x7366746E |
95 | #endif |
96 | |
97 | // From linux/fs/exfat/exfat_fs.h |
98 | #ifndef EXFAT_SUPER_MAGIC |
99 | #define EXFAT_SUPER_MAGIC 0x2011BAB0UL |
100 | #endif |
101 | |
102 | // From linux/fs/cifs/smb2glob.h |
103 | #ifndef SMB2_MAGIC_NUMBER |
104 | #define SMB2_MAGIC_NUMBER 0xFE534D42 |
105 | #endif |
106 | |
107 | // From linux/fs/cifs/cifsglob.h |
108 | #ifndef CIFS_MAGIC_NUMBER |
109 | #define CIFS_MAGIC_NUMBER 0xFF534D42 |
110 | #endif |
111 | |
112 | // From linux/fs/fuse/inode.c |
113 | #ifndef FUSE_SUPER_MAGIC |
114 | #define FUSE_SUPER_MAGIC 0x65735546 |
115 | #endif |
116 | |
117 | #ifndef AUTOFSNG_SUPER_MAGIC |
118 | #define AUTOFSNG_SUPER_MAGIC 0x7d92b1a0 |
119 | #endif |
120 | |
121 | #ifdef Q_OS_HURD |
122 | #ifndef NFS_SUPER_MAGIC |
123 | #define NFS_SUPER_MAGIC 0x00006969 |
124 | #endif |
125 | #ifndef AUTOFS_SUPER_MAGIC |
126 | #define AUTOFS_SUPER_MAGIC 0x00000187 |
127 | #endif |
128 | #ifndef MSDOS_SUPER_MAGIC |
129 | #define MSDOS_SUPER_MAGIC 0x00004d44 |
130 | #endif |
131 | #ifndef SMB_SUPER_MAGIC |
132 | #define SMB_SUPER_MAGIC 0x0000517B |
133 | #endif |
134 | #ifndef RAMFS_MAGIC |
135 | #define RAMFS_MAGIC 0x858458F6 |
136 | #endif |
137 | #endif |
138 | |
139 | KFileSystemType::Type probeFuseBlkType(const QByteArray &path) |
140 | { |
141 | using namespace KFileSystemType; |
142 | |
143 | #if HAVE_UDEV |
144 | struct stat buf; |
145 | if (stat(file: path.constData(), buf: &buf) != 0) { |
146 | return Fuse; |
147 | } |
148 | |
149 | using UdevPtr = std::unique_ptr<struct udev, decltype(&udev_unref)>; |
150 | using UDevicePtr = std::unique_ptr<struct udev_device, decltype(&udev_device_unref)>; |
151 | |
152 | // Code originally copied from util-linux/misc-utils/lsblk.c |
153 | auto udevP = UdevPtr(udev_new(), udev_unref); |
154 | if (!udevP) { |
155 | return Fuse; |
156 | } |
157 | |
158 | // 'b' for block devices |
159 | auto devPtr = UDevicePtr(udev_device_new_from_devnum(udev: udevP.get(), type: 'b', devnum: buf.st_dev), udev_device_unref); |
160 | if (!devPtr) { |
161 | // If is not block device, assume conservatively it is a remote FS under FUSE. |
162 | return Nfs; |
163 | } |
164 | |
165 | const QLatin1String fsType(udev_device_get_property_value(udev_device: devPtr.get(), key: "ID_FS_TYPE" )); |
166 | return kde_typeFromName(name: fsType); |
167 | #endif |
168 | |
169 | return Fuse; |
170 | } |
171 | |
172 | // Reverse-engineering without C++ code: |
173 | // strace stat -f /mnt 2>&1|grep statfs|grep mnt, and look for f_type |
174 | // |
175 | // Also grep'ing in /usr/src/<kernel-version>/fs/ |
176 | |
177 | static KFileSystemType::Type determineFileSystemTypeImpl(const QByteArray &path) |
178 | { |
179 | struct statfs buf; |
180 | if (statfs(file: path.constData(), buf: &buf) != 0) { |
181 | return KFileSystemType::Unknown; |
182 | } |
183 | |
184 | switch (static_cast<unsigned long>(buf.f_type)) { |
185 | case NFS_SUPER_MAGIC: |
186 | case AUTOFS_SUPER_MAGIC: |
187 | case AUTOFSNG_SUPER_MAGIC: |
188 | return KFileSystemType::Nfs; |
189 | case FUSE_SUPER_MAGIC: |
190 | return probeFuseBlkType(path); |
191 | case SMB_SUPER_MAGIC: |
192 | case SMB2_MAGIC_NUMBER: |
193 | case CIFS_MAGIC_NUMBER: |
194 | return KFileSystemType::Smb; |
195 | case MSDOS_SUPER_MAGIC: |
196 | return KFileSystemType::Fat; |
197 | case NTFS_SB_MAGIC: |
198 | case NTFS3_MAGIC: |
199 | return KFileSystemType::Ntfs; |
200 | case EXFAT_SUPER_MAGIC: |
201 | return KFileSystemType::Exfat; |
202 | case RAMFS_MAGIC: |
203 | return KFileSystemType::Ramfs; |
204 | default: |
205 | return KFileSystemType::Other; |
206 | } |
207 | } |
208 | |
209 | #elif defined(Q_OS_AIX) || defined(Q_OS_HPUX) || defined(Q_OS_QNX) || defined(Q_OS_SCO) || defined(Q_OS_UNIXWARE) || defined(Q_OS_RELIANT) \ |
210 | || defined(Q_OS_NETBSD) |
211 | #include <sys/statvfs.h> |
212 | |
213 | KFileSystemType::Type determineFileSystemTypeImpl(const QByteArray &path) |
214 | { |
215 | struct statvfs buf; |
216 | if (statvfs(path.constData(), &buf) != 0) { |
217 | return KFileSystemType::Unknown; |
218 | } |
219 | #if defined(Q_OS_NETBSD) |
220 | return kde_typeFromName(buf.f_fstypename); |
221 | #else |
222 | return kde_typeFromName(buf.f_basetype); |
223 | #endif |
224 | } |
225 | #endif |
226 | #else |
227 | KFileSystemType::Type determineFileSystemTypeImpl(const QByteArray &path) |
228 | { |
229 | Q_UNUSED(path); |
230 | return KFileSystemType::Unknown; |
231 | } |
232 | #endif |
233 | |
234 | KFileSystemType::Type KFileSystemType::fileSystemType(const QString &path) |
235 | { |
236 | if (KNetworkMounts::self()->isSlowPath(path, type: KNetworkMounts::KNetworkMountsType::SmbPaths)) { |
237 | return KFileSystemType::Smb; |
238 | } else if (KNetworkMounts::self()->isSlowPath(path, type: KNetworkMounts::KNetworkMountsType::NfsPaths)) { |
239 | return KFileSystemType::Nfs; |
240 | } else { |
241 | return determineFileSystemTypeImpl(path: QFile::encodeName(fileName: path)); |
242 | } |
243 | } |
244 | |
245 | QString KFileSystemType::fileSystemName(KFileSystemType::Type type) |
246 | { |
247 | switch (type) { |
248 | case KFileSystemType::Nfs: |
249 | return QCoreApplication::translate(context: "KFileSystemType" , key: "NFS" ); |
250 | case KFileSystemType::Smb: |
251 | return QCoreApplication::translate(context: "KFileSystemType" , key: "SMB" ); |
252 | case KFileSystemType::Fat: |
253 | return QCoreApplication::translate(context: "KFileSystemType" , key: "FAT" ); |
254 | case KFileSystemType::Ramfs: |
255 | return QCoreApplication::translate(context: "KFileSystemType" , key: "RAMFS" ); |
256 | case KFileSystemType::Other: |
257 | return QCoreApplication::translate(context: "KFileSystemType" , key: "Other" ); |
258 | case KFileSystemType::Ntfs: |
259 | return QCoreApplication::translate(context: "KFileSystemType" , key: "NTFS" ); |
260 | case KFileSystemType::Exfat: |
261 | return QCoreApplication::translate(context: "KFileSystemType" , key: "ExFAT" ); |
262 | case KFileSystemType::Fuse: |
263 | return QCoreApplication::translate(context: "KFileSystemType" , key: "FUSE" ); |
264 | case KFileSystemType::Unknown: |
265 | return QCoreApplication::translate(context: "KFileSystemType" , key: "Unknown" ); |
266 | } |
267 | |
268 | Q_UNREACHABLE(); |
269 | return {}; |
270 | } |
271 | |