/* grub-mount.c - FUSE driver for filesystems that GRUB understands */ /* * GRUB -- GRand Unified Bootloader * Copyright (C) 2008,2009,2010 Free Software Foundation, Inc. * * GRUB 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. * * GRUB 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 GRUB. If not, see . */ #define FUSE_USE_VERSION 26 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "progname.h" #include "argp.h" static char *root = NULL; static char **images = NULL; static char *debug_str = NULL; static char **fuse_args = NULL; static int fuse_argc = 0; static int num_disks = 0; static grub_err_t execute_command (char *name, int n, char **args) { grub_command_t cmd; cmd = grub_command_find (name); if (! cmd) grub_util_error (_("can\'t find command %s"), name); return (cmd->func) (cmd, n, args); } static int fuse_getattr (const char *path, struct stat *st) { char *filename, *pathname, *path2; const char *pathname_t; grub_fs_t fs; grub_device_t dev; struct grub_dirhook_info file_info; int file_exists = 0; /* A hook for iterating directories. */ auto int find_file (const char *cur_filename, const struct grub_dirhook_info *info); int find_file (const char *cur_filename, const struct grub_dirhook_info *info) { if ((info->case_insensitive ? grub_strcasecmp (cur_filename, filename) : grub_strcmp (cur_filename, filename)) == 0) { file_info = *info; file_exists = 1; return 1; } return 0; } if (path[0] == '/' && path[1] == 0) { st->st_dev = 0; st->st_ino = 0; st->st_mode = 0555 | S_IFDIR; st->st_uid = 0; st->st_gid = 0; st->st_rdev = 0; st->st_size = 0; st->st_blksize = 512; st->st_blocks = (st->st_blksize + 511) >> 9; st->st_atime = st->st_mtime = st->st_ctime = 0; return 0; } file_exists = 0; dev = grub_device_open (0); if (! dev) return -1; fs = grub_fs_probe (dev); if (! fs) { grub_device_close (dev); return -1; } pathname_t = grub_strchr (path, ')'); if (! pathname_t) pathname_t = path; else pathname_t++; pathname = xstrdup (pathname_t); /* Remove trailing '/'. */ while (*pathname && pathname[grub_strlen (pathname) - 1] == '/') pathname[grub_strlen (pathname) - 1] = 0; /* Split into path and filename. */ filename = grub_strrchr (pathname, '/'); if (! filename) { path2 = grub_strdup ("/"); filename = pathname; } else { filename++; path2 = grub_strdup (pathname); path2[filename - pathname] = 0; } /* It's the whole device. */ (fs->dir) (dev, path2, find_file); grub_device_close (dev); grub_free (path2); if (!file_exists) return -1; st->st_dev = 0; st->st_ino = 0; st->st_mode = file_info.dir ? (0555 | S_IFDIR) : (0444 | S_IFREG); st->st_uid = 0; st->st_gid = 0; st->st_rdev = 0; if (!file_info.dir) { grub_file_t file; file = grub_file_open (path); if (! file) { grub_print_error (); return -1; } st->st_size = file->size; grub_file_close (file); } else st->st_size = 0; st->st_blksize = 512; st->st_blocks = (st->st_size + 511) >> 9; st->st_atime = st->st_mtime = st->st_ctime = file_info.mtimeset ? file_info.mtime : 0; return 0; } static int fuse_opendir (const char *path, struct fuse_file_info *fi) { return 0; } /* FIXME */ static grub_file_t files[65536]; static int first_fd = 1; static int fuse_open (const char *path, struct fuse_file_info *fi __attribute__ ((unused))) { grub_file_t file; file = grub_file_open (path); if (! file) { grub_print_error (); return -1; } files[first_fd++] = file; fi->fh = first_fd; files[first_fd++] = file; return 0; } static int fuse_read (const char *path, char *buf, size_t sz, off_t off, struct fuse_file_info *fi) { grub_file_t file = files[fi->fh]; if (off > file->size) return -1; file->offset = off; return grub_file_read (file, buf, sz); } static int fuse_release (const char *path, struct fuse_file_info *fi) { grub_file_close (files[fi->fh]); files[fi->fh] = NULL; return 0; } static int fuse_readdir (const char *path, void *buf, fuse_fill_dir_t fill, off_t off, struct fuse_file_info *fi) { char *pathname; grub_fs_t fs; grub_device_t dev; auto int call_fill (const char *filename, const struct grub_dirhook_info *info); int call_fill (const char *filename, const struct grub_dirhook_info *info) { fill (buf, filename, NULL, 0); return 0; } dev = grub_device_open (0); if (! dev) { return 1; } fs = grub_fs_probe (dev); if (! fs) { grub_device_close (dev); return 1; } pathname = xstrdup (path); /* Remove trailing '/'. */ while (pathname [0] && pathname[1] && pathname[grub_strlen (pathname) - 1] == '/') pathname[grub_strlen (pathname) - 1] = 0; (fs->dir) (dev, pathname, call_fill); grub_device_close (dev); free (pathname); return 0; } struct fuse_operations grub_opers = { .getattr = fuse_getattr, .open = fuse_open, .release = fuse_release, .opendir = fuse_opendir, .readdir = fuse_readdir, .read = fuse_read }; static void fuse_init (void) { int i; for (i = 0; i < num_disks; i++) { char *argv[2]; char *host_file; char *loop_name; loop_name = grub_xasprintf ("loop%d", i); if (!loop_name) grub_util_error (grub_errmsg); host_file = grub_xasprintf ("(host)%s", images[i]); if (!host_file) grub_util_error (grub_errmsg); argv[0] = loop_name; argv[1] = host_file; if (execute_command ("loopback", 2, argv)) grub_util_error (_("loopback command fails")); grub_free (loop_name); grub_free (host_file); } grub_lvm_fini (); grub_mdraid09_fini (); grub_mdraid1x_fini (); grub_raid_fini (); grub_raid_init (); grub_mdraid09_init (); grub_mdraid1x_init (); grub_lvm_init (); fuse_main (fuse_argc, fuse_args, &grub_opers, NULL); for (i = 0; i < num_disks; i++) { char *argv[2]; char *loop_name; loop_name = grub_xasprintf ("loop%d", i); if (!loop_name) grub_util_error (grub_errmsg); argv[0] = "-d"; argv[1] = loop_name; execute_command ("loopback", 2, argv); grub_free (loop_name); } } static struct argp_option options[] = { {"root", 'r', N_("DEVICE_NAME"), 0, N_("Set root device."), 2}, {"debug", 'd', "S", 0, N_("Set debug environment variable."), 2}, {"verbose", 'v', NULL, OPTION_ARG_OPTIONAL, N_("Print verbose messages."), 2}, {0, 0, 0, 0, 0, 0} }; /* Print the version information. */ static void print_version (FILE *stream, struct argp_state *state) { fprintf (stream, "%s (%s) %s\n", program_name, PACKAGE_NAME, PACKAGE_VERSION); } void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version; error_t argp_parser (int key, char *arg, struct argp_state *state) { char *p; switch (key) { case 'r': root = arg; return 0; case 'd': debug_str = arg; return 0; case 'v': verbosity++; return 0; case ARGP_KEY_ARG: if (arg[0] != '-') break; default: if (!arg) return 0; fuse_args = xrealloc (fuse_args, (fuse_argc + 1) * sizeof (fuse_args[0])); fuse_args[fuse_argc] = xstrdup (arg); fuse_argc++; return 0; } if (arg[0] != '/') { fprintf (stderr, "%s", _("Must use absolute path.\n")); argp_usage (state); } images = xrealloc (images, (num_disks + 1) * sizeof (images[0])); images[num_disks] = xstrdup (arg); num_disks++; return 0; } struct argp argp = { options, argp_parser, N_("IMAGE1 [IMAGE2 ...] MOUNTPOINT"), N_("Debug tool for filesystem driver."), NULL, NULL, NULL }; int main (int argc, char *argv[]) { char *default_root, *alloc_root; set_program_name (argv[0]); grub_util_init_nls (); fuse_args = xrealloc (fuse_args, (fuse_argc + 1) * sizeof (fuse_args[0])); fuse_args[fuse_argc] = xstrdup (argv[0]); fuse_argc++; argp_parse (&argp, argc, argv, 0, 0, 0); if (num_disks < 2) grub_util_error ("need an image and mountpoint"); fuse_args = xrealloc (fuse_args, (fuse_argc + 2) * sizeof (fuse_args[0])); fuse_args[fuse_argc] = images[num_disks - 1]; fuse_argc++; num_disks--; fuse_args[fuse_argc] = NULL; /* Initialize all modules. */ grub_init_all (); if (debug_str) grub_env_set ("debug", debug_str); default_root = (num_disks == 1) ? "loop0" : "md0"; alloc_root = 0; if (root) { if ((*root >= '0') && (*root <= '9')) { alloc_root = xmalloc (strlen (default_root) + strlen (root) + 2); sprintf (alloc_root, "%s,%s", default_root, root); root = alloc_root; } } else root = default_root; grub_env_set ("root", root); if (alloc_root) free (alloc_root); /* Do it. */ fuse_init (); /* Free resources. */ grub_fini_all (); return 0; }