1975 lines · 54.1 KB · C
RawDownload
/*	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.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t