// SPDX: GPL-3.0-or-later #define FUSE_USE_VERSION 35 #include #include #include #include #include #include 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 \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); } }