Commit Diff


commit - 001dc1709be54e944d8ce0f0fdb94500cad2d16a
commit + 63408c39a1d07b210540e33a5fec030296cc5f49
blob - 16a04239f1a4105e19d0474ce3afd7ac52f2f3f3
blob + 521296532119b5a16ff2511df26fcc9a97400320
--- src/libdiskfs/ext2.c
+++ src/libdiskfs/ext2.c
@@ -594,6 +594,7 @@ ext2readdir(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h
 		return ok;
 
 	if(cookie >= ino.size){
+		*peof = 1;
 		*pcount = 0;
 		*pdata = 0;
 		return Nfs3Ok;
blob - 9607cfbebb91c1a8f782a264f2d0b7b7d6d6601b
blob + d8e2339245c284d54173c45f8d837f9ad958519c
--- src/libdiskfs/ffs.c
+++ src/libdiskfs/ffs.c
@@ -696,6 +696,7 @@ ffsreaddir(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h,
 		return ok;
 
 	if(cookie >= ino.size){
+		*peof = 1;
 		*pcount = 0;
 		*pdata = 0;
 		return Nfs3Ok;
blob - 8f2823d284eea381fbbb07b7747123b48d3ad1b3
blob + f219357291cd3dbd855d08e7af0f3426a5531d97
--- src/libdiskfs/hfs.c
+++ src/libdiskfs/hfs.c
@@ -1,7 +1,6 @@
 /*
 	Copyright (c) 2007 David Swasey; see COPYRIGHT.
 	Limitations:
-		Resource forks are hidden.
 		Hfsreaddir skips entries whose names contain NUL.
 		Simulated hard links are ignored.
 		Hfsbadblock is untested.
@@ -17,6 +16,9 @@
 
 #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);
@@ -27,6 +29,7 @@ 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*, Catalogkey*, 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**);
@@ -58,6 +61,7 @@ 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*);
@@ -307,59 +311,82 @@ hfsblockread(Fsys *fsys, u64int vbno)
 	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->rfork.size>0;
 }
 
 static void
-mkhandle(Nfs3Handle *h, u32int cnid)
+useresource(Inode *ino)
 {
-	h->h[0] = cnid >> 24;
-	h->h[1] = cnid >> 16;
-	h->h[2] = cnid >> 8;
-	h->h[3] = cnid;
-	h->len = 4;
+	ino->fileid = ((u64int)1)<<32 | ino->cnid;
+	ino->nhdr = Adlen;
+	ino->fork = &ino->rfork;
 }
 
+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, RootId);
+	mkhandle(h, 0, RootId);
 	return Nfs3Ok;
 }
 
 static Nfs3Status
-handle2ino(Hfs *fs, Nfs3Handle *h, u32int *parent, Treeref *ref, Inode *ino)
+handle2ino(Hfs *fs, Nfs3Handle *h, Treeref *ref, Catalogkey *key, Inode *ino)
 {
 	Treeref refbuf;
 	u32int cnid;
-	Catalogkey key;
+	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;
 
-	if(h->len != 4)
-		return Nfs3ErrBadHandle;
-	cnid = get32(h->h);
-	if(debug) print("cnid %ud...", cnid);
-
 	/* map cnid to full catalog key */
-	key.parent = cnid;
-	key.name.len = 0;
-	if(hfscatsearch(fs, &key, ref) < 0)
+	key->parent = cnid;
+	key->name.len = 0;
+	if(hfscatsearch(fs, key, ref) < 0)
 		goto error;
-	if(getcatalogthread(&key, ref->data, ref->dlen) < 0)
+	if(getcatalogthread(key, ref->data, ref->dlen) < 0)
 		goto error;
 	hfsrefput(ref);
 
-	if(debug) print("{%ud,%.*S}...", key.parent, key.name.len, key.name.name);
+	if(debug) print("{%ud,%.*S}...", key->parent, key->name.len, key->name.name);
 
 	/* map full key to catalog info */
-	if(hfscatsearch(fs, &key, ref) < 0)
+	if(hfscatsearch(fs, key, ref) < 0)
 		goto error;
-	if(parent)
-		*parent = key.parent;
-	if(ino != nil && getcatalogrecord(ino, ref->data, ref->dlen) < 0)
-		goto error;
+	if(ino != nil){
+		if(getcatalogrecord(ino, ref->data, ref->dlen) < 0)
+			goto error;
+		if(rsrc){
+			if(!hasresource(ino)){
+				hfsrefput(ref);
+				return Nfs3ErrBadHandle;
+			}
+			useresource(ino);
+		}
+	}
 	if(ref == &refbuf)
 		hfsrefput(ref);
 
@@ -408,8 +435,8 @@ ino2attr(Hfs *fs, Inode *ino, Nfs3Attr *attr)
 	attr->uid = ino->uid;
 	attr->gid = ino->gid;
 	if(attr->type==Nfs3FileReg || attr->type==Nfs3FileSymlink){
-		attr->size = ino->dfork.size;
-		attr->used = (u64int)ino->dfork.nblocks*fs->blocksize;
+		attr->size = ino->nhdr+ino->fork->size;
+		attr->used = (u64int)ino->fork->nblocks*fs->blocksize;
 	}
 	else{
 		attr->size = 0;
@@ -424,7 +451,7 @@ ino2attr(Hfs *fs, Inode *ino, Nfs3Attr *attr)
 		attr->minor = 0;
 	}
 	attr->fsid = 0;
-	attr->fileid = ino->cnid;
+	attr->fileid = ino->fileid;
 	attr->atime.sec = ino->atime;
 	attr->atime.nsec = 0;
 	attr->mtime.sec = ino->mtime;
@@ -550,14 +577,14 @@ static Nfs3Status
 hfslookup(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, char *name, Nfs3Handle *nh)
 {
 	Hfs *fs;
-	u32int parent;
 	Inode ino, target;
 	Nfs3Status ok;
 	Catalogkey key;
 	Treeref ref;
+	int rsrc;
 
 	fs = fsys->priv;
-	if((ok = handle2ino(fs, h, &parent, nil, &ino)) != Nfs3Ok)
+	if((ok = handle2ino(fs, h, nil, &key, &ino)) != Nfs3Ok)
 		return ok;
 
 	if((ino.mode&IFMT) != IFDIR)
@@ -567,22 +594,28 @@ hfslookup(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, 
 		return ok;
 
 	if(strcmp(name, ".") == 0){
-		mkhandle(nh, ino.cnid);
+		mkhandle(nh, 0, ino.cnid);
 		return Nfs3Ok;
 	}
 	if(strcmp(name, "..") == 0){
-		mkhandle(nh, parent);
+		mkhandle(nh, 0, key.parent);
 		return Nfs3Ok;
 	}
-	key.parent = ino.cnid;
-	if((ok = utf2name(&key.name, name)) != Nfs3Ok)
-		return ok;
-	if(hfscatsearch(fs, &key, &ref) < 0)
-		return Nfs3ErrNoEnt;
+
+	rsrc = 0;
+	if((ok = _hfslookup(fs, ino.cnid, name, &key, &ref)) != Nfs3Ok){
+		if(memcmp(name, Rprefix, Rplen)==0
+		&& _hfslookup(fs, ino.cnid, name+Rplen, &key, &ref)==Nfs3Ok)
+			rsrc = 1;
+		else
+			return ok;
+	}
 	if(getcatalogrecord(&target, ref.data, ref.dlen) < 0)
 		goto error;
 	hfsrefput(&ref);
-	mkhandle(nh, target.cnid);
+	if(rsrc && !hasresource(&target))
+		return Nfs3ErrNoEnt;
+	mkhandle(nh, rsrc, target.cnid);
 	return Nfs3Ok;
 
 error:
@@ -590,6 +623,20 @@ error:
 	return Nfs3ErrIo;
 }
 
+static Nfs3Status
+_hfslookup(Hfs *fs, u32int parent, char *name, Catalogkey *key, Treeref *ref)
+{
+	Nfs3Status ok;
+
+	key->parent = parent;
+	if((ok = utf2name(&key->name, name)) != Nfs3Ok)
+		return ok;
+	if(hfscatsearch(fs, key, ref) < 0)
+		return Nfs3ErrNoEnt;
+	return Nfs3Ok;
+}
+
+
 /*
 	For the moment, hfsreaddir does not return entries whose names
 	contain NUL, avoiding errors like "ls: : No such file or
@@ -615,11 +662,13 @@ hfsreaddir(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h,
 	Treeref ref;
 	Catalogkey key;
 	Nfs3Entry e;
-	char name[UTFNAMELEN];
+	int rsrc;
+	char name[Rplen+UTFNAMELEN];
 	uchar *dp, *dep, *ndp, *data;
 	Hfs *fs;
 	Inode ino, child;
 	Nfs3Status ok;
+	u32int nentries;
 
 	fs = fsys->priv;
 	if((ok = handle2ino(fs, h, nil, nil, &ino)) != Nfs3Ok)
@@ -631,10 +680,15 @@ hfsreaddir(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h,
 	if((ok = inoperm(&ino, au, AREAD)) != Nfs3Ok)
 		return ok;
 
+	if(ino.nentries>>31)
+		return Nfs3ErrIo;
+	nentries = ino.nentries*2;	/* even data, odd resource */
+	
 	i = cookie>>32;
 	cnid = cookie&0xFFFFFFFF;
-	if(debug) print("readdir %ud %ud %ud...", cnid, i, ino.nentries);
-	if(i > ino.nentries){
+	if(debug) print("readdir %ud %ud %ud...", cnid, i, nentries);
+	if(i >= nentries){
+		*peof = 1;
 		*pcount = 0;
 		*pdata = nil;
 		return Nfs3Ok;
@@ -658,50 +712,89 @@ hfsreaddir(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h,
 			goto error;
 	}
 	else{
-		mkhandle(&ch, cnid);
-		if((ok = handle2ino(fs, &ch, nil, &ref, nil)) != Nfs3Ok)
+		mkhandle(&ch, i&1, cnid);
+		if((ok = handle2ino(fs, &ch, &ref, &key, &child)) != Nfs3Ok)
 			return ok;
+		if(key.parent != ino.cnid)
+			goto badparent;
 		i++;
 	}
 	
 	memset(&e, 0, sizeof e);
-	for(; i<ino.nentries; i++){
-		if(hfsrefnextrec(&ref) < 0)
-			goto error;
-		if(getcatalogkey(&key, ref.key, ref.klen, 1) < 0)
-			goto error;
-		if(key.parent != ino.cnid){
-			if(debug) fprint(2, "%.*S: bad parent %ud != %ud\n",
-				key.name.len, key.name.name, key.parent, ino.cnid);
-			goto error;
+	for(; i<nentries; i++){
+		rsrc = i&1;
+		if(!rsrc){
+			if(hfsrefnextrec(&ref) < 0)
+				goto error;
+			if(getcatalogkey(&key, ref.key, ref.klen, 1) < 0)
+				goto error;
+			if(key.parent != ino.cnid)
+				goto badparent;
+			if(getcatalogrecord(&child, ref.data, ref.dlen) < 0)
+				goto error;
 		}
-		if(getcatalogrecord(&child, ref.data, ref.dlen) < 0)
-			goto error;
+		else if(!hasresource(&child))
+			continue;
+		else
+			useresource(&child);
 		if(hfshidename(&key.name))
 			continue;
-		if(debug) print("{%ud,%ud,%.*S} ",
-			i, child.cnid, key.name.len, key.name.name);
-		e.fileid = child.cnid;
+		e.fileid = child.fileid;
 		e.name = name;
-		e.namelen = name2utf(name, &key.name);
+		if(rsrc){
+			memcpy(name, Rprefix, Rplen);
+			e.namelen = Rplen+name2utf(name+Rplen, &key.name);
+		}
+		else
+			e.namelen = name2utf(name, &key.name);
 		e.cookie = ((u64int)i)<<32 | child.cnid;
 		e.haveAttr = (ino2attr(fs, &child, &e.attr) == Nfs3Ok);
 		e.haveHandle = 1;
-		mkhandle(&e.handle, child.cnid);
+		mkhandle(&e.handle, rsrc, child.cnid);
+		if(debug) print("%s/0x%llux ", e.name, e.fileid);
 		if(nfs3entrypack(dp, dep, &ndp, &e) < 0)
 			break;
 		dp = ndp;
 	}
 	hfsrefput(&ref);
-	if(i == ino.nentries)
+	if(i == nentries)
 		*peof = 1;
 	*pcount = dp - data;
 	*pdata = data;
 	return Nfs3Ok;
 
+badparent:
+	if(debug) fprint(2, "%.*S: bad parent %ud != %ud\n",
+		key.name.len, key.name.name, key.parent, ino.cnid);
 error:
 	hfsrefput(&ref);
 	return Nfs3ErrIo;
+}
+
+/* See RFC 1740. */
+static int
+appledouble(Inode *ino, uchar *ad)
+{
+	static uchar Adhdr[Adlen-4-Filen] =
+	{
+		0,5,22,7,0,2,0,0,		/* magic */
+		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,		/* filler */
+		0,2,		/* nentries */
+			0,0,0,9,		/* magic: finder info */
+			0,0,0,Fioff,		/* offset */
+			0,0,0,Filen,		/* length */
+			0,0,0,2,		/* magic: rfork */
+			0,0,0,Adlen,		/* offset */
+	};
+
+	if(ino->rfork.size>>32){
+		if(debug) fprint(2, "resource fork %ud too large\n", ino->cnid);
+		return -1;
+	}
+	memcpy(ad, Adhdr, nelem(Adhdr));
+	put32(ad+nelem(Adhdr), ino->rfork.size);
+	memcpy(ad+Fioff, ino->info, Filen);
+	return 0;
 }
 
 static Nfs3Status
@@ -709,7 +802,8 @@ hfsreadfile(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h
 	u64int offset, uchar **pdata, u32int *pcount, u1int *peof)
 {
 	u64int size;
-	uchar *data;
+	int skip;
+	uchar *data, prefix[Adlen];
 	Hfs *fs;
 	Inode ino;
 	Nfs3Status ok;
@@ -724,7 +818,7 @@ hfsreadfile(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h
 	if((ok = inoperm(&ino, au, AREAD)) != Nfs3Ok)
 		return ok;
 
-	size = ino.dfork.size;
+	size = ino.nhdr+ino.fork->size;
 	if(offset >= size){
 		*pdata = 0;
 		*pcount = 0;
@@ -736,15 +830,30 @@ hfsreadfile(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h
 	data = mallocz(count, 1);
 	if(data == nil)
 		return Nfs3ErrNoMem;
-	if(hfsforkread(fs, &ino.dfork, data, count, offset) < 0){
-		free(data);
-		return Nfs3ErrIo;
+	if(offset < ino.nhdr){
+		if(appledouble(&ino, prefix) < 0)
+			goto error;
+		skip = Adlen-offset;
+		if(skip > count)
+			skip = count;
+		memcpy(data, prefix+offset, skip);
+		offset = 0;
 	}
+	else{
+		offset -= ino.nhdr;
+		skip = 0;
+	}
+	if(hfsforkread(fs, ino.fork, data+skip, count-skip, offset) < 0)
+		goto error;
 
 	*peof = (offset+count == size);
 	*pcount = count;
 	*pdata = data;
 	return Nfs3Ok;
+
+error:
+	free(data);
+	return Nfs3ErrIo;
 }
 
 static Nfs3Status
@@ -1242,6 +1351,7 @@ getcatalogrecord(Inode *ino, uchar *b, int blen)
 		return -1;
 	}
 	ino->cnid = get32(b+8);
+	ino->fileid = ino->cnid;
 	ino->mtime = gettime(b+16);
 	ino->ctime = gettime(b+20);
 	ino->atime = gettime(b+24);
@@ -1252,8 +1362,13 @@ getcatalogrecord(Inode *ino, uchar *b, int blen)
 	ino->special = get32(p+12);
 	if(t == Folder)
 		ino->nentries = get32(b+4);
-	else
+	else{
 		getfork(&ino->dfork, ino->cnid, Dfork, b+88);
+		getfork(&ino->rfork, ino->cnid, Rfork, b+168);
+		memcpy(ino->info, b+48, Filen);
+		ino->nhdr = 0;
+		ino->fork = &ino->dfork;
+	}
 	return 0;
 }
 
@@ -1328,6 +1443,15 @@ get16(uchar *b)
 	return b[0]<<8 | b[1];
 }
 
+static void
+put32(uchar *b, u32int n)
+{
+	b[0] = n>>24;
+	b[1] = n>>16;
+	b[2] = n>>8;
+	b[3] = n;
+}
+
 /*
 	Adapted from FastUnicodeCompare in Apple technical note 1150.
 */
blob - 2f6d943d1866c165848fbf75e62b439920ed4600
blob + 5be65a76dc742d8278e09ac1b45505ec16b51186
--- src/libdiskfs/hfs.h
+++ src/libdiskfs/hfs.h
@@ -65,6 +65,9 @@ enum
 	Extentlen = 8,		/* Extent */
 	Ndlen = 14,		/* Node */
 	Folderlen = 88, Filelen = 248,		/* Inode */
+	Adlen = 82,		/* Apple double header */
+		Fioff = 50,
+	Filen = 32,		/* Finder info */
 
 	/* values in Node.type */
 	LeafNode = -1, IndexNode, HeaderNode, MapNode,
@@ -113,6 +116,7 @@ struct Fork
 struct Inode
 {
 	u32int	cnid;
+	u64int	fileid;		/* in memory only */
 	u32int	mtime;		/* modification */
 	u32int	ctime;		/* attribute modification */
 	u32int	atime;		/* access */
@@ -124,7 +128,12 @@ struct Inode
 		u32int	nentries;		/* directories */
 		struct{		/* files */
 			Fork	dfork;
-			/*Fork rfork;*/
+			Fork	rfork;
+			uchar	info[Filen];
+
+			/* in memory only */
+			int	nhdr;		/* 0 or Adlen */
+			Fork	*fork;		/* dfork or rfork */
 		};
 	};
 };
@@ -204,7 +213,6 @@ struct Hfs
 	Fork	catalogfork;
 	Tree	extents;		/* Extentkey -> Extent[NEXTENT] */
 	Tree	catalog;		/* Catalogkey -> Catalogkey + Inode */
-	int	wrapper;		/* HFS wrapper used? */
 	Disk	*disk;
 	Fsys	*fsys;
 };