Recognize read-only block devices.

Add tcgetblob(2) harddisk-writable key that says whether a block device is
writable.

Prefer writable block devices in disked(8).

Ignore read-only block devices in sysinstall(8) and sysupgrade(8) when
searching for existing installations and other operating systems.

This is a compatible ABI change.
This commit is contained in:
Jonas 'Sortie' Termansen 2025-01-21 11:36:29 +01:00
parent 74f12febba
commit 03e8ed9415
19 changed files with 94 additions and 25 deletions

View file

@ -283,7 +283,7 @@ sysroot-system: sysroot-fsh sysroot-base-headers
echo 'ID=sortix' && \
echo 'VERSION_ID="$(VERSION)"' && \
echo 'PRETTY_NAME="Sortix $(VERSION)"' && \
echo 'SORTIX_ABI=2.1' && \
echo 'SORTIX_ABI=2.2' && \
true) > "$(SYSROOT)/etc/sortix-release"
echo /etc/sortix-release >> "$(SYSROOT)/tix/manifest/system"
ln -sf sortix-release "$(SYSROOT)/etc/os-release"

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2016, 2017 Jonas 'Sortie' Termansen.
* Copyright (c) 2015, 2016, 2017, 2025 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -494,10 +494,14 @@ static void remove_partition_devices(const char* path)
free(dir_path);
}
static int harddisk_compare_path(const void* a_ptr, const void* b_ptr)
static int harddisk_compare(const void* a_ptr, const void* b_ptr)
{
struct harddisk* a = *((struct harddisk**) a_ptr);
struct harddisk* b = *((struct harddisk**) b_ptr);
if ( a->writable && !b->writable )
return -1;
if ( !a->writable && b->writable )
return 1;
return strcmp(a->path, b->path);
}
@ -1371,7 +1375,7 @@ static void on_devices(size_t argc, char** argv)
{
(void) argc;
(void) argv;
qsort(hds, hds_count, sizeof(struct harddisk*), harddisk_compare_path);
qsort(hds, hds_count, sizeof(struct harddisk*), harddisk_compare);
display_rows_columns(display_harddisk_format, hds, 1 + hds_count, 5);
}
@ -2870,7 +2874,7 @@ int main(int argc, char* argv[])
if ( !devices_open_all(&hds, &hds_count) )
err(1, "iterating devices");
qsort(hds, hds_count, sizeof(struct harddisk*), harddisk_compare_path);
qsort(hds, hds_count, sizeof(struct harddisk*), harddisk_compare);
if ( 2 <= argc )
{

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011-2024 Jonas 'Sortie' Termansen.
* Copyright (c) 2011-2025 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -4310,7 +4310,10 @@ static bool mountpoint_mount(struct mountpoint* mountpoint)
const char* pretend_where = mountpoint->entry.fs_file;
const char* where = mountpoint->absolute;
const char* read_only = NULL;
if ( fs->flags & (FILESYSTEM_FLAG_FSCK_SHOULD | FILESYSTEM_FLAG_FSCK_MUST) )
if ( !(fs->flags & FILESYSTEM_FLAG_WRITABLE) )
read_only = "-r";
else if ( fs->flags & (FILESYSTEM_FLAG_FSCK_SHOULD |
FILESYSTEM_FLAG_FSCK_MUST) )
{
if ( !fsck(fs) && (fs->flags & FILESYSTEM_FLAG_FSCK_MUST) )
{

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, 2014, 2015, 2016, 2021, 2024 Jonas 'Sortie' Termansen.
* Copyright (c) 2013, 2014, 2015, 2016, 2021, 2024, 2025 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -552,6 +552,11 @@ const unsigned char* Port::GetATAIdentify(size_t* size_ptr)
return *size_ptr = sizeof(identify_data), identify_data;
}
bool Port::IsWritable()
{
return true;
}
int Port::sync(ioctx_t* ctx)
{
(void) ctx;

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, 2014, 2015, 2016 Jonas 'Sortie' Termansen.
* Copyright (c) 2013, 2014, 2015, 2016, 2025 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -59,6 +59,7 @@ public:
virtual const char* GetSerial();
virtual const char* GetRevision();
virtual const unsigned char* GetATAIdentify(size_t* size_ptr);
virtual bool IsWritable();
virtual int sync(ioctx_t* ctx);
virtual ssize_t pread(ioctx_t* ctx, unsigned char* buf, size_t count, off_t off);
virtual ssize_t pwrite(ioctx_t* ctx, const unsigned char* buf, size_t count, off_t off);

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011-2016, 2018, 2021, 2024 Jonas 'Sortie' Termansen.
* Copyright (c) 2011-2016, 2018, 2021-2022, 2024-2025 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -681,6 +681,11 @@ const unsigned char* Port::GetATAIdentify(size_t* size_ptr)
return *size_ptr = sizeof(identify_data), identify_data;
}
bool Port::IsWritable()
{
return !is_packet_interface;
}
int Port::sync(ioctx_t* ctx)
{
if ( is_packet_interface )

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011-2016, 2018, 2021 Jonas 'Sortie' Termansen.
* Copyright (c) 2011-2016, 2018, 2021, 2025 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -54,6 +54,7 @@ public:
virtual const char* GetSerial();
virtual const char* GetRevision();
virtual const unsigned char* GetATAIdentify(size_t* size_ptr);
virtual bool IsWritable();
virtual int sync(ioctx_t* ctx);
virtual ssize_t pread(ioctx_t* ctx, unsigned char* buf, size_t count, off_t off);
virtual ssize_t pwrite(ioctx_t* ctx, const unsigned char* buf, size_t count, off_t off);

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011-2017 Jonas 'Sortie' Termansen.
* Copyright (c) 2011-2017, 2025 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -103,7 +103,8 @@ ssize_t PortNode::tcgetblob(ioctx_t* ctx, const char* name, void* buffer,
"harddisk-cylinders\0"
"harddisk-heads\0"
"harddisk-sectors\0"
"harddisk-ata-identify\0";
"harddisk-ata-identify\0"
"harddisk-writable\0";
if ( !name )
{
@ -171,6 +172,11 @@ ssize_t PortNode::tcgetblob(ioctx_t* ctx, const char* name, void* buffer,
if ( !(result_pointer = harddisk->GetATAIdentify(&result_size)) )
return -1;
}
else if ( !strcmp(name, "harddisk-writable") )
{
result_pointer = harddisk->IsWritable() ? "true" : "false";
result_size = strlen((const char*) result_pointer);
}
else
{
return errno = ENOENT, -1;

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015 Jonas 'Sortie' Termansen.
* Copyright (c) 2015, 2025 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -41,6 +41,7 @@ public:
virtual const char* GetSerial() = 0;
virtual const char* GetRevision() = 0;
virtual const unsigned char* GetATAIdentify(size_t* size_ptr) = 0;
virtual bool IsWritable() = 0;
virtual int sync(ioctx_t* ctx) = 0;
virtual ssize_t pread(ioctx_t* ctx, unsigned char* buf, size_t count, off_t off) = 0;
virtual ssize_t pwrite(ioctx_t* ctx, const unsigned char* buf, size_t count, off_t off) = 0;

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2016 Jonas 'Sortie' Termansen.
* Copyright (c) 2015, 2016, 2025 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -30,6 +30,14 @@
#include <mount/harddisk.h>
#include <mount/partition.h>
bool blockdevice_is_writable(const struct blockdevice* bdev)
{
while ( bdev->p )
bdev = bdev->p->parent_bdev;
assert(bdev->hd);
return bdev->hd->writable;
}
blksize_t blockdevice_logical_block_size(const struct blockdevice* bdev)
{
while ( bdev->p )

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2016 Jonas 'Sortie' Termansen.
* Copyright (c) 2015, 2016, 2025 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -150,6 +150,9 @@ static enum filesystem_error ext2_inspect(struct filesystem** fs_ptr,
fs->driver = "extfs";
if ( sb->s_feature_incompat & ~EXT2_FEATURE_INCOMPAT_SUPPORTED )
fs->driver = NULL;
else if ( !(sb->s_feature_compat & ~EXT2_FEATURE_COMPAT_SUPPORTED) &&
blockdevice_is_writable(bdev) )
fs->flags |= FILESYSTEM_FLAG_WRITABLE;
fs->flags |= FILESYSTEM_FLAG_UUID;
memcpy(&fs->uuid, sb->s_uuid, 16);
struct timespec now;

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2021 Jonas 'Sortie' Termansen.
* Copyright (c) 2015, 2021, 2025 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -97,6 +97,10 @@ bool harddisk_inspect_blockdevice(struct harddisk* hd)
return harddisk_close(hd), false;
hd->sectors = (uint16_t) strtoul(str, NULL, 10);
free(str);
if ( !(str = atcgetblob(hd->fd, "harddisk-writable", NULL)) )
return harddisk_close(hd), false;
hd->writable = !strcmp(str, "true");
free(str);
if ( !hd->logical_block_size )
return errno = ENOMEDIUM, false;
// TODO: To avoid potential overflow bugs (assuming malicious filesystem),

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015 Jonas 'Sortie' Termansen.
* Copyright (c) 2015, 2025 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -49,6 +49,7 @@ struct blockdevice
extern "C" {
#endif
bool blockdevice_is_writable(const struct blockdevice*);
blksize_t blockdevice_logical_block_size(const struct blockdevice*);
bool blockdevice_check_reasonable_block_size(blksize_t);
off_t blockdevice_size(const struct blockdevice*);

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2016 Jonas 'Sortie' Termansen.
* Copyright (c) 2015, 2016, 2025 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -36,6 +36,7 @@ struct filesystem_handler;
#define FILESYSTEM_FLAG_FSCK_SHOULD (1 << 1)
#define FILESYSTEM_FLAG_FSCK_MUST (1 << 2)
#define FILESYSTEM_FLAG_NOT_FILESYSTEM (1 << 3)
#define FILESYSTEM_FLAG_WRITABLE (1 << 4)
struct filesystem
{

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015 Jonas 'Sortie' Termansen.
* Copyright (c) 2015, 2025 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -43,6 +43,7 @@ struct harddisk
uint16_t cylinders;
uint16_t heads;
uint16_t sectors;
bool writable;
};
#if defined(__cplusplus)

View file

@ -69,6 +69,13 @@ releasing Sortix x.y, foo." to allow the maintainer to easily
.Xr grep 1
for it after a release.
.Sh CHANGES
.Ss Recognize read-only block devices
The
.Xr tcgetblob 2
system call has gained the new
.Sy harddisk-writable
key, which returns whether a block device is writable.
This is a minor compatible ABI change.
.Ss Add tix-repository(8)
The new
.Xr tix-repository 8

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2016, 2021, 2022 Jonas 'Sortie' Termansen.
* Copyright (c) 2015, 2016, 2021, 2022, 2025 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -190,7 +190,17 @@ bool check_lacking_partition_table(void)
bool check_multiple_harddisks(void)
{
return 2 <= hds_count;
bool any_writable = false;
for ( size_t di = 0; di < hds_count; di++ )
{
if ( hds[di]->writable )
{
if ( any_writable )
return true;
any_writable = true;
}
}
return false;
}
bool fsck(struct filesystem* fs)
@ -332,6 +342,9 @@ bool mountpoint_mount(struct mountpoint* mountpoint)
struct blockdevice* bdev = fs->bdev;
const char* bdev_path = path_of_blockdevice(bdev);
assert(bdev_path);
const char* read_only = NULL;
if ( !(fs->flags & FILESYSTEM_FLAG_WRITABLE) )
read_only = "-r";
if ( fs->flags & FILESYSTEM_FLAG_FSCK_MUST && !fsck(fs) )
{
warnx("Failed to fsck %s", bdev_path);
@ -378,7 +391,8 @@ bool mountpoint_mount(struct mountpoint* mountpoint)
_exit(127);
}
execlp(fs->driver, fs->driver, "--foreground", bdev_path, where,
"--pretend-mount-path", pretend_where, (const char*) NULL);
"--pretend-mount-path", pretend_where, read_only,
(const char*) NULL);
warn("Failed mount %s on %s: execvp: %s",
bdev_path, pretend_where, fs->driver);
_exit(127);

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2016, 2020, 2021, 2022, 2023 Jonas 'Sortie' Termansen.
* Copyright (c) 2015-2016, 2020-2025 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -192,6 +192,8 @@ static bool should_install_bootloader(void)
for ( size_t i = 0; i < hds_count; i++ )
{
struct harddisk* hd = hds[i];
if ( !hd->writable )
continue;
if ( hd->bdev.pt )
{
for ( size_t n = 0; n < hd->bdev.pt->partitions_count; n++ )

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2016, 2020, 2021 Jonas 'Sortie' Termansen.
* Copyright (c) 2015-2016, 2020-2025 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -199,6 +199,8 @@ static void search_installations(const char* mnt)
for ( size_t i = 0; i < hds_count; i++ )
{
struct harddisk* hd = hds[i];
if ( !hd->writable )
continue;
if ( hd->bdev.pt )
{
for ( size_t n = 0; n < hd->bdev.pt->partitions_count; n++ )