mirror of
https://github.com/BoredDevNL/BoredOS.git
synced 2026-05-15 10:48:38 +00:00
feature(install): add system installer and FAT32 formatting utility
This commit is contained in:
parent
fd18369bd7
commit
91edd3bc78
5 changed files with 1191 additions and 0 deletions
288
src/fs/mkfs_fat32.c
Normal file
288
src/fs/mkfs_fat32.c
Normal file
|
|
@ -0,0 +1,288 @@
|
||||||
|
// Copyright (c) 2023-2026 Chris (boreddevnl)
|
||||||
|
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
|
||||||
|
// This header needs to maintain in any file it is present in, as per the GPL license terms.
|
||||||
|
#include "mkfs_fat32.h"
|
||||||
|
#include "../dev/disk.h"
|
||||||
|
#include <stddef.h>
|
||||||
|
#include "memory_manager.h"
|
||||||
|
|
||||||
|
extern void serial_write(const char *str);
|
||||||
|
extern void serial_write_num(uint64_t num);
|
||||||
|
|
||||||
|
// Internal helpers
|
||||||
|
|
||||||
|
static void mf_memset(void *dst, int val, int len) {
|
||||||
|
unsigned char *p = (unsigned char *)dst;
|
||||||
|
while (len-- > 0) *p++ = (unsigned char)val;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mf_memcpy(void *dst, const void *src, int len) {
|
||||||
|
unsigned char *d = (unsigned char *)dst;
|
||||||
|
const unsigned char *s = (const unsigned char *)src;
|
||||||
|
while (len-- > 0) *d++ = *s++;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mf_strncpy(char *dst, const char *src, int n) {
|
||||||
|
int i = 0;
|
||||||
|
while (i < n && src[i]) { dst[i] = src[i]; i++; }
|
||||||
|
while (i < n) { dst[i++] = ' '; } /* FAT labels are space-padded */
|
||||||
|
}
|
||||||
|
|
||||||
|
// On-disk BPB structures
|
||||||
|
|
||||||
|
typedef struct __attribute__((packed)) {
|
||||||
|
/* DOS 2.0 BPB */
|
||||||
|
uint8_t jump_boot[3]; /* EB 58 90 */
|
||||||
|
char oem_name[8]; /* "MSDOS5.0" */
|
||||||
|
uint16_t bytes_per_sector; /* 512 */
|
||||||
|
uint8_t sectors_per_cluster;
|
||||||
|
uint16_t reserved_sector_count;
|
||||||
|
uint8_t num_fats; /* 2 */
|
||||||
|
uint16_t root_entry_count; /* 0 for FAT32 */
|
||||||
|
uint16_t total_sectors_16; /* 0 for FAT32 */
|
||||||
|
uint8_t media; /* 0xF8 for fixed disk */
|
||||||
|
uint16_t fat_size_16; /* 0 for FAT32 */
|
||||||
|
uint16_t sectors_per_track;
|
||||||
|
uint16_t num_heads;
|
||||||
|
uint32_t hidden_sectors;
|
||||||
|
uint32_t total_sectors_32;
|
||||||
|
|
||||||
|
/* FAT32 extended BPB */
|
||||||
|
uint32_t fat_size_32;
|
||||||
|
uint16_t ext_flags;
|
||||||
|
uint16_t fs_version; /* 0x0000 */
|
||||||
|
uint32_t root_cluster; /* 2 */
|
||||||
|
uint16_t fs_info; /* sector 1 */
|
||||||
|
uint16_t backup_boot_sector; /* sector 6 */
|
||||||
|
uint8_t reserved[12];
|
||||||
|
uint8_t drive_number; /* 0x80 for fixed disk */
|
||||||
|
uint8_t reserved1;
|
||||||
|
uint8_t boot_sig; /* 0x29 */
|
||||||
|
uint32_t volume_id;
|
||||||
|
char volume_label[11]; /* space-padded */
|
||||||
|
char fs_type[8]; /* "FAT32 " */
|
||||||
|
uint8_t boot_code[420];
|
||||||
|
uint16_t boot_signature; /* 0xAA55 */
|
||||||
|
} FAT32_BPB;
|
||||||
|
|
||||||
|
typedef struct __attribute__((packed)) {
|
||||||
|
uint32_t lead_sig; /* 0x41615252 */
|
||||||
|
uint8_t reserved1[480];
|
||||||
|
uint32_t struct_sig; /* 0x61417272 */
|
||||||
|
uint32_t free_count; /* 0xFFFFFFFF = unknown */
|
||||||
|
uint32_t next_free; /* 0xFFFFFFFF = unknown */
|
||||||
|
uint8_t reserved2[12];
|
||||||
|
uint32_t trail_sig; /* 0xAA550000 */
|
||||||
|
} FAT32_FSInfo;
|
||||||
|
|
||||||
|
// Public API
|
||||||
|
|
||||||
|
int mkfs_fat32_format(Disk *disk, uint32_t sector_count, const char *label) {
|
||||||
|
if (sector_count < MIN_FAT32_SECTORS) {
|
||||||
|
serial_write("[MKFS] Error: partition too small for FAT32 (< 32 MB)\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (!disk || !disk->write_sector) {
|
||||||
|
serial_write("[MKFS] Error: null disk or no write function\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t spc; /* sectors per cluster */
|
||||||
|
if (sector_count < 532480) spc = 1; /* < 260 MB -> 512 B clusters */
|
||||||
|
else if (sector_count < 1064960) spc = 2; /* < 520 MB -> 1 KB */
|
||||||
|
else if (sector_count < 2097152) spc = 4; /* < 1 GB -> 2 KB */
|
||||||
|
else if (sector_count < 16777216) spc = 8; /* < 8 GB -> 4 KB */
|
||||||
|
else if (sector_count < 33554432) spc = 16; /* < 16 GB -> 8 KB */
|
||||||
|
else if (sector_count < 67108864) spc = 32; /* < 32 GB -> 16 KB */
|
||||||
|
else spc = 64; /* >= 32 GB -> 32 KB */
|
||||||
|
|
||||||
|
const uint32_t reserved_sectors = 32;
|
||||||
|
const uint8_t num_fats = 2;
|
||||||
|
const uint32_t root_cluster = 2;
|
||||||
|
|
||||||
|
uint32_t data_sectors = sector_count - reserved_sectors;
|
||||||
|
uint32_t cluster_count = data_sectors / spc;
|
||||||
|
uint32_t fat_bytes = (cluster_count + 2) * 4;
|
||||||
|
uint32_t sectors_per_fat = (fat_bytes + 511) / 512;
|
||||||
|
|
||||||
|
uint8_t *buf = (uint8_t *)kmalloc(512);
|
||||||
|
if (!buf) {
|
||||||
|
serial_write("[MKFS] Error: out of memory\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
FAT32_BPB *bpb = (FAT32_BPB *)buf;
|
||||||
|
mf_memset(bpb, 0, 512);
|
||||||
|
|
||||||
|
bpb->jump_boot[0] = 0xEB;
|
||||||
|
bpb->jump_boot[1] = 0x58;
|
||||||
|
bpb->jump_boot[2] = 0x90;
|
||||||
|
mf_memcpy(bpb->oem_name, "MSDOS5.0", 8);
|
||||||
|
bpb->bytes_per_sector = 512;
|
||||||
|
bpb->sectors_per_cluster = spc;
|
||||||
|
bpb->reserved_sector_count = (uint16_t)reserved_sectors;
|
||||||
|
bpb->num_fats = num_fats;
|
||||||
|
bpb->root_entry_count = 0;
|
||||||
|
bpb->total_sectors_16 = 0;
|
||||||
|
bpb->media = 0xF8;
|
||||||
|
bpb->fat_size_16 = 0;
|
||||||
|
bpb->sectors_per_track = 63;
|
||||||
|
bpb->num_heads = 255;
|
||||||
|
bpb->hidden_sectors = disk->partition_lba_offset;
|
||||||
|
bpb->total_sectors_32 = sector_count;
|
||||||
|
bpb->fat_size_32 = sectors_per_fat;
|
||||||
|
bpb->ext_flags = 0;
|
||||||
|
bpb->fs_version = 0;
|
||||||
|
bpb->root_cluster = root_cluster;
|
||||||
|
bpb->fs_info = 1;
|
||||||
|
bpb->backup_boot_sector = 6;
|
||||||
|
bpb->drive_number = 0x80;
|
||||||
|
bpb->boot_sig = 0x29;
|
||||||
|
bpb->volume_id = 0x12345678; /* arbitrary non-zero volume ID */
|
||||||
|
|
||||||
|
/* Volume label */
|
||||||
|
const char *vol_label = (label && label[0]) ? label : "NO NAME ";
|
||||||
|
char upper_label[11];
|
||||||
|
for (int i = 0; i < 11; i++) {
|
||||||
|
if (vol_label[i] == 0) {
|
||||||
|
for (int j = i; j < 11; j++) upper_label[j] = ' ';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
char c = vol_label[i];
|
||||||
|
if (c >= 'a' && c <= 'z') c -= 32;
|
||||||
|
upper_label[i] = c;
|
||||||
|
}
|
||||||
|
mf_memcpy(bpb->volume_label, upper_label, 11);
|
||||||
|
mf_memcpy(bpb->fs_type, "FAT32 ", 8);
|
||||||
|
|
||||||
|
/* Boot sector signature */
|
||||||
|
bpb->boot_signature = 0xAA55;
|
||||||
|
|
||||||
|
/* Write sector 0 (BPB) */
|
||||||
|
if (disk->write_sector(disk, 0, buf) != 0) {
|
||||||
|
serial_write("[MKFS] Error: failed to write BPB (sector 0)\n");
|
||||||
|
kfree(buf);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
FAT32_FSInfo *fsinfo = (FAT32_FSInfo *)buf;
|
||||||
|
mf_memset(fsinfo, 0, 512);
|
||||||
|
fsinfo->lead_sig = 0x41615252;
|
||||||
|
fsinfo->struct_sig = 0x61417272;
|
||||||
|
fsinfo->free_count = 0xFFFFFFFF;
|
||||||
|
fsinfo->next_free = 0xFFFFFFFF;
|
||||||
|
fsinfo->trail_sig = 0xAA550000;
|
||||||
|
|
||||||
|
if (disk->write_sector(disk, 1, buf) != 0) {
|
||||||
|
serial_write("[MKFS] Error: failed to write FSInfo (sector 1)\n");
|
||||||
|
kfree(buf);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
FAT32_BPB *bpb2 = (FAT32_BPB *)buf;
|
||||||
|
mf_memset(bpb2, 0, 512);
|
||||||
|
mf_memcpy(bpb2->jump_boot, "\xEB\x58\x90", 3);
|
||||||
|
mf_memcpy(bpb2->oem_name, "MSDOS5.0", 8);
|
||||||
|
bpb2->bytes_per_sector = 512;
|
||||||
|
bpb2->sectors_per_cluster = spc;
|
||||||
|
bpb2->reserved_sector_count = (uint16_t)reserved_sectors;
|
||||||
|
bpb2->num_fats = num_fats;
|
||||||
|
bpb2->root_entry_count = 0;
|
||||||
|
bpb2->total_sectors_16 = 0;
|
||||||
|
bpb2->media = 0xF8;
|
||||||
|
bpb2->fat_size_16 = 0;
|
||||||
|
bpb2->sectors_per_track = 63;
|
||||||
|
bpb2->num_heads = 255;
|
||||||
|
bpb2->hidden_sectors = disk->partition_lba_offset;
|
||||||
|
bpb2->total_sectors_32 = sector_count;
|
||||||
|
bpb2->fat_size_32 = sectors_per_fat;
|
||||||
|
bpb2->ext_flags = 0;
|
||||||
|
bpb2->fs_version = 0;
|
||||||
|
bpb2->root_cluster = root_cluster;
|
||||||
|
bpb2->fs_info = 1;
|
||||||
|
bpb2->backup_boot_sector = 6;
|
||||||
|
bpb2->drive_number = 0x80;
|
||||||
|
bpb2->boot_sig = 0x29;
|
||||||
|
bpb2->volume_id = 0x12345678;
|
||||||
|
mf_memcpy(bpb2->volume_label, upper_label, 11);
|
||||||
|
mf_memcpy(bpb2->fs_type, "FAT32 ", 8);
|
||||||
|
bpb2->boot_signature = 0xAA55;
|
||||||
|
|
||||||
|
if (disk->write_sector(disk, 6, buf) != 0) {
|
||||||
|
serial_write("[MKFS] Error: failed to write backup BPB (sector 6)\n");
|
||||||
|
kfree(buf);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
FAT32_FSInfo *fsinfo2 = (FAT32_FSInfo *)buf;
|
||||||
|
mf_memset(fsinfo2, 0, 512);
|
||||||
|
fsinfo2->lead_sig = 0x41615252;
|
||||||
|
fsinfo2->struct_sig = 0x61417272;
|
||||||
|
fsinfo2->free_count = 0xFFFFFFFF;
|
||||||
|
fsinfo2->next_free = 0xFFFFFFFF;
|
||||||
|
fsinfo2->trail_sig = 0xAA550000;
|
||||||
|
|
||||||
|
if (disk->write_sector(disk, 7, buf) != 0) {
|
||||||
|
serial_write("[MKFS] Error: failed to write backup FSInfo (sector 7)\n");
|
||||||
|
kfree(buf);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Zero both FATs */
|
||||||
|
mf_memset(buf, 0, 512);
|
||||||
|
for (uint32_t f = 0; f < num_fats; f++) {
|
||||||
|
uint32_t fat_start = reserved_sectors + (f * sectors_per_fat);
|
||||||
|
for (uint32_t s = 0; s < sectors_per_fat; s++) {
|
||||||
|
if (disk->write_sector(disk, fat_start + s, buf) != 0) {
|
||||||
|
serial_write("[MKFS] Error: failed to zero FAT\n");
|
||||||
|
kfree(buf);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Write markers to both FATs */
|
||||||
|
mf_memset(buf, 0, 512);
|
||||||
|
uint32_t *fat_buf = (uint32_t *)buf;
|
||||||
|
fat_buf[0] = 0x0FFFFFF8; // Media type
|
||||||
|
fat_buf[1] = 0x0FFFFFFF; // Reserved
|
||||||
|
fat_buf[2] = 0x0FFFFFFF; // Root directory (Cluster 2)
|
||||||
|
|
||||||
|
for (uint32_t f = 0; f < num_fats; f++) {
|
||||||
|
uint32_t fat_start = reserved_sectors + (f * sectors_per_fat);
|
||||||
|
if (disk->write_sector(disk, fat_start, buf) != 0) {
|
||||||
|
serial_write("[MKFS] Error: failed to write FAT markers\n");
|
||||||
|
kfree(buf);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Zero root cluster */
|
||||||
|
mf_memset(buf, 0, 512);
|
||||||
|
uint32_t root_start = reserved_sectors + num_fats * sectors_per_fat;
|
||||||
|
for (uint32_t s = 0; s < (uint32_t)spc; s++) {
|
||||||
|
if (disk->write_sector(disk, root_start + s, buf) != 0) {
|
||||||
|
serial_write("[MKFS] Error: failed to zero root cluster\n");
|
||||||
|
kfree(buf);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
kfree(buf);
|
||||||
|
|
||||||
|
serial_write("[MKFS] FAT32 formatted: ");
|
||||||
|
serial_write(disk->devname);
|
||||||
|
serial_write(" label=");
|
||||||
|
char lb[12];
|
||||||
|
mf_memcpy(lb, vol_label, 11);
|
||||||
|
lb[11] = 0;
|
||||||
|
for (int i = 0; i < 11; i++) lb[i] = (lb[i] >= 'a' && lb[i] <= 'z') ? lb[i] - 32 : lb[i];
|
||||||
|
for (int i = 10; i >= 0 && lb[i] == ' '; i--) lb[i] = 0;
|
||||||
|
serial_write(lb);
|
||||||
|
serial_write(" spc=");
|
||||||
|
serial_write_num(spc);
|
||||||
|
serial_write(" fat_sectors=");
|
||||||
|
serial_write_num(sectors_per_fat);
|
||||||
|
serial_write("\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
12
src/fs/mkfs_fat32.h
Normal file
12
src/fs/mkfs_fat32.h
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
// Copyright (c) 2023-2026 Chris (boreddevnl)
|
||||||
|
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
|
||||||
|
// This header needs to maintain in any file it is present in, as per the GPL license terms.
|
||||||
|
#ifndef MKFS_FAT32_H
|
||||||
|
#define MKFS_FAT32_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "../dev/disk.h"
|
||||||
|
#define MIN_FAT32_SECTORS 65536
|
||||||
|
int mkfs_fat32_format(Disk *disk, uint32_t sector_count, const char *label);
|
||||||
|
|
||||||
|
#endif
|
||||||
459
src/userland/cli/boredos_install.c
Normal file
459
src/userland/cli/boredos_install.c
Normal file
|
|
@ -0,0 +1,459 @@
|
||||||
|
// Copyright (c) 2023-2026 Chris (boreddevnl)
|
||||||
|
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
|
||||||
|
// This header needs to maintain in any file it is present in, as per the GPL license terms.
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include "../libc/syscall.h"
|
||||||
|
#include "../libc/stdlib.h"
|
||||||
|
#include "../libc/string.h"
|
||||||
|
#include "../libc/stdio.h"
|
||||||
|
|
||||||
|
static int sc_strcmp(const char *a, const char *b) {
|
||||||
|
while (*a && *a == *b) { a++; b++; }
|
||||||
|
return (unsigned char)*a - (unsigned char)*b;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sc_strncpy(char *dst, const char *src, int n) {
|
||||||
|
int i = 0;
|
||||||
|
while (i < n - 1 && src[i]) { dst[i] = src[i]; i++; }
|
||||||
|
dst[i] = 0;
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void read_line_blocking(char *buf, int max_len) {
|
||||||
|
int len = 0;
|
||||||
|
while (1) {
|
||||||
|
char ch;
|
||||||
|
if (sys_tty_read_in(&ch, 1) <= 0) {
|
||||||
|
sleep(10);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (ch == '\r' || ch == '\n') {
|
||||||
|
sys_write(1, "\n", 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (ch == '\b' || ch == 127) {
|
||||||
|
if (len > 0) {
|
||||||
|
len--;
|
||||||
|
sys_write(1, "\b \b", 3);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (len < max_len - 1) {
|
||||||
|
buf[len++] = ch;
|
||||||
|
sys_write(1, &ch, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buf[len] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
static int copy_file(const char *src, const char *dst) {
|
||||||
|
printf(" [FILE] %s\n", dst);
|
||||||
|
int sfd = sys_open(src, "r");
|
||||||
|
if (sfd < 0) { printf("[ERROR] Cannot open: %s\n", src); return -1; }
|
||||||
|
sys_delete(dst);
|
||||||
|
int dfd = sys_open(dst, "w");
|
||||||
|
if (dfd < 0) { sys_close(sfd); printf("[ERROR] Cannot create: %s\n", dst); return -1; }
|
||||||
|
|
||||||
|
char *buf = (char*)malloc(65536);
|
||||||
|
if (!buf) { sys_close(sfd); sys_close(dfd); printf("[ERROR] Out of memory copying: %s\n", dst); return -1; }
|
||||||
|
int n;
|
||||||
|
while ((n = sys_read(sfd, buf, 65536)) > 0) {
|
||||||
|
if (sys_write_fs(dfd, buf, n) != (uint32_t)n) {
|
||||||
|
sys_close(sfd); sys_close(dfd);
|
||||||
|
free(buf);
|
||||||
|
printf("[ERROR] Write error: %s\n", dst);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(buf);
|
||||||
|
sys_close(sfd);
|
||||||
|
sys_close(dfd);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int copy_file_optional(const char *src, const char *dst) {
|
||||||
|
if (!sys_exists(src)) return 0;
|
||||||
|
return copy_file(src, dst);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int copy_tree(const char *src_dir, const char *dst_dir) {
|
||||||
|
printf(" [DIR ] %s\n", dst_dir);
|
||||||
|
if (sys_mkdir(dst_dir) != 0) {
|
||||||
|
}
|
||||||
|
FAT32_FileInfo entries[64];
|
||||||
|
int n = sys_list(src_dir, entries, 64);
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
if (entries[i].name[0] == '.' && entries[i].name[1] == '_') continue;
|
||||||
|
|
||||||
|
char src_path[512], dst_path[512];
|
||||||
|
int sl = 0, dl = 0;
|
||||||
|
for (; src_dir[sl]; sl++) src_path[sl] = src_dir[sl];
|
||||||
|
src_path[sl++] = '/';
|
||||||
|
for (int j = 0; entries[i].name[j]; j++) src_path[sl++] = entries[i].name[j];
|
||||||
|
src_path[sl] = 0;
|
||||||
|
|
||||||
|
for (; dst_dir[dl]; dl++) dst_path[dl] = dst_dir[dl];
|
||||||
|
dst_path[dl++] = '/';
|
||||||
|
for (int j = 0; entries[i].name[j]; j++) dst_path[dl++] = entries[i].name[j];
|
||||||
|
dst_path[dl] = 0;
|
||||||
|
|
||||||
|
if (entries[i].is_directory) {
|
||||||
|
if (copy_tree(src_path, dst_path) != 0) return -1;
|
||||||
|
} else {
|
||||||
|
if (copy_file(src_path, dst_path) != 0) return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_usage(void) {
|
||||||
|
printf("boredos_install [OPTIONS] /dev/DEVICE\n");
|
||||||
|
printf(" --uefi UEFI (GPT + ESP) [default]\n");
|
||||||
|
printf(" --bios BIOS/MBR\n");
|
||||||
|
printf(" --no-partition Skip fdisk\n");
|
||||||
|
printf(" --no-format Skip mkfs\n");
|
||||||
|
printf(" --no-files Skip file copy\n");
|
||||||
|
printf(" --no-bootloader Skip bootloader files\n");
|
||||||
|
printf(" --esp-size N ESP MB (default: 512)\n");
|
||||||
|
printf(" --esp-dev DEV Explicit ESP device\n");
|
||||||
|
printf(" --root-dev DEV Explicit root device\n");
|
||||||
|
printf(" -y, --yes Auto-accept warning\n");
|
||||||
|
printf(" -v, --verbose\n");
|
||||||
|
printf(" -h, --help\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t get_ticks(void) {
|
||||||
|
return (uint64_t)sys_system(16 /* SYSTEM_CMD_GET_TICKS */, 0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define SYS_SERIAL_WRITE 8
|
||||||
|
|
||||||
|
static void serial_write_user(const char *str) {
|
||||||
|
__asm__ volatile("mov $8, %%rax; mov %0, %%rdi; syscall" : : "r"(str) : "rax", "rdi");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void serial_printf(const char *fmt, ...) {
|
||||||
|
char buf[512];
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
vsnprintf(buf, sizeof(buf), fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
serial_write_user(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
int is_uefi = 1;
|
||||||
|
int do_partition = 1;
|
||||||
|
int do_format = 1;
|
||||||
|
int do_files = 1;
|
||||||
|
int do_bootloader = 1;
|
||||||
|
int opt_yes = 0;
|
||||||
|
int esp_size_mb = 512;
|
||||||
|
const char *devname = NULL;
|
||||||
|
const char *esp_dev_arg = NULL;
|
||||||
|
const char *root_dev_arg = NULL;
|
||||||
|
|
||||||
|
for (int i = 1; i < argc; i++) {
|
||||||
|
if (sc_strcmp(argv[i], "--uefi") == 0) is_uefi = 1;
|
||||||
|
else if (sc_strcmp(argv[i], "--bios") == 0) is_uefi = 0;
|
||||||
|
else if (sc_strcmp(argv[i], "--no-partition") == 0) do_partition = 0;
|
||||||
|
else if (sc_strcmp(argv[i], "--no-format") == 0) do_format = 0;
|
||||||
|
else if (sc_strcmp(argv[i], "--no-files") == 0) do_files = 0;
|
||||||
|
else if (sc_strcmp(argv[i], "--no-bootloader") == 0) do_bootloader = 0;
|
||||||
|
else if ((sc_strcmp(argv[i], "-y") == 0 || sc_strcmp(argv[i], "--yes") == 0)) opt_yes = 1;
|
||||||
|
else if (sc_strcmp(argv[i], "--esp-size") == 0 && i + 1 < argc) {
|
||||||
|
esp_size_mb = 0;
|
||||||
|
const char *s = argv[++i];
|
||||||
|
while (*s >= '0' && *s <= '9') esp_size_mb = esp_size_mb * 10 + (*s++ - '0');
|
||||||
|
} else if (sc_strcmp(argv[i], "--esp-dev") == 0 && i + 1 < argc) {
|
||||||
|
esp_dev_arg = argv[++i];
|
||||||
|
if (esp_dev_arg[0]=='/' && esp_dev_arg[1]=='d' && esp_dev_arg[2]=='e' && esp_dev_arg[3]=='v' && esp_dev_arg[4]=='/')
|
||||||
|
esp_dev_arg += 5;
|
||||||
|
} else if (sc_strcmp(argv[i], "--root-dev") == 0 && i + 1 < argc) {
|
||||||
|
root_dev_arg = argv[++i];
|
||||||
|
if (root_dev_arg[0]=='/' && root_dev_arg[1]=='d' && root_dev_arg[2]=='e' && root_dev_arg[3]=='v' && root_dev_arg[4]=='/')
|
||||||
|
root_dev_arg += 5;
|
||||||
|
} else if (sc_strcmp(argv[i], "-h") == 0 || sc_strcmp(argv[i], "--help") == 0) {
|
||||||
|
print_usage(); return 0;
|
||||||
|
} else if (argv[i][0] != '-') {
|
||||||
|
devname = argv[i];
|
||||||
|
if (devname[0]=='/' && devname[1]=='d' && devname[2]=='e' && devname[3]=='v' && devname[4]=='/')
|
||||||
|
devname += 5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!devname) { print_usage(); return 1; }
|
||||||
|
|
||||||
|
/* --- Step 0: Prerequisites --- */
|
||||||
|
if (!sys_exists("/boot/boredos.elf")) {
|
||||||
|
printf("[ERROR] /boot/boredos.elf not found. Cannot install.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (is_uefi && !sys_exists("/boot/BOOTX64.EFI")) {
|
||||||
|
printf("[ERROR] /boot/BOOTX64.EFI not found. Cannot install UEFI.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
disk_info_t disk_info;
|
||||||
|
{
|
||||||
|
int found = 0;
|
||||||
|
int n = sys_disk_get_count();
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
if (sys_disk_get_info(i, &disk_info) != 0) continue;
|
||||||
|
if (!disk_info.is_partition && sc_strcmp(disk_info.devname, devname) == 0) { found = 1; break; }
|
||||||
|
}
|
||||||
|
if (!found) { printf("[ERROR] Device not found: /dev/%s\n", devname); return 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (disk_info.total_sectors < MIN_INSTALL_SECTORS) {
|
||||||
|
printf("[ERROR] Disk too small: %u sectors. Minimum is 1 GB (%u sectors).\n",
|
||||||
|
disk_info.total_sectors, MIN_INSTALL_SECTORS);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (do_partition || do_format) {
|
||||||
|
uint32_t mb = disk_info.total_sectors / 2048;
|
||||||
|
printf("╔══════════════════════════════════════════════════════╗\n");
|
||||||
|
printf("║ WARNING: ALL DATA on /dev/%s will be ERASED. ║\n", devname);
|
||||||
|
printf("║ Disk: %u sectors (%u MB) ║\n", disk_info.total_sectors, mb);
|
||||||
|
printf("║ This is IRREVERSIBLE. ║\n");
|
||||||
|
printf("╚══════════════════════════════════════════════════════╝\n");
|
||||||
|
if (opt_yes) {
|
||||||
|
printf("Auto-accepted (--yes).\n");
|
||||||
|
} else {
|
||||||
|
printf("Proceed with installation? (y/n): ");
|
||||||
|
char resp[8];
|
||||||
|
read_line_blocking(resp, 8);
|
||||||
|
if (resp[0] != 'y' && resp[0] != 'Y') {
|
||||||
|
printf("Aborted.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sys_mkdir("/mnt");
|
||||||
|
sys_mkdir("/mnt/boot");
|
||||||
|
sys_mkdir("/mnt/esp");
|
||||||
|
|
||||||
|
if (do_partition) {
|
||||||
|
char fdisk_args[128];
|
||||||
|
int ai = 0;
|
||||||
|
const char *cmd = "--script ";
|
||||||
|
for (; *cmd; cmd++) fdisk_args[ai++] = *cmd;
|
||||||
|
if (is_uefi) {
|
||||||
|
const char *u = "--uefi --esp-size ";
|
||||||
|
for (; *u; u++) fdisk_args[ai++] = *u;
|
||||||
|
char num[16]; int ni = 0;
|
||||||
|
int v = esp_size_mb;
|
||||||
|
if (v == 0) { num[ni++] = '0'; } else {
|
||||||
|
char tmp[16]; int ti = 0;
|
||||||
|
while (v > 0) { tmp[ti++] = '0' + (v % 10); v /= 10; }
|
||||||
|
for (int j = ti - 1; j >= 0; j--) num[ni++] = tmp[j];
|
||||||
|
}
|
||||||
|
for (int j = 0; j < ni; j++) fdisk_args[ai++] = num[j];
|
||||||
|
fdisk_args[ai++] = ' ';
|
||||||
|
} else {
|
||||||
|
const char *b = "--mbr "; for (; *b; b++) fdisk_args[ai++] = *b;
|
||||||
|
}
|
||||||
|
fdisk_args[ai++] = '/'; fdisk_args[ai++] = 'd'; fdisk_args[ai++] = 'e';
|
||||||
|
fdisk_args[ai++] = 'v'; fdisk_args[ai++] = '/';
|
||||||
|
for (int j = 0; devname[j]; j++) fdisk_args[ai++] = devname[j];
|
||||||
|
fdisk_args[ai] = 0;
|
||||||
|
|
||||||
|
int pid = sys_spawn("/bin/fdisk.elf", fdisk_args, SPAWN_FLAG_INHERIT_TTY, 0);
|
||||||
|
if (pid < 0) { printf("[ERROR] Failed to run fdisk.\n"); return 1; }
|
||||||
|
int status = 0;
|
||||||
|
sys_waitpid(pid, &status, 0);
|
||||||
|
if (status != 0) { printf("[ERROR] fdisk failed.\n"); return 1; }
|
||||||
|
|
||||||
|
sys_disk_rescan(devname);
|
||||||
|
}
|
||||||
|
|
||||||
|
char esp_dev[16] = {0};
|
||||||
|
char root_dev[16] = {0};
|
||||||
|
|
||||||
|
if (esp_dev_arg) sc_strncpy(esp_dev, esp_dev_arg, 16);
|
||||||
|
if (root_dev_arg) sc_strncpy(root_dev, root_dev_arg, 16);
|
||||||
|
|
||||||
|
if (!esp_dev[0] || !root_dev[0]) {
|
||||||
|
int n = sys_disk_get_count();
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
disk_info_t d;
|
||||||
|
if (sys_disk_get_info(i, &d) != 0) continue;
|
||||||
|
if (!d.is_partition) continue;
|
||||||
|
int match = 1;
|
||||||
|
for (int j = 0; devname[j]; j++) {
|
||||||
|
if (d.devname[j] != devname[j]) { match = 0; break; }
|
||||||
|
}
|
||||||
|
if (!match) continue;
|
||||||
|
if (is_uefi && d.is_esp && !esp_dev[0])
|
||||||
|
sc_strncpy(esp_dev, d.devname, 16);
|
||||||
|
else if (!d.is_esp && !root_dev[0]) {
|
||||||
|
if (d.is_fat32 || do_format)
|
||||||
|
sc_strncpy(root_dev, d.devname, 16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!root_dev[0]) { printf("[ERROR] Could not find root partition.\n"); return 1; }
|
||||||
|
if (is_uefi && !esp_dev[0]) { printf("[ERROR] Could not find ESP.\n"); return 1; }
|
||||||
|
|
||||||
|
if (do_format) {
|
||||||
|
if (is_uefi) {
|
||||||
|
if (sys_disk_mkfs_fat32(esp_dev, "EFI") != 0) {
|
||||||
|
printf("[ERROR] Failed to format ESP.\n"); return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (sys_disk_mkfs_fat32(root_dev, "BOREDOS") != 0) {
|
||||||
|
printf("[ERROR] Failed to format root partition.\n"); return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sys_disk_mount(root_dev, "/mnt") != 0) {
|
||||||
|
printf("[ERROR] Cannot mount /dev/%s to /mnt\n", root_dev);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_uefi) {
|
||||||
|
sys_mkdir("/mnt/boot");
|
||||||
|
if (sys_disk_mount(esp_dev, "/mnt/boot") != 0) {
|
||||||
|
printf("[ERROR] Cannot mount /dev/%s to /mnt/boot\n", esp_dev);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// In BIOS mode, /boot is just a directory on the root partition
|
||||||
|
sys_mkdir("/mnt/boot");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sys_mkdir("/mnt/Library") == 0 || sys_exists("/mnt/Library")) {
|
||||||
|
int mfd = sys_open("/mnt/Library/.boredos_root", "w");
|
||||||
|
if (mfd >= 0) sys_close(mfd);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (do_files) {
|
||||||
|
serial_printf("Copying system files...\n");
|
||||||
|
uint64_t t0 = get_ticks();
|
||||||
|
if (copy_tree("/bin", "/mnt/bin") != 0) { serial_printf("[ERROR] copy /bin failed\n"); return 1; }
|
||||||
|
if (copy_tree("/Library", "/mnt/Library") != 0) { serial_printf("[ERROR] copy /Library failed\n"); return 1; }
|
||||||
|
if (copy_tree("/docs", "/mnt/docs") != 0) { serial_printf("[ERROR] copy /docs failed\n"); return 1; }
|
||||||
|
if (copy_tree("/root", "/mnt/root") != 0) { serial_printf("[ERROR] copy /root failed\n"); return 1; }
|
||||||
|
|
||||||
|
serial_printf("Copying kernel and initrd...\n");
|
||||||
|
if (copy_file("/boot/boredos.elf", "/mnt/boot/boredos.elf") != 0) {
|
||||||
|
serial_printf("[ERROR] Failed to copy kernel to ESP\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (sys_exists("/boot/initrd.tar")) {
|
||||||
|
if (copy_file("/boot/initrd.tar", "/mnt/boot/initrd.tar") != 0) {
|
||||||
|
printf("[ERROR] Failed to copy /boot/initrd.tar to ESP\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
} else if (sys_exists("/initrd.tar")) {
|
||||||
|
if (copy_file("/initrd.tar", "/mnt/boot/initrd.tar") != 0) {
|
||||||
|
serial_printf("[ERROR] Failed to copy /initrd.tar to ESP\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
serial_printf("[WARNING] initrd.tar not found in live system (checked /boot/ and /)!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
copy_file_optional("README.md", "/mnt/README.md");
|
||||||
|
copy_file_optional("LICENSE", "/mnt/LICENSE");
|
||||||
|
uint64_t t1 = get_ticks();
|
||||||
|
serial_printf("Files copied (%llu ticks).\n", (unsigned long long)(t1 - t0));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (do_bootloader) {
|
||||||
|
if (is_uefi) {
|
||||||
|
if (sys_mkdir("/mnt/boot/EFI") != 0 && !sys_exists("/mnt/boot/EFI")) {
|
||||||
|
printf("[ERROR] Failed to create /mnt/boot/EFI\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (sys_mkdir("/mnt/boot/EFI/BOOT") != 0 && !sys_exists("/mnt/boot/EFI/BOOT")) {
|
||||||
|
printf("[ERROR] Failed to create /mnt/boot/EFI/BOOT\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (copy_file("/boot/BOOTX64.EFI", "/mnt/boot/EFI/BOOT/BOOTX64.EFI") != 0) {
|
||||||
|
printf("[ERROR] Failed to copy BOOTX64.EFI\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
copy_file_optional("/boot/BOOTIA32.EFI", "/mnt/boot/EFI/BOOT/BOOTIA32.EFI");
|
||||||
|
|
||||||
|
int fd = sys_open("/mnt/boot/limine.conf", "w");
|
||||||
|
if (fd >= 0) {
|
||||||
|
char cfg[512];
|
||||||
|
int len = snprintf(cfg, sizeof(cfg),
|
||||||
|
"timeout: 3\n"
|
||||||
|
"verbose: yes\n"
|
||||||
|
"\n"
|
||||||
|
"/BoredOS\n"
|
||||||
|
" protocol: limine\n"
|
||||||
|
" path: boot():/boredos.elf\n"
|
||||||
|
" cmdline: -v root=/dev/%s --disk --accept-tos\n"
|
||||||
|
" module_path: boot():/initrd.tar\n",
|
||||||
|
root_dev);
|
||||||
|
if (len > 0) sys_write_fs(fd, cfg, len);
|
||||||
|
sys_close(fd);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
copy_file_optional("/boot/limine-bios.sys", "/mnt/limine-bios.sys");
|
||||||
|
|
||||||
|
int fd = sys_open("/mnt/limine.conf", "w");
|
||||||
|
if (fd >= 0) {
|
||||||
|
char cfg[512];
|
||||||
|
int len = snprintf(cfg, sizeof(cfg),
|
||||||
|
"timeout: 3\n"
|
||||||
|
"verbose: yes\n"
|
||||||
|
"\n"
|
||||||
|
"/BoredOS\n"
|
||||||
|
" protocol: limine\n"
|
||||||
|
" root: boot()\n"
|
||||||
|
" path: /boredos.elf\n"
|
||||||
|
" cmdline: -v root=/dev/%s --disk --accept-tos\n"
|
||||||
|
" module_path: /initrd.tar\n",
|
||||||
|
root_dev);
|
||||||
|
if (len > 0) sys_write_fs(fd, cfg, len);
|
||||||
|
sys_close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("[NOTICE] BIOS bootstrap requires host-side: limine bios-install /dev/%s\n", devname);
|
||||||
|
printf(" limine-bios.sys has been copied to the root of the target partition.\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_uefi) {
|
||||||
|
printf("Verifying BOOTX64.EFI...\n");
|
||||||
|
int vfd = sys_open("/mnt/boot/EFI/BOOT/BOOTX64.EFI", "r");
|
||||||
|
if (vfd >= 0) {
|
||||||
|
char magic[2];
|
||||||
|
if (sys_read(vfd, magic, 2) == 2) {
|
||||||
|
if (magic[0] == 'M' && magic[1] == 'Z') {
|
||||||
|
printf("[OK] BOOTX64.EFI verification successful (MZ found)\n");
|
||||||
|
} else {
|
||||||
|
printf("[ERROR] BOOTX64.EFI verification failed: Invalid magic bytes %02x %02x\n", magic[0], magic[1]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
printf("[ERROR] BOOTX64.EFI verification failed: Could not read 2 bytes\n");
|
||||||
|
}
|
||||||
|
sys_close(vfd);
|
||||||
|
} else {
|
||||||
|
printf("[ERROR] BOOTX64.EFI verification failed: Could not open file for verification\n");
|
||||||
|
}
|
||||||
|
sys_disk_sync("/mnt/boot");
|
||||||
|
sys_disk_umount("/mnt/boot");
|
||||||
|
}
|
||||||
|
sys_disk_sync("/mnt");
|
||||||
|
sys_disk_umount("/mnt");
|
||||||
|
|
||||||
|
// Sync physical disk
|
||||||
|
sys_disk_sync(devname);
|
||||||
|
|
||||||
|
|
||||||
|
printf("\nInstallation complete.\n");
|
||||||
|
printf(" Root partition: /dev/%s\n", root_dev);
|
||||||
|
if (is_uefi) printf(" ESP: /dev/%s\n", esp_dev);
|
||||||
|
printf(" Mode: %s\n", is_uefi ? "UEFI" : "BIOS");
|
||||||
|
printf("Reboot and select the target disk to boot BoredOS.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
366
src/userland/cli/fdisk.c
Normal file
366
src/userland/cli/fdisk.c
Normal file
|
|
@ -0,0 +1,366 @@
|
||||||
|
// Copyright (c) 2023-2026 Chris (boreddevnl)
|
||||||
|
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
|
||||||
|
// This header needs to maintain in any file it is present in, as per the GPL license terms.
|
||||||
|
#include "../libc/syscall.h"
|
||||||
|
#include "../libc/stdlib.h"
|
||||||
|
#include "../libc/string.h"
|
||||||
|
#include "../libc/stdio.h"
|
||||||
|
|
||||||
|
#define MAX_PARTS 4
|
||||||
|
#define SECTOR_SIZE_BYTES 512ULL
|
||||||
|
#define ONE_MB (1024ULL * 1024ULL)
|
||||||
|
#define ONE_GB (1024ULL * 1024ULL * 1024ULL)
|
||||||
|
|
||||||
|
static void print_usage(void) {
|
||||||
|
printf("fdisk [OPTIONS] /dev/DEVICE\n");
|
||||||
|
printf(" -p, --print Print partition table and exit\n");
|
||||||
|
printf(" -s, --script Non-interactive auto-partition\n");
|
||||||
|
printf(" --mbr Use MBR instead of GPT\n");
|
||||||
|
printf(" --uefi Include ESP (default with GPT)\n");
|
||||||
|
printf(" --esp-size N ESP size (b/mb/gb, default: 512mb)\n");
|
||||||
|
printf(" -h, --help\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sc_strcmp(const char *a, const char *b) {
|
||||||
|
while (*a && *a == *b) { a++; b++; }
|
||||||
|
return (unsigned char)*a - (unsigned char)*b;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sc_atoi(const char *s) {
|
||||||
|
int n = 0;
|
||||||
|
while (*s >= '0' && *s <= '9') n = n * 10 + (*s++ - '0');
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char sc_tolower(char ch) {
|
||||||
|
if (ch >= 'A' && ch <= 'Z') return (char)(ch + 32);
|
||||||
|
return ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t sc_parse_size_bytes(const char *s) {
|
||||||
|
uint64_t n = 0;
|
||||||
|
int has_digit = 0;
|
||||||
|
|
||||||
|
while (*s == ' ' || *s == '\t') s++;
|
||||||
|
if (*s == '+') s++;
|
||||||
|
|
||||||
|
while (*s >= '0' && *s <= '9') {
|
||||||
|
has_digit = 1;
|
||||||
|
n = n * 10ULL + (uint64_t)(*s - '0');
|
||||||
|
s++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!has_digit) return 0;
|
||||||
|
|
||||||
|
while (*s == ' ' || *s == '\t') s++;
|
||||||
|
|
||||||
|
if (*s == '\0') return n * ONE_MB;
|
||||||
|
|
||||||
|
{
|
||||||
|
char c1 = sc_tolower(*s);
|
||||||
|
char c2 = sc_tolower(*(s + 1));
|
||||||
|
|
||||||
|
if (c1 == 'g' && c2 == 'b') return n * ONE_GB;
|
||||||
|
if (c1 == 'm' && c2 == 'b') return n * ONE_MB;
|
||||||
|
if (c1 == 'b') return n;
|
||||||
|
if (c1 == 'g' && c2 == '\0') return n * ONE_GB;
|
||||||
|
if (c1 == 'm' && c2 == '\0') return n * ONE_MB;
|
||||||
|
}
|
||||||
|
|
||||||
|
return n * ONE_MB;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t sc_bytes_to_sectors_ceil(uint64_t bytes) {
|
||||||
|
if (bytes == 0) return 0;
|
||||||
|
return (uint32_t)((bytes + SECTOR_SIZE_BYTES - 1ULL) / SECTOR_SIZE_BYTES);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sc_format_size(uint64_t bytes, char *out, size_t out_len) {
|
||||||
|
if (bytes >= ONE_GB) {
|
||||||
|
unsigned long long whole = (unsigned long long)(bytes / ONE_GB);
|
||||||
|
uint64_t rem = bytes % ONE_GB;
|
||||||
|
unsigned long long frac = (unsigned long long)((rem * 100ULL + (ONE_GB / 2)) / ONE_GB);
|
||||||
|
if (frac >= 100ULL) { whole++; frac = 0; }
|
||||||
|
snprintf(out, out_len, "%llu.%02llugb", whole, frac);
|
||||||
|
} else if (bytes >= ONE_MB) {
|
||||||
|
unsigned long long whole = (unsigned long long)(bytes / ONE_MB);
|
||||||
|
uint64_t rem = bytes % ONE_MB;
|
||||||
|
unsigned long long frac = (unsigned long long)((rem * 100ULL + (ONE_MB / 2)) / ONE_MB);
|
||||||
|
if (frac >= 100ULL) { whole++; frac = 0; }
|
||||||
|
snprintf(out, out_len, "%llu.%02llumb", whole, frac);
|
||||||
|
} else {
|
||||||
|
snprintf(out, out_len, "%llub", (unsigned long long)bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void read_line_blocking(char *buf, int max_len) {
|
||||||
|
int len = 0;
|
||||||
|
while (1) {
|
||||||
|
char ch;
|
||||||
|
if (sys_tty_read_in(&ch, 1) <= 0) {
|
||||||
|
sleep(10);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (ch == '\r' || ch == '\n') {
|
||||||
|
sys_write(1, "\n", 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (ch == '\b' || ch == 127) {
|
||||||
|
if (len > 0) {
|
||||||
|
len--;
|
||||||
|
sys_write(1, "\b \b", 3);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (len < max_len - 1) {
|
||||||
|
buf[len++] = ch;
|
||||||
|
sys_write(1, &ch, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buf[len] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_partition_table(const char *devname) {
|
||||||
|
int n = sys_disk_get_count();
|
||||||
|
int found = 0;
|
||||||
|
printf("Partition table for /dev/%s:\n", devname);
|
||||||
|
printf("%-10s %-12s %-12s %-10s %-6s %s\n",
|
||||||
|
"Device", "Start", "End", "Size", "ESP", "FAT32");
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
disk_info_t d;
|
||||||
|
if (sys_disk_get_info(i, &d) != 0) continue;
|
||||||
|
if (!d.is_partition) continue;
|
||||||
|
char parent[16];
|
||||||
|
int len = 0;
|
||||||
|
while (devname[len]) len++;
|
||||||
|
int match = 1;
|
||||||
|
for (int j = 0; j < len; j++) {
|
||||||
|
if (d.devname[j] != devname[j]) { match = 0; break; }
|
||||||
|
}
|
||||||
|
if (!match) continue;
|
||||||
|
{
|
||||||
|
char start_buf[24];
|
||||||
|
char end_buf[24];
|
||||||
|
char size_buf[24];
|
||||||
|
uint64_t start_bytes = (uint64_t)d.lba_offset * SECTOR_SIZE_BYTES;
|
||||||
|
uint64_t end_bytes = (uint64_t)(d.lba_offset + d.total_sectors - 1) * SECTOR_SIZE_BYTES;
|
||||||
|
uint64_t size_bytes = (uint64_t)d.total_sectors * SECTOR_SIZE_BYTES;
|
||||||
|
sc_format_size(start_bytes, start_buf, sizeof(start_buf));
|
||||||
|
sc_format_size(end_bytes, end_buf, sizeof(end_buf));
|
||||||
|
sc_format_size(size_bytes, size_buf, sizeof(size_buf));
|
||||||
|
printf("/dev/%-5s %-12s %-12s %-10s %-6s %s\n",
|
||||||
|
d.devname,
|
||||||
|
start_buf,
|
||||||
|
end_buf,
|
||||||
|
size_buf,
|
||||||
|
d.is_esp ? "yes" : "no",
|
||||||
|
d.is_fat32 ? "yes" : "no");
|
||||||
|
}
|
||||||
|
found++;
|
||||||
|
}
|
||||||
|
if (!found) printf(" (no partitions)\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
int opt_print = 0;
|
||||||
|
int opt_script = 0;
|
||||||
|
int opt_mbr = 0;
|
||||||
|
int opt_uefi = 1;
|
||||||
|
uint64_t esp_size_bytes = 512ULL * ONE_MB;
|
||||||
|
const char *devname = NULL;
|
||||||
|
|
||||||
|
for (int i = 1; i < argc; i++) {
|
||||||
|
if (sc_strcmp(argv[i], "-p") == 0 || sc_strcmp(argv[i], "--print") == 0)
|
||||||
|
opt_print = 1;
|
||||||
|
else if (sc_strcmp(argv[i], "-s") == 0 || sc_strcmp(argv[i], "--script") == 0)
|
||||||
|
opt_script = 1;
|
||||||
|
else if (sc_strcmp(argv[i], "--mbr") == 0) { opt_mbr = 1; opt_uefi = 0; }
|
||||||
|
else if (sc_strcmp(argv[i], "--uefi") == 0) opt_uefi = 1;
|
||||||
|
else if (sc_strcmp(argv[i], "--esp-size") == 0 && i + 1 < argc) {
|
||||||
|
uint64_t parsed = sc_parse_size_bytes(argv[++i]);
|
||||||
|
if (parsed > 0) esp_size_bytes = parsed;
|
||||||
|
}
|
||||||
|
else if (sc_strcmp(argv[i], "-h") == 0 || sc_strcmp(argv[i], "--help") == 0) {
|
||||||
|
print_usage();
|
||||||
|
return 0;
|
||||||
|
} else if (argv[i][0] != '-') {
|
||||||
|
devname = argv[i];
|
||||||
|
if (devname[0]=='/' && devname[1]=='d' && devname[2]=='e' && devname[3]=='v' && devname[4]=='/')
|
||||||
|
devname += 5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!devname) { print_usage(); return 1; }
|
||||||
|
|
||||||
|
if (opt_print) {
|
||||||
|
print_partition_table(devname);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!opt_script) {
|
||||||
|
disk_info_t disk;
|
||||||
|
int found = -1;
|
||||||
|
int n = sys_disk_get_count();
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
disk_info_t d;
|
||||||
|
if (sys_disk_get_info(i, &d) != 0) continue;
|
||||||
|
if (!d.is_partition && sc_strcmp(d.devname, devname) == 0) {
|
||||||
|
disk = d;
|
||||||
|
found = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (found < 0) { printf("[ERROR] Device not found: /dev/%s\n", devname); return 1; }
|
||||||
|
|
||||||
|
{
|
||||||
|
char disk_size[24];
|
||||||
|
sc_format_size((uint64_t)disk.total_sectors * SECTOR_SIZE_BYTES, disk_size, sizeof(disk_size));
|
||||||
|
printf("fdisk /dev/%s (%s)\n", devname, disk_size);
|
||||||
|
}
|
||||||
|
printf("Commands: p (print), n (new), d NUM (delete), w (write), q (quit)\n");
|
||||||
|
printf("Sizes accept b/mb/gb (default unit: mb).\n");
|
||||||
|
|
||||||
|
partition_spec_t parts[MAX_PARTS];
|
||||||
|
int part_count = 0;
|
||||||
|
char line[128];
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
printf("fdisk> ");
|
||||||
|
read_line_blocking(line, 128);
|
||||||
|
int len = 0;
|
||||||
|
while(line[len]) len++;
|
||||||
|
if (len == 0) continue;
|
||||||
|
|
||||||
|
if (line[0] == 'q') break;
|
||||||
|
if (line[0] == 'p') {
|
||||||
|
for (int i = 0; i < part_count; i++) {
|
||||||
|
char start_buf[24];
|
||||||
|
char size_buf[24];
|
||||||
|
sc_format_size((uint64_t)parts[i].lba_start * SECTOR_SIZE_BYTES,
|
||||||
|
start_buf, sizeof(start_buf));
|
||||||
|
sc_format_size((uint64_t)parts[i].sector_count * SECTOR_SIZE_BYTES,
|
||||||
|
size_buf, sizeof(size_buf));
|
||||||
|
printf(" %d: start=%s size=%s esp=%s\n", i + 1,
|
||||||
|
start_buf, size_buf,
|
||||||
|
(parts[i].flags & PART_FLAG_ESP) ? "yes" : "no");
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (line[0] == 'n') {
|
||||||
|
if (part_count >= MAX_PARTS) { printf("Max partitions reached\n"); continue; }
|
||||||
|
uint32_t start = 2048;
|
||||||
|
if (part_count > 0)
|
||||||
|
start = parts[part_count-1].lba_start + parts[part_count-1].sector_count;
|
||||||
|
if (start % 2048) start = ((start + 2047) / 2048) * 2048;
|
||||||
|
{
|
||||||
|
char start_buf[24];
|
||||||
|
sc_format_size((uint64_t)start * SECTOR_SIZE_BYTES, start_buf, sizeof(start_buf));
|
||||||
|
printf("First offset [%s]: ", start_buf);
|
||||||
|
}
|
||||||
|
read_line_blocking(line, 64);
|
||||||
|
len = 0; while(line[len]) len++;
|
||||||
|
if (len > 0) start = sc_bytes_to_sectors_ceil(sc_parse_size_bytes(line));
|
||||||
|
if (start % 2048) start = ((start + 2047) / 2048) * 2048;
|
||||||
|
|
||||||
|
uint32_t last_usable = opt_mbr ? (disk.total_sectors - 1) : (disk.total_sectors - 34);
|
||||||
|
uint32_t remaining = (last_usable >= start) ? (last_usable - start + 1) : 0;
|
||||||
|
{
|
||||||
|
char rem_buf[24];
|
||||||
|
sc_format_size((uint64_t)remaining * SECTOR_SIZE_BYTES, rem_buf, sizeof(rem_buf));
|
||||||
|
printf("Size [%s]: ", rem_buf);
|
||||||
|
}
|
||||||
|
read_line_blocking(line, 64);
|
||||||
|
len = 0; while(line[len]) len++;
|
||||||
|
uint32_t size = remaining;
|
||||||
|
if (len > 0) size = sc_bytes_to_sectors_ceil(sc_parse_size_bytes(line));
|
||||||
|
|
||||||
|
parts[part_count].lba_start = start;
|
||||||
|
parts[part_count].sector_count = size;
|
||||||
|
parts[part_count].part_type = 0;
|
||||||
|
parts[part_count].flags = 0;
|
||||||
|
parts[part_count].label[0] = 0;
|
||||||
|
part_count++;
|
||||||
|
printf("Partition %d added\n", part_count);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (line[0] == 'd') {
|
||||||
|
int idx = sc_atoi(line + 1) - 1;
|
||||||
|
if (idx < 0 || idx >= part_count) { printf("Invalid partition\n"); continue; }
|
||||||
|
for (int i = idx; i < part_count - 1; i++) parts[i] = parts[i+1];
|
||||||
|
part_count--;
|
||||||
|
printf("Partition %d deleted\n", idx + 1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (line[0] == 'w') {
|
||||||
|
int ret;
|
||||||
|
if (opt_mbr)
|
||||||
|
ret = sys_disk_write_mbr(devname, parts, part_count);
|
||||||
|
else
|
||||||
|
ret = sys_disk_write_gpt(devname, parts, part_count);
|
||||||
|
if (ret == 0) printf("Partition table written.\n");
|
||||||
|
else printf("[ERROR] Failed to write partition table.\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
disk_info_t disk;
|
||||||
|
int found = 0;
|
||||||
|
{
|
||||||
|
int n = sys_disk_get_count();
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
disk_info_t d;
|
||||||
|
if (sys_disk_get_info(i, &d) != 0) continue;
|
||||||
|
if (!d.is_partition && sc_strcmp(d.devname, devname) == 0) {
|
||||||
|
disk = d;
|
||||||
|
found = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) { printf("[ERROR] Device not found: /dev/%s\n", devname); return 1; }
|
||||||
|
|
||||||
|
partition_spec_t parts[2];
|
||||||
|
int count = 0;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!opt_mbr && opt_uefi) {
|
||||||
|
uint32_t esp_sectors = sc_bytes_to_sectors_ceil(esp_size_bytes);
|
||||||
|
if (esp_sectors % 2048) esp_sectors = ((esp_sectors + 2047) / 2048) * 2048;
|
||||||
|
parts[0].lba_start = 2048;
|
||||||
|
parts[0].sector_count = esp_sectors;
|
||||||
|
parts[0].part_type = 0;
|
||||||
|
parts[0].flags = PART_FLAG_ESP;
|
||||||
|
parts[0].label[0]='E'; parts[0].label[1]='F'; parts[0].label[2]='I';
|
||||||
|
parts[0].label[3]=' '; parts[0].label[4]='S'; parts[0].label[5]='y';
|
||||||
|
parts[0].label[6]='s'; parts[0].label[7]='t'; parts[0].label[8]='e';
|
||||||
|
parts[0].label[9]='m'; parts[0].label[10]=0;
|
||||||
|
|
||||||
|
uint32_t root_start = 2048 + esp_sectors;
|
||||||
|
if (root_start % 2048) root_start = ((root_start + 2047) / 2048) * 2048;
|
||||||
|
parts[1].lba_start = root_start;
|
||||||
|
parts[1].sector_count = disk.total_sectors - root_start - 34;
|
||||||
|
parts[1].part_type = 0;
|
||||||
|
parts[1].flags = 0;
|
||||||
|
parts[1].label[0]='B'; parts[1].label[1]='o'; parts[1].label[2]='r';
|
||||||
|
parts[1].label[3]='e'; parts[1].label[4]='d'; parts[1].label[5]='O';
|
||||||
|
parts[1].label[6]='S'; parts[1].label[7]=0;
|
||||||
|
count = 2;
|
||||||
|
ret = sys_disk_write_gpt(devname, parts, count);
|
||||||
|
} else {
|
||||||
|
parts[0].lba_start = 2048;
|
||||||
|
parts[0].sector_count = disk.total_sectors - 2048;
|
||||||
|
parts[0].part_type = 0;
|
||||||
|
parts[0].flags = 0;
|
||||||
|
parts[0].label[0]='B'; parts[0].label[1]='o'; parts[0].label[2]='r';
|
||||||
|
parts[0].label[3]='e'; parts[0].label[4]='d'; parts[0].label[5]='O';
|
||||||
|
parts[0].label[6]='S'; parts[0].label[7]=0;
|
||||||
|
count = 1;
|
||||||
|
ret = sys_disk_write_mbr(devname, parts, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret != 0) { printf("[ERROR] Partition write failed.\n"); return 1; }
|
||||||
|
printf("Partition table written to /dev/%s.\n", devname);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
66
src/userland/cli/mkfs_fat.c
Normal file
66
src/userland/cli/mkfs_fat.c
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
// Copyright (c) 2023-2026 Chris (boreddevnl)
|
||||||
|
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
|
||||||
|
// This header needs to maintain in any file it is present in, as per the GPL license terms.
|
||||||
|
#include "../libc/syscall.h"
|
||||||
|
#include "../libc/stdlib.h"
|
||||||
|
#include "../libc/string.h"
|
||||||
|
#include "../libc/stdio.h"
|
||||||
|
|
||||||
|
static int sc_strcmp(const char *a, const char *b) {
|
||||||
|
while (*a && *a == *b) { a++; b++; }
|
||||||
|
return (unsigned char)*a - (unsigned char)*b;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
const char *devname = NULL;
|
||||||
|
const char *label = "BOREDOS";
|
||||||
|
int fat_type = 32;
|
||||||
|
|
||||||
|
for (int i = 1; i < argc; i++) {
|
||||||
|
if (sc_strcmp(argv[i], "-F") == 0 && i + 1 < argc) {
|
||||||
|
fat_type = 0;
|
||||||
|
const char *s = argv[++i];
|
||||||
|
while (*s >= '0' && *s <= '9') fat_type = fat_type * 10 + (*s++ - '0');
|
||||||
|
} else if (sc_strcmp(argv[i], "-n") == 0 && i + 1 < argc) {
|
||||||
|
label = argv[++i];
|
||||||
|
} else if (sc_strcmp(argv[i], "-h") == 0 || sc_strcmp(argv[i], "--help") == 0) {
|
||||||
|
printf("mkfs.fat [OPTIONS] /dev/DEVICE\n");
|
||||||
|
printf(" -F 32 FAT type (32 only)\n");
|
||||||
|
printf(" -n LABEL Volume label (max 11 chars, default: BOREDOS)\n");
|
||||||
|
return 0;
|
||||||
|
} else if (argv[i][0] != '-') {
|
||||||
|
devname = argv[i];
|
||||||
|
if (devname[0]=='/' && devname[1]=='d' && devname[2]=='e' && devname[3]=='v' && devname[4]=='/')
|
||||||
|
devname += 5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!devname) {
|
||||||
|
printf("Usage: mkfs.fat -F 32 [-n LABEL] /dev/DEVICE\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (fat_type != 32) {
|
||||||
|
printf("[ERROR] Only FAT32 (-F 32) is supported.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
disk_info_t d;
|
||||||
|
int found = 0;
|
||||||
|
int n = sys_disk_get_count();
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
if (sys_disk_get_info(i, &d) != 0) continue;
|
||||||
|
if (sc_strcmp(d.devname, devname) == 0) { found = 1; break; }
|
||||||
|
}
|
||||||
|
if (!found) { printf("[ERROR] Device not found: /dev/%s\n", devname); return 1; }
|
||||||
|
if (!d.is_partition) { printf("[ERROR] /dev/%s is a whole disk, not a partition.\n", devname); return 1; }
|
||||||
|
if (d.total_sectors < 65536) {
|
||||||
|
printf("[ERROR] Partition too small (< 32 MB) for FAT32.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Formatting /dev/%s as FAT32 (label: %s)...\n", devname, label);
|
||||||
|
int ret = sys_disk_mkfs_fat32(devname, label);
|
||||||
|
if (ret != 0) { printf("[ERROR] Format failed.\n"); return 1; }
|
||||||
|
printf("Done.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue