397 lines
11 KiB
C
397 lines
11 KiB
C
// SPDX: GPL-3.0-or-later
|
|
#define FUSE_USE_VERSION 35
|
|
|
|
#include <fuse3/fuse.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <unistd.h>
|
|
|
|
static const char *script_path;
|
|
static int use_serial = 0;
|
|
static char serial_device[256];
|
|
static const char *END_TOKEN = "\0\0\0";
|
|
|
|
static int call_script_serial(const char *command, char **output) {
|
|
int fd = open(serial_device, O_RDWR | O_NOCTTY);
|
|
if (fd < 0) {
|
|
perror("open serial device");
|
|
return -errno;
|
|
}
|
|
|
|
// Write the command to the serial device
|
|
size_t command_len = strlen(command);
|
|
if (write(fd, command, command_len) != command_len) {
|
|
perror("write to serial");
|
|
close(fd);
|
|
return -EIO;
|
|
}
|
|
|
|
// Read the response
|
|
size_t size = 0;
|
|
*output = NULL;
|
|
char buffer[1024];
|
|
ssize_t bytes_read;
|
|
|
|
while ((bytes_read = read(fd, buffer, sizeof(buffer))) > 0) {
|
|
*output = realloc(*output, size + bytes_read + 1);
|
|
memcpy(*output + size, buffer, bytes_read);
|
|
size += bytes_read;
|
|
|
|
if (size >= 3 && memcmp(*output + size - 3, END_TOKEN, 3) == 0) {
|
|
size -= 3; // Remove the END_TOKEN
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (bytes_read < 0) {
|
|
perror("read from serial");
|
|
free(*output);
|
|
close(fd);
|
|
return -EIO;
|
|
}
|
|
|
|
if (*output) {
|
|
(*output)[size] = '\0';
|
|
}
|
|
|
|
close(fd);
|
|
return 0;
|
|
}
|
|
|
|
static int call_script(const char *action, const char *path, char **output) {
|
|
char command[4096];
|
|
|
|
snprintf(command, sizeof(command), "%s %s '%s'", script_path, action, path);
|
|
|
|
if (use_serial) {
|
|
return call_script_serial(command, output);
|
|
} else {
|
|
FILE *fp = popen(command, "r");
|
|
if (!fp) {
|
|
return -errno;
|
|
}
|
|
|
|
size_t size = 0;
|
|
*output = NULL;
|
|
char line[1024];
|
|
while (fgets(line, sizeof(line), fp)) {
|
|
size_t len = strlen(line);
|
|
*output = realloc(*output, size + len + 1);
|
|
memcpy(*output + size, line, len);
|
|
size += len;
|
|
}
|
|
|
|
if (*output) {
|
|
(*output)[size] = '\0';
|
|
}
|
|
|
|
return pclose(fp);
|
|
}
|
|
}
|
|
|
|
static int fusescript_getattr(const char *path, struct stat *stbuf, struct fuse_file_info *fi) {
|
|
(void) fi;
|
|
memset(stbuf, 0, sizeof(struct stat));
|
|
|
|
if( path == NULL ) return -EINVAL;
|
|
|
|
if (strcmp(path, "/") == 0) {
|
|
// Root directory
|
|
stbuf->st_mode = S_IFDIR | 0755; // Directory with permissions 0755
|
|
stbuf->st_nlink = 2; // Standard for directories
|
|
return 0;
|
|
}
|
|
|
|
// Call the script for other paths
|
|
char *output = NULL;
|
|
int ret = call_script("getattr", path, &output);
|
|
if (ret != 0) {
|
|
free(output);
|
|
return -EIO;
|
|
}
|
|
|
|
// Parse the script output
|
|
int mode, nlink, uid, gid, size, blocks;
|
|
if( output == NULL ) return -EIO;
|
|
if (sscanf(output, "%d %d %d %d %d %d", &mode, &nlink, &uid, &gid, &size, &blocks) != 6) {
|
|
free(output);
|
|
return -EIO; // Error parsing the output
|
|
}
|
|
|
|
// Populate the stat structure
|
|
stbuf->st_mode = mode; // File mode (e.g., regular file, directory)
|
|
stbuf->st_nlink = nlink; // Number of hard links
|
|
stbuf->st_uid = uid; // User ID (owner)
|
|
stbuf->st_gid = gid; // Group ID
|
|
stbuf->st_size = size; // File size in bytes
|
|
stbuf->st_blocks = blocks; // Number of 512-byte blocks allocated
|
|
stbuf->st_blksize = 512; // Block size (optional)
|
|
|
|
// Set default values for atime, mtime, ctime (if not handled by your script)
|
|
stbuf->st_atime = time(NULL); // Last access time
|
|
stbuf->st_mtime = time(NULL); // Last modification time
|
|
stbuf->st_ctime = time(NULL); // Last status change time
|
|
|
|
free(output);
|
|
return 0;
|
|
}
|
|
|
|
static int fusescript_access(const char *path, int mask) {
|
|
// Call the script for permission checks (optional)
|
|
char *output = NULL;
|
|
int ret = call_script("access", path, &output);
|
|
if (ret != 0) {
|
|
free(output);
|
|
return 0; //-EIO;
|
|
}
|
|
|
|
free(output);
|
|
return 0; // Allow all accesses for simplicity
|
|
}
|
|
|
|
|
|
static int fusescript_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
|
|
off_t offset, struct fuse_file_info *fi, enum fuse_readdir_flags flags) {
|
|
(void) offset;
|
|
(void) fi;
|
|
(void) flags;
|
|
|
|
// Add default entries for directories
|
|
filler(buf, ".", NULL, 0, 0); // Current directory
|
|
filler(buf, "..", NULL, 0, 0); // Parent directory
|
|
|
|
// Call the script for additional entries
|
|
char *output = NULL;
|
|
int ret = call_script("readdir", path, &output);
|
|
if (ret != 0) {
|
|
free(output);
|
|
return -EIO;
|
|
}
|
|
|
|
// Parse script output, assuming it provides one entry per line
|
|
char *line = strtok(output, "\n");
|
|
while (line) {
|
|
filler(buf, line, NULL, 0, 0);
|
|
line = strtok(NULL, "\n");
|
|
}
|
|
|
|
free(output);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int fusescript_open(const char *path, struct fuse_file_info *fi) {
|
|
(void) fi;
|
|
char *output = NULL;
|
|
int ret = call_script("open", path, &output);
|
|
free(output);
|
|
return ret == 0 ? 0 : -EIO;
|
|
}
|
|
|
|
static int fusescript_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi) {
|
|
(void) fi;
|
|
char command[4096];
|
|
snprintf(command, sizeof(command), "%s read '%s' %zu %jd", script_path, path, size, (intmax_t)offset);
|
|
|
|
FILE *fp = popen(command, "r");
|
|
if (!fp) {
|
|
return -errno;
|
|
}
|
|
|
|
size_t bytes_read = fread(buf, 1, size, fp);
|
|
int ret = pclose(fp);
|
|
return ret == 0 ? bytes_read : -EIO;
|
|
}
|
|
|
|
static int fusescript_write(const char *path, const char *buf, size_t size, off_t offset, struct fuse_file_info *fi) {
|
|
(void) fi;
|
|
char command[4096];
|
|
snprintf(command, sizeof(command), "echo -n '%.*s' | %s write '%s' %zu %jd", (int)size, buf, script_path, path, size, (intmax_t)offset);
|
|
|
|
int ret = system(command);
|
|
return ret == 0 ? size : -EIO;
|
|
}
|
|
|
|
static int fusescript_mkdir(const char *path, mode_t mode) {
|
|
char command[4096];
|
|
snprintf(command, sizeof(command), "%s mkdir '%s' %o", script_path, path, mode);
|
|
|
|
int ret = system(command);
|
|
return ret == 0 ? 0 : -EIO;
|
|
}
|
|
|
|
static int fusescript_unlink(const char *path) {
|
|
char command[4096];
|
|
snprintf(command, sizeof(command), "%s unlink '%s'", script_path, path);
|
|
|
|
int ret = system(command);
|
|
return ret == 0 ? 0 : -EIO;
|
|
}
|
|
|
|
static int fusescript_rmdir(const char *path) {
|
|
char command[4096];
|
|
snprintf(command, sizeof(command), "%s rmdir '%s'", script_path, path);
|
|
|
|
int ret = system(command);
|
|
return ret == 0 ? 0 : -EIO;
|
|
}
|
|
|
|
// Add getxattr function
|
|
static int fusescript_getxattr(const char *path, const char *name, char *value, size_t size) {
|
|
char command[4096];
|
|
snprintf(command, sizeof(command), "%s getxattr '%s' '%s'", script_path, path, name);
|
|
|
|
FILE *fp = popen(command, "r");
|
|
if (!fp) {
|
|
return -errno;
|
|
}
|
|
|
|
char *output = NULL;
|
|
size_t output_len = 0;
|
|
char line[1024];
|
|
while (fgets(line, sizeof(line), fp)) {
|
|
size_t len = strlen(line);
|
|
output = realloc(output, output_len + len + 1);
|
|
memcpy(output + output_len, line, len);
|
|
output_len += len;
|
|
}
|
|
|
|
if (output) {
|
|
output[output_len] = '\0';
|
|
}
|
|
|
|
int ret = pclose(fp);
|
|
if (ret != 0 || !output) {
|
|
free(output);
|
|
return -EIO;
|
|
}
|
|
|
|
if (size == 0) {
|
|
// Return the size of the required buffer
|
|
free(output);
|
|
return output_len;
|
|
} else if (size < output_len) {
|
|
// Buffer too small
|
|
free(output);
|
|
return -ERANGE;
|
|
}
|
|
|
|
// Copy the attribute value
|
|
memcpy(value, output, output_len);
|
|
free(output);
|
|
return output_len;
|
|
}
|
|
|
|
// Add setxattr function
|
|
static int fusescript_setxattr(const char *path, const char *name, const char *value, size_t size, int flags) {
|
|
char value_str[4096];
|
|
snprintf(value_str, sizeof(value_str), "%.*s", (int)size, value);
|
|
|
|
char command[4096];
|
|
snprintf(command, sizeof(command), "%s setxattr '%s' '%s' '%s'", script_path, path, name, value_str);
|
|
|
|
int ret = system(command);
|
|
return ret == 0 ? 0 : -EIO;
|
|
}
|
|
|
|
// Add listxattr function
|
|
static int fusescript_listxattr(const char *path, char *list, size_t size) {
|
|
char command[4096];
|
|
snprintf(command, sizeof(command), "%s listxattr '%s'", script_path, path);
|
|
|
|
FILE *fp = popen(command, "r");
|
|
if (!fp) {
|
|
return -errno;
|
|
}
|
|
|
|
char *output = NULL;
|
|
size_t output_len = 0;
|
|
char line[1024];
|
|
while (fgets(line, sizeof(line), fp)) {
|
|
size_t len = strlen(line);
|
|
output = realloc(output, output_len + len + 1);
|
|
memcpy(output + output_len, line, len);
|
|
output_len += len;
|
|
}
|
|
fprintf(stderr, "listxattr script output: %s\n", output);
|
|
fprintf(stderr, "listxattr output length: %zu\n", output_len);
|
|
|
|
if (output) {
|
|
output[output_len] = '\0';
|
|
}
|
|
|
|
int ret = pclose(fp);
|
|
if (ret != 0 || !output) {
|
|
free(output);
|
|
return -EIO;
|
|
}
|
|
|
|
if (size == 0) {
|
|
// Return the size of the required buffer
|
|
free(output);
|
|
return output_len;
|
|
} else if (size < output_len) {
|
|
// Buffer too small
|
|
free(output);
|
|
return -ERANGE;
|
|
}
|
|
|
|
// Copy the attribute list
|
|
memcpy(list, output, output_len);
|
|
free(output);
|
|
return output_len;
|
|
}
|
|
|
|
static const struct fuse_operations fusescript_ops = {
|
|
.getattr = fusescript_getattr,
|
|
.readdir = fusescript_readdir,
|
|
.open = fusescript_open,
|
|
.access = fusescript_access,
|
|
.read = fusescript_read,
|
|
.write = fusescript_write,
|
|
.mkdir = fusescript_mkdir,
|
|
.unlink = fusescript_unlink,
|
|
.rmdir = fusescript_rmdir,
|
|
.getxattr = fusescript_getxattr,
|
|
.setxattr = fusescript_setxattr,
|
|
.listxattr = fusescript_listxattr,
|
|
};
|
|
|
|
int main(int argc, char *argv[]) {
|
|
if (argc < 3) {
|
|
fprintf(stderr, "Usage: %s <script_or_serialdevice> <mountpoint>\n", argv[0]);
|
|
return 1;
|
|
}
|
|
|
|
if (strncmp(argv[2], "/dev/", 5) == 0) {
|
|
use_serial = 1;
|
|
strncpy(serial_device, argv[2], sizeof(serial_device) - 1);
|
|
serial_device[sizeof(serial_device) - 1] = '\0';
|
|
|
|
if (argc < 4) {
|
|
fprintf(stderr, "Missing mountpoint for serial device\n");
|
|
return 1;
|
|
}
|
|
|
|
script_path = realpath(argv[1], NULL);
|
|
if (!script_path) {
|
|
perror("realpath");
|
|
return 1;
|
|
}
|
|
|
|
argv[1] = argv[3];
|
|
return fuse_main(argc - 2, argv + 1, &fusescript_ops, NULL);
|
|
} else {
|
|
script_path = realpath(argv[1], NULL);
|
|
if (!script_path) {
|
|
perror("realpath");
|
|
return 1;
|
|
}
|
|
|
|
argv[1] = argv[2];
|
|
return fuse_main(argc - 1, argv + 1, &fusescript_ops, NULL);
|
|
}
|
|
}
|