patch-2.2.0-pre4 linux/fs/ntfs/dir.c

Next file: linux/fs/ntfs/dir.h
Previous file: linux/fs/ntfs/attr.h
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.2.0-pre3/linux/fs/ntfs/dir.c linux/fs/ntfs/dir.c
@@ -4,9 +4,10 @@
  *  Copyright (C) 1995-1997 Martin von Löwis
  */
 
-#include "types.h"
+#include "ntfstypes.h"
 #include "struct.h"
 #include "dir.h"
+#include "macros.h"
 
 #include <linux/errno.h>
 #include "super.h"
@@ -26,27 +27,27 @@
 				 ino->u.index.recordsize);
 }
 
-static inline int ntfs_is_top(long long stack)
+static inline int ntfs_is_top(ntfs_u64 stack)
 {
 	return stack==14;
 }
 
-static long long ntfs_pop(long long *stack)
+static int ntfs_pop(ntfs_u64 *stack)
 {
 	static int width[16]={1,2,1,3,1,2,1,4,1,2,1,3,1,2,1,-1};
 	int res=-1;
 	switch(width[*stack & 15])
 	{
-	case 1:res=(*stack&15)>>1;
+	case 1:res=(int)((*stack&15)>>1);
 		*stack>>=4;
 		break;
-	case 2:res=((*stack&63)>>2)+7;
+	case 2:res=(int)(((*stack&63)>>2)+7);
 		*stack>>=6;
 		break;
-	case 3:res=((*stack & 255)>>3)+23;
+	case 3:res=(int)(((*stack & 255)>>3)+23);
 		*stack>>=8;
 		break;
-	case 4:res=((*stack & 1023)>>4)+55;
+	case 4:res=(int)(((*stack & 1023)>>4)+55);
 		*stack>>=10;
 		break;
 	default:ntfs_error("Unknown encoding\n");
@@ -59,18 +60,18 @@
 	return 14;
 }
 
-static long long ntfs_push(long long stack,int i)
+static ntfs_u64 ntfs_push(ntfs_u64 stack,int i)
 {
 	if(i<7)return (stack<<4)|(i<<1);
 	if(i<23)return (stack<<6)|((i-7)<<2)|1;
 	if(i<55)return (stack<<8)|((i-23)<<3)|3;
 	if(i<120)return (stack<<10)|((i-55)<<4)|7;
 	ntfs_error("Too many entries\n");
-	return -1;
+	return 0xFFFFFFFFFFFFFFFF;
 }
 
 #if 0
-static void ntfs_display_stack(long long stack)
+static void ntfs_display_stack(ntfs_u64 stack)
 {
 	while(!ntfs_is_top(stack))
 	{
@@ -162,35 +163,30 @@
 	if(walk->newblock * vol->clustersize >= size){
 		/* build index record */
 		int s1=walk->dir->u.index.recordsize;
+		int nr_fix = s1/vol->blocksize+1;
+		int hsize;
 		char *record=ntfs_malloc(s1);
-		int newlen;
 		ntfs_bzero(record,s1);
 		/* magic */
 		ntfs_memcpy(record,"INDX",4);
 		/* offset to fixups */
 		NTFS_PUTU16(record+4,0x28);
 		/* number of fixups */
-		NTFS_PUTU16(record+6,s1/vol->blocksize+1);
+		NTFS_PUTU16(record+6,nr_fix);
 		/* FIXME: log file number */
 		/* VCN of buffer */
 		NTFS_PUTU64(record+0x10,walk->newblock);
-		/* header size. FIXME */
-		NTFS_PUTU16(record+0x18,28);
+		/* header size. */
+		hsize = 0x10+2*nr_fix;
+		hsize = (hsize+7) & ~7; /* Align. */
+		NTFS_PUTU16(record+0x18, hsize);
 		/* total size of record */
 		NTFS_PUTU32(record+0x20,s1-0x18);
+		/* Writing the data will extend the attribute. */
 		io.param=record;
-		newlen=walk->dir->u.index.recordsize;
-		/* allocate contiguous index record */
-		error=ntfs_extend_attr(walk->dir,allocation,&newlen,
-				       ALLOC_REQUIRE_SIZE);
-		if(error){
-			/* FIXME: try smaller allocations */
-			ntfs_free(record);
-			return ENOSPC;
-		}
 		io.size=s1;
-		error=ntfs_write_attr(walk->dir,vol->at_index_allocation,I30,
-				      size,&io);
+		io.do_read=0;
+		error=ntfs_readwrite_attr(walk->dir, allocation, size, &io);
 		if(error || io.size!=s1){
 			ntfs_free(record);
 			return error?error:EIO;
@@ -201,29 +197,38 @@
 	return 0;
 }
 
+/* Write an index block (root or allocation) back to storage.
+   used is the total number of bytes in buf, including all headers. */
+
 static int ntfs_index_writeback(ntfs_iterate_s *walk, ntfs_u8 *buf, int block,
-	int used)
+				int used)
 {
 	ntfs_io io;
 	int error;
+	ntfs_attribute *a;
+	ntfs_volume *vol = walk->dir->vol;
+	
 	io.fn_put=0;
 	io.fn_get=ntfs_get;
 	io.param=buf;
-	if(walk->block==-1){
+	if(block==-1){
 		NTFS_PUTU16(buf+0x14,used-0x10);
 		/* 0x18 is a copy thereof */
 		NTFS_PUTU16(buf+0x18,used-0x10);
 		io.size=used;
-		error=ntfs_write_attr(walk->dir,walk->dir->vol->at_index_root,
+		error=ntfs_write_attr(walk->dir,vol->at_index_root,
 				      I30,0,&io);
 		if(error)return error;
 		if(io.size!=used)return EIO;
+		/* shrink if necessary */
+		a = ntfs_find_attr(walk->dir, vol->at_index_root, I30);
+		ntfs_resize_attr(walk->dir, a, used);
 	}else{
-		NTFS_PUTU16(buf+0x1C,used-0x20);
-		ntfs_insert_fixups(buf,walk->dir->vol->blocksize);
+		NTFS_PUTU16(buf+0x1C,used-0x18);
+		ntfs_insert_fixups(buf,vol->blocksize);
 		io.size=walk->dir->u.index.recordsize;
-		error=ntfs_write_attr(walk->dir,walk->dir->vol->at_index_allocation,I30,
-				      walk->block*walk->dir->vol->clustersize,
+		error=ntfs_write_attr(walk->dir,vol->at_index_allocation,I30,
+				      block*vol->clustersize,
 				      &io);
 		if(error)return error;
 		if(io.size!=walk->dir->u.index.recordsize)
@@ -240,13 +245,20 @@
 	int error,othersize,mlen;
 	ntfs_io io;
 	ntfs_volume *vol=walk->dir->vol;
+	int oldblock;
+
 	error=ntfs_allocate_index_block(walk);
 	if(error)
 		return error;
-	for(entry=prev=start+NTFS_GETU16(start+0x18)+0x18;
-	    entry-start<usize/2;
-	    entry+=NTFS_GETU16(entry+8))
+	/* This should not happen */
+	if(walk->block == -1){
+		ntfs_error("Trying to split root");
+		return EOPNOTSUPP;
+	}
+	entry = start+NTFS_GETU16(start+0x18)+0x18; 
+	for(prev=entry; entry-start<usize/2; entry += NTFS_GETU16(entry+8))
 		prev=entry;
+
 	newbuf=ntfs_malloc(vol->index_recordsize);
 	if(!newbuf)
 		return ENOMEM;
@@ -266,11 +278,19 @@
 	/* copy everything from entry to new block */
 	othersize=usize-(entry-start);
 	ntfs_memcpy(newbuf+NTFS_GETU16(newbuf+0x18)+0x18,entry,othersize);
-	error=ntfs_index_writeback(walk,newbuf,walk->newblock,othersize);
+	/* Copy flags. */
+	NTFS_PUTU32(newbuf+0x24, NTFS_GETU32(start+0x24));
+	error=ntfs_index_writeback(walk,newbuf,walk->newblock,
+				   othersize+NTFS_GETU16(newbuf+0x18)+0x18);
 	if(error)goto out;
 
 	/* move prev to walk */
 	mlen=NTFS_GETU16(prev+0x8);
+	/* Remember old child node. */
+	if(ntfs_entry_has_subnodes(prev))
+		oldblock = NTFS_GETU32(prev+mlen-8);
+	else
+		oldblock = -1;
 	/* allow for pointer to subnode */
 	middle=ntfs_malloc(ntfs_entry_has_subnodes(prev)?mlen:mlen+8);
 	if(!middle){
@@ -286,8 +306,21 @@
 		ntfs_error("entry not reset");
 	walk->new_entry=middle;
 	walk->u.flags|=ITERATE_SPLIT_DONE;
+	/* Terminate old block. */
+	othersize = usize-(prev-start);
+	NTFS_PUTU64(prev, 0);
+	if(oldblock==-1){
+		NTFS_PUTU32(prev+8, 0x10);
+		NTFS_PUTU32(prev+0xC, 2);
+		othersize += 0x10;
+	}else{
+		NTFS_PUTU32(prev+8, 0x18);
+		NTFS_PUTU32(prev+0xC, 3);
+		NTFS_PUTU64(prev+0x10, oldblock);
+		othersize += 0x18;
+	}
 	/* write back original block */
-	error=ntfs_index_writeback(walk,start,walk->block,usize-(prev-start));
+	error=ntfs_index_writeback(walk,start,walk->block,othersize);
  out:
 	if(newbuf)ntfs_free(newbuf);
 	if(middle)ntfs_free(middle);
@@ -300,12 +333,11 @@
 	int do_split=0;
 	offset=entry-start;
 	if(walk->block==-1){ /*index root */
-		/* FIXME: adjust to maximum allowed index root value */
 		blocksize=walk->dir->vol->mft_recordsize;
 		usedsize=NTFS_GETU16(start+0x14)+0x10;
 	}else{
 		blocksize=walk->dir->u.index.recordsize;
-		usedsize=NTFS_GETU16(start+0x1C)+0x20;
+		usedsize=NTFS_GETU16(start+0x1C)+0x18;
 	}
 	if(usedsize+walk->new_entry_size > blocksize){
 		char* s1=ntfs_malloc(blocksize+walk->new_entry_size);
@@ -321,7 +353,6 @@
 	usedsize+=walk->new_entry_size;
 	ntfs_free(walk->new_entry);
 	walk->new_entry=0;
-	/*FIXME: split root */
 	if(do_split){
 		error=ntfs_split_record(walk,start,blocksize,usedsize);
 		ntfs_free(start);
@@ -330,6 +361,81 @@
 	return 0;
 }
 
+/* Try to split INDEX_ROOT attributes. Return E2BIG if nothing changed. */
+
+int
+ntfs_split_indexroot(ntfs_inode *ino)
+{
+	ntfs_attribute *ra;
+	ntfs_u8 *root=0, *index=0;
+	ntfs_io io;
+	int error, off, i, bsize, isize;
+	ntfs_iterate_s walk;
+
+	ra = ntfs_find_attr(ino, ino->vol->at_index_root, I30);
+	if(!ra)
+		return E2BIG;
+	bsize = ino->vol->mft_recordsize;
+	root = ntfs_malloc(bsize);
+	if(!root)
+		return E2BIG;
+	io.fn_put = ntfs_put;
+	io.param = root;
+	io.size = bsize;
+	error = ntfs_read_attr(ino, ino->vol->at_index_root, I30, 0, &io);
+	if(error)
+		goto out;
+	off = 0x20;
+	/* Count number of entries. */
+	for(i = 0; ntfs_entry_is_used(root+off); i++)
+		off += NTFS_GETU16(root+off+8);
+	if(i<=2){
+		/* We don't split small index roots. */
+		error = E2BIG;
+		goto out;
+	}
+	index = ntfs_malloc(ino->vol->index_recordsize);
+	if(!index)
+		goto out;
+	walk.dir = ino;
+	walk.block = -1;
+	walk.result = walk.new_entry = 0;
+	walk.name = 0;
+	error = ntfs_allocate_index_block(&walk);
+	if(error)
+		goto out;
+
+	/* Write old root to new index block. */
+	io.param = index;
+	io.size = ino->vol->index_recordsize;
+	error = ntfs_read_attr(ino, ino->vol->at_index_allocation, I30,
+			       walk.newblock*ino->vol->clustersize, &io);
+	if(error)
+		goto out;
+	isize = NTFS_GETU16(root+0x18) - 0x10;
+	ntfs_memcpy(index+NTFS_GETU16(index+0x18)+0x18, root+0x20, isize);
+	/* Copy flags. */
+	NTFS_PUTU32(index+0x24, NTFS_GETU32(root+0x1C));
+
+	error = ntfs_index_writeback(&walk, index, walk.newblock, 
+				     isize+NTFS_GETU16(index+0x18)+0x18);
+	if(error)
+		goto out;
+
+	/* Mark root as split. */
+	NTFS_PUTU32(root+0x1C, 1);
+	/* Truncate index root. */
+	NTFS_PUTU64(root+0x20, 0);
+	NTFS_PUTU32(root+0x28, 0x18);
+	NTFS_PUTU32(root+0x2C, 3);
+	NTFS_PUTU64(root+0x30, walk.newblock);
+	error = ntfs_index_writeback(&walk,root,-1,0x38);
+ out:
+	ntfs_free(root);
+	ntfs_free(index);
+	return error;
+}
+
 /* The entry has been found. Copy the result in the caller's buffer */
 static int ntfs_copyresult(char *dest,char *source)
 {
@@ -419,9 +525,9 @@
 	error=ntfs_getdir_record(walk,nextblock);
 	if(!error && walk->type==DIR_INSERT && 
 	   (walk->u.flags & ITERATE_SPLIT_DONE)){
-		/* split has occurred. adjust entry, insert new_entry */
+		/* Split has occurred. Adjust entry, insert new_entry. */
 		NTFS_PUTU32(entry+length-8,walk->newblock);
-		/* reset flags, as the current block might be split again */
+		/* Reset flags, as the current block might be split again. */
 		walk->u.flags &= ~ITERATE_SPLIT_DONE;
 		error=ntfs_dir_insert(walk,start,entry);
 	}
@@ -637,6 +743,7 @@
 		ntfs_error("Inode %d has no volume\n",ino->i_number);
 		return EINVAL;
 	}
+	ntfs_debug(DEBUG_DIR3,"unsorted 1\n");
 	/* are we still in the index root */
 	if(*p_high==0){
 		buf=ntfs_malloc(length=vol->mft_recordsize);
@@ -651,6 +758,7 @@
 		ino->u.index.recordsize = NTFS_GETU32(buf+0x8);
 		ino->u.index.clusters_per_record = NTFS_GETU32(buf+0xC);
 		entry=buf+0x20;
+		ntfs_debug(DEBUG_DIR3,"unsorted 2\n");
 	}else{ /* we are in an index record */
 		length=ino->u.index.recordsize;
 		buf=ntfs_malloc(length);
@@ -673,14 +781,17 @@
 			return ENOTDIR;
 		}
 		entry=buf+NTFS_GETU16(buf+0x18)+0x18;
+		ntfs_debug(DEBUG_DIR3,"unsorted 3\n");
 	}
 
 	/* process the entries */
 	start=*p_low;
 	while(ntfs_entry_is_used(entry)){
+		ntfs_debug(DEBUG_DIR3,"unsorted 4\n");
 		if(start)
 			start--; /* skip entries that were already processed */
 		else{
+			ntfs_debug(DEBUG_DIR3,"unsorted 5\n");
 			if((error=cb(entry,param)))
 				/* the entry could not be processed */
 				break;
@@ -689,9 +800,11 @@
 		entry+=NTFS_GETU16(entry+8);
 	}
 
+	ntfs_debug(DEBUG_DIR3,"unsorted 6\n");
 	/* caller did not process all entries */
 	if(error){
 		ntfs_free(buf);
+		ntfs_debug(DEBUG_DIR3,"unsorted 7\n");
 		return error;
 	}
 
@@ -704,6 +817,7 @@
 		/* directory does not have index allocation */
 		*p_high=0xFFFFFFFF;
 		*p_low=0;
+		ntfs_debug(DEBUG_DIR3,"unsorted 8\n");
 		return 0;
 	}
 	buf=ntfs_malloc(length=attr->size);
@@ -713,6 +827,7 @@
 	if(!error && io.size!=length)error=EIO;
 	if(error){
 		ntfs_free(buf);
+		ntfs_debug(DEBUG_DIR3,"unsorted 9\n");
 		return EIO;
 	}
 	attr=ntfs_find_attr(ino,vol->at_index_allocation,I30);
@@ -721,6 +836,7 @@
 			/* no more index records */
 			*p_high=0xFFFFFFFF;
 			ntfs_free(buf);
+			ntfs_debug(DEBUG_DIR3,"unsorted 10\n");
 			return 0;
 		}
 		*p_high+=ino->u.index.clusters_per_record;
@@ -731,6 +847,7 @@
 		if(buf[byte] & bit)
 			break;
 	}
+	ntfs_debug(DEBUG_DIR3,"unsorted 11\n");
 	ntfs_free(buf);
 	return 0;
 }
@@ -800,6 +917,62 @@
 	return error;
 }
 #endif
+
+/* Fills out and creates an INDEX_ROOT attribute. */
+
+static int
+add_index_root (ntfs_inode *ino, int type)
+{
+	ntfs_attribute *da;
+	ntfs_u8 data[0x30]; /* 0x20 header, 0x10 last entry */
+	char name[10];
+
+	NTFS_PUTU32(data, type);
+	/* ??? */
+	NTFS_PUTU32(data+4, 1);
+	NTFS_PUTU32(data+8, ino->vol->index_recordsize);
+	NTFS_PUTU32(data+0xC, ino->vol->index_clusters_per_record);
+	/* ??? */
+	NTFS_PUTU32(data+0x10, 0x10);
+	/* Size of entries, including header. */
+	NTFS_PUTU32(data+0x14, 0x20);
+	NTFS_PUTU32(data+0x18, 0x20);
+	/* No index allocation, yet. */
+	NTFS_PUTU32(data+0x1C, 0);
+	/* add last entry. */
+	/* indexed MFT record. */
+	NTFS_PUTU64(data+0x20, 0);
+	/* size of entry */
+	NTFS_PUTU32(data+0x28, 0x10);
+	/* flags: last entry, no child nodes. */
+	NTFS_PUTU32(data+0x2C, 2);
+	/* compute name */
+	ntfs_indexname(name, type);
+	return ntfs_create_attr(ino, ino->vol->at_index_root, name,
+				data, sizeof(data), &da);
+}
+
+int
+ntfs_mkdir(ntfs_inode* dir,const char* name,int namelen, ntfs_inode *result)
+{
+	int error;
+	error = ntfs_alloc_inode(dir, result, name, namelen, NTFS_AFLAG_DIR);
+	if(error)
+		goto out;
+	error = add_index_root(result, 0x30);
+	if (error)
+		goto out;
+	/* Set directory bit */
+	result->attr[0x16] |= 2;
+	error = ntfs_update_inode (dir);
+	if (error)
+		goto out;
+	error = ntfs_update_inode (result);
+	if (error)
+		goto out;
+ out:
+	return error;
+}
 
 /*
  * Local variables:

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov