/* This file is part of 'minixfs' Copyright 1991,1992,1993 S.N.Henson */ #include "minixfs.h" #include "proto.h" #include "global.h" /* Inode routines */ /* Read an inode from the cache , if the filesystem is V1 convert to V2 * first , this greatly simplifies matters as other routines can then deal * exclusively with V2 inodes. */ int read_inode(num,rip,drv) unsigned num; d_inode *rip; int drv; { bufr *tmp; super_info *psblk=super_ptr[drv]; num-=1; tmp=cget_block(num/psblk->ipb+psblk->ioff,drv,&icache); if(psblk->version) *rip=tmp->binode[num%psblk->ipb]; else { d_inode1 *oldrip; int i; oldrip=&tmp->binode1[num%psblk->ipb]; /* Convert V1 inode to V2 */ rip->i_mode=oldrip->i_mode; rip->i_nlinks=oldrip->i_nlinks; rip->i_uid=oldrip->i_uid; rip->i_gid=oldrip->i_gid; rip->i_size=oldrip->i_size; rip->i_atime=oldrip->i_mtime; rip->i_mtime=oldrip->i_mtime; rip->i_ctime=oldrip->i_mtime; for(i=0;i< NR_ZONE_NUMS;i++) rip->i_zone[i]=oldrip->i_zone[i]; rip->i_zone[NR_ZONE_NUMS]=0; } return(0); } /* Write out an inode , assuming it is V2. If the filesystem is V1 it converts * the supplied inode first. */ int write_inode(num,rip,drv) unsigned num; d_inode *rip; int drv; { cache *tmp; super_info *psblk=super_ptr[drv]; num-=1; tmp=cache_get(num/psblk->ipb+psblk->ioff,drv,&icache,NOGUESS); if(psblk->version) tmp->buffer->binode[num%psblk->ipb]=*rip; else { d_inode1 *oldrip; int i; oldrip=&tmp->buffer->binode1[num%psblk->ipb]; /* Convert V2 inode to V1 */ oldrip->i_mode=rip->i_mode; oldrip->i_nlinks=rip->i_nlinks; oldrip->i_uid=rip->i_uid; oldrip->i_gid=rip->i_gid; oldrip->i_size=rip->i_size; oldrip->i_mtime=rip->i_mtime; for(i=0;ii_zone[i]=rip->i_zone[i]; } tmp->status=2; return(0); } /* 'get' an inode from the cache, return a pointer to the inode and * if the pointer 'flag' is non-zero, return a pointer to the 'status' * flag as well. Need two versions for this as well. */ d_inode1 *get_inode1(inum,drive,flag,guess) unsigned inum; int drive; int **flag; cache **guess; { cache *tmp; super_info *psblk=super_ptr[drive]; inum-=1; tmp=cache_get((inum>>L_IPB)+psblk->ioff,drive,&icache,guess); if(flag) *flag=&tmp->status; return &tmp->buffer->binode1[inum & (INODES_PER_BLOCK-1)] ; } d_inode *get_inode2(inum,drive,flag,guess) unsigned inum; int drive; int **flag; cache **guess; { cache *tmp; super_info *psblk=super_ptr[drive]; inum-=1; tmp=cache_get((inum>>L_IPB2)+psblk->ioff,drive,&icache,guess); if(flag) *flag=&tmp->status; return &tmp->buffer->binode[inum & (INODES_PER_BLOCK2-1)] ; } /* Truncate an inode to 'count' zones, this is used by unlink() as well as * (f)truncate() . Bit tricky this , we have to note which blocks to free, * and free indirection/double indirection blocks too but iff all the blocks * inside them are free too. We also need to keep count of how much to leave * alone , sparse files complicate this a bit .... so do 2 fs versions .... */ void trunc_inode(rip,drive,count,zap) d_inode *rip; int drive; long count; /* number of blocks to leave */ int zap; /* flag to alter inode */ { int i,j; bufr *tmp; char some,dirty; super_info *psblk=super_ptr[drive]; char vers; cache_control *control = (IS_DIR((*rip))||IS_SYM((*rip))) ? &syscache : &usrcache; cache *p, *q, *guess = control->start; vers=psblk->version; /* Handle zones in inode first */ if(countdzpi) { for(i=count;idzpi;i++) { if(rip->i_zone[i]) { /* remove zones from cache too so they can't end up in both ones when later reallocated... also may save a few needless writes. -nox */ if( (p=in_cache(rip->i_zone[i],drive,control,&guess)) ) p->status=0; free_zone(rip->i_zone[i],drive); } if(zap)rip->i_zone[i]=0; } count=0; } else count-=psblk->dzpi; /* Handle indirect zone */ if(count< psblk->zpind) { some=0; dirty=0; if(rip->i_zone[7]) { tmp=(q=cache_get(rip->i_zone[7],drive,&syscache,NOGUESS))->buffer; for(i=0;izpind;i++) { if(PIND(vers,tmp,i)) { if(count)some=1; else { if( (p=in_cache(PIND(vers,tmp,i),drive,control,&guess)) ) p->status=0; free_zone(PIND(vers,tmp,i),drive); if(zap)PIND(vers,tmp,i)=0; dirty=1; } } if(count)count--; } if(!some) { q->status=0; free_zone(rip->i_zone[7],drive); if(zap)rip->i_zone[7]=0; } else if(dirty) #if 1 /* a bit faster :) */ q->status=2; #else write_zone(rip->i_zone[7],tmp,drive,&syscache); #endif } } else count-=psblk->zpind; /* Handle double indirect ... */ if (count < (long) psblk->zpind * psblk->zpind) { if(rip->i_zone[8]) { some=0; dirty=0; read_zone(rip->i_zone[8],&temp,drive,&syscache); for(i=0;izpind;i++) { if(IND(vers,temp,i)) { char lsome,ldirty; /* local some,dirty for inds */ lsome=0; ldirty=0; tmp=(q=cache_get(IND(vers,temp,i),drive,&syscache,NOGUESS))->buffer; for(j=0;jzpind;j++) { if(PIND(vers,tmp,j)) { if(count) { some=1; lsome=1; } else { if( (p=in_cache(PIND(vers,tmp,j),drive,control,&guess)) ) p->status=0; free_zone(PIND(vers,tmp,j),drive); if(zap)PIND(vers,tmp,j)=0; ldirty=1; } } if(count)count--; } if(!lsome) { q->status=0; free_zone(IND(vers,temp,i),drive); if(zap)IND(vers,temp,i)=0; dirty=1; } else if(ldirty) #if 1 q->status=2; #else write_zone(IND(vers,temp,i),tmp,drive,&syscache); #endif } else { if(count>=psblk->zpind)count-=psblk->zpind; else count=0; } } if(!some) { if( (p=in_cache(rip->i_zone[8],drive,&syscache,NOGUESS)) ) p->status=0; free_zone(rip->i_zone[8],drive); if(zap)rip->i_zone[8]=0; } else if(dirty)write_zone(rip->i_zone[8],&temp,drive,&syscache); } } else count -= (long) psblk->zpind * psblk->zpind; /* Handle triple indirect ... */ if (rip->i_zone[9]) { some = 0; dirty = 0; read_zone (rip->i_zone[9], &temp, drive, &syscache); for (i = 0; i < psblk->zpind; i++) { if (IND (vers, temp, i)) { char lsome, ldirty; /* local some, dirty for inds */ lsome = 0; ldirty = 0; q = cache_get (IND (vers, temp, i), drive, &syscache, NOGUESS); tmp = q->buffer; for (j = 0; j < psblk->zpind; j++) { if (PIND (vers, tmp, j)) { char lsome1, ldirty1; int k; bufr *tmp2; cache *r; lsome1 = 0; ldirty1 = 0; r = cache_get (PIND (vers, tmp, j), drive, &syscache, NOGUESS); tmp2 = q->buffer; for (k = 0; k < psblk->zpind; k++) { if (PIND (vers, tmp2, k)) { if (count) { some = 1; lsome = 1; lsome1 = 1; } else { p = in_cache (PIND (vers, tmp2, k), drive, control, &guess); if (p) p->status = 0; free_zone (PIND (vers, tmp2, k), drive); if (zap) PIND (vers, tmp2, k) = 0; ldirty1 = 1; } } if (count) count--; } if (!lsome1) { r->status = 0; free_zone (PIND (vers, tmp, j), drive); if (zap) PIND (vers, tmp, j) = 0; ldirty = 1; } else if (ldirty1) r->status = 2; } } if (!lsome) { q->status = 0; free_zone (IND (vers, temp, i), drive); if (zap) IND (vers, temp, i) = 0; dirty = 1; } else if (ldirty) q->status = 2; } else { if (count >= psblk->zpind) count -= psblk->zpind; else count = 0; } } if (!some) { p = in_cache (rip->i_zone[9], drive, &syscache, NOGUESS); if (p) p->status = 0; free_zone (rip->i_zone[9], drive); if (zap) rip->i_zone[9] = 0; } else if (dirty) write_zone (rip->i_zone[9], &temp, drive, &syscache); } } /* Inode version of (f)truncate , truncates a file to size 'length' */ long itruncate(inum,drive,length) unsigned inum; int drive; long length; { long count; d_inode rip; FILEPTR *p; read_inode(inum,&rip,drive); /* Regulars only , clever directory compaction stuff later ... */ if(!IS_REG(rip))return EACCDN; /* If file smaller than 'length' nothing to do */ if(rip.i_size <= length) { #if 0 rip.i_size=length; #endif return 0; } count=(length+1023)/1024; /* invalidate f_cache zones */ for(p=firstptr;p;p=p->next) if((drive==p->fc.dev) && (inum==p->fc.index) && ((f_cache *) p->devinfo)->lzone > count) ((f_cache *) p->devinfo)->lzone = 0; trunc_inode(&rip,drive,count,1); rip.i_size=length; write_inode(inum,&rip,drive); if(cache_mode) l_sync(); return 0; }