/*
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;
}