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 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 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 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 * 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 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