fastrpc: add hexagonfs

The DSP demands a file system, but shouldn't have unrestricted access to
it (especially if the reverse tunnel is running as root). It makes
requests to files with no regard to any Linux FHS and a sysfs file that
doesn't exist on mainline. Add a virtual file system to take care of
the DSP's needs.
This commit is contained in:
Richard Acayan
2023-01-30 18:52:16 -05:00
parent 6e2661d5ab
commit 3201f4b74f
5 changed files with 894 additions and 0 deletions

368
fastrpc/hexagonfs.c Normal file
View File

@@ -0,0 +1,368 @@
/*
* Virtual read-only filesystem for Hexagon processors
*
* Copyright (C) 2023 Richard Acayan
*
* This file is part of sensh.
*
* Sensh 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 3 of the License, or
* (at your option) any later version.
*
* This program 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, see <https://www.gnu.org/licenses/>.
*/
#include <errno.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include "hexagonfs.h"
#define HEXAGONFS_MAX_FD 256
#define DEFINE_VIRT_DIR(dirname, files...) \
(struct hexagonfs_dirent) { \
.name = dirname, \
.ops = &hexagonfs_virt_dir_ops, \
.u.dir = (struct hexagonfs_dirent *[]) { files }, \
}
#define DEFINE_MAPPED(dirname, path) \
(struct hexagonfs_dirent) { \
.name = dirname, \
.ops = &hexagonfs_mapped_ops, \
.u.phys = path, \
}
#define DEFINE_SYSFILE(dirname, func) \
(struct hexagonfs_dirent) { \
.name = dirname, \
.ops = &hexagonfs_sysfs_ops, \
.u.func = func, \
}
static struct hexagonfs_dirent root_dir = DEFINE_VIRT_DIR("/",
&DEFINE_VIRT_DIR("mnt",
&DEFINE_VIRT_DIR("vendor",
&DEFINE_VIRT_DIR("persist",
&DEFINE_VIRT_DIR("sensors",
&DEFINE_MAPPED("registry",
"/var/lib/qcom/sensors"),
NULL,
),
NULL,
),
NULL,
),
NULL,
),
&DEFINE_VIRT_DIR("sys",
&DEFINE_VIRT_DIR("devices",
&DEFINE_VIRT_DIR("soc0",
&DEFINE_MAPPED("hw_platform",
"/sys/kernel/debug/qcom_socinfo/hardware_platform"),
&(struct hexagonfs_dirent) {
.name = "platform_subtype",
.ops = &hexagonfs_plat_subtype_name_ops,
.u.phys = "/sys/kernel/debug/qcom_socinfo/hardware_platform_subtype",
},
&DEFINE_MAPPED("platform_subtype_id",
"/sys/kernel/debug/qcom_socinfo/hardware_platform_subtype"),
&DEFINE_MAPPED("platform_version",
"/sys/kernel/debug/qcom_socinfo/platform_version"),
&DEFINE_MAPPED("revision",
"/sys/devices/soc0/revision"),
&DEFINE_MAPPED("soc_id",
"/sys/devices/soc0/soc_id"),
NULL,
),
NULL,
),
NULL,
),
&DEFINE_VIRT_DIR("usr",
&DEFINE_VIRT_DIR("lib",
&DEFINE_VIRT_DIR("qcom",
&DEFINE_MAPPED("adsp",
"/usr/lib/qcom/adsp/"),
NULL,
),
NULL,
),
NULL,
),
&DEFINE_VIRT_DIR("vendor",
&DEFINE_VIRT_DIR("etc",
&DEFINE_VIRT_DIR("sensors",
&DEFINE_MAPPED("config",
"/etc/qcom/sensors.d/"),
&DEFINE_MAPPED("sns_reg_config",
"/etc/qcom/sns_reg.conf"),
NULL,
),
NULL,
),
NULL,
),
NULL,
);
static struct hexagonfs_fd *fds[HEXAGONFS_MAX_FD];
static char *copy_segment_and_advance(const char *path,
bool *trailing_slash,
const char **next)
{
const char *next_tmp;
char *segment;
size_t segment_len;
next_tmp = strchr(path, '/');
if (next_tmp == NULL) {
next_tmp = &path[strlen(path)];
*trailing_slash = false;
} else {
*trailing_slash = true;
}
segment_len = next_tmp - path;
while (*next_tmp == '/')
next_tmp++;
segment = malloc(segment_len + 1);
if (segment == NULL)
return NULL;
memcpy(segment, path, segment_len);
segment[segment_len] = 0;
*next = next_tmp;
return segment;
}
static struct hexagonfs_fd *pop_dir(struct hexagonfs_fd *dir,
struct hexagonfs_fd *root)
{
struct hexagonfs_fd *up;
if (dir != root && dir->up != NULL) {
up = dir->up;
dir->ops->close(dir->data);
free(dir);
} else {
up = dir;
}
return up;
}
static int allocate_file_number(struct hexagonfs_fd *fd)
{
size_t i;
for (i = 0; i < HEXAGONFS_MAX_FD; i++) {
if (fds[i] == NULL) {
fd->is_assigned = true;
fds[i] = fd;
return i;
}
}
return -EMFILE;
}
static void destroy_file_descriptor(struct hexagonfs_fd *fd)
{
struct hexagonfs_fd *curr = fd;
struct hexagonfs_fd *next;
while (curr != NULL && !curr->is_assigned) {
next = curr->up;
curr->ops->close(curr->data);
free(curr);
curr = next;
}
}
int hexagonfs_open_root()
{
struct hexagonfs_fd *fd;
int ret;
fd = malloc(sizeof(struct hexagonfs_fd));
if (fd == NULL)
return -ENOMEM;
fd->off = 0;
fd->is_assigned = false;
fd->up = NULL;
fd->ops = root_dir.ops;
ret = root_dir.ops->from_dirent(root_dir.u.ptr, true, &fd->data);
if (ret)
goto err;
ret = allocate_file_number(fd);
if (ret < 0)
goto err;
return ret;
err:
destroy_file_descriptor(fd);
return ret;
}
int hexagonfs_openat(int rootfd, int dirfd, const char *name)
{
struct hexagonfs_fd *fd;
const char *curr = name;
char *segment;
bool expect_dir;
int selected = dirfd;
int ret = 0;
if (*curr == '/') {
selected = rootfd;
while (*curr == '/')
curr++;
}
fd = fds[selected];
while (*curr != '\0' && !ret) {
segment = copy_segment_and_advance(curr, &expect_dir, &curr);
if (segment == NULL) {
ret = -ENOMEM;
goto err;
}
if (!strcmp(segment, ".")) {
goto next;
} else if (!strcmp(segment, "..")) {
fd = pop_dir(fd, fds[rootfd]);
} else {
ret = fd->ops->openat(fd, segment, expect_dir, &fd);
}
next:
free(segment);
}
if (ret)
goto err;
ret = allocate_file_number(fd);
if (ret)
goto err;
return ret;
err:
destroy_file_descriptor(fd);
return ret;
}
int hexagonfs_close(int fileno)
{
struct hexagonfs_fd *fd;
if (fileno >= HEXAGONFS_MAX_FD)
return -EBADF;
fd = fds[fileno];
if (fd == NULL || fd->ops == NULL)
return -EBADF;
fd->is_assigned = false;
destroy_file_descriptor(fd);
fds[fileno] = NULL;
return 0;
}
int hexagonfs_lseek(int fileno, off_t off, int whence)
{
struct hexagonfs_fd *fd;
if (fileno >= HEXAGONFS_MAX_FD)
return -EBADF;
fd = fds[fileno];
if (fd == NULL)
return -EBADF;
if (fd->ops->seek == NULL)
return -ENOSYS;
return fd->ops->seek(fd, off, whence);
}
ssize_t hexagonfs_read(int fileno, size_t size, void *ptr)
{
struct hexagonfs_fd *fd;
if (fileno >= HEXAGONFS_MAX_FD)
return -EBADF;
fd = fds[fileno];
if (fd == NULL)
return -EBADF;
if (fd->ops->read == NULL)
return -ENOSYS;
return fd->ops->read(fd, size, ptr);
}
int hexagonfs_readdir(int fileno, size_t ent_size, char *ent)
{
struct hexagonfs_fd *fd;
int ret;
if (fileno >= HEXAGONFS_MAX_FD)
return -EBADF;
fd = fds[fileno];
if (fd == NULL)
return -EBADF;
if (fd->ops->readdir == NULL)
return -ENOSYS;
return fd->ops->readdir(fd, ent_size, ent);
}
int hexagonfs_fstat(int fileno, struct stat *stats)
{
struct hexagonfs_fd *fd;
if (fileno >= HEXAGONFS_MAX_FD)
return -EBADF;
fd = fds[fileno];
if (fd == NULL)
return -EBADF;
if (fd->ops->stat == NULL)
return -ENOSYS;
return fd->ops->stat(fd, stats);
}

78
fastrpc/hexagonfs.h Normal file
View File

@@ -0,0 +1,78 @@
/*
* Virtual read-only filesystem for Hexagon processors
*
* Copyright (C) 2023 Richard Acayan
*
* This file is part of sensh.
*
* Sensh 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 3 of the License, or
* (at your option) any later version.
*
* This program 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, see <https://www.gnu.org/licenses/>.
*/
#ifndef HEXAGONFS_H
#define HEXAGONFS_H
#include <stdbool.h>
#include <stddef.h>
#include <sys/types.h>
#include <sys/stat.h>
struct hexagonfs_fd;
struct hexagonfs_file_ops {
void (*close)(void *fd_data);
int (*from_dirent)(void *dirent_data, bool dir, void **fd_data);
int (*openat)(struct hexagonfs_fd *dir,
const char *segment,
bool expect_dir,
struct hexagonfs_fd **out);
int (*readdir)(struct hexagonfs_fd *fd, size_t size, char *out);
ssize_t (*read)(struct hexagonfs_fd *fd, size_t size, void *ptr);
int (*stat)(struct hexagonfs_fd *fd, struct stat *stats);
int (*seek)(struct hexagonfs_fd *fd, off_t off, int whence);
};
struct hexagonfs_dirent {
const char *name;
struct hexagonfs_file_ops *ops;
union hexagonfs_dirent_data {
void *ptr;
struct hexagonfs_dirent **dir;
const char *phys;
} u;
};
struct hexagonfs_fd {
off_t off;
bool is_assigned;
struct hexagonfs_fd *up;
void *data;
struct hexagonfs_file_ops *ops;
};
extern struct hexagonfs_file_ops hexagonfs_mapped_ops;
extern struct hexagonfs_file_ops hexagonfs_plat_subtype_name_ops;
extern struct hexagonfs_file_ops hexagonfs_virt_dir_ops;
int hexagonfs_open_root();
int hexagonfs_openat(int rootfd, int dirfd, const char *name);
int hexagonfs_close(int fileno);
int hexagonfs_fstat(int fileno, struct stat *stats);
int hexagonfs_lseek(int fileno, off_t pos, int whence);
int hexagonfs_readdir(int fileno, size_t size, char *name);
ssize_t hexagonfs_read(int fileno, size_t size, void *ptr);
#endif

226
fastrpc/hexagonfs_mapped.c Normal file
View File

@@ -0,0 +1,226 @@
/*
* HexagonFS mapped file/directory operations
*
* Copyright (C) 2023 Richard Acayan
*
* This file is part of sensh.
*
* Sensh 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 3 of the License, or
* (at your option) any later version.
*
* This program 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, see <https://www.gnu.org/licenses/>.
*/
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include "hexagonfs.h"
struct mapped_ctx {
int fd;
DIR *dir;
};
static void mapped_close(void *fd_data)
{
struct mapped_ctx *ctx = fd_data;
if (ctx->dir != NULL)
closedir(ctx->dir);
else
close(ctx->fd);
free(ctx);
}
static int mapped_from_dirent(void *dirent_data, bool dir, void **fd_data)
{
struct mapped_ctx *ctx;
const char *name = dirent_data;
int flags = O_RDONLY;
int fd;
int ret;
ctx = malloc(sizeof(struct mapped_ctx));
if (ctx == NULL)
return -ENOMEM;
if (dir)
flags |= O_DIRECTORY;
ctx->fd = open(name, flags);
if (ctx->fd == -1) {
ret = -errno;
goto err;
}
ctx->dir = NULL;
*fd_data = ctx;
return 0;
err:
free(ctx);
return ret;
}
static int mapped_openat(struct hexagonfs_fd *dir,
const char *segment,
bool expect_dir,
struct hexagonfs_fd **out)
{
struct mapped_ctx *dir_ctx = dir->data;
struct hexagonfs_fd *fd;
struct mapped_ctx *ctx;
int flags = O_RDONLY;
int ret;
ctx = malloc(sizeof(struct mapped_ctx));
if (ctx == NULL)
return -ENOMEM;
fd = malloc(sizeof(struct hexagonfs_fd));
if (fd == NULL) {
ret = -ENOMEM;
goto err;
}
if (expect_dir)
flags |= O_DIRECTORY;
ctx->fd = openat(dir_ctx->fd, segment, flags);
if (ctx->fd == -1) {
ret = -errno;
goto err_free_fd;
}
ctx->dir = NULL;
fd->off = 0;
fd->up = dir;
fd->ops = &hexagonfs_mapped_ops;
fd->data = ctx;
*out = fd;
return 0;
err_free_fd:
free(fd);
err:
free(ctx);
return ret;
}
ssize_t mapped_read(struct hexagonfs_fd *fd, size_t size, void *out)
{
struct mapped_ctx *ctx = fd->data;
ssize_t ret;
ret = read(ctx->fd, out, size);
if (ret < 0)
return -errno;
return ret;
}
int mapped_readdir(struct hexagonfs_fd *fd, size_t size, char *out)
{
struct mapped_ctx *ctx = fd->data;
struct dirent *ent;
if (ctx->dir == NULL) {
ctx->dir = fdopendir(ctx->fd);
if (ctx->dir == NULL)
return -errno;
ctx->fd = dirfd(ctx->dir);
if (ctx->fd == -1)
return -errno;
}
errno = 0;
ent = readdir(ctx->dir);
if (ent == NULL)
return -errno;
strncpy(out, ent->d_name, size);
out[size - 1] = '\0';
return 0;
}
static int mapped_seek(struct hexagonfs_fd *fd, off_t off, int whence)
{
struct mapped_ctx *ctx = fd->data;
int ret;
ret = lseek(ctx->fd, off, whence);
if (ret == -1)
return -errno;
return 0;
}
static int mapped_stat(struct hexagonfs_fd *fd, struct stat *stats)
{
struct mapped_ctx *ctx = fd->data;
struct stat phys;
int ret;
ret = fstat(ctx->fd, &phys);
if (ret)
return -errno;
stats->st_size = phys.st_size;
stats->st_dev = 0;
stats->st_rdev = 0;
stats->st_ino = 0;
stats->st_nlink = 0;
if (phys.st_mode & S_IFDIR) {
stats->st_mode = S_IFDIR
| S_IRUSR | S_IXUSR
| S_IRGRP | S_IXGRP
| S_IROTH | S_IXOTH;
} else {
stats->st_mode = S_IRUSR | S_IRGRP | S_IROTH;
}
stats->st_atim.tv_sec = phys.st_atim.tv_sec;
stats->st_atim.tv_nsec = phys.st_atim.tv_nsec;
stats->st_ctim.tv_sec = phys.st_ctim.tv_sec;
stats->st_ctim.tv_nsec = phys.st_ctim.tv_nsec;
stats->st_mtim.tv_sec = phys.st_mtim.tv_sec;
stats->st_mtim.tv_nsec = phys.st_mtim.tv_nsec;
return 0;
}
struct hexagonfs_file_ops hexagonfs_mapped_ops = {
.close = mapped_close,
.from_dirent = mapped_from_dirent,
.openat = mapped_openat,
.read = mapped_read,
.readdir = mapped_readdir,
.seek = mapped_seek,
.stat = mapped_stat,
};

View File

@@ -0,0 +1,100 @@
/*
* HexagonFS operations for a missing sysfs file
*
* Copyright (C) 2023 Richard Acayan
*
* This file is part of sensh.
*
* Sensh 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 3 of the License, or
* (at your option) any later version.
*
* This program 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, see <https://www.gnu.org/licenses/>.
*/
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include "hexagonfs.h"
struct plat_subtype_ctx {
int fd;
char *name;
};
static void plat_subtype_name_close(void *fd_data)
{
struct plat_subtype_ctx *ctx = fd_data;
close(ctx->fd);
if (ctx->name != NULL)
free(ctx->name);
}
static int plat_subtype_name_from_dirent(void *dirent_data,
bool dir,
void **fd_data)
{
struct plat_subtype_ctx *ctx;
ctx = malloc(sizeof(struct plat_subtype_ctx));
if (ctx == NULL)
return -ENOMEM;
ctx->fd = open((const char *) dirent_data, O_RDONLY);
if (ctx->fd == -1)
return -errno;
ctx->name = NULL;
*fd_data = ctx;
return 0;
}
static int plat_subtype_name_openat(struct hexagonfs_fd *dir,
const char *segment,
bool expect_dir,
struct hexagonfs_fd **out)
{
return -ENOTDIR;
}
static int plat_subtype_stat(struct hexagonfs_fd *fd, struct stat *stats)
{
stats->st_size = 0;
stats->st_dev = 0;
stats->st_rdev = 0;
stats->st_ino = 0;
stats->st_nlink = 0;
stats->st_mode = S_IRUSR | S_IRGRP | S_IROTH;
stats->st_atim.tv_sec = 0;
stats->st_atim.tv_nsec = 0;
stats->st_ctim.tv_sec = 0;
stats->st_ctim.tv_nsec = 0;
stats->st_mtim.tv_sec = 0;
stats->st_mtim.tv_nsec = 0;
return 0;
}
struct hexagonfs_file_ops hexagonfs_plat_subtype_name_ops = {
.close = plat_subtype_name_close,
.from_dirent = plat_subtype_name_from_dirent,
.openat = plat_subtype_name_openat,
.stat = plat_subtype_stat,
};

View File

@@ -0,0 +1,122 @@
/*
* HexagonFS virtual directory operations
*
* Copyright (C) 2023 Richard Acayan
*
* This file is part of sensh.
*
* Sensh 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 3 of the License, or
* (at your option) any later version.
*
* This program 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, see <https://www.gnu.org/licenses/>.
*/
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include "hexagonfs.h"
/*
* This function searches for the relevant path segment in the path directory.
* It is not optimized because it is dealing with small directories.
*/
static struct hexagonfs_dirent *walk_dir(struct hexagonfs_dirent **dir,
const char *segment)
{
struct hexagonfs_dirent **curr = dir;
while (*curr != NULL) {
if (!strcmp(segment, (*curr)->name))
break;
curr++;
}
return *curr;
}
static int virt_dir_from_dirent(void *dirent_data, bool dir, void **fd_data)
{
*fd_data = dirent_data;
return 0;
}
static int virt_dir_openat(struct hexagonfs_fd *dir,
const char *segment,
bool expect_dir,
struct hexagonfs_fd **out)
{
struct hexagonfs_fd *fd;
struct hexagonfs_dirent *ent;
struct hexagonfs_dirent **dirlist = dir->data;
int ret;
ent = walk_dir(dirlist, segment);
if (ent == NULL)
return -ENOENT;
fd = malloc(sizeof(struct hexagonfs_fd));
if (fd == NULL)
return -ENOMEM;
fd->off = 0;
fd->up = dir;
fd->ops = ent->ops;
ret = ent->ops->from_dirent(ent->u.ptr, expect_dir, &fd->data);
if (ret)
goto err;
*out = fd;
return 0;
err:
free(fd);
return ret;
}
static void virt_dir_close(void *fd_data)
{
}
static int virt_dir_stat(struct hexagonfs_fd *fd, struct stat *stats)
{
stats->st_size = 0;
stats->st_dev = 0;
stats->st_rdev = 0;
stats->st_ino = 0;
stats->st_nlink = 0;
stats->st_mode = S_IRUSR | S_IXUSR
| S_IRGRP | S_IXGRP
| S_IROTH | S_IXOTH;
stats->st_atim.tv_sec = 0;
stats->st_atim.tv_nsec = 0;
stats->st_ctim.tv_sec = 0;
stats->st_ctim.tv_nsec = 0;
stats->st_mtim.tv_sec = 0;
stats->st_mtim.tv_nsec = 0;
return 0;
}
struct hexagonfs_file_ops hexagonfs_virt_dir_ops = {
.close = virt_dir_close,
.from_dirent = virt_dir_from_dirent,
.openat = virt_dir_openat,
.stat = virt_dir_stat,
};