vx32

Local 9vx git repository for patches.
git clone git://r-36.net/vx32
Log | Files | Refs

devfs-posix.c (15130B)


      1 #include	"u.h"
      2 #include	<stdio.h> /* for remove, rename */
      3 #include	<pwd.h>
      4 #include	<grp.h>	/* going to regret this - getgrgid is a stack smasher */
      5 #include	<sys/socket.h>
      6 #include	<sys/un.h>
      7 
      8 #if defined(__FreeBSD__)
      9 #include	<sys/disk.h>
     10 #include	<sys/disklabel.h>
     11 #include	<sys/ioctl.h>
     12 #endif
     13 
     14 #if defined(__APPLE__)
     15 #include	<sys/disk.h>
     16 #endif
     17 
     18 #if defined(__linux__)
     19 #include	<linux/hdreg.h>
     20 #include	<linux/fs.h>
     21 #include	<sys/ioctl.h>
     22 #endif
     23 
     24 #include	"lib.h"
     25 #include	"mem.h"
     26 #include	"dat.h"
     27 #include	"fns.h"
     28 #include	"error.h"
     29 
     30 enum
     31 {
     32 	Trace = 0,
     33 	FsChar = 'Z',
     34 };
     35 
     36 extern char *canopen;
     37 extern Path *addelem(Path*, char*, Chan*);
     38 static char *uidtoname(int);
     39 static char *gidtoname(int);
     40 static int nametouid(char*);
     41 static int nametogid(char*);
     42 
     43 static vlong disksize(int, struct stat*);
     44 
     45 typedef struct UnixFd UnixFd;
     46 struct UnixFd
     47 {
     48 	int	fd;
     49 	int	issocket;
     50 	DIR*	dir;
     51 	vlong	diroffset;
     52 	QLock	dirlock;
     53 	struct dirent *nextde;
     54 	Path	*path;
     55 };
     56 
     57 void
     58 oserrstr(void)
     59 {
     60 	if (errno == EINTR)
     61 		kstrcpy(up->errstr, Eintr, ERRMAX);
     62 	else
     63 		kstrcpy(up->errstr, strerror(errno), ERRMAX);
     64 }
     65 
     66 void
     67 oserror(void)
     68 {
     69 	if (errno == EINTR)
     70 		error(Eintr);
     71 	else
     72 		error(strerror(errno));
     73 }
     74 
     75 static Qid
     76 fsqid(struct stat *st)
     77 {
     78 	Qid q;
     79 	int dev;
     80 	static int nqdev;
     81 	static uchar *qdev;
     82 
     83 	if(qdev == 0)
     84 		qdev = smalloc(65536U);
     85 
     86 	q.type = 0;
     87 	if((st->st_mode&S_IFMT) ==  S_IFDIR)
     88 		q.type = QTDIR;
     89 
     90 	dev = st->st_dev & 0xFFFFUL;
     91 	if(qdev[dev] == 0)
     92 		qdev[dev] = ++nqdev;
     93 	
     94 	q.path = (vlong)qdev[dev]<<48;
     95 	q.path ^= st->st_ino;
     96 	q.vers = st->st_mtime;
     97 	
     98 	return q;
     99 }
    100 
    101 static Chan*
    102 fsattach(char *spec)
    103 {
    104 	struct stat st;
    105 	Chan *c;
    106 	UnixFd *ufd;
    107 	int dev;
    108 	
    109 	dev = 1;
    110 	if(spec && spec[0]){
    111 		snprint(up->genbuf, sizeof up->genbuf, "no file system #%C%s", FsChar, spec);
    112 		error(up->genbuf);
    113 	}
    114 
    115 	if(stat("/", &st) < 0)
    116 		oserror();
    117 
    118 	c = devattach(FsChar, 0);
    119 	ufd = mallocz(sizeof(UnixFd), 1);
    120 	ufd->path = newpath("/");
    121 	ufd->fd = -1;
    122 
    123 	c->aux = ufd;
    124 	c->dev = dev;
    125 	c->qid = fsqid(&st);
    126 	
    127 	if(Trace)
    128 		print("fsattach /\n");
    129 
    130 	return c;
    131 }
    132 
    133 static Chan*
    134 fsclone(Chan *c, Chan *nc)
    135 {
    136 	UnixFd *ufd;
    137 	
    138 	ufd = mallocz(sizeof(UnixFd), 1);
    139 	*ufd = *(UnixFd*)c->aux;
    140 	if(ufd->path)
    141 		incref(&ufd->path->ref);
    142 	ufd->fd = -1;
    143 	nc->aux = ufd;
    144 	return nc;
    145 }
    146 
    147 static char*
    148 lastelem(char *s)
    149 {
    150 	char *t;
    151 
    152 	if(s[0] == '/' && s[1] == 0)
    153 		return s;
    154 	t = strrchr(s, '/');
    155 	if(t == nil)
    156 		return s;
    157 	return t+1;
    158 }
    159 
    160 static char*
    161 fspath(Chan *c, char *suffix)
    162 {
    163 	char *s, *t;
    164 	int len;
    165 	UnixFd *ufd;
    166 	
    167 	ufd = c->aux;
    168 	s = ufd->path->s;
    169 	len = strlen(s)+1;
    170 	if(suffix)
    171 		len += 1+strlen(suffix);
    172 	t = smalloc(len);
    173 	strcpy(t, s);
    174 	if(suffix){
    175 		if(s[strlen(s)-1] != '/')
    176 			strcat(t, "/");
    177 		strcat(t, suffix);
    178 	}
    179 	return t;
    180 }
    181 
    182 static int
    183 fswalk1(Chan *c, char *name)
    184 {
    185 	struct stat st;
    186 	char *path;
    187 	UnixFd *ufd;
    188 	
    189 	ufd = c->aux;
    190 	if(strcmp(name, "..") == 0 && strcmp(ufd->path->s, "/") == 0)
    191 		return 0;
    192 	
    193 	path = fspath(c, name);
    194 	if(stat(path, &st) < 0){
    195 		if(Trace)
    196 			print("fswalk1 %s (%s)\n", path, strerror(errno));	
    197 		free(path);
    198 		return -1;
    199 	}
    200 	if(Trace)
    201 		print("fswalk1 %s\n", path);	
    202 	free(path);
    203 	
    204 	c->qid = fsqid(&st);
    205 	return 0;
    206 }
    207 
    208 static void
    209 replacepath(Chan *c, Path *p)
    210 {
    211 	UnixFd *ufd;
    212 	
    213 	ufd = c->aux;
    214 	incref(&p->ref);
    215 	pathclose(ufd->path);
    216 	ufd->path = p;
    217 }
    218 
    219 static Walkqid*
    220 fswalk(Chan *c, Chan *nc, char **name, int nname)
    221 {
    222 	int i;
    223 	Path *path;
    224 	Walkqid *wq;
    225 	UnixFd *ufd;
    226 
    227 	if(nc != nil)
    228 		panic("fswalk: nc != nil");
    229 	wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid));
    230 	nc = devclone(c);
    231 	fsclone(c, nc);
    232 	ufd = c->aux;
    233 	path = ufd->path;
    234 	incref(&path->ref);
    235 
    236 	wq->clone = nc;
    237 	for(i=0; i<nname; i++){
    238 		ufd = nc->aux;
    239 		replacepath(nc, path);
    240 		if(fswalk1(nc, name[i]) < 0){
    241 			if(i == 0){
    242 				pathclose(path);
    243 				cclose(nc);
    244 				free(wq);
    245 				error(Enonexist);
    246 			}
    247 			break;
    248 		}
    249 		path = addelem(path, name[i], nil);
    250 		wq->qid[i] = nc->qid;
    251 	}
    252 	replacepath(nc, path);
    253 	pathclose(path);
    254 	if(i != nname){
    255 		cclose(nc);
    256 		wq->clone = nil;
    257 	}
    258 	wq->nqid = i;
    259 	return wq;
    260 }
    261 
    262 static int
    263 fsdirstat(char *path, int dev, Dir *d)
    264 {
    265 	int fd;
    266 	struct stat st;
    267 	
    268 	if(stat(path, &st) < 0 && lstat(path, &st) < 0)
    269 		return -1;
    270 
    271 	d->name = lastelem(path);
    272 	d->uid = uidtoname(st.st_uid);
    273 	d->gid = gidtoname(st.st_gid);
    274 	d->muid = "";
    275 	d->qid = fsqid(&st);
    276 	d->mode = (d->qid.type<<24) | (st.st_mode&0777);
    277 	d->atime = st.st_atime;
    278 	d->mtime = st.st_mtime;
    279 	d->length = st.st_size;
    280 	if(S_ISBLK(st.st_mode) && (fd = open(path, O_RDONLY)) >= 0){
    281 		d->length = disksize(fd, &st);
    282 		close(fd);
    283 	}
    284 	
    285 	// devmnt leaves 1-9 unused so that we can steal them.
    286 	// it is easier for all involved if #Z shows M as the file type instead of Z.
    287 	// dev is c->dev, either 1 (#Z) or 2 (#Zplan9).
    288 	d->type = 'M';
    289 	d->dev = dev;
    290 	return 0;
    291 }
    292 
    293 static int
    294 fsstat(Chan *c, uchar *buf, int n)
    295 {
    296 	Dir d;
    297 	char *path;
    298 	UnixFd *ufd;
    299 
    300 	ufd = c->aux;
    301 	if(Trace)
    302 		print("fsstat %s\n", ufd->path->s);
    303 
    304 	if(n < BIT16SZ)
    305 		error(Eshortstat);
    306 
    307 	path = fspath(c, nil);
    308 	if(fsdirstat(path, c->dev, &d) < 0){
    309 		free(path);
    310 		oserror();
    311 	}
    312 	if(strcmp(ufd->path->s, "/") == 0)
    313 		d.name = "/";
    314 	n = convD2M(&d, buf, n);
    315 	free(path);
    316 	return n;
    317 }
    318 
    319 static int
    320 opensocket(UnixFd *ufd, char *path)
    321 {
    322 	int fd;
    323 	struct stat st;
    324 	struct sockaddr_un su;
    325 	
    326 	if(stat(path, &st) < 0)
    327 		return -1;
    328 	if(!S_ISSOCK(st.st_mode))
    329 		return -1;
    330 	memset(&su, 0, sizeof su);
    331 	su.sun_family = AF_UNIX;
    332 	if(strlen(path)+1 > sizeof su.sun_path){
    333 		errno = ENAMETOOLONG;
    334 		return -1;
    335 	}
    336 	strcpy(su.sun_path, path);
    337 	if((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
    338 		return -1;
    339 	if(connect(fd, (struct sockaddr*)&su, sizeof su) < 0){
    340 		close(fd);
    341 		return -1;
    342 	}
    343 	ufd->fd = fd;
    344 	ufd->issocket = 1;
    345 	return 0;
    346 }		
    347 
    348 static Chan*
    349 fsopen(Chan *c, int mode)
    350 {
    351 	char *path;
    352 	int m;
    353 	UnixFd *ufd;
    354 	
    355 	ufd = c->aux;
    356 	if(Trace)
    357 		print("fsopen %s %#x\n", ufd->path->s, mode);
    358 
    359 	/* protect files whose path does not begin with allowed */
    360 	if(strncmp(ufd->path->s, canopen, strlen(canopen)) != 0)
    361 		error(Eperm);
    362 
    363 	if(mode & ~(OTRUNC|ORCLOSE|3))
    364 		error(Ebadarg);
    365 
    366 	if((c->qid.type & QTDIR) && mode != OREAD)
    367 		error(Eperm);
    368 	
    369 	if((c->qid.type&QTDIR) && mode != OREAD)
    370 		error(Eperm);
    371 
    372 	c->mode = openmode(mode);
    373 	path = fspath(c, nil);
    374 	if(c->qid.type & QTDIR){
    375 		ufd->dir = opendir(path);
    376 		if(ufd->dir == nil){
    377 			free(path);
    378 			oserror();
    379 		}
    380 		ufd->diroffset = 0;
    381 		ufd->nextde = nil;
    382 	}else{
    383 		m = mode & 3;
    384 		if(m == OEXEC)
    385 			m = OREAD;
    386 		if(mode & OTRUNC)
    387 			m |= O_TRUNC;
    388 		if((ufd->fd = open(path, m)) < 0 && opensocket(ufd, path) < 0){
    389 			free(path);
    390 			oserror();
    391 		}
    392 	}
    393 	free(path);
    394 	c->flag |= COPEN;
    395 	return c;
    396 }
    397 
    398 static void
    399 fscreate(Chan *c, char *name, int mode, ulong perm)
    400 {
    401 	char *path, *path0;
    402 	int fd, mm;
    403 	UnixFd *ufd;
    404 	struct stat st;
    405 
    406 	ufd = c->aux;
    407 	if(Trace)
    408 		print("fscreate %s %#x %#o\n", ufd->path->s, mode, perm);
    409 
    410 	if(!(c->qid.type & QTDIR))
    411 		error(Enotdir);
    412 
    413 	if(mode & ~(OTRUNC|ORCLOSE|3))
    414 		error(Ebadarg);
    415 
    416 	if(perm & ~(DMDIR|0777))
    417 		error(Ebadarg);
    418 
    419 	path0 = fspath(c, nil);
    420 	path = fspath(c, name);
    421 	if(waserror()){
    422 		free(path);
    423 		free(path0);
    424 		nexterror();
    425 	}
    426 	
    427 	if(stat(path0, &st) < 0)
    428 		oserror();
    429 
    430 	if(perm & DMDIR){
    431 		if(mode != OREAD)
    432 			error(Eperm);
    433 		/* have to do the minimum 0400 so we can open it */
    434 		if(mkdir(path, (0400 | perm) & 0777) < 0)
    435 			oserror();
    436 		if((fd = open(path, 0)) < 0)
    437 			oserror();
    438 		fchown(fd, -1, st.st_gid);
    439 		if(fstat(fd, &st) < 0){
    440 			close(fd);
    441 			oserror();
    442 		}
    443 		if((ufd->dir = opendir(path)) == nil) {
    444 			/* arguably we should set the mode here too 
    445  			 * but it's hard to see that this case 
    446  			 * will ever happen 
    447  			 */
    448 			close(fd);
    449 			oserror();
    450 		}
    451 		// Be like Plan 9 file servers: inherit mode bits 
    452 		// and group from parent.
    453 		fchmod(fd, perm & st.st_mode & 0777);
    454 		close(fd);
    455 		ufd->diroffset = 0;
    456 		ufd->nextde = nil;
    457 	}else{
    458 		mm = mode & 3;
    459 		if(mode & OTRUNC)
    460 			mm |= O_TRUNC;
    461 		if((fd = open(path, mm|O_CREAT|O_EXCL, 0666)) < 0)
    462 			oserror();
    463 		// Be like Plan 9 file servers: inherit mode bits 
    464 		// and group from parent.
    465 		fchmod(fd, perm & st.st_mode & 0777);
    466 		fchown(fd, -1, st.st_gid);
    467 		if(fstat(fd, &st) < 0){
    468 			close(fd);
    469 			oserror();
    470 		}
    471 		ufd->fd = fd;
    472 	}
    473 	free(path);
    474 	free(path0);
    475 	poperror();
    476 	
    477 	ufd->path = addelem(ufd->path, name, nil);
    478 	c->qid = fsqid(&st);
    479 	c->offset = 0;
    480 	c->flag |= COPEN;
    481 	c->mode = openmode(mode);
    482 }
    483 
    484 static void
    485 fsclose(Chan *c)
    486 {
    487 	UnixFd *ufd;
    488 	char *path;
    489 	
    490 	ufd = c->aux;
    491 	if(Trace)
    492 		print("fsclose %s\n", ufd->path->s);
    493 
    494 	if(c->flag & COPEN) {
    495 		if(c->flag & CRCLOSE) {
    496 			path = fspath(c, nil);
    497 			unlink(path);
    498 			free(path);
    499 		}
    500 		if(c->qid.type & QTDIR)
    501 			closedir(ufd->dir);
    502 		else
    503 			close(ufd->fd);
    504 	}
    505 	if(ufd->path)
    506 		pathclose(ufd->path);
    507 	free(ufd);
    508 }
    509 
    510 static long fsdirread(Chan*, uchar*, long, vlong);
    511 
    512 static long
    513 fsread(Chan *c, void *va, long n, vlong offset)
    514 {
    515 	int r;
    516 	UnixFd *ufd;
    517 
    518 	if(c->qid.type & QTDIR)
    519 		return fsdirread(c, va, n, offset);
    520 
    521 	ufd = c->aux;
    522 	if(ufd->issocket)
    523 		r = read(ufd->fd, va, n);
    524 	else
    525 		r = pread(ufd->fd, va, n, offset);
    526 	if(r < 0)
    527 		oserror();
    528 	return r;
    529 }
    530 
    531 static long
    532 fswrite(Chan *c, void *va, long n, vlong offset)
    533 {
    534 	int r;
    535 	UnixFd *ufd;
    536 
    537 	ufd = c->aux;
    538 	if(ufd->issocket)
    539 		r = write(ufd->fd, va, n);
    540 	else
    541 		r = pwrite(ufd->fd, va, n, offset);
    542 	if(r < 0)
    543 		oserror();
    544 	return r;
    545 }
    546 
    547 static int
    548 fswstat(Chan *c, uchar *buf, int n)
    549 {
    550 	char *elem, *path, *npath, *strs, *t;
    551 	int nn;
    552 	Dir d;
    553 	UnixFd *ufd;
    554 
    555 	if(n < 2)
    556 		error(Ebadstat);
    557 
    558 	nn = GBIT16((uchar*)buf);
    559 	strs = smalloc(nn);
    560 	if(convM2D(buf, n, &d, strs) != n){
    561 		free(strs);
    562 		error(Ebadstat);
    563 	}
    564 	
    565 	path = fspath(c, nil);
    566 	if(waserror()){
    567 		free(path);
    568 		free(strs);
    569 		nexterror();
    570 	}
    571 	
    572 	if(d.muid[0])
    573 		error("cannot change muid");
    574 
    575 	if(d.uid[0] || d.gid[0]){
    576 		int uid, gid;
    577 		
    578 		uid = -1;
    579 		gid = -1;
    580 		if(d.uid[0] && (uid = nametouid(d.uid)) < 0)
    581 			error("unknown uid");
    582 		if(d.gid[0] && (gid = nametogid(d.gid)) < 0)
    583 			error("unknown gid");
    584 		if(chown(path, uid, gid) < 0)
    585 			oserror();
    586 	}
    587 	
    588 	ufd = c->aux;
    589 	elem = lastelem(path);
    590 	if(d.name[0] && strcmp(d.name, elem) != 0){
    591 		if(strchr(d.name, '/'))
    592 			error(Ebadarg);
    593 		npath = smalloc(strlen(path)+strlen(d.name)+1);
    594 		strcpy(npath, path);
    595 		t = strrchr(npath, '/');
    596 		strcpy(t+1, d.name);
    597 		if(rename(path, npath) < 0){
    598 			free(npath);
    599 			oserror();
    600 		}
    601 		free(npath);
    602 	}
    603 	
    604 	if(~d.mode != 0 && chmod(path, d.mode&0777) < 0)
    605 		oserror();
    606 
    607 	// TODO: Code to change uid, gid.
    608 	
    609 	poperror();
    610 	return n;
    611 }
    612 
    613 static int
    614 isdots(char *name)
    615 {
    616 	if(name[0] != '.')
    617 		return 0;
    618 	if(name[1] == '\0')
    619 		return 1;
    620 	if(name[1] != '.')
    621 		return 0;
    622 	if(name[2] == '\0')
    623 		return 1;
    624 	return 0;
    625 }
    626 
    627 static long
    628 fsdirread(Chan *c, uchar *va, long count, vlong offset)
    629 {
    630 	char *path;
    631 	int n, total;
    632 	struct dirent *de;
    633 	UnixFd *ufd;
    634 	Dir d;
    635 
    636 	ufd = c->aux;
    637 	qlock(&ufd->dirlock);
    638 	if(waserror()){
    639 		qunlock(&ufd->dirlock);
    640 		nexterror();
    641 	}
    642 
    643 	if(ufd->diroffset != offset){
    644 		if(offset != 0)
    645 			error(Ebadarg);
    646 		ufd->diroffset = 0;
    647 		ufd->nextde = nil;
    648 		rewinddir(ufd->dir);
    649 	}
    650 
    651 	total = 0;
    652 	while(total+BIT16SZ < count) {
    653 		if(ufd->nextde){
    654 			de = ufd->nextde;
    655 			ufd->nextde = nil;
    656 		}
    657 		else if((de = readdir(ufd->dir)) == nil)
    658 			break;
    659 		if(isdots(de->d_name))
    660 			continue;
    661 		path = fspath(c, de->d_name);
    662 		if(fsdirstat(path, c->dev, &d) < 0){
    663 			free(path);
    664 			continue;
    665 		}
    666 		n = convD2M(&d, (uchar*)va+total, count-total);
    667 		free(path);
    668 		if(n == BIT16SZ){
    669 			ufd->nextde = de;
    670 			break;
    671 		}
    672 		total += n;
    673 	}
    674 	ufd->diroffset += total;
    675 	qunlock(&ufd->dirlock);
    676 	poperror();
    677 	return total;
    678 }
    679 
    680 static void
    681 fsremove(Chan *c)
    682 {
    683 	char *path;
    684 	UnixFd *ufd;
    685 
    686 	ufd = c->aux;
    687 	if(Trace)
    688 		print("fsremove %s\n", ufd->path->s);
    689 
    690 	path = fspath(c, nil);
    691 	if(waserror()){
    692 		free(path);
    693 		nexterror();
    694 	}
    695 	if(c->qid.type & QTDIR){
    696 		if(rmdir(path) < 0)
    697 			oserror();
    698 	}else{
    699 		if(remove(path) < 0)
    700 			oserror();
    701 	}
    702 	free(path);
    703 	poperror();
    704 }
    705 
    706 Dev fsdevtab = {
    707 	FsChar,
    708 	"fs",
    709 
    710 	devreset,
    711 	devinit,
    712 	devshutdown,
    713 	fsattach,
    714 	fswalk,
    715 	fsstat,
    716 	fsopen,
    717 	fscreate,
    718 	fsclose,
    719 	fsread,
    720 	devbread,
    721 	fswrite,
    722 	devbwrite,
    723 	fsremove,
    724 	fswstat,
    725 };
    726 
    727 
    728 /* Uid management code adapted from u9fs */
    729 
    730 /*
    731  * we keep a table by numeric id.  by name lookups happen infrequently
    732  * while by-number lookups happen once for every directory entry read
    733  * and every stat request.
    734  */
    735 typedef struct User User;
    736 struct User {
    737 	int id;
    738 	gid_t defaultgid;
    739 	char *name;
    740 	User *next;
    741 };
    742 
    743 
    744 static User *utab[64];
    745 static User *gtab[64];
    746 
    747 static User*
    748 adduser(struct passwd *p)
    749 {
    750 	User *u;
    751 
    752 	u = smalloc(sizeof(*u));
    753 	u->id = p->pw_uid;
    754 	kstrdup(&u->name, p->pw_name);
    755 	u->next = utab[p->pw_uid%nelem(utab)];
    756 	u->defaultgid = p->pw_gid;
    757 	utab[p->pw_uid%nelem(utab)] = u;
    758 	return u;
    759 }
    760 
    761 static User*
    762 addgroup(struct group *g)
    763 {
    764 	User *u;
    765 
    766 	u = smalloc(sizeof(*u));
    767 	u->id = g->gr_gid;
    768 	kstrdup(&u->name, g->gr_name);
    769 	u->next = gtab[g->gr_gid%nelem(gtab)];
    770 	gtab[g->gr_gid%nelem(gtab)] = u;
    771 	return u;
    772 }
    773 
    774 static User*
    775 uname2user(char *name)
    776 {
    777 	int i;
    778 	User *u;
    779 	struct passwd *p;
    780 
    781 	for(i=0; i<nelem(utab); i++)
    782 		for(u=utab[i]; u; u=u->next)
    783 			if(strcmp(u->name, name) == 0)
    784 				return u;
    785 
    786 	if((p = getpwnam(name)) == nil)
    787 		return nil;
    788 	return adduser(p);
    789 }
    790 
    791 static User*
    792 uid2user(int id)
    793 {
    794 	User *u;
    795 	struct passwd *p;
    796 
    797 	for(u=utab[id%nelem(utab)]; u; u=u->next)
    798 		if(u->id == id)
    799 			return u;
    800 
    801 	if((p = getpwuid(id)) == nil)
    802 		return nil;
    803 	return adduser(p);
    804 }
    805 
    806 static User*
    807 gname2user(char *name)
    808 {
    809 	int i;
    810 	User *u;
    811 	struct group *g;
    812 
    813 	for(i=0; i<nelem(gtab); i++)
    814 		for(u=gtab[i]; u; u=u->next)
    815 			if(strcmp(u->name, name) == 0)
    816 				return u;
    817 
    818 	if((g = getgrnam(name)) == nil)
    819 		return nil;
    820 	return addgroup(g);
    821 }
    822 
    823 static User*
    824 gid2user(int id)
    825 {
    826 	User *u;
    827 	struct group *g;
    828 
    829 	for(u=gtab[id%nelem(gtab)]; u; u=u->next)
    830 		if(u->id == id)
    831 			return u;
    832 
    833 	if((g = getgrgid(id)) == nil)
    834 		return nil;
    835 	return addgroup(g);
    836 }
    837 
    838 static char*
    839 uidtoname(int uid)
    840 {
    841 	User *u;
    842 	
    843 	u = uid2user(uid);
    844 	if(u == nil)
    845 		return "?";
    846 	return u->name;
    847 }
    848 
    849 static char*
    850 gidtoname(int gid)
    851 {
    852 	User *u;
    853 	
    854 	u = gid2user(gid);
    855 	if(u == nil)
    856 		return "?";
    857 	return u->name;
    858 }
    859 
    860 static int
    861 nametouid(char *name)
    862 {
    863 	User *u;
    864 	
    865 	u = uname2user(name);
    866 	if(u == nil)
    867 		return -1;
    868 	return u->id;
    869 }
    870 
    871 static int
    872 nametogid(char *name)
    873 {
    874 	User *u;
    875 	
    876 	u = gname2user(name);
    877 	if(u == nil)
    878 		return -1;
    879 	return u->id;
    880 }
    881 
    882 #if defined(__linux__)
    883 
    884 static vlong
    885 disksize(int fd, struct stat *st)
    886 {
    887 	uvlong u64;
    888 	long l;
    889 	struct hd_geometry geo;
    890 	
    891 	memset(&geo, 0, sizeof geo);
    892 	l = 0;
    893 	u64 = 0;
    894 #ifdef BLKGETSIZE64
    895 	if(ioctl(fd, BLKGETSIZE64, &u64) >= 0)
    896 		return u64;
    897 #endif
    898 	if(ioctl(fd, BLKGETSIZE, &l) >= 0)
    899 		return l*512;
    900 	if(ioctl(fd, HDIO_GETGEO, &geo) >= 0)
    901 		return (vlong)geo.heads*geo.sectors*geo.cylinders*512;
    902 	return 0;
    903 }
    904 
    905 #elif defined(__FreeBSD__) && defined(DIOCGMEDIASIZE)
    906 
    907 static vlong
    908 disksize(int fd, struct stat *st)
    909 {
    910 	off_t mediasize;
    911 	
    912 	if(ioctl(fd, DIOCGMEDIASIZE, &mediasize) >= 0)
    913 		return mediasize;
    914 	return 0;
    915 }
    916 
    917 #elif defined(__APPLE__)
    918 
    919 static vlong
    920 disksize(int fd, struct stat *st)
    921 {
    922 	uvlong bc;
    923 	unsigned int bs;
    924 
    925 	bs = 0;
    926 	bc = 0;
    927 	ioctl(fd, DKIOCGETBLOCKSIZE, &bs);
    928 	ioctl(fd, DKIOCGETBLOCKCOUNT, &bc);
    929 	if(bs >0 && bc > 0)
    930 		return bc*bs;
    931 	return 0;
    932 }
    933 
    934 #else
    935 
    936 static vlong
    937 disksize(int fd, struct stat *st)
    938 {
    939 	return 0;
    940 }
    941 
    942 #endif