Hello everyone! I wrote a partfs translator, and I think it works quite well, although perhaps I'm just not experienced enough to spot any errors, as I'm new to operating system development. :) I wrote this translator based on the code of storeio, unionfs, and xmlfs. The translator receives a disk image and displays the disk partitions as separate files in a directory. So, if you pass a disk image with three partitions, the translator directory will contain the files device0p1, device0p2, and device0p3. Any advice or comments are welcome! Mikhail
Signed-off-by: Mikhail Karpov <[email protected]> --- Makefile | 4 + partfs/Makefile | 30 +++ partfs/netfs.c | 582 +++++++++++++++++++++++++++++++++++++++++++++++ partfs/options.c | 79 +++++++ partfs/options.h | 37 +++ partfs/partfs.c | 253 ++++++++++++++++++++ partfs/partfs.h | 66 ++++++ 7 files changed, 1051 insertions(+) create mode 100644 partfs/Makefile create mode 100644 partfs/netfs.c create mode 100644 partfs/options.c create mode 100644 partfs/options.h create mode 100644 partfs/partfs.c create mode 100644 partfs/partfs.h diff --git a/Makefile b/Makefile index c51e8c1c..3a1707e7 100644 --- a/Makefile +++ b/Makefile @@ -70,6 +70,10 @@ ifeq ($(HAVE_LIBACPICA),yes) prog-subdirs += acpi endif +ifneq ($(PARTED_LIBS),) +prog-subdirs += partfs +endif + # Other directories other-subdirs = hurd doc config release include diff --git a/partfs/Makefile b/partfs/Makefile new file mode 100644 index 00000000..c708a2c9 --- /dev/null +++ b/partfs/Makefile @@ -0,0 +1,30 @@ +# Copyright (C) 2026 Free Software Foundation +# Written by Mikhail Karpov. +# +# This file is part of the GNU Hurd. +# +# The GNU Hurd is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2, or (at +# your option) any later version. +# +# The GNU Hurd 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 +# General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with the GNU Hurd. If not, see <http://www.gnu.org/licenses/>. + +dir := partfs +makemode := server +target = partfs + +#CFLAGS += -DDEBUG +SRCS = netfs.c options.c partfs.c + +OBJS = $(SRCS:.c=.o) +HURDLIBS = fshelp iohelp netfs ports shouldbeinlibc store +LDLIBS = -lparted -lpthread + +include ../Makeconf diff --git a/partfs/netfs.c b/partfs/netfs.c new file mode 100644 index 00000000..2b2f4f43 --- /dev/null +++ b/partfs/netfs.c @@ -0,0 +1,582 @@ +/* Copyright (C) 2026 Free Software Foundation + Written by Mikhail Karpov. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the GNU Hurd. If not, see <http://www.gnu.org/licenses/>. */ + +#include <argp.h> +#include <dirent.h> +#include <pthread.h> +#include <sys/mman.h> + +#include "partfs.h" + +error_t +netfs_validate_stat (struct node *np, struct iouser *cred) +{ + return 0; +} + +error_t +netfs_attempt_chown (struct iouser *cred, struct node *np, uid_t uid, + uid_t gid) +{ + return EOPNOTSUPP; +} + +error_t +netfs_attempt_chauthor (struct iouser *cred, struct node *np, uid_t author) +{ + return EOPNOTSUPP; +} + +error_t +netfs_attempt_chmod (struct iouser *cred, struct node *np, mode_t mode) +{ + return EOPNOTSUPP; +} + +error_t +netfs_attempt_mksymlink (struct iouser *cred, struct node *np, + const char *name) +{ + return EOPNOTSUPP; +} + +error_t +netfs_attempt_mkdev (struct iouser *cred, struct node *np, mode_t type, + dev_t indexes) +{ + return EOPNOTSUPP; +} + +error_t +netfs_attempt_chflags (struct iouser *cred, struct node *np, int flags) +{ + return EOPNOTSUPP; +} + +error_t +netfs_attempt_utimes (struct iouser *cred, struct node *np, + struct timespec *atime, struct timespec *mtime) +{ + return EOPNOTSUPP; +} + +error_t +netfs_attempt_set_size (struct iouser *cred, struct node *np, loff_t size) +{ + return EOPNOTSUPP; +} + +error_t +netfs_attempt_statfs (struct iouser *cred, struct node *np, + fsys_statfsbuf_t *st) +{ + return EOPNOTSUPP; +} + +error_t +netfs_attempt_sync (struct iouser *cred, struct node *np, int wait) +{ + return 0; +} + +error_t +netfs_attempt_syncfs (struct iouser *cred, int wait) +{ + return 0; +} + +error_t +netfs_attempt_lookup (struct iouser *user, struct node *dir, + const char *name, struct node **np) +{ + debug ("netfs_attempt_lookup (user: %p, dir: %p, name: %s, np: %p)\n", + user, dir, name, np); + pthread_mutex_unlock (&dir->lock); + + if (!dir->nn->entries) + { + debug ("!dir->nn->entries\n"); + debug ("netfs_attempt_lookup end with ENOTDIR\n"); + return ENOTDIR; + } + + if (*name == '\0' || strcmp (name, ".") == 0) + { + *np = dir; + pthread_mutex_lock (&dir->lock); + netfs_nref (dir); + pthread_mutex_unlock (&dir->lock); + debug ("*name == '\\0' || strcmp (name, \".\") == 0\n"); + debug ("netfs_attempt_lookup end with 0\n"); + return 0; + } + + struct node *current_node = NULL; + for (size_t i = 0; i < partfs.last_partition_num; ++i) + { + current_node = netfs_root_node->nn->entries[i]; + + if (strcmp (name, current_node->nn->name) == 0) + break; + } + + if (current_node) + { + pthread_mutex_lock (&dir->lock); + *np = current_node; + netfs_nref (*np); + pthread_mutex_unlock (&dir->lock); + debug ("current_node->nn->name: %s\n", current_node->nn->name); + debug ("netfs_attempt_lookup end with 0\n"); + return 0; + } + + debug ("netfs_attempt_lookup end with ENOENT\n"); + return ENOENT; +} + +error_t +netfs_attempt_unlink (struct iouser *user, struct node *dir, const char *name) +{ + return EOPNOTSUPP; +} + +error_t +netfs_attempt_rename (struct iouser *user, struct node *fromdir, + const char *fromname, struct node *todir, + const char *toname, int excl) +{ + return EOPNOTSUPP; +} + +error_t +netfs_attempt_mkdir (struct iouser *user, struct node *dir, const char *name, + mode_t mode) +{ + return EOPNOTSUPP; +} + +error_t +netfs_attempt_rmdir (struct iouser *user, struct node *dir, const char *name) +{ + return EOPNOTSUPP; +} + +error_t +netfs_attempt_link (struct iouser *user, struct node *dir, struct node *file, + const char *name, int excl) +{ + return EOPNOTSUPP; +} + +/* We don't use this function, but we need to unlock the dir. */ +error_t +netfs_attempt_mkfile (struct iouser *user, struct node *dir, mode_t mode, + struct node **np) +{ + pthread_mutex_unlock (&dir->lock); + return EOPNOTSUPP; +} + +/* We don't use this function, but we need to unlock the dir. */ +error_t +netfs_attempt_create_file (struct iouser *user, struct node *dir, + const char *name, mode_t mode, struct node **np) +{ + pthread_mutex_unlock (&dir->lock); + return EOPNOTSUPP; +} + +error_t +netfs_attempt_readlink (struct iouser *user, struct node *np, char *buf) +{ + return EOPNOTSUPP; +} + +error_t +netfs_check_open_permissions (struct iouser *user, struct node *np, + int flags, int newnode) +{ + error_t err = 0; + + if (!err && (flags & O_READ)) + err = fshelp_access (&np->nn_stat, S_IREAD, user); + if (!err && (flags & O_WRITE)) + err = fshelp_access (&np->nn_stat, S_IWRITE, user); + if (!err && (flags & O_EXEC)) + err = fshelp_access (&np->nn_stat, S_IEXEC, user); + + return err; +} + +/* We don't use this function, but it has to be defined. */ +error_t +netfs_attempt_read (struct iouser *cred, struct node *np, loff_t offset, + size_t *len, void *data) +{ + return EOPNOTSUPP; +} + +/* We don't use this function, but it has to be defined. */ +error_t +netfs_attempt_write (struct iouser *cred, struct node *np, loff_t offset, + size_t *len, const void *data) +{ + return EOPNOTSUPP; +} + +error_t +netfs_report_access (struct iouser *cred, struct node *np, int *types) +{ + return EOPNOTSUPP; +} + +struct iouser * +netfs_make_user (uid_t *uids, int nuids, uid_t *gids, int ngids) +{ + return NULL; +} + +void +netfs_node_norefs (struct node *np) +{ + return; +} + +/* Returned directory entries are aligned to blocks this many bytes long. + Must be a power of two. */ +#define DIRENT_ALIGN 4 +#define DIRENT_NAME_OFFS offsetof (struct dirent, d_name) + +/* Length is structure before the name + the name + '\0', all + padded to a four-byte alignment. */ +#define DIRENT_LEN(name_len) \ + ((DIRENT_NAME_OFFS + (name_len) + 1 + (DIRENT_ALIGN - 1)) \ + & ~(DIRENT_ALIGN - 1)) + +static inline int +bump_size (size_t *size, int *count, const char *name, const int nentries, + const vm_size_t buffsize) +{ + if (nentries == -1 || *count < nentries) + { + size_t new_size = *size + DIRENT_LEN (strlen (name)); + if (buffsize > 0 && new_size > buffsize) + return 0; + + *size = new_size; + *count += 1; + return 1; + } + + return 0; +} + +static inline int +add_dir_entry (char **data, const char *name, const ino_t fileno, + const int type, int *count, const int nentries, size_t *size) +{ + if (nentries == -1 || *count < nentries) + { + struct dirent hdr; + size_t namlen = strlen (name); + size_t sz = DIRENT_LEN (namlen); + + if (sz > *size) + return 0; + + *size -= sz; + + hdr.d_fileno = fileno; + hdr.d_reclen = sz; + hdr.d_type = type; + hdr.d_namlen = namlen; + + memcpy (*data, &hdr, DIRENT_NAME_OFFS); + strcpy (*data + DIRENT_NAME_OFFS, name); + + *data += sz; + *count += 1; + + return 1; + } + + return 0; +} + +error_t +netfs_get_dirents (struct iouser *cred, struct node *dir, int entry, + int nentries, char **data, mach_msg_type_number_t *datacnt, + vm_size_t bufsize, int *amt) +{ + debug ("netfs_get_dirents (cred: %p, dir: %p, entry: %d, nentries: %d, " + "datacnt: %u, bufsize: %u, amt: %d)\n", cred, dir, entry, nentries, + *datacnt, bufsize, *amt); + + if (!dir->nn->entries) + { + debug ("!dir->nn->entries\n"); + debug ("netfs_attempt_lookup end with ENOTDIR\n"); + return ENOTDIR; + } + + if (partfs.last_partition_num + 2 <= entry) + { + *datacnt = 0; + *amt = 0; + *data = NULL; + debug ("partfs.last_partition_num + 2 <= entry\n"); + debug ("netfs_get_dirents end with 0\n"); + return 0; + } + + int count = 0; + size_t size = 0; + + if (entry == 0) + bump_size (&size, &count, ".", nentries, bufsize); + if (entry <= 1) + bump_size (&size, &count, "..", nentries, bufsize); + + struct node *current_node; + for (size_t i = 0; i < partfs.last_partition_num; ++i) + { + current_node = netfs_root_node->nn->entries[i]; + bump_size (&size, &count, current_node->nn->name, nentries, bufsize); + } + + *data = mmap (0, size, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0); + + error_t err = 0; + if ((void *) *data == (void *) -1) + err = errno; + + if (!err) + { + *datacnt = size; + *amt = count; + + count = 0; + char *ptr_data = *data; + + if (entry == 0) + { + debug ("entry == 0\n"); + add_dir_entry (&ptr_data, ".", dir->nn_stat.st_ino, DT_DIR, &count, + nentries, &size); + } + if (entry <= 1) + { + debug ("entry <= 1\n"); + add_dir_entry (&ptr_data, "..", 2, DT_DIR, &count, nentries, &size); + } + + debug ("Fill in the real directory entries\n"); + for (size_t i = 0; i < partfs.last_partition_num; ++i) + { + current_node = netfs_root_node->nn->entries[i]; + add_dir_entry (&ptr_data, current_node->nn->name, + current_node->nn_stat.st_ino, DT_REG, &count, + nentries, &size); + } + } + + debug ("netfs_get_dirents final end with err = %d\n", err); + return err; +} + +static inline error_t +check_offset_and_len (loff_t offset, store_offset_t store_size, size_t *len) +{ + if (offset < 0 || offset > store_size) + return EINVAL; + + if (offset + *len > store_size) + *len = store_size - offset; + + return 0; +} + +static error_t +attempt_read (struct netnode *netnode, loff_t offset, size_t *len, + vm_size_t *amount, void **data) +{ + debug ("attempt_read (netnode: %p, offset: %lld, len: %zu, amount: %zu)\n", + netnode, offset, *len, *amount); + + struct store *store = netnode->store; + + if (store->size > 0 && offset == store->size) + { + *len = 0; + debug ("if (store->size > 0 && offset == store->size)\n"); + debug ("attempt_read end with 0\n"); + return 0; + } + + error_t err = check_offset_and_len (offset, store->size, len); + if (err) + { + debug ("err in check_offset_and_len\n"); + debug ("attempt_read end with err = %d\n", err); + return err; + } + + off_t addr = offset >> store->log2_block_size; + pthread_rwlock_rdlock (&netnode->io_lock); + err = store_read (store, addr, *len, data, amount); + pthread_rwlock_unlock (&netnode->io_lock); + + debug ("attempt_read end with err = %d\n", err); + return err; +} + +kern_return_t +netfs_S_io_read (struct protid *user, data_t *data, + mach_msg_type_number_t *datalen, off_t offset, + vm_size_t amount) +{ + debug ("netfs_S_io_read:\n"); + + if (!user) + { + debug ("!user\n"); + debug ("netfs_S_io_read end with EOPNOTSUPP\n"); + return EOPNOTSUPP; + } + + struct node *node = user->po->np; + pthread_mutex_lock (&user->po->np->lock); + + if ((user->po->openstat & O_READ) == 0) + { + pthread_mutex_unlock (&node->lock); + debug ("(user->po->openstat & O_READ) == 0\n"); + debug ("netfs_S_io_read end with EBADF"); + return EBADF; + } + + int alloced = 0; + size_t data_size = *datalen; + if (amount > data_size) + { + alloced = 1; + *data = mmap (0, amount, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0); + } + data_size = amount; + + off_t start = (offset == -1 ? user->po->filepointer : offset); + error_t err; + err = attempt_read (node->nn, start, &data_size, &amount, (void **)data); + + if (offset == -1 && !err) + user->po->filepointer += data_size; + + pthread_mutex_unlock (&node->lock); + + if (err && alloced) + munmap (*data, amount); + + if (!err && alloced && (round_page (data_size) < round_page (amount))) + munmap (*data + round_page (data_size), + round_page (amount) - round_page (data_size)); + + *datalen = data_size; + debug ("netfs_S_io_read end with err = %d\n", err); + return err; +} + +static error_t +attempt_write (struct netnode *netnode, loff_t offset, size_t len, + vm_size_t *amount, const void *data) +{ + debug ("attempt_write (netnode: %p, offset: %lld, len: %zu amount: %zu):\n", + netnode, offset, len, *amount); + + struct store *store = netnode->store; + + error_t err = check_offset_and_len (offset, store->size, &len); + if (err) + { + debug ("err in check_offset_and_len\n"); + debug ("attempt_write end with err = %d\n", err); + return err; + } + + off_t addr = offset >> store->log2_block_size; + pthread_rwlock_rdlock (&netnode->io_lock); + err = store_write (store, addr, data, len, amount); + pthread_rwlock_unlock (&netnode->io_lock); + + debug ("attempt_write end with err = %d\n", err); + return err; +} + +kern_return_t +netfs_S_io_write (struct protid *user, const_data_t data, + mach_msg_type_number_t datalen, off_t offset, + vm_size_t *amount) +{ + debug ("netfs_S_io_write:\n"); + + if (!user) + { + debug ("!user\n"); + debug ("netfs_S_io_write end with EOPNOTSUPP\n"); + return EOPNOTSUPP; + } + + if ((user->po->openstat & O_WRITE) == 0) + { + debug ("(user->po->openstat & O_WRITE) == 0\n"); + debug ("netfs_S_io_write end with EBADF\n"); + return EBADF; + } + + *amount = datalen; + + struct node *np = user->po->np; + pthread_mutex_lock (&np->lock); + + off_t start = offset; + error_t err; + if (start == -1) + { + if (user->po->openstat & O_APPEND) + { + err = netfs_validate_stat (np, user->user); + if (err) + { + pthread_mutex_unlock (&np->lock); + debug ("err in netfs_validate_stat\n"); + debug ("netfs_S_io_write end with err = %d\n", err); + return err; + } + user->po->filepointer = np->nn_stat.st_size; + } + start = user->po->filepointer; + } + + err = attempt_write (np->nn, start, datalen, amount, (const void *)data); + if (offset == -1 && !err) + user->po->filepointer += *amount; + pthread_mutex_unlock (&np->lock); + + debug ("netfs_S_io_write end with err = %d\n", err); + return err; +} diff --git a/partfs/options.c b/partfs/options.c new file mode 100644 index 00000000..43e8f6c4 --- /dev/null +++ b/partfs/options.c @@ -0,0 +1,79 @@ +/* Copyright (C) 2026 Free Software Foundation + Written by Mikhail Karpov. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the GNU Hurd. If not, see <http://www.gnu.org/licenses/>. */ + +#include <error.h> +#include <string.h> +#include <unistd.h> + +#include "options.h" + +const struct argp_option options[] = +{ +#ifdef DEBUG + {"debug", 'd', "FILE", 0, + "Enable debug and write debug statements to FILE. The FILE must be " + "located outside the translator directory."}, +#endif + {"device-name", 'n', "NAME", 0, "Set the device NAME for files " + "provided by the translator."}, + {0} +}; + +error_t +parse_opt (int key, char *arg, struct argp_state *state) +{ + struct arguments *arguments = state->input; + + switch (key) + { +#ifdef DEBUG + case 'd': + arguments->debug_file_name = arg; + break; +#endif + case 'n': + arguments->device_name = arg; + break; + case ARGP_KEY_ARG: + if (state->arg_num == 0) + arguments->source_file_name = arg; + else + return ARGP_ERR_UNKNOWN; + break; + case ARGP_KEY_END: + if (!arguments->source_file_name) + error (1, 0, "No disk image file specified\n"); + if (!arguments->device_name) + arguments->device_name = "device0"; + break; + case ARGP_KEY_INIT: +#ifdef DEBUG + arguments->debug_file_name = NULL; +#endif + arguments->device_name = NULL; + arguments->source_file_name = NULL; + break; + case ARGP_KEY_SUCCESS: + case ARGP_KEY_ERROR: + break; + default: + return ARGP_ERR_UNKNOWN; + } + + return 0; +} diff --git a/partfs/options.h b/partfs/options.h new file mode 100644 index 00000000..240ef17f --- /dev/null +++ b/partfs/options.h @@ -0,0 +1,37 @@ +/* Copyright (C) 2026 Free Software Foundation + Written by Mikhail Karpov. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the GNU Hurd. If not, see <http://www.gnu.org/licenses/>. */ + +#ifndef PARTFS_OPTIONS_H +#define PARTFS_OPTIONS_H + +#include <argp.h> + +struct arguments +{ +#ifdef DEBUG + char *debug_file_name; +#endif + char *device_name; + char *source_file_name; +}; + +extern const struct argp_option options[]; + +error_t parse_opt (int key, char *arg, struct argp_state *state); + +#endif /* PARTFS_OPTIONS_H */ diff --git a/partfs/partfs.c b/partfs/partfs.c new file mode 100644 index 00000000..7346c016 --- /dev/null +++ b/partfs/partfs.c @@ -0,0 +1,253 @@ +/* Copyright (C) 2026 Free Software Foundation + Written by Mikhail Karpov. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the GNU Hurd. If not, see <http://www.gnu.org/licenses/>. */ + +#include <error.h> +#include <fcntl.h> +#include <parted/parted.h> +#include <pthread.h> +#include <stddef.h> +#include <stdio.h> +#include <string.h> +#include <sys/types.h> + +#include <version.h> + +#include "options.h" +#include "partfs.h" + +char *netfs_server_name = "partfs"; +char *netfs_server_version = HURD_VERSION; +int netfs_maxsymlinks = 0; /* arbitrary */ + +const char *argp_program_version = STANDARD_HURD_VERSION (partfs); + +struct partfs partfs; + +static error_t +set_last_partition_num (const char *source_file_name, + size_t *last_partition_num) +{ + ped_exception_fetch_all (); + PedDevice *device = ped_device_get (source_file_name); + if (!device || !ped_device_open (device)) + return 1; + + PedDisk *disk = ped_disk_new (device); + if (!disk) + { + ped_device_close (device); + return 1; + } + + error_t err = 0; + *last_partition_num = ped_disk_get_last_partition_num (disk); + if (*last_partition_num < 0) + err = 1; + + ped_disk_destroy (disk); + ped_device_close (device); + + return err; +} + +static inline char * +create_node_name (const char *device_name, unsigned long part_num) +{ + if (!device_name) + return NULL; + + char buffer[300]; + memset (buffer, 0, sizeof (buffer)); + strcat (buffer, device_name); + strcat (buffer, "p"); + char num_buffer[20]; + memset (num_buffer, 0, sizeof (num_buffer)); + snprintf (num_buffer, sizeof (num_buffer), "%lu", part_num); + strcat (buffer, num_buffer); + + return strdup (buffer); +} + +static error_t +create_node (struct node **node, const struct partfs *partfs, + struct node *dir, const char *device_name, + const unsigned long part_num, struct store *store) +{ + debug ("create_node:\n"); + struct netnode *netnode = malloc (sizeof (struct netnode)); + if (!netnode) + return ENOMEM; + + struct node *new_node = netfs_make_node (netnode); + if (!new_node) + { + free (netnode); + return ENOMEM; + } + + static ino_t id = 1; + io_statbuf_t statbuf = { + .st_fstype = FSTYPE_MISC, + .st_fsid = partfs->pid, + .st_dev = partfs->pid, + .st_rdev = partfs->pid, + .st_uid = partfs->uid, + .st_author = partfs->uid, + .st_gid = partfs->gid, + .st_mode = partfs->mode, + .st_ino = id++, + .st_nlink = 1, + .st_blksize = 1, + .st_blocks = 1, + .st_gen = 0 + }; + new_node->nn_stat = statbuf; + new_node->next = NULL; + new_node->prevp = NULL; + pthread_rwlock_init (&new_node->nn->io_lock, NULL); + new_node->nn->name = create_node_name (device_name, part_num); + new_node->nn->store = store; + new_node->nn->entries = NULL; + + if (dir) + { + netfs_nref (dir); + new_node->nn_stat.st_size = store->size; + + if (store->block_size == 1) + new_node->nn_stat.st_mode |= S_IFCHR; + else if (store->block_size > 1) + { + new_node->nn_stat.st_mode |= S_IFBLK; + new_node->nn_stat.st_blksize = store->block_size; + } + } + else + new_node->nn_stat.st_size = 0; + + fshelp_touch (&new_node->nn_stat, TOUCH_ATIME|TOUCH_CTIME|TOUCH_MTIME, + partfs->current_time); + + *node = new_node; + + debug ("new_node: %p\n", new_node); + debug ("new_node->name: %s\n", new_node->nn->name); + debug ("create_node end\n"); + return 0; +} + +static error_t +create_partfs (const struct arguments *arguments, struct partfs *partfs) +{ + debug ("create_partfs:\n"); + error_t err = set_last_partition_num(arguments->source_file_name, + &partfs->last_partition_num); + if (err) + return err; + + partfs->pid = getpid(); + partfs->uid = getuid(); + partfs->gid = getgid(); + err = maptime_map (0, 0, &partfs->current_time); + if (err) + return err; + + partfs->mode = 0644; + + netfs_root_node = NULL; + err = create_node (&netfs_root_node, partfs, NULL, NULL, 0, NULL); + if (err) + return err; + + netfs_root_node->nn_stat.st_nlink = 2; + netfs_root_node->nn->entries = malloc (partfs->last_partition_num + * sizeof (struct node *)); + debug ("netfs_root_node: %p\n", netfs_root_node); + + struct store *source, *store; + for (size_t i = 1; i <= partfs->last_partition_num; ++i) + { + err = store_file_open (arguments->source_file_name, 0, &source); + if (err) + return err; + + err = store_part_create (source, i, 0, &store); + if (err) + return err; + + create_node (&netfs_root_node->nn->entries[i - 1], partfs, + netfs_root_node, arguments->device_name, i, store); + } + + debug ("create_partfs end\n"); + return 0; +} + +#ifdef DEBUG +FILE *debug_file = NULL; +pthread_mutex_t debug_lock = PTHREAD_MUTEX_INITIALIZER; +#endif + +static const char argp_doc[] = "PARTFS-DOC"; +static const char doc[] = + "A translator for obtaining disk partitions using parted."; + +int +main (int argc, char *argv[]) +{ + struct arguments arguments; + struct argp argp = {options, parse_opt, argp_doc, doc}; + argp_parse (&argp, argc, argv, 0, 0, &arguments); + + mach_port_t bootstrap; + task_get_bootstrap_port (mach_task_self (), &bootstrap); + + netfs_init (); + + mach_port_t underlying_node = netfs_startup (bootstrap, O_READ); + io_statbuf_t underlying_stat; + + error_t err = io_stat (underlying_node, &underlying_stat); + if (err) + error (1, err, "Cannot stat underlying node"); + +#ifdef DEBUG + if (arguments.debug_file_name) + { + debug_file = fopen (arguments.debug_file_name, "w"); + setbuf (debug_file, NULL); + } +#endif + + debug ("\n---------------start main---------------\n"); + debug ("device_name: %s\n", arguments.device_name); + debug ("source_file_name: %s\n", arguments.source_file_name); + + err = create_partfs (&arguments, &partfs); + if (err) + error (1, err, "Cannot creare partfs"); + + netfs_root_node->nn_stat = underlying_stat; + netfs_root_node->nn_stat.st_mode = + S_IFDIR | (underlying_stat.st_mode & ~S_IFMT & ~S_ITRANS); + + debug ("netfs_server_loop()...\n"); + netfs_server_loop (); + + return 0; +} diff --git a/partfs/partfs.h b/partfs/partfs.h new file mode 100644 index 00000000..8d18723b --- /dev/null +++ b/partfs/partfs.h @@ -0,0 +1,66 @@ +/* Copyright (C) 2026 Free Software Foundation + Written by Mikhail Karpov. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the GNU Hurd. If not, see <http://www.gnu.org/licenses/>. */ + +#ifndef PARTFS_PARTFS_H +#define PARTFS_PARTFS_H + +#include <hurd/store.h> +#include <hurd/netfs.h> +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +struct netnode +{ + char *name; + struct store *store; + pthread_rwlock_t io_lock; + struct node **entries; +}; + +struct partfs +{ + pid_t pid; + uid_t uid; + gid_t gid; + volatile struct mapped_time_value *current_time; + size_t last_partition_num; + mode_t mode; +}; + +extern struct partfs partfs; + +#ifdef DEBUG +extern FILE *debug_file; +extern pthread_mutex_t debug_lock; +# define debug(format, ...) \ + do \ + { \ + if (debug_file) \ + { \ + pthread_mutex_lock (&debug_lock); \ + fprintf (debug_file, format, ## __VA_ARGS__); \ + pthread_mutex_unlock (&debug_lock); \ + } \ + } \ + while (0) +#else +# define debug(format, ...) do {} while (0) +#endif + +#endif /* PARTFS_PARTFS_H */ -- 2.43.0
