first version of hacksm
authorroot <root@hsm1n1.VSOFS1.COM>
Sat, 16 Aug 2008 10:30:53 +0000 (20:30 +1000)
committerroot <root@hsm1n1.VSOFS1.COM>
Sat, 16 Aug 2008 10:30:53 +0000 (20:30 +1000)
Makefile [new file with mode: 0644]
common.c [new file with mode: 0644]
hacksm.h [new file with mode: 0644]
hacksm_ls.c [new file with mode: 0644]
hacksm_migrate.c [new file with mode: 0644]
hacksmd.c [new file with mode: 0644]

diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..e8cc470
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,20 @@
+CC=gcc
+CFLAGS=-Wall -g -m32
+LIBS=-ldmapi
+
+all: hacksmd hacksm_migrate hacksm_ls
+
+.c.o:
+       $(CC) $(CFLAGS) -c $< -o $@
+
+hacksmd: hacksmd.o common.o
+       $(CC) $(CFLAGS) -o $@ $^ $(LIBS)
+
+hacksm_migrate: hacksm_migrate.o common.o
+       $(CC) $(CFLAGS) -o $@ $^ $(LIBS)
+
+hacksm_ls: hacksm_ls.o common.o
+       $(CC) $(CFLAGS) -o $@ $^ $(LIBS)
+
+clean: 
+       rm -f *.o hacksmd hacksm_migrate hacksm_ls
diff --git a/common.c b/common.c
new file mode 100644 (file)
index 0000000..096c5c5
--- /dev/null
+++ b/common.c
@@ -0,0 +1,128 @@
+/*
+  a test implementation of a HSM daemon
+
+  Andrew Tridgell August 2008
+
+ */
+
+#include "hacksm.h"
+
+static const struct {
+       dm_eventtype_t ev;
+       const char *name;
+} dmapi_event_strings[] = {
+#define EVENT_STRING(x) { x, #x }
+       EVENT_STRING(DM_EVENT_INVALID),
+       EVENT_STRING(DM_EVENT_CLOSE),
+       EVENT_STRING(DM_EVENT_MOUNT),
+       EVENT_STRING(DM_EVENT_PREUNMOUNT),
+       EVENT_STRING(DM_EVENT_UNMOUNT),
+       EVENT_STRING(DM_EVENT_NOSPACE),
+       EVENT_STRING(DM_EVENT_DEBUT),
+       EVENT_STRING(DM_EVENT_CREATE),
+       EVENT_STRING(DM_EVENT_POSTCREATE),
+       EVENT_STRING(DM_EVENT_REMOVE),
+       EVENT_STRING(DM_EVENT_POSTREMOVE),
+       EVENT_STRING(DM_EVENT_RENAME),
+       EVENT_STRING(DM_EVENT_POSTRENAME),
+       EVENT_STRING(DM_EVENT_SYMLINK),
+       EVENT_STRING(DM_EVENT_POSTSYMLINK),
+       EVENT_STRING(DM_EVENT_LINK),
+       EVENT_STRING(DM_EVENT_POSTLINK),
+       EVENT_STRING(DM_EVENT_READ),
+       EVENT_STRING(DM_EVENT_WRITE),
+       EVENT_STRING(DM_EVENT_TRUNCATE),
+       EVENT_STRING(DM_EVENT_ATTRIBUTE),
+       EVENT_STRING(DM_EVENT_CANCEL),
+       EVENT_STRING(DM_EVENT_DESTROY),
+       EVENT_STRING(DM_EVENT_USER),
+       EVENT_STRING(DM_EVENT_PREPERMCHANGE),
+       EVENT_STRING(DM_EVENT_POSTPERMCHANGE),
+       EVENT_STRING(DM_EVENT_MAX),
+};
+
+const char *dmapi_event_string(dm_eventtype_t ev)
+{
+       int i;
+       for (i=0;i<sizeof(dmapi_event_strings)/sizeof(dmapi_event_strings[0]);i++) {
+               if (dmapi_event_strings[i].ev == ev) {
+                       return dmapi_event_strings[i].name;
+               }
+       }
+       return "UNKNOWN";
+}
+
+void hsm_recover_session(const char *name, dm_sessid_t *sid)
+{
+       int ret, i;
+       u_int n;
+       dm_sessid_t *sess = NULL;
+       dm_sessid_t oldsid = DM_NO_SESSION;
+
+       ret = dm_getall_sessions(0, NULL, &n);
+       if (ret == 0) {
+               goto new_session;
+       }
+       if (errno != E2BIG) {
+               printf("Bad error code %s from dm_getall_sessions\n", strerror(errno));
+               exit(1);
+       }
+
+       sess = (dm_sessid_t *)calloc(sizeof(dm_sessid_t), n);
+       if (sess == NULL) {
+               printf("No memory for %u sessions\n", n);
+               exit(1);
+       }
+
+       ret = dm_getall_sessions(n, sess, &n);
+       if (ret != 0) {
+               printf("dm_getall_sessions failed\n");
+               exit(1);
+       }
+
+       for (i=0;i<n;i++) {
+               char buf[DM_SESSION_INFO_LEN+1];
+               size_t len;
+
+               ret = dm_query_session(sess[i], DM_SESSION_INFO_LEN, buf, &len);
+               if (ret != 0) {
+                       continue;
+               }
+               buf[len] = 0;
+               if (strcmp(buf, name) == 0) {
+                       printf("Recovered existing session\n");
+                       oldsid = sess[i];
+                       break;
+               }
+       }
+       free(sess);
+
+new_session:
+       ret = dm_create_session(oldsid, discard_const(name), sid);
+       if (ret != 0) {
+               printf("Failed to create session\n");
+               exit(1);
+       }
+}
+
+
+int hsm_store_open(dev_t device, ino_t inode, int flags)
+{
+       char *fname = NULL;
+       asprintf(&fname, HSM_STORE "/0x%llx:0x%llx",
+                (unsigned long long)device, (unsigned long long)inode);
+       int fd = open(fname, flags, 0600);
+       free(fname);
+       return fd;
+}
+
+int hsm_store_unlink(dev_t device, ino_t inode)
+{
+       char *fname = NULL;
+       int ret;
+       asprintf(&fname, HSM_STORE "/0x%llx:0x%llx",
+                (unsigned long long)device, (unsigned long long)inode);
+       ret = unlink(fname);
+       free(fname);
+       return ret;
+}
diff --git a/hacksm.h b/hacksm.h
new file mode 100644 (file)
index 0000000..922265e
--- /dev/null
+++ b/hacksm.h
@@ -0,0 +1,45 @@
+#define _XOPEN_SOURCE 500
+#define _GNU_SOURCE 
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <getopt.h>
+#include <signal.h>
+#include <utime.h>
+#include <stdbool.h>
+#include <sys/mman.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include <time.h>
+#include <aio.h>
+#include <dmapi.h>
+
+#define discard_const(ptr) ((void *)((intptr_t)(ptr)))
+
+const char *dmapi_event_string(dm_eventtype_t ev);
+void hsm_recover_session(const char *name, dm_sessid_t *sid);
+int hsm_store_open(dev_t device, ino_t inode, int flags);
+int hsm_store_unlink(dev_t device, ino_t inode);
+
+enum hsm_migrate_state {
+       HSM_STATE_START     = 0,
+       HSM_STATE_MIGRATED  = 1,
+       HSM_STATE_RECALL    = 2};
+
+struct hsm_attr {
+       char magic[4];
+       off_t size;
+       dev_t device;
+       ino_t inode;
+       enum hsm_migrate_state state;
+};
+
+#define HSM_MAGIC "HSM1"
+#define HSM_ATTRNAME "hackattr"
+
+#define HSM_STORE "/hacksm_store"
diff --git a/hacksm_ls.c b/hacksm_ls.c
new file mode 100644 (file)
index 0000000..24ef895
--- /dev/null
@@ -0,0 +1,192 @@
+/*
+  a test implementation of a HSM listing tool
+
+  Andrew Tridgell August 2008
+
+ */
+
+#include "hacksm.h"
+#include <dirent.h>
+
+#define SESSION_NAME "hacksm_ls"
+
+static struct {
+       dm_sessid_t sid;
+} dmapi = {
+       .sid = DM_NO_SESSION
+};
+
+static void hsm_term_handler(int signal)
+{
+       printf("Got signal %d - exiting\n", signal);
+       exit(1);
+}
+
+
+static void hsm_init(void)
+{
+       char *dmapi_version = NULL;
+       int ret;
+
+       ret = dm_init_service(&dmapi_version);
+       if (ret != 0) {
+               printf("Failed to init dmapi\n");
+               exit(1);
+       }
+
+       printf("Initialised DMAPI version '%s'\n", dmapi_version);      
+
+       hsm_recover_session(SESSION_NAME, &dmapi.sid);
+}
+
+
+static void hsm_ls(const char *path)
+{
+       int ret;
+       void *hanp = NULL;
+       size_t hlen = 0;
+       dm_attrname_t attrname;
+       dm_token_t token = DM_NO_TOKEN;
+       size_t rlen;
+       struct hsm_attr h;
+       int fd;
+       int have_right = 0;
+
+       ret = dm_path_to_handle(discard_const(path), &hanp, &hlen);
+       if (ret != 0) {
+               printf("dm_path_to_handle failed for %s - %s\n", path, strerror(errno));
+               return;
+       }
+
+       ret = dm_create_userevent(dmapi.sid, 0, NULL, &token);
+       if (ret != 0) {
+               printf("dm_create_userevent failed for %s - %s\n", path, strerror(errno));
+               dm_handle_free(hanp, hlen);
+               return;
+       }
+
+       ret = dm_request_right(dmapi.sid, hanp, hlen, token, DM_RR_WAIT, DM_RIGHT_SHARED);
+       if (ret != 0) {
+               printf("dm_request_right failed for %s - %s\n", path, strerror(errno));
+               goto done;
+       }
+       have_right = 1;
+
+        memset(attrname.an_chars, 0, DM_ATTR_NAME_SIZE);
+        strncpy((char*)attrname.an_chars, HSM_ATTRNAME, DM_ATTR_NAME_SIZE);
+
+       ret = dm_get_dmattr(dmapi.sid, hanp, hlen, token, &attrname, 
+                           sizeof(h), &h, &rlen);
+       if (ret != 0 && errno != ENOENT) {
+               printf("dm_get_dmattr failed for %s - %s\n", path, strerror(errno));
+               goto done;
+       }
+
+       if (ret != 0) {
+               printf("p            %s\n", path);
+               goto done;
+       }
+       if (strncmp(h.magic, HSM_MAGIC, sizeof(h.magic)) != 0) {
+               printf("Bad magic '%*.*s'\n", (int)sizeof(h.magic), (int)sizeof(h.magic), h.magic);
+               goto done;
+       }
+
+       if (h.state == HSM_STATE_MIGRATED) {
+               fd = hsm_store_open(h.device, h.inode, O_RDONLY);
+               if (fd == -1) {
+                       printf("Failed to open store file for %s - %s (0x%llx:0x%llx)\n", 
+                              path, strerror(errno), 
+                              (unsigned long long)h.device, (unsigned long long)h.inode);
+               }
+               close(fd);
+       }
+
+       printf("m %7u %d  %s\n", (unsigned)h.size, (int)h.state, path);
+
+done:
+       if (have_right) {
+               ret = dm_release_right(dmapi.sid, hanp, hlen, token);
+               if (ret == -1) {
+                       printf("failed dm_release_right on %s - %s\n", path, strerror(errno));
+               }
+       }
+
+       ret = dm_respond_event(dmapi.sid, token, DM_RESP_CONTINUE, 0, 0, NULL);
+       if (ret == -1) {
+               printf("failed dm_respond_event on %s - %s\n", path, strerror(errno));
+               exit(1);
+       }
+
+       dm_handle_free(hanp, hlen);
+}
+
+
+static void hsm_lsdir(const char *path)
+{
+       DIR *d;
+       struct dirent *de;
+
+       d = opendir(path);
+       if (d == NULL) {
+               return;
+       }
+
+       while ((de = readdir(d))) {
+               struct stat st;
+               char *name = NULL;
+               asprintf(&name, "%s/%s", path, de->d_name);
+               if (stat(name, &st) == 0 && S_ISREG(st.st_mode)) {
+                       hsm_ls(name);
+               }
+               free(name);
+       }
+
+       closedir(d);
+}
+
+static void usage(void)
+{
+       printf("Usage: hacksm_ls PATH..\n");
+       exit(0);
+}
+
+int main(int argc, char * const argv[])
+{
+       int opt, i;
+
+       /* parse command-line options */
+       while ((opt = getopt(argc, argv, "h")) != -1) {
+               switch (opt) {
+               case 'h':
+               default:
+                       usage();
+                       break;
+               }
+       }
+
+       setlinebuf(stdout);     
+
+       argv += optind;
+       argc -= optind;
+
+       if (argc == 0) {
+               usage();
+       }
+
+       signal(SIGTERM, hsm_term_handler);
+       signal(SIGINT, hsm_term_handler);
+
+       hsm_init();
+
+       for (i=0;i<argc;i++) {
+               struct stat st;
+               if (lstat(argv[i], &st) != 0) continue;
+               if (S_ISDIR(st.st_mode)) {
+                       hsm_lsdir(argv[i]);
+               } else if (S_ISREG(st.st_mode)) {
+                       hsm_ls(argv[i]);
+               }
+       }
+
+       return 0;
+}
diff --git a/hacksm_migrate.c b/hacksm_migrate.c
new file mode 100644 (file)
index 0000000..d95958b
--- /dev/null
@@ -0,0 +1,257 @@
+/*
+  a test implementation of a HSM migrate tool
+
+  Andrew Tridgell August 2008
+
+ */
+
+#include "hacksm.h"
+
+#define SESSION_NAME "hacksm_migrate"
+
+static struct {
+       dm_sessid_t sid;
+} dmapi = {
+       .sid = DM_NO_SESSION
+};
+
+static void hsm_term_handler(int signal)
+{
+       printf("Got signal %d - exiting\n", signal);
+       exit(1);
+}
+
+
+static void hsm_init(void)
+{
+       char *dmapi_version = NULL;
+       int ret;
+
+       ret = dm_init_service(&dmapi_version);
+       if (ret != 0) {
+               printf("Failed to init dmapi\n");
+               exit(1);
+       }
+
+       printf("Initialised DMAPI version '%s'\n", dmapi_version);      
+
+       hsm_recover_session(SESSION_NAME, &dmapi.sid);
+}
+
+
+static void hsm_migrate(const char *path)
+{
+       int ret;
+       void *hanp = NULL;
+       size_t hlen = 0;
+       dm_attrname_t attrname;
+       dm_token_t token = DM_NO_TOKEN;
+       char buf[0x1000];
+       size_t rlen;
+       struct stat st;
+       struct hsm_attr h;
+       dm_region_t region;
+       dm_boolean_t exactFlag;
+       off_t ofs;
+       int fd;
+
+       ret = dm_path_to_handle(discard_const(path), &hanp, &hlen);
+       if (ret != 0) {
+               printf("dm_path_to_handle failed for %s - %s\n", path, strerror(errno));
+               exit(1);
+       }
+
+       ret = dm_create_userevent(dmapi.sid, 0, NULL, &token);
+       if (ret != 0) {
+               printf("dm_create_userevent failed for %s - %s\n", path, strerror(errno));
+               exit(1);
+       }
+
+       ret = dm_request_right(dmapi.sid, hanp, hlen, token, DM_RR_WAIT, DM_RIGHT_EXCL);
+       if (ret != 0) {
+               printf("dm_request_right failed for %s - %s\n", path, strerror(errno));
+               goto respond;
+       }
+
+        memset(attrname.an_chars, 0, DM_ATTR_NAME_SIZE);
+        strncpy((char*)attrname.an_chars, HSM_ATTRNAME, DM_ATTR_NAME_SIZE);
+
+       ret = dm_get_dmattr(dmapi.sid, hanp, hlen, token, &attrname, 
+                           sizeof(h), &h, &rlen);
+       if (ret != 0 && errno != ENOENT) {
+               printf("dm_get_dmattr failed for %s - %s\n", path, strerror(errno));
+               goto respond;
+       }
+
+       if (ret == 0) {
+               if (strncmp(h.magic, HSM_MAGIC, sizeof(h.magic)) != 0) {
+                       printf("Bad magic '%*.*s'\n", (int)sizeof(h.magic), (int)sizeof(h.magic), h.magic);
+                       exit(1);
+               }
+               if (h.state == HSM_STATE_START) {
+                       printf("Continuing migration of partly migrated file\n");
+                       hsm_store_unlink(h.device, h.inode);
+               } else {
+                       printf("Not migrating already migrated file %s\n", path);
+                       goto respond;
+               }
+       }
+
+       if (lstat(path, &st) != 0) {
+               printf("failed to stat %s - %s\n", path, strerror(errno));
+               goto respond;
+       }
+
+       if (!S_ISREG(st.st_mode)) {
+               printf("Not migrating non-regular file %s\n", path);
+               goto respond;
+       }
+
+       if (st.st_size == 0) {
+               printf("Not migrating file '%s' of size 0\n", path);
+               goto respond;
+       }
+
+       fd = hsm_store_open(st.st_dev, st.st_ino, O_CREAT|O_TRUNC|O_WRONLY);
+       if (fd == -1) {
+               printf("Failed to open store file for %s - %s\n", path, strerror(errno));
+               goto respond;
+       }
+
+       ofs = 0;
+       while ((ret = dm_read_invis(dmapi.sid, hanp, hlen, token, ofs, sizeof(buf), buf)) > 0) {
+               if (write(fd, buf, ret) != ret) {
+                       printf("Failed to write to store for %s - %s\n", path, strerror(errno));
+                       hsm_store_unlink(st.st_dev, st.st_ino);
+                       goto respond;
+               }
+               ofs += ret;
+       }
+       if (ret == -1) {
+               printf("failed dm_read_invis on %s - %s\n", path, strerror(errno));
+               hsm_store_unlink(st.st_dev, st.st_ino);
+               goto respond;
+       }
+       fsync(fd);
+       close(fd);
+
+       strncpy(h.magic, HSM_MAGIC, sizeof(h.magic));
+       h.size = st.st_size;
+       h.device = st.st_dev;
+       h.inode = st.st_ino;
+       h.state = HSM_STATE_START;
+
+       ret = dm_set_dmattr(dmapi.sid, hanp, hlen, token, &attrname, 0, sizeof(h), (void*)&h);
+       if (ret == -1) {
+               printf("failed dm_set_dmattr on %s - %s\n", path, strerror(errno));
+               hsm_store_unlink(st.st_dev, st.st_ino);
+               goto respond;
+       }
+
+       region.rg_offset = 0;
+       region.rg_size   = st.st_size;
+       region.rg_flags  = DM_REGION_WRITE | DM_REGION_READ;
+
+       ret = dm_set_region(dmapi.sid, hanp, hlen, token, 1, &region, &exactFlag);
+       if (ret == -1) {
+               printf("failed dm_set_region on %s - %s\n", path, strerror(errno));
+               hsm_store_unlink(st.st_dev, st.st_ino);
+               goto respond;
+       }
+
+       /* we now release the right to let any reads that
+          are pending to continue before we destroy the data */
+       ret = dm_release_right(dmapi.sid, hanp, hlen, token);
+       if (ret == -1) {
+               printf("failed dm_release_right on %s - %s\n", path, strerror(errno));
+               goto respond;
+       }
+
+       usleep(100000);
+
+       ret = dm_request_right(dmapi.sid, hanp, hlen, token, DM_RR_WAIT, DM_RIGHT_EXCL);
+       if (ret != 0) {
+               printf("dm_request_right failed for %s - %s\n", path, strerror(errno));
+               goto respond;
+       }
+
+       ret = dm_get_dmattr(dmapi.sid, hanp, hlen, token, &attrname, 
+                           sizeof(h), &h, &rlen);
+       if (ret != 0) {
+               printf("Abandoning partial migrate - attribute gone\n", h.state);
+               goto respond;
+       }
+
+       if (h.state != HSM_STATE_START) {
+               printf("Abandoning partial migrate - state=%d\n", h.state);
+               goto respond;
+       }
+
+       ret = dm_punch_hole(dmapi.sid, hanp, hlen, token, 0, st.st_size);
+       if (ret == -1) {
+               printf("failed dm_punch_hole on %s - %s\n", path, strerror(errno));
+               hsm_store_unlink(st.st_dev, st.st_ino);
+               goto respond;
+       }
+
+       h.state = HSM_STATE_MIGRATED;
+
+       ret = dm_set_dmattr(dmapi.sid, hanp, hlen, token, &attrname, 0, sizeof(h), (void*)&h);
+       if (ret == -1) {
+               printf("failed dm_set_dmattr on %s - %s\n", path, strerror(errno));
+               hsm_store_unlink(st.st_dev, st.st_ino);
+               goto respond;
+       }
+
+       printf("Migrated file '%s' of size %d\n", path, (int)st.st_size);
+
+respond:
+       ret = dm_respond_event(dmapi.sid, token, DM_RESP_CONTINUE, 0, 0, NULL);
+       if (ret == -1) {
+               printf("failed dm_respond_event on %s - %s\n", path, strerror(errno));
+               exit(1);
+       }
+
+       dm_handle_free(hanp, hlen);
+}
+
+static void usage(void)
+{
+       printf("Usage: hacksm_migrate PATH..\n");
+       exit(0);
+}
+
+int main(int argc, char * const argv[])
+{
+       int opt, i;
+
+       /* parse command-line options */
+       while ((opt = getopt(argc, argv, "h")) != -1) {
+               switch (opt) {
+               case 'h':
+               default:
+                       usage();
+                       break;
+               }
+       }
+
+       setlinebuf(stdout);     
+
+       argv += optind;
+       argc -= optind;
+
+       if (argc == 0) {
+               usage();
+       }
+
+       signal(SIGTERM, hsm_term_handler);
+       signal(SIGINT, hsm_term_handler);
+
+       hsm_init();
+
+       for (i=0;i<argc;i++) {
+               hsm_migrate(argv[i]);
+       }
+
+       return 0;
+}
diff --git a/hacksmd.c b/hacksmd.c
new file mode 100644 (file)
index 0000000..dcbfa1f
--- /dev/null
+++ b/hacksmd.c
@@ -0,0 +1,399 @@
+/*
+  a test implementation of a HSM daemon
+
+  Andrew Tridgell August 2008
+
+ */
+
+#include "hacksm.h"
+
+static struct {
+       dm_sessid_t sid;
+} dmapi = {
+       .sid = DM_NO_SESSION
+};
+
+#define SESSION_NAME "hacksmd"
+
+static void hsm_term_handler(int signal)
+{
+       printf("Got signal %d - exiting\n", signal);
+       exit(1);
+}
+
+
+static void hsm_init(const char *path)
+{
+       char *dmapi_version = NULL;
+       dm_eventset_t eventSet;
+       int ret;
+
+       ret = dm_init_service(&dmapi_version);
+       if (ret != 0) {
+               printf("Failed to init dmapi\n");
+               exit(1);
+       }
+
+       printf("Initialised DMAPI version '%s'\n", dmapi_version);      
+
+       hsm_recover_session(SESSION_NAME, &dmapi.sid);
+
+       /* we want mount events only initially */
+       DMEV_ZERO(eventSet);
+       DMEV_SET(DM_EVENT_MOUNT, eventSet);
+       ret = dm_set_disp(dmapi.sid, DM_GLOBAL_HANP, DM_GLOBAL_HLEN, DM_NO_TOKEN,
+                         &eventSet, DM_EVENT_MAX);
+       if (ret != 0) {
+               printf("Failed to setup events\n");
+               exit(1);
+       }
+}
+
+
+static void hsm_handle_mount(dm_eventmsg_t *msg)
+{
+       dm_mount_event_t *mount;
+       void *hand1;
+       size_t hand1len;
+       dm_eventset_t eventSet;
+       int ret;
+       
+       mount = DM_GET_VALUE(msg, ev_data, dm_mount_event_t*);
+       hand1 = DM_GET_VALUE(mount , me_handle1, void *);
+       hand1len = DM_GET_LEN(mount, me_handle1);
+       
+       DMEV_ZERO(eventSet);
+       DMEV_SET(DM_EVENT_READ, eventSet);
+       DMEV_SET(DM_EVENT_WRITE, eventSet);
+       DMEV_SET(DM_EVENT_TRUNCATE, eventSet);
+       DMEV_SET(DM_EVENT_DESTROY, eventSet);
+       ret = dm_set_eventlist(dmapi.sid, hand1, hand1len,
+                              DM_NO_TOKEN, &eventSet, DM_EVENT_MAX);
+       if (ret != 0) {
+               printf("Failed to setup all event handler\n");
+               exit(1);
+       }
+       
+       ret = dm_set_disp(dmapi.sid, hand1, hand1len, DM_NO_TOKEN,
+                         &eventSet, DM_EVENT_MAX);
+       if (ret != 0) {
+               printf("Failed to setup disposition for all events\n");
+               exit(1);
+       }
+       
+       ret = dm_respond_event(dmapi.sid, msg->ev_token, 
+                              DM_RESP_CONTINUE, 0, 0, NULL);
+       if (ret != 0) {
+               printf("Failed to respond to mount event\n");
+               exit(1);
+       }
+}
+
+
+static void hsm_handle_recall(dm_eventmsg_t *msg)
+{
+       dm_data_event_t *ev;
+       void *hanp;
+       size_t hlen, rlen;
+       int ret;
+       dm_attrname_t attrname;
+       dm_token_t token = msg->ev_token;
+       struct hsm_attr h;
+       int retcode = -1;
+       dm_boolean_t exactFlag;
+       int fd;
+       char buf[0x10000];
+       off_t ofs;
+       int have_right = 0;
+
+        ev = DM_GET_VALUE(msg, ev_data, dm_data_event_t *);
+        hanp = DM_GET_VALUE(ev, de_handle, void *);
+        hlen = DM_GET_LEN(ev, de_handle);
+
+        memset(attrname.an_chars, 0, DM_ATTR_NAME_SIZE);
+        strncpy((char*)attrname.an_chars, HSM_ATTRNAME, DM_ATTR_NAME_SIZE);
+
+       ret = dm_request_right(dmapi.sid, hanp, hlen, token, DM_RR_WAIT, DM_RIGHT_EXCL);
+       if (ret != 0) {
+               printf("dm_request_right failed - %s\n", strerror(errno));
+               goto done;
+       }
+
+       have_right = 1;
+
+       ret = dm_get_dmattr(dmapi.sid, hanp, hlen, token, &attrname, 
+                           sizeof(h), &h, &rlen);
+       if (ret != 0) {
+               if (errno == ENOENT) {
+                       printf("File already recalled (no attribute)\n");
+                       goto done;
+               }
+               printf("dm_get_dmattr failed - %s\n", strerror(errno));
+               goto done;
+       }
+
+       if (rlen != sizeof(h)) {
+               printf("hsm_handle_read - bad attribute size %d\n", (int)rlen);
+               goto done;
+       }
+
+       if (strncmp(h.magic, HSM_MAGIC, sizeof(h.magic)) != 0) {
+               printf("Bad magic '%*.*s'\n", (int)sizeof(h.magic), (int)sizeof(h.magic), h.magic);
+               goto done;
+       }
+
+       h.state = HSM_STATE_RECALL;
+       ret = dm_set_dmattr(dmapi.sid, hanp, hlen, token, &attrname, 0, sizeof(h), (void*)&h);
+       if (ret != 0) {
+               printf("dm_set_dmattr failed - %s\n", strerror(errno));
+               goto done;
+       }
+
+       fd = hsm_store_open(h.device, h.inode, O_RDONLY);
+       if (fd == -1) {
+               printf("Failed to open store file for file 0x%llx:0x%llx\n",
+                      (unsigned long long)h.device, (unsigned long long)h.inode);
+               goto done;
+       }
+
+       printf("Recalling file %llx:%llx of size %d\n", 
+              (unsigned long long)h.device, (unsigned long long)h.inode,
+              (int)h.size);
+
+       ofs = 0;
+       while ((ret = read(fd, buf, sizeof(buf))) > 0) {
+               int ret2 = dm_write_invis(dmapi.sid, hanp, hlen, token, DM_WRITE_SYNC, ofs, ret, buf);
+               if (ret2 != ret) {
+                       printf("dm_write_invis failed - %s\n", strerror(errno));
+                       goto done;
+               }
+               ofs += ret;
+       }
+       close(fd);
+
+       ret = dm_remove_dmattr(dmapi.sid, hanp, hlen, token, 0, &attrname);
+       if (ret != 0) {
+               printf("dm_remove_dmattr failed - %s\n", strerror(errno));
+               goto done;
+       }
+
+       ret = hsm_store_unlink(h.device, h.inode);
+       if (ret != 0) {
+               printf("Failed to unlink store file\n");
+               goto done;
+       }
+
+       ret = dm_set_region(dmapi.sid, hanp, hlen, token, 0, NULL, &exactFlag);
+       if (ret == -1) {
+               printf("failed dm_set_region - %s\n", strerror(errno));
+               exit(1);
+       }
+
+done:
+       if (have_right) {
+               ret = dm_release_right(dmapi.sid, hanp, hlen, token);
+               if (ret == -1) {
+                       printf("failed dm_release_right on %s\n", strerror(errno));
+               }
+       }
+
+       ret = dm_respond_event(dmapi.sid, msg->ev_token, 
+                              DM_RESP_CONTINUE, retcode, 0, NULL);
+       if (ret != 0) {
+               printf("Failed to respond to read event\n");
+               exit(1);
+       }
+}
+
+
+static void hsm_handle_destroy(dm_eventmsg_t *msg)
+{
+       dm_destroy_event_t *ev;
+       void *hanp;
+       size_t hlen, rlen;
+       int ret;
+       dm_attrname_t attrname;
+       dm_token_t token = msg->ev_token;
+       struct hsm_attr h;
+       int retcode = -1;
+       dm_boolean_t exactFlag;
+
+        ev = DM_GET_VALUE(msg, ev_data, dm_destroy_event_t *);
+        hanp = DM_GET_VALUE(ev, ds_handle, void *);
+        hlen = DM_GET_LEN(ev, ds_handle);
+
+       if (DM_TOKEN_EQ(token, DM_INVALID_TOKEN)) {
+               goto done;
+       }
+
+        memset(attrname.an_chars, 0, DM_ATTR_NAME_SIZE);
+        strncpy((char*)attrname.an_chars, HSM_ATTRNAME, DM_ATTR_NAME_SIZE);
+
+       ret = dm_get_dmattr(dmapi.sid, hanp, hlen, token, &attrname, 
+                           sizeof(h), &h, &rlen);
+       if (ret != 0) {
+               printf("dm_get_dmattr failed - %s\n", strerror(errno));
+               goto done;
+       }
+
+       if (rlen != sizeof(h)) {
+               printf("hsm_handle_read - bad attribute size %d\n", (int)rlen);
+               goto done;
+       }
+
+       if (strncmp(h.magic, HSM_MAGIC, sizeof(h.magic)) != 0) {
+               printf("Bad magic '%*.*s'\n", (int)sizeof(h.magic), (int)sizeof(h.magic), h.magic);
+               goto done;
+       }
+
+       ret = hsm_store_unlink(h.device, h.inode);
+       if (ret == -1) {
+               printf("Failed to unlink store file for file 0x%llx:0x%llx\n",
+                      (unsigned long long)h.device, (unsigned long long)h.inode);
+               goto done;
+       }
+
+       ret = hsm_store_unlink(h.device, h.inode);
+       if (ret != 0) {
+               printf("Failed to unlink store file\n");
+               goto done;
+       }
+
+       ret = dm_remove_dmattr(dmapi.sid, hanp, hlen, token, 0, &attrname);
+       if (ret != 0) {
+               printf("dm_remove_dmattr failed - %s\n", strerror(errno));
+               goto done;
+       }
+
+       ret = dm_set_region(dmapi.sid, hanp, hlen, token, 0, NULL, &exactFlag);
+       if (ret == -1) {
+               printf("failed dm_set_region - %s\n", strerror(errno));
+               exit(1);
+       }
+
+done:
+       if (!DM_TOKEN_EQ(msg->ev_token,DM_NO_TOKEN) &&
+           !DM_TOKEN_EQ(msg->ev_token, DM_INVALID_TOKEN)) {
+               ret = dm_respond_event(dmapi.sid, msg->ev_token, 
+                                      DM_RESP_CONTINUE, retcode, 0, NULL);
+               if (ret != 0) {
+                       printf("Failed to respond to destroy event\n");
+                       exit(1);
+               }
+       }
+}
+
+
+static void hsm_handle_message(dm_eventmsg_t *msg)
+{
+       printf("Got event %s from node 0x%x\n",
+              dmapi_event_string(msg->ev_type), msg->ev_nodeid);
+
+       switch (msg->ev_type) {
+       case DM_EVENT_MOUNT:
+               hsm_handle_mount(msg);
+               break;
+       case DM_EVENT_READ:
+       case DM_EVENT_WRITE:
+               hsm_handle_recall(msg);
+               break;
+       case DM_EVENT_DESTROY:
+               hsm_handle_destroy(msg);
+               break;
+       default:
+               if (!DM_TOKEN_EQ(msg->ev_token,DM_NO_TOKEN) &&
+                   !DM_TOKEN_EQ(msg->ev_token, DM_INVALID_TOKEN)) {
+                       printf("Giving default response\n");
+                       int ret = dm_respond_event(dmapi.sid, msg->ev_token, 
+                                              DM_RESP_CONTINUE, 0, 0, NULL);
+                       if (ret != 0) {
+                               printf("Failed to respond to mount event\n");
+                               exit(1);
+                       }
+               }
+               break;
+       }
+}
+
+static void hsm_cleanup_tokens(void)
+{
+       dm_token_t tok[10];
+       u_int n;
+       int ret, i;
+
+       while ((ret = dm_getall_tokens(dmapi.sid, 10, tok, &n)) == 0 && n > 0) {
+               printf("Cleaning up %u tokens\n", n);
+               for (i=0;i<n;i++) {
+                       dm_respond_event(dmapi.sid, tok[i], 
+                                        DM_RESP_CONTINUE, 0, 0, NULL);
+               }
+       }
+}
+
+static void hsm_wait_events(void)
+{
+       int ret;
+       char buf[0x10000];
+       size_t rlen;
+
+       printf("Waiting for events\n");
+       
+       while (1) {
+               dm_eventmsg_t *msg;
+               /* we don't use DM_RR_WAIT to ensure that the daemon can be killed */
+               usleep(10000);
+               ret = dm_get_events(dmapi.sid, 0, 0, sizeof(buf), buf, &rlen);
+               if (ret < 0) {
+                       if (errno == EAGAIN) continue;
+                       printf("Failed to get event (%s)\n", strerror(errno));
+                       exit(1);
+               }
+               
+               for (msg=(dm_eventmsg_t *)buf; msg; msg = DM_STEP_TO_NEXT(msg, dm_eventmsg_t *)) {
+                       hsm_handle_message(msg);
+               }
+       }
+}
+
+static void usage(void)
+{
+       printf("Usage: hacksmd PATH\n");
+       exit(0);
+}
+
+int main(int argc, char * const argv[])
+{
+       int opt;
+       char *fspath;   
+       /* parse command-line options */
+       while ((opt = getopt(argc, argv, "h")) != -1) {
+               switch (opt) {
+               case 'h':
+               default:
+                       usage();
+                       break;
+               }
+       }
+
+       setlinebuf(stdout);     
+
+       argv += optind;
+       argc -= optind;
+
+       if (argc == 0) {
+               usage();
+       }
+
+       fspath = argv[0];
+
+       signal(SIGTERM, hsm_term_handler);
+       signal(SIGINT, hsm_term_handler);
+
+       hsm_init(fspath);
+
+       hsm_cleanup_tokens();
+
+       hsm_wait_events();
+
+       return 0;
+}