>From 22758da0b147980c11dbd456f7a4e00404261212 Mon Sep 17 00:00:00 2001 From: Damien Zammit Date: Tue, 6 Nov 2018 02:53:36 -0500 Subject: [PATCH 1/3] ACPI tables translator Exposes x86 ACPI tables as a netfs on a mount point --- Makefile | 3 +- acpi/Makefile | 35 ++++ acpi/acpi.c | 290 +++++++++++++++++++++++++ acpi/acpi.h | 74 +++++++ acpi/acpifs.c | 265 +++++++++++++++++++++++ acpi/acpifs.h | 147 +++++++++++++ acpi/func_files.c | 76 +++++++ acpi/func_files.h | 39 ++++ acpi/main.c | 100 +++++++++ acpi/ncache.c | 90 ++++++++ acpi/ncache.h | 32 +++ acpi/netfs_impl.c | 525 ++++++++++++++++++++++++++++++++++++++++++++++ acpi/netfs_impl.h | 43 ++++ acpi/options.c | 149 +++++++++++++ acpi/options.h | 51 +++++ hurd/hurd_types.h | 1 + 16 files changed, 1919 insertions(+), 1 deletion(-) create mode 100644 acpi/Makefile create mode 100644 acpi/acpi.c create mode 100644 acpi/acpi.h create mode 100644 acpi/acpifs.c create mode 100644 acpi/acpifs.h create mode 100644 acpi/func_files.c create mode 100644 acpi/func_files.h create mode 100644 acpi/main.c create mode 100644 acpi/ncache.c create mode 100644 acpi/ncache.h create mode 100644 acpi/netfs_impl.c create mode 100644 acpi/netfs_impl.h create mode 100644 acpi/options.c create mode 100644 acpi/options.h diff --git a/Makefile b/Makefile index 6288a157..aa4ddc5a 100644 --- a/Makefile +++ b/Makefile @@ -45,7 +45,8 @@ prog-subdirs = auth proc exec term \ init \ devnode \ eth-multiplexer \ - pci-arbiter + pci-arbiter \ + acpi ifeq ($(HAVE_SUN_RPC),yes) prog-subdirs += nfs nfsd diff --git a/acpi/Makefile b/acpi/Makefile new file mode 100644 index 00000000..b3b7407b --- /dev/null +++ b/acpi/Makefile @@ -0,0 +1,35 @@ +# Copyright (C) 2018 Free Software Foundation, Inc. +# +# 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 = acpi +makemode = server + +PORTDIR = $(srcdir)/port + +SRCS = main.c netfs_impl.c acpi.c \ + acpifs.c ncache.c options.c func_files.c +MIGSRCS = +OBJS = $(patsubst %.S,%.o,$(patsubst %.c,%.o, $(SRCS) $(MIGSRCS))) + +HURDLIBS= fshelp ports shouldbeinlibc netfs +LDLIBS = -lpthread + +target = acpi + +include ../Makeconf + +CFLAGS += -I$(PORTDIR)/include diff --git a/acpi/acpi.c b/acpi/acpi.c new file mode 100644 index 00000000..63066aaf --- /dev/null +++ b/acpi/acpi.c @@ -0,0 +1,290 @@ +/* + Copyright (C) 2018 Free Software Foundation, Inc. + + 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "acpi.h" + +int +mmap_phys_acpi_header(uintptr_t base_addr, struct acpi_header **ptr_to_header, + void **virt_addr, int fd) +{ + /* The memory mapping must be done aligned to page size + * but we have a known physical address we want to inspect, + * therefore we must compute offsets. + */ + uintptr_t pa_acpi = base_addr & ~(sysconf(_SC_PAGE_SIZE) - 1); + uintptr_t pa_start = base_addr - pa_acpi; + + /* Map the ACPI table at the nearest page (rounded down) */ + *virt_addr = 0; + *virt_addr = mmap(NULL, ESCD_SIZE, PROT_READ, MAP_SHARED | MAP_FIXED, + fd, (off_t) pa_acpi); + + if (*virt_addr == MAP_FAILED) + return errno; + + /* Fabricate a pointer to our magic address */ + *ptr_to_header = (struct acpi_header *)(*virt_addr + pa_start); + + return 0; +} + +int +acpi_get_num_tables(size_t *num_tables) +{ + int fd_mem; + int err; + void *virt_addr, *virt_addr2; + bool found = false; + struct rsdp_descr2 rsdp = { 0 }; + uintptr_t sdt_base = (uintptr_t)0; + bool is_64bit = false; + unsigned char *buf; + struct acpi_header *root_sdt; + struct acpi_header *next; + + if ((fd_mem = open("/dev/mem", O_RDWR)) < 0) + return EPERM; + + virt_addr = mmap(NULL, ESCD_SIZE, PROT_READ, + MAP_SHARED | MAP_FIXED, fd_mem, ESCD); + if (virt_addr == MAP_FAILED) + return errno; + + buf = (unsigned char *)virt_addr; + found = false; + + /* RSDP magic string is 16 byte aligned */ + for (int i = 0; i < ESCD_SIZE; i += 16) + { + if (!memcmp(&buf[i], RSDP_MAGIC, 8)) { + rsdp = *((struct rsdp_descr2 *)(&buf[i])); + found = true; + break; + } + } + + if (!found) { + munmap(virt_addr, ESCD_SIZE); + return ENODEV; + } + + if (rsdp.v1.revision == 0) { + // ACPI 1.0 + sdt_base = rsdp.v1.rsdt_addr; + is_64bit = false; + } else if (rsdp.v1.revision == 2) { + // ACPI >= 2.0 + sdt_base = rsdp.xsdt_addr; + is_64bit = true; + } else { + munmap(virt_addr, ESCD_SIZE); + return ENODEV; + } + + munmap(virt_addr, ESCD_SIZE); + + /* Now we have the sdt_base address and knowledge of 32/64 bit ACPI */ + + err = mmap_phys_acpi_header(sdt_base, &root_sdt, &virt_addr, fd_mem); + if (err) { + munmap(virt_addr, ESCD_SIZE); + return err; + } + + /* Get total tables */ + uint32_t ntables; + uint8_t sz_ptr; + sz_ptr = is_64bit ? 8 : 4; + ntables = (root_sdt->length - sizeof(*root_sdt)) / sz_ptr; + + /* Get pointer to first ACPI table */ + uintptr_t acpi_ptr = (uintptr_t)root_sdt + sizeof(*root_sdt); + + /* Get number of readable tables */ + *num_tables = 0; + for (int i = 0; i < ntables; i++) + { + uintptr_t acpi_ptr32 = (uintptr_t)*((uint32_t *)(acpi_ptr + i*sz_ptr)); + uintptr_t acpi_ptr64 = (uintptr_t)*((uint64_t *)(acpi_ptr + i*sz_ptr)); + if (is_64bit) { + err = mmap_phys_acpi_header(acpi_ptr64, &next, &virt_addr2, fd_mem); + } else { + err = mmap_phys_acpi_header(acpi_ptr32, &next, &virt_addr2, fd_mem); + } + + char name[5] = { 0 }; + snprintf(name, 5, "%s", &next->signature[0]); + if (next->signature[0] == '\0' || next->length == 0) { + munmap(virt_addr2, ESCD_SIZE); + continue; + } + *num_tables += 1; + munmap(virt_addr2, ESCD_SIZE); + } + + munmap(virt_addr, ESCD_SIZE); + + return 0; +} + +int +acpi_get_tables(struct acpi_table **tables) +{ + int err; + int fd_mem; + void *virt_addr, *virt_addr2; + uint32_t phys_addr = ESCD; + bool found = false; + struct rsdp_descr2 rsdp = { 0 }; + uintptr_t sdt_base = (uintptr_t)0; + bool is_64bit = false; + unsigned char *buf; + struct acpi_header *root_sdt; + struct acpi_header *next; + size_t ntables_actual; + int cur_tab = 0; + + err = acpi_get_num_tables(&ntables_actual); + if (err) + return err; + + *tables = malloc(ntables_actual * sizeof(**tables)); + if (!*tables) + return ENOMEM; + + if ((fd_mem = open("/dev/mem", O_RDWR)) < 0) + return EPERM; + + virt_addr = mmap(NULL, ESCD_SIZE, PROT_READ, MAP_SHARED | MAP_FIXED, + fd_mem, (off_t) phys_addr); + + if (virt_addr == MAP_FAILED) + return errno; + + buf = (unsigned char *)virt_addr; + found = false; + + /* RSDP magic string is 16 byte aligned */ + for (int i = 0; i < ESCD_SIZE; i += 16) + { + if (!memcmp(&buf[i], RSDP_MAGIC, 8)) { + rsdp = *((struct rsdp_descr2 *)(&buf[i])); + found = true; + break; + } + } + + if (!found) { + munmap(virt_addr, ESCD_SIZE); + return ENODEV; + } + + if (rsdp.v1.revision == 0) { + // ACPI 1.0 + sdt_base = rsdp.v1.rsdt_addr; + is_64bit = false; + } else if (rsdp.v1.revision == 2) { + // ACPI >= 2.0 + sdt_base = rsdp.xsdt_addr; + is_64bit = true; + } else { + munmap(virt_addr, ESCD_SIZE); + return ENODEV; + } + + munmap(virt_addr, ESCD_SIZE); + + /* Now we have the sdt_base address and knowledge of 32/64 bit ACPI */ + + err = mmap_phys_acpi_header(sdt_base, &root_sdt, &virt_addr, fd_mem); + if (err) { + munmap(virt_addr, ESCD_SIZE); + return err; + } + + /* Get total tables */ + uint32_t ntables; + uint8_t sz_ptr; + sz_ptr = is_64bit ? 8 : 4; + ntables = (root_sdt->length - sizeof(*root_sdt)) / sz_ptr; + + /* Get pointer to first ACPI table */ + uintptr_t acpi_ptr = (uintptr_t)root_sdt + sizeof(*root_sdt); + + /* Get all tables and data */ + for (int i = 0; i < ntables; i++) + { + uintptr_t acpi_ptr32 = (uintptr_t)*((uint32_t *)(acpi_ptr + i*sz_ptr)); + uintptr_t acpi_ptr64 = (uintptr_t)*((uint64_t *)(acpi_ptr + i*sz_ptr)); + if (is_64bit) { + err = mmap_phys_acpi_header(acpi_ptr64, &next, &virt_addr2, fd_mem); + if (err) { + munmap(virt_addr, ESCD_SIZE); + return err; + } + } else { + err = mmap_phys_acpi_header(acpi_ptr32, &next, &virt_addr2, fd_mem); + if (err) { + munmap(virt_addr, ESCD_SIZE); + return err; + } + } + + char name[5] = { 0 }; + snprintf(name, 5, "%s", &next->signature[0]); + if (next->signature[0] == '\0' || next->length == 0) { + munmap(virt_addr2, ESCD_SIZE); + continue; + } + uint32_t datalen = next->length - sizeof(*next); + void *data = (void *)((uintptr_t)next + sizeof(*next)); + + /* We now have a pointer to the data, + * its length and header. + */ + struct acpi_table *t = *tables + cur_tab; + memcpy(&t->h, next, sizeof(*next)); + t->datalen = 0; + t->data = malloc(datalen); + if (!t->data) { + munmap(virt_addr2, ESCD_SIZE); + munmap(virt_addr, ESCD_SIZE); + return ENOMEM; + } + t->datalen = datalen; + memcpy(t->data, data, datalen); + cur_tab++; + munmap(virt_addr2, ESCD_SIZE); + } + + munmap(virt_addr, ESCD_SIZE); + + return 0; +} diff --git a/acpi/acpi.h b/acpi/acpi.h new file mode 100644 index 00000000..7c21a442 --- /dev/null +++ b/acpi/acpi.h @@ -0,0 +1,74 @@ +/* + Copyright (C) 2018 Free Software Foundation, Inc. + + 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/>. +*/ + +/* ACPI tables basic structure */ + +#ifndef ACPI_H +#define ACPI_H + +#include +#include + +/* PnP Extended System Configuration Data (ESCD) memory region */ +#define ESCD 0xe0000U +#define RSDP_MAGIC (const unsigned char *)"RSD PTR " +#define ESCD_SIZE 0x20000U + +struct rsdp_descr +{ + uint8_t magic[8]; + uint8_t checksum; + uint8_t oem_id[6]; + uint8_t revision; + uint32_t rsdt_addr; +} __attribute__ ((packed)); + +struct rsdp_descr2 +{ + struct rsdp_descr v1; + uint32_t length; + uint64_t xsdt_addr; + uint8_t checksum; + uint8_t reserved[3]; +} __attribute__ ((packed)); + +struct acpi_header +{ + uint8_t signature[4]; + uint32_t length; + uint8_t revision; + uint8_t checksum; + uint8_t oem_id[6]; + uint8_t oem_table_id[8]; + uint32_t oem_revision; + uint32_t creator_id; + uint32_t creator_revision; +} __attribute__ ((packed)); + +struct acpi_table +{ + struct acpi_header h; + void *data; + size_t datalen; +} __attribute__ ((packed)); + +int acpi_get_num_tables(size_t *num_tables); +int acpi_get_tables(struct acpi_table **tables); + +#endif /* ACPI_H */ diff --git a/acpi/acpifs.c b/acpi/acpifs.c new file mode 100644 index 00000000..e779e0f9 --- /dev/null +++ b/acpi/acpifs.c @@ -0,0 +1,265 @@ +/* + Copyright (C) 2018 Free Software Foundation, Inc. + + 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/>. +*/ + +/* ACPI Filesystem implementation */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +static error_t +create_dir_entry (char *name, struct acpi_table *t, + struct acpifs_dirent *parent, io_statbuf_t stat, + struct node *node, struct acpifs_dirent *entry) +{ + uint16_t parent_num_entries; + + strncpy (entry->name, name, NAME_SIZE); + entry->acpitable = t; + entry->parent = parent; + entry->stat = stat; + entry->dir = 0; + entry->node = node; + + /* Update parent's child list */ + if (entry->parent) + { + if (!entry->parent->dir) + { + /* First child */ + entry->parent->dir = calloc (1, sizeof (struct acpifs_dir)); + if (!entry->parent->dir) + return ENOMEM; + } + + parent_num_entries = entry->parent->dir->num_entries++; + entry->parent->dir->entries = realloc (entry->parent->dir->entries, + entry->parent->dir->num_entries * + sizeof (struct acpifs_dirent *)); + if (!entry->parent->dir->entries) + return ENOMEM; + entry->parent->dir->entries[parent_num_entries] = entry; + } + + return 0; +} + +error_t +alloc_file_system (struct acpifs **fs) +{ + *fs = calloc (1, sizeof (struct acpifs)); + if (!*fs) + return ENOMEM; + + return 0; +} + +error_t +init_file_system (file_t underlying_node, struct acpifs *fs) +{ + error_t err; + struct node *np; + io_statbuf_t underlying_node_stat; + + /* Initialize status from underlying node. */ + err = io_stat (underlying_node, &underlying_node_stat); + if (err) + return err; + + np = netfs_make_node_alloc (sizeof (struct netnode)); + if (!np) + return ENOMEM; + np->nn_stat = underlying_node_stat; + np->nn_stat.st_fsid = getpid (); + np->nn_stat.st_mode = + S_IFDIR | S_IROOT | S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | + S_IXOTH; + np->nn_translated = np->nn_stat.st_mode; + + /* Set times to now */ + fshelp_touch (&np->nn_stat, TOUCH_ATIME | TOUCH_MTIME | TOUCH_CTIME, + acpifs_maptime); + + fs->entries = calloc (1, sizeof (struct acpifs_dirent)); + if (!fs->entries) + { + free (fs->entries); + return ENOMEM; + } + + /* Create the root entry */ + err = create_dir_entry ("", 0, 0, np->nn_stat, np, fs->entries); + + fs->num_entries = 1; + fs->root = netfs_root_node = np; + fs->root->nn->ln = fs->entries; + pthread_mutex_init (&fs->node_cache_lock, 0); + + return 0; +} + +error_t +create_fs_tree (struct acpifs *fs) +{ + error_t err = 0; + int i; + size_t nentries, ntables = 0; + struct acpifs_dirent *e, *list, *parent; + struct stat e_stat; + char entry_name[NAME_SIZE]; + struct acpi_table *iter = NULL; + + /* Copy the root stat */ + e_stat = fs->entries->stat; + + err = acpi_get_num_tables(&ntables); + if (err) + return err; + + /* Allocate enough for / + "tables"/ + each table */ + nentries = ntables + 2; + + list = realloc (fs->entries, nentries * sizeof (struct acpifs_dirent)); + if (!list) { + if (fs->entries) + free(fs->entries); + return ENOMEM; + } + + e = list + 1; + parent = list; + e_stat.st_mode &= ~S_IROOT; /* Remove the root mode */ + memset (entry_name, 0, NAME_SIZE); + strncpy (entry_name, DIR_TABLES_NAME, NAME_SIZE); + + err = create_dir_entry (entry_name, 0, parent, e_stat, 0, e); + if (err) + return err; + + parent = e; + + /* Remove all permissions to others */ + e_stat.st_mode &= ~(S_IROTH | S_IWOTH | S_IXOTH); + + /* Change mode to a regular read-only file */ + e_stat.st_mode &= ~(S_IFDIR | S_IXUSR | S_IXGRP | S_IWUSR | S_IWGRP); + e_stat.st_mode |= S_IFREG; + + /* Get all ACPI tables */ + err = acpi_get_tables(&iter); + if (err) + return err; + + for (i = 0; i < ntables; i++, iter++) + { + e_stat.st_size = iter->datalen; + + // Create ACPI table entry + memset (entry_name, 0, NAME_SIZE); + + snprintf (entry_name, NAME_SIZE, "%c%c%c%c", + iter->h.signature[0], + iter->h.signature[1], + iter->h.signature[2], + iter->h.signature[3]); + e++; + err = create_dir_entry (entry_name, iter, parent, e_stat, 0, e); + if (err) + return err; + } + + /* The root node points to the first element of the entry list */ + fs->entries = list; + fs->num_entries = nentries; + fs->root->nn->ln = fs->entries; + + return err; +} + +error_t +entry_check_perms (struct iouser *user, struct acpifs_dirent *e, int flags) +{ + error_t err = 0; + + if (!err && (flags & O_READ)) + err = fshelp_access (&e->stat, S_IREAD, user); + if (!err && (flags & O_WRITE)) + err = fshelp_access (&e->stat, S_IWRITE, user); + if (!err && (flags & O_EXEC)) + err = fshelp_access (&e->stat, S_IEXEC, user); + + return err; +} + +/* Set default permissions to the given entry */ +static void +entry_default_perms (struct acpifs *fs, struct acpifs_dirent *e) +{ + /* Set default owner and group */ + UPDATE_OWNER (e, fs->root->nn->ln->stat.st_uid); + UPDATE_GROUP (e, fs->root->nn->ln->stat.st_gid); + + /* Update ctime */ + UPDATE_TIMES (e, TOUCH_CTIME); + + return; +} + +static void +entry_set_perms (struct acpifs *fs, struct acpifs_dirent *e) +{ + struct acpifs_perm *perm = &fs->perm; + if (perm->uid >= 0) + UPDATE_OWNER (e, perm->uid); + if (perm->gid >= 0) + UPDATE_GROUP (e, perm->gid); + + /* Update ctime */ + UPDATE_TIMES (e, TOUCH_CTIME); + + return; +} + +/* Update all entries' permissions */ +error_t +fs_set_permissions (struct acpifs *fs) +{ + int i; + struct acpifs_dirent *e; + + for (i = 0, e = fs->entries; i < fs->num_entries; i++, e++) + { + /* Restore default perms, as this may be called from fsysopts */ + entry_default_perms (fs, e); + + /* Set new permissions, if any */ + entry_set_perms (fs, e); + } + + return 0; +} diff --git a/acpi/acpifs.h b/acpi/acpifs.h new file mode 100644 index 00000000..2e9063cc --- /dev/null +++ b/acpi/acpifs.h @@ -0,0 +1,147 @@ +/* + Copyright (C) 2018 Free Software Foundation, Inc. + + 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/>. +*/ + +/* ACPI Filesystem header */ + +#ifndef ACPIFS_H +#define ACPIFS_H + +#include +#include +#include + +#include +#include + +/* Size of a directory entry name */ +#ifndef NAME_SIZE +#define NAME_SIZE 8 +#endif + +/* Node cache defaults size */ +#define NODE_CACHE_MAX 16 + +/* + * User and group ids to grant permission to acpi + */ +struct acpifs_perm +{ + int32_t uid; + int32_t gid; +}; + +/* + * Directory entry. + * + * All directory entries are created on startup and used to generate the + * fs tree and create or retrieve libnetfs node objects. + * + * From libnetfs' point of view, these are the light nodes. + */ +struct acpifs_dirent +{ + char name[NAME_SIZE]; + struct acpifs_dirent *parent; + io_statbuf_t stat; + + /* + * We only need two kind of nodes: files and directories. + * When `dir' is null, this is a file; when not null, it's a directory. + */ + struct acpifs_dir *dir; + + /* Active node on this entry */ + struct node *node; + + /* ACPI table related to an entry */ + struct acpi_table *acpitable; +}; + +/* + * A directory, it only contains a list of directory entries + */ +struct acpifs_dir +{ + /* Number of directory entries */ + uint16_t num_entries; + + /* Array of directory entries */ + struct acpifs_dirent **entries; +}; + +/* A particular ACPI filesystem. */ +struct acpifs +{ + /* Root of filesystem. */ + struct node *root; + + /* A cache that holds a reference to recently used nodes. */ + struct node *node_cache_mru, *node_cache_lru; + size_t node_cache_len; /* Number of entries in it. */ + size_t node_cache_max; + pthread_mutex_t node_cache_lock; + + struct acpifs_perm perm; + + struct acpifs_dirent *entries; + size_t num_entries; +}; + +/* Main FS pointer */ +struct acpifs *fs; + +/* Global mapped time */ +volatile struct mapped_time_value *acpifs_maptime; + +/* Update entry and node times */ +#define UPDATE_TIMES(e, what) (\ + {\ + fshelp_touch (&e->stat, what, acpifs_maptime);\ + if(e->node)\ + fshelp_touch (&e->node->nn_stat, what, acpifs_maptime);\ + }\ +) + +/* Update entry and node owner */ +#define UPDATE_OWNER(e, uid) (\ + {\ + e->stat.st_uid = uid;\ + if(e->node)\ + e->node->nn_stat.st_uid = uid;\ + }\ +) + +/* Update entry and node group */ +#define UPDATE_GROUP(e, gid) (\ + {\ + e->stat.st_gid = gid;\ + if(e->node)\ + e->node->nn_stat.st_gid = gid;\ + }\ +) + +/* FS manipulation functions */ +error_t alloc_file_system (struct acpifs **fs); +error_t init_file_system (file_t underlying_node, struct acpifs *fs); +error_t create_fs_tree (struct acpifs *fs); +error_t fs_set_permissions (struct acpifs *fs); +error_t entry_check_perms (struct iouser *user, struct acpifs_dirent *e, + int flags); + +#endif /* ACPIFS_H */ diff --git a/acpi/func_files.c b/acpi/func_files.c new file mode 100644 index 00000000..371f989a --- /dev/null +++ b/acpi/func_files.c @@ -0,0 +1,76 @@ +/* + Copyright (C) 2017 Free Software Foundation, Inc. + + 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/>. +*/ + +/* + * Per-function files implementation. + * + * Implementation of all files repeated for each function. + */ + +#include +#include + +/* Read an acpi table */ +error_t +io_acpi_table (struct acpi_table *t, off_t offset, size_t *len, void *data) +{ + error_t err; + size_t datalen; + + /* This should never happen */ + assert_backtrace (t != 0); + + datalen = t->datalen; + + /* Don't exceed the size of the acpi table */ + if (offset > datalen) + return EINVAL; + if ((offset + *len) > datalen) + *len = datalen - offset; + + memcpy (data, t->data + offset, *len); + + return err; +} + +/* Read from an acpi table file */ +error_t +io_acpi_file (struct acpifs_dirent *e, off_t offset, size_t *len, + void *data) +{ + size_t datalen; + struct acpi_table *table; + + /* This should never happen */ + assert_backtrace (e->acpitable != 0); + + /* Get the table */ + table = e->acpitable; + + datalen = table->datalen; + /* Don't exceed the region size */ + if (offset > datalen) + return EINVAL; + if ((offset + *len) > datalen) + *len = datalen - offset; + + memcpy (data, table->data + offset, *len); + + return 0; +} diff --git a/acpi/func_files.h b/acpi/func_files.h new file mode 100644 index 00000000..90d92cc3 --- /dev/null +++ b/acpi/func_files.h @@ -0,0 +1,39 @@ +/* + Copyright (C) 2018 Free Software Foundation, Inc. + + 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/>. +*/ + +/* Per-function files header */ + +#ifndef FUNC_FILES_H +#define FUNC_FILES_H + +#include +#include + +typedef int (*acpi_read_op_t) (struct acpi_table *t, void *data, + off_t offset, size_t *len); + +/* Tables */ +#define DIR_TABLES_NAME "tables" + +error_t io_read_table (struct acpi_table *t, struct acpifs_dirent *e, + off_t offset, size_t *len, void *data); +error_t io_acpi_file (struct acpifs_dirent *e, off_t offset, size_t *len, + void *data); + +#endif /* FUNC_FILES_H */ diff --git a/acpi/main.c b/acpi/main.c new file mode 100644 index 00000000..f675470c --- /dev/null +++ b/acpi/main.c @@ -0,0 +1,100 @@ +/* + Copyright (C) 2017 Free Software Foundation, Inc. + + 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/>. +*/ + +/* Translator initialization and demuxing */ + +#include +#include +#include +#include +#include +#include + +#include "libnetfs/io_S.h" +#include "libnetfs/fs_S.h" +#include "libports/notify_S.h" +#include "libnetfs/fsys_S.h" +#include "libports/interrupt_S.h" +#include "libnetfs/ifsock_S.h" +#include + +/* Libnetfs stuff */ +int netfs_maxsymlinks = 0; +char *netfs_server_name = "acpi"; +char *netfs_server_version = HURD_VERSION; + +int +netfs_demuxer (mach_msg_header_t * inp, mach_msg_header_t * outp) +{ + mig_routine_t routine; + + if ((routine = netfs_io_server_routine (inp)) || + (routine = netfs_fs_server_routine (inp)) || + (routine = ports_notify_server_routine (inp)) || + (routine = netfs_fsys_server_routine (inp)) || + (routine = ports_interrupt_server_routine (inp)) || + (routine = netfs_ifsock_server_routine (inp))) + { + (*routine) (inp, outp); + return TRUE; + } + else + return FALSE; +} + +int +main (int argc, char **argv) +{ + error_t err; + mach_port_t bootstrap; + + /* Parse options */ + alloc_file_system (&fs); + argp_parse (netfs_runtime_argp, argc, argv, 0, 0, 0); + + task_get_bootstrap_port (mach_task_self (), &bootstrap); + if (bootstrap == MACH_PORT_NULL) + error (1, 0, "must be started as a translator"); + + /* Initialize netfs and start the translator. */ + netfs_init (); + + err = maptime_map (0, 0, &acpifs_maptime); + if (err) + error (1, err, "mapping time"); + + /* Create the ACPI filesystem */ + err = init_file_system (netfs_startup (bootstrap, O_READ), fs); + if (err) + error (1, err, "creating the ACPI filesystem"); + + /* Create the filesystem tree */ + err = create_fs_tree (fs); + if (err) + error (1, err, "creating the ACPI filesystem tree"); + + /* Set permissions */ + err = fs_set_permissions (fs); + if (err) + error (1, err, "setting permissions"); + + netfs_server_loop (); /* Never returns. */ + + return 0; +} diff --git a/acpi/ncache.c b/acpi/ncache.c new file mode 100644 index 00000000..61bc6592 --- /dev/null +++ b/acpi/ncache.c @@ -0,0 +1,90 @@ +/* Node caching + + Copyright (C) 1997 Free Software Foundation, Inc. + Written by Miles Bader + 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 this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#include + +#include +#include +#include + +#include +#include + +/* Implementation of node caching functions */ + +/* Remove NN's node from its position in FS's node cache. */ +void +node_unlink (struct node *node, struct acpifs *fs) +{ + struct netnode *nn = node->nn; + if (nn->ncache_next) + nn->ncache_next->nn->ncache_prev = nn->ncache_prev; + if (nn->ncache_prev) + nn->ncache_prev->nn->ncache_next = nn->ncache_next; + if (fs->node_cache_mru == node) + fs->node_cache_mru = nn->ncache_next; + if (fs->node_cache_lru == node) + fs->node_cache_lru = nn->ncache_prev; + nn->ncache_next = 0; + nn->ncache_prev = 0; + fs->node_cache_len--; +} + +/* Add NODE to the recently-used-node cache, which adds a reference to + prevent it from going away. NODE should be locked. */ +void +node_cache (struct node *node) +{ + struct netnode *nn = node->nn; + + pthread_mutex_lock (&fs->node_cache_lock); + + if (fs->node_cache_max > 0 || fs->node_cache_len > 0) + { + if (fs->node_cache_mru != node) + { + if (nn->ncache_next || nn->ncache_prev) + /* Node is already in the cache. */ + node_unlink (node, fs); + else + /* Add a reference from the cache. */ + netfs_nref (node); + + nn->ncache_next = fs->node_cache_mru; + nn->ncache_prev = 0; + if (fs->node_cache_mru) + fs->node_cache_mru->nn->ncache_prev = node; + if (!fs->node_cache_lru) + fs->node_cache_lru = node; + fs->node_cache_mru = node; + fs->node_cache_len++; + } + + /* Forget the least used nodes. */ + while (fs->node_cache_len > fs->node_cache_max) + { + struct node *lru = fs->node_cache_lru; + node_unlink (lru, fs); + netfs_nrele (lru); + } + } + + pthread_mutex_unlock (&fs->node_cache_lock); +} diff --git a/acpi/ncache.h b/acpi/ncache.h new file mode 100644 index 00000000..b4fa430b --- /dev/null +++ b/acpi/ncache.h @@ -0,0 +1,32 @@ +/* + Copyright (C) 2017 Free Software Foundation, Inc. + + 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/>. +*/ + +/* Header for node caching functions */ + +#ifndef NCACHE_H +#define NCACHE_H + +#include + +#include + +void node_cache (struct node *node); +void node_unlink (struct node *node, struct acpifs *fs); + +#endif /* NCACHE_H */ diff --git a/acpi/netfs_impl.c b/acpi/netfs_impl.c new file mode 100644 index 00000000..84f52c89 --- /dev/null +++ b/acpi/netfs_impl.c @@ -0,0 +1,525 @@ +/* + Copyright (C) 2017 Free Software Foundation, Inc. + Written by Miles Bader + 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/>. +*/ + +/* Libnetfs callbacks */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define DIRENTS_CHUNK_SIZE (8*1024) +/* 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)) + +/* Fetch a directory, as for netfs_get_dirents. */ +static error_t +get_dirents (struct acpifs_dirent *dir, + int first_entry, int max_entries, char **data, + mach_msg_type_number_t *data_len, + vm_size_t max_data_len, int *data_entries) +{ + struct acpifs_dirent *e; + error_t err = 0; + int i, count; + size_t size; + char *p; + + if (first_entry >= dir->dir->num_entries) + { + *data_len = 0; + *data_entries = 0; + return 0; + } + + if (max_entries < 0) + count = dir->dir->num_entries; + else + { + count = ((first_entry + max_entries) >= dir->dir->num_entries ? + dir->dir->num_entries : max_entries) - first_entry; + } + + size = + (count * DIRENTS_CHUNK_SIZE) > + max_data_len ? max_data_len : count * DIRENTS_CHUNK_SIZE; + + *data = mmap (0, size, PROT_READ | PROT_WRITE, MAP_ANON, 0, 0); + err = ((void *) *data == (void *) -1) ? errno : 0; + if (err) + return err; + + p = *data; + for (i = 0; i < count; i++) + { + struct dirent hdr; + size_t name_len; + size_t sz; + int entry_type; + + e = dir->dir->entries[i + first_entry]; + name_len = strlen (e->name) + 1; + sz = DIRENT_LEN (name_len); + entry_type = IFTODT (e->stat.st_mode); + + hdr.d_namlen = name_len; + hdr.d_fileno = e->stat.st_ino; + hdr.d_reclen = sz; + hdr.d_type = entry_type; + + memcpy (p, &hdr, DIRENT_NAME_OFFS); + strncpy (p + DIRENT_NAME_OFFS, e->name, name_len); + p += sz; + } + + vm_address_t alloc_end = (vm_address_t) (*data + size); + vm_address_t real_end = round_page (p); + if (alloc_end > real_end) + munmap ((caddr_t) real_end, alloc_end - real_end); + *data_len = p - *data; + *data_entries = count; + + return err; +} + +static struct acpifs_dirent * +lookup (struct node *np, char *name) +{ + int i; + struct acpifs_dirent *ret = 0, *e; + + for (i = 0; i < np->nn->ln->dir->num_entries; i++) + { + e = np->nn->ln->dir->entries[i]; + + if (!strncmp (e->name, name, NAME_SIZE)) + { + ret = e; + break; + } + } + + return ret; +} + +static error_t +create_node (struct acpifs_dirent * e, struct node ** node) +{ + struct node *np; + struct netnode *nn; + + np = netfs_make_node_alloc (sizeof (struct netnode)); + if (!np) + return ENOMEM; + np->nn_stat = e->stat; + np->nn_translated = np->nn_stat.st_mode; + + nn = netfs_node_netnode (np); + memset (nn, 0, sizeof (struct netnode)); + nn->ln = e; + + *node = e->node = np; + + return 0; +} + +static void +destroy_node (struct node *node) +{ + if (node->nn->ln) + node->nn->ln->node = 0; + free (node); +} + +/* Attempt to create a file named NAME in DIR for USER with MODE. Set *NODE + to the new node upon return. On any error, clear *NODE. *NODE should be + locked on success; no matter what, unlock DIR before returning. */ +error_t +netfs_attempt_create_file (struct iouser * user, struct node * dir, + char *name, mode_t mode, struct node ** node) +{ + *node = 0; + pthread_mutex_unlock (&dir->lock); + return EOPNOTSUPP; +} + +/* Node NODE is being opened by USER, with FLAGS. NEWNODE is nonzero if we + just created this node. Return an error if we should not permit the open + to complete because of a permission restriction. */ +error_t +netfs_check_open_permissions (struct iouser * user, struct node * node, + int flags, int newnode) +{ + return entry_check_perms (user, node->nn->ln, flags); +} + +/* This should attempt a utimes call for the user specified by CRED on node + NODE, to change the atime to ATIME and the mtime to MTIME. */ +error_t +netfs_attempt_utimes (struct iouser * cred, struct node * node, + struct timespec * atime, struct timespec * mtime) +{ + return EOPNOTSUPP; +} + +/* Return the valid access types (bitwise OR of O_READ, O_WRITE, and O_EXEC) + in *TYPES for file NODE and user CRED. */ +error_t +netfs_report_access (struct iouser * cred, struct node * node, int *types) +{ + return EOPNOTSUPP; +} + +/* Trivial definitions. */ + +/* Make sure that NP->nn_stat is filled with current information. CRED + identifies the user responsible for the operation. */ +error_t +netfs_validate_stat (struct node * node, struct iouser * cred) +{ + /* Nothing to do here */ + return 0; +} + +/* This should sync the file NODE completely to disk, for the user CRED. If + WAIT is set, return only after sync is completely finished. */ +error_t +netfs_attempt_sync (struct iouser * cred, struct node * node, int wait) +{ + return EOPNOTSUPP; +} + +error_t +netfs_get_dirents (struct iouser * cred, struct node * dir, + int first_entry, int max_entries, char **data, + mach_msg_type_number_t * data_len, + vm_size_t max_data_len, int *data_entries) +{ + error_t err = 0; + + if (dir->nn->ln->dir) + { + err = get_dirents (dir->nn->ln, first_entry, max_entries, + data, data_len, max_entries, data_entries); + } + else + err = ENOTDIR; + + if (!err) + /* Update atime */ + UPDATE_TIMES (dir->nn->ln, TOUCH_ATIME); + + return err; +} + +/* Lookup NAME in DIR for USER; set *NODE to the found name upon return. If + the name was not found, then return ENOENT. On any error, clear *NODE. + (*NODE, if found, should be locked, this call should unlock DIR no matter + what.) */ +error_t +netfs_attempt_lookup (struct iouser *user, struct node *dir, + char *name, struct node **node) +{ + error_t err = 0; + struct acpifs_dirent *entry; + + if (*name == '\0' || strcmp (name, ".") == 0) + /* Current directory -- just add an additional reference to DIR's node + and return it. */ + { + netfs_nref (dir); + *node = dir; + return 0; + } + else if (strcmp (name, "..") == 0) + /* Parent directory. */ + { + if (dir->nn->ln->parent) + { + *node = dir->nn->ln->parent->node; + pthread_mutex_lock (&(*node)->lock); + netfs_nref (*node); + } + else + { + err = ENOENT; /* No .. */ + *node = 0; + } + + pthread_mutex_unlock (&dir->lock); + + return err; + } + + /* `name' is not . nor .. */ + if (dir->nn->ln->dir) + { + /* `dir' is a directory */ + + /* Check dir permissions */ + err = entry_check_perms (user, dir->nn->ln, O_READ | O_EXEC); + if (!err) + { + entry = lookup (dir, name); + if (!entry) + { + err = ENOENT; + } + else + { + if (entry->node) + { + netfs_nref (entry->node); + } + else + { + /* + * No active node, create one. + * The new node is created with a reference. + */ + err = create_node (entry, node); + } + + if (!err) + { + *node = entry->node; + /* We have to unlock DIR's node before locking the child node + because the locking order is always child-parent. We know + the child node won't go away because we already hold the + additional reference to it. */ + pthread_mutex_unlock (&dir->lock); + pthread_mutex_lock (&(*node)->lock); + } + } + } + } + else + { + err = ENOTDIR; + } + + if (err) + { + *node = 0; + pthread_mutex_unlock (&dir->lock); + } + else + { + /* Update the node cache */ + node_cache (*node); + } + + return err; +} + +/* Delete NAME in DIR for USER. */ +error_t +netfs_attempt_unlink (struct iouser * user, struct node * dir, char *name) +{ + return EOPNOTSUPP; +} + +/* Note that in this one call, neither of the specific nodes are locked. */ +error_t +netfs_attempt_rename (struct iouser * user, struct node * fromdir, + char *fromname, struct node * todir, + char *toname, int excl) +{ + return EOPNOTSUPP; +} + +/* Attempt to create a new directory named NAME in DIR for USER with mode + MODE. */ +error_t +netfs_attempt_mkdir (struct iouser * user, struct node * dir, + char *name, mode_t mode) +{ + return EOPNOTSUPP; +} + +/* Attempt to remove directory named NAME in DIR for USER. */ +error_t +netfs_attempt_rmdir (struct iouser * user, struct node * dir, char *name) +{ + return EOPNOTSUPP; +} + +/* This should attempt a chmod call for the user specified by CRED on node + NODE, to change the owner to UID and the group to GID. */ +error_t +netfs_attempt_chown (struct iouser * cred, struct node * node, + uid_t uid, uid_t gid) +{ + return EOPNOTSUPP; +} + +/* This should attempt a chauthor call for the user specified by CRED on node + NODE, to change the author to AUTHOR. */ +error_t +netfs_attempt_chauthor (struct iouser * cred, struct node * node, + uid_t author) +{ + return EOPNOTSUPP; +} + +/* This should attempt a chmod call for the user specified by CRED on node + NODE, to change the mode to MODE. Unlike the normal Unix and Hurd meaning + of chmod, this function is also used to attempt to change files into other + types. If such a transition is attempted which is impossible, then return + EOPNOTSUPP. */ +error_t +netfs_attempt_chmod (struct iouser * cred, struct node * node, mode_t mode) +{ + return EOPNOTSUPP; +} + +/* Attempt to turn NODE (user CRED) into a symlink with target NAME. */ +error_t +netfs_attempt_mksymlink (struct iouser * cred, struct node * node, char *name) +{ + return EOPNOTSUPP; +} + +/* Attempt to turn NODE (user CRED) into a device. TYPE is either S_IFBLK or + S_IFCHR. */ +error_t +netfs_attempt_mkdev (struct iouser * cred, struct node * node, + mode_t type, dev_t indexes) +{ + return EOPNOTSUPP; +} + +/* This should attempt a chflags call for the user specified by CRED on node + NODE, to change the flags to FLAGS. */ +error_t +netfs_attempt_chflags (struct iouser * cred, struct node * node, int flags) +{ + return EOPNOTSUPP; +} + +/* This should attempt to set the size of the file NODE (for user CRED) to + SIZE bytes long. */ +error_t +netfs_attempt_set_size (struct iouser * cred, struct node * node, off_t size) +{ + /* Do nothing */ + return 0; +} + +/* This should attempt to fetch filesystem status information for the remote + filesystem, for the user CRED. */ +error_t +netfs_attempt_statfs (struct iouser * cred, struct node * node, + struct statfs * st) +{ + memset (st, 0, sizeof *st); + st->f_type = FSTYPE_ACPI; + st->f_fsid = getpid (); + return 0; +} + +/* This should sync the entire remote filesystem. If WAIT is set, return + only after sync is completely finished. */ +error_t +netfs_attempt_syncfs (struct iouser * cred, int wait) +{ + return 0; +} + +/* Create a link in DIR with name NAME to FILE for USER. Note that neither + DIR nor FILE are locked. If EXCL is set, do not delete the target, but + return EEXIST if NAME is already found in DIR. */ +error_t +netfs_attempt_link (struct iouser * user, struct node * dir, + struct node * file, char *name, int excl) +{ + return EOPNOTSUPP; +} + +/* Attempt to create an anonymous file related to DIR for USER with MODE. + Set *NODE to the returned file upon success. No matter what, unlock DIR. */ +error_t +netfs_attempt_mkfile (struct iouser * user, struct node * dir, + mode_t mode, struct node ** node) +{ + return EOPNOTSUPP; +} + +/* Read the contents of NODE (a symlink), for USER, into BUF. */ +error_t +netfs_attempt_readlink (struct iouser * user, struct node * node, char *buf) +{ + return EOPNOTSUPP; +} + +/* Read from the file NODE for user CRED starting at OFFSET and continuing for + up to *LEN bytes. Put the data at DATA. Set *LEN to the amount + successfully read upon return. */ +error_t +netfs_attempt_read (struct iouser * cred, struct node * node, + off_t offset, size_t * len, void *data) +{ + error_t err; + + if (!strncmp (node->nn->ln->name, DIR_TABLES_NAME, NAME_SIZE)) + return EOPNOTSUPP; + else + { + err = io_acpi_file (node->nn->ln, offset, len, data); + if (!err) + /* Update atime */ + UPDATE_TIMES (node->nn->ln, TOUCH_ATIME); + } + return err; +} + +/* Write to the file NODE for user CRED starting at OFSET and continuing for up + to *LEN bytes from DATA. Set *LEN to the amount seccessfully written upon + return. */ +error_t +netfs_attempt_write (struct iouser * cred, struct node * node, + off_t offset, size_t * len, void *data) +{ + return EOPNOTSUPP; +} + +/* Node NP is all done; free all its associated storage. */ +void +netfs_node_norefs (struct node *node) +{ + destroy_node (node); +} diff --git a/acpi/netfs_impl.h b/acpi/netfs_impl.h new file mode 100644 index 00000000..71717579 --- /dev/null +++ b/acpi/netfs_impl.h @@ -0,0 +1,43 @@ +/* + Copyright (C) 2018 Free Software Foundation, Inc. + + 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/>. +*/ + +/* Data types required to create a directory tree using libnetfs */ + +#ifndef NETFS_IMPL_H +#define NETFS_IMPL_H + +#include + +#include + +/* + * A netnode has a 1:1 relation with a generic libnetfs node. Hence, it's only + * purpose is to extend a generic node to add the new attributes our problem + * requires. + */ +struct netnode +{ + /* Light node */ + struct acpifs_dirent *ln; + + /* Position in the node cache. */ + struct node *ncache_next, *ncache_prev; +}; + +#endif /* NETFS_IMPL_H */ diff --git a/acpi/options.c b/acpi/options.c new file mode 100644 index 00000000..8dbcd263 --- /dev/null +++ b/acpi/options.c @@ -0,0 +1,149 @@ +/* + Copyright (C) 2017 Free Software Foundation, Inc. + + Written by Miles Bader + + 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/>. +*/ + +/* Fsysopts and command line option parsing */ + +#include + +#include +#include +#include +#include + +#include + +/* Option parser */ +static error_t +parse_opt (int opt, char *arg, struct argp_state *state) +{ + error_t err = 0; + struct parse_hook *h = state->hook; + + /* Return _ERR from this routine */ +#define RETURN(_err) \ + do { return _err; } while (0) + + /* Print a parsing error message and (if exiting is turned off) return the + error code ERR. */ +#define PERR(err, fmt, args...) \ + do { argp_error (state, fmt , ##args); RETURN (err); } while (0) + + /* Like PERR but for non-parsing errors. */ +#define FAIL(rerr, status, perr, fmt, args...) \ + do{ argp_failure (state, status, perr, fmt , ##args); RETURN (rerr); } while(0) + + if (!arg && state->next < state->argc && (*state->argv[state->next] != '-')) + { + arg = state->argv[state->next]; + state->next++; + } + + switch (opt) + { + case 'U': + h->perm.uid = atoi (arg); + break; + case 'G': + h->perm.gid = atoi (arg); + break; + + case ARGP_KEY_INIT: + /* Initialize our parsing state. */ + h = malloc (sizeof (struct parse_hook)); + if (!h) + FAIL (ENOMEM, 1, ENOMEM, "option parsing"); + + h->ncache_len = NODE_CACHE_MAX; + h->perm.uid = 0; + h->perm.gid = 0; + state->hook = h; + break; + + case ARGP_KEY_SUCCESS: + /* Set permissions to FS */ + fs->perm = h->perm; + + /* Set cache len */ + fs->node_cache_max = h->ncache_len; + + if (fs->root) + { + /* + * FS is already initialized, that means we've been called by fsysopts. + * Update permissions. + */ + + /* Don't accept new RPCs during this process */ + err = ports_inhibit_all_rpcs (); + if (err) + return err; + + err = fs_set_permissions (fs); + + /* Accept RPCs again */ + ports_resume_all_rpcs (); + } + + /* Free the hook */ + free (h); + + break; + + case ARGP_KEY_ERROR: + /* Parsing error occurred, free the permissions. */ + free (h); + break; + + default: + return ARGP_ERR_UNKNOWN; + } + + return err; +} + +/* + * Print current permissions. Called by fsysopts. + */ +error_t +netfs_append_args (char **argz, size_t * argz_len) +{ + error_t err = 0; + struct acpifs_perm *p; + +#define ADD_OPT(fmt, args...) \ + do { char buf[100]; \ + if (! err) { \ + snprintf (buf, sizeof buf, fmt , ##args); \ + err = argz_add (argz, argz_len, buf); } } while (0) + + p = &fs->perm; + if (p->uid >= 0) + ADD_OPT ("--uid=%u", p->uid); + if (p->gid >= 0) + ADD_OPT ("--gid=%u", p->gid); + +#undef ADD_OPT + return err; +} + +struct argp acpi_argp = { options, parse_opt, 0, doc }; + +struct argp *netfs_runtime_argp = &acpi_argp; diff --git a/acpi/options.h b/acpi/options.h new file mode 100644 index 00000000..36ccc48b --- /dev/null +++ b/acpi/options.h @@ -0,0 +1,51 @@ +/* + Copyright (C) 2018 Free Software Foundation, Inc. + + 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/>. +*/ + +/* Command line option parsing */ + +#ifndef OPTIONS_H +#define OPTIONS_H + +#include +#include +#include + +#include + +#define STR2(x) #x +#define STR(x) STR2(x) + +/* Used to hold data during argument parsing. */ +struct parse_hook +{ + struct acpifs_perm perm; + size_t ncache_len; +}; + +/* ACPI translator options. Used for both startup and runtime. */ +static const struct argp_option options[] = { + {0, 0, 0, 0, "These apply to the whole acpi tree:", 1}, + {"uid", 'U', "UID", 0, "User ID to give permissions to"}, + {"gid", 'G', "GID", 0, "Group ID to give permissions to"}, + {0} +}; + +static const char doc[] = "Permissions on acpi are currently global."; + +#endif // OPTIONS_H diff --git a/hurd/hurd_types.h b/hurd/hurd_types.h index eb06029b..a77a9e43 100644 --- a/hurd/hurd_types.h +++ b/hurd/hurd_types.h @@ -360,6 +360,7 @@ typedef const int *const_procinfo_t; #define FSTYPE_MEMFS 0x00000019 /* In-core filesystem */ #define FSTYPE_ISO9660 0x0000001a /* ISO9660 */ #define FSTYPE_PCI 0x0000001b /* PCI filesystem */ +#define FSTYPE_ACPI 0x0000001c /* ACPI filesystem */ /* Standard port assignments for file_exec_paths and exec_* */ enum -- 2.17.1