Warning: Attempt to read property "date" on null in /usr/local/www/websvn.planix.org/blame.php on line 247

Warning: Attempt to read property "msg" on null in /usr/local/www/websvn.planix.org/blame.php on line 247
WebSVN – planix.SVN – Blame – /os/branches/feature_posix/sys/src/cmd/fossil/fs.c – Rev 2

Subversion Repositories planix.SVN

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
#include "stdinc.h"
2
#include "dat.h"
3
#include "fns.h"
4
#include "error.h"
5
 
6
static void fsMetaFlush(void *a);
7
static Snap *snapInit(Fs*);
8
static void snapClose(Snap*);
9
 
10
Fs *
11
fsOpen(char *file, VtSession *z, long ncache, int mode)
12
{
13
	int fd, m;
14
	uchar oscore[VtScoreSize];
15
	Block *b, *bs;
16
	Disk *disk;
17
	Fs *fs;
18
	Super super;
19
 
20
	switch(mode){
21
	default:
22
		vtSetError(EBadMode);
23
		return nil;
24
	case OReadOnly:
25
		m = OREAD;
26
		break;
27
	case OReadWrite:
28
		m = ORDWR;
29
		break;
30
	}
31
	fd = open(file, m);
32
	if(fd < 0){
33
		vtSetError("open %s: %r", file);
34
		return nil;
35
	}
36
 
37
	bwatchInit();
38
	disk = diskAlloc(fd);
39
	if(disk == nil){
40
		vtSetError("diskAlloc: %R");
41
		close(fd);
42
		return nil;
43
	}
44
 
45
	fs = vtMemAllocZ(sizeof(Fs));
46
	fs->mode = mode;
47
	fs->name = vtStrDup(file);
48
	fs->blockSize = diskBlockSize(disk);
49
	fs->elk = vtLockAlloc();
50
	fs->cache = cacheAlloc(disk, z, ncache, mode);
51
	if(mode == OReadWrite && z)
52
		fs->arch = archInit(fs->cache, disk, fs, z);
53
	fs->z = z;
54
 
55
	b = cacheLocal(fs->cache, PartSuper, 0, mode);
56
	if(b == nil)
57
		goto Err;
58
	if(!superUnpack(&super, b->data)){
59
		blockPut(b);
60
		vtSetError("bad super block");
61
		goto Err;
62
	}
63
	blockPut(b);
64
 
65
	fs->ehi = super.epochHigh;
66
	fs->elo = super.epochLow;
67
 
68
//fprint(2, "%s: fs->ehi %d fs->elo %d active=%d\n", argv0, fs->ehi, fs->elo, super.active);
69
 
70
	fs->source = sourceRoot(fs, super.active, mode);
71
	if(fs->source == nil){
72
		/*
73
		 * Perhaps it failed because the block is copy-on-write.
74
		 * Do the copy and try again.
75
		 */
76
		if(mode == OReadOnly || strcmp(vtGetError(), EBadRoot) != 0)
77
			goto Err;
78
		b = cacheLocalData(fs->cache, super.active, BtDir, RootTag,
79
			OReadWrite, 0);
80
		if(b == nil){
81
			vtSetError("cacheLocalData: %R");
82
			goto Err;
83
		}
84
		if(b->l.epoch == fs->ehi){
85
			blockPut(b);
86
			vtSetError("bad root source block");
87
			goto Err;
88
		}
89
		b = blockCopy(b, RootTag, fs->ehi, fs->elo);
90
		if(b == nil)
91
			goto Err;
92
		localToGlobal(super.active, oscore);
93
		super.active = b->addr;
94
		bs = cacheLocal(fs->cache, PartSuper, 0, OReadWrite);
95
		if(bs == nil){
96
			blockPut(b);
97
			vtSetError("cacheLocal: %R");
98
			goto Err;
99
		}
100
		superPack(&super, bs->data);
101
		blockDependency(bs, b, 0, oscore, nil);
102
		blockPut(b);
103
		blockDirty(bs);
104
		blockRemoveLink(bs, globalToLocal(oscore), BtDir, RootTag, 0);
105
		blockPut(bs);
106
		fs->source = sourceRoot(fs, super.active, mode);
107
		if(fs->source == nil){
108
			vtSetError("sourceRoot: %R");
109
			goto Err;
110
		}
111
	}
112
 
113
//fprint(2, "%s: got fs source\n", argv0);
114
 
115
	vtRLock(fs->elk);
116
	fs->file = fileRoot(fs->source);
117
	fs->source->file = fs->file;		/* point back */
118
	vtRUnlock(fs->elk);
119
	if(fs->file == nil){
120
		vtSetError("fileRoot: %R");
121
		goto Err;
122
	}
123
 
124
//fprint(2, "%s: got file root\n", argv0);
125
 
126
	if(mode == OReadWrite){
127
		fs->metaFlush = periodicAlloc(fsMetaFlush, fs, 1000);
128
		fs->snap = snapInit(fs);
129
	}
130
	return fs;
131
 
132
Err:
133
fprint(2, "%s: fsOpen error\n", argv0);
134
	fsClose(fs);
135
	return nil;
136
}
137
 
138
void
139
fsClose(Fs *fs)
140
{
141
	vtRLock(fs->elk);
142
	periodicKill(fs->metaFlush);
143
	snapClose(fs->snap);
144
	if(fs->file){
145
		fileMetaFlush(fs->file, 0);
146
		if(!fileDecRef(fs->file))
147
			vtFatal("fsClose: files still in use: %r\n");
148
	}
149
	fs->file = nil;
150
	sourceClose(fs->source);
151
	cacheFree(fs->cache);
152
	if(fs->arch)
153
		archFree(fs->arch);
154
	vtMemFree(fs->name);
155
	vtRUnlock(fs->elk);
156
	vtLockFree(fs->elk);
157
	memset(fs, ~0, sizeof(Fs));
158
	vtMemFree(fs);
159
}
160
 
161
int
162
fsRedial(Fs *fs, char *host)
163
{
164
	if(!vtRedial(fs->z, host))
165
		return 0;
166
	if(!vtConnect(fs->z, 0))
167
		return 0;
168
	return 1;
169
}
170
 
171
File *
172
fsGetRoot(Fs *fs)
173
{
174
	return fileIncRef(fs->file);
175
}
176
 
177
int
178
fsGetBlockSize(Fs *fs)
179
{
180
	return fs->blockSize;
181
}
182
 
183
Block*
184
superGet(Cache *c, Super* super)
185
{
186
	Block *b;
187
 
188
	if((b = cacheLocal(c, PartSuper, 0, OReadWrite)) == nil){
189
		fprint(2, "%s: superGet: cacheLocal failed: %R\n", argv0);
190
		return nil;
191
	}
192
	if(!superUnpack(super, b->data)){
193
		fprint(2, "%s: superGet: superUnpack failed: %R\n", argv0);
194
		blockPut(b);
195
		return nil;
196
	}
197
 
198
	return b;
199
}
200
 
201
void
202
superWrite(Block* b, Super* super, int forceWrite)
203
{
204
	superPack(super, b->data);
205
	blockDirty(b);
206
	if(forceWrite){
207
		while(!blockWrite(b, Waitlock)){
208
			/* this should no longer happen */
209
			fprint(2, "%s: could not write super block; "
210
				"waiting 10 seconds\n", argv0);
211
			sleep(10*1000);
212
		}
213
		while(b->iostate != BioClean && b->iostate != BioDirty){
214
			assert(b->iostate == BioWriting);
215
			vtSleep(b->ioready);
216
		}
217
		/*
218
		 * it's okay that b might still be dirty.
219
		 * that means it got written out but with an old root pointer,
220
		 * but the other fields went out, and those are the ones
221
		 * we really care about.  (specifically, epochHigh; see fsSnapshot).
222
		 */
223
	}
224
}
225
 
226
/*
227
 * Prepare the directory to store a snapshot.
228
 * Temporary snapshots go into /snapshot/yyyy/mmdd/hhmm[.#]
229
 * Archival snapshots go into /archive/yyyy/mmdd[.#].
230
 *
231
 * TODO This should be rewritten to eliminate most of the duplication.
232
 */
233
static File*
234
fileOpenSnapshot(Fs *fs, char *dstpath, int doarchive)
235
{
236
	int n;
237
	char buf[30], *s, *p, *elem;
238
	File *dir, *f;
239
	Tm now;
240
 
241
	if(dstpath){
242
		if((p = strrchr(dstpath, '/')) != nil){
243
			*p++ = '\0';
244
			elem = p;
245
			p = dstpath;
246
			if(*p == '\0')
247
				p = "/";
248
		}else{
249
			p = "/";
250
			elem = dstpath;
251
		}
252
		if((dir = fileOpen(fs, p)) == nil)
253
			return nil;
254
		f = fileCreate(dir, elem, ModeDir|ModeSnapshot|0555, "adm");
255
		fileDecRef(dir);
256
		return f;
257
	}else if(doarchive){
258
		/*
259
		 * a snapshot intended to be archived to venti.
260
		 */
261
		dir = fileOpen(fs, "/archive");
262
		if(dir == nil)
263
			return nil;
264
		now = *localtime(time(0));
265
 
266
		/* yyyy */
267
		snprint(buf, sizeof(buf), "%d", now.year+1900);
268
		f = fileWalk(dir, buf);
269
		if(f == nil)
270
			f = fileCreate(dir, buf, ModeDir|0555, "adm");
271
		fileDecRef(dir);
272
		if(f == nil)
273
			return nil;
274
		dir = f;
275
 
276
		/* mmdd[#] */
277
		snprint(buf, sizeof(buf), "%02d%02d", now.mon+1, now.mday);
278
		s = buf+strlen(buf);
279
		for(n=0;; n++){
280
			if(n)
281
				seprint(s, buf+sizeof(buf), ".%d", n);
282
			f = fileWalk(dir, buf);
283
			if(f != nil){
284
				fileDecRef(f);
285
				continue;
286
			}
287
			f = fileCreate(dir, buf, ModeDir|ModeSnapshot|0555, "adm");
288
			break;
289
		}
290
		fileDecRef(dir);
291
		return f;
292
	}else{
293
		/*
294
		 * Just a temporary snapshot
295
		 * We'll use /snapshot/yyyy/mmdd/hhmm.
296
		 * There may well be a better naming scheme.
297
		 * (I'd have used hh:mm but ':' is reserved in Microsoft file systems.)
298
		 */
299
		dir = fileOpen(fs, "/snapshot");
300
		if(dir == nil)
301
			return nil;
302
 
303
		now = *localtime(time(0));
304
 
305
		/* yyyy */
306
		snprint(buf, sizeof(buf), "%d", now.year+1900);
307
		f = fileWalk(dir, buf);
308
		if(f == nil)
309
			f = fileCreate(dir, buf, ModeDir|0555, "adm");
310
		fileDecRef(dir);
311
		if(f == nil)
312
			return nil;
313
		dir = f;
314
 
315
		/* mmdd */
316
		snprint(buf, sizeof(buf), "%02d%02d", now.mon+1, now.mday);
317
		f = fileWalk(dir, buf);
318
		if(f == nil)
319
			f = fileCreate(dir, buf, ModeDir|0555, "adm");
320
		fileDecRef(dir);
321
		if(f == nil)
322
			return nil;
323
		dir = f;
324
 
325
		/* hhmm[.#] */
326
		snprint(buf, sizeof buf, "%02d%02d", now.hour, now.min);
327
		s = buf+strlen(buf);
328
		for(n=0;; n++){
329
			if(n)
330
				seprint(s, buf+sizeof(buf), ".%d", n);
331
			f = fileWalk(dir, buf);
332
			if(f != nil){
333
				fileDecRef(f);
334
				continue;
335
			}
336
			f = fileCreate(dir, buf, ModeDir|ModeSnapshot|0555, "adm");
337
			break;
338
		}
339
		fileDecRef(dir);
340
		return f;
341
	}
342
}
343
 
344
static int
345
fsNeedArch(Fs *fs, uint archMinute)
346
{
347
	int need;
348
	File *f;
349
	char buf[100];
350
	Tm now;
351
	ulong then;
352
 
353
	then = time(0);
354
	now = *localtime(then);
355
 
356
	/* back up to yesterday if necessary */
357
	if(now.hour < archMinute/60
358
	|| now.hour == archMinute/60 && now.min < archMinute%60)
359
		now = *localtime(then-86400);
360
 
361
	snprint(buf, sizeof buf, "/archive/%d/%02d%02d",
362
		now.year+1900, now.mon+1, now.mday);
363
	need = 1;
364
	vtRLock(fs->elk);
365
	f = fileOpen(fs, buf);
366
	if(f){
367
		need = 0;
368
		fileDecRef(f);
369
	}
370
	vtRUnlock(fs->elk);
371
	return need;
372
}
373
 
374
int
375
fsEpochLow(Fs *fs, u32int low)
376
{
377
	Block *bs;
378
	Super super;
379
 
380
	vtLock(fs->elk);
381
	if(low > fs->ehi){
382
		vtSetError("bad low epoch (must be <= %ud)", fs->ehi);
383
		vtUnlock(fs->elk);
384
		return 0;
385
	}
386
 
387
	if((bs = superGet(fs->cache, &super)) == nil){
388
		vtUnlock(fs->elk);
389
		return 0;
390
	}
391
 
392
	super.epochLow = low;
393
	fs->elo = low;
394
	superWrite(bs, &super, 1);
395
	blockPut(bs);
396
	vtUnlock(fs->elk);
397
 
398
	return 1;
399
}
400
 
401
static int
402
bumpEpoch(Fs *fs, int doarchive)
403
{
404
	uchar oscore[VtScoreSize];
405
	u32int oldaddr;
406
	Block *b, *bs;
407
	Entry e;
408
	Source *r;
409
	Super super;
410
 
411
	/*
412
	 * Duplicate the root block.
413
	 *
414
	 * As a hint to flchk, the garbage collector,
415
	 * and any (human) debuggers, store a pointer
416
	 * to the old root block in entry 1 of the new root block.
417
	 */
418
	r = fs->source;
419
	b = cacheGlobal(fs->cache, r->score, BtDir, RootTag, OReadOnly);
420
	if(b == nil)
421
		return 0;
422
 
423
	memset(&e, 0, sizeof e);
424
	e.flags = VtEntryActive | VtEntryLocal | VtEntryDir;
425
	memmove(e.score, b->score, VtScoreSize);
426
	e.tag = RootTag;
427
	e.snap = b->l.epoch;
428
 
429
	b = blockCopy(b, RootTag, fs->ehi+1, fs->elo);
430
	if(b == nil){
431
		fprint(2, "%s: bumpEpoch: blockCopy: %R\n", argv0);
432
		return 0;
433
	}
434
 
435
	if(0) fprint(2, "%s: snapshot root from %d to %d\n", argv0, oldaddr, b->addr);
436
	entryPack(&e, b->data, 1);
437
	blockDirty(b);
438
 
439
	/*
440
	 * Update the superblock with the new root and epoch.
441
	 */
442
	if((bs = superGet(fs->cache, &super)) == nil)
443
		return 0;
444
 
445
	fs->ehi++;
446
	memmove(r->score, b->score, VtScoreSize);
447
	r->epoch = fs->ehi;
448
 
449
	super.epochHigh = fs->ehi;
450
	oldaddr = super.active;
451
	super.active = b->addr;
452
	if(doarchive)
453
		super.next = oldaddr;
454
 
455
	/*
456
	 * Record that the new super.active can't get written out until
457
	 * the new b gets written out.  Until then, use the old value.
458
	 */
459
	localToGlobal(oldaddr, oscore);
460
	blockDependency(bs, b, 0, oscore, nil);
461
	blockPut(b);
462
 
463
	/*
464
	 * We force the super block to disk so that super.epochHigh gets updated.
465
	 * Otherwise, if we crash and come back, we might incorrectly treat as active
466
	 * some of the blocks that making up the snapshot we just created.
467
	 * Basically every block in the active file system and all the blocks in
468
	 * the recently-created snapshot depend on the super block now.
469
	 * Rather than record all those dependencies, we just force the block to disk.
470
	 *
471
	 * Note that blockWrite might actually (will probably) send a slightly outdated
472
	 * super.active to disk.  It will be the address of the most recent root that has
473
	 * gone to disk.
474
	 */
475
	superWrite(bs, &super, 1);
476
	blockRemoveLink(bs, globalToLocal(oscore), BtDir, RootTag, 0);
477
	blockPut(bs);
478
 
479
	return 1;
480
}
481
 
482
int
483
saveQid(Fs *fs)
484
{
485
	Block *b;
486
	Super super;
487
	u64int qidMax;
488
 
489
	if((b = superGet(fs->cache, &super)) == nil)
490
		return 0;
491
	qidMax = super.qid;
492
	blockPut(b);
493
 
494
	if(!fileSetQidSpace(fs->file, 0, qidMax))
495
		return 0;
496
 
497
	return 1;
498
}
499
 
500
int
501
fsSnapshot(Fs *fs, char *srcpath, char *dstpath, int doarchive)
502
{
503
	File *src, *dst;
504
 
505
	assert(fs->mode == OReadWrite);
506
 
507
	dst = nil;
508
 
509
	if(fs->halted){
510
		vtSetError("file system is halted");
511
		return 0;
512
	}
513
 
514
	/*
515
	 * Freeze file system activity.
516
	 */
517
	vtLock(fs->elk);
518
 
519
	/*
520
	 * Get the root of the directory we're going to save.
521
	 */
522
	if(srcpath == nil)
523
		srcpath = "/active";
524
	src = fileOpen(fs, srcpath);
525
	if(src == nil)
526
		goto Err;
527
 
528
	/*
529
	 * It is important that we maintain the invariant that:
530
	 *	if both b and bb are marked as Active with start epoch e
531
	 *	and b points at bb, then no other pointers to bb exist.
532
	 * 
533
	 * When bb is unlinked from b, its close epoch is set to b's epoch.
534
	 * A block with epoch == close epoch is
535
	 * treated as free by cacheAllocBlock; this aggressively
536
	 * reclaims blocks after they have been stored to Venti.
537
	 *
538
	 * Let's say src->source is block sb, and src->msource is block
539
	 * mb.  Let's also say that block b holds the Entry structures for
540
	 * both src->source and src->msource (their Entry structures might
541
	 * be in different blocks, but the argument is the same).
542
	 * That is, right now we have:
543
	 *
544
	 *	b	Active w/ epoch e, holds ptrs to sb and mb.
545
	 *	sb	Active w/ epoch e.
546
	 *	mb	Active w/ epoch e.
547
	 *
548
	 * With things as they are now, the invariant requires that
549
	 * b holds the only pointers to sb and mb.  We want to record
550
	 * pointers to sb and mb in new Entries corresponding to dst,
551
	 * which breaks the invariant.  Thus we need to do something
552
	 * about b.  Specifically, we bump the file system's epoch and
553
	 * then rewalk the path from the root down to and including b.
554
	 * This will copy-on-write as we walk, so now the state will be:
555
	 *
556
	 *	b	Snap w/ epoch e, holds ptrs to sb and mb.
557
	 *	new-b	Active w/ epoch e+1, holds ptrs to sb and mb.
558
	 *	sb	Active w/ epoch e.
559
	 *	mb	Active w/ epoch e.
560
	 *
561
	 * In this state, it's perfectly okay to make more pointers to sb and mb.
562
	 */
563
	if(!bumpEpoch(fs, 0) || !fileWalkSources(src))
564
		goto Err;
565
 
566
	/*
567
	 * Sync to disk.  I'm not sure this is necessary, but better safe than sorry.
568
	 */
569
	cacheFlush(fs->cache, 1);
570
 
571
	/*
572
	 * Create the directory where we will store the copy of src.
573
	 */
574
	dst = fileOpenSnapshot(fs, dstpath, doarchive);
575
	if(dst == nil)
576
		goto Err;
577
 
578
	/*
579
	 * Actually make the copy by setting dst's source and msource
580
	 * to be src's.
581
	 */
582
	if(!fileSnapshot(dst, src, fs->ehi-1, doarchive))
583
		goto Err;
584
 
585
	fileDecRef(src);
586
	fileDecRef(dst);
587
	src = nil;
588
	dst = nil;
589
 
590
	/*
591
	 * Make another copy of the file system.  This one is for the
592
	 * archiver, so that the file system we archive has the recently
593
	 * added snapshot both in /active and in /archive/yyyy/mmdd[.#].
594
	 */
595
	if(doarchive){
596
		if(!saveQid(fs))
597
			goto Err;
598
		if(!bumpEpoch(fs, 1))
599
			goto Err;
600
	}
601
 
602
	vtUnlock(fs->elk);
603
 
604
	/* BUG? can fs->arch fall out from under us here? */
605
	if(doarchive && fs->arch)
606
		archKick(fs->arch);
607
 
608
	return 1;
609
 
610
Err:
611
	fprint(2, "%s: fsSnapshot: %R\n", argv0);
612
	if(src)
613
		fileDecRef(src);
614
	if(dst)
615
		fileDecRef(dst);
616
	vtUnlock(fs->elk);
617
	return 0;
618
}
619
 
620
int
621
fsVac(Fs *fs, char *name, uchar score[VtScoreSize])
622
{
623
	int r;
624
	DirEntry de;
625
	Entry e, ee;
626
	File *f;
627
 
628
	vtRLock(fs->elk);
629
	f = fileOpen(fs, name);
630
	if(f == nil){
631
		vtRUnlock(fs->elk);
632
		return 0;
633
	}
634
 
635
	if(!fileGetSources(f, &e, &ee) || !fileGetDir(f, &de)){
636
		fileDecRef(f);
637
		vtRUnlock(fs->elk);
638
		return 0;
639
	}
640
	fileDecRef(f);
641
 
642
	r = mkVac(fs->z, fs->blockSize, &e, &ee, &de, score);
643
	vtRUnlock(fs->elk);
644
	return r;
645
}
646
 
647
static int
648
vtWriteBlock(VtSession *z, uchar *buf, uint n, uint type, uchar score[VtScoreSize])
649
{
650
	if(!vtWrite(z, score, type, buf, n))
651
		return 0;
652
	if(!vtSha1Check(score, buf, n))
653
		return 0;
654
	return 1;
655
}
656
 
657
int
658
mkVac(VtSession *z, uint blockSize, Entry *pe, Entry *pee, DirEntry *pde, uchar score[VtScoreSize])
659
{
660
	uchar buf[8192];
661
	int i;
662
	uchar *p;
663
	uint n;
664
	DirEntry de;
665
	Entry e, ee, eee;
666
	MetaBlock mb;
667
	MetaEntry me;
668
	VtRoot root;
669
 
670
	e = *pe;
671
	ee = *pee;
672
	de = *pde;
673
 
674
	if(globalToLocal(e.score) != NilBlock
675
	|| (ee.flags&VtEntryActive && globalToLocal(ee.score) != NilBlock)){
676
		vtSetError("can only vac paths already stored on venti");
677
		return 0;
678
	}
679
 
680
	/*
681
	 * Build metadata source for root.
682
	 */
683
	n = deSize(&de);
684
	if(n+MetaHeaderSize+MetaIndexSize > sizeof buf){
685
		vtSetError("DirEntry too big");
686
		return 0;
687
	}
688
	memset(buf, 0, sizeof buf);
689
	mbInit(&mb, buf, n+MetaHeaderSize+MetaIndexSize, 1);
690
	p = mbAlloc(&mb, n);
691
	if(p == nil)
692
		abort();
693
	mbSearch(&mb, de.elem, &i, &me);
694
	assert(me.p == nil);
695
	me.p = p;
696
	me.size = n;
697
	dePack(&de, &me);
698
	mbInsert(&mb, i, &me);
699
	mbPack(&mb);
700
 
701
	eee.size = n+MetaHeaderSize+MetaIndexSize;
702
	if(!vtWriteBlock(z, buf, eee.size, VtDataType, eee.score))
703
		return 0;
704
	eee.psize = 8192;
705
	eee.dsize = 8192;
706
	eee.depth = 0;
707
	eee.flags = VtEntryActive;
708
 
709
	/*
710
	 * Build root source with three entries in it.
711
	 */
712
	entryPack(&e, buf, 0);
713
	entryPack(&ee, buf, 1);
714
	entryPack(&eee, buf, 2);
715
 
716
	n = VtEntrySize*3;
717
	memset(&root, 0, sizeof root);
718
	if(!vtWriteBlock(z, buf, n, VtDirType, root.score))
719
		return 0;
720
 
721
	/*
722
	 * Save root.
723
	 */
724
	root.version = VtRootVersion;
725
	strecpy(root.type, root.type+sizeof root.type, "vac");
726
	strecpy(root.name, root.name+sizeof root.name, de.elem);
727
	root.blockSize = blockSize;
728
	vtRootPack(&root, buf);
729
	if(!vtWriteBlock(z, buf, VtRootSize, VtRootType, score))
730
		return 0;
731
 
732
	return 1;
733
}
734
 
735
int
736
fsSync(Fs *fs)
737
{
738
	vtLock(fs->elk);
739
	fileMetaFlush(fs->file, 1);
740
	cacheFlush(fs->cache, 1);
741
	vtUnlock(fs->elk);
742
	return 1;
743
}
744
 
745
int
746
fsHalt(Fs *fs)
747
{
748
	vtLock(fs->elk);
749
	fs->halted = 1;
750
	fileMetaFlush(fs->file, 1);
751
	cacheFlush(fs->cache, 1);
752
	return 1;
753
}
754
 
755
int
756
fsUnhalt(Fs *fs)
757
{
758
	if(!fs->halted)
759
		return 0;
760
	fs->halted = 0;
761
	vtUnlock(fs->elk);
762
	return 1;
763
}
764
 
765
int
766
fsNextQid(Fs *fs, u64int *qid)
767
{
768
	Block *b;
769
	Super super;
770
 
771
	if((b = superGet(fs->cache, &super)) == nil)
772
		return 0;
773
 
774
	*qid = super.qid++;
775
 
776
	/*
777
	 * It's okay if the super block doesn't go to disk immediately,
778
	 * since fileMetaAlloc will record a dependency between the
779
	 * block holding this qid and the super block.  See file.c:/^fileMetaAlloc.
780
	 */
781
	superWrite(b, &super, 0);
782
	blockPut(b);
783
	return 1;
784
}
785
 
786
static void
787
fsMetaFlush(void *a)
788
{
789
	int rv;
790
	Fs *fs = a;
791
 
792
	vtRLock(fs->elk);
793
	rv = fileMetaFlush(fs->file, 1);
794
	vtRUnlock(fs->elk);
795
	if(rv > 0)
796
		cacheFlush(fs->cache, 0);
797
}
798
 
799
static int
800
fsEsearch1(File *f, char *path, u32int savetime, u32int *plo)
801
{
802
	int n, r;
803
	DirEntry de;
804
	DirEntryEnum *dee;
805
	File *ff;
806
	Entry e, ee;
807
	char *t;
808
 
809
	dee = deeOpen(f);
810
	if(dee == nil)
811
		return 0;
812
 
813
	n = 0;
814
	for(;;){
815
		r = deeRead(dee, &de);
816
		if(r <= 0)
817
			break;
818
		if(de.mode & ModeSnapshot){
819
			if((ff = fileWalk(f, de.elem)) != nil){
820
				if(fileGetSources(ff, &e, &ee))
821
					if(de.mtime >= savetime && e.snap != 0)
822
						if(e.snap < *plo)
823
							*plo = e.snap;
824
				fileDecRef(ff);
825
			}
826
		}
827
		else if(de.mode & ModeDir){
828
			if((ff = fileWalk(f, de.elem)) != nil){
829
				t = smprint("%s/%s", path, de.elem);
830
				n += fsEsearch1(ff, t, savetime, plo);
831
				vtMemFree(t);
832
				fileDecRef(ff);
833
			}
834
		}
835
		deCleanup(&de);
836
		if(r < 0)
837
			break;
838
	}
839
	deeClose(dee);
840
 
841
	return n;
842
}
843
 
844
static int
845
fsEsearch(Fs *fs, char *path, u32int savetime, u32int *plo)
846
{
847
	int n;
848
	File *f;
849
	DirEntry de;
850
 
851
	f = fileOpen(fs, path);
852
	if(f == nil)
853
		return 0;
854
	if(!fileGetDir(f, &de)){
855
		fileDecRef(f);
856
		return 0;
857
	}
858
	if((de.mode & ModeDir) == 0){
859
		fileDecRef(f);
860
		deCleanup(&de);
861
		return 0;
862
	}
863
	deCleanup(&de);
864
	n = fsEsearch1(f, path, savetime, plo);
865
	fileDecRef(f);
866
	return n;
867
}
868
 
869
void
870
fsSnapshotCleanup(Fs *fs, u32int age)
871
{
872
	u32int lo;
873
 
874
	/*
875
	 * Find the best low epoch we can use,
876
	 * given that we need to save all the unventied archives
877
	 * and all the snapshots younger than age.
878
	 */
879
	vtRLock(fs->elk);
880
	lo = fs->ehi;
881
	fsEsearch(fs, "/archive", 0, &lo);
882
	fsEsearch(fs, "/snapshot", time(0)-age*60, &lo);
883
	vtRUnlock(fs->elk);
884
 
885
	fsEpochLow(fs, lo);
886
	fsSnapshotRemove(fs);
887
}
888
 
889
/* remove all snapshots that have expired */
890
/* return number of directory entries remaining */
891
static int
892
fsRsearch1(File *f, char *s)
893
{
894
	int n, r;
895
	DirEntry de;
896
	DirEntryEnum *dee;
897
	File *ff;
898
	char *t;
899
 
900
	dee = deeOpen(f);
901
	if(dee == nil)
902
		return 0;
903
 
904
	n = 0;
905
	for(;;){
906
		r = deeRead(dee, &de);
907
		if(r <= 0)
908
			break;
909
		n++;
910
		if(de.mode & ModeSnapshot){
911
			if((ff = fileWalk(f, de.elem)) != nil)
912
				fileDecRef(ff);
913
			else if(strcmp(vtGetError(), ESnapOld) == 0){
914
				if(fileClri(f, de.elem, "adm"))
915
					n--;
916
			}
917
		}
918
		else if(de.mode & ModeDir){
919
			if((ff = fileWalk(f, de.elem)) != nil){
920
				t = smprint("%s/%s", s, de.elem);
921
				if(fsRsearch1(ff, t) == 0)
922
					if(fileRemove(ff, "adm"))
923
						n--;
924
				vtMemFree(t);
925
				fileDecRef(ff);
926
			}
927
		}
928
		deCleanup(&de);
929
		if(r < 0)
930
			break;
931
	}
932
	deeClose(dee);
933
 
934
	return n;
935
}
936
 
937
static int
938
fsRsearch(Fs *fs, char *path)
939
{
940
	File *f;
941
	DirEntry de;
942
 
943
	f = fileOpen(fs, path);
944
	if(f == nil)
945
		return 0;
946
	if(!fileGetDir(f, &de)){
947
		fileDecRef(f);
948
		return 0;
949
	}
950
	if((de.mode & ModeDir) == 0){
951
		fileDecRef(f);
952
		deCleanup(&de);
953
		return 0;
954
	}
955
	deCleanup(&de);
956
	fsRsearch1(f, path);
957
	fileDecRef(f);
958
	return 1;
959
}
960
 
961
void
962
fsSnapshotRemove(Fs *fs)
963
{
964
	vtRLock(fs->elk);
965
	fsRsearch(fs, "/snapshot");
966
	vtRUnlock(fs->elk);
967
}
968
 
969
struct Snap
970
{
971
	Fs	*fs;
972
	Periodic*tick;
973
	VtLock	*lk;
974
	uint	snapMinutes;
975
	uint	archMinute;
976
	uint	snapLife;
977
	u32int	lastSnap;
978
	u32int	lastArch;
979
	u32int	lastCleanup;
980
	uint	ignore;
981
};
982
 
983
static void
984
snapEvent(void *v)
985
{
986
	Snap *s;
987
	u32int now, min;
988
	Tm tm;
989
	int need;
990
	u32int snaplife;
991
 
992
	s = v;
993
 
994
	now = time(0)/60;
995
	vtLock(s->lk);
996
 
997
	/*
998
	 * Snapshots happen every snapMinutes minutes.
999
	 * If we miss a snapshot (for example, because we
1000
	 * were down), we wait for the next one.
1001
	 */
1002
	if(s->snapMinutes != ~0 && s->snapMinutes != 0
1003
	&& now%s->snapMinutes==0 && now != s->lastSnap){
1004
		if(!fsSnapshot(s->fs, nil, nil, 0))
1005
			fprint(2, "%s: fsSnapshot snap: %R\n", argv0);
1006
		s->lastSnap = now;
1007
	}
1008
 
1009
	/*
1010
	 * Archival snapshots happen at archMinute.
1011
	 * If we miss an archive (for example, because we
1012
	 * were down), we do it as soon as possible.
1013
	 */
1014
	tm = *localtime(now*60);
1015
	min = tm.hour*60+tm.min;
1016
	if(s->archMinute != ~0){
1017
		need = 0;
1018
		if(min == s->archMinute && now != s->lastArch)
1019
			need = 1;
1020
		if(s->lastArch == 0){
1021
			s->lastArch = 1;
1022
			if(fsNeedArch(s->fs, s->archMinute))
1023
				need = 1;
1024
		}
1025
		if(need){
1026
			fsSnapshot(s->fs, nil, nil, 1);
1027
			s->lastArch = now;
1028
		}
1029
	}
1030
 
1031
	/*
1032
	 * Snapshot cleanup happens every snaplife or every day.
1033
	 */
1034
	snaplife = s->snapLife;
1035
	if(snaplife == ~0)
1036
		snaplife = 24*60;
1037
	if(s->lastCleanup+snaplife < now){
1038
		fsSnapshotCleanup(s->fs, s->snapLife);
1039
		s->lastCleanup = now;
1040
	}
1041
	vtUnlock(s->lk);
1042
}
1043
 
1044
static Snap*
1045
snapInit(Fs *fs)
1046
{
1047
	Snap *s;
1048
 
1049
	s = vtMemAllocZ(sizeof(Snap));
1050
	s->fs = fs;
1051
	s->tick = periodicAlloc(snapEvent, s, 10*1000);
1052
	s->lk = vtLockAlloc();
1053
	s->snapMinutes = -1;
1054
	s->archMinute = -1;
1055
	s->snapLife = -1;
1056
	s->ignore = 5*2;	/* wait five minutes for clock to stabilize */
1057
	return s;
1058
}
1059
 
1060
void
1061
snapGetTimes(Snap *s, u32int *arch, u32int *snap, u32int *snaplen)
1062
{
1063
	if(s == nil){
1064
		*snap = -1;
1065
		*arch = -1;
1066
		*snaplen = -1;
1067
		return;
1068
	}
1069
 
1070
	vtLock(s->lk);
1071
	*snap = s->snapMinutes;
1072
	*arch = s->archMinute;
1073
	*snaplen = s->snapLife;
1074
	vtUnlock(s->lk);
1075
}
1076
 
1077
void
1078
snapSetTimes(Snap *s, u32int arch, u32int snap, u32int snaplen)
1079
{
1080
	if(s == nil)
1081
		return;
1082
 
1083
	vtLock(s->lk);
1084
	s->snapMinutes = snap;
1085
	s->archMinute = arch;
1086
	s->snapLife = snaplen;
1087
	vtUnlock(s->lk);
1088
}
1089
 
1090
static void
1091
snapClose(Snap *s)
1092
{
1093
	if(s == nil)
1094
		return;
1095
 
1096
	periodicKill(s->tick);
1097
	vtMemFree(s);
1098
}
1099