xref: /plan9/sys/src/cmd/cfs/file.c (revision 41fb754a868aff65c81b63b0b07ae1fab2010fd9)
1 #include <u.h>
2 #include <libc.h>
3 #include "cformat.h"
4 #include "lru.h"
5 #include "bcache.h"
6 #include "disk.h"
7 #include "inode.h"
8 #include "file.h"
9 
10 /*
11  *  merge data with that which already exists in a block
12  *
13  *  we allow only one range per block, always use the new
14  *  data if the ranges don't overlap.
15  */
16 void
fmerge(Dptr * p,char * to,char * from,int start,int len)17 fmerge(Dptr *p, char *to, char *from, int start, int len)
18 {
19 	int end;
20 
21 	end = start + len;
22 	memmove(to+start, from, end-start);
23 
24 	/*
25 	 *  if ranges do not overlap...
26 	 */
27 	if(start>p->end || p->start>end){
28 		/*
29 		 *  just use the new data
30 		 */
31 		p->start = start;
32 		p->end = end;
33 	} else {
34 		/*
35 		 *  merge ranges
36 		 */
37 		if(start < p->start)
38 			p->start = start;
39 		if(end > p->end)
40 			p->end = end;
41 	}
42 
43 }
44 
45 /*
46  *  write a block (or less) of data onto a disk, follow it with any necessary
47  *  pointer writes.
48  *
49  *	N.B. ordering is everything
50  */
51 int
fbwrite(Icache * ic,Ibuf * b,char * a,ulong off,int len)52 fbwrite(Icache *ic, Ibuf *b, char *a, ulong off, int len)
53 {
54 	int wrinode;
55 	ulong fbno;
56 	Bbuf *dbb;	/* data block */
57 	Bbuf *ibb;	/* indirect block */
58 	Dptr *p;
59 	Dptr t;
60 
61 	fbno = off / ic->bsize;
62 	p = &b->inode.ptr;
63 	ibb = 0;
64 	wrinode = 0;
65 
66 	/*
67 	 *  are there any pages for this inode?
68 	 */
69 	if(p->bno == Notabno){
70 		wrinode = 1;
71 		goto dowrite;
72 	}
73 
74 	/*
75  	 *  is it an indirect block?
76 	 */
77 	if(p->bno & Indbno){
78 		ibb = bcread(ic, p->bno);
79 		if(ibb == 0)
80 			return -1;
81 		p = (Dptr*)ibb->data;
82 		p += fbno % ic->p2b;
83 		goto dowrite;
84 	}
85 
86 	/*
87  	 *  is it the wrong direct block?
88 	 */
89 	if((p->fbno%ic->p2b) != (fbno%ic->p2b)){
90 		/*
91 		 *  yes, make an indirect block
92 		 */
93 		t = *p;
94 		dpalloc(ic, p);
95 		if(p->bno == Notabno){
96 			*p = t;
97 			return -1;
98 		}
99 		ibb = bcalloc(ic, p->bno);
100 		if(ibb == 0){
101 			*p = t;
102 			return -1;
103 		}
104 		p = (Dptr*)ibb->data;
105 		p += t.fbno % ic->p2b;
106 		*p = t;
107 		p = (Dptr*)ibb->data;
108 		p += fbno % ic->p2b;
109 	}
110 	wrinode = 1;
111 
112 dowrite:
113 	/*
114 	 *  get the data block into the block cache
115 	 */
116 	if(p->bno == Notabno){
117 		/*
118 		 *  create a new block
119 		 */
120 		dalloc(ic, p);
121 		if(p->bno == Notabno)
122 			return -1;		/* no blocks left (maybe) */
123 		dbb = bcalloc(ic, p->bno);
124 	} else {
125 		/*
126 		 *  use what's there
127 		 */
128 		dbb = bcread(ic, p->bno);
129 	}
130 	if(dbb == 0)
131 		return -1;
132 
133 	/*
134 	 *  merge in the new data
135 	 */
136 	if(p->fbno != fbno){
137 		p->start = p->end = 0;
138 		p->fbno = fbno;
139 	}
140 	fmerge(p, dbb->data, a, off % ic->bsize, len);
141 
142 	/*
143 	 *  write changed blocks back in the
144 	 *  correct order
145 	 */
146 	bcmark(ic, dbb);
147 	if(ibb)
148 		bcmark(ic, ibb);
149 	if(wrinode)
150 		if(iwrite(ic, b) < 0)
151 			return -1;
152 	return len;
153 }
154 
155 /*
156  *  write `n' bytes to the cache
157  *
158  *  return number of bytes written
159  */
160 long
fwrite(Icache * ic,Ibuf * b,char * a,ulong off,long n)161 fwrite(Icache *ic, Ibuf *b, char *a, ulong off, long n)
162 {
163 	int len;
164 	long sofar;
165 
166 	for(sofar = 0; sofar < n; sofar += len){
167 		len = ic->bsize - ((off+sofar)%ic->bsize);
168 		if(len > n - sofar)
169 			len = n - sofar;
170 		if(fbwrite(ic, b, a+sofar, off+sofar, len) < 0)
171 			return sofar;
172 	}
173 	return sofar;
174 }
175 
176 /*
177  *  get a pointer to the next valid data at or after `off'
178  */
179 Dptr *
fpget(Icache * ic,Ibuf * b,ulong off)180 fpget(Icache *ic, Ibuf *b, ulong off)
181 {
182 	ulong fbno;
183 	long doff;
184 	Bbuf *ibb;	/* indirect block */
185 	Dptr *p, *p0, *pf;
186 
187 	fbno = off / ic->bsize;
188 	p = &b->inode.ptr;
189 
190 	/*
191 	 *  are there any pages for this inode?
192 	 */
193 	if(p->bno == Notabno)
194 		return 0;
195 
196 	/*
197  	 *  if it's a direct block, life is easy?
198 	 */
199 	if(!(p->bno & Indbno)){
200 		/*
201 		 *  a direct block, return p if it's at least past what we want
202 		 */
203 		if(p->fbno > fbno)
204 			return p;
205 		if(p->fbno < fbno)
206 			return 0;
207 		doff = off % ic->bsize;
208 		if(doff>=p->start && doff<p->end)
209 			return p;
210 		else
211 			return 0;
212 	}
213 
214 	/*
215 	 *  read the indirect block
216 	 */
217 	ibb = bcread(ic, p->bno);
218 	if(ibb == 0)
219 		return 0;
220 
221 	/*
222 	 *  find the next valid pointer
223 	 */
224 	p0 = (Dptr*)ibb->data;
225 	pf = p0 + (fbno % ic->p2b);
226 	if(pf->bno!=Notabno && pf->fbno==fbno){
227 		doff = off % ic->bsize;
228 		if(doff<pf->end)
229 			return pf;
230 	}
231 	for(p = pf+1; p < p0 + ic->p2b; p++){
232 		fbno++;
233 		if(p->fbno==fbno && p->bno!=Notabno && p->start<p->end)
234 			return p;
235 	}
236 	for(p = p0; p < pf; p++){
237 		fbno++;
238 		if(p->fbno==fbno && p->bno!=Notabno && p->start<p->end)
239 			return p;
240 	}
241 	return 0;
242 }
243 
244 /*
245  *  read `n' bytes from the cache.
246  *
247  *  if we hit a gap and we've read something,
248  *  return number of bytes read so far.
249  *
250  *  if we start with a gap, return minus the number of bytes
251  *  to the next data.
252  *
253  *  if there are no bytes cached, return 0.
254  */
255 long
fread(Icache * ic,Ibuf * b,char * a,ulong off,long n)256 fread(Icache *ic, Ibuf *b, char *a, ulong off, long n)
257 {
258 	int len, start;
259 	long sofar, gap;
260 	Dptr *p;
261 	Bbuf *bb;
262 
263 	for(sofar = 0; sofar < n; sofar += len, off += len){
264 		/*
265 		 *  get pointer to next data
266 		 */
267 		len = n - sofar;
268 		p = fpget(ic, b, off);
269 
270 		/*
271 		 *  if no more data, return what we have so far
272 		 */
273 		if(p == 0)
274 			return sofar;
275 
276 		/*
277 		 *  if there's a gap, return the size of the gap
278 		 */
279 		gap = (ic->bsize*p->fbno + p->start) - off;
280 		if(gap>0)
281 			if(sofar == 0)
282 				return -gap;
283 			else
284 				return sofar;
285 
286 		/*
287 		 *  return what we have
288 		 */
289 		bb = bcread(ic, p->bno);
290 		if(bb == 0)
291 			return sofar;
292 		start = p->start - gap;
293 		if(p->end - start < len)
294 			len = p->end - start;
295 		memmove(a + sofar, bb->data + start, len);
296 	}
297 	return sofar;
298 }
299