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 Bbuf *dbb; /* data block */ 55 Bbuf *ibb; /* indirect block */ 56 Dptr *p; 57 Dptr t; 58 ulong fbno; 59 int wrinode; 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 } 150 if(wrinode){ 151 if(iwrite(ic, b) < 0) 152 return -1; 153 } 154 return len; 155 } 156 157 /* 158 * write `n' bytes to the cache 159 * 160 * return number of bytes written 161 */ 162 long 163 fwrite(Icache *ic, Ibuf *b, char *a, ulong off, long n) 164 { 165 int len; 166 long sofar; 167 168 for(sofar = 0; sofar < n; sofar += len){ 169 len = ic->bsize - ((off+sofar)%ic->bsize); 170 if(len > n - sofar) 171 len = n - sofar; 172 if(fbwrite(ic, b, a+sofar, off+sofar, len) < 0) 173 return sofar; 174 } 175 return sofar; 176 } 177 178 /* 179 * get a pointer to the next valid data at or after `off' 180 */ 181 Dptr * 182 fpget(Icache *ic, Ibuf *b, ulong off) 183 { 184 Bbuf *ibb; /* indirect block */ 185 ulong fbno; 186 long doff; 187 Dptr *p; 188 Dptr *p0; 189 Dptr *pf; 190 191 fbno = off / ic->bsize; 192 p = &b->inode.ptr; 193 194 /* 195 * are there any pages for this inode? 196 */ 197 if(p->bno == Notabno) 198 return 0; 199 200 /* 201 * if it's a direct block, life is easy? 202 */ 203 if(!(p->bno & Indbno)){ 204 /* 205 * a direct block, return p if it's at least past what we want 206 */ 207 if(p->fbno > fbno) 208 return p; 209 if(p->fbno < fbno) 210 return 0; 211 doff = off % ic->bsize; 212 if(doff>=p->start && doff<p->end) 213 return p; 214 else 215 return 0; 216 } 217 218 /* 219 * read the indirect block 220 */ 221 ibb = bcread(ic, p->bno); 222 if(ibb == 0) 223 return 0; 224 225 /* 226 * find the next valid pointer 227 */ 228 p0 = (Dptr*)ibb->data; 229 pf = p0 + (fbno % ic->p2b); 230 if(pf->bno!=Notabno && pf->fbno==fbno){ 231 doff = off % ic->bsize; 232 if(doff<pf->end) 233 return pf; 234 } 235 for(p = pf+1 ;p < p0 + ic->p2b; p++){ 236 fbno++; 237 if(p->fbno==fbno && p->bno!=Notabno && p->start<p->end) 238 return p; 239 } 240 for(p = p0 ;p < pf; p++){ 241 fbno++; 242 if(p->fbno==fbno && p->bno!=Notabno && p->start<p->end) 243 return p; 244 } 245 return 0; 246 } 247 248 /* 249 * read `n' bytes from the cache. 250 * 251 * if we hit a gap and we've read something, 252 * return number of bytes read so far. 253 * 254 * if we start with a gap, return minus the number of bytes 255 * to the next data. 256 * 257 * if there are no bytes cached, return 0. 258 */ 259 long 260 fread(Icache *ic, Ibuf *b, char *a, ulong off, long n) 261 { 262 int len; 263 Dptr *p; 264 Bbuf *bb; 265 long sofar; 266 long gap; 267 int start; 268 269 for(sofar = 0; sofar < n; sofar += len, off += len){ 270 /* 271 * get pointer to next data 272 */ 273 len = n - sofar; 274 p = fpget(ic, b, off); 275 276 /* 277 * if no more data, return what we have so far 278 */ 279 if(p == 0) 280 return sofar; 281 282 /* 283 * if there's a gap, return the size of the gap 284 */ 285 gap = (ic->bsize*p->fbno + p->start) - off; 286 if(gap>0){ 287 if(sofar == 0) 288 return -gap; 289 else 290 return sofar; 291 } 292 293 /* 294 * return what we have 295 */ 296 bb = bcread(ic, p->bno); 297 if(bb == 0) 298 return sofar; 299 start = p->start - gap; 300 if(p->end - start < len) 301 len = p->end - start; 302 memmove(a + sofar, bb->data + start, len); 303 } 304 return sofar; 305 } 306