From 598bf9199e89a53bc0ffd2e32141a42d5ec85184 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Thu, 15 Oct 2009 17:20:41 +0000 Subject: [PATCH] Reworked the server commands. Track mounts. --create does now work as expected. --- g13/Makefile.am | 1 + g13/backend.c | 9 +- g13/backend.h | 6 +- g13/be-encfs.c | 27 ++-- g13/be-encfs.h | 6 +- g13/create.c | 4 +- g13/g13.c | 8 +- g13/mount.c | 110 ++++++++++++-- g13/mount.h | 7 +- g13/mountinfo.c | 183 ++++++++++++++++++++++++ g13/mountinfo.h | 40 ++++++ g13/runner.c | 66 +++++++-- g13/runner.h | 6 + g13/server.c | 373 ++++++++++++++++++++++++++---------------------- 14 files changed, 629 insertions(+), 217 deletions(-) create mode 100644 g13/mountinfo.c create mode 100644 g13/mountinfo.h diff --git a/g13/Makefile.am b/g13/Makefile.am index e65684c0f..3143e8a4d 100644 --- a/g13/Makefile.am +++ b/g13/Makefile.am @@ -33,6 +33,7 @@ g13_SOURCES = \ server.c server.h \ create.c create.h \ mount.c mount.h \ + mountinfo.c mountinfo.h \ call-gpg.c call-gpg.h \ runner.c runner.h \ backend.c backend.h \ diff --git a/g13/backend.c b/g13/backend.c index 08aec324f..531e745ed 100644 --- a/g13/backend.c +++ b/g13/backend.c @@ -101,14 +101,15 @@ be_create_new_keys (int conttype, membuf_t *mb) /* Dispatcher to the backend's create function. */ gpg_error_t be_create_container (ctrl_t ctrl, int conttype, - const char *fname, int fd, tupledesc_t tuples) + const char *fname, int fd, tupledesc_t tuples, + unsigned int *r_id) { (void)fd; /* Not yet used. */ switch (conttype) { case CONTTYPE_ENCFS: - return be_encfs_create_container (ctrl, fname, tuples); + return be_encfs_create_container (ctrl, fname, tuples, r_id); default: return no_such_backend (conttype); @@ -120,12 +121,12 @@ be_create_container (ctrl_t ctrl, int conttype, gpg_error_t be_mount_container (ctrl_t ctrl, int conttype, const char *fname, const char *mountpoint, - tupledesc_t tuples) + tupledesc_t tuples, unsigned int *r_id) { switch (conttype) { case CONTTYPE_ENCFS: - return be_encfs_mount_container (ctrl, fname, mountpoint, tuples); + return be_encfs_mount_container (ctrl, fname, mountpoint, tuples, r_id); default: return no_such_backend (conttype); diff --git a/g13/backend.h b/g13/backend.h index 7cdde9e4b..2048697dc 100644 --- a/g13/backend.h +++ b/g13/backend.h @@ -30,10 +30,12 @@ gpg_error_t be_create_new_keys (int conttype, membuf_t *mb); gpg_error_t be_create_container (ctrl_t ctrl, int conttype, const char *fname, int fd, - tupledesc_t tuples); + tupledesc_t tuples, + unsigned int *r_id); gpg_error_t be_mount_container (ctrl_t ctrl, int conttype, const char *fname, const char *mountpoint, - tupledesc_t tuples); + tupledesc_t tuples, + unsigned int *r_id); #endif /*G13_BACKEND_H*/ diff --git a/g13/be-encfs.c b/g13/be-encfs.c index de3209a91..06e8792b3 100644 --- a/g13/be-encfs.c +++ b/g13/be-encfs.c @@ -211,7 +211,8 @@ encfs_handler_cleanup (void *opaque) /* Run the encfs tool. */ static gpg_error_t run_encfs_tool (ctrl_t ctrl, enum encfs_cmds cmd, - const char *rawdir, const char *mountpoint, tupledesc_t tuples) + const char *rawdir, const char *mountpoint, tupledesc_t tuples, + unsigned int *r_id) { gpg_error_t err; encfs_parm_t parm; @@ -240,15 +241,9 @@ run_encfs_tool (ctrl_t ctrl, enum encfs_cmds cmd, goto leave; } - { - static int namecounter; - char buffer[50]; - - snprintf (buffer, sizeof buffer, "encfs-%d", ++namecounter); - err = runner_new (&runner, buffer); - if (err) - goto leave; - } + err = runner_new (&runner, "encfs"); + if (err) + goto leave; err = gnupg_create_inbound_pipe (inbound); if (!err) @@ -295,6 +290,7 @@ run_encfs_tool (ctrl_t ctrl, enum encfs_cmds cmd, if (err) goto leave; + *r_id = runner_get_rid (runner); log_info ("running `%s' in the background\n", pgmname); leave: @@ -400,7 +396,8 @@ be_encfs_create_new_keys (membuf_t *mb) /* Create the container described by the filename FNAME and the keyblob information in TUPLES. */ gpg_error_t -be_encfs_create_container (ctrl_t ctrl, const char *fname, tupledesc_t tuples) +be_encfs_create_container (ctrl_t ctrl, const char *fname, tupledesc_t tuples, + unsigned int *r_id) { gpg_error_t err; int dummy; @@ -426,7 +423,7 @@ be_encfs_create_container (ctrl_t ctrl, const char *fname, tupledesc_t tuples) } err = run_encfs_tool (ctrl, ENCFS_CMD_CREATE, containername, mountpoint, - tuples); + tuples, r_id); /* In any case remove the temporary mount point. */ if (rmdir (mountpoint)) @@ -442,11 +439,11 @@ be_encfs_create_container (ctrl_t ctrl, const char *fname, tupledesc_t tuples) /* Mount the container described by the filename FNAME and the keyblob - information in TUPLES. */ + information in TUPLES. On success the runner id is stored at R_ID. */ gpg_error_t be_encfs_mount_container (ctrl_t ctrl, const char *fname, const char *mountpoint, - tupledesc_t tuples) + tupledesc_t tuples, unsigned int *r_id) { gpg_error_t err; int dummy; @@ -464,7 +461,7 @@ be_encfs_mount_container (ctrl_t ctrl, goto leave; err = run_encfs_tool (ctrl, ENCFS_CMD_MOUNT, containername, mountpoint, - tuples); + tuples, r_id); leave: xfree (containername); diff --git a/g13/be-encfs.h b/g13/be-encfs.h index c6c8396c5..8ac35f947 100644 --- a/g13/be-encfs.h +++ b/g13/be-encfs.h @@ -28,12 +28,14 @@ gpg_error_t be_encfs_create_new_keys (membuf_t *mb); gpg_error_t be_encfs_create_container (ctrl_t ctrl, const char *fname, - tupledesc_t tuples); + tupledesc_t tuples, + unsigned int *r_id); gpg_error_t be_encfs_mount_container (ctrl_t ctrl, const char *fname, const char *mountpoint, - tupledesc_t tuples); + tupledesc_t tuples, + unsigned int *r_id); #endif /*G13_BE_ENCFS_H*/ diff --git a/g13/create.c b/g13/create.c index 0279c337d..9de9edc0b 100644 --- a/g13/create.c +++ b/g13/create.c @@ -231,6 +231,7 @@ g13_create_container (ctrl_t ctrl, const char *filename) char *detachedname = NULL; int detachedisdir; tupledesc_t tuples = NULL; + unsigned int dummy_rid; /* A quick check to see that no container with that name already exists. */ @@ -304,7 +305,8 @@ g13_create_container (ctrl_t ctrl, const char *filename) /* Create and append the container. FIXME: We should pass the estream object in addition to the filename, so that the backend can append the container to the g13 file. */ - err = be_create_container (ctrl, ctrl->conttype, filename, -1, tuples); + err = be_create_container (ctrl, ctrl->conttype, filename, -1, tuples, + &dummy_rid); leave: diff --git a/g13/g13.c b/g13/g13.c index 288689457..453fa9e65 100644 --- a/g13/g13.c +++ b/g13/g13.c @@ -39,7 +39,8 @@ #include "server.h" #include "runner.h" #include "create.h" -#include "./mount.h" +#include "mount.h" +#include "mountinfo.h" enum cmd_and_opt_values { @@ -707,6 +708,8 @@ main ( int argc, char **argv) if (err) log_error ("server exited with error: %s <%s>\n", gpg_strerror (err), gpg_strsource (err)); + else + shutdown_pending++; } break; @@ -719,6 +722,8 @@ main ( int argc, char **argv) if (err) log_error ("error creating a new container: %s <%s>\n", gpg_strerror (err), gpg_strsource (err)); + else + shutdown_pending++; } break; @@ -807,6 +812,7 @@ handle_signal (int signo) case SIGUSR1: log_info ("SIGUSR1 received - printing internal information:\n"); pth_ctrl (PTH_CTRL_DUMPSTATE, log_get_stream ()); + mountinfo_dump_all (); break; case SIGUSR2: diff --git a/g13/mount.c b/g13/mount.c index 85851e9a8..dc23b6b2b 100644 --- a/g13/mount.c +++ b/g13/mount.c @@ -35,6 +35,8 @@ #include "utils.h" #include "call-gpg.h" #include "estream.h" +#include "mountinfo.h" +#include "runner.h" /* Parse the header prefix and return the length of the entire header. */ @@ -89,28 +91,23 @@ parse_header (const char *filename, } - -/* Read the keyblob at FILENAME. The caller should have acquired a - lockfile and checked that the file exists. */ +/* Read the prefix of the keyblob and do some basic parsing. On + success returns an open estream file at R_FP and the length of the + header at R_HEADERLEN. */ static gpg_error_t -read_keyblob (const char *filename, - void **r_enckeyblob, size_t *r_enckeybloblen) +read_keyblob_prefix (const char *filename, estream_t *r_fp, size_t *r_headerlen) { gpg_error_t err; estream_t fp; unsigned char packet[32]; - size_t headerlen, msglen; - void *msg = NULL; - *r_enckeyblob = NULL; - *r_enckeybloblen = 0; + *r_fp = NULL; fp = es_fopen (filename, "rb"); if (!fp) { err = gpg_error_from_syserror (); - log_error ("error reading `%s': %s\n", - filename, gpg_strerror (err)); + log_error ("error reading `%s': %s\n", filename, gpg_strerror (err)); return err; } @@ -120,10 +117,35 @@ read_keyblob (const char *filename, err = gpg_error_from_syserror (); log_error ("error reading the header of `%s': %s\n", filename, gpg_strerror (err)); - goto leave; + es_fclose (fp); + return err; } - err = parse_header (filename, packet, 32, &headerlen); + err = parse_header (filename, packet, 32, r_headerlen); + if (err) + es_fclose (fp); + else + *r_fp = fp; + + return err; +} + + +/* Read the keyblob at FILENAME. The caller should have acquired a + lockfile and checked that the file exists. */ +static gpg_error_t +read_keyblob (const char *filename, + void **r_enckeyblob, size_t *r_enckeybloblen) +{ + gpg_error_t err; + estream_t fp = NULL; + size_t headerlen, msglen; + void *msg = NULL; + + *r_enckeyblob = NULL; + *r_enckeybloblen = 0; + + err = read_keyblob_prefix (filename, &fp, &headerlen); if (err) goto leave; @@ -227,6 +249,7 @@ g13_mount_container (ctrl_t ctrl, const char *filename, const char *mountpoint) size_t n; const unsigned char *value; int conttype; + unsigned int rid; /* A quick check to see whether the container exists. */ if (access (filename, R_OK)) @@ -292,7 +315,13 @@ g13_mount_container (ctrl_t ctrl, const char *filename, const char *mountpoint) err = gpg_error (GPG_ERR_NOT_SUPPORTED); goto leave; } - err = be_mount_container (ctrl, conttype, filename, mountpoint, tuples); + err = be_mount_container (ctrl, conttype, filename, mountpoint, tuples, &rid); + if (!err) + { + err = mountinfo_add_mount (filename, mountpoint, conttype, rid); + /* Fixme: What shall we do if this fails? Add a provisional + mountinfo entry first and remove it on error? */ + } leave: destroy_tupledesc (tuples); @@ -301,3 +330,56 @@ g13_mount_container (ctrl_t ctrl, const char *filename, const char *mountpoint) destroy_dotlock (lock); return err; } + + +/* Unmount the container with name FILENAME or the one mounted at + MOUNTPOINT. If both are given the FILENAME takes precedence. */ +gpg_error_t +g13_umount_container (ctrl_t ctrl, const char *filename, const char *mountpoint) +{ + gpg_error_t err; + unsigned int rid; + runner_t runner; + + (void)ctrl; + + if (!filename && !mountpoint) + return gpg_error (GPG_ERR_ENOENT); + err = mountinfo_find_mount (filename, mountpoint, &rid); + if (err) + return err; + + runner = runner_find_by_rid (rid); + if (!runner) + { + log_error ("runner %u not found\n", rid); + return gpg_error (GPG_ERR_NOT_FOUND); + } + + runner_cancel (runner); + runner_release (runner); + + return 0; +} + + +/* Test whether the container with name FILENAME is a suitable G13 + container. This function may even be called on a mounted + container. */ +gpg_error_t +g13_is_container (ctrl_t ctrl, const char *filename) +{ + gpg_error_t err; + estream_t fp = NULL; + size_t dummy; + + (void)ctrl; + + /* Read just the prefix of the header. */ + err = read_keyblob_prefix (filename, &fp, &dummy); + if (!err) + es_fclose (fp); + return err; +} + + diff --git a/g13/mount.h b/g13/mount.h index 03b8264c9..f99426494 100644 --- a/g13/mount.h +++ b/g13/mount.h @@ -1,4 +1,4 @@ -/* mmount.h - Defs to mount a crypto container +/* mount.h - Defs to mount a crypto container * Copyright (C) 2009 Free Software Foundation, Inc. * * This file is part of GnuPG. @@ -23,6 +23,11 @@ gpg_error_t g13_mount_container (ctrl_t ctrl, const char *filename, const char *mountpoint); +gpg_error_t g13_umount_container (ctrl_t ctrl, + const char *filename, + const char *mountpoint); + +gpg_error_t g13_is_container (ctrl_t ctrl, const char *filename); #endif /*G13_MOUNT_H*/ diff --git a/g13/mountinfo.c b/g13/mountinfo.c new file mode 100644 index 000000000..100c1e475 --- /dev/null +++ b/g13/mountinfo.c @@ -0,0 +1,183 @@ +/* mountinfo.c - Track infos about mounts + * Copyright (C) 2009 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG 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. + * + * GnuPG 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "g13.h" +#include "i18n.h" +#include "mountinfo.h" + +#include "keyblob.h" +#include "utils.h" + + + +/* The object to keep track of mount information. */ +struct mounttable_s +{ + int in_use; /* The slot is in use. */ + char *container; /* Name of the container. */ + char *mountpoint; /* Name of the mounttype. */ + int conttype; /* Type of the container. */ + unsigned int rid; /* Identifier of the runner task. */ +}; + + +/* The allocated table of mounts and its size. */ +static mtab_t mounttable; +size_t mounttable_size; + + + +/* Add CONTAINER,MOUNTPOINT,CONTTYPE,RID to the mounttable. */ +gpg_error_t +mountinfo_add_mount (const char *container, const char *mountpoint, + int conttype, unsigned int rid) +{ + size_t idx; + mtab_t m; + + for (idx=0; idx < mounttable_size; idx++) + if (!mounttable[idx].in_use) + break; + if (!(idx < mounttable_size)) + { + size_t nslots = mounttable_size; + + mounttable_size += 10; + m = xtrycalloc (mounttable_size, sizeof *mounttable); + if (!m) + return gpg_error_from_syserror (); + if (mounttable) + { + for (idx=0; idx < nslots; idx++) + m[idx] = mounttable[idx]; + xfree (mounttable); + } + mounttable = m; + m = mounttable + nslots; + assert (!m->in_use); + } + else + m = mounttable + idx; + + m->container = xtrystrdup (container); + if (!m->container) + return gpg_error_from_syserror (); + m->mountpoint = xtrystrdup (mountpoint); + if (!m->mountpoint) + { + xfree (m->container); + m->container = NULL; + return gpg_error_from_syserror (); + } + m->conttype = conttype; + m->rid = rid; + m->in_use = 1; + + return 0; +} + + +/* Remove a mount info. Either the CONTAINER, the MOUNTPOINT or the + RID must be given. The first argument given is used. */ +gpg_error_t +mountinfo_del_mount (const char *container, const char *mountpoint, + unsigned int rid) +{ + gpg_error_t err; + size_t idx; + mtab_t m; + + /* If a container or mountpint is givem search the RID via the + standard find fucntion. */ + if (container || mountpoint) + { + err = mountinfo_find_mount (container, mountpoint, &rid); + if (err) + return err; + } + + /* Find via RID and delete. */ + for (idx=0, m = mounttable; idx < mounttable_size; idx++, m++) + if (m->in_use && m->rid == rid) + { + m->in_use = 0; + xfree (m->container); + m->container = NULL; + xfree (m->mountpoint); + m->mountpoint = NULL; + return 0; + } + return gpg_error (GPG_ERR_NOT_FOUND); +} + + +/* Find a mount and return its rid at R_RID. If CONTAINER is given, + the search is done by the container name, if it is not given the + search is done by MOUNTPOINT. */ +gpg_error_t +mountinfo_find_mount (const char *container, const char *mountpoint, + unsigned int *r_rid) +{ + size_t idx; + mtab_t m; + + if (container) + { + for (idx=0, m = mounttable; idx < mounttable_size; idx++, m++) + if (m->in_use && !strcmp (m->container, container)) + break; + } + else if (mountpoint) + { + for (idx=0, m = mounttable; idx < mounttable_size; idx++, m++) + if (m->in_use && !strcmp (m->mountpoint, mountpoint)) + break; + } + else + idx = mounttable_size; + if (!(idx < mounttable_size)) + return gpg_error (GPG_ERR_NOT_FOUND); + + *r_rid = m->rid; + return 0; +} + + +/* Dump all info to the log stream. */ +void +mountinfo_dump_all (void) +{ + size_t idx; + mtab_t m; + + for (idx=0, m = mounttable; idx < mounttable_size; idx++, m++) + if (m->in_use) + log_info ("mtab[%d] %s on %s type %d rid %u\n", + idx, m->container, m->mountpoint, m->conttype, m->rid); +} + diff --git a/g13/mountinfo.h b/g13/mountinfo.h new file mode 100644 index 000000000..187dff0db --- /dev/null +++ b/g13/mountinfo.h @@ -0,0 +1,40 @@ +/* mountinfo.h - Track infos about mounts + * Copyright (C) 2009 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG 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. + * + * GnuPG 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 . + */ + +#ifndef G13_MOUNTINFO_H +#define G13_MOUNTINFO_H + +struct mounttable_s; +typedef struct mounttable_s *mtab_t; + +gpg_error_t mountinfo_add_mount (const char *container, + const char *mountpoint, + int conttype, unsigned int rid); +gpg_error_t mountinfo_del_mount (const char *container, + const char *mountpoint, + unsigned int rid); +gpg_error_t mountinfo_find_mount (const char *container, + const char *mountpoint, + unsigned int *r_rid); + +void mountinfo_dump_all (void); + + +#endif /*G13_MOUNTINFO_H*/ + diff --git a/g13/runner.c b/g13/runner.c index 27d03702a..6cf88c0b4 100644 --- a/g13/runner.c +++ b/g13/runner.c @@ -31,12 +31,13 @@ #include "keyblob.h" #include "runner.h" #include "../common/exechelp.h" - +#include "mountinfo.h" /* The runner object. */ struct runner_s { - char *name; /* The name of this runner. */ + char *name; /* The name of this runner. */ + unsigned int identifier; /* The runner identifier. */ int spawned; /* True if runner_spawn has been called. */ pth_t threadid; /* The TID of the runner thread. */ @@ -131,12 +132,19 @@ runner_get_threads (void) void runner_release (runner_t runner) { + gpg_error_t err; + if (!runner) return; if (!--runner->refcount) return; + err = mountinfo_del_mount (NULL, NULL, runner->identifier); + if (err) + log_error ("failed to remove mount with rid %u from mtab: %s\n", + runner->identifier, gpg_strerror (err)); + es_fclose (runner->status_fp); if (runner->in_fd != -1) close (runner->in_fd); @@ -171,14 +179,32 @@ runner_release (runner_t runner) gpg_error_t runner_new (runner_t *r_runner, const char *name) { - runner_t runner; + static unsigned int namecounter; /* Global name counter. */ + char *namebuffer; + runner_t runner, r; *r_runner = NULL; runner = xtrycalloc (1, sizeof *runner); if (!runner) return gpg_error_from_syserror (); - runner->name = xtrystrdup (name? name: "[unknown]"); + + /* Bump up the namecounter. In case we ever had an overflow we + check that this number is currently not in use. The algorithm is + a bit lame but should be sufficient because such an wrap is not + very likely: Assuming that we do a mount 10 times a second, then + we would overwrap on a 32 bit system after 13 years. */ + do + { + namecounter++; + for (r = running_threads; r; r = r->next_running) + if (r->identifier == namecounter) + break; + } + while (r); + + runner->identifier = namecounter; + runner->name = namebuffer = xtryasprintf ("%s-%d", name, namecounter); if (!runner->name) { xfree (runner); @@ -189,13 +215,37 @@ runner_new (runner_t *r_runner, const char *name) runner->in_fd = -1; runner->out_fd = -1; - *r_runner = runner; return 0; } -/* A runner usually maintaines two file descriptors to control the +/* Return the identifier of RUNNER. */ +unsigned int +runner_get_rid (runner_t runner) +{ + return runner->identifier; +} + + +/* Find a runner by its rid. Returns the runner object. The caller + must release the runner object. */ +runner_t +runner_find_by_rid (unsigned int rid) +{ + runner_t r; + + for (r = running_threads; r; r = r->next_running) + if (r->identifier == rid) + { + r->refcount++; + return r; + } + return NULL; +} + + +/* A runner usually maintains two file descriptors to control the backend engine. This function is used to set these file descriptors. The function takes ownership of these file descriptors. IN_FD will be used to read from engine and OUT_FD to @@ -382,8 +432,8 @@ runner_spawn (runner_t runner) } tattr = pth_attr_new (); - pth_attr_set (tattr, PTH_ATTR_JOINABLE, 1); - pth_attr_set (tattr, PTH_ATTR_STACK_SIZE, 256*1024); + pth_attr_set (tattr, PTH_ATTR_JOINABLE, 0); + pth_attr_set (tattr, PTH_ATTR_STACK_SIZE, 128*1024); pth_attr_set (tattr, PTH_ATTR_NAME, runner->name); tid = pth_spawn (tattr, runner_thread, runner); diff --git a/g13/runner.h b/g13/runner.h index 88ebc7d2d..e68eca03a 100644 --- a/g13/runner.h +++ b/g13/runner.h @@ -40,6 +40,12 @@ gpg_error_t runner_new (runner_t *r_runner, const char *name); /* Free a runner object. */ void runner_release (runner_t runner); +/* Return the identifier of RUNNER. */ +unsigned int runner_get_rid (runner_t runner); + +/* Find a runner by its rid. */ +runner_t runner_find_by_rid (unsigned int rid); + /* Functions to set properties of the runner. */ void runner_set_fds (runner_t runner, int in_fd, int out_fd); diff --git a/g13/server.c b/g13/server.c index bdf214f32..900dbc909 100644 --- a/g13/server.c +++ b/g13/server.c @@ -40,42 +40,28 @@ struct server_local_s /* The Assuan contect we are working on. */ assuan_context_t assuan_ctx; - char *mountpoint; /* Malloced current mountpoint. */ + char *containername; /* Malloced active containername. */ }; -/* Cookie definition for assuan data line output. */ -static ssize_t data_line_cookie_write (void *cookie, - const void *buffer, size_t size); -static int data_line_cookie_close (void *cookie); -static es_cookie_io_functions_t data_line_cookie_functions = - { - NULL, - data_line_cookie_write, - NULL, - data_line_cookie_close - }; - - -/* The filepointer for status message used in non-server mode. */ -/* static FILE *statusfp; FIXME; */ - - +/* Local prototypes. */ static int command_has_option (const char *cmd, const char *cmdopt); +/* + Helper functions. + */ + +/* Set an error and a description. */ #define set_error(e,t) assuan_set_error (ctx, gpg_error (e), (t)) - - -/* Skip over options. - Blanks after the options are also removed. */ +/* Skip over options. Blanks after the options are also removed. */ static char * skip_options (const char *line) { @@ -93,52 +79,41 @@ skip_options (const char *line) /* Check whether the option NAME appears in LINE. */ -static int -has_option (const char *line, const char *name) +/* static int */ +/* has_option (const char *line, const char *name) */ +/* { */ +/* const char *s; */ +/* int n = strlen (name); */ + +/* s = strstr (line, name); */ +/* if (s && s >= skip_options (line)) */ +/* return 0; */ +/* return (s && (s == line || spacep (s-1)) && (!s[n] || spacep (s+n))); */ +/* } */ + + +/* Helper to print a message while leaving a command. */ +static gpg_error_t +leave_cmd (assuan_context_t ctx, gpg_error_t err) { - const char *s; - int n = strlen (name); - - s = strstr (line, name); - if (s && s >= skip_options (line)) - return 0; - return (s && (s == line || spacep (s-1)) && (!s[n] || spacep (s+n))); -} - - -/* A write handler used by es_fopencookie to write Assuan data - lines. */ -static ssize_t -data_line_cookie_write (void *cookie, const void *buffer, size_t size) -{ - assuan_context_t ctx = cookie; - - if (assuan_send_data (ctx, buffer, size)) + if (err) { - errno = EIO; - return -1; + const char *name = assuan_get_command_name (ctx); + if (!name) + name = "?"; + if (gpg_err_source (err) == GPG_ERR_SOURCE_DEFAULT) + log_error ("command '%s' failed: %s\n", name, + gpg_strerror (err)); + else + log_error ("command '%s' failed: %s <%s>\n", name, + gpg_strerror (err), gpg_strsource (err)); } - - return size; -} - -/* A close handler used by es_fopencookie to write Assuan data - lines. */ -static int -data_line_cookie_close (void *cookie) -{ - assuan_context_t ctx = cookie; - - if (assuan_send_data (ctx, NULL, 0)) - { - errno = EIO; - return -1; - } - - return 0; + return err; } + + /* The handler for Assuan OPTION commands. */ static gpg_error_t option_handler (assuan_context_t ctx, const char *key, const char *value) @@ -146,6 +121,8 @@ option_handler (assuan_context_t ctx, const char *key, const char *value) ctrl_t ctrl = assuan_get_pointer (ctx); gpg_error_t err = 0; + (void)ctrl; + if (!strcmp (key, "putenv")) { /* Change the session's environment to be used for the @@ -213,31 +190,167 @@ reset_notify (assuan_context_t ctx) { ctrl_t ctrl = assuan_get_pointer (ctx); - xfree (ctrl->server_local->mountpoint); - ctrl->server_local->mountpoint = NULL; + xfree (ctrl->server_local->containername); + ctrl->server_local->containername = NULL; assuan_close_input_fd (ctx); assuan_close_output_fd (ctx); } -/* Helper to print a message while leaving a command. */ + +/* OPEN [options] + + Open the container FILENAME. FILENAME must be percent-plus + escaped. A quick check to see whether this is a suitable G13 + container file is done. However no cryptographic check or any + other check is done. This command is used to define the target for + further commands. The filename is reset with the RESET command, + another OPEN or the CREATE command. + */ static gpg_error_t -leave_cmd (assuan_context_t ctx, gpg_error_t err) +cmd_open (assuan_context_t ctx, char *line) { - if (err) + ctrl_t ctrl = assuan_get_pointer (ctx); + gpg_error_t err = 0; + char *p, *pend; + size_t len; + + /* In any case reset the active container. */ + xfree (ctrl->server_local->containername); + ctrl->server_local->containername = NULL; + + /* Parse the line. */ + line = skip_options (line); + for (p=line; *p && !spacep (p); p++) + ; + pend = p; + while (spacep(p)) + p++; + if (*p || pend == line) { - const char *name = assuan_get_command_name (ctx); - if (!name) - name = "?"; - if (gpg_err_source (err) == GPG_ERR_SOURCE_DEFAULT) - log_error ("command '%s' failed: %s\n", name, - gpg_strerror (err)); - else - log_error ("command '%s' failed: %s <%s>\n", name, - gpg_strerror (err), gpg_strsource (err)); + err = gpg_error (GPG_ERR_ASS_SYNTAX); + goto leave; } - return err; + *pend = 0; + + /* Unescape the line and check for embedded Nul bytes. */ + len = percent_plus_unescape_inplace (line, 0); + line[len] = 0; + if (!len || memchr (line, 0, len)) + { + err = gpg_error (GPG_ERR_INV_NAME); + goto leave; + } + + /* Do a basic check. */ + err = g13_is_container (ctrl, line); + if (err) + goto leave; + + /* Store the filename. */ + ctrl->server_local->containername = xtrystrdup (line); + if (!ctrl->server_local->containername) + err = gpg_error_from_syserror (); + + + leave: + return leave_cmd (ctx, err); +} + + +/* MOUNT [options] [] + + Mount the currently open file onto MOUNTPOINT. If MOUNTPOINT is + not given the system picks an unused mountpoint. MOUNTPOINT must + be percent-plus escaped to allow for arbitrary names. + */ +static gpg_error_t +cmd_mount (assuan_context_t ctx, char *line) +{ + ctrl_t ctrl = assuan_get_pointer (ctx); + gpg_error_t err = 0; + char *p, *pend; + size_t len; + + line = skip_options (line); + for (p=line; *p && !spacep (p); p++) + ; + pend = p; + while (spacep(p)) + p++; + if (*p) + { + err = gpg_error (GPG_ERR_ASS_SYNTAX); + goto leave; + } + *pend = 0; + + /* Unescape the line and check for embedded Nul bytes. */ + len = percent_plus_unescape_inplace (line, 0); + line[len] = 0; + if (memchr (line, 0, len)) + { + err = gpg_error (GPG_ERR_INV_NAME); + goto leave; + } + + if (!ctrl->server_local->containername) + { + err = gpg_error (GPG_ERR_MISSING_ACTION); + goto leave; + } + + /* Perform the mount. */ + err = g13_mount_container (ctrl, ctrl->server_local->containername, + *line? line : NULL); + + leave: + return leave_cmd (ctx, err); +} + + +/* UMOUNT [options] [] + + Unmount the currently open file or the one opened at MOUNTPOINT. + MOUNTPOINT must be percent-plus escaped. + */ +static gpg_error_t +cmd_umount (assuan_context_t ctx, char *line) +{ + ctrl_t ctrl = assuan_get_pointer (ctx); + gpg_error_t err = 0; + char *p, *pend; + size_t len; + + line = skip_options (line); + for (p=line; *p && !spacep (p); p++) + ; + pend = p; + while (spacep(p)) + p++; + if (*p) + { + err = gpg_error (GPG_ERR_ASS_SYNTAX); + goto leave; + } + *pend = 0; + + /* Unescape the line and check for embedded Nul bytes. */ + len = percent_plus_unescape_inplace (line, 0); + line[len] = 0; + if (memchr (line, 0, len)) + { + err = gpg_error (GPG_ERR_INV_NAME); + goto leave; + } + + /* Perform the unmount. */ + err = g13_umount_container (ctrl, ctrl->server_local->containername, + *line? line : NULL); + + leave: + return leave_cmd (ctx, err); } @@ -285,106 +398,26 @@ cmd_signer (assuan_context_t ctx, char *line) } -/* SETMOUNTPOINT [options] [] +/* CREATE [options] filename - Set DIRNAME as the new mount point for future operations. + Create a new container. On success the OPEN command is done + implictly for the new container. */ static gpg_error_t -cmd_setmountpoint (assuan_context_t ctx, char *line) +cmd_create (assuan_context_t ctx, char *line) { ctrl_t ctrl = assuan_get_pointer (ctx); - gpg_error_t err = 0; - char *p, *pend; - size_t len; + gpg_error_t err; - line = skip_options (line); - for (p=line; *p && !spacep (p); p++) - ; - pend = p; - while (spacep(p)) - p++; - if (*p) - { - err = gpg_error (GPG_ERR_ASS_SYNTAX); - goto leave; - } - *pend = 0; + (void)ctrl; - /* Unescape the line and check for embedded Nul bytes. */ - len = percent_plus_unescape_inplace (line, 0); - line[len] = 0; - if (memchr (line, 0, len)) - { - err = gpg_error (GPG_ERR_INV_NAME); - goto leave; - } - - xfree (ctrl->server_local->mountpoint); - if (!len) /* Reset mountpoint. */ - ctrl->server_local->mountpoint = NULL; - else - { - ctrl->server_local->mountpoint = xtrystrdup (line); - if (!ctrl->server_local->mountpoint) - err = gpg_error_from_syserror (); - } - - if (!err) - log_debug ("mountpoint is now `%s'\n", - ctrl->server_local->mountpoint - ? ctrl->server_local->mountpoint: "[none]"); - - leave: - return leave_cmd (ctx, err); -} + /* First we close the active container. */ + xfree (ctrl->server_local->containername); + ctrl->server_local->containername = NULL; -/* MOUNT [options] - Mount CONTAINERNAME onto the current mount point. CONTAINERNAME is - the name of a file in the g13 format and must be percent-plus - escaped to allow for arbitrary names. The mount poiunt must have - been set already. - - - A reason why we use a separate command for the mount point is to - allow for longer filenames (an assuan command line is limited to - ~1000 byte. - */ -static gpg_error_t -cmd_mount (assuan_context_t ctx, char *line) -{ - ctrl_t ctrl = assuan_get_pointer (ctx); - gpg_error_t err = 0; - char *p, *pend; - size_t len; - - line = skip_options (line); - for (p=line; *p && !spacep (p); p++) - ; - pend = p; - while (spacep(p)) - p++; - if (*p || pend == line) - { - err = gpg_error (GPG_ERR_ASS_SYNTAX); - goto leave; - } - *pend = 0; - - /* Unescape the line and check for embedded Nul bytes. */ - len = percent_plus_unescape_inplace (line, 0); - line[len] = 0; - if (!len || memchr (line, 0, len)) - { - err = gpg_error (GPG_ERR_INV_NAME); - goto leave; - } - - /* Perform the mount. */ - err = g13_mount_container (ctrl, line, ctrl->server_local->mountpoint); - - leave: + err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); return leave_cmd (ctx, err); } @@ -476,10 +509,12 @@ register_commands (assuan_context_t ctx) const char *name; gpg_error_t (*handler)(assuan_context_t, char *line); } table[] = { + { "OPEN", cmd_open }, + { "MOUNT", cmd_mount }, + { "UMOUNT", cmd_umount }, { "RECIPIENT", cmd_recipient }, { "SIGNER", cmd_signer }, - { "MOUNT", cmd_mount }, - { "SETMOUNTPOINT", cmd_setmountpoint }, + { "CREATE", cmd_create }, { "INPUT", NULL }, { "OUTPUT", NULL }, { "GETINFO", cmd_getinfo },