/*  inode.c
 *
 *  Copyright (C) 1995-1999 Martin von Löwis
 *  Copyright (C) 1996 Albert D. Cahalan
 *  Copyright (C) 1996-1997 Régis Duchesne
 *  Copyright (C) 1998 Joseph Malicki
 *  Copyright (C) 1999 Steve Dodd
 *  Copyright (C) 2000-2001 Anton Altaparmakov (AIA)
 */

#include "ntfstypes.h"
#include "ntfsendian.h"
#include "struct.h"
#include "inode.h"
#include <linux/errno.h>
#include "macros.h"
#include "attr.h"
#include "super.h"
#include "dir.h"
#include "support.h"
#include "util.h"

typedef struct {
	int recno;
	unsigned char *record;
} ntfs_mft_record;

typedef struct {
	int size;
	int count;
	ntfs_mft_record *records;
} ntfs_disk_inode;

void ntfs_fill_mft_header(ntfs_u8 *mft, int record_size, int blocksize,
			  int sequence_number)
{
	int fixup_count = record_size / blocksize + 1;
	int attr_offset = (0x2a + (2 * fixup_count) + 7) & ~7;
	int fixup_offset = 0x2a;

	NTFS_PUTU32(mft + 0x00, 0x454c4946);	     /* FILE */
	NTFS_PUTU16(mft + 0x04, 0x2a);		     /* Offset to fixup. */
	NTFS_PUTU16(mft + 0x06, fixup_count);	     /* Number of fixups. */
	NTFS_PUTU16(mft + 0x10, sequence_number);    /* Sequence number. */
	NTFS_PUTU16(mft + 0x12, 1);                  /* Hard link count. */
	NTFS_PUTU16(mft + 0x14, attr_offset);	     /* Offset to attributes. */
	NTFS_PUTU16(mft + 0x16, 1);                  /* Flags: 1 = In use,
							       2 = Directory. */
	NTFS_PUTU32(mft + 0x18, attr_offset + 0x08); /* Bytes in use. */
	NTFS_PUTU32(mft + 0x1c, record_size);	     /* Total allocated size. */
	NTFS_PUTU16(mft + fixup_offset, 1);	     /* Fixup word. */
	NTFS_PUTU32(mft + attr_offset, 0xffffffff);  /* End marker. */
}

/* Search in an inode an attribute by type and name. 
 * FIXME: Check that when attributes are inserted all attribute list
 * attributes are expanded otherwise need to modify this function to deal
 * with attribute lists. (AIA) */
ntfs_attribute *ntfs_find_attr(ntfs_inode *ino, int type, char *name)
{
	int i;
	
	if (!ino) {
		ntfs_error("ntfs_find_attr: NO INODE!\n");
		return 0;
	}
	for (i = 0; i < ino->attr_count; i++) {
		if (type < ino->attrs[i].type)
			return 0;
		if (type == ino->attrs[i].type) {
			if (!name) {
				if (!ino->attrs[i].name)
					return ino->attrs + i;
			} else if (ino->attrs[i].name &&
				   !ntfs_ua_strncmp(ino->attrs[i].name, name,
						    strlen(name)))
				return ino->attrs + i;
		}
	}
	return 0;
}

/* FIXME: Need better strategy to extend the MFT. */
static int ntfs_extend_mft(ntfs_volume *vol)
{
	/* Try to allocate at least 0.1% of the remaining disk space for
	 * inodes. If the disk is almost full, make sure at least one inode is
	 * requested. */
	int size, rcount, error, block;
	ntfs_attribute *mdata, *bmp;
	ntfs_u8 *buf;
	ntfs_io io;

	mdata = ntfs_find_attr(vol->mft_ino, vol->at_data, 0);
	/* First check whether there is uninitialized space. */
	if (mdata->allocated < mdata->size + vol->mft_recordsize) {
		size = ntfs_get_free_cluster_count(vol->bitmap) *
							      vol->clustersize;
		block = vol->mft_recordsize;
		size = max(size / 1000, mdata->size + vol->mft_recordsize);
		size = ((size + block - 1) / block) * block;
		/* Require this to be a single chunk. */
		error = ntfs_extend_attr(vol->mft_ino, mdata, &size,
							   ALLOC_REQUIRE_SIZE);
		/* Try again, now we have the largest available fragment. */
		if (error == -ENOSPC) {
			/* Round down to multiple of mft record size. */
			size = (size / vol->mft_recordsize) *
							   vol->mft_recordsize;
			if (!size)
				return -ENOSPC;
			error = ntfs_extend_attr(vol->mft_ino, mdata, &size,
							   ALLOC_REQUIRE_SIZE);
		}
		if (error)
			return error;
	}
	/* Even though we might have allocated more than needed, we initialize
	 * only one record. */
	mdata->size += vol->mft_recordsize;
	/* Now extend the bitmap if necessary. */
	rcount = mdata->size / vol->mft_recordsize;
	bmp = ntfs_find_attr(vol->mft_ino, vol->at_bitmap, 0);
	if (bmp->size * 8 < rcount) { /* Less bits than MFT records. */
		ntfs_u8 buf[1];
		/* Extend bitmap by one byte. */
		error = ntfs_resize_attr(vol->mft_ino, bmp, bmp->size + 1);
		if (error)
			return error;
		/* Write the single byte. */
		buf[0] = 0;
		io.fn_put = ntfs_put;
		io.fn_get = ntfs_get;
		io.param = buf;
		io.size = 1;
		error = ntfs_write_attr(vol->mft_ino, vol->at_bitmap, 0,
					bmp->size - 1, &io);
		if (error)
			return error;
		if (io.size != 1)
			return -EIO;
	}
	/* Now fill in the MFT header for the new block. */
	buf = ntfs_calloc(vol->mft_recordsize);
	if (!buf)
		return -ENOMEM;
	ntfs_fill_mft_header(buf, vol->mft_recordsize, vol->blocksize, 0);
	ntfs_insert_fixups(buf, vol->blocksize);
	io.param = buf;
	io.size = vol->mft_recordsize;
	io.fn_put = ntfs_put;
	io.fn_get = ntfs_get;
	error = ntfs_write_attr(vol->mft_ino, vol->at_data, 0,
				(rcount - 1) * vol->mft_recordsize, &io);
	if (error)
		return error;
	if (io.size != vol->mft_recordsize)
		return -EIO;
	error = ntfs_update_inode(vol->mft_ino);
	if (error)
		return error;
	return 0;
}

/* Insert all attributes from the record mftno of the MFT in the inode ino.
 * FIXME: We should be performing structural consistency checks. (AIA)
 * Return 0 on success or -errno on error. */
static int ntfs_insert_mft_attributes(ntfs_inode* ino, char *mft, int mftno)
{
	int i, error, type, len;
	char *it;
	
	/* Check for duplicate. */
	for(i = 0; i < ino->record_count; i++)
		if (ino->records[i] == mftno)
			return 0;
	/* (re-)allocate space if necessary. */
	if (ino->record_count % 8 == 0)	{
		int *new;

		new = ntfs_malloc((ino->record_count + 8) * sizeof(int));
		if (!new)
			return -ENOMEM;
		if (ino->records) {
			for (i = 0; i < ino->record_count; i++)
				new[i] = ino->records[i];
			ntfs_free(ino->records);
		}
		ino->records = new;
	}
	ino->records[ino->record_count] = mftno;
	ino->record_count++;
	it = mft + NTFS_GETU16(mft + 0x14);
	do {
		type = NTFS_GETU32(it);
		len = NTFS_GETU32(it + 4);
		if (type != -1) {
			error = ntfs_insert_attribute(ino, it);
			if (error)
				return error;
		}
		it += len;
	} while (type != -1); /* Attribute list ends with type -1. */
	return 0;
}

/* Read and insert all the attributes of an 'attribute list' attribute.
 * Return the number of remaining bytes in *plen. */
static int parse_attributes(ntfs_inode *ino, ntfs_u8 *alist, int *plen)
{
	char *mft;
	int mftno, l, error;
	int last_mft = -1;
	int len = *plen;
	
	mft = ntfs_malloc(ino->vol->mft_recordsize);
	if (!mft)
		return -ENOMEM;
	while (len > 8)	{
		l = NTFS_GETU16(alist + 4);
		if (l > len)
			break;
	        /* Process an attribute description. */
		mftno = NTFS_GETU32(alist + 0x10); 
			/* FIXME: The mft reference (alist + 0x10) is __u64.
			* - Not a problem unless we encounter a huge partition.
			* - Should be consistency checking the sequence numbers
			*   though! This should maybe happen in 
			*   ntfs_read_mft_record() itself and a hotfix could
			*   then occur there or the user notified to run
			*   ntfsck. (AIA) */
		if (mftno != last_mft){
			last_mft = mftno;
			/* FIXME: Avoid loading record if it's already
			 * processed. */
			error = ntfs_read_mft_record(ino->vol, mftno, mft);
			if (error)
				return error;
			error = ntfs_insert_mft_attributes(ino, mft, mftno);
			if (error)
				return error;
		}
		len -= l;
		alist += l;
	}
	ntfs_free(mft);
	*plen = len;
	return 0;
}

static void ntfs_load_attributes(ntfs_inode* ino)
{
	ntfs_attribute *alist;
	int datasize;
	int offset, len, delta;
	char *buf;
	ntfs_volume *vol = ino->vol;
	
	ntfs_debug(DEBUG_FILE2, "load_attributes %x 1\n", ino->i_number);
	if (ntfs_insert_mft_attributes(ino, ino->attr, ino->i_number))
		return;
	ntfs_debug(DEBUG_FILE2, "load_attributes %x 2\n", ino->i_number);
	alist = ntfs_find_attr(ino, vol->at_attribute_list, 0);
	ntfs_debug(DEBUG_FILE2, "load_attributes %x 3\n", ino->i_number);
	if (!alist)
		return;
	ntfs_debug(DEBUG_FILE2, "load_attributes %x 4\n", ino->i_number);
	datasize = alist->size;
	if (alist->resident) {
		parse_attributes(ino, alist->d.data, &datasize);
		return;
	}
	buf = ntfs_malloc(1024);
	if (!buf)    /* FIXME: Should be passing error code to caller. (AIA) */
		return;
	delta = 0;
	for (offset = 0; datasize; datasize -= len, offset += len) {
		ntfs_io io;
		
		io.fn_put = ntfs_put;
		io.fn_get = 0;
		io.param = buf + delta;
		io.size = len = min(datasize, 1024 - delta);
		if (ntfs_read_attr(ino, vol->at_attribute_list, 0, offset,
				   &io))
			ntfs_error("error in load_attributes\n");
		delta += len;
		parse_attributes(ino, buf, &delta);
		if (delta)
			/* Move remaining bytes to buffer start. */
			ntfs_memmove(buf, buf + len - delta, delta);
	}
	ntfs_debug(DEBUG_FILE2, "load_attributes %x 5\n", ino->i_number);
	ntfs_free(buf);
}
	
int ntfs_init_inode(ntfs_inode *ino, ntfs_volume *vol, int inum)
{
	char *buf;
	int error;

	ntfs_debug(DEBUG_FILE1, "Initializing inode %x\n", inum);
	if (!vol)
		ntfs_error("NO VOLUME!\n");
	ino->i_number = inum;
	ino->vol = vol;
	ino->attr = buf = ntfs_malloc(vol->mft_recordsize);
	if (!buf)
		return -ENOMEM;
	error = ntfs_read_mft_record(vol, inum, ino->attr);
	if (error) {
		ntfs_debug(DEBUG_OTHER, "Init inode: %x failed\n", inum);
		return error;
	}
	ntfs_debug(DEBUG_FILE2, "Init inode: got mft %x\n", inum);
	ino->sequence_number = NTFS_GETU16(buf + 0x10);
	ino->attr_count = 0;
	ino->record_count = 0;
	ino->records = 0;
	ino->attrs = 0;
	ntfs_load_attributes(ino);
	ntfs_debug(DEBUG_FILE2, "Init inode: done %x\n", inum);
	return 0;
}

void ntfs_clear_inode(ntfs_inode *ino)
{
	int i;
	if (!ino->attr) {
		ntfs_error("ntfs_clear_inode: double free\n");
		return;
	}
	ntfs_free(ino->attr);
	ino->attr = 0;
	ntfs_free(ino->records);
	ino->records = 0;
	for (i = 0; i < ino->attr_count; i++) {
		if (ino->attrs[i].name)
			ntfs_free(ino->attrs[i].name);
		if (ino->attrs[i].resident) {
			if (ino->attrs[i].d.data)
				ntfs_free(ino->attrs[i].d.data);
		} else {
			if (ino->attrs[i].d.r.runlist)
				ntfs_free(ino->attrs[i].d.r.runlist);
		}
	}
	ntfs_free(ino->attrs);
	ino->attrs = 0;
}

/* Check and fixup a MFT record. */
int ntfs_check_mft_record(ntfs_volume *vol, char *record)
{
	return ntfs_fixup_record(vol, record, "FILE", vol->mft_recordsize);
}

/* Return (in result) the value indicating the next available attribute 
 * chunk number. Works for inodes w/o extension records only. */
int ntfs_allocate_attr_number(ntfs_inode *ino, int *result)
{
	if (ino->record_count != 1)
		return -EOPNOTSUPP;
	*result = NTFS_GETU16(ino->attr + 0x28);
	NTFS_PUTU16(ino->attr + 0x28, (*result) + 1);
	return 0;
}

/* Find the location of an attribute in the inode. A name of NULL indicates
 * unnamed attributes. Return pointer to attribute or NULL if not found. */
char *ntfs_get_attr(ntfs_inode *ino, int attr, char *name)
{
	/* Location of first attribute. */
	char *it = ino->attr + NTFS_GETU16(ino->attr + 0x14);
	int type;
	int len;
	
	/* Only check for magic DWORD here, fixup should have happened before.*/
	if (!IS_MFT_RECORD(ino->attr))
		return 0;
	do {
		type = NTFS_GETU32(it);
		len = NTFS_GETU16(it + 4);
		/* We found the attribute type. Is the name correct, too? */
		if (type == attr) {
			int namelen = NTFS_GETU8(it + 9);
			char *name_it, *n = name;
			/* Match given name and attribute name if present.
			   Make sure attribute name is Unicode. */
			if (!name) {
				goto check_namelen;
			} else if (namelen) {
				for (name_it = it + NTFS_GETU16(it + 10);
				     namelen; n++, name_it += 2, namelen--)
					if (*name_it != *n || name_it[1])
						break;
check_namelen:
				if (!namelen)
					break;
			}
		}
		it += len;
	} while (type != -1); /* List of attributes ends with type -1. */
	if (type == -1)
		return 0;
	return it;
}

int ntfs_get_attr_size(ntfs_inode *ino, int type, char *name)
{
	ntfs_attribute *attr = ntfs_find_attr(ino, type, name);
	if (!attr)
		return 0;
	return
		attr->size;
}
	
int ntfs_attr_is_resident(ntfs_inode *ino, int type, char *name)
{
	ntfs_attribute *attr = ntfs_find_attr(ino, type, name);
	if (!attr)
		return 0;
	return
		attr->resident;
}
	
/*
 * A run is coded as a type indicator, an unsigned length, and a signed cluster
 * offset.
 * . To save space, length and offset are fields of variable length. The low
 *   nibble of the type indicates the width of the length :), the high nibble
 *   the width of the offset.
 * . The first offset is relative to cluster 0, later offsets are relative to
 *   the previous cluster.
 *
 * This function decodes a run. Length is an output parameter, data and cluster
 * are in/out parameters.
 */
int ntfs_decompress_run(unsigned char **data, int *length, 
			ntfs_cluster_t *cluster, int *ctype)
{
	unsigned char type = *(*data)++;
	*ctype = 0;
	switch (type & 0xF) {
	case 1: 
		*length = NTFS_GETS8(*data);
		break;
	case 2: 
		*length = NTFS_GETS16(*data);
		break;
	case 3: 
		*length = NTFS_GETS24(*data);
		break;
        case 4: 
		*length = NTFS_GETS32(*data);
		break;
        	/* Note: cases 5-8 are probably pointless to code, since how
		 * many runs > 4GB of length are there? At the most, cases 5
		 * and 6 are probably necessary, and would also require making
		 * length 64-bit throughout. */
	default:
		ntfs_error("Can't decode run type field %x\n", type);
		return -1;
	}
	if (*length < 0)
	{
		ntfs_error("Negative run length decoded\n");
		return -1;
	}
	*data += (type & 0xF);
	switch (type & 0xF0) {
	case 0:
		*ctype = 2;
		break;
	case 0x10:
		*cluster += NTFS_GETS8(*data);
		break;
	case 0x20:
		*cluster += NTFS_GETS16(*data);
		break;
	case 0x30:
		*cluster += NTFS_GETS24(*data);
		break;
	case 0x40:
		*cluster += NTFS_GETS32(*data);
		break;
#if 0 /* Keep for future, in case ntfs_cluster_t ever becomes 64bit. */
	case 0x50: 
		*cluster += NTFS_GETS40(*data);
		break;
	case 0x60: 
		*cluster += NTFS_GETS48(*data);
		break;
	case 0x70: 
		*cluster += NTFS_GETS56(*data);
		break;
	case 0x80: 
		*cluster += NTFS_GETS64(*data);
		break;
#endif
	default:
		ntfs_error("Can't decode run type field %x\n", type);
		return -1;
	}
	*data += (type >> 4);
	return 0;
}

/*
 * FIXME: ntfs_readwrite_attr() has the effect of writing @dest to @offset of
 * the attribute value of the attribute @attr in the in memory inode @ino.
 * If the attribute value of @attr is non-resident the value's contents at
 * @offset are actually written to disk (from @dest). The on disk mft record
 * describing the non-resident attribute value is not updated!
 * If the attribute value is resident then the value is written only in
 * memory. The on disk mft record containing the value is not written to disk.
 * A possible fix would be to call ntfs_update_inode() before returning. (AIA)
 */
/* Reads l bytes of the attribute (attr, name) of ino starting at offset on
 * vol into buf. Returns the number of bytes read in the ntfs_io struct.
 * Returns 0 on success, errno on failure */
int ntfs_readwrite_attr(ntfs_inode *ino, ntfs_attribute *attr, int offset,
			ntfs_io *dest)
{
	int rnum;
	ntfs_cluster_t cluster, s_cluster, vcn, len;
	int l, chunk, copied;
	int s_vcn;
	int clustersize;
	int error;

	clustersize = ino->vol->clustersize;
	l = dest->size;
	if (l == 0)
		return 0;
	if (dest->do_read) {
		/* If read _starts_ beyond end of stream, return nothing. */
		if (offset >= attr->size) {
			dest->size = 0;
			return 0;
		}
		/* If read _extends_ beyond end of stream, return as much
		 * initialised data as we have. */
		if (offset + l >= attr->size)
			l = dest->size = attr->size - offset;
	} else {
		/* Fixed by CSA: If writing beyond end, extend attribute. */
		/* If write extends beyond _allocated_ size, extend attrib. */
		if (offset + l > attr->allocated) {
			error = ntfs_resize_attr(ino, attr, offset + l);
			if (error)
				return error;
		}
		/* The amount of initialised data has increased: update. */
		/* FIXME: Shouldn't we zero-out the section between the old
		 * 	  initialised length and the write start? */
		if (offset + l > attr->initialized) {
			attr->initialized = offset + l;
			attr->size = offset + l;
		}
	}
	if (attr->resident) {
		if (dest->do_read)
			dest->fn_put(dest, (ntfs_u8*)attr->d.data + offset, l);
		else
			dest->fn_get((ntfs_u8*)attr->d.data + offset, dest, l);
		dest->size = l;
		return 0;
	}
	/* Read uninitialized data. */
	if (offset >= attr->initialized && dest->do_read)
		return ntfs_read_zero(dest, l);
	if (offset + l > attr->initialized && dest->do_read) {
		dest->size = chunk = offset + l - attr->initialized;
		error = ntfs_readwrite_attr(ino, attr, offset, dest);
		if (error)
			return error;		
		return ntfs_read_zero(dest, l - chunk);
	}
	if (attr->compressed) {
		if (dest->do_read)
			return ntfs_read_compressed(ino, attr, offset, dest);
		else
			return ntfs_write_compressed(ino, attr, offset, dest);
	}
	vcn = 0;
	s_vcn = offset / clustersize;
	for (rnum = 0; rnum < attr->d.r.len && 
		       vcn + attr->d.r.runlist[rnum].len <= s_vcn; rnum++)
		vcn += attr->d.r.runlist[rnum].len;
	if (rnum == attr->d.r.len)
		/*FIXME: Should extend runlist. */
		return -EOPNOTSUPP;
	copied = 0;
	while (l) {
		s_vcn = offset / clustersize;
		cluster = attr->d.r.runlist[rnum].cluster;
		len = attr->d.r.runlist[rnum].len;
		s_cluster = cluster + s_vcn - vcn;
		chunk = min((vcn + len) * clustersize - offset, l);
		dest->size = chunk;
		error = ntfs_getput_clusters(ino->vol, s_cluster, 
					   offset - s_vcn * clustersize, dest);
		if (error) {
			ntfs_error("Read/write error\n");
			dest->size = copied;
			return error;
		}
		l -= chunk;
		copied += chunk;
		offset += chunk;
		if (l && offset >= ((vcn + len) * clustersize))	{
			rnum++;
			vcn += len;
			cluster = attr->d.r.runlist[rnum].cluster;
			len = attr->d.r.runlist[rnum].len;
		}
	}
	dest->size = copied;
	return 0;
}

int ntfs_read_attr(ntfs_inode *ino, int type, char *name, int offset,
		   ntfs_io *buf)
{
	ntfs_attribute *attr;

	buf->do_read = 1;
	attr = ntfs_find_attr(ino, type, name);
	if (!attr)
		return -EINVAL;
	return ntfs_readwrite_attr(ino, attr, offset, buf);
}

int ntfs_write_attr(ntfs_inode *ino, int type, char *name, int offset,
		    ntfs_io *buf)
{
	ntfs_attribute *attr;
	
	buf->do_read = 0;
	attr = ntfs_find_attr(ino, type, name);
	if (!attr)
		return -EINVAL;
	return ntfs_readwrite_attr(ino, attr, offset, buf);
}

int ntfs_vcn_to_lcn(ntfs_inode *ino, int vcn)
{
	int rnum;
	ntfs_attribute *data;
	
	data = ntfs_find_attr(ino, ino->vol->at_data, 0);
	/* It's hard to give an error code. */
	if (!data || data->resident || data->compressed)
		return -1;
	if (data->size <= vcn*ino->vol->clustersize)
		return -1;
	/* For Linux, block number 0 represents a hole.
	 * Hopefully, nobody will attempt to bmap $Boot. */
	if (data->initialized <= vcn * ino->vol->clustersize)
		return 0;
	for (rnum = 0; rnum < data->d.r.len &&
		       vcn >= data->d.r.runlist[rnum].len; rnum++)
		vcn -= data->d.r.runlist[rnum].len;
	return data->d.r.runlist[rnum].cluster + vcn;
}

static int allocate_store(ntfs_volume *vol, ntfs_disk_inode *store, int count)
{
	int i;
	
	if (store->count > count)
		return 0;
	if (store->size < count) {
		ntfs_mft_record *n = ntfs_malloc((count + 4) * 
						 sizeof(ntfs_mft_record));
		if (!n)
			return -ENOMEM;
		if (store->size) {
			for (i = 0; i < store->size; i++)
				n[i] = store->records[i];
			ntfs_free(store->records);
		}
		store->size = count + 4;
		store->records = n;
	}
	for (i = store->count; i < count; i++) {
		store->records[i].record = ntfs_malloc(vol->mft_recordsize);
		if (!store->records[i].record)
			return -ENOMEM;
		store->count++;
	}
	return 0;
}

static void deallocate_store(ntfs_disk_inode* store)
{
	int i;
	
	for (i = 0; i < store->count; i++)
		ntfs_free(store->records[i].record);
	ntfs_free(store->records);
	store->count = store->size = 0;
	store->records = 0;
}

/**
 * layout_runs - compress runlist into mapping pairs array
 * @attr:	attribute containing the runlist to compress
 * @rec:	destination buffer to hold the mapping pairs array
 * @offs:	current position in @rec (in/out variable)
 * @size:	size of the buffer @rec
 *
 * layout_runs walks the runlist in @attr, compresses it and writes it out the
 * resulting mapping pairs array into @rec (up to a maximum of @size bytes are
 * written). On entry @offs is the offset in @rec at which to begin writting the
 * mapping pairs array. On exit, it contains the offset in @rec of the first
 * byte after the end of the mapping pairs array.
 */
static int layout_runs(ntfs_attribute *attr, char* rec, int* offs, int size)
{
	int i, len, offset, coffs;
	ntfs_cluster_t cluster, rclus;
	ntfs_runlist *rl = attr->d.r.runlist;
	cluster = 0;
	offset = *offs;
	for (i = 0; i < attr->d.r.len; i++) {
		rclus = rl[i].cluster - cluster;
		len = rl[i].len;
		rec[offset] = 0;
 		if (offset + 9 > size)
			return -E2BIG; /* It might still fit, but this
					* simplifies testing. */
		/* Run length is stored as signed number. */
		if (len <= 0x7F) {
			NTFS_PUTU8(rec + offset + 1, len);
			coffs = 1;
 		} else if (len <= 0x7FFF) {
			NTFS_PUTU16(rec + offset + 1, len);
			coffs = 2;
 		} else if (len <= 0x7FFFFF) {
			NTFS_PUTU24(rec + offset + 1, len);
			coffs = 3;
		} else {
			NTFS_PUTU32(rec + offset + 1, len);
			coffs = 4;
		}
    		*(rec + offset) |= coffs++;
		if (rl[i].cluster == MAX_CLUSTER_T) /* Compressed run. */
			/* Nothing */;
		else if (rclus >= -0x80 && rclus <= 0x7F) {
			*(rec + offset) |= 0x10;
			NTFS_PUTS8(rec + offset + coffs, rclus);
			coffs += 1;
		} else if(rclus >= -0x8000 && rclus <= 0x7FFF) {
			*(rec + offset) |= 0x20;
			NTFS_PUTS16(rec + offset + coffs, rclus);
			coffs += 2;
		} else if(rclus >= -0x800000 && rclus <= 0x7FFFFF) {
			*(rec + offset) |= 0x30;
			NTFS_PUTS24(rec + offset + coffs, rclus);
			coffs += 3;
		} else
#if 0 /* In case ntfs_cluster_t ever becomes 64bit. */
	       	if (rclus >= -0x80000000LL && rclus <= 0x7FFFFFFF)
#endif
		{
			*(rec + offset) |= 0x40;
			NTFS_PUTS32(rec + offset + coffs, rclus);
			coffs += 4;
		}
#if 0 /* For 64-bit ntfs_cluster_t */
		else if (rclus >= -0x8000000000 && rclus <= 0x7FFFFFFFFF) {
			*(rec + offset) |= 0x50;
			NTFS_PUTS40(rec + offset + coffs, rclus);
			coffs += 5;
		} else if (rclus >= -0x800000000000 && 
						rclus <= 0x7FFFFFFFFFFF) {
			*(rec + offset) |= 0x60;
			NTFS_PUTS48(rec + offset + coffs, rclus);
			coffs += 6;
		} else if (rclus >= -0x80000000000000 && 
						rclus <= 0x7FFFFFFFFFFFFF) {
			*(rec + offset) |= 0x70;
			NTFS_PUTS56(rec + offset + coffs, rclus);
			coffs += 7;
		} else {
			*(rec + offset) |= 0x80;
			NTFS_PUTS64(rec + offset + coffs, rclus);
			coffs += 8;
		}
#endif
		offset += coffs;
		if (rl[i].cluster)
			cluster = rl[i].cluster;
	}
	if (offset >= size)
		return -E2BIG;
	/* Terminating null. */
	*(rec + offset++) = 0;
	*offs = offset;
	return 0;
}

static void count_runs(ntfs_attribute *attr, char *buf)
{
	ntfs_u32 first, count, last, i;
	
	first = 0;
	for (i = 0, count = 0; i < attr->d.r.len; i++)
		count += attr->d.r.runlist[i].len;
	last = first + count - 1;
	NTFS_PUTU64(buf + 0x10, first);
	NTFS_PUTU64(buf + 0x18, last);
} 

/**
 * layout_attr - convert in memory attribute to on disk attribute record
 * @attr:	in memory attribute to convert
 * @buf:	destination buffer for on disk attribute record
 * @size:	size of the destination buffer
 * @psize:	size of converted on disk attribute record (out variable)
 *
 * layout_attr takes the attribute @attr and converts it into the appropriate
 * on disk structure, writing it into @buf (up to @size bytes are written).
 * On return, @psize contains the actual size of the on disk attribute written
 * into @buf.
 */
static int layout_attr(ntfs_attribute* attr, char *buf, int size, int *psize)
{
	int asize, error;
	if (size < 10)
		return -E2BIG;
	NTFS_PUTU32(buf, attr->type);
	/* Fill in length later. */
	NTFS_PUTU8(buf + 8, attr->resident ? 0 : 1);
	NTFS_PUTU8(buf + 9, attr->namelen);
	/* Fill in offset to name later. */
	NTFS_PUTU16(buf + 0xA, 0);
	NTFS_PUTU16(buf + 0xC, attr->compressed);
	/* Assign attribute instance. */
	NTFS_PUTU16(buf + 0xE, attr->attrno);
	if (attr->resident) {
		if (size < attr->size + 0x18 + attr->namelen)
			return -E2BIG;
		asize = 0x18;
		NTFS_PUTU32(buf + 0x10, attr->size);
		NTFS_PUTU16(buf + 0x16, attr->indexed);
		if (attr->name) {
			ntfs_memcpy(buf + asize, attr->name, 2 * attr->namelen);
			NTFS_PUTU16(buf + 0xA, asize);
			asize += 2 * attr->namelen;
			asize = (asize + 7) & ~7;
		}
		NTFS_PUTU16(buf + 0x14, asize);
		ntfs_memcpy(buf + asize, attr->d.data, attr->size);
		asize += attr->size;
	} else {
		/* FIXME: fragments */
		count_runs(attr, buf);
		/* Offset to data is added later. */
		NTFS_PUTU16(buf + 0x22, attr->cengine);
		NTFS_PUTU32(buf + 0x24, 0);
		NTFS_PUTU64(buf + 0x28, attr->allocated);
		NTFS_PUTU64(buf + 0x30, attr->size);
		NTFS_PUTU64(buf + 0x38, attr->initialized);
		if (attr->compressed) {
			NTFS_PUTU64(buf + 0x40, attr->compsize);
			asize = 0x48;
		} else
			asize = 0x40;
		if (attr->name) {
			NTFS_PUTU16(buf + 0xA, asize);
			ntfs_memcpy(buf + asize, attr->name, 2 * attr->namelen);
			asize += 2 * attr->namelen;
			/* SRD: you whaaa? - AIA: Align to next 8 byte boundary
			 * as required by NTFS design and implementation. */
			asize = (asize + 7) & ~7;
		}
		/* asize points at the beginning of the data. */
		NTFS_PUTU16(buf + 0x20, asize);
		error = layout_runs(attr, buf, &asize, size);
		/* Now asize points at the end of the data. */
		if (error)
			return error;
	}
	asize = (asize + 7) & ~7;
	NTFS_PUTU32(buf + 4, asize);
	*psize = asize;
	return 0;
}

/**
 * layout_inode - convert an in memory inode into on disk mft record(s)
 * @ino:	in memory inode to convert
 * @store:	on disk inode, contain buffers for the on disk mft record(s)
 *
 * layout_inode takes the in memory inode @ino, converts it into a (sequence of)
 * mft record(s) and writes them to the appropriate buffers in the @store.
 *
 * Return 0 on success,
 * the required mft record count (>0) if the inode does not fit,
 * -ENOMEM if memory allocation problem or
 * -EOPNOTSUP if beyond our capabilities.
 */
int layout_inode(ntfs_inode *ino, ntfs_disk_inode *store)
{
	int offset, i;
	ntfs_attribute *attr;
	unsigned char *rec;
	int size, psize;
	int error;

	if (ino->record_count > 1) {
		ntfs_error("layout_inode: attribute lists not supported\n");
		return -EOPNOTSUPP;
	}
	error = allocate_store(ino->vol, store, 1);
	if (error)
		return error;
	rec = store->records[0].record;
	size = ino->vol->mft_recordsize;
	store->records[0].recno = ino->records[0];
	/* Copy header. */
	offset = NTFS_GETU16(ino->attr + 0x14);
	ntfs_memcpy(rec, ino->attr, offset);
	for (i = 0; i < ino->attr_count; i++) {
		attr = ino->attrs + i;
		error = layout_attr(attr, rec + offset, size - offset, &psize);
		if (error)
			return error;
		offset += psize;
	}
	/* Terminating attribute. */
	if (offset + 8 < size) {
		NTFS_PUTU32(rec + offset, 0xFFFFFFFF);
		offset += 4;
		NTFS_PUTU32(rec + offset, 0);
		offset += 4;
	} else
		return -E2BIG;
	NTFS_PUTU32(rec + 0x18, offset);
	return 0;
}

/*
 * FIXME: ntfs_update_inode() calls layout_inode() to create the mft record on
 * disk structure corresponding to the inode @ino. After that, ntfs_write_attr()
 * is called to write out the created mft record to disk.
 * We shouldn't need to re-layout every single time we are updating an mft
 * record. No wonder the ntfs driver is slow like hell. (AIA)
 */
int ntfs_update_inode(ntfs_inode *ino)
{
	int error;
	ntfs_disk_inode store;
	ntfs_io io;
	int i;

	store.count = store.size = 0;
	store.records = 0;
	error = layout_inode(ino, &store);
	if (error == -E2BIG) {
		error = ntfs_split_indexroot(ino);
		if (!error)
			error = layout_inode(ino, &store);
	}
	if (error == -E2BIG) {
		error = ntfs_attr_allnonresident(ino);
		if (!error)
			error = layout_inode(ino, &store);
	}
	if (error == -E2BIG) {
		/* FIXME: should try: introduce extension records */
		ntfs_error("cannot handle saving inode %x\n", ino->i_number);
		deallocate_store(&store);
		return -EOPNOTSUPP;
	}
	if (error) {
		deallocate_store(&store);
		return error;
	}
	io.fn_get = ntfs_get;
	io.fn_put = 0;
	for (i = 0; i < store.count; i++) {
		ntfs_insert_fixups(store.records[i].record,
							  ino->vol->blocksize);
		io.param = store.records[i].record;
		io.size = ino->vol->mft_recordsize;
		/* FIXME: Is this the right way? */
		error = ntfs_write_attr(ino->vol->mft_ino, ino->vol->at_data,
					0, store.records[i].recno * 
					ino->vol->mft_recordsize, &io);
		if (error || io.size != ino->vol->mft_recordsize) {
			/* Big trouble, partially written file. */
			ntfs_error("Please unmount: Write error in inode %x\n",
				   ino->i_number);
			deallocate_store(&store);
			return error ? error : -EIO;
		}
	}
	return 0;
}	

void ntfs_decompress(unsigned char *dest, unsigned char *src, ntfs_size_t l)
{
	int head, comp;
	int copied = 0;
	unsigned char *stop;
	int bits;
	int tag = 0;
	int clear_pos;
	
	while (1) {
		head = NTFS_GETU16(src) & 0xFFF;
		/* High bit indicates that compression was performed. */
		comp = NTFS_GETU16(src) & 0x8000;
		src += 2;
		stop = src + head;
		bits = 0;
		clear_pos = 0;
		if (head == 0)
			/* Block is not used. */
			return;/* FIXME: copied */
		if (!comp) { /* uncompressible */
			ntfs_memcpy(dest, src, 0x1000);
			dest += 0x1000;
			copied += 0x1000;
			src += 0x1000;
			if (l == copied)
				return;
			continue;
		}
		while (src <= stop) {
			if (clear_pos > 4096) {
				ntfs_error("Error 1 in decompress\n");
				return;
			}
			if (!bits) {
				tag = NTFS_GETU8(src);
				bits = 8;
				src++;
				if (src > stop)
					break;
			}
			if (tag & 1) {
				int i, len, delta, code, lmask, dshift;
				code = NTFS_GETU16(src);
				src += 2;
				if (!clear_pos) {
					ntfs_error("Error 2 in decompress\n");
					return;
				}
				for (i = clear_pos - 1, lmask = 0xFFF,
				     dshift = 12; i >= 0x10; i >>= 1) {
					lmask >>= 1;
					dshift--;
				}
				delta = code >> dshift;
				len = (code & lmask) + 3;
				for (i = 0; i < len; i++) {
					dest[clear_pos] = dest[clear_pos - 
								    delta - 1];
					clear_pos++;
					copied++;
					if (copied==l)
						return;
				}
			} else {
				dest[clear_pos++] = NTFS_GETU8(src);
				src++;
				copied++;
				if (copied==l)
					return;
			}
			tag >>= 1;
			bits--;
		}
		dest += clear_pos;
	}
}

/* Caveat: No range checking in either ntfs_set_bit or ntfs_clear_bit. */
void ntfs_set_bit(unsigned char *byte, int bit)
{
	byte += (bit >> 3);
	bit &= 7;
	*byte |= (1 << bit);
}

void ntfs_clear_bit(unsigned char *byte, int bit)
{
	byte += (bit >> 3);
	bit &= 7;
	*byte &= ~(1 << bit);
}

/**
 * ntfs_new_inode - allocate an mft record
 * @vol:	volume to allocate an mft record on
 * @result:	the mft record number allocated
 *
 * Allocate a new mft record on disk by finding the first free mft record
 * and allocating it in the mft bitmap.
 * Return 0 on success or -ERRNO on error.
 *
 * TODO(AIA): Implement mft bitmap caching. Replace function by race safe one.
 */
static int ntfs_new_inode(ntfs_volume* vol, int* result)
{
	int byte, error;
	int bit;
	int size, length;
	unsigned char value;
	ntfs_u8 *buffer;
	ntfs_io io;
	ntfs_attribute *data;

	buffer = ntfs_malloc(2048);
	if (!buffer)
		return -ENOMEM;
	io.fn_put = ntfs_put;
	io.fn_get = ntfs_get;
	io.param = buffer;
	/* FIXME: Bitmaps larger than 2048 bytes. */
	io.size = 2048;
	error = ntfs_read_attr(vol->mft_ino, vol->at_bitmap, 0, 0, &io);
	if (error) {
		ntfs_free(buffer);
		return error;
	}
	size = io.size;
	data = ntfs_find_attr(vol->mft_ino, vol->at_data, 0);
	length = data->size / vol->mft_recordsize;
	/* SRD: Start at byte 0: bits for system files _are_ already set in 
	 * bitmap. AIA: This includes the reserved entries as well. */
	for (byte = 0; 8 * byte < length; byte++) {
		value = buffer[byte];
		if (value == 0xFF)
			continue;
		for (bit = 0; (bit < 8) && (8 * byte + bit < length); 
							 bit++, value >>= 1) {
			if (!(value & 1)) {
				*result = byte * 8 + bit;
				return 0;
			}
		}
	}
	/* There is no free space. We must first extend the MFT. */
	return -ENOSPC;
}

static int add_mft_header(ntfs_inode *ino)
{
	unsigned char *mft;
	ntfs_volume *vol = ino->vol;

	mft = ino->attr;
	ntfs_bzero(mft, vol->mft_recordsize);
	ntfs_fill_mft_header(mft, vol->mft_recordsize, vol->blocksize,
			     ino->sequence_number);
	return 0;
}

/* We need 0x48 bytes in total. */
static int add_standard_information(ntfs_inode *ino)
{
	ntfs_time64_t now;
	char data[0x30];
	char *position = data;
	int error;
	ntfs_attribute *si;

	now = ntfs_now();
	NTFS_PUTU64(position + 0x00, now);		/* File creation */
	NTFS_PUTU64(position + 0x08, now);		/* Last modification */
	NTFS_PUTU64(position + 0x10, now);		/* Last mod for MFT */
	NTFS_PUTU64(position + 0x18, now);		/* Last access */
	NTFS_PUTU64(position + 0x20, 0);		/* MSDOS file perms */
	NTFS_PUTU64(position + 0x28, 0);		/* unknown */
	error = ntfs_create_attr(ino, ino->vol->at_standard_information, 0,
				 data, sizeof(data), &si);
	return error;
}

/* FIXME: Need to pass in the size of the file (Data size) as well as the
 * allocated size for file data on disk (Allocated size). (AIA) */
static int add_filename(ntfs_inode* ino, ntfs_inode* dir, 
			const unsigned char *filename, int length, 
			ntfs_u32 flags)
{
	unsigned char *position;
	unsigned int size;
	ntfs_time64_t now;
	int count, error;
	unsigned char* data;
	ntfs_attribute *fn;

	/* Work out the size. */
	size = 0x42 + 2 * length;
	data = ntfs_malloc(size);
	if (!data)
		return -ENOMEM;
	ntfs_bzero(data, size);
	/* Search for a position. */
	position = data;
	NTFS_PUTINUM(position, dir);			/* Inode num of dir */
	now = ntfs_now();
	NTFS_PUTU64(position + 0x08, now);		/* File creation */
	NTFS_PUTU64(position + 0x10, now);		/* Last modification */
	NTFS_PUTU64(position + 0x18, now);		/* Last mod for MFT */
	NTFS_PUTU64(position + 0x20, now);		/* Last access */
	/*NTFS_PUTU64(position + 0x28, 0);*/		/* Allocated size */
	/*NTFS_PUTU64(position + 0x30, 0);*/		/* Data size */
	NTFS_PUTU32(position + 0x38, flags);		/* File flags */
	/*NTFS_PUTU32(position + 0x3c, 0);*/		/* We don't use these
							 * features yet. */
	NTFS_PUTU8(position + 0x40, length);		/* Filename length */
	NTFS_PUTU8(position + 0x41, 0);			/* Only long name */
		/* FIXME: This is madness. We are defining the POSIX namespace
		 * for the filename here which can mean that the file will be
		 * invisible when in Windows NT/2k! )-: (AIA) */
	position += 0x42;
	for (count = 0; count < length; count++) {
		NTFS_PUTU16(position + 2 * count, filename[count]);
	}
	error = ntfs_create_attr(ino, ino->vol->at_file_name, 0, data, size,
				 &fn);
	if (!error)
		error = ntfs_dir_add(dir, ino, fn);
	ntfs_free(data);
	return error;
}

int add_security(ntfs_inode* ino, ntfs_inode* dir)
{
	int error;
	char *buf;
	int size;
	ntfs_attribute* attr;
	ntfs_io io;
	ntfs_attribute *se;

	attr = ntfs_find_attr(dir, ino->vol->at_security_descriptor, 0);
	if (!attr)
		return -EOPNOTSUPP; /* Need security in directory. */
	size = attr->size;
	if (size > 512)
		return -EOPNOTSUPP;
	buf = ntfs_malloc(size);
	if (!buf)
		return -ENOMEM;
	io.fn_get = ntfs_get;
	io.fn_put = ntfs_put;
	io.param = buf;
	io.size = size;
	error = ntfs_read_attr(dir, ino->vol->at_security_descriptor, 0, 0,&io);
	if (!error && io.size != size)
		ntfs_error("wrong size in add_security");
	if (error) {
		ntfs_free(buf);
		return error;
	}
	/* FIXME: Consider ACL inheritance. */
	error = ntfs_create_attr(ino, ino->vol->at_security_descriptor,
				 0, buf, size, &se);
	ntfs_free(buf);
	return error;
}

static int add_data(ntfs_inode* ino, unsigned char *data, int length)
{
	int error;
	ntfs_attribute *da;
	
	error = ntfs_create_attr(ino, ino->vol->at_data, 0, data, length, &da);
	return error;
}

/* We _could_ use 'dir' to help optimise inode allocation. */
int ntfs_alloc_inode(ntfs_inode *dir, ntfs_inode *result, const char *filename,
		     int namelen, ntfs_u32 flags)
{
	ntfs_io io;
	int error;
	ntfs_u8 buffer[2];
	ntfs_volume *vol = dir->vol;
	int byte, bit;

	error = ntfs_new_inode(vol, &(result->i_number));
	if (error == -ENOSPC) {
		error = ntfs_extend_mft(vol);
		if (error)
			return error;
		error = ntfs_new_inode(vol, &(result->i_number));
	}
	if (error) {
		ntfs_error("ntfs_get_empty_inode: no free inodes\n");
		return error;
	}
	byte = result->i_number / 8;
	bit = result->i_number & 7;
	io.fn_put = ntfs_put;
	io.fn_get = ntfs_get;
	io.param = buffer;
	io.size = 1;
	/* Set a single bit. */
	error = ntfs_read_attr(vol->mft_ino, vol->at_bitmap, 0, byte, &io);
	if (error)
		return error;
	if (io.size != 1)
		return -EIO;
	ntfs_set_bit(buffer, bit);
	io.param = buffer;
	io.size = 1;
 	error = ntfs_write_attr(vol->mft_ino, vol->at_bitmap, 0, byte, &io);
	if (error)
		return error;
	if (io.size != 1)
		return -EIO;
	/* FIXME: Should change $Mft on disk.
	 * error = ntfs_update_inode(vol->mft_ino);
	 * if (error)
	 * 	return error; 
	 * FIXME: And don't forget $MftMirr, though this probably belongs
	 * in ntfs_update_inode() (or even deeper). (AIA) */
	/* Get the sequence number. */
	io.param = buffer;
	io.size = 2;
	error = ntfs_read_attr(vol->mft_ino, vol->at_data, 0, 
			       result->i_number * vol->mft_recordsize + 0x10,
			       &io);
	if (error)
		return error;
	/* Increment the sequence number skipping zero. */
	if (NTFS_GETU16(buffer) == 0xffff)
		result->sequence_number = 1;
	else
		result->sequence_number = NTFS_GETU16(buffer) + 1;
	result->vol = vol;
	result->attr = ntfs_malloc(vol->mft_recordsize);
	if (!result->attr)
		return -ENOMEM;
	result->attr_count = 0;
	result->attrs = 0;
	result->record_count = 1;
	result->records = ntfs_malloc(8 * sizeof(int));
	if (!result->records) {
		ntfs_free(result->attr);
		result->attr = 0;
		return -ENOMEM;
	}
	result->records[0] = result->i_number;
	error = add_mft_header(result);
	if (error)
		return error;
	error = add_standard_information(result);
	if (error)
		return error;
	error = add_filename(result, dir, filename, namelen, flags);
	if (error)
		return error;
	error = add_security(result, dir);
	if (error)
		return error;
	return 0;
}

int ntfs_alloc_file(ntfs_inode *dir, ntfs_inode *result, char *filename,
		    int namelen)
{
	int error = ntfs_alloc_inode(dir, result, filename, namelen, 0);
	if (error)
		return error;
	error = add_data(result, 0, 0);
	return error;
}

