summaryrefslogtreecommitdiffstats
path: root/libparted/fs/udf/udf.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--libparted/fs/udf/udf.c175
1 files changed, 175 insertions, 0 deletions
diff --git a/libparted/fs/udf/udf.c b/libparted/fs/udf/udf.c
new file mode 100644
index 0000000..a48a42e
--- /dev/null
+++ b/libparted/fs/udf/udf.c
@@ -0,0 +1,175 @@
+/*
+ libparted - a library for manipulating disk partitions
+ Copyright (C) 2018-2023 Free Software Foundation, Inc.
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#include <parted/parted.h>
+
+/* Read bytes using ped_geometry_read() function */
+static int read_bytes (const PedGeometry* geom, void* buffer, PedSector offset, PedSector count)
+{
+ char* sector_buffer;
+ PedSector sector_offset, sector_count, buffer_offset;
+
+ sector_offset = offset / geom->dev->sector_size;
+ sector_count = (offset + count + geom->dev->sector_size - 1) / geom->dev->sector_size - sector_offset;
+ buffer_offset = offset - sector_offset * geom->dev->sector_size;
+
+ sector_buffer = alloca (sector_count * geom->dev->sector_size);
+
+ if (!ped_geometry_read (geom, sector_buffer, sector_offset, sector_count))
+ return 0;
+
+ memcpy (buffer, sector_buffer + buffer_offset, count);
+ return 1;
+}
+
+/* Scan VSR and check for UDF VSD */
+static int check_vrs (const PedGeometry* geom, unsigned int vsdsize)
+{
+ PedSector block;
+ PedSector offset;
+ unsigned char ident[5];
+
+ /* Check only first 64 blocks, but theoretically standard does not define upper limit */
+ for (block = 0; block < 64; block++) {
+ /* VRS starts at fixed offset 32kB, it is independent of block size or vsd size */
+ offset = 32768 + block * vsdsize;
+
+ /* Read VSD identifier, it is at offset 1 */
+ if (!read_bytes (geom, ident, offset + 1, 5))
+ return 0;
+
+ /* Check for UDF identifier */
+ if (memcmp (ident, "NSR02", 5) == 0 ||
+ memcmp (ident, "NSR03", 5) == 0)
+ return 1;
+
+ /* Unknown VSD identifier means end of VRS */
+ if (memcmp (ident, "BEA01", 5) != 0 &&
+ memcmp (ident, "TEA01", 5) != 0 &&
+ memcmp (ident, "BOOT2", 5) != 0 &&
+ memcmp (ident, "CD001", 5) != 0 &&
+ memcmp (ident, "CDW02", 5) != 0)
+ break;
+ }
+
+ return 0;
+}
+
+/* Check for UDF AVDP */
+static int check_anchor (const PedGeometry* geom, unsigned int blocksize, int rel_block)
+{
+ PedSector block;
+ unsigned char tag[16];
+
+ /* Negative block means relative to the end of device */
+ if (rel_block < 0) {
+ block = geom->length * geom->dev->sector_size / blocksize;
+ if (block <= (PedSector)(-rel_block))
+ return 0;
+ block -= (PedSector)(-rel_block);
+ if (block < 257)
+ return 0;
+ } else {
+ block = rel_block;
+ }
+
+ if (!read_bytes (geom, tag, block * blocksize, 16))
+ return 0;
+
+ /* Check for AVDP type (0x0002) */
+ if (((unsigned short)tag[0] | ((unsigned short)tag[1] << 8)) != 0x0002)
+ return 0;
+
+ /* Check that location stored in AVDP matches */
+ if (((unsigned long)tag[12] | ((unsigned long)tag[13] << 8) | ((unsigned long)tag[14] << 16) | ((unsigned long)tag[15] << 24)) != block)
+ return 0;
+
+ return 1;
+}
+
+/* Detect presence of UDF AVDP */
+static int detect_anchor(const PedGeometry* geom, unsigned int blocksize)
+{
+ /* All possible AVDP locations in preferred order */
+ static int anchors[] = { 256, -257, -1, 512 };
+ size_t i;
+
+ for (i = 0; i < sizeof (anchors)/sizeof (*anchors); i++) {
+ if (check_anchor (geom, blocksize, anchors[i]))
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Detect UDF filesystem, it must have VRS and AVDP */
+static int detect_udf (const PedGeometry* geom)
+{
+ unsigned int blocksize;
+
+ /* VSD size is min(2048, UDF block size), check for block sizes <= 2048 */
+ if (check_vrs (geom, 2048)) {
+ for (blocksize = 512; blocksize <= 2048; blocksize *= 2) {
+ if (detect_anchor (geom, blocksize))
+ return 1;
+ }
+ }
+
+ /* Check for block sizes larger then 2048, maximal theoretical block size is 32kB */
+ for (blocksize = 4096; blocksize <= 32768; blocksize *= 2) {
+ if (!check_vrs (geom, blocksize))
+ continue;
+ if (detect_anchor (geom, blocksize))
+ return 1;
+ }
+
+ return 0;
+}
+
+PedGeometry*
+udf_probe (PedGeometry* geom)
+{
+ if (!detect_udf (geom))
+ return NULL;
+
+ return ped_geometry_duplicate (geom);
+}
+
+static PedFileSystemOps udf_ops = {
+ probe: udf_probe,
+};
+
+static PedFileSystemType udf_type = {
+ next: NULL,
+ ops: &udf_ops,
+ name: "udf",
+};
+
+void
+ped_file_system_udf_init ()
+{
+ ped_file_system_type_register (&udf_type);
+}
+
+void
+ped_file_system_udf_done ()
+{
+ ped_file_system_type_unregister (&udf_type);
+}