# HG changeset patch # User Josef 'Jeff' Sipek # Date 1206662632 14400 # Node ID cd351af3a8d2c65dd35f66474fa5dbcabdf85ffc import source diff -r 000000000000 -r cd351af3a8d2 .hgignore --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.hgignore Thu Mar 27 20:03:52 2008 -0400 @@ -0,0 +1,7 @@ +^botbackup$ +^testgmail$ +^tmp$ +\.o$ +^test_base85$ +^split$ +^migout$ diff -r 000000000000 -r cd351af3a8d2 Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Makefile Thu Mar 27 20:03:52 2008 -0400 @@ -0,0 +1,17 @@ +CC=gcc +CFLAGS=-Wall -g -D_FILE_OFFSET_BITS=64 + +CFLAGS_LINK=-ldm + +BINS=migout + +all: $(BINS) + +clean: + rm -rf *.o $(BINS) + +migout: migout.o die.o common.o split.o base85.o + $(CC) $(CFLAGS) $(CFLAGS_LINK) -o $@ $^ + +%.o: %.c + $(CC) $(CFLAGS) -c -o $@ $< diff -r 000000000000 -r cd351af3a8d2 base85.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/base85.c Thu Mar 27 20:03:52 2008 -0400 @@ -0,0 +1,33 @@ +#include "types.h" +#include "base85.h" + +int base85_encode(u8 *in, u8 *out, int inlen) +{ + u32 t; + u8 *src = in; + u8 *dst = out; + + while (in + inlen > src) { + t = ((u32) src[0] << 24) | + ((u32) src[1] << 16) | + ((u32) src[2] << 8) | + ((u32) src[3]); + src += 4; + + /* compression of all zero group */ + if (!t) { + *dst = 'z'; + dst++; + continue; + } + + dst[4] = (t % 85) + 33; t /= 85; + dst[3] = (t % 85) + 33; t /= 85; + dst[2] = (t % 85) + 33; t /= 85; + dst[1] = (t % 85) + 33; t /= 85; + dst[0] = t + 33; + dst += 5; + } + + return (int) (dst - out); +} diff -r 000000000000 -r cd351af3a8d2 base85.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/base85.h Thu Mar 27 20:03:52 2008 -0400 @@ -0,0 +1,8 @@ +#ifndef __BASE85_H +#define __BASE85_H + +#include "types.h" + +extern int base85_encode(u8 *in, u8 *out, int inlen); + +#endif diff -r 000000000000 -r cd351af3a8d2 common.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/common.c Thu Mar 27 20:03:52 2008 -0400 @@ -0,0 +1,293 @@ +#include + +#include "types.h" +#include "die.h" +#include "common.h" + +#include "settings.h" + +#define SMTP_MSG "SMTP DMAPI session" + +/* borrowed from xfstest repo */ +static int session_compare(const void *a, const void *b) +{ + return(*((dm_sessid_t *)a) - *((dm_sessid_t *)b)); +} + +/* borrowed from xfstest repo */ +static void find_session(dm_sessid_t *session) +{ + char buffer[DM_SESSION_INFO_LEN]; + dm_sessid_t *sidbuf = NULL; + dm_sessid_t new_session; + u_int allocelem = 0; + u_int nelem; + size_t rlen; + int error; + u_int i; + + /* Retrieve the list of all active sessions on the host. */ + + nelem = 100; /* a reasonable first guess */ + do { + if (allocelem < nelem) { + allocelem = nelem; + sidbuf = realloc(sidbuf, nelem * sizeof(*sidbuf)); + if (sidbuf == NULL) { + fprintf(stderr, "realloc of %d bytes failed\n", + nelem * sizeof(*sidbuf)); + exit(1); + } + } + error = dm_getall_sessions(allocelem, sidbuf, &nelem); + } while (error < 0 && errno == E2BIG); + + /* If an error occurred, translate it into something meaningful. */ + + if (error < 0) { + fprintf(stderr, "unexpected dm_getall_sessions failure, %s\n", + strerror(errno)); + free(sidbuf); + exit(1); + } + + /* We have the list of all active sessions. Scan the list looking + for an existing "test" session that we can use. The list must + first be sorted in case other processes happen to be creating test + sessions at the same time; we need to make sure that we pick the one + with the lowest session ID. + */ + + qsort(sidbuf, nelem, sizeof(sidbuf[0]), session_compare); + + for (i = 0; i < nelem; i++) { + error = dm_query_session(sidbuf[i], sizeof(buffer), + buffer, &rlen); + if (error < 0) { + fprintf(stderr, "unexpected dm_query_session " + "failure, %s\n", strerror(errno)); + free(sidbuf); + exit(1); + } + + if (!strncmp(buffer, SMTP_MSG, sizeof(SMTP_MSG))) + break; + } + if (i < nelem) { + *session = (dm_sessid_t)sidbuf[i]; + free(sidbuf); + return; + } + + /* No test session exists, so we have to create one ourselves. */ + + if (dm_create_session(DM_NO_SESSION, SMTP_MSG, &new_session) != 0) { + fprintf(stderr, "dm_create_session failed, %s\n", + strerror(errno)); + free(sidbuf); + exit(1); + } + + /* Now re-retrieve the list of active sessions. */ + + do { + if (allocelem < nelem) { + allocelem = nelem; + sidbuf = realloc(sidbuf, nelem * sizeof(*sidbuf)); + if (sidbuf == NULL) { + fprintf(stderr, "realloc of %d bytes failed\n", + nelem * sizeof(*sidbuf)); + exit(1); + } + } + error = dm_getall_sessions(allocelem, sidbuf, &nelem); + } while (error < 0 && errno == E2BIG); + + if (error < 0) { + fprintf(stderr, "dm_getall_sessions failed, %s\n", + strerror(errno)); + free(sidbuf); + exit(1); + } + + /* Sort the session ID list into ascending ID order, then find the + test session with the lowest session ID. We better find at + least one since we created one! + */ + + qsort(sidbuf, nelem, sizeof(sidbuf[0]), session_compare); + + for (i = 0; i < nelem; i++) { + error = dm_query_session(sidbuf[i], sizeof(buffer), + buffer, &rlen); + if (error < 0) { + fprintf(stderr, "dm_query_session failed, %s\n", + strerror(errno)); + free(sidbuf); + exit(1); + } + if (!strncmp(buffer, SMTP_MSG, sizeof(SMTP_MSG))) + break; + } + if (i == nelem) { + fprintf(stderr, "can't find the session we created\n"); + free(sidbuf); + exit(1); + } + *session = (dm_sessid_t)sidbuf[i]; + free(sidbuf); + + /* If the session we are going to use is not the one we created, + then we need to get rid of the created one. + */ + + if (*session != new_session) { + if ((new_session) != 0) { + fprintf(stderr, "dm_destroy_session failed, %s\n", + strerror(errno)); + exit(1); + } + } +} + +int setup_dmapi(dm_sessid_t *sid) +{ + char *c; + + if (dm_init_service(&c) == -1) { + warn("cannot init dmapi"); + return 1; + } + + if (strcmp(c, DM_VER_STR_CONTENTS)) { + warn("wrong version of dmapi"); + return 1; + } + + find_session(sid); + return 0; +} + +int get_dmchange(dm_sessid_t sid, u8 *handle, size_t hlen, dm_token_t token, unsigned int *change) +{ + int err; + dm_stat_t stat; + + err = dm_get_fileattr(sid, handle, hlen, token, DM_AT_CFLAG, &stat); + if (err == -1) { + warn("could not get fileattr"); + return 1; + } + + *change = stat.dt_change; + return 0; +} + +int lock_file(dm_sessid_t sid, u8 *handle, size_t hlen, int right, dm_token_t *token) +{ + int err; + + err = dm_create_userevent(sid, 0, NULL, token); + if (err == -1) { + warn("cannot create user event for session"); + return 1; + } + + err = dm_request_right(sid, handle, hlen, *token, DM_RR_WAIT, right); + if (err == -1) { + warn("cannot obtain requested right"); + return 1; + } + + return 0; +} + +void unlock_file(dm_sessid_t sid, dm_token_t token) +{ + int err; + + err = dm_respond_event(sid, token, DM_RESP_CONTINUE, 0, 0, 0); + if (err == -1) + warn("cannot respond to event/release token"); +} + +int save_dataloc(dm_sessid_t sid, u8 *handle, ssize_t hlen, dm_token_t token) +{ + dm_attrname_t attrname; + int err; + + memcpy(&attrname.an_chars[0], "dhanloc", 8); + err = dm_set_dmattr(sid, handle, hlen, token, &attrname, 0, hlen, handle); + if (err) { + warn("can't set data location"); + return 1; + } + + memcpy(&attrname.an_chars[0], "dhanlen", 8); + err = dm_set_dmattr(sid, handle, hlen, token, &attrname, 0, sizeof(hlen), (void*)&hlen); + if (err) { + warn("can't set data location length"); + return 1; + } + + return 0; +} + +int set_man_regions(dm_sessid_t sid, u8 *handle, size_t hlen, dm_token_t token, dm_size_t fsize, dm_off_t *off) +{ + dm_off_t rroff = 0; + dm_size_t rlen; + dm_region_t reg; + unsigned int exact; + int err; + + if (fsize > FENCEPOST_SIZE) { + err = dm_probe_hole(sid, handle, hlen, token, FENCEPOST_SIZE, 0, &rroff, &rlen); + if (err) { + warn("could not probe hole in file"); + return 1; + } + } + + *off = rroff; + + reg.rg_offset = rroff; + reg.rg_size = 0; + reg.rg_flags = DM_REGION_READ | DM_REGION_WRITE | DM_REGION_TRUNCATE; + + err = dm_set_region(sid, handle, hlen, token, 1, ®, &exact); + if (err) { + warn("could not set managed region"); + return 1; + } + + return 0; +} + +int make_nonres(dm_sessid_t sid, u8 *handle, size_t hlen, dm_token_t token, dm_off_t off) +{ + int err; + + err = dm_punch_hole(sid, handle, hlen, token, off, 0); + if (err) { + warn("could not punch hole in file"); + return 1; + } + + return 0; +} + +void clear_man_regions(dm_sessid_t sid, u8 *handle, size_t hlen, dm_token_t token) +{ + dm_region_t reg; + unsigned int exact; + int err; + + reg.rg_offset = 0; + reg.rg_size = 0; + reg.rg_flags = DM_REGION_NOEVENT; + + err = dm_set_region(sid, handle, hlen, token, 1, ®, &exact); + if (err) + warn("could not clear managed regions from file"); +} diff -r 000000000000 -r cd351af3a8d2 common.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/common.h Thu Mar 27 20:03:52 2008 -0400 @@ -0,0 +1,17 @@ +#ifndef __COMMON_H +#define __COMMON_H + +#include + +#include "types.h" + +extern int setup_dmapi(dm_sessid_t *sid); +extern int get_dmchange(dm_sessid_t sid, u8 *handle, size_t hlen, dm_token_t token, unsigned int *change); +extern int lock_file(dm_sessid_t sid, u8 *handle, size_t hlen, int flag, dm_token_t *token); +extern void unlock_file(dm_sessid_t sid, dm_token_t token); +extern int save_dataloc(dm_sessid_t sid, u8 *handle, ssize_t hlen, dm_token_t token); +extern int set_man_regions(dm_sessid_t sid, u8 *handle, size_t hlen, dm_token_t token, dm_size_t fsize, dm_off_t *off); +extern int make_nonres(dm_sessid_t sid, u8 *handle, size_t hlen, dm_token_t token, dm_off_t off); +extern void clear_man_regions(dm_sessid_t sid, u8 *handle, size_t hlen, dm_token_t token); + +#endif diff -r 000000000000 -r cd351af3a8d2 die.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/die.c Thu Mar 27 20:03:52 2008 -0400 @@ -0,0 +1,15 @@ +#include +#include + +#include "die.h" + +void do_warn(char *msg, int line, char *file) +{ + fprintf(stderr, "fatal: %s:%d:%s\n", file, line, msg); +} + +void do_die(char *msg, int line, char *file) +{ + do_warn(msg, line, file); + exit(1); +} diff -r 000000000000 -r cd351af3a8d2 die.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/die.h Thu Mar 27 20:03:52 2008 -0400 @@ -0,0 +1,10 @@ +#ifndef __DIE_H +#define __DIE_H + +#define die(msg) do_die(msg, __LINE__, __FILE__) +#define warn(msg) do_warn(msg, __LINE__, __FILE__) + +extern void do_die(char *msg, int line, char *file); +extern void do_warn(char *msg, int line, char *file); + +#endif diff -r 000000000000 -r cd351af3a8d2 fake-sendmail --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fake-sendmail Thu Mar 27 20:03:52 2008 -0400 @@ -0,0 +1,3 @@ +#!/bin/bash + +cat > /tmp/mbox.$$ diff -r 000000000000 -r cd351af3a8d2 migout.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/migout.c Thu Mar 27 20:03:52 2008 -0400 @@ -0,0 +1,138 @@ +#include +#include + +#include + +#include "types.h" +#include "die.h" +#include "common.h" +#include "split.h" + +#include "settings.h" + +#define IBUFSIZE 1024 + +static inline u8 hex2bin(char c) +{ + if (c > '9') + return c - 'a' + 10; + return c - '0'; +} + +int extract_fields(char *buf, u8 *handle, size_t *hlen, dm_size_t *fsize) +{ + size_t off; + + *hlen = atoi(buf); + + if (*hlen > HANDLE_MAX_SIZE) { + warn("handle length over maximum"); + return 1; + } + + while(*buf++ != '\t') + ; + + for(off=0; off < *hlen; off++, buf+=2) { + *handle = hex2bin(buf[0]) << 4 | + hex2bin(buf[1]); + handle++; + } + + buf +=2; /* skip space & tab */ + + *fsize = atoi(buf); + + return 0; +} + +int mig_files(dm_sessid_t sid) +{ + size_t hlen; + dm_size_t fsize; + dm_off_t off; + dm_token_t token; + int err; + char ibuf[IBUFSIZE]; + u8 handle[HANDLE_MAX_SIZE]; + unsigned int change_start, change_end; + + while(fgets(ibuf, IBUFSIZE, stdin)) { + err = extract_fields(ibuf, handle, &hlen, &fsize); + if (err) { + warn("mangled line"); + continue; + } + + fprintf(stderr, "> handle (len=%d, size=%llu)\n", hlen, fsize); + + err = get_dmchange(sid, handle, hlen, DM_NO_TOKEN, &change_start); + if (err) { + warn("failed to get the dmapi change indicator"); + continue; + } + + err = save_filedata(sid, handle, hlen, fsize); + if (err) { + warn("could not save file data"); + continue; + } + + err = lock_file(sid, handle, hlen, DM_RIGHT_EXCL, &token); + if (err) { + warn("could not get exclusive file lock"); + continue; + } + + err = get_dmchange(sid, handle, hlen, token, &change_end); + if (err) { + unlock_file(sid, token); + warn("failed to get the dmapi change indicator"); + continue; + } + if (change_start != change_end) { + unlock_file(sid, token); + warn("file changed during stageout, ignoring"); + continue; + } + + err = save_dataloc(sid, handle, hlen, token); + if (err) { + unlock_file(sid, token); + warn("could not save data location"); + continue; + } + + err = set_man_regions(sid, handle, hlen, token, fsize, &off); + if (err) { + unlock_file(sid, token); + warn("could not set managed regions"); + continue; + } + + err = make_nonres(sid, handle, hlen, token, off); + if (err) + clear_man_regions(sid, handle, hlen, token); + + unlock_file(sid, token); + } + + return 0; +} + +int main(int argc, char **argv) +{ + dm_sessid_t sid; + int err; + + if (setup_dmapi(&sid)) + die("Cannot initialize dmapi session"); + + err = mig_files(sid); + + if (dm_destroy_session(sid)) + warn("Cannot shutdown dmapi session"); + + return err; +} + diff -r 000000000000 -r cd351af3a8d2 settings.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/settings.h Thu Mar 27 20:03:52 2008 -0400 @@ -0,0 +1,15 @@ +#define IMAP_USER "storage@example.com" +#define IMAP_SERVER "imap.example.com" + +#define SMTP_USER "storage@example.com" +#define SMTP_MAX_FILESIZE (10 * 1024 * 1024) + +#define FROM_ADDRESS "storage@example.com" + +#define SENDMAIL_PATH "/usr/sbin/sendmail" +//#define SENDMAIL_PATH "./fake-sendmail" + +#define FENCEPOST_SIZE 8192 + +#define HANDLE_MAX_SIZE 64 + diff -r 000000000000 -r cd351af3a8d2 split.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/split.c Thu Mar 27 20:03:52 2008 -0400 @@ -0,0 +1,116 @@ +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include "settings.h" + +#include "base85.h" +#include "split.h" +#include "die.h" + +#define INFILE "infile" +#define CHUNKSIZE (SMTP_MAX_FILESIZE * 4 / 5) + +int do_dm_read_invis(dm_sessid_t sid, u8 *handle, size_t hlen, char *buf, int len, dm_off_t start) +{ + int ret; + int off = 0; + + do { + ret = dm_read_invis(sid, handle, hlen, DM_NO_TOKEN, start + off, len - off, buf + off); + if (ret > 0) + off += ret; + if (ret <= 0) + break; + } while(off < len); + + return (ret < 0 ? ret : off); +} + +void dump_buffer(FILE *sendmail, char *buf, char *obuf, int len) +{ + int i; + + len = base85_encode((u8*)buf, (u8*)obuf, len); + + for(i = 0; i < len; ) { + fprintf(sendmail, "%c", obuf[i++]); + if (i % 76 == 0) + fprintf(sendmail, "\n"); + } + fprintf(sendmail, "\n\n"); +} + +static inline char bin2hex(u8 u) +{ + if (u >= 10) + return u - 10 + 'a'; + return u + '0'; +} + +static void printable_handle(u8 *handle, size_t hlen, char *ascii) +{ + size_t i; + + for(i=0; i> 4); + ascii[i*2+1] = bin2hex(handle[i] & 0xf); + } + + ascii[i*2] = '\0'; +} + +int save_filedata(dm_sessid_t sid, u8 *handle, size_t hlen, dm_size_t fsize) +{ + FILE *sendmail; + int mbox; + char *buf, *obuf; + char asciihandle[HANDLE_MAX_SIZE]; + int len; + + buf = malloc(CHUNKSIZE); + obuf = malloc(CHUNKSIZE * 5 / 4); + if (!buf || !obuf) { + warn("could not allocate buffers"); + return 1; + } + + printable_handle(handle, hlen, asciihandle); + + for(mbox=0; ; mbox++) { + /* + * Read first to break out of the loop in case we're done + */ + len = do_dm_read_invis(sid, handle, hlen, buf, CHUNKSIZE, CHUNKSIZE * mbox); + if (len <= 0) + break; + + sendmail = popen(SENDMAIL_PATH " -t \"" SMTP_USER "\"", "w"); + if (!sendmail) { + warn("could not spawn sendmail"); + return 1; + } + + fprintf(sendmail, "From " FROM_ADDRESS " / %s\n", asciihandle); + fprintf(sendmail, "From: " FROM_ADDRESS "\n"); + fprintf(sendmail, "To: " SMTP_USER "\n"); + fprintf(sendmail, "Subject: %s, part %d\n", asciihandle, mbox); + fprintf(sendmail, "X-DM-Handle: %s\n", asciihandle); + fprintf(sendmail, "X-DM-Part: %d\n", mbox); + fprintf(sendmail, "X-DM-Part-Length: %d\n", len); + fprintf(sendmail, "\n"); + + dump_buffer(sendmail, buf, obuf, len); + fclose(sendmail); + } + + return 0; +} + diff -r 000000000000 -r cd351af3a8d2 split.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/split.h Thu Mar 27 20:03:52 2008 -0400 @@ -0,0 +1,10 @@ +#ifndef __SPLIT_H +#define __SPLIT_H + +#include + +#include "types.h" + +extern int save_filedata(dm_sessid_t sid, u8 *handle, size_t hlen, dm_size_t fsize); + +#endif diff -r 000000000000 -r cd351af3a8d2 test_migout --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test_migout Thu Mar 27 20:03:52 2008 -0400 @@ -0,0 +1,31 @@ +#!/bin/bash + +DEV=/dev/hda6 +MNT=/mnt/xfs_scratch +S1=/home/jeffpc/xfs-cmds/xfstests/dmapi/src/suite1/cmd +SHSM=/home/jeffpc/xfs-cmds/xfstests/dmapi/src/sample_hsm +MYHSM=/home/jeffpc/myprogs/xfs_smtp_hsm + +set -x +umount $MNT +mkfs.xfs -f $DEV +mount $DEV $MNT -o dmapi,mtpt=$MNT + +cp /tmp/t $MNT/large + +fshandle=`$S1/path_to_fshandle $MNT` +$S1/get_config_events $fshandle + +while true ; do + $SHSM/migfind -s 5k $MNT > /tmp/dmapi-list + [ -s /tmp/dmapi-list ] && break + sleep 45 +done + +cat /tmp/dmapi-list + +read n + +$MYHSM/migout < /tmp/dmapi-list + + diff -r 000000000000 -r cd351af3a8d2 types.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/types.h Thu Mar 27 20:03:52 2008 -0400 @@ -0,0 +1,10 @@ +#ifndef __TYPES_H +#define __TYPES_H + +#include +#include + +typedef uint8_t u8; +typedef uint32_t u32; + +#endif