/*
 * Copyright (c) 2010, Gaetan Bisson. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
 * NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * StirFS: the secure, transparent and irresistible filesystem.
 *
 * StirFS is an encrypted filesystem for FUSE on Linux; it is designed with
 * minimalism, flexibility and security in mind. Minimalism should be obvious
 * at the view of this single, small C file; flexibility means StirFS just
 * encrypts filenames and blocks, and relies on a host filesystem for the rest;
 * security stems from strong cryptographic primitives and modes of operation.
 */

/*
 * For a basic compilation, run:
 *
 *    cc -O2 `pkg-config --cflags --libs fuse openssl` -o stirfs stirfs.c
 *
 * How advanced compilation should be performed (compiler options, linker
 * options, binary stripping, etc.) is entirely up to the packager.
 */

/* 
 * Overview of the design:
 *
 * An AES128 master key is derived from the input password by computing the
 * SHA256 digest of its concatenation with a fixed salt value.
 *
 * Filenames are encrypted as follows: first pad with zeroes to ensure the
 * length is a multiple of 128, then encrypt by AES128 in CBC mode, then xor
 * the last block into the first one and encrypt again (to ensure two-way
 * diffusion), and finally base64-encode.
 *
 * Each file's first 128 bits are a nonce; following blocks consist of actual
 * data (with offsets shifted) encrypted by AES128 in (modified) CTR mode.
 */


/* ****************************************************************************
 *
 * HEADERS
 *
 */


#include <dirent.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/xattr.h>
#include <time.h>

#define __USE_UNIX98
#include <unistd.h>

#define FUSE_USE_VERSION 26
#include <fuse.h>
#ifndef PATH_MAX
#define PATH_MAX 4096
#endif

// PATH_MAX could be superseded by dynamic allocation, since FUSE now supports
// arbitrarily-long pathnames, but it certainly wouldn't be as efficient.

#include <openssl/aes.h>
#include <openssl/sha.h>


/* ****************************************************************************
 *
 * STIRFS INTERNAL STRUCTURES
 *
 * Our FUSE context consists of the rootdir, encryption and decryption keys.
 * Our FUSE file handlers consist of a backend file descriptor and file nonce.
 * 
 */


struct stir_ctx {
	char *root;
	AES_KEY *ekey;
	AES_KEY *dkey;
};

#define CTX ((struct stir_ctx*) fuse_get_context()->private_data)

struct stir_fh {
	uintptr_t fd;
	unsigned char *nonce;
};

#define FH ((struct stir_fh *) (uintptr_t) fi->fh)
#define MKFH \
	struct stir_fh *fh = malloc(sizeof(struct stir_fh)); \
	fi->fh = (uintptr_t) fh


/* ****************************************************************************
 *
 * STIRFS INTERNAL PRIMITIVES
 *
 */


// Functions here are static and we inline them all but stir_root_cpath, since
// it is called from almost all FUSE primitives.

inline static void stir_encode (char *res, const unsigned char *buf, int n) {
	char c[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789,-";
	int i,j,x;
	for (i=0,j=0; i<n; i+=3,j+=4) {
		x = buf[i]<<16;
		if (i+1<n) x += buf[i+1]<<8;
		if (i+2<n) x += buf[i+2];
		res[j] = c[(x>>18)&63];
		res[j+1] = c[(x>>12)&63];
		res[j+2] = c[(x>>6)&63];
		res[j+3] = c[x&63];
		if (i+2>n) res[j+2] = '+';
		if (i+3>n) res[j+3] = '+';
	}
	res[j] = '\0';
}

inline static void stir_decode (unsigned char *res, int *n, const char *buf) {
	int d (char c) { return c>96 ? c-97 : c>64 ? c-39 : c>47 ? c+4 : c>43 ? c+18 : 0; }
	int m = strlen(buf);
	*n = buf[m-2]=='+' ? 3*m/4-2 : buf[m-1]=='+' ? 3*m/4-1 : 3*m/4;
	int i,j,x;
	for (i=0,j=0; j<m; i+=3,j+=4) {
		x = d(buf[j])<<18;
		x += d(buf[j+1])<<12;
		x += d(buf[j+2])<<6;
		x += d(buf[j+3]);
		res[i] = (x>>16)&255;
		if (i+1<*n) res[i+1] = (x>>8)&255;
		if (i+2<*n) res[i+2] = x&255;
	}
}

inline static int stir_cbc_encrypt (unsigned char *cbuf, unsigned char *buf, AES_KEY *key, int l) {
	unsigned char *iv = NULL;
	int n=0, i;
	while (l>n) {
		memcpy(cbuf+n, buf+n, AES_BLOCK_SIZE);
		if (l<n+AES_BLOCK_SIZE) memset(cbuf+l, 0, AES_BLOCK_SIZE-l%AES_BLOCK_SIZE);
		if (iv) for (i=0; i<AES_BLOCK_SIZE; i++) cbuf[n+i] ^= iv[i];
		AES_encrypt(cbuf+n, cbuf+n, key);
		iv = cbuf+n;
		n += AES_BLOCK_SIZE;
	}
	return n;
}

inline static void stir_cbc_decrypt (unsigned char *buf, unsigned char *cbuf, AES_KEY *key, int l) {
	unsigned char *iv = NULL;
	int n=0, i;
	while (l>n) {
		AES_decrypt(cbuf+n, buf+n, key);
		if (iv) for (i=0; i<AES_BLOCK_SIZE; i++) buf[n+i] ^= iv[i];
		iv = cbuf+n;
		n += AES_BLOCK_SIZE;
	}
}

// strncpy/strncat are impractical and irrelevant here from a security
// standpoint; from a stability standpoint, if the user wants very long paths,
// let them increase PATH_MAX.

inline static void stir_cname (char *cname, const char *name) {
	if (!strcmp(name,"") || !strcmp(name,".") || !strcmp(name,"..")) {
		strcpy(cname, name);
		return;
	}
	int i,n;
	unsigned char xname[PATH_MAX], yname[PATH_MAX];
	n = stir_cbc_encrypt(xname, (unsigned char *)name, CTX->ekey, strlen(name)+1);
	if (n>AES_BLOCK_SIZE) for (i=0; i<AES_BLOCK_SIZE; i++) xname[i] ^= xname[n-AES_BLOCK_SIZE+i];
	stir_cbc_encrypt(yname, xname, CTX->ekey, n);
	stir_encode(cname, yname, n);
}

inline static void stir_name (char *name, const char *cname) {
	if (!strcmp(cname,"") || !strcmp(cname,".") || !strcmp(cname,"..")) {
		strcpy(name, cname);
		return;
	}
	int i,n;
	unsigned char xname[PATH_MAX], yname[PATH_MAX];
	stir_decode(yname, &n, cname);
	stir_cbc_decrypt(xname, yname, CTX->dkey, n);
	if (n>AES_BLOCK_SIZE) for (i=0; i<AES_BLOCK_SIZE; i++) xname[i] ^= xname[n-AES_BLOCK_SIZE+i];
	stir_cbc_decrypt((unsigned char *)name, xname, CTX->dkey, n);
}

inline static void stir_cpath (char *cpath, const char *path) {
	char rpath[PATH_MAX];
	strcpy(rpath, path);
	strcpy(cpath, "");
	char *tok, *rem=rpath;
	while (tok=strsep(&rem,"/")) {
		stir_cname(cpath+strlen(cpath), tok);
		strcat(cpath, "/");
	}
	cpath[strlen(cpath)-1]='\0';
}

inline static void stir_path (char *path, const char *cpath) {
	char rpath[PATH_MAX];
	strcpy(rpath, cpath);
	strcpy(path, "");
	char *tok, *rem=rpath;
	while (tok=strsep(&rem,"/")) {
		stir_name(path+strlen(path), tok);
		strcat(path, "/");
	}
	path[strlen(path)-1]='\0';
}

static void stir_root_cpath (char *rpath, const char *path) {
	strcpy(rpath, CTX->root);
	strcat(rpath, "/");
	stir_cpath(rpath+strlen(rpath), path);
}

// CTR has the weakness that by xoring two encrypted versions of the same file
// (i.e. same nonce) one retrieves the xor of the two clear-text versions.
//
// To address that, we stir a little bit more the bytes by consecutively xoring
// and adding them with bytes of the AES mask; this yields a good compromise
// between security (at least as secure as CTR, probably more) and convenience
// (writing full AES blocks would make implementing write() quite involved).

inline static void stir_cblock (unsigned char *cbuf, const unsigned char *buf,
size_t count, off_t off, AES_KEY *key, unsigned char *nonce) {
	unsigned int rp=off, bp=0, i;
	unsigned char mask[AES_BLOCK_SIZE];
	unsigned char x;
	while (bp<count) {
		if (!(rp%AES_BLOCK_SIZE) || !bp) {
			memcpy(mask, nonce, AES_BLOCK_SIZE);
			mask[0] += rp/AES_BLOCK_SIZE;
			AES_encrypt(mask, mask, key);
		}
		x = buf[bp];
		for (i=2; i<=8; i+=2) {
			x ^= mask[(rp+i)%AES_BLOCK_SIZE];
			x += mask[(rp+i+1)%AES_BLOCK_SIZE];
		}
		cbuf[bp] = x;
		bp++;
		rp++;
	}
}

inline static void stir_block (unsigned char *buf, const unsigned char *cbuf,
size_t count, off_t off, AES_KEY *key, unsigned char *nonce) {
	unsigned int rp=off, bp=0, i;
	unsigned char mask[AES_BLOCK_SIZE];
	unsigned char x;
	while (bp<count) {
		if (!(rp%AES_BLOCK_SIZE) || !bp) {
			memcpy(mask, nonce, AES_BLOCK_SIZE);
			mask[0] += rp/AES_BLOCK_SIZE;
			AES_encrypt(mask, mask, key);
		}
		x = cbuf[bp];
		for (i=8; i>=2; i-=2) {
			x -= mask[(rp+i+1)%AES_BLOCK_SIZE];
			x ^= mask[(rp+i)%AES_BLOCK_SIZE];
		}
		buf[bp] = x;
		bp++;
		rp++;
	}
}


/* ****************************************************************************
 *
 * SITRFS FUSE PRIMITIVES
 *
 * cf. http://fuse.sourceforge.net/doxygen/structfuse__operations.html
 *
 */


// Get file system statistics
int stir_statfs (const char *path, struct statvfs *stat) {
	char rpath[PATH_MAX];
	stir_root_cpath(rpath, path);
	int r = statvfs(rpath, stat);
	if (r) return -errno;
	return 0;
}

// Get attributes from an open file
int stir_fgetattr (const char *path, struct stat *buf, struct fuse_file_info *fi) {
	int r = fstat(FH->fd, buf);
	if (r) return -errno;
	if (S_ISREG(buf->st_mode)) buf->st_size -= AES_BLOCK_SIZE;
	return 0;
}

// Get file attributes
int stir_getattr (const char *path, struct stat *buf) {
	char rpath[PATH_MAX];
	stir_root_cpath(rpath, path);
	int r = lstat(rpath, buf);
	if (r) return -errno;
	if (S_ISREG(buf->st_mode)) buf->st_size -= AES_BLOCK_SIZE;
	return 0;
}

// Change the size of an open file
int stir_ftruncate (const char *path, off_t off, struct fuse_file_info *fi) {
	int r = ftruncate(FH->fd, off+AES_BLOCK_SIZE);
	if (r) return -errno;
	return 0;
}

// Change the size of a file
int stir_truncate (const char *path, off_t off) {
	char rpath[PATH_MAX];
	stir_root_cpath(rpath, path);
	int r = truncate(rpath, off+AES_BLOCK_SIZE);
	if (r) return -errno;
	return 0;
}

// Check file access permissions
int stir_access (const char *path, int mode) {
	char rpath[PATH_MAX];
	stir_root_cpath(rpath, path);
	int r = access(rpath, mode);
	if (r) return -errno;
	return 0;
}

// Change the permission bits of a file
int stir_chmod (const char *path, mode_t mode) {
	char rpath[PATH_MAX];
	stir_root_cpath(rpath, path);
	int r = chmod(rpath, mode);
	if (r) return -errno;
	return 0;
}

// Change the owner and group of a file
int stir_chown (const char *path, uid_t uid, gid_t gid) {
	char rpath[PATH_MAX];
	stir_root_cpath(rpath, path);
	int r = chown(rpath, uid, gid);
	if (r) return -errno;
	return 0;
}

// Change the access and modification times of a file with nanosecond resolution
int stir_utimens (const char *path, const struct timespec times[2]) {
	char rpath[PATH_MAX];
	stir_root_cpath(rpath, path);
	int r = utimensat(-1, rpath, times, 0);
	if (r) return -errno;
	return 0;
}

// Set extended attributes
int stir_setxattr (const char *path, const char *name, const char *value, size_t size, int flags) {
	char rpath[PATH_MAX];
	stir_root_cpath(rpath, path);
	int r = lsetxattr(rpath, name, value, size, flags);
	if (r) return -errno;
	return 0;
}

// Get extended attributes
int stir_getxattr (const char *path, const char *name, char *value, size_t size) {
	char rpath[PATH_MAX];
	stir_root_cpath(rpath, path);
	int r = lgetxattr(rpath, name, value, size);
	if (r<0) return -errno;
	return r;
}

// List extended attributes
int stir_listxattr (const char *path, char *list, size_t size) {
	char rpath[PATH_MAX];
	stir_root_cpath(rpath, path);
	int r = llistxattr(rpath, list, size);
	if (r<0) return -errno;
	return r;
}

// Remove extended attributes
int stir_removexattr (const char *path, const char *name) {
	char rpath[PATH_MAX];
	stir_root_cpath(rpath, path);
	int r = lremovexattr(rpath, name);
	if (r) return -errno;
	return 0;
}

// Read the target of a symbolic link
int stir_readlink (const char *head, char *path, size_t size) {
	char rhead[PATH_MAX], cpath[PATH_MAX];
	stir_root_cpath(rhead, head);
	int r = readlink(rhead, cpath, PATH_MAX);
	if (r<0) return -errno;
	stir_path(path, cpath);
	return 0;
}

// Create a symbolic link
int stir_symlink (const char *path, const char *head) {
	char cpath[PATH_MAX], rhead[PATH_MAX];
	stir_cpath(cpath, path);
	stir_root_cpath(rhead, head);
	int r = symlink(cpath, rhead);
	if (r) return -errno;
	return 0;
}

// Rename a file
int stir_rename (const char *path, const char *npath) {
	char rpath[PATH_MAX], rnpath[PATH_MAX];
	stir_root_cpath(rpath, path);
	stir_root_cpath(rnpath, npath);
	int r = rename(rpath, rnpath);
	if (r) return -errno;
	return 0;
}

// Create a hard link to a file
int stir_link (const char *path, const char *head) {
	char rpath[PATH_MAX], rhead[PATH_MAX];
	stir_root_cpath(rpath, path);
	stir_root_cpath(rhead, head);
	int r = link(rpath, rhead);
	if (r) return -errno;
	return 0;
}

// Create a directory
int stir_mkdir (const char *path, mode_t mode) {
	char rpath[PATH_MAX];
	stir_root_cpath(rpath, path);
	int r = mkdir(rpath, mode);
	if (r) return -errno;
	return 0;
}

// Remove a file
int stir_unlink (const char *path) {
	char rpath[PATH_MAX];
	stir_root_cpath(rpath, path);
	int r = unlink(rpath);
	if (r) return -errno;
	return 0;
}

// Remove a directory
int stir_rmdir (const char *path) {
	char rpath[PATH_MAX];
	stir_root_cpath(rpath, path);
	int r = rmdir(rpath);
	if (r) return -errno;
	return 0;
}

// Open directory
int stir_opendir (const char *path, struct fuse_file_info *fi) {
	char rpath[PATH_MAX];
	stir_root_cpath(rpath, path);
	MKFH;
	DIR *dp = opendir(rpath);
	FH->fd = (uintptr_t) dp;
	if (!dp) return -errno;
	return 0;
}

// Read directory
int stir_readdir (const char *path, void *buf, fuse_fill_dir_t fill, off_t off, struct fuse_file_info *fi) {
	char name[PATH_MAX];
	DIR *dp = (DIR *) FH->fd;
	struct dirent *de;
	struct stat st;
	seekdir(dp, off);
	while (de=readdir(dp)) {
		stir_name(name, de->d_name);
		memset(&st, 0, sizeof(st));
		st.st_ino = de->d_ino;
		st.st_mode = DTTOIF(de->d_type);
		if (fill(buf,name,&st,telldir(dp))) return -ENOMEM;
	}
	return 0;
}

// Release directory
int stir_releasedir (const char *path, struct fuse_file_info *fi) {
	int r = closedir((DIR *) FH->fd);
	if (r) return -errno;
	free(FH);
	return 0;
}

// Release an open file
int stir_release (const char *path, struct fuse_file_info *fi) {
	int r = close(FH->fd);
	if (r) return -errno;
	free(FH->nonce);
	free(FH);
	return 0;
}

// [FUSE operations structure reference] No creation (O_CREAT, O_EXCL) and by
// default also no truncation (O_TRUNC) flags will be passed to open().

// File open operation
int stir_open (const char *path, struct fuse_file_info *fi) {
	char rpath[PATH_MAX];
	stir_root_cpath(rpath, path);
	MKFH;
	if (fi->flags&O_WRONLY) fi->flags ^= O_WRONLY|O_RDWR;
	int fd = open(rpath,fi->flags);
	FH->fd = fd;
	if (fd<0) return -errno;
	unsigned char *nonce;
	struct stat stbuf;
	fstat(fd, &stbuf);
	if (S_ISREG(stbuf.st_mode)) {
		nonce = malloc(AES_BLOCK_SIZE);
		int r = pread(fd, nonce, AES_BLOCK_SIZE, 0);
		if (r<AES_BLOCK_SIZE) return -EIO;
	} else nonce = NULL;
	FH->nonce = nonce;
	return 0;
}

// Create and open a file
int stir_create (const char *path, mode_t mode, struct fuse_file_info *fi) {
	char rpath[PATH_MAX];
	stir_root_cpath(rpath, path);
	MKFH;
	int fd = open(rpath, fi->flags, mode);
	FH->fd = fd;
	if (fd<0) return -errno;
	unsigned char *nonce;
	if (S_ISREG(mode)) {
		nonce = malloc(SHA256_DIGEST_LENGTH);
		int rnd[] = { time(NULL), rand(), clock(), rand() };
		SHA256((unsigned char *)rnd, sizeof(rnd), nonce);
		int r = pwrite(fd, nonce, AES_BLOCK_SIZE, 0);
		if (r<AES_BLOCK_SIZE) return -EIO;
	} else nonce = NULL;
	FH->nonce = nonce;
	return 0;
}

// Create a file node
int stir_mknod (const char *path, mode_t mode, dev_t dev) {
	char rpath[PATH_MAX];
	stir_root_cpath(rpath, path);
	int r = mknod(rpath,mode,dev);
	if (r) return -errno;
	if (S_ISREG(mode)) {
		unsigned char nonce[SHA256_DIGEST_LENGTH];
		int rnd[] = { time(NULL), rand(), clock(), rand() };
		SHA256((unsigned char *)rnd, sizeof(rnd), nonce);
		int fr = open(rpath, O_WRONLY);
		int r = pwrite(fr, nonce, AES_BLOCK_SIZE, 0);
		if (r<AES_BLOCK_SIZE) return -EIO;
		close(fr);
	}
	return 0;
}

// Read data from an open file
int stir_read (const char *path, char *buf, size_t count, off_t off, struct fuse_file_info *fi) {
	unsigned char *cbuf = malloc(count);
	int r = pread(FH->fd, cbuf, count, off+AES_BLOCK_SIZE);
	if (r<0) return -errno;
	if (!FH->nonce) memcpy(buf, cbuf, count);
	else stir_block((unsigned char *)buf, cbuf, count, off, CTX->ekey, FH->nonce);
	free(cbuf);
	return r;
}

// Write data to an open file
int stir_write (const char *path, const char *buf, size_t count, off_t off, struct fuse_file_info *fi) {
	unsigned char *cbuf = malloc(count);
	if (!FH->nonce) memcpy(cbuf, buf, count);
	else stir_cblock(cbuf, (unsigned char *)buf, count, off, CTX->ekey, FH->nonce);
	int r = pwrite(FH->fd, cbuf, count, off+AES_BLOCK_SIZE);
	if (r<0) return -errno;
	free(cbuf);
	return r;
}

// Possibly flush cached data
int stir_flush (const char *path, struct fuse_file_info *fi) {
	return 0;
}

// Synchronize file contents
int stir_fsync (const char *path, int data, struct fuse_file_info *fi) {
	int r = data ? fdatasync(FH->fd) : fsync(FH->fd);
	if (r) return -errno;
	return 0;
}

// Synchronize directory contents
int stir_fsyncdir (const char *path, int data, struct fuse_file_info *fi) {
	return 0;
}

// Initialize filesystem
void *stir_init (struct fuse_conn_info *conn) {
	return CTX;
}

// Clean up filesystem
void stir_destroy (void *stuff) {
}


/* ****************************************************************************
 *
 * PUTTING IT ALL TOGETHER
 *
 */


// Operation structure to be fed to FUSE
struct fuse_operations stir_oper = {
	.access = stir_access,
//	.bmap = bmap,
	.chmod = stir_chmod,
	.chown = stir_chown,
	.create = stir_create,
	.destroy = stir_destroy,
	.fgetattr = stir_fgetattr,
//	.flag_nullpath_ok = flag_nullpath_ok,
	.flush = stir_flush,
	.fsync = stir_fsync,
	.fsyncdir = stir_fsyncdir,
	.ftruncate = stir_ftruncate,
	.getattr = stir_getattr,
	.getxattr = stir_getxattr,
	.init = stir_init,
//	.ioctl = ioctl,
	.link = stir_link,
	.listxattr = stir_listxattr,
//	.lock = lock,
	.mkdir = stir_mkdir,
	.mknod = stir_mknod,
	.open = stir_open,
	.opendir = stir_opendir,
//	.poll = poll,
	.read = stir_read,
	.readdir = stir_readdir,
	.readlink = stir_readlink,
	.release = stir_release,
	.releasedir = stir_releasedir,
	.removexattr = stir_removexattr,
	.rename = stir_rename,
	.rmdir = stir_rmdir,
	.setxattr = stir_setxattr,
	.statfs = stir_statfs,
	.symlink = stir_symlink,
	.truncate = stir_truncate,
	.unlink = stir_unlink,
	.utimens = stir_utimens,
	.write = stir_write,
};

// Ask for password and run FUSE
int main (int argc, char *argv[]) {
	if (argc<3) {
		printf("\
StirFS 0.9, (c) 2010 Gaetan Bisson\n\
The secure, transparent and irresistible filesystem.\n\
\n\
Mounts an encrypted filesystem in mountpoint using rootdir as backend directory.\n\
\n\
");
		char *argv[] = { "stirfs rootdir", "-h" };
		return fuse_main(2, argv, NULL, NULL);
	}

	char iv[] = "{StirFS}{StirFS}";
	char *passwd = getpass("Password: ");
	int n = strlen(passwd)+strlen(iv)+1;
	char *salt = malloc(n);
	strcpy(salt, iv);
	strcat(salt, passwd);
	unsigned char hash[SHA256_DIGEST_LENGTH];
	SHA256((unsigned char *)salt, n, hash);
	AES_KEY *ekey = malloc(sizeof(AES_KEY));
	AES_KEY *dkey = malloc(sizeof(AES_KEY));
	AES_set_encrypt_key(hash, 128, ekey);
	AES_set_decrypt_key(hash, 128, dkey);
	free(passwd);
	free(salt);

	struct stir_ctx *ctx = malloc(sizeof(struct stir_ctx));
	ctx->root = realpath(argv[1], NULL);
	ctx->ekey = ekey;
	ctx->dkey = dkey;
	argv[1] = argv[0];
	return fuse_main(argc-1, argv+1, &stir_oper, ctx);
}
