/* 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-1); entry->name[NAME_SIZE-1] = '\0'; 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; }