From e6a1cd6dee5d40770604c555e4f055eee1524ba4 Mon Sep 17 00:00:00 2001 From: Jonas 'Sortie' Termansen Date: Sat, 7 Mar 2015 13:34:43 +0100 Subject: [PATCH] Add libmount. --- Makefile | 1 + libmount/.gitignore | 2 + libmount/Makefile | 46 ++++ libmount/biosboot.c | 87 +++++++ libmount/blockdevice.c | 84 +++++++ libmount/crc32.c | 46 ++++ libmount/devices.c | 170 ++++++++++++++ libmount/ext2.c | 172 ++++++++++++++ libmount/extended.c | 85 +++++++ libmount/filesystem.c | 102 +++++++++ libmount/gpt.c | 325 +++++++++++++++++++++++++++ libmount/harddisk.c | 123 ++++++++++ libmount/include/mount/biosboot.h | 42 ++++ libmount/include/mount/blockdevice.h | 66 ++++++ libmount/include/mount/devices.h | 46 ++++ libmount/include/mount/error.h | 56 +++++ libmount/include/mount/ext2.h | 105 +++++++++ libmount/include/mount/extended.h | 40 ++++ libmount/include/mount/filesystem.h | 79 +++++++ libmount/include/mount/gpt.h | 93 ++++++++ libmount/include/mount/harddisk.h | 65 ++++++ libmount/include/mount/mbr.h | 86 +++++++ libmount/include/mount/partition.h | 109 +++++++++ libmount/include/mount/uuid.h | 46 ++++ libmount/mbr.c | 270 ++++++++++++++++++++++ libmount/partition.c | 238 ++++++++++++++++++++ libmount/util.h | 74 ++++++ libmount/uuid.c | 125 +++++++++++ 28 files changed, 2783 insertions(+) create mode 100644 libmount/.gitignore create mode 100644 libmount/Makefile create mode 100644 libmount/biosboot.c create mode 100644 libmount/blockdevice.c create mode 100644 libmount/crc32.c create mode 100644 libmount/devices.c create mode 100644 libmount/ext2.c create mode 100644 libmount/extended.c create mode 100644 libmount/filesystem.c create mode 100644 libmount/gpt.c create mode 100644 libmount/harddisk.c create mode 100644 libmount/include/mount/biosboot.h create mode 100644 libmount/include/mount/blockdevice.h create mode 100644 libmount/include/mount/devices.h create mode 100644 libmount/include/mount/error.h create mode 100644 libmount/include/mount/ext2.h create mode 100644 libmount/include/mount/extended.h create mode 100644 libmount/include/mount/filesystem.h create mode 100644 libmount/include/mount/gpt.h create mode 100644 libmount/include/mount/harddisk.h create mode 100644 libmount/include/mount/mbr.h create mode 100644 libmount/include/mount/partition.h create mode 100644 libmount/include/mount/uuid.h create mode 100644 libmount/mbr.c create mode 100644 libmount/partition.c create mode 100644 libmount/util.h create mode 100644 libmount/uuid.c 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'; +}