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

source code of kcoreaddons/src/lib/io/kfilesystemtype.cpp