/* This file is part of 'minixfs' Copyright 1991,1992,1993 S.N. Henson */ #include "minixfs.h" #include "proto.h" #include "global.h" int read_zone(num,buf,drive,control) long num ; void *buf; int drive; cache_control *control; { if(num) bcopy(cget_block(num,drive,control),buf,(size_t)BLOCK_SIZE); else bzero(buf,(size_t)BLOCK_SIZE); return(0); } /* Only ever used for directories so always syscache */ bufr *get_zone(num,drive) long num ; int drive; { return(cget_block(num,drive,&syscache)); } /* Zone cache stuff (same as cache stuff but with checking) */ cache *cget_zone(num,drive,control,guess) long num; int drive; cache_control *control; cache **guess; { return (cache_get(num,drive,control,guess)); } cache *cput_zone(num,drive,control) long num; int drive; cache_control *control; { return (cache_put(num,drive,control)); } int write_zone(num,buf,drive,control) long num ; void *buf; int drive; cache_control *control; { cput_block(num,drive,buf,control); return(0); } /* This is a 'clever' write_zone which recognises consecutive blocks and * queues requests until it gets one out of sequence.This allows large * i/o requests to be done with a single Rwabs for consecutive blocks * which is much faster than lots of little ones. */ int write_zones(num,buf,drive,control) long num; void *buf; int drive; cache_control *control; { static void *qstart,*qnext; static long nstart,nnext,count; static short qdrive=-1; cache *p; int i; if( (p=in_cache(num,drive,control,NOGUESS)) ) { bcopy(buf,p->buffer,(size_t)BLOCK_SIZE); p->status=1; } if(buf!=qnext || nnext!=num || qdrive!=drive || count > MAX_RWS ) { /* Flush out queue */ if(qdrive!=-1) { chk_zone(nstart,count,drive); if(count<8) for(i=0;i<count;i++) write_zone(nstart+i,qstart+(i<<L_BS),qdrive,control); else crwabs(3,qstart,count,nstart,qdrive); } qdrive=drive; qstart=buf; qnext=buf+BLOCK_SIZE; nstart=num; nnext=num+1; count=1; } else { qnext+=BLOCK_SIZE; count++; nnext++; } return 0; } /* This is an equivalent for read ... but this is a bit harder as it is * not obvious what to do with the cache . What we finally do is to * always get data from the cache if we can , though this could easily * turn a large consecutive i/o request into lots of little ones . The * cache is not filled from the new read unless we are only reading * 1 zone ... basically this assumes that if the user reads several zones * then the program will be doing doing some sort of cacheing itself . */ int read_zones(num,buf,drive,control) long num; void *buf; int drive; cache_control *control; { static void *qstart,*qnext; static long nstart,nnext,count; static short qdrive=-1; cache *p; /* Read from cache if necessary */ if( (p=in_cache(num,drive,control,NOGUESS)) ) { bcopy(p->buffer,buf,(size_t)BLOCK_SIZE); drive=-1; /* Force flush of queued entries */ } if( qdrive!=drive || buf!=qnext || nnext!=num || (count > MAX_RWS) ) { /* Flush out queue */ if(qdrive!=-1) { if(count==1) read_zone(nstart,qstart,qdrive,control); else { if(nnext) crwabs(2,qstart,count,nstart,qdrive); else bzero(qstart,count*(size_t)BLOCK_SIZE); } } qdrive=drive; qstart=buf; qnext=buf; nstart=num; nnext=num; count=0; } if(qdrive!=-1) { qnext+=BLOCK_SIZE; count++; if(nnext)nnext++; } return 0; } /* This routine finds the zone 'numr' of an inode , traversing indirect * and double indirect zones as required if flag!=0 zones are added as * required . Having two filesystem versions makes this a bit trickier, in * fact although using a single routine looks more elegant it is slow, * so two versions are used. */ long find_zone(rip,numr,drive,flag) d_inode *rip; long numr; int drive; int flag; { super_info *psblk=super_ptr[drive]; return psblk->version ? find_zone2(rip,numr,drive,flag,&dummy) : find_zone1(rip,numr,drive,flag); } long find_zone11(rip,numr,drive,flag,fch) d_inode1 *rip; long numr; int drive; int flag; f_cache *fch; { long temp_zone; unshort *zptr,*zptr2; cache *tmp,*tmp2; /* Past EOF ? */ if(!flag && (numr*BLOCK_SIZE >= rip->i_size) ) return(0); /* Zone in inode ? */ if(numr < NR_DZONE_NUM) { temp_zone=rip->i_zone[numr]; if(temp_zone || !flag ) return temp_zone; return(rip->i_zone[numr]=alloc_zone(drive)); } numr-=NR_DZONE_NUM; /* In indirect zone then ? */ if(numr < NR_INDIRECTS) { if(rip->i_zone[7]) { tmp= cget_zone(rip->i_zone[7],drive,&syscache,&fch->izguess); zptr=&tmp->buffer->bind1[numr]; if( *zptr || !flag )return *zptr; if( (*zptr=alloc_zone(drive)) ) tmp->status=2; return *zptr; } else { if(!flag || !(rip->i_zone[7]=alloc_zone(drive))) return 0; tmp=cput_zone(rip->i_zone[7],drive,&syscache); fch->izguess=tmp; bzero(tmp->buffer,(size_t)BLOCK_SIZE); return tmp->buffer->bind1[numr]=alloc_zone(drive); } } /* Erk double indirect .... */ numr-=NR_INDIRECTS; { if(rip->i_zone[8]) { tmp2=cget_zone(rip->i_zone[8],drive,&syscache,&fch->dizguess); zptr2=&tmp2->buffer->bind1[numr>>LNR_IND]; if(*zptr2) { tmp=cget_zone(*zptr2,drive,&syscache,&fch->izguess); zptr=&tmp->buffer->bind1[numr & (NR_INDIRECTS-1)]; if(*zptr || !flag)return *zptr; if( (*zptr=alloc_zone(drive)) ) tmp->status=2; return *zptr; } else { if(!flag ||!(*zptr2=alloc_zone(drive)) )return 0; tmp2->status=2; tmp=cput_zone(*zptr2,drive,&syscache); fch->izguess=tmp; bzero(tmp->buffer,(size_t)BLOCK_SIZE); zptr=&tmp->buffer->bind1[numr & (NR_INDIRECTS-1)]; return *zptr=alloc_zone(drive); } } if(!flag || !(rip->i_zone[8]=alloc_zone(drive)) ) return 0; tmp2=cput_zone(rip->i_zone[8],drive,&syscache); fch->dizguess=tmp2; bzero(tmp2->buffer,(size_t)BLOCK_SIZE); zptr2=&tmp2->buffer->bind1[numr>>LNR_IND]; if(!(*zptr2=alloc_zone(drive))) return 0; tmp=cput_zone(*zptr2,drive,&syscache); fch->izguess=tmp; bzero(tmp->buffer,(size_t)BLOCK_SIZE); zptr=&tmp->buffer->bind1[numr & (NR_INDIRECTS-1)]; return *zptr=alloc_zone(drive) ; } } long find_zone1(rip,numr,drive,flag) d_inode *rip; long numr; int drive; int flag; { long temp_zone; unshort *zptr,*zptr2; cache *tmp,*tmp2; /* Past EOF ? */ if(!flag && (numr*BLOCK_SIZE >= rip->i_size) ) return(0); /* Zone in inode ? */ if(numr < NR_DZONE_NUM) { temp_zone=rip->i_zone[numr]; if(temp_zone || !flag ) return temp_zone; return(rip->i_zone[numr]=alloc_zone(drive)); } numr-=NR_DZONE_NUM; /* In indirect zone then ? */ if(numr < NR_INDIRECTS) { if(rip->i_zone[7]) { tmp=cget_zone(rip->i_zone[7],drive,&syscache,NOGUESS); zptr=&tmp->buffer->bind1[numr]; if( *zptr || !flag )return *zptr; if( (*zptr=alloc_zone(drive)) ) tmp->status=2; return *zptr; } else { if(!flag || !(rip->i_zone[7]=alloc_zone(drive))) return 0; tmp=cput_zone(rip->i_zone[7],drive,&syscache); bzero(tmp->buffer,(size_t)BLOCK_SIZE); return tmp->buffer->bind1[numr]=alloc_zone(drive); } } /* Erk double indirect .... */ numr-=NR_INDIRECTS; { if(rip->i_zone[8]) { tmp2=cget_zone(rip->i_zone[8],drive,&syscache,NOGUESS); zptr2=&tmp2->buffer->bind1[numr>>LNR_IND]; if(*zptr2) { tmp=cget_zone(*zptr2,drive,&syscache,NOGUESS); zptr=&tmp->buffer->bind1[numr & (NR_INDIRECTS-1)]; if(*zptr || !flag)return *zptr; if( (*zptr=alloc_zone(drive)) ) tmp->status=2; return *zptr; } else { if(!flag ||!(*zptr2=alloc_zone(drive)) )return 0; tmp2->status=2; tmp=cput_zone(*zptr2,drive,&syscache); bzero(tmp->buffer,(size_t)BLOCK_SIZE); zptr=&tmp->buffer->bind1[numr & (NR_INDIRECTS-1)]; return *zptr=alloc_zone(drive); } } if(!flag || !(rip->i_zone[8]=alloc_zone(drive)) ) return 0; tmp2=cput_zone(rip->i_zone[8],drive,&syscache); bzero(tmp2->buffer,(size_t)BLOCK_SIZE); zptr2=&tmp2->buffer->bind1[numr>>LNR_IND]; if(!(*zptr2=alloc_zone(drive))) return 0; tmp=cput_zone(*zptr2,drive,&syscache); bzero(tmp->buffer,(size_t)BLOCK_SIZE); zptr=&tmp->buffer->bind1[numr & (NR_INDIRECTS-1)]; return *zptr=alloc_zone(drive) ; } } long find_zone2(rip,numr,drive,flag,fch) d_inode *rip; long numr; int drive; int flag; f_cache *fch; { long temp_zone; long *zptr,*zptr2; cache *tmp,*tmp2; /* Past EOF ? */ if((numr*BLOCK_SIZE >= rip->i_size) && !flag ) return(0); /* Zone in inode ? */ if(numr < NR_DZONE_NUM2) { temp_zone=rip->i_zone[numr]; if(temp_zone || !flag ) return temp_zone; return(rip->i_zone[numr]=alloc_zone(drive)); } numr-=NR_DZONE_NUM2; /* In indirect zone then ? */ if(numr < NR_INDIRECTS2) { if(rip->i_zone[7]) { tmp= cget_zone(rip->i_zone[7],drive,&syscache,&fch->izguess); zptr=&tmp->buffer->bind[numr]; if( *zptr || !flag )return *zptr; if( (*zptr=alloc_zone(drive)) ) tmp->status=2; return *zptr; } else { if(!flag || !(rip->i_zone[7]=alloc_zone(drive))) return 0; tmp=cput_zone(rip->i_zone[7],drive,&syscache); bzero(tmp->buffer,(size_t)BLOCK_SIZE); return tmp->buffer->bind[numr]=alloc_zone(drive); } } /* Erk double indirect .... */ numr-=NR_INDIRECTS2; { if(rip->i_zone[8]) { tmp2=cget_zone(rip->i_zone[8],drive,&syscache,&fch->dizguess); zptr2=&tmp2->buffer->bind[numr>>LNR_IND2]; if(*zptr2) { tmp= cget_zone(*zptr2,drive,&syscache,&fch->izguess); zptr=&tmp->buffer->bind[numr & (NR_INDIRECTS2-1)]; if(*zptr || !flag)return *zptr; if( (*zptr=alloc_zone(drive)) ) tmp->status=2; return *zptr; } else { if(!flag ||!(*zptr2=alloc_zone(drive)) )return 0; tmp2->status=2; tmp=cput_zone(*zptr2,drive,&syscache); bzero(tmp->buffer,(size_t)BLOCK_SIZE); zptr=&tmp->buffer->bind[numr & (NR_INDIRECTS2-1)]; return *zptr=alloc_zone(drive); } } if(!flag || !(rip->i_zone[8]=alloc_zone(drive)) ) return 0; tmp2=cput_zone(rip->i_zone[8],drive,&syscache); bzero(tmp2->buffer,(size_t)BLOCK_SIZE); zptr2=&tmp2->buffer->bind[numr>>LNR_IND2]; if(!(*zptr2=alloc_zone(drive))) return 0; tmp=cput_zone(*zptr2,drive,&syscache); bzero(tmp->buffer,(size_t)BLOCK_SIZE); zptr=&tmp->buffer->bind[numr & (NR_INDIRECTS-1)]; return *zptr=alloc_zone(drive) ; } } /* This reads zone number 'numr' of an inode . * It returns the actual number of valid characters in 'numr' , this is only * used for directories so it is hard-coded for the system cache. */ int next_zone(rip,numr,buf,drive) d_inode *rip; long numr; void *buf; int drive; { long ztemp; long ret; ret=min(rip->i_size-numr*BLOCK_SIZE,BLOCK_SIZE); if(ret <= 0)return 0; ztemp=find_zone(rip,numr,drive,0); read_zone(ztemp,buf,drive,&syscache); return (int)ret; } /* As above but reads in cache pointer */ int cnext_zone(rip,numr,buf,drive) d_inode *rip; long numr; bufr **buf; int drive; { long ztemp; long ret; ret=min(rip->i_size-numr*BLOCK_SIZE,BLOCK_SIZE); if(ret <= 0)return 0; ztemp=find_zone(rip,numr,drive,0); *buf=get_zone(ztemp,drive); return (int)ret; } /* l_write is used internally for doing things a normal user cannot such * as writing to a directory ... it accepts 5 parameters , an inode num * a position (current position of write) a count which is the number of * characters to write,a buffer and a drive , it updates i_size as needed * and allocates zones as required , it is nastier than a read because it * has to write partial blocks within valid blocks and to write beyond EOF */ long l_write(inum,pos,len,buf,drive) unsigned inum; long pos; long len; const void *buf; int drive; { return super_ptr[drive]->version ? l_write2(inum,pos,len,buf,drive) : l_write1(inum,pos,len,buf,drive); } long l_write1(inum,pos,len,buf,drive) unsigned inum; long pos; long len; const void *buf; int drive; { register const void *p = buf; /* Current position in buffer */ d_inode1 *rip; long chunk; long left=len; long zne; cache_control *control; int *status; rip=get_inode1(inum,drive,&status,NOGUESS); /* Work out which cache to use */ control = IS_DIR((*rip)) ? &syscache : &usrcache; if(pos==-1l) pos=rip->i_size; /* If pos==-1 append */ chunk=pos/BLOCK_SIZE; while(left) /* Loop while characters remain to be written */ { long zoff; ushort wleft; cache *cp; zne=find_zone11(rip,chunk++,drive,1,&dummy); /* Current zone in file */ if(zne==0) break; /* Partition full */ zoff = pos & (BLOCK_SIZE -1); /* Current zone position */ wleft=min(BLOCK_SIZE-zoff,left); /*Left to write in curr blk*/ if((zoff) || ( (left < BLOCK_SIZE) && (pos+left<rip->i_size))) { cp=cget_zone(zne,drive,control,NOGUESS); cp->status=2; } else { cp=cput_zone(zne,drive,control); if(wleft!=BLOCK_SIZE)bzero(cp->buffer->bdata,(size_t)BLOCK_SIZE); } bcopy(p,&cp->buffer->bdata[zoff],(size_t)wleft); pos+=wleft; p+=wleft; if(pos>rip->i_size)rip->i_size=pos; left-=wleft; } rip->i_mtime=Unixtime(Timestamp(), Datestamp()); *status=2; return(len-left); } long l_write2(inum,pos,len,buf,drive) unsigned inum; long pos; long len; const void *buf; int drive; { register const void *p = buf; /* Current position in buffer */ d_inode *rip; long chunk; long left=len; long zne; cache_control *control; int *status; rip=get_inode2(inum,drive,&status,NOGUESS); /* Work out which cache to use */ control = IS_DIR((*rip)) ? &syscache : &usrcache; if(pos==-1l) pos=rip->i_size; /* If pos==-1 append */ chunk=pos/BLOCK_SIZE; while(left) /* Loop while characters remain to be written */ { long zoff; ushort wleft; cache *cp; zne=find_zone2(rip,chunk++,drive,1,&dummy); /* Current zone in file */ if(zne==0)break; /* Partition full */ zoff = pos & (BLOCK_SIZE -1); /* Current zone position */ wleft=min(BLOCK_SIZE-zoff,left); /*Left to write in curr blk*/ if((zoff) || ( (left < BLOCK_SIZE) && (pos+left<rip->i_size))) { cp=cget_zone(zne,drive,control,NOGUESS); cp->status=2; } else { cp=cput_zone(zne,drive,control); if(wleft!=BLOCK_SIZE)bzero(cp->buffer->bdata,(size_t)BLOCK_SIZE); } bcopy(p,&cp->buffer->bdata[zoff],(size_t)wleft); pos+=wleft; p+=wleft; if(pos>rip->i_size)rip->i_size=pos; left-=wleft; } rip->i_mtime=Unixtime(Timestamp(), Datestamp()); *status=2; return(len-left); }