1858 lines
30 KiB
C
1858 lines
30 KiB
C
#include "stdinc.h"
|
|
#include "9.h" /* for consPrint */
|
|
#include "dat.h"
|
|
#include "fns.h"
|
|
#include "error.h"
|
|
|
|
/*
|
|
* locking order is upwards. A thread can hold the lock for a File
|
|
* and then acquire the lock of its parent
|
|
*/
|
|
|
|
struct File {
|
|
Fs *fs; /* immutable */
|
|
|
|
/* meta data for file: protected by the lk in the parent */
|
|
int ref; /* holds this data structure up */
|
|
|
|
int partial; /* file was never really open */
|
|
int removed; /* file has been removed */
|
|
int dirty; /* dir is dirty with respect to meta data in block */
|
|
u32int boff; /* block offset within msource for this file's meta data */
|
|
|
|
DirEntry dir; /* meta data for this file, including component name */
|
|
|
|
File *up; /* parent file (directory) */
|
|
File *next; /* sibling */
|
|
|
|
/* data for file */
|
|
RWLock lk; /* lock for the following */
|
|
Source *source;
|
|
Source *msource; /* for directories: meta data for children */
|
|
File *down; /* children */
|
|
|
|
int mode;
|
|
int issnapshot;
|
|
};
|
|
|
|
static int fileMetaFlush2(File*, char*);
|
|
static u32int fileMetaAlloc(File*, DirEntry*, u32int);
|
|
static int fileRLock(File*);
|
|
static void fileRUnlock(File*);
|
|
static int fileLock(File*);
|
|
static void fileUnlock(File*);
|
|
static void fileMetaLock(File*);
|
|
static void fileMetaUnlock(File*);
|
|
static void fileRAccess(File*);
|
|
static void fileWAccess(File*, char*);
|
|
|
|
static File *
|
|
fileAlloc(Fs *fs)
|
|
{
|
|
File *f;
|
|
|
|
f = vtmallocz(sizeof(File));
|
|
f->ref = 1;
|
|
f->fs = fs;
|
|
f->boff = NilBlock;
|
|
f->mode = fs->mode;
|
|
return f;
|
|
}
|
|
|
|
static void
|
|
fileFree(File *f)
|
|
{
|
|
sourceClose(f->source);
|
|
sourceClose(f->msource);
|
|
deCleanup(&f->dir);
|
|
|
|
memset(f, ~0, sizeof(File));
|
|
vtfree(f);
|
|
}
|
|
|
|
/*
|
|
* the file is locked already
|
|
* f->msource is unlocked
|
|
*/
|
|
static File *
|
|
dirLookup(File *f, char *elem)
|
|
{
|
|
int i;
|
|
MetaBlock mb;
|
|
MetaEntry me;
|
|
Block *b;
|
|
Source *meta;
|
|
File *ff;
|
|
u32int bo, nb;
|
|
|
|
meta = f->msource;
|
|
b = nil;
|
|
if(!sourceLock(meta, -1))
|
|
return nil;
|
|
nb = (sourceGetSize(meta)+meta->dsize-1)/meta->dsize;
|
|
for(bo=0; bo<nb; bo++){
|
|
b = sourceBlock(meta, bo, OReadOnly);
|
|
if(b == nil)
|
|
goto Err;
|
|
if(!mbUnpack(&mb, b->data, meta->dsize))
|
|
goto Err;
|
|
if(mbSearch(&mb, elem, &i, &me)){
|
|
ff = fileAlloc(f->fs);
|
|
if(!deUnpack(&ff->dir, &me)){
|
|
fileFree(ff);
|
|
goto Err;
|
|
}
|
|
sourceUnlock(meta);
|
|
blockPut(b);
|
|
ff->boff = bo;
|
|
ff->mode = f->mode;
|
|
ff->issnapshot = f->issnapshot;
|
|
return ff;
|
|
}
|
|
|
|
blockPut(b);
|
|
b = nil;
|
|
}
|
|
werrstr(ENoFile);
|
|
/* fall through */
|
|
Err:
|
|
sourceUnlock(meta);
|
|
blockPut(b);
|
|
return nil;
|
|
}
|
|
|
|
File *
|
|
fileRoot(Source *r)
|
|
{
|
|
Block *b;
|
|
Source *r0, *r1, *r2;
|
|
MetaBlock mb;
|
|
MetaEntry me;
|
|
File *root, *mr;
|
|
Fs *fs;
|
|
|
|
b = nil;
|
|
root = nil;
|
|
mr = nil;
|
|
r1 = nil;
|
|
r2 = nil;
|
|
|
|
fs = r->fs;
|
|
if(!sourceLock(r, -1))
|
|
return nil;
|
|
r0 = sourceOpen(r, 0, fs->mode, 0);
|
|
if(r0 == nil)
|
|
goto Err;
|
|
r1 = sourceOpen(r, 1, fs->mode, 0);
|
|
if(r1 == nil)
|
|
goto Err;
|
|
r2 = sourceOpen(r, 2, fs->mode, 0);
|
|
if(r2 == nil)
|
|
goto Err;
|
|
|
|
mr = fileAlloc(fs);
|
|
mr->msource = r2;
|
|
r2 = nil;
|
|
|
|
root = fileAlloc(fs);
|
|
root->boff = 0;
|
|
root->up = mr;
|
|
root->source = r0;
|
|
r0->file = root; /* point back to source */
|
|
r0 = nil;
|
|
root->msource = r1;
|
|
r1 = nil;
|
|
|
|
mr->down = root;
|
|
|
|
if(!sourceLock(mr->msource, -1))
|
|
goto Err;
|
|
b = sourceBlock(mr->msource, 0, OReadOnly);
|
|
sourceUnlock(mr->msource);
|
|
if(b == nil)
|
|
goto Err;
|
|
|
|
if(!mbUnpack(&mb, b->data, mr->msource->dsize))
|
|
goto Err;
|
|
|
|
meUnpack(&me, &mb, 0);
|
|
if(!deUnpack(&root->dir, &me))
|
|
goto Err;
|
|
blockPut(b);
|
|
sourceUnlock(r);
|
|
fileRAccess(root);
|
|
|
|
return root;
|
|
Err:
|
|
blockPut(b);
|
|
if(r0)
|
|
sourceClose(r0);
|
|
if(r1)
|
|
sourceClose(r1);
|
|
if(r2)
|
|
sourceClose(r2);
|
|
if(mr)
|
|
fileFree(mr);
|
|
if(root)
|
|
fileFree(root);
|
|
sourceUnlock(r);
|
|
|
|
return nil;
|
|
}
|
|
|
|
static Source *
|
|
fileOpenSource(File *f, u32int offset, u32int gen, int dir, uint mode,
|
|
int issnapshot)
|
|
{
|
|
char *rname, *fname;
|
|
Source *r;
|
|
|
|
if(!sourceLock(f->source, mode))
|
|
return nil;
|
|
r = sourceOpen(f->source, offset, mode, issnapshot);
|
|
sourceUnlock(f->source);
|
|
if(r == nil)
|
|
return nil;
|
|
if(r->gen != gen){
|
|
werrstr(ERemoved);
|
|
goto Err;
|
|
}
|
|
if(r->dir != dir && r->mode != -1){
|
|
/* this hasn't been as useful as we hoped it would be. */
|
|
rname = sourceName(r);
|
|
fname = fileName(f);
|
|
consPrint("%s: source %s for file %s: fileOpenSource: "
|
|
"dir mismatch %d %d\n",
|
|
f->source->fs->name, rname, fname, r->dir, dir);
|
|
free(rname);
|
|
free(fname);
|
|
|
|
werrstr(EBadMeta);
|
|
goto Err;
|
|
}
|
|
return r;
|
|
Err:
|
|
sourceClose(r);
|
|
return nil;
|
|
}
|
|
|
|
File *
|
|
_fileWalk(File *f, char *elem, int partial)
|
|
{
|
|
File *ff;
|
|
|
|
fileRAccess(f);
|
|
|
|
if(elem[0] == 0){
|
|
werrstr(EBadPath);
|
|
return nil;
|
|
}
|
|
|
|
if(!fileIsDir(f)){
|
|
werrstr(ENotDir);
|
|
return nil;
|
|
}
|
|
|
|
if(strcmp(elem, ".") == 0){
|
|
return fileIncRef(f);
|
|
}
|
|
|
|
if(strcmp(elem, "..") == 0){
|
|
if(fileIsRoot(f))
|
|
return fileIncRef(f);
|
|
return fileIncRef(f->up);
|
|
}
|
|
|
|
if(!fileLock(f))
|
|
return nil;
|
|
|
|
for(ff = f->down; ff; ff=ff->next){
|
|
if(strcmp(elem, ff->dir.elem) == 0 && !ff->removed){
|
|
ff->ref++;
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
ff = dirLookup(f, elem);
|
|
if(ff == nil)
|
|
goto Err;
|
|
|
|
if(ff->dir.mode & ModeSnapshot){
|
|
ff->mode = OReadOnly;
|
|
ff->issnapshot = 1;
|
|
}
|
|
|
|
if(partial){
|
|
/*
|
|
* Do nothing. We're opening this file only so we can clri it.
|
|
* Usually the sources can't be opened, hence we won't even bother.
|
|
* Be VERY careful with the returned file. If you hand it to a routine
|
|
* expecting ff->source and/or ff->msource to be non-nil, we're
|
|
* likely to dereference nil. FileClri should be the only routine
|
|
* setting partial.
|
|
*/
|
|
ff->partial = 1;
|
|
}else if(ff->dir.mode & ModeDir){
|
|
ff->source = fileOpenSource(f, ff->dir.entry, ff->dir.gen,
|
|
1, ff->mode, ff->issnapshot);
|
|
ff->msource = fileOpenSource(f, ff->dir.mentry, ff->dir.mgen,
|
|
0, ff->mode, ff->issnapshot);
|
|
if(ff->source == nil || ff->msource == nil)
|
|
goto Err;
|
|
}else{
|
|
ff->source = fileOpenSource(f, ff->dir.entry, ff->dir.gen,
|
|
0, ff->mode, ff->issnapshot);
|
|
if(ff->source == nil)
|
|
goto Err;
|
|
}
|
|
|
|
/* link in and up parent ref count */
|
|
if (ff->source)
|
|
ff->source->file = ff; /* point back */
|
|
ff->next = f->down;
|
|
f->down = ff;
|
|
ff->up = f;
|
|
fileIncRef(f);
|
|
Exit:
|
|
fileUnlock(f);
|
|
return ff;
|
|
Err:
|
|
fileUnlock(f);
|
|
if(ff != nil)
|
|
fileDecRef(ff);
|
|
return nil;
|
|
}
|
|
|
|
File *
|
|
fileWalk(File *f, char *elem)
|
|
{
|
|
return _fileWalk(f, elem, 0);
|
|
}
|
|
|
|
File *
|
|
_fileOpen(Fs *fs, char *path, int partial)
|
|
{
|
|
File *f, *ff;
|
|
char *p, elem[VtMaxStringSize], *opath;
|
|
int n;
|
|
|
|
f = fs->file;
|
|
fileIncRef(f);
|
|
opath = path;
|
|
while(*path != 0){
|
|
for(p = path; *p && *p != '/'; p++)
|
|
;
|
|
n = p - path;
|
|
if(n > 0){
|
|
if(n > VtMaxStringSize){
|
|
werrstr("%s: element too long", EBadPath);
|
|
goto Err;
|
|
}
|
|
memmove(elem, path, n);
|
|
elem[n] = 0;
|
|
ff = _fileWalk(f, elem, partial && *p=='\0');
|
|
if(ff == nil){
|
|
werrstr("%.*s: %r", utfnlen(opath, p-opath),
|
|
opath);
|
|
goto Err;
|
|
}
|
|
fileDecRef(f);
|
|
f = ff;
|
|
}
|
|
if(*p == '/')
|
|
p++;
|
|
path = p;
|
|
}
|
|
return f;
|
|
Err:
|
|
fileDecRef(f);
|
|
return nil;
|
|
}
|
|
|
|
File*
|
|
fileOpen(Fs *fs, char *path)
|
|
{
|
|
return _fileOpen(fs, path, 0);
|
|
}
|
|
|
|
static void
|
|
fileSetTmp(File *f, int istmp)
|
|
{
|
|
int i;
|
|
Entry e;
|
|
Source *r;
|
|
|
|
for(i=0; i<2; i++){
|
|
if(i==0)
|
|
r = f->source;
|
|
else
|
|
r = f->msource;
|
|
if(r == nil)
|
|
continue;
|
|
if(!sourceGetEntry(r, &e)){
|
|
fprint(2, "sourceGetEntry failed (cannot happen): %r\n");
|
|
continue;
|
|
}
|
|
if(istmp)
|
|
e.flags |= VtEntryNoArchive;
|
|
else
|
|
e.flags &= ~VtEntryNoArchive;
|
|
if(!sourceSetEntry(r, &e)){
|
|
fprint(2, "sourceSetEntry failed (cannot happen): %r\n");
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
File *
|
|
fileCreate(File *f, char *elem, ulong mode, char *uid)
|
|
{
|
|
File *ff;
|
|
DirEntry *dir;
|
|
Source *pr, *r, *mr;
|
|
int isdir;
|
|
|
|
if(!fileLock(f))
|
|
return nil;
|
|
|
|
r = nil;
|
|
mr = nil;
|
|
for(ff = f->down; ff; ff=ff->next){
|
|
if(strcmp(elem, ff->dir.elem) == 0 && !ff->removed){
|
|
ff = nil;
|
|
werrstr(EExists);
|
|
goto Err1;
|
|
}
|
|
}
|
|
|
|
ff = dirLookup(f, elem);
|
|
if(ff != nil){
|
|
werrstr(EExists);
|
|
goto Err1;
|
|
}
|
|
|
|
pr = f->source;
|
|
if(pr->mode != OReadWrite){
|
|
werrstr(EReadOnly);
|
|
goto Err1;
|
|
}
|
|
|
|
if(!sourceLock2(f->source, f->msource, -1))
|
|
goto Err1;
|
|
|
|
ff = fileAlloc(f->fs);
|
|
isdir = mode & ModeDir;
|
|
|
|
r = sourceCreate(pr, pr->dsize, isdir, 0);
|
|
if(r == nil)
|
|
goto Err;
|
|
if(isdir){
|
|
mr = sourceCreate(pr, pr->dsize, 0, r->offset);
|
|
if(mr == nil)
|
|
goto Err;
|
|
}
|
|
|
|
dir = &ff->dir;
|
|
dir->elem = vtstrdup(elem);
|
|
dir->entry = r->offset;
|
|
dir->gen = r->gen;
|
|
if(isdir){
|
|
dir->mentry = mr->offset;
|
|
dir->mgen = mr->gen;
|
|
}
|
|
dir->size = 0;
|
|
if(!fsNextQid(f->fs, &dir->qid))
|
|
goto Err;
|
|
dir->uid = vtstrdup(uid);
|
|
dir->gid = vtstrdup(f->dir.gid);
|
|
dir->mid = vtstrdup(uid);
|
|
dir->mtime = time(0L);
|
|
dir->mcount = 0;
|
|
dir->ctime = dir->mtime;
|
|
dir->atime = dir->mtime;
|
|
dir->mode = mode;
|
|
|
|
ff->boff = fileMetaAlloc(f, dir, 0);
|
|
if(ff->boff == NilBlock)
|
|
goto Err;
|
|
|
|
sourceUnlock(f->source);
|
|
sourceUnlock(f->msource);
|
|
|
|
ff->source = r;
|
|
r->file = ff; /* point back */
|
|
ff->msource = mr;
|
|
|
|
if(mode&ModeTemporary){
|
|
if(!sourceLock2(r, mr, -1))
|
|
goto Err1;
|
|
fileSetTmp(ff, 1);
|
|
sourceUnlock(r);
|
|
if(mr)
|
|
sourceUnlock(mr);
|
|
}
|
|
|
|
/* committed */
|
|
|
|
/* link in and up parent ref count */
|
|
ff->next = f->down;
|
|
f->down = ff;
|
|
ff->up = f;
|
|
fileIncRef(f);
|
|
|
|
fileWAccess(f, uid);
|
|
|
|
fileUnlock(f);
|
|
return ff;
|
|
|
|
Err:
|
|
sourceUnlock(f->source);
|
|
sourceUnlock(f->msource);
|
|
Err1:
|
|
if(r){
|
|
sourceLock(r, -1);
|
|
sourceRemove(r);
|
|
}
|
|
if(mr){
|
|
sourceLock(mr, -1);
|
|
sourceRemove(mr);
|
|
}
|
|
if(ff)
|
|
fileDecRef(ff);
|
|
fileUnlock(f);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
fileRead(File *f, void *buf, int cnt, vlong offset)
|
|
{
|
|
Source *s;
|
|
uvlong size;
|
|
u32int bn;
|
|
int off, dsize, n, nn;
|
|
Block *b;
|
|
uchar *p;
|
|
|
|
if(0)fprint(2, "fileRead: %s %d, %lld\n", f->dir.elem, cnt, offset);
|
|
|
|
if(!fileRLock(f))
|
|
return -1;
|
|
|
|
if(offset < 0){
|
|
werrstr(EBadOffset);
|
|
goto Err1;
|
|
}
|
|
|
|
fileRAccess(f);
|
|
|
|
if(!sourceLock(f->source, OReadOnly))
|
|
goto Err1;
|
|
|
|
s = f->source;
|
|
dsize = s->dsize;
|
|
size = sourceGetSize(s);
|
|
|
|
if(offset >= size)
|
|
offset = size;
|
|
|
|
if(cnt > size-offset)
|
|
cnt = size-offset;
|
|
bn = offset/dsize;
|
|
off = offset%dsize;
|
|
p = buf;
|
|
while(cnt > 0){
|
|
b = sourceBlock(s, bn, OReadOnly);
|
|
if(b == nil)
|
|
goto Err;
|
|
n = cnt;
|
|
if(n > dsize-off)
|
|
n = dsize-off;
|
|
nn = dsize-off;
|
|
if(nn > n)
|
|
nn = n;
|
|
memmove(p, b->data+off, nn);
|
|
memset(p+nn, 0, nn-n);
|
|
off = 0;
|
|
bn++;
|
|
cnt -= n;
|
|
p += n;
|
|
blockPut(b);
|
|
}
|
|
sourceUnlock(s);
|
|
fileRUnlock(f);
|
|
return p-(uchar*)buf;
|
|
|
|
Err:
|
|
sourceUnlock(s);
|
|
Err1:
|
|
fileRUnlock(f);
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* Changes the file block bn to be the given block score.
|
|
* Very sneaky. Only used by flfmt.
|
|
*/
|
|
int
|
|
fileMapBlock(File *f, ulong bn, uchar score[VtScoreSize], ulong tag)
|
|
{
|
|
Block *b;
|
|
Entry e;
|
|
Source *s;
|
|
|
|
if(!fileLock(f))
|
|
return 0;
|
|
|
|
s = nil;
|
|
if(f->dir.mode & ModeDir){
|
|
werrstr(ENotFile);
|
|
goto Err;
|
|
}
|
|
|
|
if(f->source->mode != OReadWrite){
|
|
werrstr(EReadOnly);
|
|
goto Err;
|
|
}
|
|
|
|
if(!sourceLock(f->source, -1))
|
|
goto Err;
|
|
|
|
s = f->source;
|
|
b = _sourceBlock(s, bn, OReadWrite, 1, tag);
|
|
if(b == nil)
|
|
goto Err;
|
|
|
|
if(!sourceGetEntry(s, &e))
|
|
goto Err;
|
|
if(b->l.type == BtDir){
|
|
memmove(e.score, score, VtScoreSize);
|
|
assert(e.tag == tag || e.tag == 0);
|
|
e.tag = tag;
|
|
e.flags |= VtEntryLocal;
|
|
entryPack(&e, b->data, f->source->offset % f->source->epb);
|
|
}else
|
|
memmove(b->data + (bn%(e.psize/VtScoreSize))*VtScoreSize, score, VtScoreSize);
|
|
blockDirty(b);
|
|
blockPut(b);
|
|
sourceUnlock(s);
|
|
fileUnlock(f);
|
|
return 1;
|
|
|
|
Err:
|
|
if(s)
|
|
sourceUnlock(s);
|
|
fileUnlock(f);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
fileSetSize(File *f, uvlong size)
|
|
{
|
|
int r;
|
|
|
|
if(!fileLock(f))
|
|
return 0;
|
|
r = 0;
|
|
if(f->dir.mode & ModeDir){
|
|
werrstr(ENotFile);
|
|
goto Err;
|
|
}
|
|
if(f->source->mode != OReadWrite){
|
|
werrstr(EReadOnly);
|
|
goto Err;
|
|
}
|
|
if(!sourceLock(f->source, -1))
|
|
goto Err;
|
|
r = sourceSetSize(f->source, size);
|
|
sourceUnlock(f->source);
|
|
Err:
|
|
fileUnlock(f);
|
|
return r;
|
|
}
|
|
|
|
int
|
|
fileWrite(File *f, void *buf, int cnt, vlong offset, char *uid)
|
|
{
|
|
Source *s;
|
|
ulong bn;
|
|
int off, dsize, n;
|
|
Block *b;
|
|
uchar *p;
|
|
vlong eof;
|
|
|
|
if(0)fprint(2, "fileWrite: %s %d, %lld\n", f->dir.elem, cnt, offset);
|
|
|
|
if(!fileLock(f))
|
|
return -1;
|
|
|
|
s = nil;
|
|
if(f->dir.mode & ModeDir){
|
|
werrstr(ENotFile);
|
|
goto Err;
|
|
}
|
|
|
|
if(f->source->mode != OReadWrite){
|
|
werrstr(EReadOnly);
|
|
goto Err;
|
|
}
|
|
if(offset < 0){
|
|
werrstr(EBadOffset);
|
|
goto Err;
|
|
}
|
|
|
|
fileWAccess(f, uid);
|
|
|
|
if(!sourceLock(f->source, -1))
|
|
goto Err;
|
|
s = f->source;
|
|
dsize = s->dsize;
|
|
|
|
eof = sourceGetSize(s);
|
|
if(f->dir.mode & ModeAppend)
|
|
offset = eof;
|
|
bn = offset/dsize;
|
|
off = offset%dsize;
|
|
p = buf;
|
|
while(cnt > 0){
|
|
n = cnt;
|
|
if(n > dsize-off)
|
|
n = dsize-off;
|
|
b = sourceBlock(s, bn, n<dsize?OReadWrite:OOverWrite);
|
|
if(b == nil){
|
|
if(offset > eof)
|
|
sourceSetSize(s, offset);
|
|
goto Err;
|
|
}
|
|
memmove(b->data+off, p, n);
|
|
off = 0;
|
|
cnt -= n;
|
|
p += n;
|
|
offset += n;
|
|
bn++;
|
|
blockDirty(b);
|
|
blockPut(b);
|
|
}
|
|
if(offset > eof && !sourceSetSize(s, offset))
|
|
goto Err;
|
|
sourceUnlock(s);
|
|
fileUnlock(f);
|
|
return p-(uchar*)buf;
|
|
Err:
|
|
if(s)
|
|
sourceUnlock(s);
|
|
fileUnlock(f);
|
|
return -1;
|
|
}
|
|
|
|
int
|
|
fileGetDir(File *f, DirEntry *dir)
|
|
{
|
|
if(!fileRLock(f))
|
|
return 0;
|
|
|
|
fileMetaLock(f);
|
|
deCopy(dir, &f->dir);
|
|
fileMetaUnlock(f);
|
|
|
|
if(!fileIsDir(f)){
|
|
if(!sourceLock(f->source, OReadOnly)){
|
|
fileRUnlock(f);
|
|
return 0;
|
|
}
|
|
dir->size = sourceGetSize(f->source);
|
|
sourceUnlock(f->source);
|
|
}
|
|
fileRUnlock(f);
|
|
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
fileTruncate(File *f, char *uid)
|
|
{
|
|
if(fileIsDir(f)){
|
|
werrstr(ENotFile);
|
|
return 0;
|
|
}
|
|
|
|
if(!fileLock(f))
|
|
return 0;
|
|
|
|
if(f->source->mode != OReadWrite){
|
|
werrstr(EReadOnly);
|
|
fileUnlock(f);
|
|
return 0;
|
|
}
|
|
if(!sourceLock(f->source, -1)){
|
|
fileUnlock(f);
|
|
return 0;
|
|
}
|
|
if(!sourceTruncate(f->source)){
|
|
sourceUnlock(f->source);
|
|
fileUnlock(f);
|
|
return 0;
|
|
}
|
|
sourceUnlock(f->source);
|
|
fileUnlock(f);
|
|
|
|
fileWAccess(f, uid);
|
|
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
fileSetDir(File *f, DirEntry *dir, char *uid)
|
|
{
|
|
File *ff;
|
|
char *oelem;
|
|
u32int mask;
|
|
u64int size;
|
|
|
|
/* can not set permissions for the root */
|
|
if(fileIsRoot(f)){
|
|
werrstr(ERoot);
|
|
return 0;
|
|
}
|
|
|
|
if(!fileLock(f))
|
|
return 0;
|
|
|
|
if(f->source->mode != OReadWrite){
|
|
werrstr(EReadOnly);
|
|
fileUnlock(f);
|
|
return 0;
|
|
}
|
|
|
|
fileMetaLock(f);
|
|
|
|
/* check new name does not already exist */
|
|
if(strcmp(f->dir.elem, dir->elem) != 0){
|
|
for(ff = f->up->down; ff; ff=ff->next){
|
|
if(strcmp(dir->elem, ff->dir.elem) == 0 && !ff->removed){
|
|
werrstr(EExists);
|
|
goto Err;
|
|
}
|
|
}
|
|
|
|
ff = dirLookup(f->up, dir->elem);
|
|
if(ff != nil){
|
|
fileDecRef(ff);
|
|
werrstr(EExists);
|
|
goto Err;
|
|
}
|
|
}
|
|
|
|
if(!sourceLock2(f->source, f->msource, -1))
|
|
goto Err;
|
|
if(!fileIsDir(f)){
|
|
size = sourceGetSize(f->source);
|
|
if(size != dir->size){
|
|
if(!sourceSetSize(f->source, dir->size)){
|
|
sourceUnlock(f->source);
|
|
if(f->msource)
|
|
sourceUnlock(f->msource);
|
|
goto Err;
|
|
}
|
|
/* commited to changing it now */
|
|
}
|
|
}
|
|
/* commited to changing it now */
|
|
if((f->dir.mode&ModeTemporary) != (dir->mode&ModeTemporary))
|
|
fileSetTmp(f, dir->mode&ModeTemporary);
|
|
sourceUnlock(f->source);
|
|
if(f->msource)
|
|
sourceUnlock(f->msource);
|
|
|
|
oelem = nil;
|
|
if(strcmp(f->dir.elem, dir->elem) != 0){
|
|
oelem = f->dir.elem;
|
|
f->dir.elem = vtstrdup(dir->elem);
|
|
}
|
|
|
|
if(strcmp(f->dir.uid, dir->uid) != 0){
|
|
vtfree(f->dir.uid);
|
|
f->dir.uid = vtstrdup(dir->uid);
|
|
}
|
|
|
|
if(strcmp(f->dir.gid, dir->gid) != 0){
|
|
vtfree(f->dir.gid);
|
|
f->dir.gid = vtstrdup(dir->gid);
|
|
}
|
|
|
|
f->dir.mtime = dir->mtime;
|
|
f->dir.atime = dir->atime;
|
|
|
|
//fprint(2, "mode %x %x ", f->dir.mode, dir->mode);
|
|
mask = ~(ModeDir|ModeSnapshot);
|
|
f->dir.mode &= ~mask;
|
|
f->dir.mode |= mask & dir->mode;
|
|
f->dirty = 1;
|
|
//fprint(2, "->%x\n", f->dir.mode);
|
|
|
|
fileMetaFlush2(f, oelem);
|
|
vtfree(oelem);
|
|
|
|
fileMetaUnlock(f);
|
|
fileUnlock(f);
|
|
|
|
fileWAccess(f->up, uid);
|
|
|
|
return 1;
|
|
Err:
|
|
fileMetaUnlock(f);
|
|
fileUnlock(f);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
fileSetQidSpace(File *f, u64int offset, u64int max)
|
|
{
|
|
int ret;
|
|
|
|
if(!fileLock(f))
|
|
return 0;
|
|
fileMetaLock(f);
|
|
f->dir.qidSpace = 1;
|
|
f->dir.qidOffset = offset;
|
|
f->dir.qidMax = max;
|
|
ret = fileMetaFlush2(f, nil)>=0;
|
|
fileMetaUnlock(f);
|
|
fileUnlock(f);
|
|
return ret;
|
|
}
|
|
|
|
|
|
uvlong
|
|
fileGetId(File *f)
|
|
{
|
|
/* immutable */
|
|
return f->dir.qid;
|
|
}
|
|
|
|
ulong
|
|
fileGetMcount(File *f)
|
|
{
|
|
ulong mcount;
|
|
|
|
fileMetaLock(f);
|
|
mcount = f->dir.mcount;
|
|
fileMetaUnlock(f);
|
|
return mcount;
|
|
}
|
|
|
|
ulong
|
|
fileGetMode(File *f)
|
|
{
|
|
ulong mode;
|
|
|
|
fileMetaLock(f);
|
|
mode = f->dir.mode;
|
|
fileMetaUnlock(f);
|
|
return mode;
|
|
}
|
|
|
|
int
|
|
fileIsDir(File *f)
|
|
{
|
|
/* immutable */
|
|
return (f->dir.mode & ModeDir) != 0;
|
|
}
|
|
|
|
int
|
|
fileIsAppend(File *f)
|
|
{
|
|
return (f->dir.mode & ModeAppend) != 0;
|
|
}
|
|
|
|
int
|
|
fileIsExclusive(File *f)
|
|
{
|
|
return (f->dir.mode & ModeExclusive) != 0;
|
|
}
|
|
|
|
int
|
|
fileIsTemporary(File *f)
|
|
{
|
|
return (f->dir.mode & ModeTemporary) != 0;
|
|
}
|
|
|
|
int
|
|
fileIsRoot(File *f)
|
|
{
|
|
return f == f->fs->file;
|
|
}
|
|
|
|
int
|
|
fileIsRoFs(File *f)
|
|
{
|
|
return f->fs->mode == OReadOnly;
|
|
}
|
|
|
|
int
|
|
fileGetSize(File *f, uvlong *size)
|
|
{
|
|
if(!fileRLock(f))
|
|
return 0;
|
|
if(!sourceLock(f->source, OReadOnly)){
|
|
fileRUnlock(f);
|
|
return 0;
|
|
}
|
|
*size = sourceGetSize(f->source);
|
|
sourceUnlock(f->source);
|
|
fileRUnlock(f);
|
|
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
fileMetaFlush(File *f, int rec)
|
|
{
|
|
File **kids, *p;
|
|
int nkids;
|
|
int i, rv;
|
|
|
|
fileMetaLock(f);
|
|
rv = fileMetaFlush2(f, nil);
|
|
fileMetaUnlock(f);
|
|
|
|
if(!rec || !fileIsDir(f))
|
|
return rv;
|
|
|
|
if(!fileLock(f))
|
|
return rv;
|
|
nkids = 0;
|
|
for(p=f->down; p; p=p->next)
|
|
nkids++;
|
|
kids = vtmalloc(nkids*sizeof(File*));
|
|
i = 0;
|
|
for(p=f->down; p; p=p->next){
|
|
kids[i++] = p;
|
|
p->ref++;
|
|
}
|
|
fileUnlock(f);
|
|
|
|
for(i=0; i<nkids; i++){
|
|
rv |= fileMetaFlush(kids[i], 1);
|
|
fileDecRef(kids[i]);
|
|
}
|
|
vtfree(kids);
|
|
return rv;
|
|
}
|
|
|
|
/* assumes metaLock is held */
|
|
static int
|
|
fileMetaFlush2(File *f, char *oelem)
|
|
{
|
|
File *fp;
|
|
Block *b, *bb;
|
|
MetaBlock mb;
|
|
MetaEntry me, me2;
|
|
int i, n;
|
|
u32int boff;
|
|
|
|
if(!f->dirty)
|
|
return 0;
|
|
|
|
if(oelem == nil)
|
|
oelem = f->dir.elem;
|
|
|
|
//print("fileMetaFlush %s->%s\n", oelem, f->dir.elem);
|
|
|
|
fp = f->up;
|
|
|
|
if(!sourceLock(fp->msource, -1))
|
|
return -1;
|
|
/* can happen if source is clri'ed out from under us */
|
|
if(f->boff == NilBlock)
|
|
goto Err1;
|
|
b = sourceBlock(fp->msource, f->boff, OReadWrite);
|
|
if(b == nil)
|
|
goto Err1;
|
|
|
|
if(!mbUnpack(&mb, b->data, fp->msource->dsize))
|
|
goto Err;
|
|
if(!mbSearch(&mb, oelem, &i, &me))
|
|
goto Err;
|
|
|
|
n = deSize(&f->dir);
|
|
if(0)fprint(2, "old size %d new size %d\n", me.size, n);
|
|
|
|
if(mbResize(&mb, &me, n)){
|
|
/* fits in the block */
|
|
mbDelete(&mb, i);
|
|
if(strcmp(f->dir.elem, oelem) != 0)
|
|
mbSearch(&mb, f->dir.elem, &i, &me2);
|
|
dePack(&f->dir, &me);
|
|
mbInsert(&mb, i, &me);
|
|
mbPack(&mb);
|
|
blockDirty(b);
|
|
blockPut(b);
|
|
sourceUnlock(fp->msource);
|
|
f->dirty = 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* moving entry to another block
|
|
* it is feasible for the fs to crash leaving two copies
|
|
* of the directory entry. This is just too much work to
|
|
* fix. Given that entries are only allocated in a block that
|
|
* is less than PercentageFull, most modifications of meta data
|
|
* will fit within the block. i.e. this code should almost
|
|
* never be executed.
|
|
*/
|
|
boff = fileMetaAlloc(fp, &f->dir, f->boff+1);
|
|
if(boff == NilBlock){
|
|
/* mbResize might have modified block */
|
|
mbPack(&mb);
|
|
blockDirty(b);
|
|
goto Err;
|
|
}
|
|
fprint(2, "fileMetaFlush moving entry from %ud -> %ud\n", f->boff, boff);
|
|
f->boff = boff;
|
|
|
|
/* make sure deletion goes to disk after new entry */
|
|
bb = sourceBlock(fp->msource, f->boff, OReadWrite);
|
|
mbDelete(&mb, i);
|
|
mbPack(&mb);
|
|
blockDependency(b, bb, -1, nil, nil);
|
|
blockPut(bb);
|
|
blockDirty(b);
|
|
blockPut(b);
|
|
sourceUnlock(fp->msource);
|
|
|
|
f->dirty = 0;
|
|
|
|
return 1;
|
|
|
|
Err:
|
|
blockPut(b);
|
|
Err1:
|
|
sourceUnlock(fp->msource);
|
|
return -1;
|
|
}
|
|
|
|
static int
|
|
fileMetaRemove(File *f, char *uid)
|
|
{
|
|
Block *b;
|
|
MetaBlock mb;
|
|
MetaEntry me;
|
|
int i;
|
|
File *up;
|
|
|
|
up = f->up;
|
|
|
|
fileWAccess(up, uid);
|
|
|
|
fileMetaLock(f);
|
|
|
|
sourceLock(up->msource, OReadWrite);
|
|
b = sourceBlock(up->msource, f->boff, OReadWrite);
|
|
if(b == nil)
|
|
goto Err;
|
|
|
|
if(!mbUnpack(&mb, b->data, up->msource->dsize))
|
|
{
|
|
fprint(2, "U\n");
|
|
goto Err;
|
|
}
|
|
if(!mbSearch(&mb, f->dir.elem, &i, &me))
|
|
{
|
|
fprint(2, "S\n");
|
|
goto Err;
|
|
}
|
|
mbDelete(&mb, i);
|
|
mbPack(&mb);
|
|
sourceUnlock(up->msource);
|
|
|
|
blockDirty(b);
|
|
blockPut(b);
|
|
|
|
f->removed = 1;
|
|
f->boff = NilBlock;
|
|
f->dirty = 0;
|
|
|
|
fileMetaUnlock(f);
|
|
return 1;
|
|
|
|
Err:
|
|
sourceUnlock(up->msource);
|
|
blockPut(b);
|
|
fileMetaUnlock(f);
|
|
return 0;
|
|
}
|
|
|
|
/* assume file is locked, assume f->msource is locked */
|
|
static int
|
|
fileCheckEmpty(File *f)
|
|
{
|
|
u32int i, n;
|
|
Block *b;
|
|
MetaBlock mb;
|
|
Source *r;
|
|
|
|
r = f->msource;
|
|
n = (sourceGetSize(r)+r->dsize-1)/r->dsize;
|
|
for(i=0; i<n; i++){
|
|
b = sourceBlock(r, i, OReadOnly);
|
|
if(b == nil)
|
|
goto Err;
|
|
if(!mbUnpack(&mb, b->data, r->dsize))
|
|
goto Err;
|
|
if(mb.nindex > 0){
|
|
werrstr(ENotEmpty);
|
|
goto Err;
|
|
}
|
|
blockPut(b);
|
|
}
|
|
return 1;
|
|
Err:
|
|
blockPut(b);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
fileRemove(File *f, char *uid)
|
|
{
|
|
File *ff;
|
|
|
|
/* can not remove the root */
|
|
if(fileIsRoot(f)){
|
|
werrstr(ERoot);
|
|
return 0;
|
|
}
|
|
|
|
if(!fileLock(f))
|
|
return 0;
|
|
|
|
if(f->source->mode != OReadWrite){
|
|
werrstr(EReadOnly);
|
|
goto Err1;
|
|
}
|
|
if(!sourceLock2(f->source, f->msource, -1))
|
|
goto Err1;
|
|
if(fileIsDir(f) && !fileCheckEmpty(f))
|
|
goto Err;
|
|
|
|
for(ff=f->down; ff; ff=ff->next)
|
|
assert(ff->removed);
|
|
|
|
sourceRemove(f->source);
|
|
f->source->file = nil; /* erase back pointer */
|
|
f->source = nil;
|
|
if(f->msource){
|
|
sourceRemove(f->msource);
|
|
f->msource = nil;
|
|
}
|
|
|
|
fileUnlock(f);
|
|
|
|
if(!fileMetaRemove(f, uid))
|
|
return 0;
|
|
|
|
return 1;
|
|
|
|
Err:
|
|
sourceUnlock(f->source);
|
|
if(f->msource)
|
|
sourceUnlock(f->msource);
|
|
Err1:
|
|
fileUnlock(f);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
clri(File *f, char *uid)
|
|
{
|
|
int r;
|
|
|
|
if(f == nil)
|
|
return 0;
|
|
if(f->up->source->mode != OReadWrite){
|
|
werrstr(EReadOnly);
|
|
fileDecRef(f);
|
|
return 0;
|
|
}
|
|
r = fileMetaRemove(f, uid);
|
|
fileDecRef(f);
|
|
return r;
|
|
}
|
|
|
|
int
|
|
fileClriPath(Fs *fs, char *path, char *uid)
|
|
{
|
|
return clri(_fileOpen(fs, path, 1), uid);
|
|
}
|
|
|
|
int
|
|
fileClri(File *dir, char *elem, char *uid)
|
|
{
|
|
return clri(_fileWalk(dir, elem, 1), uid);
|
|
}
|
|
|
|
File *
|
|
fileIncRef(File *vf)
|
|
{
|
|
fileMetaLock(vf);
|
|
assert(vf->ref > 0);
|
|
vf->ref++;
|
|
fileMetaUnlock(vf);
|
|
return vf;
|
|
}
|
|
|
|
int
|
|
fileDecRef(File *f)
|
|
{
|
|
File *p, *q, **qq;
|
|
|
|
if(f->up == nil){
|
|
/* never linked in */
|
|
assert(f->ref == 1);
|
|
fileFree(f);
|
|
return 1;
|
|
}
|
|
|
|
fileMetaLock(f);
|
|
f->ref--;
|
|
if(f->ref > 0){
|
|
fileMetaUnlock(f);
|
|
return 0;
|
|
}
|
|
assert(f->ref == 0);
|
|
assert(f->down == nil);
|
|
|
|
fileMetaFlush2(f, nil);
|
|
|
|
p = f->up;
|
|
qq = &p->down;
|
|
for(q = *qq; q; q = *qq){
|
|
if(q == f)
|
|
break;
|
|
qq = &q->next;
|
|
}
|
|
assert(q != nil);
|
|
*qq = f->next;
|
|
|
|
fileMetaUnlock(f);
|
|
fileFree(f);
|
|
|
|
fileDecRef(p);
|
|
return 1;
|
|
}
|
|
|
|
File *
|
|
fileGetParent(File *f)
|
|
{
|
|
if(fileIsRoot(f))
|
|
return fileIncRef(f);
|
|
return fileIncRef(f->up);
|
|
}
|
|
|
|
DirEntryEnum *
|
|
deeOpen(File *f)
|
|
{
|
|
DirEntryEnum *dee;
|
|
File *p;
|
|
|
|
if(!fileIsDir(f)){
|
|
werrstr(ENotDir);
|
|
fileDecRef(f);
|
|
return nil;
|
|
}
|
|
|
|
/* flush out meta data */
|
|
if(!fileLock(f))
|
|
return nil;
|
|
for(p=f->down; p; p=p->next)
|
|
fileMetaFlush2(p, nil);
|
|
fileUnlock(f);
|
|
|
|
dee = vtmallocz(sizeof(DirEntryEnum));
|
|
dee->file = fileIncRef(f);
|
|
|
|
return dee;
|
|
}
|
|
|
|
static int
|
|
dirEntrySize(Source *s, ulong elem, ulong gen, uvlong *size)
|
|
{
|
|
Block *b;
|
|
ulong bn;
|
|
Entry e;
|
|
int epb;
|
|
|
|
epb = s->dsize/VtEntrySize;
|
|
bn = elem/epb;
|
|
elem -= bn*epb;
|
|
|
|
b = sourceBlock(s, bn, OReadOnly);
|
|
if(b == nil)
|
|
goto Err;
|
|
if(!entryUnpack(&e, b->data, elem))
|
|
goto Err;
|
|
|
|
/* hanging entries are returned as zero size */
|
|
if(!(e.flags & VtEntryActive) || e.gen != gen)
|
|
*size = 0;
|
|
else
|
|
*size = e.size;
|
|
blockPut(b);
|
|
return 1;
|
|
|
|
Err:
|
|
blockPut(b);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
deeFill(DirEntryEnum *dee)
|
|
{
|
|
int i, n;
|
|
Source *meta, *source;
|
|
MetaBlock mb;
|
|
MetaEntry me;
|
|
File *f;
|
|
Block *b;
|
|
DirEntry *de;
|
|
|
|
/* clean up first */
|
|
for(i=dee->i; i<dee->n; i++)
|
|
deCleanup(dee->buf+i);
|
|
vtfree(dee->buf);
|
|
dee->buf = nil;
|
|
dee->i = 0;
|
|
dee->n = 0;
|
|
|
|
f = dee->file;
|
|
|
|
source = f->source;
|
|
meta = f->msource;
|
|
|
|
b = sourceBlock(meta, dee->boff, OReadOnly);
|
|
if(b == nil)
|
|
goto Err;
|
|
if(!mbUnpack(&mb, b->data, meta->dsize))
|
|
goto Err;
|
|
|
|
n = mb.nindex;
|
|
dee->buf = vtmalloc(n * sizeof(DirEntry));
|
|
|
|
for(i=0; i<n; i++){
|
|
de = dee->buf + i;
|
|
meUnpack(&me, &mb, i);
|
|
if(!deUnpack(de, &me))
|
|
goto Err;
|
|
dee->n++;
|
|
if(!(de->mode & ModeDir))
|
|
if(!dirEntrySize(source, de->entry, de->gen, &de->size))
|
|
goto Err;
|
|
}
|
|
dee->boff++;
|
|
blockPut(b);
|
|
return 1;
|
|
Err:
|
|
blockPut(b);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
deeRead(DirEntryEnum *dee, DirEntry *de)
|
|
{
|
|
int ret, didread;
|
|
File *f;
|
|
u32int nb;
|
|
|
|
if(dee == nil){
|
|
werrstr("cannot happen in deeRead");
|
|
return -1;
|
|
}
|
|
|
|
f = dee->file;
|
|
if(!fileRLock(f))
|
|
return -1;
|
|
|
|
if(!sourceLock2(f->source, f->msource, OReadOnly)){
|
|
fileRUnlock(f);
|
|
return -1;
|
|
}
|
|
|
|
nb = (sourceGetSize(f->msource)+f->msource->dsize-1)/f->msource->dsize;
|
|
|
|
didread = 0;
|
|
while(dee->i >= dee->n){
|
|
if(dee->boff >= nb){
|
|
ret = 0;
|
|
goto Return;
|
|
}
|
|
didread = 1;
|
|
if(!deeFill(dee)){
|
|
ret = -1;
|
|
goto Return;
|
|
}
|
|
}
|
|
|
|
memmove(de, dee->buf + dee->i, sizeof(DirEntry));
|
|
dee->i++;
|
|
ret = 1;
|
|
|
|
Return:
|
|
sourceUnlock(f->source);
|
|
sourceUnlock(f->msource);
|
|
fileRUnlock(f);
|
|
|
|
if(didread)
|
|
fileRAccess(f);
|
|
return ret;
|
|
}
|
|
|
|
void
|
|
deeClose(DirEntryEnum *dee)
|
|
{
|
|
int i;
|
|
if(dee == nil)
|
|
return;
|
|
for(i=dee->i; i<dee->n; i++)
|
|
deCleanup(dee->buf+i);
|
|
vtfree(dee->buf);
|
|
fileDecRef(dee->file);
|
|
vtfree(dee);
|
|
}
|
|
|
|
/*
|
|
* caller must lock f->source and f->msource
|
|
* caller must NOT lock the source and msource
|
|
* referenced by dir.
|
|
*/
|
|
static u32int
|
|
fileMetaAlloc(File *f, DirEntry *dir, u32int start)
|
|
{
|
|
u32int nb, bo;
|
|
Block *b, *bb;
|
|
MetaBlock mb;
|
|
int nn;
|
|
uchar *p;
|
|
int i, n, epb;
|
|
MetaEntry me;
|
|
Source *s, *ms;
|
|
|
|
s = f->source;
|
|
ms = f->msource;
|
|
|
|
n = deSize(dir);
|
|
nb = (sourceGetSize(ms)+ms->dsize-1)/ms->dsize;
|
|
b = nil;
|
|
if(start > nb)
|
|
start = nb;
|
|
for(bo=start; bo<nb; bo++){
|
|
b = sourceBlock(ms, bo, OReadWrite);
|
|
if(b == nil)
|
|
goto Err;
|
|
if(!mbUnpack(&mb, b->data, ms->dsize))
|
|
goto Err;
|
|
nn = (mb.maxsize*FullPercentage/100) - mb.size + mb.free;
|
|
if(n <= nn && mb.nindex < mb.maxindex)
|
|
break;
|
|
blockPut(b);
|
|
b = nil;
|
|
}
|
|
|
|
/* add block to meta file */
|
|
if(b == nil){
|
|
b = sourceBlock(ms, bo, OReadWrite);
|
|
if(b == nil)
|
|
goto Err;
|
|
sourceSetSize(ms, (nb+1)*ms->dsize);
|
|
mbInit(&mb, b->data, ms->dsize, ms->dsize/BytesPerEntry);
|
|
}
|
|
|
|
p = mbAlloc(&mb, n);
|
|
if(p == nil){
|
|
/* mbAlloc might have changed block */
|
|
mbPack(&mb);
|
|
blockDirty(b);
|
|
werrstr(EBadMeta);
|
|
goto Err;
|
|
}
|
|
|
|
mbSearch(&mb, dir->elem, &i, &me);
|
|
assert(me.p == nil);
|
|
me.p = p;
|
|
me.size = n;
|
|
dePack(dir, &me);
|
|
mbInsert(&mb, i, &me);
|
|
mbPack(&mb);
|
|
|
|
/* meta block depends on super block for qid ... */
|
|
bb = cacheLocal(b->c, PartSuper, 0, OReadOnly);
|
|
blockDependency(b, bb, -1, nil, nil);
|
|
blockPut(bb);
|
|
|
|
/* ... and one or two dir entries */
|
|
epb = s->dsize/VtEntrySize;
|
|
bb = sourceBlock(s, dir->entry/epb, OReadOnly);
|
|
blockDependency(b, bb, -1, nil, nil);
|
|
blockPut(bb);
|
|
if(dir->mode & ModeDir){
|
|
bb = sourceBlock(s, dir->mentry/epb, OReadOnly);
|
|
blockDependency(b, bb, -1, nil, nil);
|
|
blockPut(bb);
|
|
}
|
|
|
|
blockDirty(b);
|
|
blockPut(b);
|
|
return bo;
|
|
Err:
|
|
blockPut(b);
|
|
return NilBlock;
|
|
}
|
|
|
|
static int
|
|
chkSource(File *f)
|
|
{
|
|
if(f->partial)
|
|
return 1;
|
|
|
|
if(f->source == nil || (f->dir.mode & ModeDir) && f->msource == nil){
|
|
werrstr(ERemoved);
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
fileRLock(File *f)
|
|
{
|
|
assert(!canwlock(&f->fs->elk));
|
|
rlock(&f->lk);
|
|
if(!chkSource(f)){
|
|
fileRUnlock(f);
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static void
|
|
fileRUnlock(File *f)
|
|
{
|
|
runlock(&f->lk);
|
|
}
|
|
|
|
static int
|
|
fileLock(File *f)
|
|
{
|
|
assert(!canwlock(&f->fs->elk));
|
|
wlock(&f->lk);
|
|
if(!chkSource(f)){
|
|
fileUnlock(f);
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static void
|
|
fileUnlock(File *f)
|
|
{
|
|
wunlock(&f->lk);
|
|
}
|
|
|
|
/*
|
|
* f->source and f->msource must NOT be locked.
|
|
* fileMetaFlush locks the fileMeta and then the source (in fileMetaFlush2).
|
|
* We have to respect that ordering.
|
|
*/
|
|
static void
|
|
fileMetaLock(File *f)
|
|
{
|
|
if(f->up == nil)
|
|
fprint(2, "f->elem = %s\n", f->dir.elem);
|
|
assert(f->up != nil);
|
|
assert(!canwlock(&f->fs->elk));
|
|
wlock(&f->up->lk);
|
|
}
|
|
|
|
static void
|
|
fileMetaUnlock(File *f)
|
|
{
|
|
wunlock(&f->up->lk);
|
|
}
|
|
|
|
/*
|
|
* f->source and f->msource must NOT be locked.
|
|
* see fileMetaLock.
|
|
*/
|
|
static void
|
|
fileRAccess(File* f)
|
|
{
|
|
if(f->mode == OReadOnly || f->fs->noatimeupd)
|
|
return;
|
|
|
|
fileMetaLock(f);
|
|
f->dir.atime = time(0L);
|
|
f->dirty = 1;
|
|
fileMetaUnlock(f);
|
|
}
|
|
|
|
/*
|
|
* f->source and f->msource must NOT be locked.
|
|
* see fileMetaLock.
|
|
*/
|
|
static void
|
|
fileWAccess(File* f, char *mid)
|
|
{
|
|
if(f->mode == OReadOnly)
|
|
return;
|
|
|
|
fileMetaLock(f);
|
|
f->dir.atime = f->dir.mtime = time(0L);
|
|
if(strcmp(f->dir.mid, mid) != 0){
|
|
vtfree(f->dir.mid);
|
|
f->dir.mid = vtstrdup(mid);
|
|
}
|
|
f->dir.mcount++;
|
|
f->dirty = 1;
|
|
fileMetaUnlock(f);
|
|
|
|
/*RSC: let's try this */
|
|
/*presotto - lets not
|
|
if(f->up)
|
|
fileWAccess(f->up, mid);
|
|
*/
|
|
}
|
|
|
|
static int
|
|
getEntry(Source *r, Entry *e, int checkepoch)
|
|
{
|
|
u32int epoch;
|
|
Block *b;
|
|
|
|
if(r == nil){
|
|
memset(&e, 0, sizeof e);
|
|
return 1;
|
|
}
|
|
|
|
b = cacheGlobal(r->fs->cache, r->score, BtDir, r->tag, OReadOnly);
|
|
if(b == nil)
|
|
return 0;
|
|
if(!entryUnpack(e, b->data, r->offset % r->epb)){
|
|
blockPut(b);
|
|
return 0;
|
|
}
|
|
epoch = b->l.epoch;
|
|
blockPut(b);
|
|
|
|
if(checkepoch){
|
|
b = cacheGlobal(r->fs->cache, e->score, entryType(e), e->tag, OReadOnly);
|
|
if(b){
|
|
if(b->l.epoch >= epoch)
|
|
fprint(2, "warning: entry %p epoch not older %#.8ux/%d %V/%d in getEntry\n",
|
|
r, b->addr, b->l.epoch, r->score, epoch);
|
|
blockPut(b);
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
setEntry(Source *r, Entry *e)
|
|
{
|
|
Block *b;
|
|
Entry oe;
|
|
|
|
b = cacheGlobal(r->fs->cache, r->score, BtDir, r->tag, OReadWrite);
|
|
if(0) fprint(2, "setEntry: b %#ux %d score=%V\n", b->addr, r->offset % r->epb, e->score);
|
|
if(b == nil)
|
|
return 0;
|
|
if(!entryUnpack(&oe, b->data, r->offset % r->epb)){
|
|
blockPut(b);
|
|
return 0;
|
|
}
|
|
e->gen = oe.gen;
|
|
entryPack(e, b->data, r->offset % r->epb);
|
|
|
|
/* BUG b should depend on the entry pointer */
|
|
|
|
blockDirty(b);
|
|
blockPut(b);
|
|
return 1;
|
|
}
|
|
|
|
/* assumes hold elk */
|
|
int
|
|
fileSnapshot(File *dst, File *src, u32int epoch, int doarchive)
|
|
{
|
|
Entry e, ee;
|
|
|
|
/* add link to snapshot */
|
|
if(!getEntry(src->source, &e, 1) || !getEntry(src->msource, &ee, 1))
|
|
return 0;
|
|
|
|
e.snap = epoch;
|
|
e.archive = doarchive;
|
|
ee.snap = epoch;
|
|
ee.archive = doarchive;
|
|
|
|
if(!setEntry(dst->source, &e) || !setEntry(dst->msource, &ee))
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
fileGetSources(File *f, Entry *e, Entry *ee)
|
|
{
|
|
if(!getEntry(f->source, e, 0)
|
|
|| !getEntry(f->msource, ee, 0))
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Walk down to the block(s) containing the Entries
|
|
* for f->source and f->msource, copying as we go.
|
|
*/
|
|
int
|
|
fileWalkSources(File *f)
|
|
{
|
|
if(f->mode == OReadOnly){
|
|
fprint(2, "readonly in fileWalkSources\n");
|
|
return 1;
|
|
}
|
|
if(!sourceLock2(f->source, f->msource, OReadWrite)){
|
|
fprint(2, "sourceLock2 failed in fileWalkSources\n");
|
|
return 0;
|
|
}
|
|
sourceUnlock(f->source);
|
|
sourceUnlock(f->msource);
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* convert File* to full path name in malloced string.
|
|
* this hasn't been as useful as we hoped it would be.
|
|
*/
|
|
char *
|
|
fileName(File *f)
|
|
{
|
|
char *name, *pname;
|
|
File *p;
|
|
static char root[] = "/";
|
|
|
|
if (f == nil)
|
|
return vtstrdup("/**GOK**");
|
|
|
|
p = fileGetParent(f);
|
|
if (p == f)
|
|
name = vtstrdup(root);
|
|
else {
|
|
pname = fileName(p);
|
|
if (strcmp(pname, root) == 0)
|
|
name = smprint("/%s", f->dir.elem);
|
|
else
|
|
name = smprint("%s/%s", pname, f->dir.elem);
|
|
free(pname);
|
|
}
|
|
fileDecRef(p);
|
|
return name;
|
|
}
|