/* Copyright (c) 2007 David Swasey; see COPYRIGHT. Limitations: Hfsreaddir skips entries whose names contain NUL. Hfsbadblock is untested.*/ #include <u.h>#include <libc.h>#include <thread.h>#include <sunrpc.h>#include <nfs3.h>#include <diskfs.h>#include "hfs.h" #define debug 0 static char Rprefix[] = "._";enum { Rplen = nelem(Rprefix)-1 }; static int hfssync(Fsys*);static int hfswrapper(Fsys*);static int hfstreesync(Hfs*, Fork*, Tree*, int);static u32int hfsmetadir(Hfs*);static void hfsclose(Fsys*);static Block* hfsblockread(Fsys*, u64int); static Nfs3Status hfsroot(Fsys*, Nfs3Handle*);static Nfs3Status hfsgetattr(Fsys*, SunAuthUnix*, Nfs3Handle*, Nfs3Attr*);static Nfs3Status hfsaccess(Fsys *fsys, SunAuthUnix*, Nfs3Handle *, u32int, u32int*, Nfs3Attr *attr);static Nfs3Status hfslookup(Fsys*, SunAuthUnix*, Nfs3Handle*, char*, Nfs3Handle*);static Nfs3Status _hfslookup(Hfs*, u32int, char*, Treeref*);static Nfs3Status hfsreaddir(Fsys *fsys, SunAuthUnix*, Nfs3Handle *h, u32int, u64int, uchar**, u32int*, u1int*);static Nfs3Status hfsreadfile(Fsys*, SunAuthUnix*, Nfs3Handle*, u32int, u64int, uchar**, u32int*, u1int*);static Nfs3Status hfsreadlink(Fsys*, SunAuthUnix*, Nfs3Handle*, char**); static int hfsextentsearch(Hfs*, u32int, int, u32int, u32int*, Extent*);static int hfscatsearch(Hfs*, Catalogkey*, Treeref*);static int _hfscatalogkeycmp(uchar*, int, int*, Key*, int);static int hfstreesearch(Tree*, Key*, int*, Treeref*);static void hfsref(Tree*, Treeref*);static void hfsrefput(Treeref*);static int hfsrefnode(Treeref*, u32int);static int hfsrefrec(Treeref*, int);static int hfsrefnextrec(Treeref*); static int hfsforkread(Hfs*, Fork*, uchar*, u32int, u64int);static Block* hfsforkblock(Hfs*, Fork*, u32int);static Block* hfsblock(Hfs*, u32int); static void getnodedesc(Node*, uchar*);static void getfork(Fork*, u32int, int, uchar*);static void getextents(Extent*, uchar*);static void getextent(Extent*, uchar*);static int getextentkey(Extentkey*, uchar*, int);static int getcatalogrecord(Inode*, uchar*, int);static int getcatalogthread(Catalogkey*, uchar*, int);static int getcatalogkey(Catalogkey*, uchar*, int, int);static int getname(Name*, uchar*);static u32int gettime(uchar*);static u64int get64(uchar*);static u32int get32(uchar*);static int get16(uchar*);static void put32(uchar*, u32int); static int hfsinamecmp(Name*, Name*);static int hfsnamecmp(Name*, Name*); Fsys*fsysopenhfs(Disk *disk, u32int bs, u64int off){ Hfs *fs; Fsys *fsys; fsys = emalloc(sizeof(Fsys)); fs = emalloc(sizeof(Hfs)); fsys->priv = fs; fs->fsys = fsys; fs->disk = disk; fsys->type = "hfs"; fsys->_readblock = hfsblockread; fsys->_sync = hfssync; fsys->_root = hfsroot; fsys->_getattr = hfsgetattr; fsys->_access = hfsaccess; fsys->_lookup = hfslookup; fsys->_readfile = hfsreadfile; fsys->_readlink = hfsreadlink; fsys->_readdir = hfsreaddir; fsys->_close = hfsclose; fsys->disk = disk; if(hfswrapper(fsys) < 0 || hfssync(fsys) < 0) goto error; return fsys; error: hfsclose(fsys); return nil;} static voidhfsclose(Fsys *fsys){ Hfs *fs; fs = fsys->priv; /* * We're not supposed to close the disk passed to * hfsopen, but if we created our own partition disk * we need to close that. It will close its subdisk * unless we call diskpartabandon. Bad planning. */ if(fs->disk && fs->disk != fsys->disk){ diskpartabandon(fs->disk); diskclose(fs->disk); } free(fs); free(fsys);} static inthfswrapper(Fsys *fsys){ Hfs *fs; Disk *disk; Block *b; uchar *mdb; int magic, hfsstart, subsig, substart, subnblocks; u32int hfsblocksize; u64int offset, size; fs = fsys->priv; disk = fsys->disk; if((b = diskread(disk, 162, 1024)) == nil) return -1; mdb = b->data; magic = get16(mdb); hfsblocksize = get32(mdb+20); hfsstart = get16(mdb+28); subsig = get16(mdb+124); substart = get16(mdb+126); subnblocks = get16(mdb+128); blockput(b); if(magic != Hfssig) return 0; if(subsig != Hfsplussig && subsig != Hfsxsig) return 0; offset = hfsstart*512 + substart*hfsblocksize; size = subnblocks*hfsblocksize; if((fs->disk = diskpart(disk, offset, size)) == nil) return -1; if(debug) fprint(2, "wrapped hfs size 0x%ullx offset 0x%ullx\n", size, offset); return 0;} static intcheckvolhdr(uchar *vh){ u32int magic; magic = get32(vh); switch(magic){ case Hfsplusmagic: return 0; case Hfsxmagic: return 1; default: werrstr("bad volume header magic 0x%ux", magic); return -1; }} static inthfssync(Fsys *fsys){ Hfs *fs; Disk *disk; Block *b; uchar *vh; int sensitive; fs = fsys->priv; disk = fs->disk; if((b = diskread(disk, 512, 1024)) == nil) goto error; vh = b->data; if((sensitive = checkvolhdr(vh)) < 0) goto error; fs->blocksize = get32(vh+40); fs->nblock = get32(vh+44); fs->nfree = get32(vh+48); fs->hasbadblocks = get32(vh+4) & (1<<9); getfork(&fs->alloc, AllocId, Dfork, vh+112); getfork(&fs->extentsfork, ExtentsId, Dfork, vh+192); getfork(&fs->catalogfork, CatalogId, Dfork, vh+272); blockput(b); b = nil; if(hfstreesync(fs, &fs->extentsfork, &fs->extents, 0) < 0) goto error; if(hfstreesync(fs, &fs->catalogfork, &fs->catalog, sensitive) < 0) goto error; fs->hlinkparent = hfsmetadir(fs); fsys->blocksize = fs->blocksize; fsys->nblock = fs->nblock; if(debug) fprint(2, "hfs %ud %ud-byte blocks (%ud free, %s bad)\n", fs->nblock, fs->blocksize, fs->nfree, fs->hasbadblocks?"some":"none"); return 0; error: blockput(b); return -1;} static inthfstreesync(Hfs *fs, Fork *fork, Tree *tree, int sensitive){ Block *b; uchar *hr; Node node; b = nil; if(fork->extent[0].count == 0){ if(debug) fprint(2, "tree fork empty\n"); goto error; } if((b = hfsblock(fs,fork->extent[0].start)) == nil) goto error; getnodedesc(&node, b->data); if(node.type != HeaderNode){ if(debug) fprint(2, "bad header node type %d\n", node.type); goto error; } hr = b->data+Ndlen; tree->nodesize = get16(hr+18); tree->nnodes = get32(hr+22); tree->root = get32(hr+2); tree->height = get16(hr); tree->maxkeylen = get16(hr+20); tree->indexkeylen = (get32(hr+38)&4) ? 0 : tree->maxkeylen; tree->sensitive = sensitive && hr[37]==0xBC; blockput(b); tree->fs = fs; tree->fork = fork; if(debug) fprint(2, "tree %ud %ud %d-byte nodes\n", fork->cnid, tree->nnodes, tree->nodesize); return 0; error: werrstr("bad tree %ud header", fork->cnid); blockput(b); return -1;} static u32inthfsmetadir(Hfs *fs){ static Rune name[] = {0,0,0,0,'H','F','S','+',' ','P','r','i','v','a','t','e',' ','D','a','t','a'}; Catalogkey key; Treeref ref; Inode ino; key.parent = RootId; key.u.name.len = nelem(name); memcpy(key.u.name.name, name, sizeof name); if(hfscatsearch(fs, &key, &ref) < 0) goto notfound; if(getcatalogrecord(&ino, ref.data, ref.dlen) < 0) goto notfound; if((ino.mode&IFMT) != IFDIR) goto notfound; hfsrefput(&ref); if(debug) fprint(2, "metadata directory %ud\n", ino.cnid); return ino.cnid; notfound: if(debug) fprint(2, "metadata directory not found\n"); hfsrefput(&ref); return 0;} static inthfsbadblock(Hfs *fs, u32int bno){ Extent e[NEXTENTS]; int i; if(hfsextentsearch(fs, BadblockId, Dfork, bno, nil, e) < 0) return 0; /* Don't lightly throw away a block. */ for(i=0; i<NEXTENTS; i++) if(e[i].start <= bno && bno < e[i].start+e[i].count) return 1; if(debug) fprint(2, "corrupt badblock record, block %ud\n", bno); return 0;} static Block*hfsblockread(Fsys *fsys, u64int vbno){ Hfs *fs; u32int bno; Block *b; int n, alloc; fs = fsys->priv; if(vbno >= fs->nblock){ if(debug) fprint(2, "block %llud past end of volume\n", vbno); return nil; } bno = vbno; n = bno>>3; if((b = hfsforkblock(fs, &fs->alloc, n/fs->blocksize)) == nil){ if(debug) fprint(2, "can't read alloc bitmap for block %ud\n", bno); /* assume it's used */ } else{ alloc = b->data[n%fs->blocksize] & (1<<(7-(bno&7))); blockput(b); if(!alloc) return nil; } if(fs->hasbadblocks && hfsbadblock(fs, bno)){ if(debug) fprint(2, "bad block %ud\n", bno); return nil; } b = hfsblock(fs, bno); if(debug && b == nil) fprint(2, "can't read block %ud\n", bno); return b;} static inthasresource(Inode *ino){ return (ino->mode&IFMT)==IFREG && ino->u.f.rfork.size>0;} static voiduseresource(Inode *ino){ ino->fileid = ((u64int)1)<<32 | ino->cnid; ino->u.f.nhdr = Adlen; ino->u.f.fork = &ino->u.f.rfork;} static intref2ino(Hfs *fs, Treeref *ref, Inode *ino){ static uchar magic[] = {'h','l','n','k','h','f','s','+'}; Catalogkey key; Treeref hlink; u32int cnid; if(getcatalogrecord(ino, ref->data, ref->dlen) < 0) return -1; if((ino->mode&IFMT) == IFREG && memcmp(ino->u.f.info, magic, nelem(magic)) == 0){ if(debug) print("iNode%ud...", ino->special); if(fs->hlinkparent == 0) return -1; key.parent = fs->hlinkparent; key.u.name.len = runesnprint(key.u.name.name, sizeof key.u.name.name, "iNode%ud", ino->special); if(hfscatsearch(fs, &key, &hlink) < 0) goto error; cnid = ino->cnid; if(getcatalogrecord(ino, hlink.data, hlink.dlen) < 0) goto error; hfsrefput(&hlink); ino->cnid = cnid; ino->fileid = cnid; ino->nlink = ino->special; } return 0; error: hfsrefput(&hlink); return -1;} static voidmkhandle(Nfs3Handle *h, int rsrc, u32int cnid){ h->h[0] = rsrc; put32(h->h+1, cnid); h->len = 5;} static Nfs3Statushfsroot(Fsys *fsys, Nfs3Handle *h){ USED(fsys); mkhandle(h, 0, RootId); return Nfs3Ok;} static Nfs3Statushandle2ino(Hfs *fs, Nfs3Handle *h, Treeref *ref, Catalogkey *key, Inode *ino){ Treeref refbuf; u32int cnid; Catalogkey keybuf; int rsrc; if(h->len != 5) return Nfs3ErrBadHandle; rsrc = h->h[0]; cnid = get32(h->h+1); if(debug) print("cnid %ud%s...", cnid, rsrc ? " rsrc" : ""); if(ref == nil) ref = &refbuf; if(key == nil) key = &keybuf; /* map cnid to full catalog key */ key->parent = cnid; key->u.name.len = 0; if(hfscatsearch(fs, key, ref) < 0) goto error; if(getcatalogthread(key, ref->data, ref->dlen) < 0) goto error; hfsrefput(ref); if(debug) print("{%ud,%.*S}...", key->parent, key->u.name.len, key->u.name.name); /* map full key to catalog info */ if(hfscatsearch(fs, key, ref) < 0) goto error; if(ino != nil){ if(ref2ino(fs, ref, ino) < 0) goto error; if(rsrc){ if(!hasresource(ino)){ hfsrefput(ref); return Nfs3ErrBadHandle; } useresource(ino); } } if(ref == &refbuf) hfsrefput(ref); return Nfs3Ok; error: hfsrefput(ref); return Nfs3ErrIo;} static Nfs3Statusino2attr(Hfs *fs, Inode *ino, Nfs3Attr *attr){ u32int rdev; switch(ino->mode&IFMT){ case IFIFO: attr->type = Nfs3FileFifo; break; case IFCHR: attr->type = Nfs3FileChar; break; case IFDIR: attr->type = Nfs3FileDir; break; case IFBLK: attr->type = Nfs3FileBlock; break; case IFREG: attr->type = Nfs3FileReg; break; case IFLNK: attr->type = Nfs3FileSymlink; break; case IFSOCK: attr->type = Nfs3FileSocket; break; case IFWHT: default: if(debug) fprint(2, "bad format %uo\n", ino->mode&IFMT); return Nfs3ErrBadHandle; } attr->mode = ino->mode&07777; attr->nlink = ino->nlink; attr->uid = ino->uid; attr->gid = ino->gid;Lines 1–500 of 1975. Load more
/* Copyright (c) 2007 David Swasey; see COPYRIGHT. Limitations: Hfsreaddir skips entries whose names contain NUL. Hfsbadblock is untested. */ #include <u.h> #include <libc.h> #include <thread.h> #include <sunrpc.h> #include <nfs3.h> #include <diskfs.h> #include "hfs.h" #define debug 0 static char Rprefix[] = "._"; enum { Rplen = nelem(Rprefix)-1 }; static int hfssync(Fsys*); static int hfswrapper(Fsys*); static int hfstreesync(Hfs*, Fork*, Tree*, int); static u32int hfsmetadir(Hfs*); static void hfsclose(Fsys*); static Block* hfsblockread(Fsys*, u64int); static Nfs3Status hfsroot(Fsys*, Nfs3Handle*); static Nfs3Status hfsgetattr(Fsys*, SunAuthUnix*, Nfs3Handle*, Nfs3Attr*); static Nfs3Status hfsaccess(Fsys *fsys, SunAuthUnix*, Nfs3Handle *, u32int, u32int*, Nfs3Attr *attr); static Nfs3Status hfslookup(Fsys*, SunAuthUnix*, Nfs3Handle*, char*, Nfs3Handle*); static Nfs3Status _hfslookup(Hfs*, u32int, char*, Treeref*); static Nfs3Status hfsreaddir(Fsys *fsys, SunAuthUnix*, Nfs3Handle *h, u32int, u64int, uchar**, u32int*, u1int*); static Nfs3Status hfsreadfile(Fsys*, SunAuthUnix*, Nfs3Handle*, u32int, u64int, uchar**, u32int*, u1int*); static Nfs3Status hfsreadlink(Fsys*, SunAuthUnix*, Nfs3Handle*, char**); static int hfsextentsearch(Hfs*, u32int, int, u32int, u32int*, Extent*); static int hfscatsearch(Hfs*, Catalogkey*, Treeref*); static int _hfscatalogkeycmp(uchar*, int, int*, Key*, int); static int hfstreesearch(Tree*, Key*, int*, Treeref*); static void hfsref(Tree*, Treeref*); static void hfsrefput(Treeref*); static int hfsrefnode(Treeref*, u32int); static int hfsrefrec(Treeref*, int); static int hfsrefnextrec(Treeref*); static int hfsforkread(Hfs*, Fork*, uchar*, u32int, u64int); static Block* hfsforkblock(Hfs*, Fork*, u32int); static Block* hfsblock(Hfs*, u32int); static void getnodedesc(Node*, uchar*); static void getfork(Fork*, u32int, int, uchar*); static void getextents(Extent*, uchar*); static void getextent(Extent*, uchar*); static int getextentkey(Extentkey*, uchar*, int); static int getcatalogrecord(Inode*, uchar*, int); static int getcatalogthread(Catalogkey*, uchar*, int); static int getcatalogkey(Catalogkey*, uchar*, int, int); static int getname(Name*, uchar*); static u32int gettime(uchar*); static u64int get64(uchar*); static u32int get32(uchar*); static int get16(uchar*); static void put32(uchar*, u32int); static int hfsinamecmp(Name*, Name*); static int hfsnamecmp(Name*, Name*); Fsys* fsysopenhfs(Disk *disk, u32int bs, u64int off) { Hfs *fs; Fsys *fsys; fsys = emalloc(sizeof(Fsys)); fs = emalloc(sizeof(Hfs)); fsys->priv = fs; fs->fsys = fsys; fs->disk = disk; fsys->type = "hfs"; fsys->_readblock = hfsblockread; fsys->_sync = hfssync; fsys->_root = hfsroot; fsys->_getattr = hfsgetattr; fsys->_access = hfsaccess; fsys->_lookup = hfslookup; fsys->_readfile = hfsreadfile; fsys->_readlink = hfsreadlink; fsys->_readdir = hfsreaddir; fsys->_close = hfsclose; fsys->disk = disk; if(hfswrapper(fsys) < 0 || hfssync(fsys) < 0) goto error; return fsys; error: hfsclose(fsys); return nil; } static void hfsclose(Fsys *fsys) { Hfs *fs; fs = fsys->priv; /* * We're not supposed to close the disk passed to * hfsopen, but if we created our own partition disk * we need to close that. It will close its subdisk * unless we call diskpartabandon. Bad planning. */ if(fs->disk && fs->disk != fsys->disk){ diskpartabandon(fs->disk); diskclose(fs->disk); } free(fs); free(fsys); } static int hfswrapper(Fsys *fsys) { Hfs *fs; Disk *disk; Block *b; uchar *mdb; int magic, hfsstart, subsig, substart, subnblocks; u32int hfsblocksize; u64int offset, size; fs = fsys->priv; disk = fsys->disk; if((b = diskread(disk, 162, 1024)) == nil) return -1; mdb = b->data; magic = get16(mdb); hfsblocksize = get32(mdb+20); hfsstart = get16(mdb+28); subsig = get16(mdb+124); substart = get16(mdb+126); subnblocks = get16(mdb+128); blockput(b); if(magic != Hfssig) return 0; if(subsig != Hfsplussig && subsig != Hfsxsig) return 0; offset = hfsstart*512 + substart*hfsblocksize; size = subnblocks*hfsblocksize; if((fs->disk = diskpart(disk, offset, size)) == nil) return -1; if(debug) fprint(2, "wrapped hfs size 0x%ullx offset 0x%ullx\n", size, offset); return 0; } static int checkvolhdr(uchar *vh) { u32int magic; magic = get32(vh); switch(magic){ case Hfsplusmagic: return 0; case Hfsxmagic: return 1; default: werrstr("bad volume header magic 0x%ux", magic); return -1; } } static int hfssync(Fsys *fsys) { Hfs *fs; Disk *disk; Block *b; uchar *vh; int sensitive; fs = fsys->priv; disk = fs->disk; if((b = diskread(disk, 512, 1024)) == nil) goto error; vh = b->data; if((sensitive = checkvolhdr(vh)) < 0) goto error; fs->blocksize = get32(vh+40); fs->nblock = get32(vh+44); fs->nfree = get32(vh+48); fs->hasbadblocks = get32(vh+4) & (1<<9); getfork(&fs->alloc, AllocId, Dfork, vh+112); getfork(&fs->extentsfork, ExtentsId, Dfork, vh+192); getfork(&fs->catalogfork, CatalogId, Dfork, vh+272); blockput(b); b = nil; if(hfstreesync(fs, &fs->extentsfork, &fs->extents, 0) < 0) goto error; if(hfstreesync(fs, &fs->catalogfork, &fs->catalog, sensitive) < 0) goto error; fs->hlinkparent = hfsmetadir(fs); fsys->blocksize = fs->blocksize; fsys->nblock = fs->nblock; if(debug) fprint(2, "hfs %ud %ud-byte blocks (%ud free, %s bad)\n", fs->nblock, fs->blocksize, fs->nfree, fs->hasbadblocks?"some":"none"); return 0; error: blockput(b); return -1; } static int hfstreesync(Hfs *fs, Fork *fork, Tree *tree, int sensitive) { Block *b; uchar *hr; Node node; b = nil; if(fork->extent[0].count == 0){ if(debug) fprint(2, "tree fork empty\n"); goto error; } if((b = hfsblock(fs,fork->extent[0].start)) == nil) goto error; getnodedesc(&node, b->data); if(node.type != HeaderNode){ if(debug) fprint(2, "bad header node type %d\n", node.type); goto error; } hr = b->data+Ndlen; tree->nodesize = get16(hr+18); tree->nnodes = get32(hr+22); tree->root = get32(hr+2); tree->height = get16(hr); tree->maxkeylen = get16(hr+20); tree->indexkeylen = (get32(hr+38)&4) ? 0 : tree->maxkeylen; tree->sensitive = sensitive && hr[37]==0xBC; blockput(b); tree->fs = fs; tree->fork = fork; if(debug) fprint(2, "tree %ud %ud %d-byte nodes\n", fork->cnid, tree->nnodes, tree->nodesize); return 0; error: werrstr("bad tree %ud header", fork->cnid); blockput(b); return -1; } static u32int hfsmetadir(Hfs *fs) { static Rune name[] = {0,0,0,0,'H','F','S','+',' ','P','r','i','v','a','t','e',' ','D','a','t','a'}; Catalogkey key; Treeref ref; Inode ino; key.parent = RootId; key.u.name.len = nelem(name); memcpy(key.u.name.name, name, sizeof name); if(hfscatsearch(fs, &key, &ref) < 0) goto notfound; if(getcatalogrecord(&ino, ref.data, ref.dlen) < 0) goto notfound; if((ino.mode&IFMT) != IFDIR) goto notfound; hfsrefput(&ref); if(debug) fprint(2, "metadata directory %ud\n", ino.cnid); return ino.cnid; notfound: if(debug) fprint(2, "metadata directory not found\n"); hfsrefput(&ref); return 0; } static int hfsbadblock(Hfs *fs, u32int bno) { Extent e[NEXTENTS]; int i; if(hfsextentsearch(fs, BadblockId, Dfork, bno, nil, e) < 0) return 0; /* Don't lightly throw away a block. */ for(i=0; i<NEXTENTS; i++) if(e[i].start <= bno && bno < e[i].start+e[i].count) return 1; if(debug) fprint(2, "corrupt badblock record, block %ud\n", bno); return 0; } static Block* hfsblockread(Fsys *fsys, u64int vbno) { Hfs *fs; u32int bno; Block *b; int n, alloc; fs = fsys->priv; if(vbno >= fs->nblock){ if(debug) fprint(2, "block %llud past end of volume\n", vbno); return nil; } bno = vbno; n = bno>>3; if((b = hfsforkblock(fs, &fs->alloc, n/fs->blocksize)) == nil){ if(debug) fprint(2, "can't read alloc bitmap for block %ud\n", bno); /* assume it's used */ } else{ alloc = b->data[n%fs->blocksize] & (1<<(7-(bno&7))); blockput(b); if(!alloc) return nil; } if(fs->hasbadblocks && hfsbadblock(fs, bno)){ if(debug) fprint(2, "bad block %ud\n", bno); return nil; } b = hfsblock(fs, bno); if(debug && b == nil) fprint(2, "can't read block %ud\n", bno); return b; } static int hasresource(Inode *ino) { return (ino->mode&IFMT)==IFREG && ino->u.f.rfork.size>0; } static void useresource(Inode *ino) { ino->fileid = ((u64int)1)<<32 | ino->cnid; ino->u.f.nhdr = Adlen; ino->u.f.fork = &ino->u.f.rfork; } static int ref2ino(Hfs *fs, Treeref *ref, Inode *ino) { static uchar magic[] = {'h','l','n','k','h','f','s','+'}; Catalogkey key; Treeref hlink; u32int cnid; if(getcatalogrecord(ino, ref->data, ref->dlen) < 0) return -1; if((ino->mode&IFMT) == IFREG && memcmp(ino->u.f.info, magic, nelem(magic)) == 0){ if(debug) print("iNode%ud...", ino->special); if(fs->hlinkparent == 0) return -1; key.parent = fs->hlinkparent; key.u.name.len = runesnprint(key.u.name.name, sizeof key.u.name.name, "iNode%ud", ino->special); if(hfscatsearch(fs, &key, &hlink) < 0) goto error; cnid = ino->cnid; if(getcatalogrecord(ino, hlink.data, hlink.dlen) < 0) goto error; hfsrefput(&hlink); ino->cnid = cnid; ino->fileid = cnid; ino->nlink = ino->special; } return 0; error: hfsrefput(&hlink); return -1; } static void mkhandle(Nfs3Handle *h, int rsrc, u32int cnid) { h->h[0] = rsrc; put32(h->h+1, cnid); h->len = 5; } static Nfs3Status hfsroot(Fsys *fsys, Nfs3Handle *h) { USED(fsys); mkhandle(h, 0, RootId); return Nfs3Ok; } static Nfs3Status handle2ino(Hfs *fs, Nfs3Handle *h, Treeref *ref, Catalogkey *key, Inode *ino) { Treeref refbuf; u32int cnid; Catalogkey keybuf; int rsrc; if(h->len != 5) return Nfs3ErrBadHandle; rsrc = h->h[0]; cnid = get32(h->h+1); if(debug) print("cnid %ud%s...", cnid, rsrc ? " rsrc" : ""); if(ref == nil) ref = &refbuf; if(key == nil) key = &keybuf; /* map cnid to full catalog key */ key->parent = cnid; key->u.name.len = 0; if(hfscatsearch(fs, key, ref) < 0) goto error; if(getcatalogthread(key, ref->data, ref->dlen) < 0) goto error; hfsrefput(ref); if(debug) print("{%ud,%.*S}...", key->parent, key->u.name.len, key->u.name.name); /* map full key to catalog info */ if(hfscatsearch(fs, key, ref) < 0) goto error; if(ino != nil){ if(ref2ino(fs, ref, ino) < 0) goto error; if(rsrc){ if(!hasresource(ino)){ hfsrefput(ref); return Nfs3ErrBadHandle; } useresource(ino); } } if(ref == &refbuf) hfsrefput(ref); return Nfs3Ok; error: hfsrefput(ref); return Nfs3ErrIo; } static Nfs3Status ino2attr(Hfs *fs, Inode *ino, Nfs3Attr *attr) { u32int rdev; switch(ino->mode&IFMT){ case IFIFO: attr->type = Nfs3FileFifo; break; case IFCHR: attr->type = Nfs3FileChar; break; case IFDIR: attr->type = Nfs3FileDir; break; case IFBLK: attr->type = Nfs3FileBlock; break; case IFREG: attr->type = Nfs3FileReg; break; case IFLNK: attr->type = Nfs3FileSymlink; break; case IFSOCK: attr->type = Nfs3FileSocket; break; case IFWHT: default: if(debug) fprint(2, "bad format %uo\n", ino->mode&IFMT); return Nfs3ErrBadHandle; } attr->mode = ino->mode&07777; attr->nlink = ino->nlink; attr->uid = ino->uid; attr->gid = ino->gid;