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