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
22struct FsInfo {
23 KFileSystemType::Type type = KFileSystemType::Unknown;
24 const char *name = nullptr;
25};
26
27#ifndef Q_OS_WIN
28static 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
49inline 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
57inline 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
66KFileSystemType::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
139KFileSystemType::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
177static 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
213KFileSystemType::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
227KFileSystemType::Type determineFileSystemTypeImpl(const QByteArray &path)
228{
229 Q_UNUSED(path);
230 return KFileSystemType::Unknown;
231}
232#endif
233
234KFileSystemType::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
245QString 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

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