From feec00f4e7d5239067052aecbd2aed3291e46791 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= Date: Thu, 17 Aug 2017 11:07:24 +0300 Subject: [PATCH] Add basic support for UDF filesystem For reading UDF label and UUID is needed blkid >= 2.30. For creating new UDF filesystem is needed mkudffs binary from the udftools package. When creating new UDF fileystem, revision 2.01 for hard disk media is used. Therefore it is not possible to use it for optical (or other) medias. Problems: * Check for min and max capacity is incorrect as it depends on logical (sector) size of the disk. * Check for max label length is incorrect too as it depends on characters itself in label. * Specifying label is not working yet as FileSystem::create() does not get label parameter. * UDF filesystem should be used on unpartitioned disk, without MBR or GPT and spanning whole disk, but KDE Partition Manager does not support it. * When MBR is used, MBR partition id should be 0x07, but currently it is incorrect 0x83. See: https://serverfault.com/a/829172 (same for GPT) --- CMakeLists.txt | 2 +- src/fs/CMakeLists.txt | 2 + src/fs/filesystem.cpp | 1 + src/fs/filesystem.h | 3 +- src/fs/filesystemfactory.cpp | 3 + src/fs/udf.cpp | 121 +++++++++++++++++++++ src/fs/udf.h | 77 +++++++++++++ src/plugins/libparted/libpartedbackend.cpp | 1 + 8 files changed, 208 insertions(+), 2 deletions(-) create mode 100644 src/fs/udf.cpp create mode 100644 src/fs/udf.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 85a91f5..5508329 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -77,7 +77,7 @@ kde_enable_exceptions() find_package(PkgConfig REQUIRED) -pkg_check_modules(BLKID REQUIRED blkid>=2.23) +pkg_check_modules(BLKID REQUIRED blkid>=2.30) pkg_check_modules(LIBATASMART REQUIRED libatasmart) include_directories(${Qt5Core_INCLUDE_DIRS} ${UUID_INCLUDE_DIRS} ${BLKID_INCLUDE_DIRS} lib/ src/) diff --git a/src/fs/CMakeLists.txt b/src/fs/CMakeLists.txt index 47a5bee..441231a 100644 --- a/src/fs/CMakeLists.txt +++ b/src/fs/CMakeLists.txt @@ -22,6 +22,7 @@ set(FS_SRC fs/ocfs2.cpp fs/reiser4.cpp fs/reiserfs.cpp + fs/udf.cpp fs/ufs.cpp fs/unformatted.cpp fs/unknown.cpp @@ -53,6 +54,7 @@ set(FS_LIB_HDRS fs/ocfs2.h fs/reiser4.h fs/reiserfs.h + fs/udf.h fs/ufs.h fs/unformatted.h fs/unknown.h diff --git a/src/fs/filesystem.cpp b/src/fs/filesystem.cpp index a07ef89..801eb66 100644 --- a/src/fs/filesystem.cpp +++ b/src/fs/filesystem.cpp @@ -408,6 +408,7 @@ static const QString* typeNames() xi18nc("@item filesystem name", "nilfs2"), xi18nc("@item filesystem name", "lvm2 pv"), xi18nc("@item filesystem name", "f2fs"), + xi18nc("@item filesystem name", "udf"), }; return s; diff --git a/src/fs/filesystem.h b/src/fs/filesystem.h index 8e01f58..8411f83 100644 --- a/src/fs/filesystem.h +++ b/src/fs/filesystem.h @@ -84,8 +84,9 @@ public: Nilfs2 = 23, Lvm2_PV = 24, F2fs = 25, + Udf = 26, - __lastType = 26 + __lastType = 27 }; /** The type of support for a given FileSystem action */ diff --git a/src/fs/filesystemfactory.cpp b/src/fs/filesystemfactory.cpp index ebc70d2..e357657 100644 --- a/src/fs/filesystemfactory.cpp +++ b/src/fs/filesystemfactory.cpp @@ -40,6 +40,7 @@ #include "fs/ocfs2.h" #include "fs/reiser4.h" #include "fs/reiserfs.h" +#include "fs/udf.h" #include "fs/ufs.h" #include "fs/unformatted.h" #include "fs/unknown.h" @@ -78,6 +79,7 @@ void FileSystemFactory::init() m_FileSystems.insert(FileSystem::Ocfs2, new FS::ocfs2(-1, -1, -1, QString())); m_FileSystems.insert(FileSystem::ReiserFS, new FS::reiserfs(-1, -1, -1, QString())); m_FileSystems.insert(FileSystem::Reiser4, new FS::reiser4(-1, -1, -1, QString())); + m_FileSystems.insert(FileSystem::Udf, new FS::udf(-1, -1, -1, QString())); m_FileSystems.insert(FileSystem::Ufs, new FS::ufs(-1, -1, -1, QString())); m_FileSystems.insert(FileSystem::Unformatted, new FS::unformatted(-1, -1, -1, QString())); m_FileSystems.insert(FileSystem::Unknown, new FS::unknown(-1, -1, -1, QString())); @@ -124,6 +126,7 @@ FileSystem* FileSystemFactory::create(FileSystem::Type t, qint64 firstsector, qi case FileSystem::Ocfs2: fs = new FS::ocfs2(firstsector, lastsector, sectorsused, label); break; case FileSystem::ReiserFS: fs = new FS::reiserfs(firstsector, lastsector, sectorsused, label); break; case FileSystem::Reiser4: fs = new FS::reiser4(firstsector, lastsector, sectorsused, label); break; + case FileSystem::Udf: fs = new FS::udf(firstsector, lastsector, sectorsused, label); break; case FileSystem::Ufs: fs = new FS::ufs(firstsector, lastsector, sectorsused, label); break; case FileSystem::Unformatted: fs = new FS::unformatted(firstsector, lastsector, sectorsused, label); break; case FileSystem::Unknown: fs = new FS::unknown(firstsector, lastsector, sectorsused, label); break; diff --git a/src/fs/udf.cpp b/src/fs/udf.cpp new file mode 100644 index 0000000..1861238 --- /dev/null +++ b/src/fs/udf.cpp @@ -0,0 +1,121 @@ +/************************************************************************* + * Copyright (C) 2017 by Pali Rohár * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 3 of * + * the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see .* + *************************************************************************/ + +#include "fs/udf.h" + +#include "util/externalcommand.h" +#include "util/capacity.h" +#include "util/report.h" + +#include + +#include +#include + +#include +#include +#include +#include + +namespace FS +{ +constexpr qint64 MIN_UDF_BLOCKS = 282; +constexpr qint64 MAX_UDF_BLOCKS = ((1ULL << 32) - 1); + +FileSystem::CommandSupportType udf::m_Create = FileSystem::cmdSupportNone; + +udf::udf(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& label) : + FileSystem(firstsector, lastsector, sectorsused, label, FileSystem::Udf) +{ +} + +void udf::init() +{ + m_Create = findExternal(QStringLiteral("mkudffs"), {}, 1) ? cmdSupportFileSystem : cmdSupportNone; +} + +bool udf::supportToolFound() const +{ + return m_Create != cmdSupportNone; +} + +FileSystem::SupportTool udf::supportToolName() const +{ + return SupportTool(QStringLiteral("udftools"), QUrl(QStringLiteral("https://github.com/pali/udftools"))); +} + + +qint64 udf::minCapacity() const +{ + // TODO: Capacity depends on logical (sector) size of disk, now hardcoded as 512 + return MIN_UDF_BLOCKS * 512; +} + +qint64 udf::maxCapacity() const +{ + // TODO: Capacity depends on logical (sector) size of disk, now hardcoded as 4096 + return MAX_UDF_BLOCKS * 4096; +} + +qint64 udf::maxLabelLength() const +{ + return 126; // and only 63 if label contains character above U+FF +} + +bool udf::create(Report& report, const QString& deviceNode) +{ + // mkudffs from udftools prior to 1.1 does not check for partition limits and crashes + if ( length() > MAX_UDF_BLOCKS ) { + report.line() << xi18nc("@info:status", "Partition is too large"); + return false; + } + if ( length() < MIN_UDF_BLOCKS ) { + report.line() << xi18nc("@info:status", "Partition is too small"); + return false; + } + + // mkudffs from udftools prior to 1.1 is not able to detect logical (sector) size + // and UDF block size must match logical sector size of underlying media + int fd = open(deviceNode.toLocal8Bit().data(), O_RDONLY); + if ( fd < 0 ) { + report.line() << xi18nc("@info:status", "Cannot open device node"); + return false; + } + int blksize; + int ret = ioctl(fd, BLKSSZGET, &blksize); + close(fd); + + if ( ret != 0 ) { + report.line() << xi18nc("@info:status", "Cannot read logical (sector) size of device node"); + return false; + } + + ExternalCommand cmd(report, QStringLiteral("mkudffs"), { + QStringLiteral("--utf8"), + // TODO: Add GUI option for choosing different optical disks and UDF revision + // For now format as UDF revision 2.01 for hard disk media type + QStringLiteral("--media-type=hd"), + QStringLiteral("--udfrev=0x201"), + QStringLiteral("--blocksize=") + QString::number(blksize), + // TODO: Pass label as udf::create() parameter + // QStringLiteral("--lvid=") + label, + // QStringLiteral("--vid=") + shortlabel, + deviceNode + }); + return cmd.run(-1) && cmd.exitCode() == 0; +} +} diff --git a/src/fs/udf.h b/src/fs/udf.h new file mode 100644 index 0000000..eeae9c1 --- /dev/null +++ b/src/fs/udf.h @@ -0,0 +1,77 @@ +/************************************************************************* + * Copyright (C) 2017 by Pali Rohár * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 3 of * + * the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see .* + *************************************************************************/ + +#if !defined(UDF__H) + +#define UDF__H + +#include "util/libpartitionmanagerexport.h" + +#include "fs/filesystem.h" + +#include + +class Report; + +class QString; + +namespace FS +{ +/** A udf file system. + @author Pali Rohár + */ +class LIBKPMCORE_EXPORT udf : public FileSystem +{ +public: + udf(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& label); + +public: + void init() override; + + bool create(Report& report, const QString& deviceNode) override; + + CommandSupportType supportGetLabel() const override { + return cmdSupportCore; + } + CommandSupportType supportCreate() const override { + return m_Create; + } + CommandSupportType supportMove() const override { + return cmdSupportCore; + } + CommandSupportType supportCopy() const override { + return cmdSupportCore; + } + CommandSupportType supportBackup() const override { + return cmdSupportCore; + } + CommandSupportType supportGetUUID() const override { + return cmdSupportCore; + } + + qint64 minCapacity() const override; + qint64 maxCapacity() const override; + qint64 maxLabelLength() const override; + SupportTool supportToolName() const override; + bool supportToolFound() const override; + +public: + static CommandSupportType m_Create; +}; +} + +#endif diff --git a/src/plugins/libparted/libpartedbackend.cpp b/src/plugins/libparted/libpartedbackend.cpp index 8f4b5cd..86c9a77 100644 --- a/src/plugins/libparted/libpartedbackend.cpp +++ b/src/plugins/libparted/libpartedbackend.cpp @@ -512,6 +512,7 @@ FileSystem::Type LibPartedBackend::detectFileSystem(const QString& partitionPath else if (s == QStringLiteral("nilfs2")) rval = FileSystem::Nilfs2; else if (s == QStringLiteral("LVM2_member")) rval = FileSystem::Lvm2_PV; else if (s == QStringLiteral("f2fs")) rval = FileSystem::F2fs; + else if (s == QStringLiteral("udf")) rval = FileSystem::Udf; else qWarning() << "blkid: unknown file system type " << s << " on " << partitionPath; }