/* Start the translator TRANS (of length TRANS_LEN) on NODE, which should be locked, and will be unlocked when this function returns. PARENT_PORT is a send right to use as the parent port passed to the translator. */ error_t _treefs_node_start_translator (struct treefs_node *node, char *trans, unsigned trans_len, file_t parent_port) { error_t err; int mode = O_READ | O_EXEC; struct treefs_auth *auth; file_t node_port; uid_t uid, gid; err = treefs_node_get_trans_auth (node, &auth); if (err) return err; if (!node->fsys->readonly && treefs_node_type (node) == S_IFREG) mode |= O_WRITE; /* Create the REALNODE port for the new filesystem. */ node_port = treefs_node_make_right (node, mode, parent_port, auth); mach_port_insert_right (mach_task_self (), node_port, node_port, MACH_MSG_TYPE_MAKE_SEND); mutex_unlock (&node->lock); /* XXXX Change libfshelp so that it take more than 1 uid/gid? */ uid = auth->nuids > 0 ? auth->uids[0] : -1; gid = auth->ngids > 0 ? auth->gids[0] : -1; /* XXX this should use fshelp_start_translator_long. */ err = fshelp_start_translator (&node->active_trans, NULL, trans, trans_len, parent_port, node_port, uid, gid); treefs_node_auth_unref (node, auth); return err; }
int main(int argc, char *argv[]) { error_t err; /* The filesystem node we're putting a translator on. */ char *node_name = 0; file_t node; /* The translator's arg vector, in '\0' separated format. */ char *argz = 0; size_t argz_len = 0; /* The control port for any active translator we start up. */ fsys_t active_control = MACH_PORT_NULL; /* Flags to pass to file_set_translator. */ int active_flags = 0; int passive_flags = 0; int lookup_flags = O_NOTRANS; int goaway_flags = 0; /* Various option flags. */ int passive = 0, active = 0, keep_active = 0, pause = 0, kill_active = 0, orphan = 0; int start = 0; int stack = 0; char *pid_file = NULL; int excl = 0; int timeout = DEFAULT_TIMEOUT * 1000; /* ms */ char *underlying_node_name = NULL; int underlying_lookup_flags; char **chroot_command = 0; char *chroot_chdir = "/"; /* Parse our options... */ error_t parse_opt (int key, char *arg, struct argp_state *state) { switch (key) { case ARGP_KEY_ARG: if (state->arg_num == 0) node_name = arg; else /* command */ { if (start) argp_error (state, "both --start and TRANSLATOR given"); error_t err = argz_create (state->argv + state->next - 1, &argz, &argz_len); if (err) error(3, err, "Can't create options vector"); state->next = state->argc; /* stop parsing */ } break; case ARGP_KEY_NO_ARGS: argp_usage (state); return EINVAL; case 'a': active = 1; break; case 's': start = 1; active = 1; /* start implies active */ break; case OPT_STACK: stack = 1; active = 1; /* stack implies active */ orphan = 1; /* stack implies orphan */ break; case 'p': passive = 1; break; case 'k': keep_active = 1; break; case 'g': kill_active = 1; break; case 'x': excl = 1; break; case 'P': pause = 1; break; case 'F': pid_file = strdup (arg); if (pid_file == NULL) error(3, ENOMEM, "Failed to duplicate argument"); break; case 'o': orphan = 1; break; case 'U': underlying_node_name = strdup (arg); if (underlying_node_name == NULL) error(3, ENOMEM, "Failed to duplicate argument"); break; case 'C': if (chroot_command) { argp_error (state, "--chroot given twice"); return EINVAL; } chroot_command = &state->argv[state->next]; while (state->next < state->argc) { if (!strcmp (state->argv[state->next], "--")) { state->argv[state->next++] = 0; if (chroot_command[0] == 0) { argp_error (state, "--chroot must be followed by a command"); return EINVAL; } return 0; } ++state->next; } argp_error (state, "--chroot command must be terminated with `--'"); return EINVAL; case OPT_CHROOT_CHDIR: if (arg[0] != '/') argp_error (state, "--chroot-chdir must be absolute"); chroot_chdir = arg; break; case 'c': lookup_flags |= O_CREAT; break; case 'L': lookup_flags &= ~O_NOTRANS; break; case 'R': goaway_flags |= FSYS_GOAWAY_RECURSE; break; case 'S': goaway_flags |= FSYS_GOAWAY_NOSYNC; break; case 'f': goaway_flags |= FSYS_GOAWAY_FORCE; break; /* Use atof so the user can specifiy fractional timeouts. */ case 't': timeout = atof (arg) * 1000.0; break; default: return ARGP_ERR_UNKNOWN; } return 0; } struct argp argp = {options, parse_opt, args_doc, doc}; argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, 0); if (stack) { underlying_node_name = node_name; underlying_lookup_flags = lookup_flags && ~O_NOTRANS; } else underlying_lookup_flags = lookup_flags; if (!active && !passive && !chroot_command) passive = 1; /* By default, set the passive translator. */ if (passive) passive_flags = FS_TRANS_SET | (excl ? FS_TRANS_EXCL : 0); if (active) active_flags = FS_TRANS_SET | (excl ? FS_TRANS_EXCL : 0) | (orphan ? FS_TRANS_ORPHAN : 0); if (passive && !active) { /* When setting just the passive, decide what to do with any active. */ if (kill_active) /* Make it go away. */ active_flags = FS_TRANS_SET; else if (! keep_active) /* Ensure that there isn't one. */ active_flags = FS_TRANS_SET | FS_TRANS_EXCL; } if (start) { /* Retrieve the passive translator record in argz. */ mach_port_t node = file_name_lookup (node_name, lookup_flags, 0); if (node == MACH_PORT_NULL) error (4, errno, "%s", node_name); char buf[1024]; argz = buf; argz_len = sizeof (buf); err = file_get_translator (node, &argz, &argz_len); if (err == EINVAL) error (4, 0, "%s: no passive translator record found", node_name); if (err) error (4, err, "%s", node_name); mach_port_deallocate (mach_task_self (), node); } if ((active || chroot_command) && argz_len > 0) { /* Error during file lookup; we use this to avoid duplicating error messages. */ error_t open_err = 0; /* The callback to start_translator opens NODE as a side effect. */ error_t open_node (int flags, mach_port_t *underlying, mach_msg_type_name_t *underlying_type, task_t task, void *cookie) { if (pause) { fprintf (stderr, "Translator pid: %d\nPausing...", task2pid (task)); getchar (); } if (pid_file != NULL) { FILE *h; h = fopen (pid_file, "w"); if (h == NULL) error (4, errno, "Failed to open pid file"); fprintf (h, "%i\n", task2pid (task)); fclose (h); } node = file_name_lookup (node_name, flags | lookup_flags, 0666); if (node == MACH_PORT_NULL) { open_err = errno; return open_err; } if (underlying_node_name) { *underlying = file_name_lookup (underlying_node_name, flags | underlying_lookup_flags, 0666); if (! MACH_PORT_VALID (*underlying)) { /* For the error message. */ node_name = underlying_node_name; open_err = errno; return open_err; } } else *underlying = node; *underlying_type = MACH_MSG_TYPE_COPY_SEND; return 0; } err = fshelp_start_translator (open_node, NULL, argz, argz, argz_len, timeout, &active_control); if (err) /* If ERR is due to a problem opening the translated node, we print that name, otherwise, the name of the translator. */ error(4, err, "%s", (err == open_err) ? node_name : argz); }
/* Mount one filesystem. */ static error_t do_mount (struct fs *fs, int remount) { error_t err; char *fsopts, *o; size_t fsopts_len; char *mntopts; size_t mntopts_len; fsys_t mounted; inline void explain (const char *command) { if (verbose) { const char *o; printf ("%s %s", command, fs->mntent.mnt_dir); for (o = fsopts; o; o = argz_next (fsopts, fsopts_len, o)) printf (" %s", o); putchar ('\n'); } } err = fs_fsys (fs, &mounted); if (err) { error (0, err, "cannot determine if %s is already mounted", fs->mntent.mnt_fsname); return err; } /* Produce an argz of translator option arguments from the given FS's options and the command-line options. */ #define ARGZ(call) \ err = argz_##call; \ if (err) \ error (3, ENOMEM, "collecting mount options"); \ if (fs->mntent.mnt_opts) { /* Append the fstab options to any specified on the command line. */ ARGZ (create_sep (fs->mntent.mnt_opts, ',', &mntopts, &mntopts_len)); /* Remove the `noauto' and `bind' options, since they're for us not the filesystem. */ for (o = mntopts; o; o = argz_next (mntopts, mntopts_len, o)) { if (strcmp (o, MNTOPT_NOAUTO) == 0) argz_delete (&mntopts, &mntopts_len, o); if (strcmp (o, "bind") == 0) { fs->mntent.mnt_type = strdup ("firmlink"); if (!fs->mntent.mnt_type) error (3, ENOMEM, "failed to allocate memory"); argz_delete (&mntopts, &mntopts_len, o); } } ARGZ (append (&mntopts, &mntopts_len, options, options_len)); } else { mntopts = options; mntopts_len = options_len; } /* Convert the list of options into a list of switch arguments. */ fsopts = 0; fsopts_len = 0; for (o = mntopts; o; o = argz_next (mntopts, mntopts_len, o)) if (*o == '-') /* Allow letter opts `-o -r,-E', BSD style. */ { ARGZ (add (&fsopts, &fsopts_len, o)); } else if ((strcmp (o, "defaults") != 0) && (strlen (o) != 0)) { /* Prepend `--' to the option to make a long option switch, e.g. `--ro' or `--rsize=1024'. */ char arg[2 + strlen (o) + 1]; arg[0] = arg[1] = '-'; memcpy (&arg[2], o, sizeof arg - 2); ARGZ (add (&fsopts, &fsopts_len, arg)); } if (mntopts != options) free (mntopts); #undef ARGZ if (remount) { if (mounted == MACH_PORT_NULL) { error (0, 0, "%s not already mounted", fs->mntent.mnt_fsname); return EBUSY; } /* Send an RPC to request the new options, including --update. */ explain ("fsysopts"); err = fsys_set_options (mounted, fsopts, fsopts_len, 0); if (err) error (0, err, "cannot remount %s", fs->mntent.mnt_fsname); return err; } else { /* Error during file lookup; we use this to avoid duplicating error messages. */ error_t open_err = 0; /* The control port for any active translator we start up. */ fsys_t active_control; file_t node; /* Port to the underlying node. */ struct fstype *type; /* The callback to start_translator opens NODE as a side effect. */ error_t open_node (int flags, mach_port_t *underlying, mach_msg_type_name_t *underlying_type, task_t task, void *cookie) { node = file_name_lookup (fs->mntent.mnt_dir, flags | O_NOTRANS, 0666); if (node == MACH_PORT_NULL) { open_err = errno; return open_err; } *underlying = node; *underlying_type = MACH_MSG_TYPE_COPY_SEND; return 0; } /* Do not fail if there is an active translator if --fake is given. This mimics Linux mount utility more closely which just looks into the mtab file. */ if (mounted != MACH_PORT_NULL && !fake) { error (0, 0, "%s already mounted", fs->mntent.mnt_fsname); return EBUSY; } if (strcmp (fs->mntent.mnt_type, "auto") == 0) { #if HAVE_BLKID char *type = blkid_get_tag_value (NULL, "TYPE", fs->mntent.mnt_fsname); if (! type) { error (0, 0, "failed to detect file system type"); return EFTYPE; } else { if (strcmp (type, "vfat") == 0) fs->mntent.mnt_type = strdup ("fat"); else fs->mntent.mnt_type = strdup (type); if (! fs->mntent.mnt_type) error (3, ENOMEM, "failed to allocate memory"); } #else fs->mntent.mnt_type = strdup ("ext2"); if (! fs->mntent.mnt_type) error (3, ENOMEM, "failed to allocate memory"); #endif } err = fs_type (fs, &type); if (err) { error (0, err, "%s: cannot determine filesystem type", fs->mntent.mnt_fsname); return err; } if (type->program == 0) { error (0, 0, "%s: filesystem type `%s' unknown", fs->mntent.mnt_fsname, type->name); return EFTYPE; } /* Stick the translator program name in front of the option switches. */ err = argz_insert (&fsopts, &fsopts_len, fsopts, type->program); /* Now stick the device name on the end as the last argument. */ if (!err) err = argz_add (&fsopts, &fsopts_len, fs->mntent.mnt_fsname); if (err) error (3, ENOMEM, "collecting mount options"); /* Now we have a translator command line argz in FSOPTS. */ if (fake) { /* Fake the translator startup. */ mach_port_t underlying; mach_msg_type_name_t underlying_type; err = open_node (O_READ, &underlying, &underlying_type, 0, NULL); if (err) error (1, errno, "cannot mount on %s", fs->mntent.mnt_dir); mach_port_deallocate (mach_task_self (), underlying); /* See if the translator is at least executable. */ if (access(type->program, X_OK) == -1) error (1, errno, "can not execute %s", type->program); return 0; } explain ("settrans -a"); err = fshelp_start_translator (open_node, NULL, fsopts, fsopts, fsopts_len, timeout, &active_control); /* If ERR is due to a problem opening the translated node, we print that name, otherwise, the name of the translator. */ if (open_err) error (0, open_err, "cannot mount on %s", fs->mntent.mnt_dir); else if (err) error (0, err, "cannot start translator %s", fsopts); else { err = file_set_translator (node, 0, FS_TRANS_SET|FS_TRANS_EXCL, 0, 0, 0, active_control, MACH_MSG_TYPE_COPY_SEND); if (err == EBUSY) error (0, 0, "%s already mounted on", fs->mntent.mnt_dir); else if (err) error (0, err, "cannot set translator on %s", fs->mntent.mnt_dir); if (err) fsys_goaway (active_control, FSYS_GOAWAY_FORCE); mach_port_deallocate (mach_task_self (), active_control); } return err; }