tfile.c - plan9port - [fork] Plan 9 from user space | |
git clone git://src.adamsgaard.dk/plan9port | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
tfile.c (40202B) | |
--- | |
1 #include "stdinc.h" | |
2 #include "vac.h" | |
3 #include "dat.h" | |
4 #include "fns.h" | |
5 #include "error.h" | |
6 | |
7 #define debug 0 | |
8 | |
9 /* | |
10 * Vac file system. This is a simplified version of the same code in Fo… | |
11 * | |
12 * The locking order in the tree is upward: a thread can hold the lock | |
13 * for a VacFile and then acquire the lock of f->up (the parent), | |
14 * but not vice-versa. | |
15 * | |
16 * A vac file is one or two venti files. Plain data files are one venti… | |
17 * while directores are two: a venti data file containing traditional | |
18 * directory entries, and a venti directory file containing venti | |
19 * directory entries. The traditional directory entries in the data file | |
20 * contain integers indexing into the venti directory entry file. | |
21 * It's a little complicated, but it makes the data usable by standard | |
22 * tools like venti/copy. | |
23 * | |
24 */ | |
25 | |
26 static int filemetaflush(VacFile*, char*); | |
27 | |
28 struct VacFile | |
29 { | |
30 VacFs *fs; /* immutable */ | |
31 | |
32 /* meta data for file: protected by the lk in the parent */ | |
33 int ref; /* holds this data structure up */ | |
34 | |
35 int partial; /* file was never really open… | |
36 int removed; /* file has been removed */ | |
37 int dirty; /* dir is dirty with respect to… | |
38 u32int boff; /* block offset within msourc… | |
39 VacDir dir; /* metadata for this file */ | |
40 VacFile *up; /* parent file */ | |
41 VacFile *next; /* sibling */ | |
42 | |
43 RWLock lk; /* lock for the following */ | |
44 VtFile *source; /* actual data */ | |
45 VtFile *msource; /* metadata for children in a dir… | |
46 VacFile *down; /* children */ | |
47 int mode; | |
48 | |
49 uvlong qidoffset; /* qid offset */ | |
50 }; | |
51 | |
52 static VacFile* | |
53 filealloc(VacFs *fs) | |
54 { | |
55 VacFile *f; | |
56 | |
57 f = vtmallocz(sizeof(VacFile)); | |
58 f->ref = 1; | |
59 f->fs = fs; | |
60 f->boff = NilBlock; | |
61 f->mode = fs->mode; | |
62 return f; | |
63 } | |
64 | |
65 static void | |
66 filefree(VacFile *f) | |
67 { | |
68 vtfileclose(f->source); | |
69 vtfileclose(f->msource); | |
70 vdcleanup(&f->dir); | |
71 memset(f, ~0, sizeof *f); /* paranoia */ | |
72 vtfree(f); | |
73 } | |
74 | |
75 static int | |
76 chksource(VacFile *f) | |
77 { | |
78 if(f->partial) | |
79 return 0; | |
80 | |
81 if(f->source == nil | |
82 || ((f->dir.mode & ModeDir) && f->msource == nil)){ | |
83 werrstr(ERemoved); | |
84 return -1; | |
85 } | |
86 return 0; | |
87 } | |
88 | |
89 static int | |
90 filelock(VacFile *f) | |
91 { | |
92 wlock(&f->lk); | |
93 if(chksource(f) < 0){ | |
94 wunlock(&f->lk); | |
95 return -1; | |
96 } | |
97 return 0; | |
98 } | |
99 | |
100 static void | |
101 fileunlock(VacFile *f) | |
102 { | |
103 wunlock(&f->lk); | |
104 } | |
105 | |
106 static int | |
107 filerlock(VacFile *f) | |
108 { | |
109 rlock(&f->lk); | |
110 if(chksource(f) < 0){ | |
111 runlock(&f->lk); | |
112 return -1; | |
113 } | |
114 return 0; | |
115 } | |
116 | |
117 static void | |
118 filerunlock(VacFile *f) | |
119 { | |
120 runlock(&f->lk); | |
121 } | |
122 | |
123 /* | |
124 * The file metadata, like f->dir and f->ref, | |
125 * are synchronized via the parent's lock. | |
126 * This is why locking order goes up. | |
127 */ | |
128 static void | |
129 filemetalock(VacFile *f) | |
130 { | |
131 assert(f->up != nil); | |
132 wlock(&f->up->lk); | |
133 } | |
134 | |
135 static void | |
136 filemetaunlock(VacFile *f) | |
137 { | |
138 wunlock(&f->up->lk); | |
139 } | |
140 | |
141 uvlong | |
142 vacfilegetid(VacFile *f) | |
143 { | |
144 /* immutable */ | |
145 return f->qidoffset + f->dir.qid; | |
146 } | |
147 | |
148 uvlong | |
149 vacfilegetqidoffset(VacFile *f) | |
150 { | |
151 return f->qidoffset; | |
152 } | |
153 | |
154 ulong | |
155 vacfilegetmcount(VacFile *f) | |
156 { | |
157 ulong mcount; | |
158 | |
159 filemetalock(f); | |
160 mcount = f->dir.mcount; | |
161 filemetaunlock(f); | |
162 return mcount; | |
163 } | |
164 | |
165 ulong | |
166 vacfilegetmode(VacFile *f) | |
167 { | |
168 ulong mode; | |
169 | |
170 filemetalock(f); | |
171 mode = f->dir.mode; | |
172 filemetaunlock(f); | |
173 return mode; | |
174 } | |
175 | |
176 int | |
177 vacfileisdir(VacFile *f) | |
178 { | |
179 /* immutable */ | |
180 return (f->dir.mode & ModeDir) != 0; | |
181 } | |
182 | |
183 int | |
184 vacfileisroot(VacFile *f) | |
185 { | |
186 return f == f->fs->root; | |
187 } | |
188 | |
189 /* | |
190 * The files are reference counted, and while the reference | |
191 * is bigger than zero, each file can be found in its parent's | |
192 * f->down list (chains via f->next), so that multiple threads | |
193 * end up sharing a VacFile* when referring to the same file. | |
194 * | |
195 * Each VacFile holds a reference to its parent. | |
196 */ | |
197 VacFile* | |
198 vacfileincref(VacFile *vf) | |
199 { | |
200 filemetalock(vf); | |
201 assert(vf->ref > 0); | |
202 vf->ref++; | |
203 filemetaunlock(vf); | |
204 return vf; | |
205 } | |
206 | |
207 int | |
208 vacfiledecref(VacFile *f) | |
209 { | |
210 VacFile *p, *q, **qq; | |
211 | |
212 if(f->up == nil){ | |
213 /* never linked in */ | |
214 assert(f->ref == 1); | |
215 filefree(f); | |
216 return 0; | |
217 } | |
218 | |
219 filemetalock(f); | |
220 f->ref--; | |
221 if(f->ref > 0){ | |
222 filemetaunlock(f); | |
223 return -1; | |
224 } | |
225 assert(f->ref == 0); | |
226 assert(f->down == nil); | |
227 | |
228 if(f->source && vtfilelock(f->source, -1) >= 0){ | |
229 vtfileflush(f->source); | |
230 vtfileunlock(f->source); | |
231 } | |
232 if(f->msource && vtfilelock(f->msource, -1) >= 0){ | |
233 vtfileflush(f->msource); | |
234 vtfileunlock(f->msource); | |
235 } | |
236 | |
237 /* | |
238 * Flush f's directory information to the cache. | |
239 */ | |
240 filemetaflush(f, nil); | |
241 | |
242 p = f->up; | |
243 qq = &p->down; | |
244 for(q = *qq; q; q = *qq){ | |
245 if(q == f) | |
246 break; | |
247 qq = &q->next; | |
248 } | |
249 assert(q != nil); | |
250 *qq = f->next; | |
251 | |
252 filemetaunlock(f); | |
253 filefree(f); | |
254 vacfiledecref(p); | |
255 return 0; | |
256 } | |
257 | |
258 | |
259 /* | |
260 * Construct a vacfile for the root of a vac tree, given the | |
261 * venti file for the root information. That venti file is a | |
262 * directory file containing VtEntries for three more venti files: | |
263 * the two venti files making up the root directory, and a | |
264 * third venti file that would be the metadata half of the | |
265 * "root's parent". | |
266 * | |
267 * Fossil generates slightly different vac files, due to a now | |
268 * impossible-to-change bug, which contain a VtEntry | |
269 * for just one venti file, that itself contains the expected | |
270 * three directory entries. Sigh. | |
271 */ | |
272 VacFile* | |
273 _vacfileroot(VacFs *fs, VtFile *r) | |
274 { | |
275 int redirected; | |
276 char err[ERRMAX]; | |
277 VtBlock *b; | |
278 VtFile *r0, *r1, *r2; | |
279 MetaBlock mb; | |
280 MetaEntry me; | |
281 VacFile *root, *mr; | |
282 | |
283 redirected = 0; | |
284 Top: | |
285 b = nil; | |
286 root = nil; | |
287 mr = nil; | |
288 r1 = nil; | |
289 r2 = nil; | |
290 | |
291 if(vtfilelock(r, -1) < 0) | |
292 return nil; | |
293 r0 = vtfileopen(r, 0, fs->mode); | |
294 if(debug) | |
295 fprint(2, "r0 %p\n", r0); | |
296 if(r0 == nil) | |
297 goto Err; | |
298 r2 = vtfileopen(r, 2, fs->mode); | |
299 if(debug) | |
300 fprint(2, "r2 %p\n", r2); | |
301 if(r2 == nil){ | |
302 /* | |
303 * some vac files (e.g., from fossil) | |
304 * have an extra layer of indirection. | |
305 */ | |
306 rerrstr(err, sizeof err); | |
307 if(!redirected && strstr(err, "not active")){ | |
308 redirected = 1; | |
309 vtfileunlock(r); | |
310 r = r0; | |
311 goto Top; | |
312 } | |
313 goto Err; | |
314 } | |
315 r1 = vtfileopen(r, 1, fs->mode); | |
316 if(debug) | |
317 fprint(2, "r1 %p\n", r1); | |
318 if(r1 == nil) | |
319 goto Err; | |
320 | |
321 mr = filealloc(fs); | |
322 mr->msource = r2; | |
323 r2 = nil; | |
324 | |
325 root = filealloc(fs); | |
326 root->boff = 0; | |
327 root->up = mr; | |
328 root->source = r0; | |
329 r0 = nil; | |
330 root->msource = r1; | |
331 r1 = nil; | |
332 | |
333 mr->down = root; | |
334 vtfileunlock(r); | |
335 | |
336 if(vtfilelock(mr->msource, VtOREAD) < 0) | |
337 goto Err1; | |
338 b = vtfileblock(mr->msource, 0, VtOREAD); | |
339 vtfileunlock(mr->msource); | |
340 if(b == nil) | |
341 goto Err1; | |
342 | |
343 if(mbunpack(&mb, b->data, mr->msource->dsize) < 0) | |
344 goto Err1; | |
345 | |
346 meunpack(&me, &mb, 0); | |
347 if(vdunpack(&root->dir, &me) < 0) | |
348 goto Err1; | |
349 vtblockput(b); | |
350 | |
351 return root; | |
352 Err: | |
353 vtfileunlock(r); | |
354 Err1: | |
355 vtblockput(b); | |
356 if(r0) | |
357 vtfileclose(r0); | |
358 if(r1) | |
359 vtfileclose(r1); | |
360 if(r2) | |
361 vtfileclose(r2); | |
362 if(mr) | |
363 filefree(mr); | |
364 if(root) | |
365 filefree(root); | |
366 | |
367 return nil; | |
368 } | |
369 | |
370 /* | |
371 * Vac directories are a sequence of metablocks, each of which | |
372 * contains a bunch of metaentries sorted by file name. | |
373 * The whole sequence isn't sorted, though, so you still have | |
374 * to look at every block to find a given name. | |
375 * Dirlookup looks in f for an element name elem. | |
376 * It returns a new VacFile with the dir, boff, and mode | |
377 * filled in, but the sources (venti files) are not, and f is | |
378 * not yet linked into the tree. These details must be taken | |
379 * care of by the caller. | |
380 * | |
381 * f must be locked, f->msource must not. | |
382 */ | |
383 static VacFile* | |
384 dirlookup(VacFile *f, char *elem) | |
385 { | |
386 int i; | |
387 MetaBlock mb; | |
388 MetaEntry me; | |
389 VtBlock *b; | |
390 VtFile *meta; | |
391 VacFile *ff; | |
392 u32int bo, nb; | |
393 | |
394 meta = f->msource; | |
395 b = nil; | |
396 if(vtfilelock(meta, -1) < 0) | |
397 return nil; | |
398 nb = (vtfilegetsize(meta)+meta->dsize-1)/meta->dsize; | |
399 for(bo=0; bo<nb; bo++){ | |
400 b = vtfileblock(meta, bo, VtOREAD); | |
401 if(b == nil) | |
402 goto Err; | |
403 if(mbunpack(&mb, b->data, meta->dsize) < 0) | |
404 goto Err; | |
405 if(mbsearch(&mb, elem, &i, &me) >= 0){ | |
406 ff = filealloc(f->fs); | |
407 if(vdunpack(&ff->dir, &me) < 0){ | |
408 filefree(ff); | |
409 goto Err; | |
410 } | |
411 ff->qidoffset = f->qidoffset + ff->dir.qidoffset; | |
412 vtfileunlock(meta); | |
413 vtblockput(b); | |
414 ff->boff = bo; | |
415 ff->mode = f->mode; | |
416 return ff; | |
417 } | |
418 vtblockput(b); | |
419 b = nil; | |
420 } | |
421 werrstr(ENoFile); | |
422 /* fall through */ | |
423 Err: | |
424 vtfileunlock(meta); | |
425 vtblockput(b); | |
426 return nil; | |
427 } | |
428 | |
429 /* | |
430 * Open the venti file at offset in the directory f->source. | |
431 * f is locked. | |
432 */ | |
433 static VtFile * | |
434 fileopensource(VacFile *f, u32int offset, u32int gen, int dir, uint mode) | |
435 { | |
436 VtFile *r; | |
437 | |
438 if((r = vtfileopen(f->source, offset, mode)) == nil) | |
439 return nil; | |
440 if(r == nil) | |
441 return nil; | |
442 if(r->gen != gen){ | |
443 werrstr(ERemoved); | |
444 vtfileclose(r); | |
445 return nil; | |
446 } | |
447 if(r->dir != dir && r->mode != -1){ | |
448 werrstr(EBadMeta); | |
449 vtfileclose(r); | |
450 return nil; | |
451 } | |
452 return r; | |
453 } | |
454 | |
455 VacFile* | |
456 vacfilegetparent(VacFile *f) | |
457 { | |
458 if(vacfileisroot(f)) | |
459 return vacfileincref(f); | |
460 return vacfileincref(f->up); | |
461 } | |
462 | |
463 /* | |
464 * Given an unlocked vacfile (directory) f, | |
465 * return the vacfile named elem in f. | |
466 * Interprets . and .. as a convenience to callers. | |
467 */ | |
468 VacFile* | |
469 vacfilewalk(VacFile *f, char *elem) | |
470 { | |
471 VacFile *ff; | |
472 | |
473 if(elem[0] == 0){ | |
474 werrstr(EBadPath); | |
475 return nil; | |
476 } | |
477 | |
478 if(!vacfileisdir(f)){ | |
479 werrstr(ENotDir); | |
480 return nil; | |
481 } | |
482 | |
483 if(strcmp(elem, ".") == 0) | |
484 return vacfileincref(f); | |
485 | |
486 if(strcmp(elem, "..") == 0) | |
487 return vacfilegetparent(f); | |
488 | |
489 if(filelock(f) < 0) | |
490 return nil; | |
491 | |
492 for(ff = f->down; ff; ff=ff->next){ | |
493 if(strcmp(elem, ff->dir.elem) == 0 && !ff->removed){ | |
494 ff->ref++; | |
495 goto Exit; | |
496 } | |
497 } | |
498 | |
499 ff = dirlookup(f, elem); | |
500 if(ff == nil) | |
501 goto Err; | |
502 | |
503 if(ff->dir.mode & ModeSnapshot) | |
504 ff->mode = VtOREAD; | |
505 | |
506 if(vtfilelock(f->source, f->mode) < 0) | |
507 goto Err; | |
508 if(ff->dir.mode & ModeDir){ | |
509 ff->source = fileopensource(f, ff->dir.entry, ff->dir.ge… | |
510 ff->msource = fileopensource(f, ff->dir.mentry, ff->dir.… | |
511 if(ff->source == nil || ff->msource == nil) | |
512 goto Err1; | |
513 }else{ | |
514 ff->source = fileopensource(f, ff->dir.entry, ff->dir.ge… | |
515 if(ff->source == nil) | |
516 goto Err1; | |
517 } | |
518 vtfileunlock(f->source); | |
519 | |
520 /* link in and up parent ref count */ | |
521 ff->next = f->down; | |
522 f->down = ff; | |
523 ff->up = f; | |
524 vacfileincref(f); | |
525 Exit: | |
526 fileunlock(f); | |
527 return ff; | |
528 | |
529 Err1: | |
530 vtfileunlock(f->source); | |
531 Err: | |
532 fileunlock(f); | |
533 if(ff != nil) | |
534 vacfiledecref(ff); | |
535 return nil; | |
536 } | |
537 | |
538 /* | |
539 * Open a path in the vac file system: | |
540 * just walk each element one at a time. | |
541 */ | |
542 VacFile* | |
543 vacfileopen(VacFs *fs, char *path) | |
544 { | |
545 VacFile *f, *ff; | |
546 char *p, elem[VtMaxStringSize], *opath; | |
547 int n; | |
548 | |
549 f = fs->root; | |
550 vacfileincref(f); | |
551 opath = path; | |
552 while(*path != 0){ | |
553 for(p = path; *p && *p != '/'; p++) | |
554 ; | |
555 n = p - path; | |
556 if(n > 0){ | |
557 if(n > VtMaxStringSize){ | |
558 werrstr("%s: element too long", EBadPath… | |
559 goto Err; | |
560 } | |
561 memmove(elem, path, n); | |
562 elem[n] = 0; | |
563 ff = vacfilewalk(f, elem); | |
564 if(ff == nil){ | |
565 werrstr("%.*s: %r", utfnlen(opath, p-opa… | |
566 goto Err; | |
567 } | |
568 vacfiledecref(f); | |
569 f = ff; | |
570 } | |
571 if(*p == '/') | |
572 p++; | |
573 path = p; | |
574 } | |
575 return f; | |
576 Err: | |
577 vacfiledecref(f); | |
578 return nil; | |
579 } | |
580 | |
581 /* | |
582 * Extract the score for the bn'th block in f. | |
583 */ | |
584 int | |
585 vacfileblockscore(VacFile *f, u32int bn, u8int *score) | |
586 { | |
587 VtFile *s; | |
588 uvlong size; | |
589 int dsize, ret; | |
590 | |
591 ret = -1; | |
592 if(filerlock(f) < 0) | |
593 return -1; | |
594 if(vtfilelock(f->source, VtOREAD) < 0) | |
595 goto out; | |
596 | |
597 s = f->source; | |
598 dsize = s->dsize; | |
599 size = vtfilegetsize(s); | |
600 if((uvlong)bn*dsize >= size) | |
601 goto out1; | |
602 ret = vtfileblockscore(f->source, bn, score); | |
603 | |
604 out1: | |
605 vtfileunlock(f->source); | |
606 out: | |
607 filerunlock(f); | |
608 return ret; | |
609 } | |
610 | |
611 /* | |
612 * Read data from f. | |
613 */ | |
614 int | |
615 vacfileread(VacFile *f, void *buf, int cnt, vlong offset) | |
616 { | |
617 int n; | |
618 | |
619 if(offset < 0){ | |
620 werrstr(EBadOffset); | |
621 return -1; | |
622 } | |
623 if(filerlock(f) < 0) | |
624 return -1; | |
625 if(vtfilelock(f->source, VtOREAD) < 0){ | |
626 filerunlock(f); | |
627 return -1; | |
628 } | |
629 n = vtfileread(f->source, buf, cnt, offset); | |
630 vtfileunlock(f->source); | |
631 filerunlock(f); | |
632 return n; | |
633 } | |
634 | |
635 static int | |
636 getentry(VtFile *f, VtEntry *e) | |
637 { | |
638 if(vtfilelock(f, VtOREAD) < 0) | |
639 return -1; | |
640 if(vtfilegetentry(f, e) < 0){ | |
641 vtfileunlock(f); | |
642 return -1; | |
643 } | |
644 vtfileunlock(f); | |
645 if(vtglobaltolocal(e->score) != NilBlock){ | |
646 werrstr("internal error - data not on venti"); | |
647 return -1; | |
648 } | |
649 return 0; | |
650 } | |
651 | |
652 /* | |
653 * Get the VtEntries for the data contained in f. | |
654 */ | |
655 int | |
656 vacfilegetentries(VacFile *f, VtEntry *e, VtEntry *me) | |
657 { | |
658 if(filerlock(f) < 0) | |
659 return -1; | |
660 if(e && getentry(f->source, e) < 0){ | |
661 filerunlock(f); | |
662 return -1; | |
663 } | |
664 if(me){ | |
665 if(f->msource == nil) | |
666 memset(me, 0, sizeof *me); | |
667 else if(getentry(f->msource, me) < 0){ | |
668 filerunlock(f); | |
669 return -1; | |
670 } | |
671 } | |
672 filerunlock(f); | |
673 return 0; | |
674 } | |
675 | |
676 /* | |
677 * Get the file's size. | |
678 */ | |
679 int | |
680 vacfilegetsize(VacFile *f, uvlong *size) | |
681 { | |
682 if(filerlock(f) < 0) | |
683 return -1; | |
684 if(vtfilelock(f->source, VtOREAD) < 0){ | |
685 filerunlock(f); | |
686 return -1; | |
687 } | |
688 *size = vtfilegetsize(f->source); | |
689 vtfileunlock(f->source); | |
690 filerunlock(f); | |
691 | |
692 return 0; | |
693 } | |
694 | |
695 /* | |
696 * Directory reading. | |
697 * | |
698 * A VacDirEnum is a buffer containing directory entries. | |
699 * Directory entries contain malloced strings and need to | |
700 * be cleaned up with vdcleanup. The invariant in the | |
701 * VacDirEnum is that the directory entries between | |
702 * vde->i and vde->n are owned by the vde and need to | |
703 * be cleaned up if it is closed. Those from 0 up to vde->i | |
704 * have been handed to the reader, and the reader must | |
705 * take care of calling vdcleanup as appropriate. | |
706 */ | |
707 VacDirEnum* | |
708 vdeopen(VacFile *f) | |
709 { | |
710 VacDirEnum *vde; | |
711 VacFile *p; | |
712 | |
713 if(!vacfileisdir(f)){ | |
714 werrstr(ENotDir); | |
715 return nil; | |
716 } | |
717 | |
718 /* | |
719 * There might be changes to this directory's children | |
720 * that have not been flushed out into the cache yet. | |
721 * Those changes are only available if we look at the | |
722 * VacFile structures directory. But the directory reader | |
723 * is going to read the cache blocks directly, so update them. | |
724 */ | |
725 if(filelock(f) < 0) | |
726 return nil; | |
727 for(p=f->down; p; p=p->next) | |
728 filemetaflush(p, nil); | |
729 fileunlock(f); | |
730 | |
731 vde = vtmallocz(sizeof(VacDirEnum)); | |
732 vde->file = vacfileincref(f); | |
733 | |
734 return vde; | |
735 } | |
736 | |
737 /* | |
738 * Figure out the size of the directory entry at offset. | |
739 * The rest of the metadata is kept in the data half, | |
740 * but since venti has to track the data size anyway, | |
741 * we just use that one and avoid updating the directory | |
742 * each time the file size changes. | |
743 */ | |
744 static int | |
745 direntrysize(VtFile *s, ulong offset, ulong gen, uvlong *size) | |
746 { | |
747 VtBlock *b; | |
748 ulong bn; | |
749 VtEntry e; | |
750 int epb; | |
751 | |
752 epb = s->dsize/VtEntrySize; | |
753 bn = offset/epb; | |
754 offset -= bn*epb; | |
755 | |
756 b = vtfileblock(s, bn, VtOREAD); | |
757 if(b == nil) | |
758 goto Err; | |
759 if(vtentryunpack(&e, b->data, offset) < 0) | |
760 goto Err; | |
761 | |
762 /* dangling entries are returned as zero size */ | |
763 if(!(e.flags & VtEntryActive) || e.gen != gen) | |
764 *size = 0; | |
765 else | |
766 *size = e.size; | |
767 vtblockput(b); | |
768 return 0; | |
769 | |
770 Err: | |
771 vtblockput(b); | |
772 return -1; | |
773 } | |
774 | |
775 /* | |
776 * Fill in vde with a new batch of directory entries. | |
777 */ | |
778 static int | |
779 vdefill(VacDirEnum *vde) | |
780 { | |
781 int i, n; | |
782 VtFile *meta, *source; | |
783 MetaBlock mb; | |
784 MetaEntry me; | |
785 VacFile *f; | |
786 VtBlock *b; | |
787 VacDir *de; | |
788 | |
789 /* clean up first */ | |
790 for(i=vde->i; i<vde->n; i++) | |
791 vdcleanup(vde->buf+i); | |
792 vtfree(vde->buf); | |
793 vde->buf = nil; | |
794 vde->i = 0; | |
795 vde->n = 0; | |
796 | |
797 f = vde->file; | |
798 | |
799 source = f->source; | |
800 meta = f->msource; | |
801 | |
802 b = vtfileblock(meta, vde->boff, VtOREAD); | |
803 if(b == nil) | |
804 goto Err; | |
805 if(mbunpack(&mb, b->data, meta->dsize) < 0) | |
806 goto Err; | |
807 | |
808 n = mb.nindex; | |
809 vde->buf = vtmalloc(n * sizeof(VacDir)); | |
810 | |
811 for(i=0; i<n; i++){ | |
812 de = vde->buf + i; | |
813 meunpack(&me, &mb, i); | |
814 if(vdunpack(de, &me) < 0) | |
815 goto Err; | |
816 vde->n++; | |
817 if(!(de->mode & ModeDir)) | |
818 if(direntrysize(source, de->entry, de->gen, &de->size) <… | |
819 goto Err; | |
820 } | |
821 vde->boff++; | |
822 vtblockput(b); | |
823 return 0; | |
824 Err: | |
825 vtblockput(b); | |
826 return -1; | |
827 } | |
828 | |
829 /* | |
830 * Read a single directory entry from vde into de. | |
831 * Returns -1 on error, 0 on EOF, and 1 on success. | |
832 * When it returns 1, it becomes the caller's responsibility | |
833 * to call vdcleanup(de) to free the strings contained | |
834 * inside, or else to call vdunread to give it back. | |
835 */ | |
836 int | |
837 vderead(VacDirEnum *vde, VacDir *de) | |
838 { | |
839 int ret; | |
840 VacFile *f; | |
841 u32int nb; | |
842 | |
843 f = vde->file; | |
844 if(filerlock(f) < 0) | |
845 return -1; | |
846 | |
847 if(vtfilelock2(f->source, f->msource, VtOREAD) < 0){ | |
848 filerunlock(f); | |
849 return -1; | |
850 } | |
851 | |
852 nb = (vtfilegetsize(f->msource)+f->msource->dsize-1)/f->msource-… | |
853 | |
854 while(vde->i >= vde->n){ | |
855 if(vde->boff >= nb){ | |
856 ret = 0; | |
857 goto Return; | |
858 } | |
859 if(vdefill(vde) < 0){ | |
860 ret = -1; | |
861 goto Return; | |
862 } | |
863 } | |
864 | |
865 memmove(de, vde->buf + vde->i, sizeof(VacDir)); | |
866 vde->i++; | |
867 ret = 1; | |
868 | |
869 Return: | |
870 vtfileunlock(f->source); | |
871 vtfileunlock(f->msource); | |
872 filerunlock(f); | |
873 | |
874 return ret; | |
875 } | |
876 | |
877 /* | |
878 * "Unread" the last directory entry that was read, | |
879 * so that the next vderead will return the same one. | |
880 * If the caller calls vdeunread(vde) it should not call | |
881 * vdcleanup on the entry being "unread". | |
882 */ | |
883 int | |
884 vdeunread(VacDirEnum *vde) | |
885 { | |
886 if(vde->i > 0){ | |
887 vde->i--; | |
888 return 0; | |
889 } | |
890 return -1; | |
891 } | |
892 | |
893 /* | |
894 * Close the enumerator. | |
895 */ | |
896 void | |
897 vdeclose(VacDirEnum *vde) | |
898 { | |
899 int i; | |
900 if(vde == nil) | |
901 return; | |
902 /* free the strings */ | |
903 for(i=vde->i; i<vde->n; i++) | |
904 vdcleanup(vde->buf+i); | |
905 vtfree(vde->buf); | |
906 vacfiledecref(vde->file); | |
907 vtfree(vde); | |
908 } | |
909 | |
910 | |
911 /* | |
912 * On to mutation. If the vac file system has been opened | |
913 * read-write, then the files and directories can all be edited. | |
914 * Changes are kept in the in-memory cache until flushed out | |
915 * to venti, so we must be careful to explicitly flush data | |
916 * that we're not likely to modify again. | |
917 * | |
918 * Each VacFile has its own copy of its VacDir directory entry | |
919 * in f->dir, but otherwise the cache is the authoratative source | |
920 * for data. Thus, for the most part, it suffices if we just | |
921 * call vtfileflushbefore and vtfileflush when we modify things. | |
922 * There are a few places where we have to remember to write | |
923 * changed VacDirs back into the cache. If f->dir *is* out of sync, | |
924 * then f->dirty should be set. | |
925 * | |
926 * The metadata in a directory is, to venti, a plain data file, | |
927 * but as mentioned above it is actually a sequence of | |
928 * MetaBlocks that contain sorted lists of VacDir entries. | |
929 * The filemetaxxx routines manipulate that stream. | |
930 */ | |
931 | |
932 /* | |
933 * Find space in fp for the directory entry dir (not yet written to disk) | |
934 * and write it to disk, returning NilBlock on failure, | |
935 * or the block number on success. | |
936 * | |
937 * Start is a suggested block number to try. | |
938 * The caller must have filemetalock'ed f and have | |
939 * vtfilelock'ed f->up->msource. | |
940 */ | |
941 static u32int | |
942 filemetaalloc(VacFile *fp, VacDir *dir, u32int start) | |
943 { | |
944 u32int nb, bo; | |
945 VtBlock *b; | |
946 MetaBlock mb; | |
947 int nn; | |
948 uchar *p; | |
949 int i, n; | |
950 MetaEntry me; | |
951 VtFile *ms; | |
952 | |
953 ms = fp->msource; | |
954 n = vdsize(dir, VacDirVersion); | |
955 | |
956 /* Look for a block with room for a new entry of size n. */ | |
957 nb = (vtfilegetsize(ms)+ms->dsize-1)/ms->dsize; | |
958 if(start == NilBlock){ | |
959 if(nb > 0) | |
960 start = nb - 1; | |
961 else | |
962 start = 0; | |
963 } | |
964 | |
965 if(start > nb) | |
966 start = nb; | |
967 for(bo=start; bo<nb; bo++){ | |
968 if((b = vtfileblock(ms, bo, VtOREAD)) == nil) | |
969 goto Err; | |
970 if(mbunpack(&mb, b->data, ms->dsize) < 0) | |
971 goto Err; | |
972 nn = (mb.maxsize*FullPercentage/100) - mb.size + mb.free; | |
973 if(n <= nn && mb.nindex < mb.maxindex){ | |
974 /* reopen for writing */ | |
975 vtblockput(b); | |
976 if((b = vtfileblock(ms, bo, VtORDWR)) == nil) | |
977 goto Err; | |
978 mbunpack(&mb, b->data, ms->dsize); | |
979 goto Found; | |
980 } | |
981 vtblockput(b); | |
982 } | |
983 | |
984 /* No block found, extend the file by one metablock. */ | |
985 vtfileflushbefore(ms, nb*(uvlong)ms->dsize); | |
986 if((b = vtfileblock(ms, nb, VtORDWR)) == nil) | |
987 goto Err; | |
988 vtfilesetsize(ms, (nb+1)*ms->dsize); | |
989 mbinit(&mb, b->data, ms->dsize, ms->dsize/BytesPerEntry); | |
990 | |
991 Found: | |
992 /* Now we have a block; allocate space to write the entry. */ | |
993 p = mballoc(&mb, n); | |
994 if(p == nil){ | |
995 /* mballoc might have changed block */ | |
996 mbpack(&mb); | |
997 werrstr(EBadMeta); | |
998 goto Err; | |
999 } | |
1000 | |
1001 /* Figure out where to put the index entry, and write it. */ | |
1002 mbsearch(&mb, dir->elem, &i, &me); | |
1003 assert(me.p == nil); /* not already there */ | |
1004 me.p = p; | |
1005 me.size = n; | |
1006 vdpack(dir, &me, VacDirVersion); | |
1007 mbinsert(&mb, i, &me); | |
1008 mbpack(&mb); | |
1009 vtblockput(b); | |
1010 return bo; | |
1011 | |
1012 Err: | |
1013 vtblockput(b); | |
1014 return NilBlock; | |
1015 } | |
1016 | |
1017 /* | |
1018 * Update f's directory entry in the block cache. | |
1019 * We look for the directory entry by name; | |
1020 * if we're trying to rename the file, oelem is the old name. | |
1021 * | |
1022 * Assumes caller has filemetalock'ed f. | |
1023 */ | |
1024 static int | |
1025 filemetaflush(VacFile *f, char *oelem) | |
1026 { | |
1027 int i, n; | |
1028 MetaBlock mb; | |
1029 MetaEntry me, me2; | |
1030 VacFile *fp; | |
1031 VtBlock *b; | |
1032 u32int bo; | |
1033 | |
1034 if(!f->dirty) | |
1035 return 0; | |
1036 | |
1037 if(oelem == nil) | |
1038 oelem = f->dir.elem; | |
1039 | |
1040 /* | |
1041 * Locate f's old metadata in the parent's metadata file. | |
1042 * We know which block it was in, but not exactly where | |
1043 * in the block. | |
1044 */ | |
1045 fp = f->up; | |
1046 if(vtfilelock(fp->msource, -1) < 0) | |
1047 return -1; | |
1048 /* can happen if source is clri'ed out from under us */ | |
1049 if(f->boff == NilBlock) | |
1050 goto Err1; | |
1051 b = vtfileblock(fp->msource, f->boff, VtORDWR); | |
1052 if(b == nil) | |
1053 goto Err1; | |
1054 if(mbunpack(&mb, b->data, fp->msource->dsize) < 0) | |
1055 goto Err; | |
1056 if(mbsearch(&mb, oelem, &i, &me) < 0) | |
1057 goto Err; | |
1058 | |
1059 /* | |
1060 * Check whether we can resize the entry and keep it | |
1061 * in this block. | |
1062 */ | |
1063 n = vdsize(&f->dir, VacDirVersion); | |
1064 if(mbresize(&mb, &me, n) >= 0){ | |
1065 /* Okay, can be done without moving to another block. */ | |
1066 | |
1067 /* Remove old data */ | |
1068 mbdelete(&mb, i, &me); | |
1069 | |
1070 /* Find new location if renaming */ | |
1071 if(strcmp(f->dir.elem, oelem) != 0) | |
1072 mbsearch(&mb, f->dir.elem, &i, &me2); | |
1073 | |
1074 /* Pack new data into new location. */ | |
1075 vdpack(&f->dir, &me, VacDirVersion); | |
1076 vdunpack(&f->dir, &me); | |
1077 mbinsert(&mb, i, &me); | |
1078 mbpack(&mb); | |
1079 | |
1080 /* Done */ | |
1081 vtblockput(b); | |
1082 vtfileunlock(fp->msource); | |
1083 f->dirty = 0; | |
1084 return 0; | |
1085 } | |
1086 | |
1087 /* | |
1088 * The entry must be moved to another block. | |
1089 * This can only really happen on renames that | |
1090 * make the name very long. | |
1091 */ | |
1092 | |
1093 /* Allocate a spot in a new block. */ | |
1094 if((bo = filemetaalloc(fp, &f->dir, f->boff+1)) == NilBlock){ | |
1095 /* mbresize above might have modified block */ | |
1096 mbpack(&mb); | |
1097 goto Err; | |
1098 } | |
1099 f->boff = bo; | |
1100 | |
1101 /* Now we're committed. Delete entry in old block. */ | |
1102 mbdelete(&mb, i, &me); | |
1103 mbpack(&mb); | |
1104 vtblockput(b); | |
1105 vtfileunlock(fp->msource); | |
1106 | |
1107 f->dirty = 0; | |
1108 return 0; | |
1109 | |
1110 Err: | |
1111 vtblockput(b); | |
1112 Err1: | |
1113 vtfileunlock(fp->msource); | |
1114 return -1; | |
1115 } | |
1116 | |
1117 /* | |
1118 * Remove the directory entry for f. | |
1119 */ | |
1120 static int | |
1121 filemetaremove(VacFile *f) | |
1122 { | |
1123 VtBlock *b; | |
1124 MetaBlock mb; | |
1125 MetaEntry me; | |
1126 int i; | |
1127 VacFile *fp; | |
1128 | |
1129 b = nil; | |
1130 fp = f->up; | |
1131 filemetalock(f); | |
1132 | |
1133 if(vtfilelock(fp->msource, VtORDWR) < 0) | |
1134 goto Err; | |
1135 b = vtfileblock(fp->msource, f->boff, VtORDWR); | |
1136 if(b == nil) | |
1137 goto Err; | |
1138 | |
1139 if(mbunpack(&mb, b->data, fp->msource->dsize) < 0) | |
1140 goto Err; | |
1141 if(mbsearch(&mb, f->dir.elem, &i, &me) < 0) | |
1142 goto Err; | |
1143 mbdelete(&mb, i, &me); | |
1144 mbpack(&mb); | |
1145 vtblockput(b); | |
1146 vtfileunlock(fp->msource); | |
1147 | |
1148 f->removed = 1; | |
1149 f->boff = NilBlock; | |
1150 f->dirty = 0; | |
1151 | |
1152 filemetaunlock(f); | |
1153 return 0; | |
1154 | |
1155 Err: | |
1156 vtfileunlock(fp->msource); | |
1157 vtblockput(b); | |
1158 filemetaunlock(f); | |
1159 return -1; | |
1160 } | |
1161 | |
1162 /* | |
1163 * That was far too much effort for directory entries. | |
1164 * Now we can write code that *does* things. | |
1165 */ | |
1166 | |
1167 /* | |
1168 * Flush all data associated with f out of the cache and onto venti. | |
1169 * If recursive is set, flush f's children too. | |
1170 * Vacfiledecref knows how to flush source and msource too. | |
1171 */ | |
1172 int | |
1173 vacfileflush(VacFile *f, int recursive) | |
1174 { | |
1175 int ret; | |
1176 VacFile **kids, *p; | |
1177 int i, nkids; | |
1178 | |
1179 if(f->mode == VtOREAD) | |
1180 return 0; | |
1181 | |
1182 ret = 0; | |
1183 filemetalock(f); | |
1184 if(filemetaflush(f, nil) < 0) | |
1185 ret = -1; | |
1186 filemetaunlock(f); | |
1187 | |
1188 if(filelock(f) < 0) | |
1189 return -1; | |
1190 | |
1191 /* | |
1192 * Lock order prevents us from flushing kids while holding | |
1193 * lock, so make a list and then flush without the lock. | |
1194 */ | |
1195 nkids = 0; | |
1196 kids = nil; | |
1197 if(recursive){ | |
1198 nkids = 0; | |
1199 for(p=f->down; p; p=p->next) | |
1200 nkids++; | |
1201 kids = vtmalloc(nkids*sizeof(VacFile*)); | |
1202 i = 0; | |
1203 for(p=f->down; p; p=p->next){ | |
1204 kids[i++] = p; | |
1205 p->ref++; | |
1206 } | |
1207 } | |
1208 if(nkids > 0){ | |
1209 fileunlock(f); | |
1210 for(i=0; i<nkids; i++){ | |
1211 if(vacfileflush(kids[i], 1) < 0) | |
1212 ret = -1; | |
1213 vacfiledecref(kids[i]); | |
1214 } | |
1215 filelock(f); | |
1216 } | |
1217 free(kids); | |
1218 | |
1219 /* | |
1220 * Now we can flush our own data. | |
1221 */ | |
1222 vtfilelock(f->source, -1); | |
1223 if(vtfileflush(f->source) < 0) | |
1224 ret = -1; | |
1225 vtfileunlock(f->source); | |
1226 if(f->msource){ | |
1227 vtfilelock(f->msource, -1); | |
1228 if(vtfileflush(f->msource) < 0) | |
1229 ret = -1; | |
1230 vtfileunlock(f->msource); | |
1231 } | |
1232 fileunlock(f); | |
1233 | |
1234 return ret; | |
1235 } | |
1236 | |
1237 /* | |
1238 * Create a new file named elem in fp with the given mode. | |
1239 * The mode can be changed later except for the ModeDir bit. | |
1240 */ | |
1241 VacFile* | |
1242 vacfilecreate(VacFile *fp, char *elem, ulong mode) | |
1243 { | |
1244 VacFile *ff; | |
1245 VacDir *dir; | |
1246 VtFile *pr, *r, *mr; | |
1247 int type; | |
1248 u32int bo; | |
1249 | |
1250 if(filelock(fp) < 0) | |
1251 return nil; | |
1252 | |
1253 /* | |
1254 * First, look to see that there's not a file in memory | |
1255 * with the same name. | |
1256 */ | |
1257 for(ff = fp->down; ff; ff=ff->next){ | |
1258 if(strcmp(elem, ff->dir.elem) == 0 && !ff->removed){ | |
1259 ff = nil; | |
1260 werrstr(EExists); | |
1261 goto Err1; | |
1262 } | |
1263 } | |
1264 | |
1265 /* | |
1266 * Next check the venti blocks. | |
1267 */ | |
1268 ff = dirlookup(fp, elem); | |
1269 if(ff != nil){ | |
1270 werrstr(EExists); | |
1271 goto Err1; | |
1272 } | |
1273 | |
1274 /* | |
1275 * By the way, you can't create in a read-only file system. | |
1276 */ | |
1277 pr = fp->source; | |
1278 if(pr->mode != VtORDWR){ | |
1279 werrstr(EReadOnly); | |
1280 goto Err1; | |
1281 } | |
1282 | |
1283 /* | |
1284 * Okay, time to actually create something. Lock the two | |
1285 * halves of the directory and create a file. | |
1286 */ | |
1287 if(vtfilelock2(fp->source, fp->msource, -1) < 0) | |
1288 goto Err1; | |
1289 ff = filealloc(fp->fs); | |
1290 ff->qidoffset = fp->qidoffset; /* hopefully fp->qidoffset… | |
1291 type = VtDataType; | |
1292 if(mode & ModeDir) | |
1293 type = VtDirType; | |
1294 mr = nil; | |
1295 if((r = vtfilecreate(pr, pr->psize, pr->dsize, type)) == nil) | |
1296 goto Err; | |
1297 if(mode & ModeDir) | |
1298 if((mr = vtfilecreate(pr, pr->psize, pr->dsize, VtDataType)) == … | |
1299 goto Err; | |
1300 | |
1301 /* | |
1302 * Fill in the directory entry and write it to disk. | |
1303 */ | |
1304 dir = &ff->dir; | |
1305 dir->elem = vtstrdup(elem); | |
1306 dir->entry = r->offset; | |
1307 dir->gen = r->gen; | |
1308 if(mode & ModeDir){ | |
1309 dir->mentry = mr->offset; | |
1310 dir->mgen = mr->gen; | |
1311 } | |
1312 dir->size = 0; | |
1313 if(_vacfsnextqid(fp->fs, &dir->qid) < 0) | |
1314 goto Err; | |
1315 dir->uid = vtstrdup(fp->dir.uid); | |
1316 dir->gid = vtstrdup(fp->dir.gid); | |
1317 dir->mid = vtstrdup(""); | |
1318 dir->mtime = time(0L); | |
1319 dir->mcount = 0; | |
1320 dir->ctime = dir->mtime; | |
1321 dir->atime = dir->mtime; | |
1322 dir->mode = mode; | |
1323 if((bo = filemetaalloc(fp, &ff->dir, NilBlock)) == NilBlock) | |
1324 goto Err; | |
1325 | |
1326 /* | |
1327 * Now we're committed. | |
1328 */ | |
1329 vtfileunlock(fp->source); | |
1330 vtfileunlock(fp->msource); | |
1331 ff->source = r; | |
1332 ff->msource = mr; | |
1333 ff->boff = bo; | |
1334 | |
1335 /* Link into tree. */ | |
1336 ff->next = fp->down; | |
1337 fp->down = ff; | |
1338 ff->up = fp; | |
1339 vacfileincref(fp); | |
1340 | |
1341 fileunlock(fp); | |
1342 | |
1343 filelock(ff); | |
1344 vtfilelock(ff->source, -1); | |
1345 vtfileunlock(ff->source); | |
1346 fileunlock(ff); | |
1347 | |
1348 return ff; | |
1349 | |
1350 Err: | |
1351 vtfileunlock(fp->source); | |
1352 vtfileunlock(fp->msource); | |
1353 if(r){ | |
1354 vtfilelock(r, -1); | |
1355 vtfileremove(r); | |
1356 } | |
1357 if(mr){ | |
1358 vtfilelock(mr, -1); | |
1359 vtfileremove(mr); | |
1360 } | |
1361 Err1: | |
1362 if(ff) | |
1363 vacfiledecref(ff); | |
1364 fileunlock(fp); | |
1365 return nil; | |
1366 } | |
1367 | |
1368 /* | |
1369 * Change the size of the file f. | |
1370 */ | |
1371 int | |
1372 vacfilesetsize(VacFile *f, uvlong size) | |
1373 { | |
1374 if(vacfileisdir(f)){ | |
1375 werrstr(ENotFile); | |
1376 return -1; | |
1377 } | |
1378 | |
1379 if(filelock(f) < 0) | |
1380 return -1; | |
1381 | |
1382 if(f->source->mode != VtORDWR){ | |
1383 werrstr(EReadOnly); | |
1384 goto Err; | |
1385 } | |
1386 if(vtfilelock(f->source, -1) < 0) | |
1387 goto Err; | |
1388 if(vtfilesetsize(f->source, size) < 0){ | |
1389 vtfileunlock(f->source); | |
1390 goto Err; | |
1391 } | |
1392 vtfileunlock(f->source); | |
1393 fileunlock(f); | |
1394 return 0; | |
1395 | |
1396 Err: | |
1397 fileunlock(f); | |
1398 return -1; | |
1399 } | |
1400 | |
1401 /* | |
1402 * Write data to f. | |
1403 */ | |
1404 int | |
1405 vacfilewrite(VacFile *f, void *buf, int cnt, vlong offset) | |
1406 { | |
1407 if(vacfileisdir(f)){ | |
1408 werrstr(ENotFile); | |
1409 return -1; | |
1410 } | |
1411 if(filelock(f) < 0) | |
1412 return -1; | |
1413 if(f->source->mode != VtORDWR){ | |
1414 werrstr(EReadOnly); | |
1415 goto Err; | |
1416 } | |
1417 if(offset < 0){ | |
1418 werrstr(EBadOffset); | |
1419 goto Err; | |
1420 } | |
1421 | |
1422 if(vtfilelock(f->source, -1) < 0) | |
1423 goto Err; | |
1424 if(f->dir.mode & ModeAppend) | |
1425 offset = vtfilegetsize(f->source); | |
1426 if(vtfilewrite(f->source, buf, cnt, offset) != cnt | |
1427 || vtfileflushbefore(f->source, offset) < 0){ | |
1428 vtfileunlock(f->source); | |
1429 goto Err; | |
1430 } | |
1431 vtfileunlock(f->source); | |
1432 fileunlock(f); | |
1433 return cnt; | |
1434 | |
1435 Err: | |
1436 fileunlock(f); | |
1437 return -1; | |
1438 } | |
1439 | |
1440 /* | |
1441 * Set (!) the VtEntry for the data contained in f. | |
1442 * This let's us efficiently copy data from one file to another. | |
1443 */ | |
1444 int | |
1445 vacfilesetentries(VacFile *f, VtEntry *e, VtEntry *me) | |
1446 { | |
1447 int ret; | |
1448 | |
1449 vacfileflush(f, 0); /* flush blocks to venti, since we wo… | |
1450 | |
1451 if(!(e->flags&VtEntryActive)){ | |
1452 werrstr("missing entry for source"); | |
1453 return -1; | |
1454 } | |
1455 if(me && !(me->flags&VtEntryActive)) | |
1456 me = nil; | |
1457 if(f->msource && !me){ | |
1458 werrstr("missing entry for msource"); | |
1459 return -1; | |
1460 } | |
1461 if(me && !f->msource){ | |
1462 werrstr("no msource to set"); | |
1463 return -1; | |
1464 } | |
1465 | |
1466 if(filelock(f) < 0) | |
1467 return -1; | |
1468 if(f->source->mode != VtORDWR | |
1469 || (f->msource && f->msource->mode != VtORDWR)){ | |
1470 werrstr(EReadOnly); | |
1471 fileunlock(f); | |
1472 return -1; | |
1473 } | |
1474 if(vtfilelock2(f->source, f->msource, -1) < 0){ | |
1475 fileunlock(f); | |
1476 return -1; | |
1477 } | |
1478 ret = 0; | |
1479 if(vtfilesetentry(f->source, e) < 0) | |
1480 ret = -1; | |
1481 else if(me && vtfilesetentry(f->msource, me) < 0) | |
1482 ret = -1; | |
1483 | |
1484 vtfileunlock(f->source); | |
1485 if(f->msource) | |
1486 vtfileunlock(f->msource); | |
1487 fileunlock(f); | |
1488 return ret; | |
1489 } | |
1490 | |
1491 /* | |
1492 * Get the directory entry for f. | |
1493 */ | |
1494 int | |
1495 vacfilegetdir(VacFile *f, VacDir *dir) | |
1496 { | |
1497 if(filerlock(f) < 0) | |
1498 return -1; | |
1499 | |
1500 filemetalock(f); | |
1501 vdcopy(dir, &f->dir); | |
1502 filemetaunlock(f); | |
1503 | |
1504 if(!vacfileisdir(f)){ | |
1505 if(vtfilelock(f->source, VtOREAD) < 0){ | |
1506 filerunlock(f); | |
1507 return -1; | |
1508 } | |
1509 dir->size = vtfilegetsize(f->source); | |
1510 vtfileunlock(f->source); | |
1511 } | |
1512 filerunlock(f); | |
1513 | |
1514 return 0; | |
1515 } | |
1516 | |
1517 /* | |
1518 * Set the directory entry for f. | |
1519 */ | |
1520 int | |
1521 vacfilesetdir(VacFile *f, VacDir *dir) | |
1522 { | |
1523 VacFile *ff; | |
1524 char *oelem; | |
1525 u32int mask; | |
1526 u64int size; | |
1527 | |
1528 /* can not set permissions for the root */ | |
1529 if(vacfileisroot(f)){ | |
1530 werrstr(ERoot); | |
1531 return -1; | |
1532 } | |
1533 | |
1534 if(filelock(f) < 0) | |
1535 return -1; | |
1536 filemetalock(f); | |
1537 | |
1538 if(f->source->mode != VtORDWR){ | |
1539 werrstr(EReadOnly); | |
1540 goto Err; | |
1541 } | |
1542 | |
1543 /* On rename, check new name does not already exist */ | |
1544 if(strcmp(f->dir.elem, dir->elem) != 0){ | |
1545 for(ff = f->up->down; ff; ff=ff->next){ | |
1546 if(strcmp(dir->elem, ff->dir.elem) == 0 && !ff->… | |
1547 werrstr(EExists); | |
1548 goto Err; | |
1549 } | |
1550 } | |
1551 ff = dirlookup(f->up, dir->elem); | |
1552 if(ff != nil){ | |
1553 vacfiledecref(ff); | |
1554 werrstr(EExists); | |
1555 goto Err; | |
1556 } | |
1557 werrstr(""); /* "failed" dirlookup poisoned it */ | |
1558 } | |
1559 | |
1560 /* Get ready... */ | |
1561 if(vtfilelock2(f->source, f->msource, -1) < 0) | |
1562 goto Err; | |
1563 if(!vacfileisdir(f)){ | |
1564 size = vtfilegetsize(f->source); | |
1565 if(size != dir->size){ | |
1566 if(vtfilesetsize(f->source, dir->size) < 0){ | |
1567 vtfileunlock(f->source); | |
1568 if(f->msource) | |
1569 vtfileunlock(f->msource); | |
1570 goto Err; | |
1571 } | |
1572 } | |
1573 } | |
1574 /* ... now commited to changing it. */ | |
1575 vtfileunlock(f->source); | |
1576 if(f->msource) | |
1577 vtfileunlock(f->msource); | |
1578 | |
1579 oelem = nil; | |
1580 if(strcmp(f->dir.elem, dir->elem) != 0){ | |
1581 oelem = f->dir.elem; | |
1582 f->dir.elem = vtstrdup(dir->elem); | |
1583 } | |
1584 | |
1585 if(strcmp(f->dir.uid, dir->uid) != 0){ | |
1586 vtfree(f->dir.uid); | |
1587 f->dir.uid = vtstrdup(dir->uid); | |
1588 } | |
1589 | |
1590 if(strcmp(f->dir.gid, dir->gid) != 0){ | |
1591 vtfree(f->dir.gid); | |
1592 f->dir.gid = vtstrdup(dir->gid); | |
1593 } | |
1594 | |
1595 if(strcmp(f->dir.mid, dir->mid) != 0){ | |
1596 vtfree(f->dir.mid); | |
1597 f->dir.mid = vtstrdup(dir->mid); | |
1598 } | |
1599 | |
1600 f->dir.mtime = dir->mtime; | |
1601 f->dir.atime = dir->atime; | |
1602 | |
1603 mask = ~(ModeDir|ModeSnapshot); | |
1604 f->dir.mode &= ~mask; | |
1605 f->dir.mode |= mask & dir->mode; | |
1606 f->dirty = 1; | |
1607 | |
1608 if(filemetaflush(f, oelem) < 0){ | |
1609 vtfree(oelem); | |
1610 goto Err; /* that sucks */ | |
1611 } | |
1612 vtfree(oelem); | |
1613 | |
1614 filemetaunlock(f); | |
1615 fileunlock(f); | |
1616 return 0; | |
1617 | |
1618 Err: | |
1619 filemetaunlock(f); | |
1620 fileunlock(f); | |
1621 return -1; | |
1622 } | |
1623 | |
1624 /* | |
1625 * Set the qid space. | |
1626 */ | |
1627 int | |
1628 vacfilesetqidspace(VacFile *f, u64int offset, u64int max) | |
1629 { | |
1630 int ret; | |
1631 | |
1632 if(filelock(f) < 0) | |
1633 return -1; | |
1634 if(f->source->mode != VtORDWR){ | |
1635 fileunlock(f); | |
1636 werrstr(EReadOnly); | |
1637 return -1; | |
1638 } | |
1639 filemetalock(f); | |
1640 f->dir.qidspace = 1; | |
1641 f->dir.qidoffset = offset; | |
1642 f->dir.qidmax = max; | |
1643 f->dirty = 1; | |
1644 ret = filemetaflush(f, nil); | |
1645 filemetaunlock(f); | |
1646 fileunlock(f); | |
1647 return ret; | |
1648 } | |
1649 | |
1650 /* | |
1651 * Check that the file is empty, returning 0 if it is. | |
1652 * Returns -1 on error (and not being empty is an error). | |
1653 */ | |
1654 static int | |
1655 filecheckempty(VacFile *f) | |
1656 { | |
1657 u32int i, n; | |
1658 VtBlock *b; | |
1659 MetaBlock mb; | |
1660 VtFile *r; | |
1661 | |
1662 r = f->msource; | |
1663 n = (vtfilegetsize(r)+r->dsize-1)/r->dsize; | |
1664 for(i=0; i<n; i++){ | |
1665 b = vtfileblock(r, i, VtOREAD); | |
1666 if(b == nil) | |
1667 return -1; | |
1668 if(mbunpack(&mb, b->data, r->dsize) < 0) | |
1669 goto Err; | |
1670 if(mb.nindex > 0){ | |
1671 werrstr(ENotEmpty); | |
1672 goto Err; | |
1673 } | |
1674 vtblockput(b); | |
1675 } | |
1676 return 0; | |
1677 | |
1678 Err: | |
1679 vtblockput(b); | |
1680 return -1; | |
1681 } | |
1682 | |
1683 /* | |
1684 * Remove the vac file f. | |
1685 */ | |
1686 int | |
1687 vacfileremove(VacFile *f) | |
1688 { | |
1689 VacFile *ff; | |
1690 | |
1691 /* Cannot remove the root */ | |
1692 if(vacfileisroot(f)){ | |
1693 werrstr(ERoot); | |
1694 return -1; | |
1695 } | |
1696 | |
1697 if(filelock(f) < 0) | |
1698 return -1; | |
1699 if(f->source->mode != VtORDWR){ | |
1700 werrstr(EReadOnly); | |
1701 goto Err1; | |
1702 } | |
1703 if(vtfilelock2(f->source, f->msource, -1) < 0) | |
1704 goto Err1; | |
1705 if(vacfileisdir(f) && filecheckempty(f)<0) | |
1706 goto Err; | |
1707 | |
1708 for(ff=f->down; ff; ff=ff->next) | |
1709 assert(ff->removed); | |
1710 | |
1711 vtfileremove(f->source); | |
1712 f->source = nil; | |
1713 if(f->msource){ | |
1714 vtfileremove(f->msource); | |
1715 f->msource = nil; | |
1716 } | |
1717 fileunlock(f); | |
1718 | |
1719 if(filemetaremove(f) < 0) | |
1720 return -1; | |
1721 return 0; | |
1722 | |
1723 Err: | |
1724 vtfileunlock(f->source); | |
1725 if(f->msource) | |
1726 vtfileunlock(f->msource); | |
1727 Err1: | |
1728 fileunlock(f); | |
1729 return -1; | |
1730 } | |
1731 | |
1732 /* | |
1733 * Vac file system format. | |
1734 */ | |
1735 static char EBadVacFormat[] = "bad format for vac file"; | |
1736 | |
1737 static VacFs * | |
1738 vacfsalloc(VtConn *z, int bsize, ulong cachemem, int mode) | |
1739 { | |
1740 VacFs *fs; | |
1741 | |
1742 fs = vtmallocz(sizeof(VacFs)); | |
1743 fs->z = z; | |
1744 fs->bsize = bsize; | |
1745 fs->mode = mode; | |
1746 fs->cache = vtcachealloc(z, cachemem); | |
1747 return fs; | |
1748 } | |
1749 | |
1750 static int | |
1751 readscore(int fd, uchar score[VtScoreSize]) | |
1752 { | |
1753 char buf[45], *pref; | |
1754 int n; | |
1755 | |
1756 n = readn(fd, buf, sizeof(buf)-1); | |
1757 if(n < sizeof(buf)-1) { | |
1758 werrstr("short read"); | |
1759 return -1; | |
1760 } | |
1761 buf[n] = 0; | |
1762 | |
1763 if(vtparsescore(buf, &pref, score) < 0){ | |
1764 werrstr(EBadVacFormat); | |
1765 return -1; | |
1766 } | |
1767 if(pref==nil || strcmp(pref, "vac") != 0) { | |
1768 werrstr("not a vac file"); | |
1769 return -1; | |
1770 } | |
1771 return 0; | |
1772 } | |
1773 | |
1774 VacFs* | |
1775 vacfsopen(VtConn *z, char *file, int mode, ulong cachemem) | |
1776 { | |
1777 int fd; | |
1778 uchar score[VtScoreSize]; | |
1779 char *prefix; | |
1780 | |
1781 if(vtparsescore(file, &prefix, score) >= 0){ | |
1782 if(prefix == nil || strcmp(prefix, "vac") != 0){ | |
1783 werrstr("not a vac file"); | |
1784 return nil; | |
1785 } | |
1786 }else{ | |
1787 fd = open(file, OREAD); | |
1788 if(fd < 0) | |
1789 return nil; | |
1790 if(readscore(fd, score) < 0){ | |
1791 close(fd); | |
1792 return nil; | |
1793 } | |
1794 close(fd); | |
1795 } | |
1796 if(debug) fprint(2, "vacfsopen %V\n", score); | |
1797 return vacfsopenscore(z, score, mode, cachemem); | |
1798 } | |
1799 | |
1800 VacFs* | |
1801 vacfsopenscore(VtConn *z, u8int *score, int mode, ulong cachemem) | |
1802 { | |
1803 VacFs *fs; | |
1804 int n; | |
1805 VtRoot rt; | |
1806 uchar buf[VtRootSize]; | |
1807 VacFile *root; | |
1808 VtFile *r; | |
1809 VtEntry e; | |
1810 | |
1811 n = vtread(z, score, VtRootType, buf, VtRootSize); | |
1812 if(n < 0) { | |
1813 if(debug) fprint(2, "read %r\n"); | |
1814 return nil; | |
1815 } | |
1816 if(n != VtRootSize){ | |
1817 werrstr("vtread on root too short"); | |
1818 if(debug) fprint(2, "size %d\n", n); | |
1819 return nil; | |
1820 } | |
1821 | |
1822 if(vtrootunpack(&rt, buf) < 0) { | |
1823 if(debug) fprint(2, "unpack: %r\n"); | |
1824 return nil; | |
1825 } | |
1826 | |
1827 if(strcmp(rt.type, "vac") != 0) { | |
1828 if(debug) fprint(2, "bad type %s\n", rt.type); | |
1829 werrstr("not a vac root"); | |
1830 return nil; | |
1831 } | |
1832 | |
1833 fs = vacfsalloc(z, rt.blocksize, cachemem, mode); | |
1834 memmove(fs->score, score, VtScoreSize); | |
1835 fs->mode = mode; | |
1836 | |
1837 memmove(e.score, rt.score, VtScoreSize); | |
1838 e.gen = 0; | |
1839 | |
1840 // Don't waste cache memory on pointer blocks | |
1841 // when rt.blocksize is large. | |
1842 e.psize = (rt.blocksize/VtEntrySize)*VtEntrySize; | |
1843 if(e.psize > 60000) | |
1844 e.psize = (60000/VtEntrySize)*VtEntrySize; | |
1845 | |
1846 e.dsize = rt.blocksize; | |
1847 if(debug) fprint(2, "openscore %d psize %d dsize %d\n", (int)rt.blocksiz… | |
1848 e.type = VtDirType; | |
1849 e.flags = VtEntryActive; | |
1850 e.size = 3*VtEntrySize; | |
1851 | |
1852 root = nil; | |
1853 if((r = vtfileopenroot(fs->cache, &e)) == nil) | |
1854 goto Err; | |
1855 if(debug) | |
1856 fprint(2, "r %p\n", r); | |
1857 root = _vacfileroot(fs, r); | |
1858 if(debug) | |
1859 fprint(2, "root %p\n", root); | |
1860 vtfileclose(r); | |
1861 if(root == nil) | |
1862 goto Err; | |
1863 fs->root = root; | |
1864 return fs; | |
1865 Err: | |
1866 if(root) | |
1867 vacfiledecref(root); | |
1868 vacfsclose(fs); | |
1869 return nil; | |
1870 } | |
1871 | |
1872 int | |
1873 vacfsmode(VacFs *fs) | |
1874 { | |
1875 return fs->mode; | |
1876 } | |
1877 | |
1878 VacFile* | |
1879 vacfsgetroot(VacFs *fs) | |
1880 { | |
1881 return vacfileincref(fs->root); | |
1882 } | |
1883 | |
1884 int | |
1885 vacfsgetblocksize(VacFs *fs) | |
1886 { | |
1887 return fs->bsize; | |
1888 } | |
1889 | |
1890 int | |
1891 vacfsgetscore(VacFs *fs, u8int *score) | |
1892 { | |
1893 memmove(score, fs->score, VtScoreSize); | |
1894 return 0; | |
1895 } | |
1896 | |
1897 int | |
1898 _vacfsnextqid(VacFs *fs, uvlong *qid) | |
1899 { | |
1900 ++fs->qid; | |
1901 *qid = fs->qid; | |
1902 return 0; | |
1903 } | |
1904 | |
1905 void | |
1906 vacfsjumpqid(VacFs *fs, uvlong step) | |
1907 { | |
1908 fs->qid += step; | |
1909 } | |
1910 | |
1911 /* | |
1912 * Set *maxqid to the maximum qid expected in this file system. | |
1913 * In newer vac archives, the maximum qid is stored in the | |
1914 * qidspace VacDir annotation. In older vac archives, the root | |
1915 * got created last, so it had the maximum qid. | |
1916 */ | |
1917 int | |
1918 vacfsgetmaxqid(VacFs *fs, uvlong *maxqid) | |
1919 { | |
1920 VacDir vd; | |
1921 | |
1922 if(vacfilegetdir(fs->root, &vd) < 0) | |
1923 return -1; | |
1924 if(vd.qidspace) | |
1925 *maxqid = vd.qidmax; | |
1926 else | |
1927 *maxqid = vd.qid; | |
1928 vdcleanup(&vd); | |
1929 return 0; | |
1930 } | |
1931 | |
1932 | |
1933 void | |
1934 vacfsclose(VacFs *fs) | |
1935 { | |
1936 if(fs->root) | |
1937 vacfiledecref(fs->root); | |
1938 fs->root = nil; | |
1939 vtcachefree(fs->cache); | |
1940 vtfree(fs); | |
1941 } | |
1942 | |
1943 /* | |
1944 * Create a fresh vac fs. | |
1945 */ | |
1946 VacFs * | |
1947 vacfscreate(VtConn *z, int bsize, ulong cachemem) | |
1948 { | |
1949 VacFs *fs; | |
1950 VtFile *f; | |
1951 uchar buf[VtEntrySize], metascore[VtScoreSize]; | |
1952 VtEntry e; | |
1953 VtBlock *b; | |
1954 MetaBlock mb; | |
1955 VacDir vd; | |
1956 MetaEntry me; | |
1957 int psize; | |
1958 | |
1959 if((fs = vacfsalloc(z, bsize, cachemem, VtORDWR)) == nil) | |
1960 return nil; | |
1961 | |
1962 /* | |
1963 * Fake up an empty vac fs. | |
1964 */ | |
1965 psize = bsize/VtScoreSize*VtScoreSize; | |
1966 if(psize > 60000) | |
1967 psize = 60000/VtScoreSize*VtScoreSize; | |
1968 if(debug) fprint(2, "create bsize %d psize %d\n", bsize, psize); | |
1969 | |
1970 f = vtfilecreateroot(fs->cache, psize, bsize, VtDirType); | |
1971 if(f == nil) | |
1972 sysfatal("vtfilecreateroot: %r"); | |
1973 vtfilelock(f, VtORDWR); | |
1974 | |
1975 /* Write metablock containing root directory VacDir. */ | |
1976 b = vtcacheallocblock(fs->cache, VtDataType, bsize); | |
1977 mbinit(&mb, b->data, bsize, bsize/BytesPerEntry); | |
1978 memset(&vd, 0, sizeof vd); | |
1979 vd.elem = "/"; | |
1980 vd.mode = 0777|ModeDir; | |
1981 vd.uid = "vac"; | |
1982 vd.gid = "vac"; | |
1983 vd.mid = ""; | |
1984 me.size = vdsize(&vd, VacDirVersion); | |
1985 me.p = mballoc(&mb, me.size); | |
1986 vdpack(&vd, &me, VacDirVersion); | |
1987 mbinsert(&mb, 0, &me); | |
1988 mbpack(&mb); | |
1989 vtblockwrite(b); | |
1990 memmove(metascore, b->score, VtScoreSize); | |
1991 vtblockput(b); | |
1992 | |
1993 /* First entry: empty venti directory stream. */ | |
1994 memset(&e, 0, sizeof e); | |
1995 e.flags = VtEntryActive; | |
1996 e.psize = psize; | |
1997 e.dsize = bsize; | |
1998 e.type = VtDirType; | |
1999 memmove(e.score, vtzeroscore, VtScoreSize); | |
2000 vtentrypack(&e, buf, 0); | |
2001 vtfilewrite(f, buf, VtEntrySize, 0); | |
2002 | |
2003 /* Second entry: empty metadata stream. */ | |
2004 e.type = VtDataType; | |
2005 vtentrypack(&e, buf, 0); | |
2006 vtfilewrite(f, buf, VtEntrySize, VtEntrySize); | |
2007 | |
2008 /* Third entry: metadata stream with root directory. */ | |
2009 memmove(e.score, metascore, VtScoreSize); | |
2010 e.size = bsize; | |
2011 vtentrypack(&e, buf, 0); | |
2012 vtfilewrite(f, buf, VtEntrySize, VtEntrySize*2); | |
2013 | |
2014 vtfileflush(f); | |
2015 vtfileunlock(f); | |
2016 | |
2017 /* Now open it as a vac fs. */ | |
2018 fs->root = _vacfileroot(fs, f); | |
2019 if(fs->root == nil){ | |
2020 werrstr("vacfileroot: %r"); | |
2021 vacfsclose(fs); | |
2022 return nil; | |
2023 } | |
2024 | |
2025 return fs; | |
2026 } | |
2027 | |
2028 int | |
2029 vacfssync(VacFs *fs) | |
2030 { | |
2031 uchar buf[1024]; | |
2032 VtEntry e; | |
2033 VtFile *f; | |
2034 VtRoot root; | |
2035 | |
2036 /* Sync the entire vacfs to disk. */ | |
2037 if(vacfileflush(fs->root, 1) < 0) | |
2038 return -1; | |
2039 if(vtfilelock(fs->root->up->msource, -1) < 0) | |
2040 return -1; | |
2041 if(vtfileflush(fs->root->up->msource) < 0){ | |
2042 vtfileunlock(fs->root->up->msource); | |
2043 return -1; | |
2044 } | |
2045 vtfileunlock(fs->root->up->msource); | |
2046 | |
2047 /* Prepare the dir stream for the root block. */ | |
2048 if(getentry(fs->root->source, &e) < 0) | |
2049 return -1; | |
2050 vtentrypack(&e, buf, 0); | |
2051 if(getentry(fs->root->msource, &e) < 0) | |
2052 return -1; | |
2053 vtentrypack(&e, buf, 1); | |
2054 if(getentry(fs->root->up->msource, &e) < 0) | |
2055 return -1; | |
2056 vtentrypack(&e, buf, 2); | |
2057 | |
2058 f = vtfilecreateroot(fs->cache, fs->bsize, fs->bsize, VtDirType); | |
2059 vtfilelock(f, VtORDWR); | |
2060 if(vtfilewrite(f, buf, 3*VtEntrySize, 0) < 0 | |
2061 || vtfileflush(f) < 0){ | |
2062 vtfileunlock(f); | |
2063 vtfileclose(f); | |
2064 return -1; | |
2065 } | |
2066 vtfileunlock(f); | |
2067 if(getentry(f, &e) < 0){ | |
2068 vtfileclose(f); | |
2069 return -1; | |
2070 } | |
2071 vtfileclose(f); | |
2072 | |
2073 /* Build a root block. */ | |
2074 memset(&root, 0, sizeof root); | |
2075 strcpy(root.type, "vac"); | |
2076 strcpy(root.name, fs->name); | |
2077 memmove(root.score, e.score, VtScoreSize); | |
2078 root.blocksize = fs->bsize; | |
2079 memmove(root.prev, fs->score, VtScoreSize); | |
2080 vtrootpack(&root, buf); | |
2081 if(vtwrite(fs->z, fs->score, VtRootType, buf, VtRootSize) < 0){ | |
2082 werrstr("writing root: %r"); | |
2083 return -1; | |
2084 } | |
2085 if(vtsync(fs->z) < 0) | |
2086 return -1; | |
2087 return 0; | |
2088 } | |
2089 | |
2090 int | |
2091 vacfiledsize(VacFile *f) | |
2092 { | |
2093 VtEntry e; | |
2094 | |
2095 if(vacfilegetentries(f,&e,nil) < 0) | |
2096 return -1; | |
2097 return e.dsize; | |
2098 } | |
2099 | |
2100 /* | |
2101 * Does block b of f have the same SHA1 hash as the n bytes at buf? | |
2102 */ | |
2103 int | |
2104 sha1matches(VacFile *f, ulong b, uchar *buf, int n) | |
2105 { | |
2106 uchar fscore[VtScoreSize]; | |
2107 uchar bufscore[VtScoreSize]; | |
2108 | |
2109 if(vacfileblockscore(f, b, fscore) < 0) | |
2110 return 0; | |
2111 n = vtzerotruncate(VtDataType, buf, n); | |
2112 sha1(buf, n, bufscore, nil); | |
2113 if(memcmp(bufscore, fscore, VtScoreSize) == 0) | |
2114 return 1; | |
2115 return 0; | |
2116 } |