📄 inode.c
字号:
/* * inode.c * * Copyright (C) 1995-1999 Martin von L鰓is * Copyright (C) 1996 Albert D. Cahalan * Copyright (C) 1996-1997 R間is Duchesne * Copyright (C) 1998 Joseph Malicki * Copyright (C) 1999 Steve Dodd */#include "ntfstypes.h"#include "ntfsendian.h"#include "struct.h"#include "inode.h"#include <errno.h>#ifdef HAVE_STRING_H#include <string.h>#endif#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;voidntfs_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,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; 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 */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 *new; new = ntfs_malloc((ino->record_count+8)*sizeof(int)); if( !new ) return; 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) { /* FIXME: check ntfs_insert_attribute for failure (e.g. no mem)? */ 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, 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); /* 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_FILE2, "load_attributes %x 1\n",ino->i_number); ntfs_insert_mft_attributes(ino,ino->attr,ino->i_number); 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 ) return; 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_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: 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: 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, ntfs_cluster_t *cluster, int *ctype){ unsigned char type=*(*data)++; *ctype=0; switch(type & 0xF) { case 1: *length=NTFS_GETU8(*data);break; case 2: *length=NTFS_GETU16(*data);break; case 3: *length=NTFS_GETU24(*data);break; case 4: *length=NTFS_GETU32(*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; } *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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -