diff options
Diffstat (limited to 'fs/ntfs/inode.c')
-rw-r--r-- | fs/ntfs/inode.c | 1202 |
1 files changed, 1202 insertions, 0 deletions
diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c new file mode 100644 index 000000000..a2a067f4c --- /dev/null +++ b/fs/ntfs/inode.c @@ -0,0 +1,1202 @@ +/* + * inode.c + * + * Copyright (C) 1995-1997 Martin von Löwis + * Copyright (C) 1996 Albert D. Cahalan + * Copyright (C) 1996-1997 Régis Duchesne + */ + +#include "types.h" +#include "struct.h" +#include "inode.h" + +#include <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; + +static void +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); + NTFS_PUTU16(mft + 0x12, 1); /* hard link count */ + NTFS_PUTU16(mft + 0x14, attr_offset); /* Offset to attributes */ + NTFS_PUTU16(mft + 0x16, 1); /*FIXME: flags ?? */ + NTFS_PUTU32(mft + 0x18, attr_offset + 0x08); /* In use */ + NTFS_PUTU32(mft + 0x1c, record_size); /* Total 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 */ +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) + { + if(!name && !ino->attrs[i].name) + return ino->attrs+i; + if(name && !ino->attrs[i].name) + return 0; + if(!name && ino->attrs[i].name) + return 0; + if(ntfs_ua_strncmp(ino->attrs[i].name,name,strlen(name))==0) + return ino->attrs+i; + } + if(type<ino->attrs[i].type) + return 0; + } + 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,block); + 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; + fill_mft_header(buf,vol->mft_recordsize,vol->blocksize,0); + ntfs_insert_fixups(buf,vol->blocksize); + io.param=buf; + io.size=vol->mft_recordsize; + 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; + return 0; +} + +/* Insert all attributes from the record mftno of the MFT in the inode ino */ +void ntfs_insert_mft_attributes(ntfs_inode* ino,char *mft,int mftno) +{ + int i; + char *it; + int type,len; + /* check for duplicate */ + for(i=0;i<ino->record_count;i++) + if(ino->records[i]==mftno) + return; + /* (re-)allocate space if necessary */ + if(ino->record_count % 8==0) + { + int *old=ino->records; + ino->records=ntfs_malloc((ino->record_count+8)*sizeof(int)); + if(old) { + for(i=0;i<ino->record_count;i++) + ino->records[i]=old[i]; + ntfs_free(old); + } + } + 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) + ntfs_insert_attribute(ino,it); + it+=len; + }while(type!=-1); /* attribute list ends with type -1 */ +} + +/* 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, void *alist, int *plen) +{ + char *mft; + int mftno,l,error; + int last_mft=-1; + int len=*plen; + mft=ntfs_malloc(ino->vol->mft_recordsize); + while(len>8) + { + l=NTFS_GETU16(alist+4); + if(l>len)break; + /* process an attribute description */ + mftno=NTFS_GETU32(alist+0x10); /* BUG: this is u64 */ + 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; + ntfs_insert_mft_attributes(ino,mft,mftno); + } + 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_OTHER, "load_attributes %x 1\n",ino->i_number); + ntfs_insert_mft_attributes(ino,ino->attr,ino->i_number); + ntfs_debug(DEBUG_OTHER, "load_attributes %x 2\n",ino->i_number); + alist=ntfs_find_attr(ino,vol->at_attribute_list,0); + ntfs_debug(DEBUG_OTHER, "load_attributes %x 3\n",ino->i_number); + if(!alist) + return; + ntfs_debug(DEBUG_OTHER, "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); + delta=0; + for(offset=0;datasize;datasize-=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_OTHER, "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_OTHER, "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); + 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_OTHER, "Init: 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_OTHER, "Init: 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; + /* match given name and attribute name if present, + make sure attribute name is Unicode */ + for(name_it=it+NTFS_GETU16(it+10);namelen; + name++,name_it+=2,namelen--) + if(*name_it!=*name || name_it[1])break; + if(!namelen)break; + } + it+=len; + }while(type!=-1); /* attribute list end 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, int *cluster, + int *ctype) +{ + unsigned char type=*(*data)++; + *ctype=0; + switch(type & 0xF) + { + case 1: *length=NTFS_GETU8(*data);(*data)++;break; + case 2: *length=NTFS_GETU16(*data); + *data+=2; + break; + case 3: *length = NTFS_GETU24(*data); + *data+=3; + break; + /* TODO: case 4-8 */ + default: + ntfs_error("Can't decode run type field %x\n",type); + return -1; + } + switch(type & 0xF0) + { + case 0: *ctype=2;break; + case 0x10: *cluster+=NTFS_GETS8(*data);(*data)++;break; + case 0x20: *cluster+=NTFS_GETS16(*data); + *data+=2; + break; + case 0x30: *cluster+=NTFS_GETS24(*data); + *data+=3; + break; + /* TODO: case 0x40-0x80 */ + default: + ntfs_error("Can't decode run type field %x\n",type); + return -1; + } + return 0; +} + +/* 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 datasize,rnum; + int cluster,s_cluster,vcn,len,l,chunk,copied; + int s_vcn; + int clustersize; + int error; + + clustersize=ino->vol->clustersize; + datasize=attr->size; + l=dest->size; + if(dest->do_read) + { + if(offset>=datasize){ + dest->size=0; + return 0; + } + if(offset+l>=datasize) + l=dest->size=datasize-offset; + }else { /* fixed by CSA: if writing beyond end, extend attribute */ + if (offset+l>datasize) { + error=ntfs_resize_attr(ino,attr,offset+l); + if(error) + return error; + } + } + if(attr->resident) + { + if(dest->do_read) + dest->fn_put(dest,attr->d.data+offset,l); + else + { + dest->fn_get(attr->d.data+offset,dest,l); + ntfs_update_inode(ino); + } + dest->size=l; + return 0; + } + 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)/* FIXME: maybe return failure */ + { + ntfs_error("Read error\n"); + dest->size=copied; + return 0; + } + 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=ntfs_find_attr(ino,ino->vol->at_data,0); + /* It's hard to give an error code */ + if(!data)return -1; + if(data->resident)return -1; + if(data->compressed)return -1; + if(data->size<vcn*ino->vol->clustersize)return -1; + + 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; +} + +int +layout_runs(ntfs_attribute *attr,char* rec,int* offs,int size) +{ + int i,cluster,rclus,len,offset,coffs; + 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+8>size) + return E2BIG; /* it might still fit, but this simplifies testing */ + if(len<0x100){ + *(rec+offset)|=1; + NTFS_PUTU8(rec+offset+1,len); + coffs=2; + }else if(len<0x10000){ + *(rec+offset)|=2; + NTFS_PUTU16(rec+offset+1,len); + coffs=3; + }else if(len<0x1000000){ + *(rec+offset)|=3; + NTFS_PUTU24(rec+offset+1,len); + coffs=4; + }else{ + *(rec+offset)|=4; + NTFS_PUTU32(rec+offset+1,len); + coffs=5; + } + + if(rl[i].cluster==0) /*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{ + *(rec+offset)|=0x40; + NTFS_PUTS32(rec+offset+coffs,rclus); + coffs+=4; + } + offset+=coffs; + if(rl[i].cluster) + cluster=rl[i].cluster; + } + *offs=offset; + return 0; +} + +static void +count_runs(ntfs_attribute *attr,char *buf) +{ + int 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_PUTU32(buf+0x10,first); + NTFS_PUTU32(buf+0x18,last); +} + +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); + /* FIXME: assign attribute ID??? */ + 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; + 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 pointes at the end of the data */ + if(error) + return error; + } + asize=(asize+7) & ~7; + NTFS_PUTU32(buf+4,asize); + *psize=asize; + return 0; +} + + + +/* Try to layout ino into store. Return 0 on success, + E2BIG if it does not fit, + ENOMEM if memory allocation problem, + EOPNOTSUP if beyound 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; +#if 0 + /* copy attribute header */ + ntfs_memcpy(rec+offset,attr->header, + min(sizeof(attr->header),size-offset)); /* consider overrun */ + if(attr->namelen) + /* named attributes are added later */ + return EOPNOTSUPP; + /* FIXME: assign attribute ID??? */ + if(attr->resident){ + asize=attr->size; + aoffset=NTFS_GETU16(rec+offset+0x14); + if(offset+aoffset+asize>size) + return E2BIG; + ntfs_memcpy(rec+offset+aoffset,attr->d.data,asize); + next=offset+aoffset+asize; + }else{ + count_runs(attr,rec+offset); + aoffset=NTFS_GETU16(rec+offset+0x20); + next=offset+aoffset; + error=layout_runs(attr,rec,&next,size); + if(error) + return error; + } + next=(next+7) & ~7; /* align to DWORD */ + NTFS_PUTU16(rec+offset+4,next-offset); + offset=next; +#endif + } + /* 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; +} + +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){ + /* should try: + make attributes non-resident + 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); +} + +/* We have to skip the 16 metafiles and the 8 reserved entries */ +static int +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; + + for (byte = 3; 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); + 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, 0x00); /* 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; +} + +static int +add_filename (ntfs_inode* ino, ntfs_inode* dir, + const unsigned char *filename, int length) +{ + unsigned char *position; + unsigned int size; + ntfs_time64_t now; + int count; + int error; + unsigned char* data; + ntfs_attribute *fn; + + /* work out the size */ + size = 0x42 + 2 * length; + data = ntfs_malloc(size); + 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 */ + + /* Don't know */ + NTFS_PUTU8(position+0x38, 0x0); /*should match standard attributes*/ + + NTFS_PUTU8(position + 0x40, length); /* Filename length */ + NTFS_PUTU8(position + 0x41, 0x0); /* only long name */ + + 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, char *filename, + int namelen) +{ + ntfs_io io; + int error; + ntfs_u8 buffer[1]; + ntfs_volume* vol=dir->vol; + int byte,bit; + + error=new_inode (vol,&(result->i_number)); + if(error==ENOSPC){ + error=ntfs_extend_mft(vol); + if(error)return error; + error=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; + */ + /* get the sequence number */ + io.param = buffer; + io.size = 0x10; + error = ntfs_read_attr(vol->mft_ino, vol->at_data, 0, + result->i_number*vol->mft_recordsize+0x10,&io); + if(error) + return error; + result->sequence_number=NTFS_GETU16(buffer)+1; + result->vol=vol; + result->attr=ntfs_malloc(vol->mft_recordsize); + result->attr_count=0; + result->attrs=0; + result->record_count=1; + result->records=ntfs_malloc(8*sizeof(int)); + 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); + if(error) + return error; + error=add_security(result,dir); + /*FIXME: check error */ + error=add_data(result,0,0); + if(error) + return error; + return 0; +} + +/* + * Local variables: + * c-file-style: "linux" + * End: + */ |