xref: /plan9/sys/src/cmd/cfs/file.c (revision 41fb754a868aff65c81b63b0b07ae1fab2010fd9)
13e12c5d1SDavid du Colombier #include <u.h>
23e12c5d1SDavid du Colombier #include <libc.h>
33e12c5d1SDavid du Colombier #include "cformat.h"
43e12c5d1SDavid du Colombier #include "lru.h"
53e12c5d1SDavid du Colombier #include "bcache.h"
63e12c5d1SDavid du Colombier #include "disk.h"
73e12c5d1SDavid du Colombier #include "inode.h"
83e12c5d1SDavid du Colombier #include "file.h"
93e12c5d1SDavid du Colombier 
103e12c5d1SDavid du Colombier /*
113e12c5d1SDavid du Colombier  *  merge data with that which already exists in a block
123e12c5d1SDavid du Colombier  *
133e12c5d1SDavid du Colombier  *  we allow only one range per block, always use the new
143e12c5d1SDavid du Colombier  *  data if the ranges don't overlap.
153e12c5d1SDavid du Colombier  */
163e12c5d1SDavid du Colombier void
fmerge(Dptr * p,char * to,char * from,int start,int len)173e12c5d1SDavid du Colombier fmerge(Dptr *p, char *to, char *from, int start, int len)
183e12c5d1SDavid du Colombier {
193e12c5d1SDavid du Colombier 	int end;
203e12c5d1SDavid du Colombier 
213e12c5d1SDavid du Colombier 	end = start + len;
223e12c5d1SDavid du Colombier 	memmove(to+start, from, end-start);
233e12c5d1SDavid du Colombier 
243e12c5d1SDavid du Colombier 	/*
253e12c5d1SDavid du Colombier 	 *  if ranges do not overlap...
263e12c5d1SDavid du Colombier 	 */
273e12c5d1SDavid du Colombier 	if(start>p->end || p->start>end){
283e12c5d1SDavid du Colombier 		/*
293e12c5d1SDavid du Colombier 		 *  just use the new data
303e12c5d1SDavid du Colombier 		 */
313e12c5d1SDavid du Colombier 		p->start = start;
323e12c5d1SDavid du Colombier 		p->end = end;
333e12c5d1SDavid du Colombier 	} else {
343e12c5d1SDavid du Colombier 		/*
353e12c5d1SDavid du Colombier 		 *  merge ranges
363e12c5d1SDavid du Colombier 		 */
373e12c5d1SDavid du Colombier 		if(start < p->start)
383e12c5d1SDavid du Colombier 			p->start = start;
393e12c5d1SDavid du Colombier 		if(end > p->end)
403e12c5d1SDavid du Colombier 			p->end = end;
413e12c5d1SDavid du Colombier 	}
423e12c5d1SDavid du Colombier 
433e12c5d1SDavid du Colombier }
443e12c5d1SDavid du Colombier 
453e12c5d1SDavid du Colombier /*
463e12c5d1SDavid du Colombier  *  write a block (or less) of data onto a disk, follow it with any necessary
473e12c5d1SDavid du Colombier  *  pointer writes.
483e12c5d1SDavid du Colombier  *
493e12c5d1SDavid du Colombier  *	N.B. ordering is everything
503e12c5d1SDavid du Colombier  */
513e12c5d1SDavid du Colombier int
fbwrite(Icache * ic,Ibuf * b,char * a,ulong off,int len)523e12c5d1SDavid du Colombier fbwrite(Icache *ic, Ibuf *b, char *a, ulong off, int len)
533e12c5d1SDavid du Colombier {
54*41fb754aSDavid du Colombier 	int wrinode;
55*41fb754aSDavid du Colombier 	ulong fbno;
563e12c5d1SDavid du Colombier 	Bbuf *dbb;	/* data block */
573e12c5d1SDavid du Colombier 	Bbuf *ibb;	/* indirect block */
583e12c5d1SDavid du Colombier 	Dptr *p;
593e12c5d1SDavid du Colombier 	Dptr t;
603e12c5d1SDavid du Colombier 
613e12c5d1SDavid du Colombier 	fbno = off / ic->bsize;
623e12c5d1SDavid du Colombier 	p = &b->inode.ptr;
633e12c5d1SDavid du Colombier 	ibb = 0;
643e12c5d1SDavid du Colombier 	wrinode = 0;
653e12c5d1SDavid du Colombier 
663e12c5d1SDavid du Colombier 	/*
673e12c5d1SDavid du Colombier 	 *  are there any pages for this inode?
683e12c5d1SDavid du Colombier 	 */
693e12c5d1SDavid du Colombier 	if(p->bno == Notabno){
703e12c5d1SDavid du Colombier 		wrinode = 1;
713e12c5d1SDavid du Colombier 		goto dowrite;
723e12c5d1SDavid du Colombier 	}
733e12c5d1SDavid du Colombier 
743e12c5d1SDavid du Colombier 	/*
753e12c5d1SDavid du Colombier  	 *  is it an indirect block?
763e12c5d1SDavid du Colombier 	 */
773e12c5d1SDavid du Colombier 	if(p->bno & Indbno){
783e12c5d1SDavid du Colombier 		ibb = bcread(ic, p->bno);
793e12c5d1SDavid du Colombier 		if(ibb == 0)
803e12c5d1SDavid du Colombier 			return -1;
813e12c5d1SDavid du Colombier 		p = (Dptr*)ibb->data;
823e12c5d1SDavid du Colombier 		p += fbno % ic->p2b;
833e12c5d1SDavid du Colombier 		goto dowrite;
843e12c5d1SDavid du Colombier 	}
853e12c5d1SDavid du Colombier 
863e12c5d1SDavid du Colombier 	/*
873e12c5d1SDavid du Colombier  	 *  is it the wrong direct block?
883e12c5d1SDavid du Colombier 	 */
893e12c5d1SDavid du Colombier 	if((p->fbno%ic->p2b) != (fbno%ic->p2b)){
903e12c5d1SDavid du Colombier 		/*
913e12c5d1SDavid du Colombier 		 *  yes, make an indirect block
923e12c5d1SDavid du Colombier 		 */
933e12c5d1SDavid du Colombier 		t = *p;
943e12c5d1SDavid du Colombier 		dpalloc(ic, p);
953e12c5d1SDavid du Colombier 		if(p->bno == Notabno){
963e12c5d1SDavid du Colombier 			*p = t;
973e12c5d1SDavid du Colombier 			return -1;
983e12c5d1SDavid du Colombier 		}
993e12c5d1SDavid du Colombier 		ibb = bcalloc(ic, p->bno);
1003e12c5d1SDavid du Colombier 		if(ibb == 0){
1013e12c5d1SDavid du Colombier 			*p = t;
1023e12c5d1SDavid du Colombier 			return -1;
1033e12c5d1SDavid du Colombier 		}
1043e12c5d1SDavid du Colombier 		p = (Dptr*)ibb->data;
1053e12c5d1SDavid du Colombier 		p += t.fbno % ic->p2b;
1063e12c5d1SDavid du Colombier 		*p = t;
1073e12c5d1SDavid du Colombier 		p = (Dptr*)ibb->data;
1083e12c5d1SDavid du Colombier 		p += fbno % ic->p2b;
1093e12c5d1SDavid du Colombier 	}
1103e12c5d1SDavid du Colombier 	wrinode = 1;
1113e12c5d1SDavid du Colombier 
1123e12c5d1SDavid du Colombier dowrite:
1133e12c5d1SDavid du Colombier 	/*
1143e12c5d1SDavid du Colombier 	 *  get the data block into the block cache
1153e12c5d1SDavid du Colombier 	 */
1163e12c5d1SDavid du Colombier 	if(p->bno == Notabno){
1173e12c5d1SDavid du Colombier 		/*
1183e12c5d1SDavid du Colombier 		 *  create a new block
1193e12c5d1SDavid du Colombier 		 */
1203e12c5d1SDavid du Colombier 		dalloc(ic, p);
1213e12c5d1SDavid du Colombier 		if(p->bno == Notabno)
1223e12c5d1SDavid du Colombier 			return -1;		/* no blocks left (maybe) */
1233e12c5d1SDavid du Colombier 		dbb = bcalloc(ic, p->bno);
1243e12c5d1SDavid du Colombier 	} else {
1253e12c5d1SDavid du Colombier 		/*
1263e12c5d1SDavid du Colombier 		 *  use what's there
1273e12c5d1SDavid du Colombier 		 */
1283e12c5d1SDavid du Colombier 		dbb = bcread(ic, p->bno);
1293e12c5d1SDavid du Colombier 	}
1303e12c5d1SDavid du Colombier 	if(dbb == 0)
1313e12c5d1SDavid du Colombier 		return -1;
1323e12c5d1SDavid du Colombier 
1333e12c5d1SDavid du Colombier 	/*
1343e12c5d1SDavid du Colombier 	 *  merge in the new data
1353e12c5d1SDavid du Colombier 	 */
1363e12c5d1SDavid du Colombier 	if(p->fbno != fbno){
1373e12c5d1SDavid du Colombier 		p->start = p->end = 0;
1383e12c5d1SDavid du Colombier 		p->fbno = fbno;
1393e12c5d1SDavid du Colombier 	}
1403e12c5d1SDavid du Colombier 	fmerge(p, dbb->data, a, off % ic->bsize, len);
1413e12c5d1SDavid du Colombier 
1423e12c5d1SDavid du Colombier 	/*
1433e12c5d1SDavid du Colombier 	 *  write changed blocks back in the
1443e12c5d1SDavid du Colombier 	 *  correct order
1453e12c5d1SDavid du Colombier 	 */
1463e12c5d1SDavid du Colombier 	bcmark(ic, dbb);
147*41fb754aSDavid du Colombier 	if(ibb)
1483e12c5d1SDavid du Colombier 		bcmark(ic, ibb);
149*41fb754aSDavid du Colombier 	if(wrinode)
1503e12c5d1SDavid du Colombier 		if(iwrite(ic, b) < 0)
1513e12c5d1SDavid du Colombier 			return -1;
1523e12c5d1SDavid du Colombier 	return len;
1533e12c5d1SDavid du Colombier }
1543e12c5d1SDavid du Colombier 
1553e12c5d1SDavid du Colombier /*
1563e12c5d1SDavid du Colombier  *  write `n' bytes to the cache
1573e12c5d1SDavid du Colombier  *
1583e12c5d1SDavid du Colombier  *  return number of bytes written
1593e12c5d1SDavid du Colombier  */
1603e12c5d1SDavid du Colombier long
fwrite(Icache * ic,Ibuf * b,char * a,ulong off,long n)1613e12c5d1SDavid du Colombier fwrite(Icache *ic, Ibuf *b, char *a, ulong off, long n)
1623e12c5d1SDavid du Colombier {
1633e12c5d1SDavid du Colombier 	int len;
1643e12c5d1SDavid du Colombier 	long sofar;
1653e12c5d1SDavid du Colombier 
1663e12c5d1SDavid du Colombier 	for(sofar = 0; sofar < n; sofar += len){
1673e12c5d1SDavid du Colombier 		len = ic->bsize - ((off+sofar)%ic->bsize);
1683e12c5d1SDavid du Colombier 		if(len > n - sofar)
1693e12c5d1SDavid du Colombier 			len = n - sofar;
1703e12c5d1SDavid du Colombier 		if(fbwrite(ic, b, a+sofar, off+sofar, len) < 0)
1713e12c5d1SDavid du Colombier 			return sofar;
1723e12c5d1SDavid du Colombier 	}
1733e12c5d1SDavid du Colombier 	return sofar;
1743e12c5d1SDavid du Colombier }
1753e12c5d1SDavid du Colombier 
1763e12c5d1SDavid du Colombier /*
1773e12c5d1SDavid du Colombier  *  get a pointer to the next valid data at or after `off'
1783e12c5d1SDavid du Colombier  */
1793e12c5d1SDavid du Colombier Dptr *
fpget(Icache * ic,Ibuf * b,ulong off)1803e12c5d1SDavid du Colombier fpget(Icache *ic, Ibuf *b, ulong off)
1813e12c5d1SDavid du Colombier {
1823e12c5d1SDavid du Colombier 	ulong fbno;
1833e12c5d1SDavid du Colombier 	long doff;
184*41fb754aSDavid du Colombier 	Bbuf *ibb;	/* indirect block */
185*41fb754aSDavid du Colombier 	Dptr *p, *p0, *pf;
1863e12c5d1SDavid du Colombier 
1873e12c5d1SDavid du Colombier 	fbno = off / ic->bsize;
1883e12c5d1SDavid du Colombier 	p = &b->inode.ptr;
1893e12c5d1SDavid du Colombier 
1903e12c5d1SDavid du Colombier 	/*
1913e12c5d1SDavid du Colombier 	 *  are there any pages for this inode?
1923e12c5d1SDavid du Colombier 	 */
1933e12c5d1SDavid du Colombier 	if(p->bno == Notabno)
1943e12c5d1SDavid du Colombier 		return 0;
1953e12c5d1SDavid du Colombier 
1963e12c5d1SDavid du Colombier 	/*
1973e12c5d1SDavid du Colombier  	 *  if it's a direct block, life is easy?
1983e12c5d1SDavid du Colombier 	 */
1993e12c5d1SDavid du Colombier 	if(!(p->bno & Indbno)){
2003e12c5d1SDavid du Colombier 		/*
2013e12c5d1SDavid du Colombier 		 *  a direct block, return p if it's at least past what we want
2023e12c5d1SDavid du Colombier 		 */
2033e12c5d1SDavid du Colombier 		if(p->fbno > fbno)
2043e12c5d1SDavid du Colombier 			return p;
2053e12c5d1SDavid du Colombier 		if(p->fbno < fbno)
2063e12c5d1SDavid du Colombier 			return 0;
2073e12c5d1SDavid du Colombier 		doff = off % ic->bsize;
2083e12c5d1SDavid du Colombier 		if(doff>=p->start && doff<p->end)
2093e12c5d1SDavid du Colombier 			return p;
2103e12c5d1SDavid du Colombier 		else
2113e12c5d1SDavid du Colombier 			return 0;
2123e12c5d1SDavid du Colombier 	}
2133e12c5d1SDavid du Colombier 
2143e12c5d1SDavid du Colombier 	/*
2153e12c5d1SDavid du Colombier 	 *  read the indirect block
2163e12c5d1SDavid du Colombier 	 */
2173e12c5d1SDavid du Colombier 	ibb = bcread(ic, p->bno);
2183e12c5d1SDavid du Colombier 	if(ibb == 0)
2193e12c5d1SDavid du Colombier 		return 0;
2203e12c5d1SDavid du Colombier 
2213e12c5d1SDavid du Colombier 	/*
2223e12c5d1SDavid du Colombier 	 *  find the next valid pointer
2233e12c5d1SDavid du Colombier 	 */
2243e12c5d1SDavid du Colombier 	p0 = (Dptr*)ibb->data;
2253e12c5d1SDavid du Colombier 	pf = p0 + (fbno % ic->p2b);
2263e12c5d1SDavid du Colombier 	if(pf->bno!=Notabno && pf->fbno==fbno){
2273e12c5d1SDavid du Colombier 		doff = off % ic->bsize;
2283e12c5d1SDavid du Colombier 		if(doff<pf->end)
2293e12c5d1SDavid du Colombier 			return pf;
2303e12c5d1SDavid du Colombier 	}
2313e12c5d1SDavid du Colombier 	for(p = pf+1; p < p0 + ic->p2b; p++){
2323e12c5d1SDavid du Colombier 		fbno++;
2333e12c5d1SDavid du Colombier 		if(p->fbno==fbno && p->bno!=Notabno && p->start<p->end)
2343e12c5d1SDavid du Colombier 			return p;
2353e12c5d1SDavid du Colombier 	}
2363e12c5d1SDavid du Colombier 	for(p = p0; p < pf; p++){
2373e12c5d1SDavid du Colombier 		fbno++;
2383e12c5d1SDavid du Colombier 		if(p->fbno==fbno && p->bno!=Notabno && p->start<p->end)
2393e12c5d1SDavid du Colombier 			return p;
2403e12c5d1SDavid du Colombier 	}
2413e12c5d1SDavid du Colombier 	return 0;
2423e12c5d1SDavid du Colombier }
2433e12c5d1SDavid du Colombier 
2443e12c5d1SDavid du Colombier /*
2453e12c5d1SDavid du Colombier  *  read `n' bytes from the cache.
2463e12c5d1SDavid du Colombier  *
2473e12c5d1SDavid du Colombier  *  if we hit a gap and we've read something,
2483e12c5d1SDavid du Colombier  *  return number of bytes read so far.
2493e12c5d1SDavid du Colombier  *
2503e12c5d1SDavid du Colombier  *  if we start with a gap, return minus the number of bytes
2513e12c5d1SDavid du Colombier  *  to the next data.
2523e12c5d1SDavid du Colombier  *
2533e12c5d1SDavid du Colombier  *  if there are no bytes cached, return 0.
2543e12c5d1SDavid du Colombier  */
2553e12c5d1SDavid du Colombier long
fread(Icache * ic,Ibuf * b,char * a,ulong off,long n)2563e12c5d1SDavid du Colombier fread(Icache *ic, Ibuf *b, char *a, ulong off, long n)
2573e12c5d1SDavid du Colombier {
258*41fb754aSDavid du Colombier 	int len, start;
259*41fb754aSDavid du Colombier 	long sofar, gap;
2603e12c5d1SDavid du Colombier 	Dptr *p;
2613e12c5d1SDavid du Colombier 	Bbuf *bb;
2623e12c5d1SDavid du Colombier 
2633e12c5d1SDavid du Colombier 	for(sofar = 0; sofar < n; sofar += len, off += len){
2643e12c5d1SDavid du Colombier 		/*
2653e12c5d1SDavid du Colombier 		 *  get pointer to next data
2663e12c5d1SDavid du Colombier 		 */
2673e12c5d1SDavid du Colombier 		len = n - sofar;
2683e12c5d1SDavid du Colombier 		p = fpget(ic, b, off);
2693e12c5d1SDavid du Colombier 
2703e12c5d1SDavid du Colombier 		/*
2713e12c5d1SDavid du Colombier 		 *  if no more data, return what we have so far
2723e12c5d1SDavid du Colombier 		 */
2733e12c5d1SDavid du Colombier 		if(p == 0)
2743e12c5d1SDavid du Colombier 			return sofar;
2753e12c5d1SDavid du Colombier 
2763e12c5d1SDavid du Colombier 		/*
2773e12c5d1SDavid du Colombier 		 *  if there's a gap, return the size of the gap
2783e12c5d1SDavid du Colombier 		 */
2793e12c5d1SDavid du Colombier 		gap = (ic->bsize*p->fbno + p->start) - off;
280*41fb754aSDavid du Colombier 		if(gap>0)
2813e12c5d1SDavid du Colombier 			if(sofar == 0)
2823e12c5d1SDavid du Colombier 				return -gap;
2833e12c5d1SDavid du Colombier 			else
2843e12c5d1SDavid du Colombier 				return sofar;
2853e12c5d1SDavid du Colombier 
2863e12c5d1SDavid du Colombier 		/*
2873e12c5d1SDavid du Colombier 		 *  return what we have
2883e12c5d1SDavid du Colombier 		 */
2893e12c5d1SDavid du Colombier 		bb = bcread(ic, p->bno);
2903e12c5d1SDavid du Colombier 		if(bb == 0)
2913e12c5d1SDavid du Colombier 			return sofar;
2923e12c5d1SDavid du Colombier 		start = p->start - gap;
2933e12c5d1SDavid du Colombier 		if(p->end - start < len)
2943e12c5d1SDavid du Colombier 			len = p->end - start;
2953e12c5d1SDavid du Colombier 		memmove(a + sofar, bb->data + start, len);
2963e12c5d1SDavid du Colombier 	}
2973e12c5d1SDavid du Colombier 	return sofar;
2983e12c5d1SDavid du Colombier }
299