Added configuration file parser (using the SAX API in libxml2). Added command line parsing. - susmb - mounting of SMB/CIFS shares via FUSE
HTML git clone git://git.codemadness.org/susmb
DIR Log
DIR Files
DIR Refs
DIR README
DIR LICENSE
---
DIR commit 0e75a1aa0cdd9993a0141293a190b7f16758371d
DIR parent c43a2e4e311601dbbee65ff680b8ec388ecb543d
HTML Author: geoff <devnull@localhost>
Date: Sun, 7 May 2006 21:25:33 +0000
Added configuration file parser (using the SAX API in libxml2).
Added command line parsing.
Diffstat:
M Makefile | 18 +++++++++++++-----
A auth.c | 10 ++++++++++
A conffile.c | 188 +++++++++++++++++++++++++++++++
A conffile.h | 51 +++++++++++++++++++++++++++++++
A doc/sax_model.mdzip | 0
A doc/sax_state_machine.png | 0
A options.c | 150 +++++++++++++++++++++++++++++++
A options.h | 9 +++++++++
A sax.c | 706 +++++++++++++++++++++++++++++++
A sax.h | 9 +++++++++
M usmb.c | 266 ++++++++++++++++++++-----------
A usmb.conf | 16 ++++++++++++++++
M usmb.h | 7 -------
M usmb_dir.c | 14 ++++++++------
M usmb_file.c | 61 +++++++++++++++++++++++++++----
M usmb_file.h | 2 ++
A utils.c | 92 +++++++++++++++++++++++++++++++
A utils.h | 15 +++++++++++++++
A version.h | 21 +++++++++++++++++++++
19 files changed, 1520 insertions(+), 115 deletions(-)
---
DIR diff --git a/Makefile b/Makefile
@@ -1,12 +1,20 @@
SAMBA = /opt/samba
CC = gcc
-CFLAGS = -Wall -W -std=c99 -pedantic -O $(shell pkg-config --cflags fuse) \
+
+CFLAGS = -Wall -W -std=c99 -pedantic -O \
-I$(SAMBA)/include -D_BSD_SOURCE -DFUSE_USE_VERSION=25 \
- -DHAVE_UTIME_H # -DDEBUG
-LDLIBS = $(shell pkg-config --libs-only-l fuse) -lsmbclient
-LDFLAGS = $(shell pkg-config --libs-only-L fuse) -L$(SAMBA)/lib
+ -DHAVE_UTIME_H -ggdb -DDEBUG
+
+LDLIBS = -lsmbclient
+LDFLAGS = -L$(SAMBA)/lib
+
+PACKAGES = glib-2.0 libxml-2.0 fuse
+
+CFLAGS += $(shell pkg-config --cflags $(PACKAGES))
+LDLIBS += $(shell pkg-config --libs-only-l $(PACKAGES))
+LDFLAGS += $(shell pkg-config --libs-only-L $(PACKAGES))
-SOURCES = usmb.c usmb_dir.c usmb_file.c
+SOURCES = conffile.c options.c sax.c usmb.c usmb_dir.c usmb_file.c utils.c
OBJECTS = $(SOURCES:.c=.o)
PROGRAM = usmb
DIR diff --git a/auth.c b/auth.c
@@ -0,0 +1,10 @@
+struct auth {
+ const char *server;
+ const char *share;
+ const char *domain;
+ const char *username;
+ const char *password;
+};
+
+
+
DIR diff --git a/conffile.c b/conffile.c
@@ -0,0 +1,188 @@
+#include <assert.h>
+#include <glib.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "conffile.h"
+#include "utils.h"
+
+
+struct conffile {
+ GHashTable *credentials;
+ GHashTable *mounts;
+};
+
+
+struct credentials * credentials_new (const char *domain,
+ const char *username,
+ const char *password)
+{
+ struct credentials *creds = malloc (sizeof (struct credentials));
+
+ if (NULL == creds)
+ return NULL;
+
+ creds->domain = xstrdup (domain);
+ creds->username = xstrdup (username);
+ creds->password = xstrdup (password);
+
+ if ((NULL == creds->domain) ||
+ (NULL == creds->username) ||
+ (NULL == creds->password))
+ {
+ credentials_destroy (creds);
+ creds = NULL;
+ }
+
+ return creds;
+}
+
+
+void credentials_destroy (struct credentials *creds)
+{
+ assert (creds != NULL);
+
+ xfree (creds->password);
+ xfree (creds->username);
+ xfree (creds->domain);
+
+ free (creds);
+}
+
+
+struct mount * mount_new (const char *server, const char *share,
+ const char *mountpoint, const char *credentials,
+ const char *options)
+{
+ struct mount *mount = malloc (sizeof (struct mount));
+
+ if (NULL == mount)
+ return NULL;
+
+ mount->server = xstrdup (server);
+ mount->share = xstrdup (share);
+ mount->mountpoint = xstrdup (mountpoint);
+ mount->options = xstrdup (options);
+ mount->credentials = xstrdup (credentials);
+
+ if ((NULL == mount->server) ||
+ (NULL == mount->share) ||
+ (NULL == mount->mountpoint) ||
+ (NULL == mount->credentials) ||
+ (NULL == mount->options))
+ {
+ mount_destroy (mount);
+ mount = NULL;
+ }
+
+ return mount;
+}
+
+
+void mount_destroy (struct mount *mount)
+{
+ assert (NULL != mount);
+
+ xfree (mount->credentials);
+ xfree (mount->options);
+ xfree (mount->mountpoint);
+ xfree (mount->share);
+ xfree (mount->server);
+
+ free (mount);
+}
+
+
+struct conffile * conffile_new (void)
+{
+ struct conffile *cf = malloc (sizeof (struct conffile));
+
+ if (NULL == cf)
+ return NULL;
+
+
+ cf->credentials = g_hash_table_new_full (g_str_hash, g_str_equal, free,
+ (GDestroyNotify)credentials_destroy);
+ cf->mounts = g_hash_table_new_full (g_str_hash, g_str_equal,
+ free, (GDestroyNotify)mount_destroy);
+
+ if ((NULL == cf->credentials) || (NULL == cf->mounts))
+ {
+ conffile_destroy (cf);
+ cf = NULL;
+ }
+
+ return cf;
+}
+
+
+void conffile_destroy (struct conffile *cf)
+{
+ assert (NULL != cf);
+
+ if (NULL != cf->mounts)
+ g_hash_table_destroy (cf->mounts);
+
+ if (NULL != cf->credentials)
+ g_hash_table_destroy (cf->credentials);
+
+ free (cf);
+}
+
+
+bool conffile_add_mount (struct conffile *cf, char *key, struct mount *mount)
+{
+ assert (NULL != cf);
+ assert (NULL != key);
+ assert (NULL != mount);
+
+ DEBUG (fprintf (stderr, "add_mount (%s)\n", key));
+
+ if (NULL != g_hash_table_lookup (cf->mounts, key))
+ {
+ fprintf (stderr, "Duplicate mount name: %s\n", key);
+ return false;
+ }
+
+ g_hash_table_insert (cf->mounts, key, mount);
+ return true;
+}
+
+
+bool conffile_add_credentials (struct conffile *cf, char *key,
+ struct credentials *credentials)
+{
+ assert (NULL != cf);
+ assert (NULL != key);
+ assert (NULL != credentials);
+
+ DEBUG (fprintf (stderr, "add_credentials (%s)\n", key));
+
+ if (NULL != g_hash_table_lookup (cf->credentials, key))
+ {
+ fprintf (stderr, "Duplicate credentials name: %s\n", key);
+ return false;
+ }
+
+ g_hash_table_insert (cf->credentials, key, credentials);
+ return true;
+}
+
+
+struct mount * conffile_get_mount (struct conffile *cf, const char *key)
+{
+ assert (NULL != cf);
+ assert (NULL != key);
+
+ return g_hash_table_lookup (cf->mounts, key);
+}
+
+
+struct credentials * conffile_get_credentials (struct conffile *cf,
+ const char *key)
+{
+ assert (NULL != cf);
+ assert (NULL != key);
+
+ return g_hash_table_lookup (cf->credentials, key);
+}
DIR diff --git a/conffile.h b/conffile.h
@@ -0,0 +1,51 @@
+#ifndef CONFFILE_H
+ #define CONFFILE_H
+
+ #include <stdbool.h>
+
+ struct credentials {
+ char *domain;
+ char *username;
+ char *password;
+ };
+
+
+ struct mount {
+ char *server;
+ char *share;
+ char *mountpoint;
+ char *credentials;
+ char *options;
+ };
+
+
+ struct conffile;
+
+
+ struct credentials * credentials_new (const char *domain,
+ const char *username,
+ const char *password);
+
+ void credentials_destroy (struct credentials *creds);
+
+ struct mount * mount_new (const char *server, const char *share,
+ const char *mountpoint, const char *credentials,
+ const char *options);
+
+ void mount_destroy (struct mount *mount);
+
+ struct conffile * conffile_new (void);
+
+ void conffile_destroy (struct conffile *cf);
+
+ bool conffile_add_mount (struct conffile *cf, char *key, struct mount *mount);
+
+ bool conffile_add_credentials (struct conffile *cf, char *key,
+ struct credentials *credentials);
+
+ struct mount * conffile_get_mount (struct conffile *cf, const char *key);
+
+ struct credentials * conffile_get_credentials (struct conffile *cf,
+ const char *key);
+
+#endif
DIR diff --git a/doc/sax_model.mdzip b/doc/sax_model.mdzip
Binary files differ.
DIR diff --git a/doc/sax_state_machine.png b/doc/sax_state_machine.png
Binary files differ.
DIR diff --git a/options.c b/options.c
@@ -0,0 +1,150 @@
+#include <assert.h>
+#include <glib.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <libsmbclient.h>
+#include <fuse.h>
+#include "options.h"
+#include "utils.h"
+#include "version.h"
+
+
+static gboolean version = FALSE;
+static gchar *conffile = NULL;
+static gboolean debug = FALSE;
+static gboolean nofork = FALSE;
+static gchar **remaining = NULL;
+
+
+static GOptionEntry entries[] = {
+ { .long_name = "version",
+ .short_name = 'v',
+ .flags = 0,
+ .arg = G_OPTION_ARG_NONE,
+ .arg_data = &version,
+ .description = "Show usmb, FUSE and Samba versions" },
+
+ { .long_name = "version",
+ .short_name = 'V',
+ .flags = G_OPTION_FLAG_HIDDEN,
+ .arg = G_OPTION_ARG_NONE,
+ .arg_data = &version,
+ .description = NULL },
+
+ { .long_name = "config",
+ .short_name = 'c',
+ .flags = 0,
+ .arg = G_OPTION_ARG_FILENAME,
+ .arg_data = &conffile,
+ .description = "usmb configuration file" },
+
+ { .long_name = "debug",
+ .short_name = 'd',
+ .arg = G_OPTION_ARG_NONE,
+ .arg_data = &debug,
+ .description = "Debug mode" },
+
+ { .long_name = "nofork",
+ .short_name = 'f',
+ .arg = G_OPTION_ARG_NONE,
+ .arg_data = &nofork,
+ .description = "Foreground operation" },
+
+ { .long_name = G_OPTION_REMAINING,
+ .short_name = 0,
+ .arg = G_OPTION_ARG_STRING_ARRAY,
+ .arg_data = &remaining,
+ .description = NULL },
+
+ { .long_name = NULL }
+};
+
+
+bool parse_args (int *argc, char ***argv,
+ const char **mountid, const char **out_conffile)
+{
+ GError *error = NULL;
+
+ GOptionContext *context =
+ g_option_context_new ("- mount SMB shares via FUSE and Samba");
+ g_option_context_add_main_entries (context, entries, NULL);
+ bool ret = g_option_context_parse (context, argc, argv, &error);
+ g_option_context_free (context);
+
+ if ((NULL == remaining) || (NULL == remaining[0]))
+ {
+ fputs ("No share ID given.\n", stderr);
+ return false;
+ }
+
+ if (NULL != remaining[1])
+ {
+ fputs ("Too many arguments.\n", stderr);
+ return false;
+ }
+
+ *mountid = remaining[0];
+ g_free (remaining);
+ DEBUG (fprintf (stderr, "Mount ID: %s\n", *mountid));
+
+ if (version)
+ {
+ show_version (stdout);
+ exit (EXIT_SUCCESS);
+ }
+
+ if (NULL != conffile)
+ *out_conffile = conffile;
+
+ return ret;
+}
+
+
+/* FUSE args are:
+ *
+ * argv[0]
+ * -s
+ * -d -- if debug mode requested
+ * -f -- if foreground mode requested
+ * -o ... -- if any mount options in the config file
+ * mount point
+ */
+#define MAXARGS 10
+void build_fuse_args (char *options, char *mountpoint,
+ int *out_argc, char ***out_argv)
+{
+ assert (NULL != mountpoint);
+ static char *argv[MAXARGS];
+
+ int argc = 0;
+
+ argv[argc++] = "usmb";
+ argv[argc++] = "-s";
+
+ if (debug)
+ argv[argc++] = "-d";
+
+ if (nofork)
+ argv[argc++] = "-f";
+
+ if ((NULL != options) && ('\0' != options[0]))
+ {
+ argv[argc++] = "-o";
+ argv[argc++] = options;
+ }
+
+ argv[argc++] = mountpoint;
+ argv[argc] = NULL; // for good measure...
+
+ assert (argc < MAXARGS);
+ *out_argc = argc;
+ *out_argv = argv;
+
+#if 0
+ for (int i = 0; i < argc; ++i)
+ fprintf (stderr, "%d: %s\n", i, argv[i]);
+#endif
+}
+
DIR diff --git a/options.h b/options.h
@@ -0,0 +1,9 @@
+#ifndef OPTIONS_H
+ #define OPTIONS_H
+
+ bool parse_args (int *argc, char ***argv,
+ const char **mountid, const char **out_conffile);
+ void build_fuse_args (char *options, char *mountpoint,
+ int *out_argc, char ***out_argv);
+
+#endif
DIR diff --git a/sax.c b/sax.c
@@ -0,0 +1,706 @@
+#include <assert.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <libxml/xmlreader.h>
+#include <glib.h>
+#include "conffile.h"
+#include "sax.h"
+#include "utils.h"
+
+
+/* Configuration file format
+ * -------------------------
+ *
+ * usmbconfig section
+ * Any number of credentials and mount sections
+ *
+ * credentials section
+ * domain
+ * username
+ * password
+ *
+ * mount section
+ * server
+ * share
+ * mountpoint
+ * options
+ *
+ * Example:
+ * <usmbconfig>
+ *
+ * <credentials id="workgroup">
+ * <domain>WORKGROUP</domain>
+ * <username>user</username>
+ * <password>password</password>
+ * </credentials>
+ *
+ * <mount id="home" credentials="workgroup">
+ * <server>127.0.0.1</server>
+ * <share>home</share>
+ * <mountpoint>/tmp/smb</mountpoint>
+ * <options>readonly</options>
+ * </mount>
+ *
+ * </usmbconfig>
+ */
+
+
+/* SAX state machine
+ * -----------------
+ *
+ * See doc/sax_state_machine.png or the MagicDraw model doc/sax_model.mdzip.
+ */
+
+
+enum sax_state {
+ START,
+ INITIAL,
+ USMBCFG,
+ DONE,
+ FINISH,
+ CREDS,
+ MOUNT,
+ SERVER,
+ SHARE,
+ MOUNTPT,
+ OPTIONS,
+ DOMAIN,
+ USER,
+ PASS,
+ ERROR
+};
+
+
+enum element {
+ ELT_USMBCFG,
+ ELT_CREDS,
+ ELT_DOMAIN,
+ ELT_USER,
+ ELT_PASS,
+ ELT_MOUNT,
+ ELT_SERVER,
+ ELT_SHARE,
+ ELT_MOUNTPT,
+ ELT_OPTIONS,
+ ELT_UNKNOWN
+};
+
+
+// passed as the ctx pointer into SAX callbacks
+struct sax_parser {
+ enum sax_state state;
+
+ union {
+ struct {
+ char *id;
+ GString *domain;
+ GString *username;
+ GString *password;
+ } credentials;
+ struct {
+ char *id;
+ char *creds;
+ GString *server;
+ GString *share;
+ GString *mountpoint;
+ GString *options;
+ } mount;
+ } strs;
+
+ struct conffile *cf;
+};
+
+
+static enum element get_element (const char *elt)
+{
+ const char *elts[] = {
+ [ ELT_USMBCFG ] = "usmbconfig",
+ [ ELT_CREDS ] = "credentials",
+ [ ELT_DOMAIN ] = "domain",
+ [ ELT_USER ] = "username",
+ [ ELT_PASS ] = "password",
+ [ ELT_MOUNT ] = "mount",
+ [ ELT_SERVER ] = "server",
+ [ ELT_SHARE ] = "share",
+ [ ELT_MOUNTPT ] = "mountpoint",
+ [ ELT_OPTIONS ] = "options"
+ };
+
+ for (size_t i = 0; i < sizeof (elts) / sizeof (elts[0]); ++i)
+ if (!strcmp (elt, elts[i]))
+ return i;
+
+ return ELT_UNKNOWN;
+}
+
+
+static void start_document (void *ctx)
+{
+ struct sax_parser *sp = ctx;
+ assert (sp->state == START);
+ sp->state = INITIAL;
+
+ // these four aren't strictly necessary as we're using a union
+ sp->strs.credentials.id = NULL;
+ sp->strs.credentials.domain = NULL;
+ sp->strs.credentials.username = NULL;
+ sp->strs.credentials.password = NULL;
+
+ sp->strs.mount.id = NULL;
+ sp->strs.mount.creds = NULL;
+ sp->strs.mount.server = NULL;
+ sp->strs.mount.share = NULL;
+ sp->strs.mount.mountpoint = NULL;
+ sp->strs.mount.options = NULL;
+}
+
+
+static void end_document (void *ctx)
+{
+ struct sax_parser *sp = ctx;
+
+ if (ERROR == sp->state)
+ return;
+
+ if (sp->state != DONE)
+ {
+ fputs ("Unexpected end of configuration file\n", stderr);
+ sp->state = ERROR;
+ }
+
+ sp->state = FINISH;
+}
+
+
+static void error (void *ctx, const char *msg, ...)
+{
+ struct sax_parser *sp = ctx;
+
+ if (ERROR == sp->state)
+ return;
+
+ va_list ap;
+
+ fputs ("Parse error: ", stderr);
+ va_start (ap, msg);
+ vfprintf (stderr, msg ,ap);
+ va_end (ap);
+ sp->state = ERROR;
+}
+
+
+static bool set_string (char **out, const char *in, const char *dup_err)
+{
+ assert (NULL != out);
+ assert (NULL != in);
+
+ if (NULL != *out)
+ {
+ if (NULL != dup_err)
+ {
+ fputs (dup_err, stderr);
+ return false;
+ }
+ }
+
+ *out = xstrdup (in);
+
+ if (NULL == *out)
+ {
+ fputs ("Out of memory\n", stderr);
+ return false;
+ }
+
+ return true;
+}
+
+
+static void start_mount (struct sax_parser *sp, const xmlChar **attrs)
+{
+ assert (NULL == sp->strs.mount.server);
+ assert (NULL == sp->strs.mount.share);
+ assert (NULL == sp->strs.mount.mountpoint);
+ assert (NULL == sp->strs.mount.options);
+
+ // must have id and credentials attributes
+ for (int i = 0; NULL != attrs[i]; i += 2)
+ {
+ assert (NULL != attrs[i+1]);
+
+ if (!strcmp (attrs[i], "id"))
+ {
+ if (!set_string (&sp->strs.mount.id, (const char *)attrs[i+1],
+ "Duplicate id attribute in <mount>\n"))
+ {
+ sp->state = ERROR;
+ return;
+ }
+ }
+
+ else if (!strcmp (attrs[i], "credentials"))
+ {
+ if (!set_string (&sp->strs.mount.creds, (const char *)attrs[i+1],
+ "Duplicate credentials attribute in <mount>\n"))
+ {
+ sp->state = ERROR;
+ return;
+ }
+ }
+
+ else
+ {
+ fprintf (stderr, "Unrecognised <mount> attribute %s\n", attrs[i]);
+ sp->state = ERROR;
+ return;
+ }
+ }
+
+ if ((NULL == sp->strs.mount.id) ||
+ (NULL == sp->strs.mount.creds))
+ {
+ fputs ("<mount> must have id and credentials attributes\n", stderr);
+ sp->state = ERROR;
+ return;
+ }
+
+ sp->state = MOUNT;
+}
+
+
+static void start_creds (struct sax_parser *sp, const xmlChar **attrs)
+{
+ assert (NULL == sp->strs.credentials.domain);
+ assert (NULL == sp->strs.credentials.username);
+ assert (NULL == sp->strs.credentials.password);
+
+ // must have an id attribute
+ for (int i = 0; NULL != attrs[i]; i += 2)
+ {
+ assert (NULL != attrs[i+1]);
+
+ if (!strcmp (attrs[i], "id"))
+ {
+ if (!set_string (&sp->strs.credentials.id, (const char *)attrs[i+1],
+ "Duplicate id attribute in <credentials>\n"))
+ {
+ sp->state = ERROR;
+ return;
+ }
+ }
+
+ else
+ {
+ fprintf (stderr, "Unrecognised <credentials> attribute %s\n", attrs[i]);
+ sp->state = ERROR;
+ return;
+ }
+ }
+
+ if (NULL == sp->strs.credentials.id)
+ {
+ fputs ("<credentials> must have an id attribute\n", stderr);
+ sp->state = ERROR;
+ return;
+ }
+
+ sp->state = CREDS;
+}
+
+
+static void start_char_state (struct sax_parser *sp,
+ GString **target,
+ const char *name,
+ const xmlChar **attrs)
+{
+ if (ERROR == sp->state)
+ return;
+
+ if ((NULL != attrs) && (NULL != attrs[0]))
+ {
+ fprintf (stderr, "Spurious attributes on <%s>\n", name);
+ sp->state = ERROR;
+ return;
+ }
+
+ if (NULL != *target)
+ {
+ fprintf (stderr, "Duplicate <%s>\n", name);
+ sp->state = ERROR;
+ return;
+ }
+
+ *target = g_string_new (NULL);
+ if (NULL == *target)
+ {
+ fputs ("Out of memory\n", stderr);
+ sp->state = ERROR;
+ return;
+ }
+}
+
+
+static void start_server (struct sax_parser *sp, const xmlChar **attrs)
+{
+ start_char_state (sp, &sp->strs.mount.server, "server", attrs);
+}
+
+
+static void start_share (struct sax_parser *sp, const xmlChar **attrs)
+{
+ start_char_state (sp, &sp->strs.mount.share, "share", attrs);
+}
+
+
+static void start_mtpt (struct sax_parser *sp, const xmlChar **attrs)
+{
+ start_char_state (sp, &sp->strs.mount.mountpoint, "mountpoint", attrs);
+}
+
+
+static void start_options (struct sax_parser *sp, const xmlChar **attrs)
+{
+ start_char_state (sp, &sp->strs.mount.options, "options", attrs);
+}
+
+
+static void start_domain (struct sax_parser *sp, const xmlChar **attrs)
+{
+ start_char_state (sp, &sp->strs.credentials.domain, "domain", attrs);
+}
+
+
+static void start_username (struct sax_parser *sp, const xmlChar **attrs)
+{
+ start_char_state (sp, &sp->strs.credentials.username, "username", attrs);
+}
+
+
+static void start_password (struct sax_parser *sp, const xmlChar **attrs)
+{
+ start_char_state (sp, &sp->strs.credentials.password, "password", attrs);
+}
+
+
+static void start_element (void *ctx,
+ const xmlChar *name, const xmlChar **attrs)
+{
+ struct sax_parser *sp = ctx;
+
+ if (ERROR == sp->state)
+ return;
+
+ /* all valid transitions on <element> */
+ const struct {
+ enum sax_state to, from;
+ enum element on;
+ void (*fn) (struct sax_parser *, const xmlChar **attrs);
+ } transitions[] = {
+ { .from = INITIAL, .on = ELT_USMBCFG, .to = USMBCFG, .fn = NULL },
+ { .from = USMBCFG, .on = ELT_MOUNT, .to = MOUNT, .fn = start_mount },
+ { .from = USMBCFG, .on = ELT_CREDS, .to = CREDS, .fn = start_creds },
+ { .from = MOUNT, .on = ELT_SERVER, .to = SERVER, .fn = start_server },
+ { .from = MOUNT, .on = ELT_SHARE, .to = SHARE, .fn = start_share },
+ { .from = MOUNT, .on = ELT_MOUNTPT, .to = MOUNTPT, .fn = start_mtpt },
+ { .from = MOUNT, .on = ELT_OPTIONS, .to = OPTIONS, .fn = start_options },
+ { .from = CREDS, .on = ELT_DOMAIN, .to = DOMAIN, .fn = start_domain },
+ { .from = CREDS, .on = ELT_USER, .to = USER, .fn = start_username },
+ { .from = CREDS, .on = ELT_PASS, .to = PASS, .fn = start_password }
+ };
+
+
+ enum element elt = get_element ((const char *)name);
+
+ for (size_t i = 0; i < sizeof (transitions) / sizeof (transitions[0]); ++i)
+ {
+ if ((transitions[i].from == sp->state) &&
+ (transitions[i].on == elt))
+ {
+ if (NULL != transitions[i].fn)
+ transitions[i].fn (sp, attrs);
+
+ if (ERROR != sp->state)
+ sp->state = transitions[i].to;
+ return;
+ }
+ }
+
+ fprintf (stderr, "Unexpected <%s>\n", name);
+ sp->state = ERROR;
+}
+
+
+static void end_mount (struct sax_parser *sp)
+{
+ // must have id, creds, server, share and mountpoint; options are optional
+ assert (NULL != sp->strs.mount.id);
+ assert (NULL != sp->strs.mount.creds);
+
+ if (NULL == conffile_get_credentials (sp->cf, sp->strs.mount.creds))
+ {
+ fprintf (stderr, "Mount %s: credentials %s not found\n",
+ sp->strs.mount.id, sp->strs.mount.creds);
+ sp->state = ERROR;
+ }
+
+ else if (NULL != conffile_get_mount (sp->cf, sp->strs.mount.id))
+ {
+ fprintf (stderr, "Duplicate mount ID: %s\n", sp->strs.mount.id);
+ sp->state = ERROR;
+ }
+
+ else if ((NULL == sp->strs.mount.server) || (0 == sp->strs.mount.server->len))
+ {
+ fprintf (stderr, "Mount %s: no server given\n", sp->strs.mount.id);
+ sp->state = ERROR;
+ }
+
+ else if ((NULL == sp->strs.mount.share) || (0 == sp->strs.mount.share->len))
+ {
+ fprintf (stderr, "Mount %s: no share given\n", sp->strs.mount.id);
+ sp->state = ERROR;
+ }
+
+ else if ((NULL == sp->strs.mount.mountpoint) ||
+ (0 == sp->strs.mount.mountpoint->len))
+ {
+ fprintf (stderr, "Mount %s: no mount point given\n", sp->strs.mount.id);
+ sp->state = ERROR;
+ }
+
+ else
+ {
+ struct mount *mount =
+ mount_new (sp->strs.mount.server ? sp->strs.mount.server->str : "",
+ sp->strs.mount.share ? sp->strs.mount.share->str : "",
+ sp->strs.mount.mountpoint ?
+ sp->strs.mount.mountpoint->str : "",
+ sp->strs.mount.creds,
+ sp->strs.mount.options ?
+ sp->strs.mount.options->str : "");
+
+ if ((NULL == mount) ||
+ !conffile_add_mount (sp->cf, sp->strs.mount.id, mount))
+ sp->state = ERROR;
+ }
+
+ if (sp->strs.mount.server)
+ g_string_free (sp->strs.mount.server, TRUE);
+
+ if (sp->strs.mount.share)
+ g_string_free (sp->strs.mount.share, TRUE);
+
+ if (sp->strs.mount.mountpoint)
+ g_string_free (sp->strs.mount.mountpoint, TRUE);
+
+ if (sp->strs.mount.options)
+ g_string_free (sp->strs.mount.options, TRUE);
+
+ sp->strs.mount.server = NULL;
+ sp->strs.mount.share = NULL;
+ sp->strs.mount.mountpoint = NULL;
+ sp->strs.mount.options = NULL;
+
+ if (ERROR == sp->state)
+ xfree (sp->strs.mount.id);
+
+ xfree (sp->strs.mount.creds);
+
+ sp->strs.mount.id = NULL;
+ sp->strs.mount.creds = NULL;
+}
+
+
+static void end_creds (struct sax_parser *sp)
+{
+ // must have username; domain and password are optional
+ assert (NULL != sp->strs.credentials.id);
+
+ if (NULL != conffile_get_credentials (sp->cf, sp->strs.credentials.id))
+ {
+ fprintf (stderr, "Duplicate credentials ID: %s\n", sp->strs.credentials.id);
+ sp->state = ERROR;
+ }
+
+ else if ((NULL == sp->strs.credentials.username) ||
+ (0 == sp->strs.credentials.username->len))
+ {
+ fprintf (stderr, "Credentials %s: no username given\n",
+ sp->strs.credentials.id);
+ sp->state = ERROR;
+ }
+
+ else
+ {
+ struct credentials *creds =
+ credentials_new (sp->strs.credentials.domain ?
+ sp->strs.credentials.domain->str : "",
+ sp->strs.credentials.username ?
+ sp->strs.credentials.username->str : "",
+ sp->strs.credentials.password ?
+ sp->strs.credentials.password->str : "");
+
+ if ((NULL == creds) ||
+ !conffile_add_credentials (sp->cf, sp->strs.credentials.id, creds))
+ sp->state = ERROR;
+ }
+
+ g_string_free (sp->strs.credentials.domain, TRUE);
+ g_string_free (sp->strs.credentials.username, TRUE);
+ g_string_free (sp->strs.credentials.password, TRUE);
+
+ sp->strs.credentials.domain = NULL;
+ sp->strs.credentials.username = NULL;
+ sp->strs.credentials.password = NULL;
+
+ if (ERROR == sp->state)
+ xfree (sp->strs.credentials.id);
+
+ sp->strs.credentials.id = NULL;
+}
+
+
+static void end_element (void *ctx, const xmlChar *name)
+{
+ struct sax_parser *sp = ctx;
+
+ if (ERROR == sp->state)
+ return;
+
+ /* all valid transitions on </element> */
+ const struct {
+ enum sax_state to, from;
+ enum element on;
+ void (*fn) (struct sax_parser *);
+ } transitions[] = {
+ { .from = USMBCFG, .on = ELT_USMBCFG, .to = DONE, .fn = NULL },
+ { .from = MOUNT, .on = ELT_MOUNT, .to = USMBCFG, .fn = end_mount },
+ { .from = CREDS, .on = ELT_CREDS, .to = USMBCFG, .fn = end_creds },
+ { .from = SERVER, .on = ELT_SERVER, .to = MOUNT, .fn = NULL },
+ { .from = SHARE, .on = ELT_SHARE, .to = MOUNT, .fn = NULL },
+ { .from = MOUNTPT, .on = ELT_MOUNTPT, .to = MOUNT, .fn = NULL },
+ { .from = OPTIONS, .on = ELT_OPTIONS, .to = OPTIONS, .fn = NULL },
+ { .from = DOMAIN, .on = ELT_DOMAIN, .to = CREDS, .fn = NULL },
+ { .from = USER, .on = ELT_USER, .to = CREDS, .fn = NULL },
+ { .from = PASS, .on = ELT_PASS, .to = CREDS, .fn = NULL }
+ };
+
+
+ enum element elt = get_element ((const char *)name);
+
+ for (size_t i = 0; i < sizeof (transitions) / sizeof (transitions[0]); ++i)
+ {
+ if ((transitions[i].from == sp->state) &&
+ (transitions[i].on == elt))
+ {
+ if (NULL != transitions[i].fn)
+ transitions[i].fn (sp);
+
+ if (ERROR != sp->state)
+ sp->state = transitions[i].to;
+ return;
+ }
+ }
+
+ fprintf (stderr, "Unexpected </%s>\n", name);
+ sp->state = ERROR;
+}
+
+
+// strips leading and trailing whitespace; returns true iff characters remain
+static bool strip_whitespace (const char **str, int *len)
+{
+ assert (NULL != str);
+ assert (NULL != len);
+
+ /* leading whitespace */
+ while ((*len > 0) && isspace (**str))
+ {
+ ++(*str);
+ --(*len);
+ }
+
+ /* trailing whitespace */
+ while ((*len > 0) && (isspace ((*str)[*len - 1])))
+ --(*len);
+
+ return (0 != *len);
+}
+
+
+static inline void append_chars (GString *target, const xmlChar *str, int len)
+{
+ assert (NULL != target);
+ assert (NULL != str);
+
+ g_string_append_len (target, (const char *)str, len);
+}
+
+
+static void chars (void *ctx, const xmlChar *str, int len)
+{
+ struct sax_parser *sp = ctx;
+
+ if (ERROR == sp->state)
+ return;
+
+ // all states admit whitespace characters
+ if (!strip_whitespace ((const char **)&str, &len))
+ return;
+
+ // these states admit non-whitespace characters
+ switch (sp->state)
+ {
+ case SERVER: append_chars (sp->strs.mount.server, str, len); break;
+ case SHARE: append_chars (sp->strs.mount.share, str, len); break;
+ case MOUNTPT: append_chars (sp->strs.mount.mountpoint, str, len); break;
+ case OPTIONS: append_chars (sp->strs.mount.options, str, len); break;
+ case DOMAIN: append_chars (sp->strs.credentials.domain, str, len); break;
+ case USER: append_chars (sp->strs.credentials.username, str, len); break;
+ case PASS: append_chars (sp->strs.credentials.password, str, len); break;
+
+ default:
+ fputs ("Unexpected characters: ", stderr);
+ while (len--)
+ fputc (*str++, stderr);
+ fputc ('\n', stderr);
+ sp->state = ERROR;
+ break;
+ }
+}
+
+
+struct conffile * parse_xml_conf (const char *file)
+{
+ LIBXML_TEST_VERSION;
+
+ struct _xmlSAXHandler sax = {
+ .startDocument = start_document,
+ .endDocument = end_document,
+ .startElement = start_element,
+ .endElement = end_element,
+ .error = error,
+ .characters = chars,
+ };
+
+
+ struct sax_parser user_data = {
+ .state = START
+ };
+
+ user_data.cf = conffile_new();
+
+ if (NULL != user_data.cf)
+ {
+ if ((0 != xmlSAXUserParseFile (&sax, &user_data, file)) ||
+ (user_data.state != FINISH))
+ {
+ fputs ("Parse error\n", stderr);
+ conffile_destroy (user_data.cf);
+ user_data.cf = NULL;
+ }
+ }
+
+ return user_data.cf;
+}
DIR diff --git a/sax.h b/sax.h
@@ -0,0 +1,9 @@
+#ifndef SAX_H
+ #define SAX_H
+
+ #include "conffile.h"
+
+ struct conffile * parse_xml_conf (const char *file);
+
+#endif
+
DIR diff --git a/usmb.c b/usmb.c
@@ -1,86 +1,37 @@
#include <sys/time.h> // struct timeval needed by libsmbclient.h
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
#include <libsmbclient.h>
#include <fuse.h>
#include <assert.h>
+#include <errno.h>
#include <stdarg.h>
+#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include "conffile.h"
+#include "options.h"
+#include "sax.h"
#include "usmb.h"
#include "usmb_dir.h"
#include "usmb_file.h"
+#include "utils.h"
+static const char *share = NULL;
-#define INIT_LEN 256
-static char * concat_strings (int num, ...)
-{
- va_list ap;
-
- char *base, *out;
-
- base = out = malloc (INIT_LEN);
- if (NULL == base)
- return NULL;
-
- size_t buff_size = INIT_LEN;
-
- va_start (ap, num);
- for (int i = 0; i < num; ++i)
- {
- const char *next = va_arg (ap, const char *);
- assert (NULL != next);
-
- size_t next_len = strlen (next);
- size_t required = (out - base) + next_len + 1;
-
- if (buff_size < required)
- {
- while (buff_size < required)
- {
- size_t dbl_len = buff_size * 2;
- if (dbl_len < buff_size)
- {
- free (base);
- return NULL;
- }
-
- buff_size = dbl_len;
- }
-
- ptrdiff_t diff = out - base;
-
- char *newbase = realloc (base, buff_size);
- if (NULL == newbase)
- {
- free (base);
- return NULL;
- }
-
- base = newbase;
- out = base + diff;
- }
-
- memcpy (out, next, next_len);
- out += next_len;
- }
- va_end (ap);
-
- *out = '\0';
- return base;
-}
-
-
-static const char *share = "//127.0.0.1/geoff";
char * make_url (const char *path)
{
- DEBUG (fprintf (stderr, "make_url (%s)\n", path));
+ assert (NULL != share);
+
if ((NULL == path) || ('\0' == path[0]))
- return concat_strings (2, "smb:", share);
+ return xstrdup (share);
else
- return concat_strings (3, "smb:", share, path);
+ return concat_strings (2, share, path);
}
@@ -91,6 +42,10 @@ static inline void do_strncpy (char *to, const char *from, int tolen)
}
+static const char *domain = NULL;
+static const char *username = NULL;
+static const char *password = NULL;
+
static void auth_fn (const char *srv, const char *shr, char *wg, int wglen,
char *un, int unlen, char *pw, int pwlen)
{
@@ -98,14 +53,19 @@ static void auth_fn (const char *srv, const char *shr, char *wg, int wglen,
(void)shr;
DEBUG (fprintf (stderr, "Authenticating for \\\\%s\\%s\n", srv, shr));
- do_strncpy (wg, "WORKGROUP", wglen);
- do_strncpy (un, "geoff", unlen);
- do_strncpy (pw, "ze]T72%5V", pwlen);
+ assert (NULL != domain);
+ assert (NULL != username);
+ assert (NULL != password);
+
+ do_strncpy (wg, domain, wglen);
+ do_strncpy (un, username, unlen);
+ do_strncpy (pw, password, pwlen);
}
static void * usmb_init (void)
{
+ DEBUG (fputs ("usmb_init()\n", stderr));
return NULL;
}
@@ -113,49 +73,41 @@ static void * usmb_init (void)
static void usmb_destroy (void *unused)
{
(void)unused;
+ DEBUG (fputs ("usmb_destroy()\n", stderr));
}
-// probably won't (can't ?) implement these.
-#define usmb_readlink NULL
-#define usmb_mknod NULL
-#define usmb_symlink NULL
-#define usmb_chmod NULL
-#define usmb_chown NULL
-#define usmb_flush NULL
-#define usmb_fsync NULL
+// probably won't (can't ?) implement these:
+// readlink mknod symlink flush fsync
// fuse.h says "Just a placeholder, don't set".
-#define usmb_statfs NULL
-
-#define usmb_access NULL
+// statfs
// no easy way of implementing these.
-#define usmb_ftruncate NULL
-
+// access ftruncate
static struct fuse_operations fuse_ops = {
.getattr = usmb_getattr,
- .readlink = usmb_readlink,
+ .readlink = NULL,
.getdir = NULL,
- .mknod = usmb_mknod,
+ .mknod = NULL,
.mkdir = usmb_mkdir,
.unlink = usmb_unlink,
.rmdir = usmb_rmdir,
- .symlink = usmb_symlink,
+ .symlink = NULL,
.rename = usmb_rename,
.link = NULL,
.chmod = usmb_chmod,
- .chown = usmb_chown,
+ .chown = NULL, // usmb_chown, --not implemented in libsmbclient
.truncate = usmb_truncate,
.utime = usmb_utime,
.open = usmb_open,
.read = usmb_read,
.write = usmb_write,
- .statfs = usmb_statfs,
- .flush = usmb_flush,
+ .statfs = NULL,
+ .flush = NULL,
.release = usmb_release,
- .fsync = usmb_fsync,
+ .fsync = NULL,
#ifdef SETXATTR
.setxattr = usmb_setxattr,
.getxattr = usmb_getxattr,
@@ -168,22 +120,156 @@ static struct fuse_operations fuse_ops = {
.fsyncdir = NULL,
.init = usmb_init,
.destroy = usmb_destroy,
- .access = usmb_access,
+ .access = NULL,
.create = usmb_create,
- .ftruncate = usmb_ftruncate,
+ .ftruncate = NULL,
.fgetattr = usmb_fgetattr
};
-
+
+// this should really open() the file and check the fd, but the XML parser
+// takes a filename, not a file descriptor
+static bool check_conf_perms (const char *conffile)
+{
+ struct stat buf;
+ if (0 == stat (conffile, &buf))
+ {
+ if (getuid() != buf.st_uid)
+ {
+ fprintf (stderr, "You do not own the configuration file %s\n",
+ conffile);
+ return false;
+ }
+
+ if (buf.st_mode & (S_IRWXG | S_IRWXO))
+ {
+ fprintf (stderr, "Configuration file %s is accessible to non-owner\n",
+ conffile);
+ return false;
+ }
+ }
+ else
+ {
+ fprintf (stderr, "Cannot configuration file %s: %s\n",
+ conffile, strerror (errno));
+ return false;
+ }
+
+ return true;
+}
+
+
+static bool fix_up_conf_details (struct conffile *cf, struct mount *mount)
+{
+ struct credentials *creds = conffile_get_credentials (cf, mount->credentials);
+ assert (NULL != creds);
+
+ size_t len = strlen ("smb:///") +
+ strlen (mount->server) +
+ strlen (mount->share) + 1;
+ char *str;
+ if (NULL == (str = malloc (len)))
+ {
+ perror ("Cannot allocate share name");
+ return false;
+ }
+
+ strcpy (str, "smb://");
+ strcat (str, mount->server);
+ strcat (str, "/");
+ strcat (str, mount->share);
+
+ domain = xstrdup (creds->domain);
+ username = xstrdup (creds->username);
+ password = xstrdup (creds->password);
+
+ if ((NULL == domain) ||
+ (NULL == username) ||
+ (NULL == password))
+ {
+ fputs ("Out of memory\n", stderr);
+ xfree (password);
+ xfree (username);
+ xfree (domain);
+ free (str);
+ return false;
+ }
+
+ share = str;
+
+ DEBUG (fprintf (stderr, "Share URL: %s\n", share));
+ DEBUG (fprintf (stderr, "Username: %s\\%s\n", domain, username));
+ return true;
+}
+
int main (int argc, char **argv)
{
+ const char *conffile;
+ const char *mountid;
+
+ {
+ static char conf[256];
+ snprintf (conf, sizeof (conf), "%s/.usmb.conf", getenv ("HOME"));
+ conffile = conf;
+ }
+
+ if (!parse_args (&argc, &argv, &mountid, &conffile))
+ return EXIT_FAILURE;
+
+ if (!check_conf_perms (conffile))
+ return EXIT_FAILURE;
+
+ struct conffile *cf = parse_xml_conf (conffile);
+ if (NULL == cf)
+ return EXIT_FAILURE;
+
+ struct mount *mount = conffile_get_mount (cf, mountid);
+
+ if (NULL == mount)
+ {
+ fprintf (stderr, "Mount %s not defined in configuration file %s\n",
+ mountid, conffile);
+ conffile_destroy (cf);
+ return EXIT_FAILURE;
+ }
+
+ char *mountpoint = xstrdup (mount->mountpoint);
+ char *options = xstrdup (mount->options);
+
+ if ((NULL == mountpoint) ||
+ (mount->options && (NULL == options)))
+ {
+ perror ("Cannot allocate mountpoint");
+ return EXIT_FAILURE;
+ }
+
+ {
+ bool ret = fix_up_conf_details (cf, mount);
+ conffile_destroy (cf);
+
+ if (false == ret)
+ return EXIT_FAILURE;
+ }
+
if (0 != smbc_init (auth_fn, 0))
{
perror ("Cannot initialise SMB library");
return EXIT_FAILURE;
}
- return fuse_main (argc, argv, &fuse_ops);
+ int fuse_argc;
+ char **fuse_argv;
+ build_fuse_args (options, mountpoint, &fuse_argc, &fuse_argv);
+ int ret = fuse_main (fuse_argc, fuse_argv, &fuse_ops);
+
+ xfree (mountpoint);
+ xfree (options);
+ xfree (domain);
+ xfree (username);
+ xfree (password);
+ xfree (share);
+
+ return ret;
}
DIR diff --git a/usmb.conf b/usmb.conf
@@ -0,0 +1,16 @@
+<usmbconfig>
+
+ <credentials id="workgroup">
+ <domain>WORKGROUP</domain>
+ <username>user</username>
+ <password>pass</password>
+ </credentials>
+
+ <mount id="home" credentials="workgroup">
+ <server>127.0.0.1</server>
+ <share>myshare</share>
+ <mountpoint>/tmp/smb</mountpoint>
+ </mount>
+
+</usmbconfig>
+
DIR diff --git a/usmb.h b/usmb.h
@@ -1,13 +1,6 @@
#ifndef USMB_H
#define USMB_H
- #ifdef DEBUG
- #undef DEBUG
- #define DEBUG(x) (x)
- #else
- #define DEBUG(x) ((void)0)
- #endif
-
char * make_url (const char *path);
#endif
DIR diff --git a/usmb_dir.c b/usmb_dir.c
@@ -10,6 +10,7 @@
#include <string.h>
#include "usmb_dir.h"
#include "usmb.h"
+#include "utils.h"
int usmb_mkdir (const char *dirname, mode_t mode)
@@ -19,7 +20,7 @@ int usmb_mkdir (const char *dirname, mode_t mode)
return -ENOMEM;
DEBUG (fprintf (stderr, "mkdir (%s)\n", url));
- int ret = smbc_mkdir (dirname, mode) ? -errno : 0;
+ int ret = smbc_mkdir (url, mode) ? -errno : 0;
free (url);
return ret;
}
@@ -31,8 +32,8 @@ int usmb_rmdir (const char *dirname)
if (NULL == url)
return -ENOMEM;
- DEBUG (fprintf (stderr, "mkdir (%s)\n", url));
- int ret = smbc_rmdir (dirname) ? -errno : 0;
+ DEBUG (fprintf (stderr, "rmdir (%s)\n", url));
+ int ret = smbc_rmdir (url) ? -errno : 0;
free (url);
return ret;
}
@@ -44,9 +45,10 @@ int usmb_opendir (const char *dirname, struct fuse_file_info *fi)
if (NULL == url)
return -ENOMEM;
- DEBUG (fprintf (stderr, "opendir (%s)\n", url));
+ DEBUG (fprintf (stderr, "opendir (%s)", url));
int fd = smbc_opendir (url);
fi->fh = fd;
+ DEBUG (fprintf (stderr, " = %d\n", fd));
int ret = (fd < 0) ? -errno : 0;
free (url);
@@ -60,7 +62,7 @@ int usmb_readdir (const char *path, void *h, fuse_fill_dir_t filler,
(void)path;
(void)offset;
- DEBUG (fprintf (stderr, "readdir (%s)\n", path));
+ DEBUG (fprintf (stderr, "readdir (%s, %llu)\n", path, fi->fh));
struct smbc_dirent *dirent;
errno = 0;
@@ -98,7 +100,7 @@ int usmb_releasedir (const char *path, struct fuse_file_info *fi)
{
(void)path;
- DEBUG (fprintf (stderr, "releasedir (%s)\n", path));
+ DEBUG (fprintf (stderr, "releasedir (%s, %llu)\n", path, fi->fh));
return (smbc_closedir (fi->fh) < 0) ? -errno : 0;
}
DIR diff --git a/usmb_file.c b/usmb_file.c
@@ -8,6 +8,7 @@
#include <string.h>
#include "usmb_file.h"
#include "usmb.h"
+#include "utils.h"
int usmb_getattr (const char *filename, struct stat *st)
@@ -25,6 +26,7 @@ int usmb_fgetattr (const char *filename, struct stat *st,
struct fuse_file_info *fi)
{
(void)filename;
+ DEBUG (fprintf (stderr, "fgetattr (%s, %llu)\n", filename, fi->fh));
return (smbc_fstat (fi->fh, st) < 0) ? -errno : 0;
}
@@ -35,6 +37,7 @@ int usmb_unlink (const char *filename)
if (NULL == url)
return -ENOMEM;
+ DEBUG (fprintf (stderr, "unlink (%s)\n", url));
int ret = (smbc_unlink (url) < 0) ? -errno : 0;
free (url);
return ret;
@@ -47,11 +50,12 @@ int usmb_open (const char *filename, struct fuse_file_info *fi)
if (NULL == url)
return -ENOMEM;
- DEBUG (fprintf (stderr, "open (%s)\n", url));
+ DEBUG (fprintf (stderr, "open (%s)", url));
int fd = smbc_open (url, fi->flags, 0);
int ret = (fd < 0) ? -errno : 0;
fi->fh = fd;
+ DEBUG (fprintf (stderr, " = %d\n", fd));
free (url);
return ret;
@@ -61,6 +65,7 @@ int usmb_open (const char *filename, struct fuse_file_info *fi)
int usmb_release (const char *filename, struct fuse_file_info *fi)
{
(void)filename;
+ DEBUG (fprintf (stderr, "release (%s, %llu)\n", filename, fi->fh));
return (smbc_close (fi->fh) < 0) ? -errno : 0;
}
@@ -71,8 +76,11 @@ int usmb_read (const char *filename, char *buff, size_t len, off_t off,
(void)filename;
(void)off;
- /* TODO: this may be WRONG: do we need to consider the offset */
DEBUG (fprintf (stderr, "read (%p, %u)\n", buff, len));
+
+ if (smbc_lseek (fi->fh, off, SEEK_SET) < 0)
+ return -errno;
+
int bytes = smbc_read (fi->fh, buff, len);
return (bytes < 0) ? -errno : bytes;
}
@@ -84,8 +92,11 @@ int usmb_write (const char *filename, const char *buff, size_t len, off_t off,
(void)filename;
(void)off;
- /* TODO: this may be WRONG: do we need to consider the offset */
- DEBUG (fprintf (stderr, "write (%p, %u)\n", buff, len));
+ DEBUG (fprintf (stderr, "write (%p, len=%u, off=%lld)\n", buff, len, off));
+
+ if (smbc_lseek (fi->fh, off, SEEK_SET) < 0)
+ return -errno;
+
int bytes = smbc_write (fi->fh, (char *)buff, len);
return (bytes < 0) ? -errno : bytes;
}
@@ -97,9 +108,12 @@ int usmb_create (const char *filename, mode_t mode, struct fuse_file_info *fi)
if (NULL == url)
return -ENOMEM;
+ DEBUG (fprintf (stderr, "creat (%s)", url));
+
int fd = smbc_creat (url, mode);
int ret = (fd < 0) ? -errno : 0;
fi->fh = fd;
+ DEBUG (fprintf (stderr, " = %d\n", fd));
free (url);
return ret;
@@ -119,6 +133,7 @@ int usmb_rename (const char *from, const char *to)
return -ENOMEM;
}
+ DEBUG (fprintf (stderr, "rename (%s, %s)\n", fromurl, tourl));
int ret = (smbc_rename (fromurl, tourl) < 0) ? -errno : 0;
free (tourl);
free (fromurl);
@@ -131,7 +146,8 @@ int usmb_utime (const char *filename, struct utimbuf *utb)
char *url = make_url (filename);
if (NULL == url)
return -ENOMEM;
-
+
+ DEBUG (fprintf (stderr, "utime (%s)\n", url));
int ret = (smbc_utime (url, utb) < 0) ? -errno : 0;
free (url);
return ret;
@@ -140,13 +156,15 @@ int usmb_utime (const char *filename, struct utimbuf *utb)
int usmb_truncate (const char *filename, off_t newsize)
{
- // FIXME: TODO: currently ignores newsize
- (void)newsize;
+ // FIXME: TODO: handle newsize != 0
+ if (0 != newsize)
+ return -ENOSYS;
char *url = make_url (filename);
if (NULL == url)
return -ENOMEM;
+ DEBUG (fprintf (stderr, "truncate (%s)\n", url));
int fd = smbc_open (url, O_WRONLY | O_TRUNC, 0);
if (fd < 0)
return -errno;
@@ -154,3 +172,32 @@ int usmb_truncate (const char *filename, off_t newsize)
smbc_close (fd);
return 0;
}
+
+
+int usmb_chmod (const char *filename, mode_t mode)
+{
+ char *url = make_url (filename);
+ if (NULL == url)
+ return -ENOMEM;
+
+ DEBUG (fprintf (stderr, "chmod (%s, %u)\n", url, mode));
+ int ret = (smbc_chmod (url, mode) < 0) ? -errno : 0;
+ free (url);
+ return ret;
+}
+
+
+#if 0
+// smbc_chown isn't implemented in libsmbclient
+int usmb_chown (const char *filename, uid_t owner, uid_t group)
+{
+ char *url = make_url (filename);
+ if (NULL == url)
+ return -ENOMEM;
+
+ DEBUG (fprintf (stderr, "chown (%s, %d, %d)\n", url, owner, group));
+ int ret = (smbc_chown (url, owner, group) < 0) ? -errno : 0;
+ free (url);
+ return ret;
+}
+#endif
DIR diff --git a/usmb_file.h b/usmb_file.h
@@ -20,5 +20,7 @@
int usmb_rename (const char *from, const char *to);
int usmb_utime (const char *filename, struct utimbuf *utb);
int usmb_truncate (const char *filename, off_t newsize);
+ int usmb_chmod (const char *filename, mode_t mode);
+// int usmb_chown (const char *filename, uid_t owner, uid_t group);
#endif
DIR diff --git a/utils.c b/utils.c
@@ -0,0 +1,92 @@
+#include <assert.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "utils.h"
+
+
+
+#define INIT_LEN 256
+char * concat_strings (int num, ...)
+{
+ va_list ap;
+
+ char *base, *out;
+
+ base = out = malloc (INIT_LEN);
+ if (NULL == base)
+ return NULL;
+
+ size_t buff_size = INIT_LEN;
+
+ va_start (ap, num);
+ for (int i = 0; i < num; ++i)
+ {
+ const char *next = va_arg (ap, const char *);
+ assert (NULL != next);
+
+ size_t next_len = strlen (next);
+ size_t required = (out - base) + next_len + 1;
+
+ if (buff_size < required)
+ {
+ while (buff_size < required)
+ {
+ size_t dbl_len = buff_size * 2;
+ if (dbl_len < buff_size)
+ {
+ free (base);
+ return NULL;
+ }
+
+ buff_size = dbl_len;
+ }
+
+ ptrdiff_t diff = out - base;
+
+ char *newbase = realloc (base, buff_size);
+ if (NULL == newbase)
+ {
+ free (base);
+ return NULL;
+ }
+
+ base = newbase;
+ out = base + diff;
+ }
+
+ memcpy (out, next, next_len);
+ out += next_len;
+ }
+ va_end (ap);
+
+ *out = '\0';
+ return base;
+}
+
+
+char * xstrdup (const char *in)
+{
+ char *out = NULL;
+
+ if (in)
+ {
+ size_t len = strlen (in) + 1;
+
+ if ((out = malloc (len)))
+ memcpy (out, in, len);
+ }
+
+ return out;
+}
+
+
+// the const here lets us pass a pointer to const
+void xfree (const void *ptr)
+{
+ if (NULL != ptr)
+ free ((void *)ptr);
+}
+
DIR diff --git a/utils.h b/utils.h
@@ -0,0 +1,15 @@
+#ifndef UTILS_H
+ #define UTILS_H
+
+ #ifdef DEBUG
+ #undef DEBUG
+ #define DEBUG(x) (x)
+ #else
+ #define DEBUG(x) ((void)0)
+ #endif
+
+ char * concat_strings (int num, ...);
+ char * xstrdup (const char *in);
+ void xfree (const void *ptr);
+
+#endif
DIR diff --git a/version.h b/version.h
@@ -0,0 +1,21 @@
+#ifndef VERSION_H
+ #define VERSION_H
+
+ #include <stdio.h>
+
+ // suffix: a = alpha, b = beta, p = pre-release, r = release
+
+ #define USMB_VERSION_MAJOR 0
+ #define USMB_VERSION_MINOR 1
+ #define USMB_VERSION_SUFFIX 'a'
+
+ static inline void show_version (FILE *fp)
+ {
+ fprintf (fp, "usmb version: %d.%d%c\n",
+ USMB_VERSION_MAJOR, USMB_VERSION_MINOR, USMB_VERSION_SUFFIX);
+ fprintf (fp, "FUSE version: %d.%d\n",
+ FUSE_MAJOR_VERSION, FUSE_MINOR_VERSION);
+ fprintf (fp, "Samba version: %s\n", smbc_version());
+ }
+
+#endif