Add libmount.

This commit is contained in:
Jonas 'Sortie' Termansen 2015-03-07 13:34:43 +01:00
parent aff8f4d89c
commit e6a1cd6dee
28 changed files with 2783 additions and 0 deletions

View File

@ -9,6 +9,7 @@ libc \
libm \
libpthread \
dispd \
libmount \
bench \
carray \
editor \

2
libmount/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
libmount.a
*.o

46
libmount/Makefile Normal file
View File

@ -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

87
libmount/biosboot.c Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
biosboot.c
GPT bios boot partition.
*******************************************************************************/
#include <stdbool.h>
#include <stddef.h>
#include <string.h>
#include <mount/biosboot.h>
#include <mount/blockdevice.h>
#include <mount/filesystem.h>
#include <mount/partition.h>
#include <mount/uuid.h>
#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,
};

84
libmount/blockdevice.c Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
blockdevice.c
Block device abstraction.
*******************************************************************************/
#include <sys/stat.h>
#include <sys/types.h>
#include <assert.h>
#include <errno.h>
#include <ioleast.h>
#include <stdbool.h>
#include <stdint.h>
#include <mount/blockdevice.h>
#include <mount/harddisk.h>
#include <mount/partition.h>
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);
}

46
libmount/crc32.c Normal file
View File

@ -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 <stddef.h>
#include <unistd.h>
#include <mount/gpt.h>
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;
}

170
libmount/devices.c Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
devices.c
Locate block devices.
*******************************************************************************/
#include <ctype.h>
#include <errno.h>
#include <dirent.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <mount/devices.h>
#include <mount/harddisk.h>
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;
}

172
libmount/ext2.c Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
ext2.c
ext2 filesystem.
*******************************************************************************/
#include <endian.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <time.h>
#include <mount/blockdevice.h>
#include <mount/ext2.h>
#include <mount/filesystem.h>
#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,
};

85
libmount/extended.c Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
extended.c
MBR extended partition.
*******************************************************************************/
#include <stdbool.h>
#include <stddef.h>
#include <string.h>
#include <mount/blockdevice.h>
#include <mount/extended.h>
#include <mount/filesystem.h>
#include <mount/partition.h>
#include <mount/uuid.h>
#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,
};

102
libmount/filesystem.c Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
filesystem.c
Filesystem abstraction.
*******************************************************************************/
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <mount/biosboot.h>
#include <mount/blockdevice.h>
#include <mount/ext2.h>
#include <mount/extended.h>
#include <mount/filesystem.h>
#include <mount/partition.h>
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;
}

325
libmount/gpt.c Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
gpt.c
GUID Partition Table.
*******************************************************************************/
#include <sys/types.h>
#include <endian.h>
#include <errno.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#include <mount/blockdevice.h>
#include <mount/gpt.h>
#include <mount/harddisk.h>
#include <mount/partition.h>
#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;
}

123
libmount/harddisk.c Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
harddisk.c
Harddisk abstraction.
*******************************************************************************/
#include <sys/types.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <mount/harddisk.h>
#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);
}

View File

@ -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 <http://www.gnu.org/licenses/>.
mount/biosboot.h
GPT bios boot partition.
*******************************************************************************/
#ifndef INCLUDE_MOUNT_BIOSBOOT_H
#define INCLUDE_MOUNT_BIOSBOOT_H
#include <mount/filesystem.h>
#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

View File

@ -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 <http://www.gnu.org/licenses/>.
mount/blockdevice.h
Block device abstraction.
*******************************************************************************/
#ifndef INCLUDE_MOUNT_BLOCKDEVICE_H
#define INCLUDE_MOUNT_BLOCKDEVICE_H
#include <sys/stat.h>
#if !defined(__cplusplus)
#include <stdbool.h>
#endif
#include <stddef.h>
#include <mount/error.h>
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

View File

@ -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 <http://www.gnu.org/licenses/>.
mount/devices.h
Locate block devices.
*******************************************************************************/
#ifndef INCLUDE_MOUNT_DEVICES_H
#define INCLUDE_MOUNT_DEVICES_H
#if !defined(__cplusplus)
#include <stdbool.h>
#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

View File

@ -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 <http://www.gnu.org/licenses/>.
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

View File

@ -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 <http://www.gnu.org/licenses/>.
mount/ext2.h
ext2 filesystem.
*******************************************************************************/
#ifndef INCLUDE_MOUNT_EXT2_H
#define INCLUDE_MOUNT_EXT2_H
#include <stdint.h>
#include <mount/filesystem.h>
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

View File

@ -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 <http://www.gnu.org/licenses/>.
mount/extended.h
MBR extended partition.
*******************************************************************************/
#ifndef INCLUDE_MOUNT_EXTENDED_H
#define INCLUDE_MOUNT_EXTENDED_H
#include <mount/filesystem.h>
#if defined(__cplusplus)
extern "C" {
#endif
extern const struct filesystem_handler extended_handler;
#if defined(__cplusplus)
} /* extern "C" */
#endif
#endif

View File

@ -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 <http://www.gnu.org/licenses/>.
mount/filesystem.h
Filesystem abstraction.
*******************************************************************************/
#ifndef INCLUDE_MOUNT_FILESYSTEM_H
#define INCLUDE_MOUNT_FILESYSTEM_H
#if !defined(__cplusplus)
#include <stdbool.h>
#endif
#include <stddef.h>
#include <time.h>
#include <mount/error.h>
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

View File

@ -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 <http://www.gnu.org/licenses/>.
mount/gpt.h
GUID Partition Table.
*******************************************************************************/
#ifndef INCLUDE_MOUNT_GPT_H
#define INCLUDE_MOUNT_GPT_H
#include <sys/types.h>
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
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

View File

@ -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 <http://www.gnu.org/licenses/>.
mount/harddisk.h
Harddisk abstraction.
*******************************************************************************/
#ifndef INCLUDE_MOUNT_HARDDISK_H
#define INCLUDE_MOUNT_HARDDISK_H
#include <sys/stat.h>
#include <sys/types.h>
#if !defined(__cplusplus)
#include <stdbool.h>
#endif
#include <stdint.h>
#include <mount/blockdevice.h>
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

View File

@ -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 <http://www.gnu.org/licenses/>.
mount/mbr.h
Master Boot Record.
*******************************************************************************/
#ifndef INCLUDE_MOUNT_MBR_H
#define INCLUDE_MOUNT_MBR_H
#include <sys/types.h>
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
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

View File

@ -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 <http://www.gnu.org/licenses/>.
mount/partition.h
Partition abstraction.
*******************************************************************************/
#ifndef INCLUDE_MOUNT_PARTITION_H
#define INCLUDE_MOUNT_PARTITION_H
#include <sys/types.h>
#if !defined(__cplusplus)
#include <stdbool.h>
#endif
#include <stddef.h>
#include <stdint.h>
#include <mount/blockdevice.h>
#include <mount/error.h>
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

View File

@ -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 <http://www.gnu.org/licenses/>.
mount/uuid.h
Universally unique identifier.
*******************************************************************************/
#ifndef INCLUDE_MOUNT_UUID_H
#define INCLUDE_MOUNT_UUID_H
#if !defined(__cplusplus)
#include <stdbool.h>
#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

270
libmount/mbr.c Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
mbr.c
Master Boot Record.
*******************************************************************************/
#include <sys/types.h>
#include <endian.h>
#include <errno.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <mount/blockdevice.h>
#include <mount/harddisk.h>
#include <mount/mbr.h>
#include <mount/partition.h>
#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;
}

238
libmount/partition.c Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
partition.c
Partition abstraction.
*******************************************************************************/
#include <sys/types.h>
#include <assert.h>
#include <endian.h>
#include <errno.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <mount/blockdevice.h>
#include <mount/gpt.h>
#include <mount/mbr.h>
#include <mount/partition.h>
#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);
}

74
libmount/util.h Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
util.h
Utility functions.
*******************************************************************************/
#ifndef UTIL_H
#define UTIL_H
#include <string.h>
#include <stdlib.h>
#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

125
libmount/uuid.c Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
uuid.c
Universally unique identifier.
*******************************************************************************/
#include <assert.h>
#include <stdbool.h>
#include <string.h>
#include <mount/uuid.h>
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';
}