diff --git a/Makefile b/Makefile
index 38a02431..e9c1ff5e 100644
--- a/Makefile
+++ b/Makefile
@@ -9,6 +9,7 @@ libc \
libm \
libpthread \
dispd \
+libmount \
bench \
carray \
editor \
diff --git a/libmount/.gitignore b/libmount/.gitignore
new file mode 100644
index 00000000..3bcc7790
--- /dev/null
+++ b/libmount/.gitignore
@@ -0,0 +1,2 @@
+libmount.a
+*.o
diff --git a/libmount/Makefile b/libmount/Makefile
new file mode 100644
index 00000000..43fca2ac
--- /dev/null
+++ b/libmount/Makefile
@@ -0,0 +1,46 @@
+SOFTWARE_MEANT_FOR_SORTIX=1
+include ../build-aux/platform.mak
+include ../build-aux/compiler.mak
+include ../build-aux/version.mak
+include ../build-aux/dirs.mak
+
+OPTLEVEL?=$(DEFAULT_OPTLEVEL)
+CFLAGS?=$(OPTLEVEL)
+
+CFLAGS:=$(CFLAGS) -Wall -Wextra
+CPPFLAGS:=$(CPPFLAGS) -Iinclude
+
+OBJS:=\
+biosboot.o \
+blockdevice.o \
+devices.o \
+crc32.o \
+ext2.o \
+extended.o \
+filesystem.o \
+gpt.o \
+harddisk.o \
+mbr.o \
+partition.o \
+uuid.o \
+
+all: libmount.a
+
+.PHONY: all clean install
+
+%.o: %.c
+ $(CC) $(CFLAGS) $(CPPFLAGS) -std=gnu11 -c $< -o $@
+
+libmount.a: $(OBJS)
+ $(AR) rcs $@ $(OBJS)
+
+install: libmount.a
+ mkdir -p $(DESTDIR)$(INCLUDEDIR)
+ cp -RTv include $(DESTDIR)$(INCLUDEDIR)
+ mkdir -p $(DESTDIR)$(LIBDIR)
+ cp libmount.a $(DESTDIR)$(LIBDIR)
+
+clean:
+ rm -f libmount.a
+ rm -f $(OBJS)
+ rm -f *.o
diff --git a/libmount/biosboot.c b/libmount/biosboot.c
new file mode 100644
index 00000000..0ee589ca
--- /dev/null
+++ b/libmount/biosboot.c
@@ -0,0 +1,87 @@
+/*******************************************************************************
+
+ Copyright(C) Jonas 'Sortie' Termansen 2015.
+
+ This file is part of Sortix libmount.
+
+ Sortix libmount is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by the
+ Free Software Foundation, either version 3 of the License, or (at your
+ option) any later version.
+
+ Sortix libmount 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 Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with Sortix libmount. If not, see .
+
+ biosboot.c
+ GPT bios boot partition.
+
+*******************************************************************************/
+
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+#include "util.h"
+
+static size_t biosboot_probe_amount(struct blockdevice* bdev)
+{
+ (void) bdev;
+ return 0;
+}
+
+static bool biosboot_probe(struct blockdevice* bdev,
+ const unsigned char* leading,
+ size_t amount)
+{
+ (void) leading;
+ (void) amount;
+ struct partition* p = bdev->p;
+ if ( !p )
+ return false;
+ if ( p->table_type != PARTITION_TABLE_TYPE_GPT )
+ return false;
+ unsigned char uuid[16];
+ uuid_from_string(uuid, BIOSBOOT_GPT_TYPE_UUID);
+ return memcmp(p->gpt_type_guid, uuid, 16) == 0;
+}
+
+static void biosboot_release(struct filesystem* fs)
+{
+ if ( !fs )
+ return;
+ free(fs);
+}
+
+static enum filesystem_error biosboot_inspect(struct filesystem** fs_ptr,
+ struct blockdevice* bdev)
+{
+ *fs_ptr = NULL;
+ struct filesystem* fs = CALLOC_TYPE(struct filesystem);
+ if ( !fs )
+ return FILESYSTEM_ERROR_ERRNO;
+ fs->bdev = bdev;
+ fs->handler = &biosboot_handler;
+ fs->handler_private = NULL;
+ fs->fstype_name = "biosboot";
+ return *fs_ptr = fs, FILESYSTEM_ERROR_NONE;
+}
+
+const struct filesystem_handler biosboot_handler =
+{
+ .handler_name = "biosboot",
+ .probe_amount = biosboot_probe_amount,
+ .probe = biosboot_probe,
+ .inspect = biosboot_inspect,
+ .release = biosboot_release,
+};
diff --git a/libmount/blockdevice.c b/libmount/blockdevice.c
new file mode 100644
index 00000000..2b63fd5f
--- /dev/null
+++ b/libmount/blockdevice.c
@@ -0,0 +1,84 @@
+/*******************************************************************************
+
+ Copyright(C) Jonas 'Sortie' Termansen 2015, 2016.
+
+ This file is part of Sortix libmount.
+
+ Sortix libmount is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by the
+ Free Software Foundation, either version 3 of the License, or (at your
+ option) any later version.
+
+ Sortix libmount 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 Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with Sortix libmount. If not, see .
+
+ blockdevice.c
+ Block device abstraction.
+
+*******************************************************************************/
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+blksize_t blockdevice_logical_block_size(const struct blockdevice* bdev)
+{
+ while ( bdev->p )
+ bdev = bdev->p->parent_bdev;
+ assert(bdev->hd);
+ return bdev->hd->logical_block_size;
+}
+
+bool blockdevice_check_reasonable_block_size(blksize_t size)
+{
+ if ( size < 512 ) // Violates assumptions.
+ return false;
+ for ( blksize_t cmp = 512; cmp <= 65536; cmp *= 2 )
+ if ( cmp == size )
+ return true;
+ return false;
+}
+
+off_t blockdevice_size(const struct blockdevice* bdev)
+{
+ if ( bdev->p )
+ return bdev->p->length;
+ assert(bdev->hd);
+ return bdev->hd->st.st_size;
+}
+
+size_t blockdevice_preadall(const struct blockdevice* bdev,
+ void* buffer,
+ size_t count,
+ off_t off)
+{
+ if ( off < 0 )
+ return errno = EINVAL, 0;
+ while ( bdev->p )
+ {
+ if ( bdev->p->length < off )
+ return 0;
+ off_t available = bdev->p->length - off;
+ if ( (uintmax_t) available < (uintmax_t) count )
+ count = (size_t) available;
+ if ( __builtin_add_overflow(bdev->p->start, off, &off) )
+ return 0;
+ bdev = bdev->p->parent_bdev;
+ }
+ assert(bdev->hd);
+ return preadall(bdev->hd->fd, buffer, count, off);
+}
diff --git a/libmount/crc32.c b/libmount/crc32.c
new file mode 100644
index 00000000..55925cf1
--- /dev/null
+++ b/libmount/crc32.c
@@ -0,0 +1,46 @@
+/* crc32.c -- compute the CRC-32 of a data stream
+ * Copyright (C) 1995-2006, 2010, 2011, 2012 Mark Adler
+ * Adapted by Jonas 'Sortie' Termansen for Sortix libmount in 2015.
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+#include
+#include
+
+#include
+
+uint32_t gpt_crc32(const void* buffer_ptr, size_t size)
+{
+ /* generate a crc for every 8-bit value */
+ uint32_t poly = 0xedb88320UL;
+ uint32_t crc_table[256];
+ for ( unsigned int n = 0; n < 256; n++ )
+ {
+ uint32_t c = n;
+ for ( unsigned int k = 0; k < 8; k++ )
+ c = c & 1 ? poly ^ (c >> 1) : c >> 1;
+ crc_table[n] = c;
+ }
+
+ /* calculate bytewise crc of input buffer */
+ const unsigned char* buffer = (const unsigned char*) buffer_ptr;
+ uint32_t crc = 0xffffffffUL;
+ for ( size_t i = 0; i < size; i++ )
+ crc = crc_table[(crc ^ buffer[i]) & 0xff] ^ (crc >> 8);
+ return crc ^ 0xffffffffUL;
+}
diff --git a/libmount/devices.c b/libmount/devices.c
new file mode 100644
index 00000000..2d41fc95
--- /dev/null
+++ b/libmount/devices.c
@@ -0,0 +1,170 @@
+/*******************************************************************************
+
+ Copyright(C) Jonas 'Sortie' Termansen 2015.
+
+ This file is part of Sortix libmount.
+
+ Sortix libmount is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by the
+ Free Software Foundation, either version 3 of the License, or (at your
+ option) any later version.
+
+ Sortix libmount 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 Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with Sortix libmount. If not, see .
+
+ devices.c
+ Locate block devices.
+
+*******************************************************************************/
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+static bool is_partition_name(const char* path)
+{
+ const char* name = path;
+ for ( size_t i = 0; path[i]; i++ )
+ if ( path[i] == '/' )
+ name = path + i + 1;
+ if ( !isalpha((unsigned char) *name) )
+ return false;
+ name++;
+ while ( isalpha((unsigned char) *name) )
+ name++;
+ if ( !isdigit((unsigned char) *name) )
+ return false;
+ name++;
+ while ( isdigit((unsigned char) *name) )
+ name++;
+ if ( *name != 'p' )
+ return false;
+ name++;
+ if ( !isdigit((unsigned char) *name) )
+ return false;
+ name++;
+ while ( isdigit((unsigned char) *name) )
+ name++;
+ return *name == '\0';
+}
+
+bool devices_iterate_path(bool (*callback)(void*, const char*), void* ctx)
+{
+ const char* dev_path = "/dev";
+ DIR* dir = opendir(dev_path);
+ if ( !dir )
+ return errno == ENOENT;
+ // TODO: This readdir loop can be derailed if the callback creates devices.
+ struct dirent* entry;
+ while ( (errno = 0, entry = readdir(dir)) )
+ {
+ if ( entry->d_name[0] == '.' )
+ continue;
+ if ( entry->d_type != DT_UNKNOWN && entry->d_type != DT_BLK )
+ continue;
+ // TODO: Remove this once partitions identify themselves as such and
+ // libmount doesn't allow using them as a real full block device.
+ if ( is_partition_name(entry->d_name) )
+ continue;
+ char* path;
+ if ( asprintf(&path, "%s/%s", dev_path, entry->d_name) < 0 )
+ return closedir(dir), false;
+ bool success = callback(ctx, path);
+ free(path);
+ if ( !success )
+ return closedir(dir), false;
+ }
+ bool success = errno == 0;
+ closedir(dir);
+ return success;
+}
+
+struct devices_iterate_open
+{
+ void* ctx;
+ bool (*callback)(void*, struct harddisk*);
+};
+
+static bool devices_iterate_open_callback(void* ctx_ptr, const char* path)
+{
+ struct devices_iterate_open* ctx = (struct devices_iterate_open*) ctx_ptr;
+ struct harddisk* hd = harddisk_openat(AT_FDCWD, path, O_RDWR);
+ if ( !hd )
+ {
+ int true_errno = errno;
+ struct stat st;
+ if ( lstat(path, 0) == 0 && !S_ISBLK(st.st_mode) )
+ return true;
+ errno = true_errno;
+ return false;
+ }
+ if ( !harddisk_inspect_blockdevice(hd) )
+ {
+ bool success = errno == ENOTBLK;
+ harddisk_close(hd);
+ return success;
+ }
+ return ctx->callback(ctx->ctx, hd);
+}
+
+bool devices_iterate_open(bool (*callback)(void*, struct harddisk*), void* ctx)
+{
+ struct devices_iterate_open myctx;
+ myctx.ctx = ctx;
+ myctx.callback = callback;
+ return devices_iterate_path(devices_iterate_open_callback, &myctx);
+}
+
+struct devices_open_all
+{
+ struct harddisk** hds;
+ size_t hds_count;
+ size_t hds_allocated;
+};
+
+static bool devices_open_all_callback(void* ctx_ptr, struct harddisk* hd)
+{
+ struct devices_open_all* ctx = (struct devices_open_all*) ctx_ptr;
+ if ( ctx->hds_count == ctx->hds_allocated )
+ {
+ size_t new_allocated = ctx->hds_allocated ? 2 * ctx->hds_allocated : 16;
+ struct harddisk** new_hds = (struct harddisk**)
+ reallocarray(ctx->hds, new_allocated, sizeof(struct harddisk*));
+ if ( !new_hds )
+ return harddisk_close(hd), false;
+ ctx->hds = new_hds;
+ ctx->hds_allocated = new_allocated;
+ }
+ ctx->hds[ctx->hds_count++] = hd;
+ return true;
+}
+
+bool devices_open_all(struct harddisk*** hds_ptr, size_t* hds_count_ptr)
+{
+ struct devices_open_all myctx;
+ myctx.hds = NULL;
+ myctx.hds_count = 0;
+ myctx.hds_allocated = 0;
+ if ( !devices_iterate_open(devices_open_all_callback, &myctx) )
+ {
+ for ( size_t i = 0; i < myctx.hds_count; i++ )
+ harddisk_close(myctx.hds[i]);
+ free(myctx.hds);
+ return false;
+ }
+ *hds_ptr = myctx.hds;
+ *hds_count_ptr = myctx.hds_count;
+ return true;
+}
diff --git a/libmount/ext2.c b/libmount/ext2.c
new file mode 100644
index 00000000..08b8372b
--- /dev/null
+++ b/libmount/ext2.c
@@ -0,0 +1,172 @@
+/*******************************************************************************
+
+ Copyright(C) Jonas 'Sortie' Termansen 2015.
+
+ This file is part of Sortix libmount.
+
+ Sortix libmount is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by the
+ Free Software Foundation, either version 3 of the License, or (at your
+ option) any later version.
+
+ Sortix libmount 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 Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with Sortix libmount. If not, see .
+
+ ext2.c
+ ext2 filesystem.
+
+*******************************************************************************/
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+#include "util.h"
+
+void ext2_superblock_decode(struct ext2_superblock* sb)
+{
+ sb->s_inodes_count = le32toh(sb->s_inodes_count);
+ sb->s_blocks_count = le32toh(sb->s_blocks_count);
+ sb->s_r_blocks_count = le32toh(sb->s_r_blocks_count);
+ sb->s_free_blocks_count = le32toh(sb->s_free_blocks_count);
+ sb->s_free_inodes_count = le32toh(sb->s_free_inodes_count);
+ sb->s_first_data_block = le32toh(sb->s_first_data_block);
+ sb->s_log_block_size = le32toh(sb->s_log_block_size);
+ sb->s_log_frag_size = (int32_t) le32toh((uint32_t) sb->s_log_frag_size);
+ sb->s_blocks_per_group = le32toh(sb->s_blocks_per_group);
+ sb->s_frags_per_group = le32toh(sb->s_frags_per_group);
+ sb->s_inodes_per_group = le32toh(sb->s_inodes_per_group);
+ sb->s_mtime = le32toh(sb->s_mtime);
+ sb->s_wtime = le32toh(sb->s_wtime);
+ sb->s_mnt_count = le16toh(sb->s_mnt_count);
+ sb->s_max_mnt_count = le16toh(sb->s_max_mnt_count);
+ sb->s_magic = le16toh(sb->s_magic);
+ sb->s_state = le16toh(sb->s_state);
+ sb->s_errors = le16toh(sb->s_errors);
+ sb->s_minor_rev_level = le16toh(sb->s_minor_rev_level);
+ sb->s_lastcheck = le32toh(sb->s_lastcheck);
+ sb->s_checkinterval = le32toh(sb->s_checkinterval);
+ sb->s_creator_os = le32toh(sb->s_creator_os);
+ sb->s_rev_level = le32toh(sb->s_rev_level);
+ sb->s_def_resuid = le16toh(sb->s_def_resuid);
+ sb->s_def_resgid = le16toh(sb->s_def_resgid);
+ sb->s_first_ino = le32toh(sb->s_first_ino);
+ sb->s_inode_size = le16toh(sb->s_inode_size);
+ sb->s_block_group_nr = le16toh(sb->s_block_group_nr);
+ sb->s_feature_compat = le32toh(sb->s_feature_compat);
+ sb->s_feature_incompat = le32toh(sb->s_feature_incompat);
+ sb->s_feature_ro_compat = le32toh(sb->s_feature_ro_compat);
+ // s_uuid is endian agnostic.
+ // s_volume_name is endian agnostic.
+ // s_last_mounted is endian agnostic.
+ sb->s_algo_bitmap = le32toh(sb->s_algo_bitmap);
+ // s_prealloc_blocks is endian agnostic.
+ // s_prealloc_dir_blocks is endian agnostic.
+ sb->alignment0 = le16toh(sb->alignment0);
+ // s_journal_uuid is endian agnostic.
+ sb->s_journal_inum = le32toh(sb->s_journal_inum);
+ sb->s_journal_dev = le32toh(sb->s_journal_dev);
+ sb->s_last_orphan = le32toh(sb->s_last_orphan);
+ for ( size_t i = 0; i < 4; i++ )
+ sb->s_hash_seed[i] = le32toh(sb->s_hash_seed[i]);
+ // s_def_hash_version is endian agnostic.
+ // alignment1 is endian agnostic.
+ sb->s_default_mount_options = le32toh(sb->s_default_mount_options);
+ sb->s_first_meta_bg = le32toh(sb->s_first_meta_bg);
+}
+
+static size_t ext2_probe_amount(struct blockdevice* bdev)
+{
+ (void) bdev;
+ return 1024 + sizeof(struct ext2_superblock);
+}
+
+static bool ext2_probe(struct blockdevice* bdev,
+ const unsigned char* leading,
+ size_t amount)
+{
+ (void) bdev;
+ if ( amount < 1024 )
+ return false;
+ leading += 1024;
+ amount -= 1024;
+ if ( amount < sizeof(struct ext2_superblock) )
+ return false;
+ struct ext2_superblock sb;
+ memcpy(&sb, leading, sizeof(sb));
+ ext2_superblock_decode(&sb);
+ if ( sb.s_magic != EXT2_SUPER_MAGIC )
+ return false;
+ return true;
+}
+
+struct ext2_private
+{
+ struct ext2_superblock sb;
+};
+
+static void ext2_release(struct filesystem* fs)
+{
+ if ( !fs )
+ return;
+ struct ext2_private* priv = (struct ext2_private*) fs->handler_private;
+ free(priv);
+ free(fs);
+}
+
+static enum filesystem_error ext2_inspect(struct filesystem** fs_ptr,
+ struct blockdevice* bdev)
+{
+ *fs_ptr = NULL;
+ struct filesystem* fs = CALLOC_TYPE(struct filesystem);
+ if ( !fs )
+ return FILESYSTEM_ERROR_ERRNO;
+ struct ext2_private* priv = CALLOC_TYPE(struct ext2_private);
+ if ( !priv )
+ return free(fs), FILESYSTEM_ERROR_ERRNO;
+ fs->bdev = bdev;
+ fs->handler = &ext2_handler;
+ fs->handler_private = priv;
+ struct ext2_superblock* sb = &priv->sb;
+ if ( blockdevice_preadall(bdev, sb, sizeof(*sb), 1024) != sizeof(*sb) )
+ return ext2_release(fs), FILESYSTEM_ERROR_ERRNO;
+ ext2_superblock_decode(sb);
+ fs->fstype_name = "ext2";
+ fs->fsck = "fsck.ext2";
+ fs->driver = "extfs";
+ fs->flags |= FILESYSTEM_FLAG_UUID;
+ memcpy(&fs->uuid, sb->s_uuid, 16);
+ struct timespec now;
+ clock_gettime(CLOCK_REALTIME, &now);
+ time_t next_check = (time_t)
+ ((uintmax_t) sb->s_lastcheck + (uintmax_t) sb->s_checkinterval);
+ if ( sb->s_state != EXT2_VALID_FS )
+ fs->flags |= FILESYSTEM_FLAG_FSCK_SHOULD |
+ FILESYSTEM_FLAG_FSCK_MUST;
+ else if ( sb->s_max_mnt_count && sb->s_max_mnt_count <= sb->s_mnt_count )
+ fs->flags |= FILESYSTEM_FLAG_FSCK_SHOULD;
+ else if ( sb->s_checkinterval && next_check <= now.tv_sec )
+ fs->flags |= FILESYSTEM_FLAG_FSCK_SHOULD;
+ return *fs_ptr = fs, FILESYSTEM_ERROR_NONE;
+}
+
+const struct filesystem_handler ext2_handler =
+{
+ .handler_name = "ext2",
+ .probe_amount = ext2_probe_amount,
+ .probe = ext2_probe,
+ .inspect = ext2_inspect,
+ .release = ext2_release,
+};
diff --git a/libmount/extended.c b/libmount/extended.c
new file mode 100644
index 00000000..a2796180
--- /dev/null
+++ b/libmount/extended.c
@@ -0,0 +1,85 @@
+/*******************************************************************************
+
+ Copyright(C) Jonas 'Sortie' Termansen 2015.
+
+ This file is part of Sortix libmount.
+
+ Sortix libmount is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by the
+ Free Software Foundation, either version 3 of the License, or (at your
+ option) any later version.
+
+ Sortix libmount 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 Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with Sortix libmount. If not, see .
+
+ extended.c
+ MBR extended partition.
+
+*******************************************************************************/
+
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+#include "util.h"
+
+static size_t extended_probe_amount(struct blockdevice* bdev)
+{
+ (void) bdev;
+ return 0;
+}
+
+static bool extended_probe(struct blockdevice* bdev,
+ const unsigned char* leading,
+ size_t amount)
+{
+ (void) leading;
+ (void) amount;
+ struct partition* p = bdev->p;
+ if ( !p )
+ return false;
+ return p->table_type == PARTITION_TABLE_TYPE_MBR &&
+ (p->mbr_system_id == 0x05 ||
+ p->mbr_system_id == 0x0F);
+}
+
+static void extended_release(struct filesystem* fs)
+{
+ if ( !fs )
+ return;
+ free(fs);
+}
+
+static enum filesystem_error extended_inspect(struct filesystem** fs_ptr,
+ struct blockdevice* bdev)
+{
+ *fs_ptr = NULL;
+ struct filesystem* fs = CALLOC_TYPE(struct filesystem);
+ if ( !fs )
+ return FILESYSTEM_ERROR_ERRNO;
+ fs->bdev = bdev;
+ fs->handler = &extended_handler;
+ fs->handler_private = NULL;
+ fs->fstype_name = "extended";
+ return *fs_ptr = fs, FILESYSTEM_ERROR_NONE;
+}
+
+const struct filesystem_handler extended_handler =
+{
+ .handler_name = "extended",
+ .probe_amount = extended_probe_amount,
+ .probe = extended_probe,
+ .inspect = extended_inspect,
+ .release = extended_release,
+};
diff --git a/libmount/filesystem.c b/libmount/filesystem.c
new file mode 100644
index 00000000..342a81fa
--- /dev/null
+++ b/libmount/filesystem.c
@@ -0,0 +1,102 @@
+/*******************************************************************************
+
+ Copyright(C) Jonas 'Sortie' Termansen 2015.
+
+ This file is part of Sortix libmount.
+
+ Sortix libmount is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by the
+ Free Software Foundation, either version 3 of the License, or (at your
+ option) any later version.
+
+ Sortix libmount 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 Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with Sortix libmount. If not, see .
+
+ filesystem.c
+ Filesystem abstraction.
+
+*******************************************************************************/
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+const char* filesystem_error_string(enum filesystem_error error)
+{
+ switch ( error )
+ {
+ case FILESYSTEM_ERROR_NONE:
+ break;
+ case FILESYSTEM_ERROR_ABSENT:
+ return "No filesystem found";
+ case FILESYSTEM_ERROR_UNRECOGNIZED:
+ return "Unrecognized filesystem type";
+ case FILESYSTEM_ERROR_ERRNO:
+ return (const char*) strerror(errno);
+ }
+ return "Unknown error condition";
+}
+
+static const struct filesystem_handler* filesystem_handlers[] =
+{
+ &biosboot_handler,
+ &extended_handler,
+ &ext2_handler,
+ NULL,
+};
+
+void filesystem_release(struct filesystem* fs)
+{
+ if ( !fs || !fs->handler )
+ return;
+ fs->handler->release(fs);
+}
+
+enum filesystem_error
+blockdevice_inspect_filesystem(struct filesystem** fs_ptr,
+ struct blockdevice* bdev)
+{
+ *fs_ptr = NULL;
+ size_t leading_size = 65536;
+ for ( size_t i = 0; filesystem_handlers[i]; i++ )
+ {
+ size_t amount = filesystem_handlers[i]->probe_amount(bdev);
+ if ( leading_size < amount )
+ leading_size = amount;
+ }
+ unsigned char* leading = (unsigned char*) malloc(leading_size);
+ if ( !leading )
+ return *fs_ptr = NULL, FILESYSTEM_ERROR_ERRNO;
+ size_t amount = blockdevice_preadall(bdev, leading, leading_size, 0);
+ if ( amount < leading_size && errno != EEOF )
+ return free(leading), *fs_ptr = NULL, FILESYSTEM_ERROR_ERRNO;
+ for ( size_t i = 0; filesystem_handlers[i]; i++ )
+ {
+ if ( !filesystem_handlers[i]->probe(bdev, leading, amount) )
+ continue;
+ free(leading);
+ return filesystem_handlers[i]->inspect(fs_ptr, bdev);
+ }
+ for ( size_t i = 0; i < amount; i++ )
+ if ( !leading[i] )
+ return free(leading), FILESYSTEM_ERROR_UNRECOGNIZED;
+ return free(leading), FILESYSTEM_ERROR_ABSENT;
+}
diff --git a/libmount/gpt.c b/libmount/gpt.c
new file mode 100644
index 00000000..e0d8d2d8
--- /dev/null
+++ b/libmount/gpt.c
@@ -0,0 +1,325 @@
+/*******************************************************************************
+
+ Copyright(C) Jonas 'Sortie' Termansen 2015.
+
+ This file is part of Sortix libmount.
+
+ Sortix libmount is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by the
+ Free Software Foundation, either version 3 of the License, or (at your
+ option) any later version.
+
+ Sortix libmount 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 Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with Sortix libmount. If not, see .
+
+ gpt.c
+ GUID Partition Table.
+
+*******************************************************************************/
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+#include "util.h"
+
+void gpt_decode(struct gpt* v)
+{
+ // signature is endian agnostic.
+ v->revision = le32toh(v->revision);
+ v->header_size = le32toh(v->header_size);
+ v->header_crc32 = le32toh(v->header_crc32);
+ v->reserved0 = le32toh(v->reserved0);
+ v->my_lba = le64toh(v->my_lba);
+ v->alternate_lba = le64toh(v->alternate_lba);
+ v->first_usable_lba = le64toh(v->first_usable_lba);
+ v->last_usable_lba = le64toh(v->last_usable_lba);
+ // disk_guid is endian agnostic.
+ v->partition_entry_lba = le64toh(v->partition_entry_lba);
+ v->number_of_partition_entries = le32toh(v->number_of_partition_entries);
+ v->size_of_partition_entry = le32toh(v->size_of_partition_entry);
+ v->partition_entry_array_crc32 = le32toh(v->partition_entry_array_crc32);
+ // reserved bytes are endian agnostic.
+}
+
+void gpt_encode(struct gpt* v)
+{
+ // signature is endian agnostic.
+ v->revision = htole32(v->revision);
+ v->header_size = htole32(v->header_size);
+ v->header_crc32 = htole32(v->header_crc32);
+ v->reserved0 = htole32(v->reserved0);
+ v->my_lba = htole64(v->my_lba);
+ v->alternate_lba = htole64(v->alternate_lba);
+ v->first_usable_lba = htole64(v->first_usable_lba);
+ v->last_usable_lba = htole64(v->last_usable_lba);
+ // disk_guid is endian agnostic.
+ v->partition_entry_lba = htole64(v->partition_entry_lba);
+ v->number_of_partition_entries = htole32(v->number_of_partition_entries);
+ v->size_of_partition_entry = htole32(v->size_of_partition_entry);
+ v->partition_entry_array_crc32 = htole32(v->partition_entry_array_crc32);
+ // reserved bytes are endian agnostic.
+}
+
+void gpt_partition_decode(struct gpt_partition* v)
+{
+ // partition_type_guid is endian agnostic.
+ // unique_partition_guid is endian agnostic.
+ v->starting_lba = le64toh(v->starting_lba);
+ v->ending_lba = le64toh(v->ending_lba);
+ v->attributes = le64toh(v->attributes);
+ for ( size_t i = 0; i < GPT_PARTITION_NAME_LENGTH; i++ )
+ v->partition_name[i] = le16toh(v->partition_name[i]);
+}
+
+void gpt_partition_encode(struct gpt_partition* v)
+{
+ // partition_type_guid is endian agnostic.
+ // unique_partition_guid is endian agnostic.
+ v->starting_lba = htole64(v->starting_lba);
+ v->ending_lba = htole64(v->ending_lba);
+ v->attributes = htole64(v->attributes);
+ for ( size_t i = 0; i < GPT_PARTITION_NAME_LENGTH; i++ )
+ v->partition_name[i] = htole16(v->partition_name[i]);
+}
+
+char* gpt_decode_utf16(uint16_t* string, size_t length)
+{
+ mbstate_t ps;
+ memset(&ps, 0, sizeof(ps));
+
+ char* result = NULL;
+ size_t result_used = 0;
+ size_t result_length = 0;
+
+ for ( size_t i = 0; true; i++ )
+ {
+ wchar_t wc;
+ if ( length <= i )
+ wc = L'\0';
+ else if ( 0xDC00 <= string[i] && string[i] <= 0xDFFF )
+ return free(result), errno = EILSEQ, (char*) NULL;
+ else if ( 0xD800 <= string[i] && string[i] <= 0xDBFF )
+ {
+ uint16_t high = string[i] - 0xD800;
+ if ( i + 1 == length )
+ return free(result), errno = EILSEQ, (char*) NULL;
+ if ( !(0xDC00 <= string[i+1] && string[i] <= 0xDFFF) )
+ return free(result), errno = EILSEQ, (char*) NULL;
+ uint16_t low = string[++i] - 0xDC00;
+ wc = (((wchar_t) high << 10) | ((wchar_t) low << 0)) + 0x10000;
+ }
+ else
+ wc = (wchar_t) string[i++];
+ char mb[MB_CUR_MAX];
+ size_t amount = wcrtomb(mb, wc, &ps);
+ if ( amount == (size_t) -1 )
+ return free(result), (char*) NULL;
+ for ( size_t n = 0; n < amount; n++ )
+ {
+ if ( result_used == result_length )
+ {
+ // TODO: Potential overflow.
+ size_t new_length = result_length ? 2 * result_length : length;
+ char* new_result = (char*) realloc(result, new_length);
+ if ( !new_result )
+ return free(result), (char*) NULL;
+ result = new_result;
+ result_length = new_length;
+ }
+ result[result_used++] = mb[n];
+ }
+ if ( wc == L'\0' )
+ {
+ char* new_result = (char*) realloc(result, result_used);
+ if ( new_result )
+ result = new_result;
+ return result;
+ }
+ }
+}
+
+void gpt_partition_table_release(struct gpt_partition_table* pt)
+{
+ if ( !pt )
+ return;
+ free(pt->rpt);
+ free(pt);
+}
+
+static bool gpt_check_power_of_two(uintmax_t value,
+ uintmax_t low,
+ uintmax_t high)
+{
+ for ( uintmax_t cmp = low; cmp <= high; cmp *= 2 )
+ if ( value == cmp )
+ return true;
+ return false;
+}
+
+enum partition_error
+blockdevice_get_partition_table_gpt(struct partition_table** pt_ptr,
+ struct blockdevice* bdev)
+{
+ *pt_ptr = NULL;
+ blksize_t logical_block_size = blockdevice_logical_block_size(bdev);
+ if ( !blockdevice_check_reasonable_block_size(logical_block_size) )
+ return errno = EINVAL, PARTITION_ERROR_ERRNO;
+ off_t device_size = blockdevice_size(bdev);
+ const char* device_path = bdev->p ? bdev->p->path : bdev->hd->path;
+
+ unsigned char* gpt_block = (unsigned char*) malloc(logical_block_size);
+ if ( !gpt_block )
+ return PARTITION_ERROR_ERRNO;
+ if ( blockdevice_preadall(bdev, gpt_block, logical_block_size,
+ 1 * logical_block_size) != (size_t) logical_block_size )
+ return free(gpt_block), PARTITION_ERROR_ERRNO;
+
+ struct gpt gpt;
+ memcpy(&gpt, gpt_block, sizeof(gpt));
+ gpt_decode(&gpt);
+
+ if ( memcmp(gpt.signature, "EFI PART", 8) != 0 )
+ return PARTITION_ERROR_INVALID;
+ static_assert( 92 == sizeof(gpt) - sizeof(gpt.reserved1),
+ "92 == sizeof(gpt) - sizeof(gpt.reserved1)");
+ if ( gpt.header_size < 92 )
+ return free(gpt_block), PARTITION_ERROR_INVALID;
+
+ struct partition_table* pt = CALLOC_TYPE(struct partition_table);
+ if ( !pt )
+ return free(gpt_block), PARTITION_ERROR_ERRNO;
+ *pt_ptr = pt;
+ pt->type = PARTITION_TABLE_TYPE_GPT;
+ size_t pt__partitions_length = 0;
+
+ if ( logical_block_size < gpt.header_size )
+ return free(gpt_block), PARTITION_ERROR_HEADER_TOO_LARGE;
+
+ struct gpt_partition_table* gptpt = CALLOC_TYPE(struct gpt_partition_table);
+ if ( !gptpt )
+ return free(gpt_block), PARTITION_ERROR_ERRNO;
+ pt->raw_partition_table = gptpt;
+ memcpy(&gptpt->gpt, gpt_block, sizeof(gptpt->gpt));
+
+ struct gpt* check_gpt = (struct gpt*) gpt_block;
+ check_gpt->header_crc32 = 0;
+ uint32_t checksum = gpt_crc32(gpt_block, gpt.header_size);
+ free(gpt_block);
+ if ( checksum != gpt.header_crc32 )
+ return PARTITION_ERROR_CHECKSUM;
+
+ memcpy(pt->gpt_disk_guid, gpt.disk_guid, 16);
+
+ if ( !gpt_check_power_of_two(gpt.size_of_partition_entry,
+ sizeof(struct gpt_partition), 65536) )
+ return PARTITION_ERROR_INVALID;
+ if ( gpt.my_lba != 1 )
+ return PARTITION_ERROR_INVALID;
+
+ if ( gpt.my_lba >= gpt.first_usable_lba )
+ return PARTITION_ERROR_INVALID;
+ // TODO: Ensure nothing collides with anything else.
+ if ( gpt.partition_entry_lba <= 1 )
+ return PARTITION_ERROR_INVALID;
+ if ( gpt.last_usable_lba < gpt.first_usable_lba )
+ return PARTITION_ERROR_INVALID;
+
+ // TODO: Potential overflow.
+ pt->usable_start = (off_t) gpt.first_usable_lba * (off_t) logical_block_size;
+ pt->usable_end = ((off_t) gpt.last_usable_lba + 1) * (off_t) logical_block_size;
+ if ( device_size < pt->usable_end )
+ return PARTITION_ERROR_INVALID;
+
+ // TODO: Potential overflow.
+ size_t rpt_size = (size_t) gpt.size_of_partition_entry *
+ (size_t) gpt.number_of_partition_entries;
+ off_t rpt_off = (off_t) gpt.partition_entry_lba * (off_t) logical_block_size;
+ off_t rpt_end = rpt_off + rpt_off;
+ if ( pt->usable_start < rpt_end )
+ return PARTITION_ERROR_INVALID;
+ if ( !(gptpt->rpt = (unsigned char*) malloc(rpt_size)) )
+ return PARTITION_ERROR_ERRNO;
+ unsigned char* rpt = gptpt->rpt;
+ if ( blockdevice_preadall(bdev, rpt, rpt_size, rpt_off) != rpt_size )
+ return PARTITION_ERROR_ERRNO;
+ if ( gpt.partition_entry_array_crc32 != gpt_crc32(rpt, rpt_size) )
+ return PARTITION_ERROR_CHECKSUM;
+
+ for ( uint32_t i = 0; i < gpt.number_of_partition_entries; i++ )
+ {
+ size_t pentry_off = (size_t) i * (size_t) gpt.size_of_partition_entry;
+ struct gpt_partition pentry;
+ memcpy(&pentry, rpt + pentry_off, sizeof(pentry));
+ gpt_partition_decode(&pentry);
+ if ( memiszero(pentry.partition_type_guid, 16) )
+ continue;
+ if ( pentry.ending_lba < pentry.starting_lba )
+ return PARTITION_ERROR_END_BEFORE_START;
+ // TODO: Potential overflow.
+ uint64_t lba_count = (pentry.ending_lba - pentry.starting_lba) + 1;
+ off_t start = (off_t) pentry.starting_lba * (off_t) logical_block_size;
+ if ( start < pt->usable_start )
+ return PARTITION_ERROR_BEFORE_USABLE;
+ off_t length = (off_t) lba_count * (off_t) logical_block_size;
+ if ( pt->usable_end < start || pt->usable_end - start < length )
+ return PARTITION_ERROR_BEYOND_USABLE;
+ struct partition* p = CALLOC_TYPE(struct partition);
+ if ( !p )
+ return PARTITION_ERROR_ERRNO;
+ memset(&p->bdev, 0, sizeof(p->bdev));
+ p->bdev.p = p;
+ p->parent_bdev = bdev;
+ p->index = 1 + i;
+ p->start = start;
+ p->length = length;
+ p->type = PARTITION_TYPE_PRIMARY;
+ p->table_type = PARTITION_TABLE_TYPE_GPT;
+ memcpy(p->gpt_type_guid, pentry.partition_type_guid, 16);
+ memcpy(p->gpt_unique_guid, pentry.unique_partition_guid, 16);
+ p->gpt_attributes = pentry.attributes;
+ if ( !array_add((void***) &pt->partitions,
+ &pt->partitions_count,
+ &pt__partitions_length,
+ p) )
+ return free(p), PARTITION_ERROR_ERRNO;
+ if ( !(p->gpt_name = gpt_decode_utf16(pentry.partition_name,
+ GPT_PARTITION_NAME_LENGTH)) )
+ return PARTITION_ERROR_ERRNO;
+ if ( device_path &&
+ asprintf(&p->path, "%sp%u", device_path, p->index) < 0 )
+ return PARTITION_ERROR_ERRNO;
+ }
+
+ qsort(pt->partitions, pt->partitions_count, sizeof(pt->partitions[0]),
+ partition_compare_start_indirect);
+ for ( size_t i = 1; i < pt->partitions_count; i++ )
+ {
+ struct partition* a = pt->partitions[i-1];
+ struct partition* b = pt->partitions[i];
+ if ( partition_check_overlap(a, b->start, b->length) )
+ return PARTITION_ERROR_OVERLAP;
+ }
+ qsort(pt->partitions, pt->partitions_count, sizeof(pt->partitions[0]),
+ partition_compare_index_indirect);
+
+ return PARTITION_ERROR_NONE;
+}
diff --git a/libmount/harddisk.c b/libmount/harddisk.c
new file mode 100644
index 00000000..711ea7a8
--- /dev/null
+++ b/libmount/harddisk.c
@@ -0,0 +1,123 @@
+/*******************************************************************************
+
+ Copyright(C) Jonas 'Sortie' Termansen 2015.
+
+ This file is part of Sortix libmount.
+
+ Sortix libmount is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by the
+ Free Software Foundation, either version 3 of the License, or (at your
+ option) any later version.
+
+ Sortix libmount 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 Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with Sortix libmount. If not, see .
+
+ harddisk.c
+ Harddisk abstraction.
+
+*******************************************************************************/
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+#include "util.h"
+
+static char* atcgetblob(int fd, const char* name, size_t* size_ptr)
+{
+ ssize_t size = tcgetblob(fd, name, NULL, 0);
+ if ( size < 0 )
+ return NULL;
+ char* result = (char*) malloc((size_t) size + 1);
+ if ( !result )
+ return NULL;
+ ssize_t second_size = tcgetblob(fd, name, result, (size_t) size);
+ if ( second_size != size )
+ return free(result), (char*) NULL;
+ result[(size_t) size] = '\0';
+ if ( size_ptr )
+ *size_ptr = (size_t) size;
+ return result;
+}
+
+struct harddisk* harddisk_openat(int dirfd, const char* relpath, int rdwr)
+{
+ struct harddisk* hd = CALLOC_TYPE(struct harddisk);
+ if ( !hd )
+ return (struct harddisk*) NULL;
+ memset(&hd->bdev, 0, sizeof(hd->bdev));
+ hd->bdev.hd = hd;
+ if ( (hd->fd = openat(dirfd, relpath, rdwr)) < 0 )
+ return harddisk_close(hd), (struct harddisk*) NULL;
+ if ( !(hd->path = canonicalize_file_name_at(dirfd, relpath)) )
+ return harddisk_close(hd), (struct harddisk*) NULL;
+ if ( fstat(hd->fd, &hd->st) < 0 )
+ return harddisk_close(hd), (struct harddisk*) NULL;
+ return hd;
+}
+
+bool harddisk_inspect_blockdevice(struct harddisk* hd)
+{
+ if ( !S_ISBLK(hd->st.st_mode) )
+ return errno = ENOTBLK, false;
+ if ( tcgetblob(hd->fd, "harddisk-driver", NULL, 0) < 0 )
+ {
+ if ( errno == ENOENT )
+ errno = ENOTBLK;
+ return false;
+ }
+ if ( !(hd->driver = atcgetblob(hd->fd, "harddisk-driver", NULL)) ||
+ !(hd->model = atcgetblob(hd->fd, "harddisk-model", NULL)) ||
+ !(hd->serial = atcgetblob(hd->fd, "harddisk-serial", NULL)) )
+ return harddisk_close(hd), false;
+ char* str;
+ if ( !(str = atcgetblob(hd->fd, "harddisk-block-size", NULL)) )
+ return harddisk_close(hd), false;
+ hd->logical_block_size = (blksize_t) strtoumax(str, NULL, 10);
+ free(str);
+ if ( !(str = atcgetblob(hd->fd, "harddisk-cylinders", NULL)) )
+ return harddisk_close(hd), false;
+ hd->cylinders = (uint16_t) strtoul(str, NULL, 10);
+ free(str);
+ if ( !(str = atcgetblob(hd->fd, "harddisk-heads", NULL)) )
+ return harddisk_close(hd), false;
+ hd->heads = (uint16_t) strtoul(str, NULL, 10);
+ free(str);
+ if ( !(str = atcgetblob(hd->fd, "harddisk-sectors", NULL)) )
+ return harddisk_close(hd), false;
+ hd->sectors = (uint16_t) strtoul(str, NULL, 10);
+ free(str);
+ // TODO: To avoid potential overflow bugs (assuming malicious filesystem),
+ // reject ridiculous block sizes.
+ if ( 65536 < hd->logical_block_size )
+ return harddisk_close(hd), errno = EINVAL, false;
+ if ( hd->logical_block_size < 512 )
+ return harddisk_close(hd), errno = EINVAL, false;
+ return true;
+}
+
+void harddisk_close(struct harddisk* hd)
+{
+ if ( 0 <= hd->fd )
+ close(hd->fd);
+ free(hd->path);
+ free(hd->driver);
+ free(hd->model);
+ free(hd->serial);
+ free(hd);
+}
diff --git a/libmount/include/mount/biosboot.h b/libmount/include/mount/biosboot.h
new file mode 100644
index 00000000..ed6ec9bb
--- /dev/null
+++ b/libmount/include/mount/biosboot.h
@@ -0,0 +1,42 @@
+/*******************************************************************************
+
+ Copyright(C) Jonas 'Sortie' Termansen 2015.
+
+ This file is part of Sortix libmount.
+
+ Sortix libmount is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by the
+ Free Software Foundation, either version 3 of the License, or (at your
+ option) any later version.
+
+ Sortix libmount 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 Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with Sortix libmount. If not, see .
+
+ mount/biosboot.h
+ GPT bios boot partition.
+
+*******************************************************************************/
+
+#ifndef INCLUDE_MOUNT_BIOSBOOT_H
+#define INCLUDE_MOUNT_BIOSBOOT_H
+
+#include
+
+#define BIOSBOOT_GPT_TYPE_UUID "48616821-4964-6f6e-744e-656564454649"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+extern const struct filesystem_handler biosboot_handler;
+
+#if defined(__cplusplus)
+} /* extern "C" */
+#endif
+
+#endif
diff --git a/libmount/include/mount/blockdevice.h b/libmount/include/mount/blockdevice.h
new file mode 100644
index 00000000..f271400c
--- /dev/null
+++ b/libmount/include/mount/blockdevice.h
@@ -0,0 +1,66 @@
+/*******************************************************************************
+
+ Copyright(C) Jonas 'Sortie' Termansen 2015.
+
+ This file is part of Sortix libmount.
+
+ Sortix libmount is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by the
+ Free Software Foundation, either version 3 of the License, or (at your
+ option) any later version.
+
+ Sortix libmount 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 Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with Sortix libmount. If not, see .
+
+ mount/blockdevice.h
+ Block device abstraction.
+
+*******************************************************************************/
+
+#ifndef INCLUDE_MOUNT_BLOCKDEVICE_H
+#define INCLUDE_MOUNT_BLOCKDEVICE_H
+
+#include
+
+#if !defined(__cplusplus)
+#include
+#endif
+#include
+
+#include
+
+struct filesystem;
+struct harddisk;
+struct partition;
+struct partition_table;
+
+struct blockdevice
+{
+ struct harddisk* hd;
+ struct partition* p;
+ struct partition_table* pt;
+ struct filesystem* fs;
+ enum partition_error pt_error;
+ enum filesystem_error fs_error;
+ void* user_ctx;
+};
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+blksize_t blockdevice_logical_block_size(const struct blockdevice*);
+bool blockdevice_check_reasonable_block_size(blksize_t);
+off_t blockdevice_size(const struct blockdevice*);
+size_t blockdevice_preadall(const struct blockdevice*, void*, size_t, off_t);
+
+#if defined(__cplusplus)
+} /* extern "C" */
+#endif
+
+#endif
diff --git a/libmount/include/mount/devices.h b/libmount/include/mount/devices.h
new file mode 100644
index 00000000..dfaf918e
--- /dev/null
+++ b/libmount/include/mount/devices.h
@@ -0,0 +1,46 @@
+/*******************************************************************************
+
+ Copyright(C) Jonas 'Sortie' Termansen 2015.
+
+ This file is part of Sortix libmount.
+
+ Sortix libmount is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by the
+ Free Software Foundation, either version 3 of the License, or (at your
+ option) any later version.
+
+ Sortix libmount 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 Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with Sortix libmount. If not, see .
+
+ mount/devices.h
+ Locate block devices.
+
+*******************************************************************************/
+
+#ifndef INCLUDE_MOUNT_DEVICES_H
+#define INCLUDE_MOUNT_DEVICES_H
+
+#if !defined(__cplusplus)
+#include
+#endif
+
+struct harddisk;
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+bool devices_iterate_path(bool (*)(void*, const char*), void*);
+bool devices_iterate_open(bool (*)(void*, struct harddisk*), void*);
+bool devices_open_all(struct harddisk***, size_t*);
+
+#if defined(__cplusplus)
+} /* extern "C" */
+#endif
+
+#endif
diff --git a/libmount/include/mount/error.h b/libmount/include/mount/error.h
new file mode 100644
index 00000000..adc5418e
--- /dev/null
+++ b/libmount/include/mount/error.h
@@ -0,0 +1,56 @@
+/*******************************************************************************
+
+ Copyright(C) Jonas 'Sortie' Termansen 2015.
+
+ This file is part of Sortix libmount.
+
+ Sortix libmount is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by the
+ Free Software Foundation, either version 3 of the License, or (at your
+ option) any later version.
+
+ Sortix libmount 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 Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with Sortix libmount. If not, see .
+
+ mount/error.h
+ Error enumerations.
+
+*******************************************************************************/
+
+#ifndef INCLUDE_MOUNT_ERROR_H
+#define INCLUDE_MOUNT_ERROR_H
+
+enum filesystem_error
+{
+ FILESYSTEM_ERROR_NONE,
+ FILESYSTEM_ERROR_ABSENT,
+ FILESYSTEM_ERROR_UNRECOGNIZED,
+ FILESYSTEM_ERROR_ERRNO,
+};
+
+enum partition_error
+{
+ PARTITION_ERROR_NONE,
+ PARTITION_ERROR_ABSENT,
+ PARTITION_ERROR_UNRECOGNIZED,
+ PARTITION_ERROR_ERRNO,
+ PARTITION_ERROR_INVALID,
+ PARTITION_ERROR_HEADER_TOO_LARGE,
+ PARTITION_ERROR_CHECKSUM,
+ PARTITION_ERROR_OVERLAP,
+ PARTITION_ERROR_END_BEFORE_START,
+ PARTITION_ERROR_BEFORE_USABLE,
+ PARTITION_ERROR_BEYOND_DEVICE,
+ PARTITION_ERROR_BEYOND_EXTENDED,
+ PARTITION_ERROR_BEYOND_USABLE,
+ PARTITION_ERROR_TOO_EXTENDED,
+ PARTITION_ERROR_BAD_EXTENDED,
+ PARTITION_ERROR_NONLINEAR_EXTENDED,
+};
+
+#endif
diff --git a/libmount/include/mount/ext2.h b/libmount/include/mount/ext2.h
new file mode 100644
index 00000000..0f68fdb9
--- /dev/null
+++ b/libmount/include/mount/ext2.h
@@ -0,0 +1,105 @@
+/*******************************************************************************
+
+ Copyright(C) Jonas 'Sortie' Termansen 2015.
+
+ This file is part of Sortix libmount.
+
+ Sortix libmount is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by the
+ Free Software Foundation, either version 3 of the License, or (at your
+ option) any later version.
+
+ Sortix libmount 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 Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with Sortix libmount. If not, see .
+
+ mount/ext2.h
+ ext2 filesystem.
+
+*******************************************************************************/
+
+#ifndef INCLUDE_MOUNT_EXT2_H
+#define INCLUDE_MOUNT_EXT2_H
+
+#include
+
+#include
+
+static const uint16_t EXT2_SUPER_MAGIC = 0xEF53;
+static const uint16_t EXT2_VALID_FS = 1;
+static const uint16_t EXT2_ERROR_FS = 2;
+
+struct ext2_superblock
+{
+ uint32_t s_inodes_count;
+ uint32_t s_blocks_count;
+ uint32_t s_r_blocks_count;
+ uint32_t s_free_blocks_count;
+ uint32_t s_free_inodes_count;
+ uint32_t s_first_data_block;
+ uint32_t s_log_block_size;
+ int32_t s_log_frag_size;
+ uint32_t s_blocks_per_group;
+ uint32_t s_frags_per_group;
+ uint32_t s_inodes_per_group;
+ uint32_t s_mtime;
+ uint32_t s_wtime;
+ uint16_t s_mnt_count;
+ uint16_t s_max_mnt_count;
+ uint16_t s_magic;
+ uint16_t s_state;
+ uint16_t s_errors;
+ uint16_t s_minor_rev_level;
+ uint32_t s_lastcheck;
+ uint32_t s_checkinterval;
+ uint32_t s_creator_os;
+ uint32_t s_rev_level;
+ uint16_t s_def_resuid;
+ uint16_t s_def_resgid;
+// EXT2_DYNAMIC_REV
+ uint32_t s_first_ino;
+ uint16_t s_inode_size;
+ uint16_t s_block_group_nr;
+ uint32_t s_feature_compat;
+ uint32_t s_feature_incompat;
+ uint32_t s_feature_ro_compat;
+ uint8_t s_uuid[16];
+ /*uint8_t*/ char s_volume_name[16];
+ /*uint8_t*/ char s_last_mounted[64];
+ uint32_t s_algo_bitmap;
+// Performance Hints
+ uint8_t s_prealloc_blocks;
+ uint8_t s_prealloc_dir_blocks;
+ uint16_t alignment0;
+// Journaling Support
+ uint8_t s_journal_uuid[16];
+ uint32_t s_journal_inum;
+ uint32_t s_journal_dev;
+ uint32_t s_last_orphan;
+// Directory Indexing Support
+ uint32_t s_hash_seed[4];
+ uint8_t s_def_hash_version;
+ uint8_t alignment1[3];
+// Other options
+ uint32_t s_default_mount_options;
+ uint32_t s_first_meta_bg;
+ uint8_t alignment2[760];
+};
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+extern const struct filesystem_handler ext2_handler;
+
+void ext2_superblock_decode(struct ext2_superblock*);
+
+#if defined(__cplusplus)
+} /* extern "C" */
+#endif
+
+#endif
diff --git a/libmount/include/mount/extended.h b/libmount/include/mount/extended.h
new file mode 100644
index 00000000..d8ee189f
--- /dev/null
+++ b/libmount/include/mount/extended.h
@@ -0,0 +1,40 @@
+/*******************************************************************************
+
+ Copyright(C) Jonas 'Sortie' Termansen 2015.
+
+ This file is part of Sortix libmount.
+
+ Sortix libmount is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by the
+ Free Software Foundation, either version 3 of the License, or (at your
+ option) any later version.
+
+ Sortix libmount 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 Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with Sortix libmount. If not, see .
+
+ mount/extended.h
+ MBR extended partition.
+
+*******************************************************************************/
+
+#ifndef INCLUDE_MOUNT_EXTENDED_H
+#define INCLUDE_MOUNT_EXTENDED_H
+
+#include
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+extern const struct filesystem_handler extended_handler;
+
+#if defined(__cplusplus)
+} /* extern "C" */
+#endif
+
+#endif
diff --git a/libmount/include/mount/filesystem.h b/libmount/include/mount/filesystem.h
new file mode 100644
index 00000000..9fbac1de
--- /dev/null
+++ b/libmount/include/mount/filesystem.h
@@ -0,0 +1,79 @@
+/*******************************************************************************
+
+ Copyright(C) Jonas 'Sortie' Termansen 2015.
+
+ This file is part of Sortix libmount.
+
+ Sortix libmount is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by the
+ Free Software Foundation, either version 3 of the License, or (at your
+ option) any later version.
+
+ Sortix libmount 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 Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with Sortix libmount. If not, see .
+
+ mount/filesystem.h
+ Filesystem abstraction.
+
+*******************************************************************************/
+
+#ifndef INCLUDE_MOUNT_FILESYSTEM_H
+#define INCLUDE_MOUNT_FILESYSTEM_H
+
+#if !defined(__cplusplus)
+#include
+#endif
+#include
+#include
+
+#include
+
+struct blockdevice;
+struct filesystem;
+struct filesystem_handler;
+
+#define FILESYSTEM_FLAG_UUID (1 << 0)
+#define FILESYSTEM_FLAG_FSCK_SHOULD (1 << 1)
+#define FILESYSTEM_FLAG_FSCK_MUST (1 << 2)
+
+struct filesystem
+{
+ struct blockdevice* bdev;
+ const struct filesystem_handler* handler;
+ void* handler_private;
+ const char* fstype_name;
+ const char* fsck;
+ const char* driver;
+ int flags;
+ unsigned char uuid[16];
+ void* user_ctx;
+};
+
+struct filesystem_handler
+{
+ const char* handler_name;
+ size_t (*probe_amount)(struct blockdevice*);
+ bool (*probe)(struct blockdevice*, const unsigned char*, size_t);
+ enum filesystem_error (*inspect)(struct filesystem**, struct blockdevice*);
+ void (*release)(struct filesystem*);
+};
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+const char* filesystem_error_string(enum filesystem_error);
+enum filesystem_error
+blockdevice_inspect_filesystem(struct filesystem**, struct blockdevice*);
+void filesystem_release(struct filesystem*);
+
+#if defined(__cplusplus)
+} /* extern "C" */
+#endif
+
+#endif
diff --git a/libmount/include/mount/gpt.h b/libmount/include/mount/gpt.h
new file mode 100644
index 00000000..f2ff79d5
--- /dev/null
+++ b/libmount/include/mount/gpt.h
@@ -0,0 +1,93 @@
+/*******************************************************************************
+
+ Copyright(C) Jonas 'Sortie' Termansen 2015.
+
+ This file is part of Sortix libmount.
+
+ Sortix libmount is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by the
+ Free Software Foundation, either version 3 of the License, or (at your
+ option) any later version.
+
+ Sortix libmount 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 Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with Sortix libmount. If not, see .
+
+ mount/gpt.h
+ GUID Partition Table.
+
+*******************************************************************************/
+
+#ifndef INCLUDE_MOUNT_GPT_H
+#define INCLUDE_MOUNT_GPT_H
+
+#include
+
+#include
+#include
+#include
+
+struct blockdevice;
+struct partition_table;
+
+struct gpt
+{
+ char signature[8];
+ uint32_t revision;
+ uint32_t header_size;
+ uint32_t header_crc32;
+ uint32_t reserved0;
+ uint64_t my_lba;
+ uint64_t alternate_lba;
+ uint64_t first_usable_lba;
+ uint64_t last_usable_lba;
+ unsigned char disk_guid[16];
+ uint64_t partition_entry_lba;
+ uint32_t number_of_partition_entries;
+ uint32_t size_of_partition_entry;
+ uint32_t partition_entry_array_crc32;
+ unsigned char reserved1[420];
+};
+
+static_assert(sizeof(struct gpt) == 512, "sizeof(struct gpt) == 512");
+
+#define GPT_PARTITION_NAME_LENGTH 36
+struct gpt_partition
+{
+ unsigned char partition_type_guid[16];
+ unsigned char unique_partition_guid[16];
+ uint64_t starting_lba;
+ uint64_t ending_lba;
+ uint64_t attributes;
+ uint16_t partition_name[GPT_PARTITION_NAME_LENGTH];
+};
+
+static_assert(sizeof(struct gpt_partition) == 128, "sizeof(struct gpt_partition) == 128");
+
+struct gpt_partition_table
+{
+ struct gpt gpt;
+ unsigned char* rpt;
+};
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+void gpt_decode(struct gpt*);
+void gpt_encode(struct gpt*);
+void gpt_partition_decode(struct gpt_partition*);
+void gpt_partition_encode(struct gpt_partition*);
+char* gpt_decode_utf16(uint16_t* string, size_t length);
+uint32_t gpt_crc32(const void*, size_t);
+void gpt_partition_table_release(struct gpt_partition_table*);
+
+#if defined(__cplusplus)
+} /* extern "C" */
+#endif
+
+#endif
diff --git a/libmount/include/mount/harddisk.h b/libmount/include/mount/harddisk.h
new file mode 100644
index 00000000..f2cf10b0
--- /dev/null
+++ b/libmount/include/mount/harddisk.h
@@ -0,0 +1,65 @@
+/*******************************************************************************
+
+ Copyright(C) Jonas 'Sortie' Termansen 2015.
+
+ This file is part of Sortix libmount.
+
+ Sortix libmount is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by the
+ Free Software Foundation, either version 3 of the License, or (at your
+ option) any later version.
+
+ Sortix libmount 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 Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with Sortix libmount. If not, see .
+
+ mount/harddisk.h
+ Harddisk abstraction.
+
+*******************************************************************************/
+
+#ifndef INCLUDE_MOUNT_HARDDISK_H
+#define INCLUDE_MOUNT_HARDDISK_H
+
+#include
+#include
+
+#if !defined(__cplusplus)
+#include
+#endif
+#include
+
+#include
+
+struct harddisk
+{
+ struct blockdevice bdev;
+ struct stat st;
+ int fd;
+ char* path;
+ char* driver;
+ char* model;
+ char* serial;
+ blksize_t logical_block_size;
+ uint16_t cylinders;
+ uint16_t heads;
+ uint16_t sectors;
+};
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+struct harddisk* harddisk_openat(int, const char*, int);
+bool harddisk_inspect_blockdevice(struct harddisk*);
+void harddisk_close(struct harddisk*);
+
+#if defined(__cplusplus)
+} /* extern "C" */
+#endif
+
+#endif
diff --git a/libmount/include/mount/mbr.h b/libmount/include/mount/mbr.h
new file mode 100644
index 00000000..e13735b9
--- /dev/null
+++ b/libmount/include/mount/mbr.h
@@ -0,0 +1,86 @@
+/*******************************************************************************
+
+ Copyright(C) Jonas 'Sortie' Termansen 2015.
+
+ This file is part of Sortix libmount.
+
+ Sortix libmount is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by the
+ Free Software Foundation, either version 3 of the License, or (at your
+ option) any later version.
+
+ Sortix libmount 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 Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with Sortix libmount. If not, see .
+
+ mount/mbr.h
+ Master Boot Record.
+
+*******************************************************************************/
+
+#ifndef INCLUDE_MOUNT_MBR_H
+#define INCLUDE_MOUNT_MBR_H
+
+#include
+
+#include
+#include
+#include
+
+struct blockdevice;
+struct partition_table;
+
+struct mbr_partition
+{
+ uint8_t flags;
+ uint8_t start_head;
+ uint16_t start_sector_cylinder;
+ uint8_t system_id;
+ uint8_t end_head;
+ uint16_t end_sector_cylinder;
+ uint32_t start_sector;
+ uint32_t total_sectors;
+};
+
+static_assert(sizeof(struct mbr_partition) == 16, "sizeof(struct mbr_partition) == 16");
+
+struct mbr
+{
+ uint8_t bootstrap[446];
+ unsigned char partitions[4][16];
+ uint8_t signature[2];
+};
+
+static_assert(sizeof(struct mbr) == 512, "sizeof(struct mbr) == 512");
+
+struct mbr_ebr_link
+{
+ struct mbr ebr;
+ off_t offset;
+};
+
+struct mbr_partition_table
+{
+ struct mbr mbr;
+ size_t ebr_chain_count;
+ struct mbr_ebr_link* ebr_chain;
+};
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+bool mbr_is_partition_used(const struct mbr_partition* partition);
+void mbr_partition_decode(struct mbr_partition*);
+void mbr_partition_encode(struct mbr_partition*);
+void mbr_partition_table_release(struct mbr_partition_table*);
+
+#if defined(__cplusplus)
+} /* extern "C" */
+#endif
+
+#endif
diff --git a/libmount/include/mount/partition.h b/libmount/include/mount/partition.h
new file mode 100644
index 00000000..c5e5ffbc
--- /dev/null
+++ b/libmount/include/mount/partition.h
@@ -0,0 +1,109 @@
+/*******************************************************************************
+
+ Copyright(C) Jonas 'Sortie' Termansen 2015.
+
+ This file is part of Sortix libmount.
+
+ Sortix libmount is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by the
+ Free Software Foundation, either version 3 of the License, or (at your
+ option) any later version.
+
+ Sortix libmount 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 Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with Sortix libmount. If not, see .
+
+ mount/partition.h
+ Partition abstraction.
+
+*******************************************************************************/
+
+#ifndef INCLUDE_MOUNT_PARTITION_H
+#define INCLUDE_MOUNT_PARTITION_H
+
+#include
+
+#if !defined(__cplusplus)
+#include
+#endif
+#include
+#include
+
+#include
+#include
+
+enum partition_type
+{
+ PARTITION_TYPE_PRIMARY,
+ PARTITION_TYPE_EXTENDED,
+ PARTITION_TYPE_LOGICAL,
+};
+
+enum partition_table_type
+{
+ PARTITION_TABLE_TYPE_NONE,
+ PARTITION_TABLE_TYPE_UNKNOWN,
+ PARTITION_TABLE_TYPE_MBR,
+ PARTITION_TABLE_TYPE_GPT,
+};
+
+struct partition
+{
+ struct blockdevice bdev;
+ struct blockdevice* parent_bdev;
+ char* path;
+ off_t start;
+ off_t length;
+ off_t extended_start;
+ off_t extended_length;
+ enum partition_type type;
+ enum partition_table_type table_type;
+ unsigned int index;
+ unsigned char gpt_type_guid[16];
+ unsigned char gpt_unique_guid[16];
+ unsigned char mbr_system_id;
+ char* gpt_name;
+ uint64_t gpt_attributes;
+};
+
+struct partition_table
+{
+ enum partition_table_type type;
+ struct partition** partitions;
+ size_t partitions_count;
+ enum partition_error error;
+ unsigned char gpt_disk_guid[16];
+ void* raw_partition_table;
+ off_t usable_start;
+ off_t usable_end;
+};
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+int partition_compare_index(const struct partition*, const struct partition*);
+int partition_compare_index_indirect(const void*, const void*);
+int partition_compare_start(const struct partition*, const struct partition*);
+int partition_compare_start_indirect(const void*, const void*);
+const char* partition_error_string(enum partition_error);
+bool partition_check_overlap(const struct partition*, off_t, off_t);
+void partition_release(struct partition*);
+bool blockdevice_probe_partition_table_type(enum partition_table_type*, struct blockdevice*);
+enum partition_error
+blockdevice_get_partition_table(struct partition_table**, struct blockdevice*);
+enum partition_error
+blockdevice_get_partition_table_mbr(struct partition_table**, struct blockdevice*);
+enum partition_error
+blockdevice_get_partition_table_gpt(struct partition_table**, struct blockdevice*);
+void partition_table_release(struct partition_table*);
+
+#if defined(__cplusplus)
+} /* extern "C" */
+#endif
+
+#endif
diff --git a/libmount/include/mount/uuid.h b/libmount/include/mount/uuid.h
new file mode 100644
index 00000000..cb3c3f24
--- /dev/null
+++ b/libmount/include/mount/uuid.h
@@ -0,0 +1,46 @@
+/*******************************************************************************
+
+ Copyright(C) Jonas 'Sortie' Termansen 2015.
+
+ This file is part of Sortix libmount.
+
+ Sortix libmount is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by the
+ Free Software Foundation, either version 3 of the License, or (at your
+ option) any later version.
+
+ Sortix libmount 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 Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with Sortix libmount. If not, see .
+
+ mount/uuid.h
+ Universally unique identifier.
+
+*******************************************************************************/
+
+#ifndef INCLUDE_MOUNT_UUID_H
+#define INCLUDE_MOUNT_UUID_H
+
+#if !defined(__cplusplus)
+#include
+#endif
+
+#define UUID_STRING_LENGTH 36 /* and of course a nul byte not included here */
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+bool uuid_validate(const char*);
+void uuid_from_string(unsigned char[16], const char*);
+void uuid_to_string(const unsigned char[16], char*);
+
+#if defined(__cplusplus)
+} /* extern "C" */
+#endif
+
+#endif
diff --git a/libmount/mbr.c b/libmount/mbr.c
new file mode 100644
index 00000000..e356dd1d
--- /dev/null
+++ b/libmount/mbr.c
@@ -0,0 +1,270 @@
+/*******************************************************************************
+
+ Copyright(C) Jonas 'Sortie' Termansen 2015.
+
+ This file is part of Sortix libmount.
+
+ Sortix libmount is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by the
+ Free Software Foundation, either version 3 of the License, or (at your
+ option) any later version.
+
+ Sortix libmount 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 Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with Sortix libmount. If not, see .
+
+ mbr.c
+ Master Boot Record.
+
+*******************************************************************************/
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+#include "util.h"
+
+void mbr_partition_decode(struct mbr_partition* v)
+{
+ // flags is endian agnostic.
+ // start_head is endian agnostic.
+ v->start_sector_cylinder = le16toh(v->start_sector_cylinder);
+ v->end_sector_cylinder = le16toh(v->end_sector_cylinder);
+ // system_id is endian agnostic.
+ // end_head is endian agnostic.
+ v->start_sector = le32toh(v->start_sector);
+ v->total_sectors = le32toh(v->total_sectors);
+}
+
+void mbr_partition_encode(struct mbr_partition* v)
+{
+ v->start_sector_cylinder = htole16(v->start_sector_cylinder);
+ v->end_sector_cylinder = htole16(v->end_sector_cylinder);
+ // flags is endian agnostic.
+ // start_head is endian agnostic.
+ v->start_sector = htole32(v->start_sector);
+ v->total_sectors = htole32(v->total_sectors);
+ // system_id is endian agnostic.
+ // end_head is endian agnostic.
+}
+
+void mbr_partition_table_release(struct mbr_partition_table* pt)
+{
+ if ( !pt )
+ return;
+ free(pt->ebr_chain);
+ free(pt);
+}
+
+static bool mbr_is_extended_partition(const struct mbr_partition* partition)
+{
+ return partition->system_id == 0x05 || // CHS addressing.
+ partition->system_id == 0x0F; // LBA addressing.
+}
+
+bool mbr_is_partition_used(const struct mbr_partition* partition)
+{
+ if ( memiszero(partition, sizeof(*partition)) )
+ return false;
+ if ( !partition->system_id ||
+ !partition->total_sectors )
+ return false;
+ return true;
+}
+
+enum partition_error
+blockdevice_get_partition_table_mbr(struct partition_table** pt_ptr,
+ struct blockdevice* bdev)
+{
+ *pt_ptr = NULL;
+ blksize_t logical_block_size = blockdevice_logical_block_size(bdev);
+ if ( !blockdevice_check_reasonable_block_size(logical_block_size) )
+ return errno = EINVAL, PARTITION_ERROR_ERRNO;
+ off_t device_size = blockdevice_size(bdev);
+ const char* device_path = bdev->p ? bdev->p->path : bdev->hd->path;
+
+ struct mbr mbr;
+ if ( blockdevice_preadall(bdev, &mbr, sizeof(mbr), 0) != sizeof(mbr) )
+ return PARTITION_ERROR_ERRNO;
+
+ if ( mbr.signature[0] != 0x55 && mbr.signature[1] != 0xAA )
+ return errno = EINVAL, PARTITION_ERROR_ERRNO;
+
+ struct partition_table* pt = CALLOC_TYPE(struct partition_table);
+ if ( !pt )
+ return PARTITION_ERROR_ERRNO;
+ *pt_ptr = pt;
+ pt->type = PARTITION_TABLE_TYPE_MBR;
+ size_t pt__partitions_length = 0;
+ pt->usable_start = logical_block_size;
+ off_t last_sector = device_size / logical_block_size;
+ if ( UINT32_MAX + 1LL < last_sector )
+ last_sector = UINT32_MAX + 1LL;
+ pt->usable_end = last_sector * logical_block_size;
+
+ struct mbr_partition_table* mbrpt = CALLOC_TYPE(struct mbr_partition_table);
+ if ( !mbrpt )
+ return PARTITION_ERROR_ERRNO;
+ pt->raw_partition_table = mbrpt;
+ memcpy(&mbrpt->mbr, &mbr, sizeof(mbr));
+
+ unsigned int extended_partition_count = 0;
+ for ( unsigned int i = 0; i < 4; i++ )
+ {
+ struct mbr_partition pmbr;
+ memcpy(&pmbr, mbr.partitions[i], sizeof(pmbr));
+ mbr_partition_decode(&pmbr);
+ if ( mbr_is_partition_used(&pmbr) && mbr_is_extended_partition(&pmbr) )
+ extended_partition_count++;
+ }
+
+ if ( 2 <= extended_partition_count ) // Violates assumptions.
+ return PARTITION_ERROR_TOO_EXTENDED;
+
+ for ( unsigned int i = 0; i < 4; i++ )
+ {
+ struct mbr_partition pmbr;
+ memcpy(&pmbr, mbr.partitions[i], sizeof(pmbr));
+ mbr_partition_decode(&pmbr);
+ if ( !mbr_is_partition_used(&pmbr) )
+ continue;
+ // TODO: Potential overflow.
+ if ( pmbr.start_sector == 0 )
+ return PARTITION_ERROR_BEFORE_USABLE;
+ off_t start = (off_t) pmbr.start_sector * (off_t) logical_block_size;
+ off_t length = (off_t) pmbr.total_sectors * (off_t) logical_block_size;
+ if ( device_size < start || device_size - start < length )
+ return PARTITION_ERROR_BEYOND_DEVICE;
+ for ( size_t j = 0; j < pt->partitions_count; j++ )
+ if ( partition_check_overlap(pt->partitions[j], start, length) )
+ return PARTITION_ERROR_OVERLAP;
+ struct partition* p = CALLOC_TYPE(struct partition);
+ if ( !p )
+ return PARTITION_ERROR_ERRNO;
+ memset(&p->bdev, 0, sizeof(p->bdev));
+ p->bdev.p = p;
+ p->parent_bdev = bdev;
+ p->index = 1 + i;
+ p->start = start;
+ p->length = length;
+ p->type = PARTITION_TYPE_PRIMARY;
+ if ( mbr_is_extended_partition(&pmbr) )
+ {
+ p->extended_start = start;
+ p->extended_length = length;
+ p->type = PARTITION_TYPE_EXTENDED;
+ }
+ p->table_type = PARTITION_TABLE_TYPE_MBR;
+ p->mbr_system_id = pmbr.system_id;
+ if ( !array_add((void***) &pt->partitions,
+ &pt->partitions_count,
+ &pt__partitions_length,
+ p) )
+ return free(p), PARTITION_ERROR_ERRNO;
+ if ( device_path &&
+ asprintf(&p->path, "%sp%u", device_path, p->index) < 0 )
+ return PARTITION_ERROR_ERRNO;
+ }
+
+ for ( unsigned int i = 0; i < 4; i++ )
+ {
+ struct mbr_partition pextmbr;
+ memcpy(&pextmbr, mbr.partitions[i], sizeof(pextmbr));
+ mbr_partition_decode(&pextmbr);
+ if ( !mbr_is_partition_used(&pextmbr) )
+ continue;
+ if ( !mbr_is_extended_partition(&pextmbr) )
+ continue;
+ // TODO: Potential overflow.
+ off_t pext_start = (off_t) pextmbr.start_sector * (off_t) logical_block_size;
+ off_t pext_length = (off_t) pextmbr.total_sectors * (off_t) logical_block_size;
+ off_t ebr_rel = 0;
+ unsigned int j = 0;
+ while ( true )
+ {
+ struct mbr ebr;
+ if ( pext_length <= ebr_rel )
+ return PARTITION_ERROR_BEYOND_EXTENDED;
+ off_t ebr_off = pext_start + ebr_rel;
+ if ( blockdevice_preadall(bdev, &ebr, sizeof(ebr), ebr_off) != sizeof(ebr) )
+ return PARTITION_ERROR_ERRNO;
+ if ( ebr.signature[0] != 0x55 && ebr.signature[1] != 0xAA )
+ return PARTITION_ERROR_BAD_EXTENDED;
+ size_t new_chain_count = mbrpt->ebr_chain_count + 1;
+ size_t chain_size = sizeof(struct mbr_ebr_link) * new_chain_count;
+ struct mbr_ebr_link* new_chain =
+ (struct mbr_ebr_link*) realloc(mbrpt->ebr_chain, chain_size);
+ if ( !new_chain )
+ return PARTITION_ERROR_ERRNO;
+ mbrpt->ebr_chain = new_chain;
+ mbrpt->ebr_chain_count = new_chain_count;
+ memcpy(&mbrpt->ebr_chain[j].ebr, &ebr, sizeof(ebr));
+ mbrpt->ebr_chain[j].offset = ebr_off;
+ struct mbr_partition pmbr;
+ memcpy(&pmbr, ebr.partitions[0], sizeof(pmbr));
+ mbr_partition_decode(&pmbr);
+ if ( mbr_is_partition_used(&pmbr) )
+ {
+ // TODO: Potential overflow.
+ off_t start = (off_t) pmbr.start_sector * (off_t) logical_block_size;
+ off_t length = (off_t) pmbr.total_sectors * (off_t) logical_block_size;
+ if ( pext_length - ebr_rel < start )
+ return PARTITION_ERROR_BEYOND_EXTENDED;
+ off_t max_length = (pext_length - ebr_rel) - start;
+ if ( max_length < length )
+ return PARTITION_ERROR_BEYOND_EXTENDED;
+ struct partition* p = CALLOC_TYPE(struct partition);
+ if ( !p )
+ return PARTITION_ERROR_ERRNO;
+ memset(&p->bdev, 0, sizeof(p->bdev));
+ p->bdev.p = p;
+ p->parent_bdev = bdev;
+ p->index = 5 + j;
+ p->start = ebr_off + start;
+ p->length = length;
+ p->type = PARTITION_TYPE_LOGICAL;
+ p->table_type = PARTITION_TABLE_TYPE_MBR;
+ p->mbr_system_id = pmbr.system_id;
+ if ( !array_add((void***) &pt->partitions,
+ &pt->partitions_count,
+ &pt__partitions_length,
+ p) )
+ return free(p), PARTITION_ERROR_ERRNO;
+ if ( device_path &&
+ asprintf(&p->path, "%sp%u", device_path, p->index) < 0 )
+ return PARTITION_ERROR_ERRNO;
+ }
+ j++;
+ struct mbr_partition next;
+ memcpy(&next, ebr.partitions[1], sizeof(next));
+ mbr_partition_decode(&next);
+ // TODO: Potential overflow.
+ off_t next_rel = (off_t) next.start_sector * (off_t) logical_block_size;
+ if ( !next_rel )
+ break;
+ if ( next_rel <= ebr_rel ) // Violates assumptions.
+ return PARTITION_ERROR_NONLINEAR_EXTENDED;
+ if ( pext_length <= next_rel )
+ return PARTITION_ERROR_BEYOND_EXTENDED;
+ ebr_rel = next_rel;
+ }
+ break;
+ }
+
+ return PARTITION_ERROR_NONE;
+}
diff --git a/libmount/partition.c b/libmount/partition.c
new file mode 100644
index 00000000..e8b1c12b
--- /dev/null
+++ b/libmount/partition.c
@@ -0,0 +1,238 @@
+/*******************************************************************************
+
+ Copyright(C) Jonas 'Sortie' Termansen 2015.
+
+ This file is part of Sortix libmount.
+
+ Sortix libmount is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by the
+ Free Software Foundation, either version 3 of the License, or (at your
+ option) any later version.
+
+ Sortix libmount 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 Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with Sortix libmount. If not, see .
+
+ partition.c
+ Partition abstraction.
+
+*******************************************************************************/
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+#include "util.h"
+
+int partition_compare_index(const struct partition* a, const struct partition* b)
+{
+ if ( a->index < b->index )
+ return -1;
+ if ( b->index < a->index )
+ return 1;
+ return 0;
+}
+
+int partition_compare_index_indirect(const void* a_ptr, const void* b_ptr)
+{
+ const struct partition* a = *(const struct partition* const*) a_ptr;
+ const struct partition* b = *(const struct partition* const*) b_ptr;
+ return partition_compare_index(a, b);
+}
+
+int partition_compare_start(const struct partition* a, const struct partition* b)
+{
+ if ( a->start < b->start )
+ return -1;
+ if ( b->start < a->start )
+ return 1;
+ return 0;
+}
+
+int partition_compare_start_indirect(const void* a_ptr, const void* b_ptr)
+{
+ const struct partition* a = *(const struct partition* const*) a_ptr;
+ const struct partition* b = *(const struct partition* const*) b_ptr;
+ return partition_compare_index(a, b);
+}
+
+const char* partition_error_string(enum partition_error error)
+{
+ switch ( error )
+ {
+ case PARTITION_ERROR_NONE:
+ break;
+ case PARTITION_ERROR_ABSENT:
+ return "No partition table found";
+ case PARTITION_ERROR_UNRECOGNIZED:
+ return "Unrecognized partitioning scheme";
+ case PARTITION_ERROR_ERRNO:
+ return (const char*) strerror(errno);
+ case PARTITION_ERROR_INVALID:
+ return "Invalid partition table";
+ case PARTITION_ERROR_HEADER_TOO_LARGE:
+ return "Partition table header larger than logical sector size";
+ case PARTITION_ERROR_CHECKSUM:
+ return "Partition table does not match its checksum";
+ case PARTITION_ERROR_OVERLAP:
+ return "Partition table contains overlapping partitions";
+ case PARTITION_ERROR_END_BEFORE_START:
+ return "Invalid partition ends before its start";
+ case PARTITION_ERROR_BEFORE_USABLE:
+ return "Invalid partition begins before the usable region";
+ case PARTITION_ERROR_BEYOND_DEVICE:
+ return "Invalid partition exceeds device";
+ case PARTITION_ERROR_BEYOND_EXTENDED:
+ return "Invalid logical partition exceeds its extended partition";
+ case PARTITION_ERROR_BEYOND_USABLE:
+ return "Invalid partition ends after the usable region";
+ case PARTITION_ERROR_TOO_EXTENDED:
+ return "Bad partition table (more than one extended partition)";
+ case PARTITION_ERROR_BAD_EXTENDED:
+ return "Bad extended partition";
+ case PARTITION_ERROR_NONLINEAR_EXTENDED:
+ return "Extended partition is not linearly linked together";
+ }
+ return "Unknown error condition";
+}
+
+bool partition_check_overlap(const struct partition* partition,
+ off_t start,
+ off_t length)
+{
+ assert(0 <= start);
+ assert(0 <= length);
+ assert(0 <= partition->start);
+ assert(0 <= partition->length);
+ if ( start <= partition->start )
+ {
+ off_t max_length = partition->start - start;
+ return max_length < length;
+ }
+ else
+ {
+ off_t max_length = start - partition->start;
+ return max_length < partition->length;
+ }
+}
+
+bool blockdevice_probe_partition_table_type(enum partition_table_type* result_out,
+ struct blockdevice* bdev)
+{
+ blksize_t logical_block_size = blockdevice_logical_block_size(bdev);
+ if ( !blockdevice_check_reasonable_block_size(logical_block_size) )
+ return errno = EINVAL, false;
+
+ // TODO: Overflow checks for truncation, and the multiplication.
+ size_t block_size = logical_block_size;
+ size_t leading_size = 2 * block_size;
+ unsigned char* leading = (unsigned char*) malloc(leading_size);
+ if ( !leading )
+ return false;
+
+ size_t amount = blockdevice_preadall(bdev, leading, leading_size, 0);
+ if ( amount < leading_size && errno != EEOF )
+ return free(leading), false;
+
+ enum partition_table_type result = PARTITION_TABLE_TYPE_NONE;
+
+ do if ( 2*block_size <= amount )
+ {
+ struct gpt gpt;
+ memcpy(&gpt, leading + 1 * block_size, sizeof(gpt));
+ gpt_decode(&gpt);
+ if ( memcmp(gpt.signature, "EFI PART", 8) != 0 )
+ break;
+ result = PARTITION_TABLE_TYPE_GPT;
+ goto out;
+ } while ( 0 );
+
+ do if ( sizeof(struct mbr) <= amount )
+ {
+ struct mbr mbr;
+ memcpy(&mbr, leading, sizeof(mbr));
+ if ( mbr.signature[0] != 0x55 && mbr.signature[1] != 0xAA )
+ break;
+ result = PARTITION_TABLE_TYPE_MBR;
+ goto out;
+ } while ( 0 );
+
+ for ( size_t i = 0; i < leading_size; i++ )
+ {
+ if ( leading[i] == 0x00 )
+ continue;
+ result = PARTITION_TABLE_TYPE_UNKNOWN;
+ goto out;
+ }
+
+out:
+ free(leading);
+ return *result_out = result, true;
+}
+
+enum partition_error
+blockdevice_get_partition_table(struct partition_table** pt_ptr,
+ struct blockdevice* bdev)
+{
+ enum partition_table_type pt_type;
+ if ( !blockdevice_probe_partition_table_type(&pt_type, bdev) )
+ return *pt_ptr = NULL, PARTITION_ERROR_ERRNO;
+ switch ( pt_type )
+ {
+ case PARTITION_TABLE_TYPE_NONE:
+ return *pt_ptr = NULL, PARTITION_ERROR_ABSENT;
+ case PARTITION_TABLE_TYPE_UNKNOWN:
+ break;
+ case PARTITION_TABLE_TYPE_MBR:
+ return blockdevice_get_partition_table_mbr(pt_ptr, bdev);
+ case PARTITION_TABLE_TYPE_GPT:
+ return blockdevice_get_partition_table_gpt(pt_ptr, bdev);
+ }
+ return *pt_ptr = NULL, PARTITION_ERROR_UNRECOGNIZED;
+}
+
+void partition_release(struct partition* p)
+{
+ if ( !p )
+ return;
+ free(p->gpt_name);
+ free(p);
+}
+
+void partition_table_release(struct partition_table* pt)
+{
+ if ( !pt )
+ return;
+ switch ( pt->type )
+ {
+ case PARTITION_TABLE_TYPE_NONE: break;
+ case PARTITION_TABLE_TYPE_UNKNOWN: break;
+ case PARTITION_TABLE_TYPE_MBR:
+ mbr_partition_table_release(
+ (struct mbr_partition_table*) pt->raw_partition_table);
+ break;
+ case PARTITION_TABLE_TYPE_GPT:
+ gpt_partition_table_release(
+ (struct gpt_partition_table*) pt->raw_partition_table);
+ break;
+ }
+ for ( size_t i = 0; i < pt->partitions_count; i++ )
+ partition_release(pt->partitions[i]);
+ free(pt->partitions);
+ free(pt);
+}
diff --git a/libmount/util.h b/libmount/util.h
new file mode 100644
index 00000000..8093c4d2
--- /dev/null
+++ b/libmount/util.h
@@ -0,0 +1,74 @@
+/*******************************************************************************
+
+ Copyright(C) Jonas 'Sortie' Termansen 2015.
+
+ This file is part of Sortix libmount.
+
+ Sortix libmount is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by the
+ Free Software Foundation, either version 3 of the License, or (at your
+ option) any later version.
+
+ Sortix libmount 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 Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with Sortix libmount. If not, see .
+
+ util.h
+ Utility functions.
+
+*******************************************************************************/
+
+#ifndef UTIL_H
+#define UTIL_H
+
+#include
+#include
+
+#define MALLOC_TYPE(type) (type*) malloc(sizeof(type))
+#define CALLOC_TYPE(type) (type*) calloc(1, sizeof(type))
+
+__attribute__((unused))
+static bool memiszero(const void* mem, size_t size)
+{
+ const unsigned char* buf = (const unsigned char*) mem;
+ for ( size_t i = 0; i < size; i++ )
+ if ( buf[i] )
+ return false;
+ return true;
+}
+
+__attribute__((unused))
+static bool array_add(void*** array_ptr,
+ size_t* used_ptr,
+ size_t* length_ptr,
+ void* value)
+{
+ void** array;
+ memcpy(&array, array_ptr, sizeof(array)); // Strict aliasing.
+
+ if ( *used_ptr == *length_ptr )
+ {
+ // TODO: Avoid overflow.
+ size_t new_length = 2 * *length_ptr;
+ if ( !new_length )
+ new_length = 16;
+ // TODO: Avoid overflow and use reallocarray.
+ size_t new_size = new_length * sizeof(void*);
+ void** new_array = (void**) realloc(array, new_size);
+ if ( !new_array )
+ return false;
+ array = new_array;
+ memcpy(array_ptr, &array, sizeof(array)); // Strict aliasing.
+ *length_ptr = new_length;
+ }
+
+ memcpy(array + (*used_ptr)++, &value, sizeof(value)); // Strict aliasing.
+
+ return true;
+}
+
+#endif
diff --git a/libmount/uuid.c b/libmount/uuid.c
new file mode 100644
index 00000000..b5b78399
--- /dev/null
+++ b/libmount/uuid.c
@@ -0,0 +1,125 @@
+/*******************************************************************************
+
+ Copyright(C) Jonas 'Sortie' Termansen 2015.
+
+ This file is part of Sortix libmount.
+
+ Sortix libmount is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by the
+ Free Software Foundation, either version 3 of the License, or (at your
+ option) any later version.
+
+ Sortix libmount 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 Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with Sortix libmount. If not, see .
+
+ uuid.c
+ Universally unique identifier.
+
+*******************************************************************************/
+
+#include
+#include
+#include
+
+#include
+
+static bool is_hex_digit(char c)
+{
+ return ('0' <= c && c <= '9') ||
+ ('a' <= c && c <= 'f') ||
+ ('A' <= c && c <= 'F');
+}
+
+static char base(unsigned char bit4)
+{
+ return "0123456789abcdef"[bit4];
+}
+
+static unsigned char debase(char c)
+{
+ if ( '0' <= c && c <= '9' )
+ return (unsigned char) (c - '0');
+ if ( 'a' <= c && c <= 'f' )
+ return (unsigned char) (c - 'a' + 10);
+ if ( 'A' <= c && c <= 'F' )
+ return (unsigned char) (c - 'A' + 10);
+ return 0;
+}
+
+bool uuid_validate(const char* uuid)
+{
+ // Format: 01234567-0123-0123-0123-0123456789AB
+ if ( strlen(uuid) != 36 )
+ return false;
+ for ( size_t i = 0; i < 36; i++ )
+ {
+ if ( i == 8 || i == 13 || i == 18 || i == 23 )
+ {
+ if ( uuid[i] != '-' )
+ return false;
+ }
+ else
+ {
+ if ( !is_hex_digit(uuid[i]) )
+ return false;
+ }
+ }
+ return true;
+}
+
+void uuid_from_string(unsigned char uuid[16], const char* string)
+{
+ size_t output_index = 0;
+ size_t i = 0;
+ while ( i < 36 )
+ {
+ assert(string[i + 0] != '\0');
+ if ( i == 8 || i == 13 || i == 18 || i == 23 )
+ {
+ i++;
+ continue;
+ }
+ uuid[output_index++] = debase(string[i + 0]) << 4 |
+ debase(string[i + 1]) << 0;
+ i += 2;
+ }
+}
+
+void uuid_to_string(const unsigned char uuid[16], char* string)
+{
+ for ( size_t i = 0; i < 4; i++ )
+ {
+ *string++ = base((uuid[i] >> 4) & 0xF);
+ *string++ = base((uuid[i] >> 0) & 0xF);
+ }
+ *string++ = '-';
+ for ( size_t i = 4; i < 6; i++ )
+ {
+ *string++ = base((uuid[i] >> 4) & 0xF);
+ *string++ = base((uuid[i] >> 0) & 0xF);
+ }
+ *string++ = '-';
+ for ( size_t i = 6; i < 8; i++ )
+ {
+ *string++ = base((uuid[i] >> 4) & 0xF);
+ *string++ = base((uuid[i] >> 0) & 0xF);
+ }
+ *string++ = '-';
+ for ( size_t i = 8; i < 10; i++ )
+ {
+ *string++ = base((uuid[i] >> 4) & 0xF);
+ *string++ = base((uuid[i] >> 0) & 0xF);
+ }
+ *string++ = '-';
+ for ( size_t i = 10; i < 16; i++ )
+ {
+ *string++ = base((uuid[i] >> 4) & 0xF);
+ *string++ = base((uuid[i] >> 0) & 0xF);
+ }
+ *string = '\0';
+}