xref: /dflybsd-src/bin/cpdup/hcproto.c (revision c9e3d8f96688a159959b1af2d4fef14b744173e3)
1 /*
2  * HCPROTO.C
3  *
4  * This module implements a simple remote control protocol
5  *
6  * $DragonFly: src/bin/cpdup/hcproto.c,v 1.8 2008/11/11 04:36:00 dillon Exp $
7  */
8 
9 #include "cpdup.h"
10 #include "hclink.h"
11 #include "hcproto.h"
12 
13 static int hc_decode_stat(struct stat *, struct HCHead *);
14 static int rc_encode_stat(hctransaction_t trans, struct stat *);
15 
16 static int rc_hello(hctransaction_t trans, struct HCHead *);
17 static int rc_stat(hctransaction_t trans, struct HCHead *);
18 static int rc_lstat(hctransaction_t trans, struct HCHead *);
19 static int rc_opendir(hctransaction_t trans, struct HCHead *);
20 static int rc_readdir(hctransaction_t trans, struct HCHead *);
21 static int rc_closedir(hctransaction_t trans, struct HCHead *);
22 static int rc_open(hctransaction_t trans, struct HCHead *);
23 static int rc_close(hctransaction_t trans, struct HCHead *);
24 static int rc_read(hctransaction_t trans, struct HCHead *);
25 static int rc_write(hctransaction_t trans, struct HCHead *);
26 static int rc_remove(hctransaction_t trans, struct HCHead *);
27 static int rc_mkdir(hctransaction_t trans, struct HCHead *);
28 static int rc_rmdir(hctransaction_t trans, struct HCHead *);
29 static int rc_chown(hctransaction_t trans, struct HCHead *);
30 static int rc_lchown(hctransaction_t trans, struct HCHead *);
31 static int rc_chmod(hctransaction_t trans, struct HCHead *);
32 static int rc_mknod(hctransaction_t trans, struct HCHead *);
33 static int rc_link(hctransaction_t trans, struct HCHead *);
34 #ifdef _ST_FLAGS_PRESENT_
35 static int rc_chflags(hctransaction_t trans, struct HCHead *);
36 #endif
37 static int rc_readlink(hctransaction_t trans, struct HCHead *);
38 static int rc_umask(hctransaction_t trans, struct HCHead *);
39 static int rc_symlink(hctransaction_t trans, struct HCHead *);
40 static int rc_rename(hctransaction_t trans, struct HCHead *);
41 static int rc_utimes(hctransaction_t trans, struct HCHead *);
42 static int rc_geteuid(hctransaction_t trans, struct HCHead *);
43 static int rc_getgroups(hctransaction_t trans, struct HCHead *);
44 
45 static int getmygroups(gid_t **gidlist);
46 
47 struct HCDesc HCDispatchTable[] = {
48     { HC_HELLO,		rc_hello },
49     { HC_STAT,		rc_stat },
50     { HC_LSTAT,		rc_lstat },
51     { HC_OPENDIR,	rc_opendir },
52     { HC_READDIR,	rc_readdir },
53     { HC_CLOSEDIR,	rc_closedir },
54     { HC_OPEN,		rc_open },
55     { HC_CLOSE,		rc_close },
56     { HC_READ,		rc_read },
57     { HC_WRITE,		rc_write },
58     { HC_REMOVE,	rc_remove },
59     { HC_MKDIR,		rc_mkdir },
60     { HC_RMDIR,		rc_rmdir },
61     { HC_CHOWN,		rc_chown },
62     { HC_LCHOWN,	rc_lchown },
63     { HC_CHMOD,		rc_chmod },
64     { HC_MKNOD,		rc_mknod },
65     { HC_LINK,		rc_link },
66 #ifdef _ST_FLAGS_PRESENT_
67     { HC_CHFLAGS,	rc_chflags },
68 #endif
69     { HC_READLINK,	rc_readlink },
70     { HC_UMASK,		rc_umask },
71     { HC_SYMLINK,	rc_symlink },
72     { HC_RENAME,	rc_rename },
73     { HC_UTIMES,	rc_utimes },
74     { HC_GETEUID,	rc_geteuid },
75     { HC_GETGROUPS,	rc_getgroups },
76 };
77 
78 static int chown_warning;
79 static int chflags_warning;
80 
81 /*
82  * If not running as root generate a silent warning and return no error.
83  *
84  * If running as root return an error.
85  */
86 static int
87 silentwarning(int *didwarn, const char *ctl, ...)
88 {
89     va_list va;
90 
91     if (DstRootPrivs)
92 	return(-1);
93     if (*didwarn == 0 && QuietOpt == 0) {
94 	*didwarn = 1;
95 	fprintf(stderr, "WARNING: Not running as root, ");
96 	va_start(va, ctl);
97 	vfprintf(stderr, ctl, va);
98 	va_end(va);
99     }
100     return(0);
101 }
102 
103 int
104 hc_connect(struct HostConf *hc)
105 {
106     if (hcc_connect(hc) < 0) {
107 	fprintf(stderr, "Unable to connect to %s\n", hc->host);
108 	return(-1);
109     }
110     return(hc_hello(hc));
111 }
112 
113 void
114 hc_slave(int fdin, int fdout)
115 {
116     hcc_slave(fdin, fdout, HCDispatchTable,
117 	      sizeof(HCDispatchTable) / sizeof(HCDispatchTable[0]));
118 
119 }
120 
121 /*
122  * A HELLO RPC is sent on the initial connect.
123  */
124 int
125 hc_hello(struct HostConf *hc)
126 {
127     struct HCHead *head;
128     struct HCLeaf *item;
129     hctransaction_t trans;
130     char hostbuf[256];
131     int error;
132 
133     bzero(hostbuf, sizeof(hostbuf));
134     if (gethostname(hostbuf, sizeof(hostbuf) - 1) < 0)
135         return(-1);
136     if (hostbuf[0] == 0)
137 	hostbuf[0] = '?';
138 
139     trans = hcc_start_command(hc, HC_HELLO);
140     hcc_leaf_string(trans, LC_HELLOSTR, hostbuf);
141     hcc_leaf_int32(trans, LC_VERSION, HCPROTO_VERSION);
142     if ((head = hcc_finish_command(trans)) == NULL) {
143 	fprintf(stderr, "Connected to %s but remote failed to complete hello\n",
144 		hc->host);
145 	return(-1);
146     }
147 
148     if (head->error) {
149 	fprintf(stderr, "Connected to %s but remote returned error %d\n",
150 		hc->host, head->error);
151 	return(-1);
152     }
153 
154     error = -1;
155     for (item = hcc_firstitem(head); item; item = hcc_nextitem(head, item)) {
156 	switch(item->leafid) {
157 	case LC_HELLOSTR:
158 	    if (QuietOpt == 0)
159 		fprintf(stderr, "Handshaked with %s\n", HCC_STRING(item));
160 	    error = 0;
161 	    break;
162 	case LC_VERSION:
163 	    hc->version = HCC_INT32(item);
164 	    break;
165 	}
166     }
167     if (hc->version < HCPROTO_VERSION_COMPAT) {
168 	fprintf(stderr, "Remote cpdup at %s has an incompatible version\n",
169 		hc->host);
170 	error = -1;
171     }
172     if (error < 0)
173 	fprintf(stderr, "Handshake failed with %s\n", hc->host);
174     return (error);
175 }
176 
177 static int
178 rc_hello(hctransaction_t trans, struct HCHead *head __unused)
179 {
180     char hostbuf[256];
181 
182     bzero(hostbuf, sizeof(hostbuf));
183     if (gethostname(hostbuf, sizeof(hostbuf) - 1) < 0)
184         return(-1);
185     if (hostbuf[0] == 0)
186 	hostbuf[0] = '?';
187 
188     hcc_leaf_string(trans, LC_HELLOSTR, hostbuf);
189     hcc_leaf_int32(trans, LC_VERSION, HCPROTO_VERSION);
190     return(0);
191 }
192 
193 /*
194  * STAT, LSTAT
195  */
196 int
197 hc_stat(struct HostConf *hc, const char *path, struct stat *st)
198 {
199     struct HCHead *head;
200     hctransaction_t trans;
201 
202     if (hc == NULL || hc->host == NULL)
203 	return(stat(path, st));
204 
205     trans = hcc_start_command(hc, HC_STAT);
206     hcc_leaf_string(trans, LC_PATH1, path);
207     if ((head = hcc_finish_command(trans)) == NULL)
208 	return(-1);
209     if (head->error)
210 	return(-1);
211     return(hc_decode_stat(st, head));
212 }
213 
214 int
215 hc_lstat(struct HostConf *hc, const char *path, struct stat *st)
216 {
217     struct HCHead *head;
218     hctransaction_t trans;
219 
220     if (hc == NULL || hc->host == NULL)
221 	return(lstat(path, st));
222 
223     trans = hcc_start_command(hc, HC_LSTAT);
224     hcc_leaf_string(trans, LC_PATH1, path);
225     if ((head = hcc_finish_command(trans)) == NULL)
226 	return(-1);
227     if (head->error)
228 	return(-1);
229     return(hc_decode_stat(st, head));
230 }
231 
232 static int
233 hc_decode_stat(struct stat *st, struct HCHead *head)
234 {
235     struct HCLeaf *item;
236 
237     bzero(st, sizeof(*st));
238     for (item = hcc_firstitem(head); item; item = hcc_nextitem(head, item)) {
239 	switch(item->leafid) {
240 	case LC_DEV:
241 		st->st_dev = HCC_INT32(item);
242 		break;
243 	case LC_INO:
244 		st->st_ino = HCC_INT64(item);
245 		break;
246 	case LC_MODE:
247 		st->st_mode = HCC_INT32(item);
248 		break;
249 	case LC_NLINK:
250 		st->st_nlink = HCC_INT32(item);
251 		break;
252 	case LC_UID:
253 		st->st_uid = HCC_INT32(item);
254 		break;
255 	case LC_GID:
256 		st->st_gid = HCC_INT32(item);
257 		break;
258 	case LC_RDEV:
259 		st->st_rdev = HCC_INT32(item);
260 		break;
261 	case LC_ATIME:
262 		st->st_atime = (time_t)HCC_INT64(item);
263 		break;
264 	case LC_MTIME:
265 		st->st_mtime = (time_t)HCC_INT64(item);
266 		break;
267 	case LC_CTIME:
268 		st->st_ctime = (time_t)HCC_INT64(item);
269 		break;
270 	case LC_FILESIZE:
271 		st->st_size = HCC_INT64(item);
272 		break;
273 	case LC_FILEBLKS:
274 		st->st_blocks = HCC_INT64(item);
275 		break;
276 	case LC_BLKSIZE:
277 		st->st_blksize = HCC_INT32(item);
278 		break;
279 #ifdef _ST_FSMID_PRESENT_
280 	case LC_FSMID:
281 		st->st_fsmid = HCC_INT64(item);
282 		break;
283 #endif
284 #ifdef _ST_FLAGS_PRESENT_
285 	case LC_FILEFLAGS:
286 		st->st_flags = (u_int32_t)HCC_INT64(item);
287 		break;
288 #endif
289 	}
290     }
291     return(0);
292 }
293 
294 static int
295 rc_stat(hctransaction_t trans, struct HCHead *head)
296 {
297     struct HCLeaf *item;
298     struct stat st;
299     const char *path = NULL;
300 
301     for (item = hcc_firstitem(head); item; item = hcc_nextitem(head, item)) {
302 	switch(item->leafid) {
303 	case LC_PATH1:
304 	    path = HCC_STRING(item);
305 	    break;
306 	}
307     }
308     if (path == NULL)
309 	return(-2);
310     if (stat(path, &st) < 0)
311 	return(-1);
312     return (rc_encode_stat(trans, &st));
313 }
314 
315 static int
316 rc_lstat(hctransaction_t trans, struct HCHead *head)
317 {
318     struct HCLeaf *item;
319     struct stat st;
320     const char *path = NULL;
321 
322     for (item = hcc_firstitem(head); item; item = hcc_nextitem(head, item)) {
323 	switch(item->leafid) {
324 	case LC_PATH1:
325 	    path = HCC_STRING(item);
326 	    break;
327 	}
328     }
329     if (path == NULL)
330 	return(-2);
331     if (lstat(path, &st) < 0)
332 	return(-1);
333     return (rc_encode_stat(trans, &st));
334 }
335 
336 static int
337 rc_encode_stat(hctransaction_t trans, struct stat *st)
338 {
339     hcc_leaf_int32(trans, LC_DEV, st->st_dev);
340     hcc_leaf_int64(trans, LC_INO, st->st_ino);
341     hcc_leaf_int32(trans, LC_MODE, st->st_mode);
342     hcc_leaf_int32(trans, LC_NLINK, st->st_nlink);
343     hcc_leaf_int32(trans, LC_UID, st->st_uid);
344     hcc_leaf_int32(trans, LC_GID, st->st_gid);
345     hcc_leaf_int32(trans, LC_RDEV, st->st_rdev);
346     hcc_leaf_int64(trans, LC_ATIME, st->st_atime);
347     hcc_leaf_int64(trans, LC_MTIME, st->st_mtime);
348     hcc_leaf_int64(trans, LC_CTIME, st->st_ctime);
349     hcc_leaf_int64(trans, LC_FILESIZE, st->st_size);
350     hcc_leaf_int64(trans, LC_FILEBLKS, st->st_blocks);
351     hcc_leaf_int32(trans, LC_BLKSIZE, st->st_blksize);
352 #ifdef _ST_FSMID_PRESENT_
353     hcc_leaf_int64(trans, LC_FSMID, st->st_fsmid);
354 #endif
355 #ifdef _ST_FLAGS_PRESENT_
356     hcc_leaf_int64(trans, LC_FILEFLAGS, st->st_flags);
357 #endif
358     return(0);
359 }
360 
361 /*
362  * OPENDIR
363  */
364 DIR *
365 hc_opendir(struct HostConf *hc, const char *path)
366 {
367     hctransaction_t trans;
368     struct HCHead *head;
369     struct HCLeaf *item;
370     struct dirent *den;
371     intptr_t desc = 0;
372 
373     if (hc == NULL || hc->host == NULL)
374 	return(opendir(path));
375 
376     trans = hcc_start_command(hc, HC_OPENDIR);
377     hcc_leaf_string(trans, LC_PATH1, path);
378     if ((head = hcc_finish_command(trans)) == NULL)
379 	return(NULL);
380     if (head->error)
381 	return(NULL);
382     for (item = hcc_firstitem(head); item; item = hcc_nextitem(head, item)) {
383 	switch(item->leafid) {
384 	case LC_DESCRIPTOR:
385 	    desc = HCC_INT32(item);
386 	    break;
387 	}
388     }
389     if (hcc_get_descriptor(hc, desc, HC_DESC_DIR)) {
390 	    fprintf(stderr, "hc_opendir: remote reused active descriptor %jd\n",
391 		(intmax_t)desc);
392 	return(NULL);
393     }
394     den = malloc(sizeof(*den));
395     bzero(den, sizeof(*den));
396     hcc_set_descriptor(hc, desc, den, HC_DESC_DIR);
397     return((void *)desc);
398 }
399 
400 static int
401 rc_opendir(hctransaction_t trans, struct HCHead *head)
402 {
403     struct HCLeaf *item;
404     const char *path = NULL;
405     DIR *dir;
406     int desc;
407 
408     for (item = hcc_firstitem(head); item; item = hcc_nextitem(head, item)) {
409 	switch(item->leafid) {
410 	case LC_PATH1:
411 	    path = HCC_STRING(item);
412 	    break;
413 	}
414     }
415     if (path == NULL)
416 	return(-2);
417     if ((dir = opendir(path)) == NULL) {
418 	head->error = errno;
419     } else {
420 	desc = hcc_alloc_descriptor(trans->hc, dir, HC_DESC_DIR);
421 	hcc_leaf_int32(trans, LC_DESCRIPTOR, desc);
422     }
423     return(0);
424 }
425 
426 /*
427  * READDIR
428  */
429 struct dirent *
430 hc_readdir(struct HostConf *hc, DIR *dir)
431 {
432     hctransaction_t trans;
433     struct HCHead *head;
434     struct HCLeaf *item;
435     struct dirent *den;
436 
437     if (hc == NULL || hc->host == NULL)
438 	return(readdir(dir));
439 
440     trans = hcc_start_command(hc, HC_READDIR);
441     hcc_leaf_int32(trans, LC_DESCRIPTOR, (intptr_t)dir);
442     if ((head = hcc_finish_command(trans)) == NULL)
443 	return(NULL);
444     if (head->error)
445 	return(NULL);	/* XXX errno */
446     den = hcc_get_descriptor(hc, (intptr_t)dir, HC_DESC_DIR);
447     if (den == NULL)
448 	return(NULL);	/* XXX errno */
449     if (den->d_name)
450 	den->d_name[0] = 0;
451     for (item = hcc_firstitem(head); item; item = hcc_nextitem(head, item)) {
452 	switch(item->leafid) {
453 	case LC_PATH1:
454 	    snprintf(den->d_name, sizeof(den->d_name), "%s", HCC_STRING(item));
455 	    break;
456 	case LC_INO:
457 	    den->d_fileno = HCC_INT64(item);
458 	    break;
459 	case LC_TYPE:
460 	    den->d_type = HCC_INT32(item);
461 	    break;
462 	}
463     }
464     if (den->d_name[0]) {
465 #ifdef _DIRENT_HAVE_D_NAMLEN
466 	den->d_namlen = strlen(den->d_name);
467 #endif
468 	return(den);
469     }
470     return(NULL);	/* XXX errno */
471 }
472 
473 static int
474 rc_readdir(hctransaction_t trans, struct HCHead *head)
475 {
476     struct HCLeaf *item;
477     struct dirent *den;
478     DIR *dir = NULL;
479 
480     for (item = hcc_firstitem(head); item; item = hcc_nextitem(head, item)) {
481 	switch(item->leafid) {
482 	case LC_DESCRIPTOR:
483 	    dir = hcc_get_descriptor(trans->hc, HCC_INT32(item), HC_DESC_DIR);
484 	    break;
485 	}
486     }
487     if (dir == NULL)
488 	return(-2);
489     if ((den = readdir(dir)) != NULL) {
490 	hcc_leaf_string(trans, LC_PATH1, den->d_name);
491 	hcc_leaf_int64(trans, LC_INO, den->d_fileno);
492 	hcc_leaf_int32(trans, LC_TYPE, den->d_type);
493     }
494     return(0);
495 }
496 
497 /*
498  * CLOSEDIR
499  *
500  * XXX cpdup needs to check error code to avoid truncated dirs?
501  */
502 int
503 hc_closedir(struct HostConf *hc, DIR *dir)
504 {
505     hctransaction_t trans;
506     struct HCHead *head;
507     struct dirent *den;
508 
509     if (hc == NULL || hc->host == NULL)
510 	return(closedir(dir));
511     den = hcc_get_descriptor(hc, (intptr_t)dir, HC_DESC_DIR);
512     if (den) {
513 	free(den);
514 	hcc_set_descriptor(hc, (intptr_t)dir, NULL, HC_DESC_DIR);
515 
516 	trans = hcc_start_command(hc, HC_CLOSEDIR);
517 	hcc_leaf_int32(trans, LC_DESCRIPTOR, (intptr_t)dir);
518 	if ((head = hcc_finish_command(trans)) == NULL)
519 	    return(-1);
520 	if (head->error)
521 	    return(-1);		/* XXX errno */
522 	return(0);
523     } else {
524 	/* errno */
525 	return(-1);
526     }
527 }
528 
529 static int
530 rc_closedir(hctransaction_t trans, struct HCHead *head)
531 {
532     struct HCLeaf *item;
533     DIR *dir = NULL;
534 
535     for (item = hcc_firstitem(head); item; item = hcc_nextitem(head, item)) {
536 	switch(item->leafid) {
537 	case LC_DESCRIPTOR:
538 	    dir = hcc_get_descriptor(trans->hc, HCC_INT32(item), HC_DESC_DIR);
539 	    if (dir != NULL)
540 		    hcc_set_descriptor(trans->hc, HCC_INT32(item), NULL, HC_DESC_DIR);
541 	    break;
542 	}
543     }
544     if (dir == NULL)
545 	return(-2);
546     return(closedir(dir));
547 }
548 
549 /*
550  * OPEN
551  */
552 int
553 hc_open(struct HostConf *hc, const char *path, int flags, mode_t mode)
554 {
555     hctransaction_t trans;
556     struct HCHead *head;
557     struct HCLeaf *item;
558     int *fdp;
559     int desc = 0;
560     int nflags;
561 
562     if (hc == NULL || hc->host == NULL) {
563 #ifdef O_LARGEFILE
564 	flags |= O_LARGEFILE;
565 #endif
566 	return(open(path, flags, mode));
567     }
568 
569     nflags = flags & XO_NATIVEMASK;
570     if (flags & O_CREAT)
571 	nflags |= XO_CREAT;
572     if (flags & O_EXCL)
573 	nflags |= XO_EXCL;
574     if (flags & O_TRUNC)
575 	nflags |= XO_TRUNC;
576 
577     trans = hcc_start_command(hc, HC_OPEN);
578     hcc_leaf_string(trans, LC_PATH1, path);
579     hcc_leaf_int32(trans, LC_OFLAGS, nflags);
580     hcc_leaf_int32(trans, LC_MODE, mode);
581 
582     if ((head = hcc_finish_command(trans)) == NULL)
583 	return(-1);
584     if (head->error)
585 	return(-1);
586     for (item = hcc_firstitem(head); item; item = hcc_nextitem(head, item)) {
587 	switch(item->leafid) {
588 	case LC_DESCRIPTOR:
589 	    desc = HCC_INT32(item);
590 	    break;
591 	}
592     }
593     if (hcc_get_descriptor(hc, desc, HC_DESC_FD)) {
594 	fprintf(stderr, "hc_opendir: remote reused active descriptor %d\n",
595 		desc);
596 	return(-1);
597     }
598     fdp = malloc(sizeof(int));
599     *fdp = desc;	/* really just a dummy */
600     hcc_set_descriptor(hc, desc, fdp, HC_DESC_FD);
601     return(desc);
602 }
603 
604 static int
605 rc_open(hctransaction_t trans, struct HCHead *head)
606 {
607     struct HCLeaf *item;
608     const char *path = NULL;
609     int nflags = 0;
610     int flags;
611     mode_t mode = 0666;
612     int desc;
613     int *fdp;
614     int fd;
615 
616     for (item = hcc_firstitem(head); item; item = hcc_nextitem(head, item)) {
617 	switch(item->leafid) {
618 	case LC_PATH1:
619 	    path = HCC_STRING(item);
620 	    break;
621 	case LC_OFLAGS:
622 	    nflags = HCC_INT32(item);
623 	    break;
624 	case LC_MODE:
625 	    mode = HCC_INT32(item);
626 	    break;
627 	}
628     }
629     if (path == NULL)
630 	return(-2);
631 
632     flags = nflags & XO_NATIVEMASK;
633     if (nflags & XO_CREAT)
634 	flags |= O_CREAT;
635     if (nflags & XO_EXCL)
636 	flags |= O_EXCL;
637     if (nflags & XO_TRUNC)
638 	flags |= O_TRUNC;
639 
640 #ifdef O_LARGEFILE
641     flags |= O_LARGEFILE;
642 #endif
643     if ((fd = open(path, flags, mode)) < 0) {
644 	head->error = errno;
645 	return(0);
646     }
647     fdp = malloc(sizeof(int));
648     *fdp = fd;
649     desc = hcc_alloc_descriptor(trans->hc, fdp, HC_DESC_FD);
650     hcc_leaf_int32(trans, LC_DESCRIPTOR, desc);
651     return(0);
652 }
653 
654 /*
655  * CLOSE
656  */
657 int
658 hc_close(struct HostConf *hc, int fd)
659 {
660     hctransaction_t trans;
661     struct HCHead *head;
662     int *fdp;
663 
664     if (hc == NULL || hc->host == NULL)
665 	return(close(fd));
666 
667     fdp = hcc_get_descriptor(hc, fd, HC_DESC_FD);
668     if (fdp) {
669 	free(fdp);
670 	hcc_set_descriptor(hc, fd, NULL, HC_DESC_FD);
671 
672 	trans = hcc_start_command(hc, HC_CLOSE);
673 	hcc_leaf_int32(trans, LC_DESCRIPTOR, fd);
674 	if ((head = hcc_finish_command(trans)) == NULL)
675 	    return(-1);
676 	if (head->error)
677 	    return(-1);
678 	return(0);
679     } else {
680 	return(-1);
681     }
682 }
683 
684 static int
685 rc_close(hctransaction_t trans, struct HCHead *head)
686 {
687     struct HCLeaf *item;
688     int *fdp = NULL;
689     int fd;
690     int desc = -1;
691 
692     for (item = hcc_firstitem(head); item; item = hcc_nextitem(head, item)) {
693 	switch(item->leafid) {
694 	case LC_DESCRIPTOR:
695 	    desc = HCC_INT32(item);
696 	    break;
697 	}
698     }
699     if (desc < 0)
700 	return(-2);
701     if ((fdp = hcc_get_descriptor(trans->hc, desc, HC_DESC_FD)) == NULL)
702 	return(-2);
703     fd = *fdp;
704     free(fdp);
705     hcc_set_descriptor(trans->hc, desc, NULL, HC_DESC_FD);
706     return(close(fd));
707 }
708 
709 static int
710 getiolimit(void)
711 {
712     return(32768);
713 }
714 
715 /*
716  * READ
717  */
718 ssize_t
719 hc_read(struct HostConf *hc, int fd, void *buf, size_t bytes)
720 {
721     hctransaction_t trans;
722     struct HCHead *head;
723     struct HCLeaf *item;
724     int *fdp;
725     int r;
726 
727     if (hc == NULL || hc->host == NULL)
728 	return(read(fd, buf, bytes));
729 
730     fdp = hcc_get_descriptor(hc, fd, HC_DESC_FD);
731     if (fdp) {
732 	r = 0;
733 	while (bytes) {
734 	    size_t limit = getiolimit();
735 	    int n = (bytes > limit) ? limit : bytes;
736 	    int x = 0;
737 
738 	    trans = hcc_start_command(hc, HC_READ);
739 	    hcc_leaf_int32(trans, LC_DESCRIPTOR, fd);
740 	    hcc_leaf_int32(trans, LC_BYTES, n);
741 	    if ((head = hcc_finish_command(trans)) == NULL)
742 		return(-1);
743 	    if (head->error)
744 		return(-1);
745 	    for (item = hcc_firstitem(head); item; item = hcc_nextitem(head, item)) {
746 		switch(item->leafid) {
747 		case LC_DATA:
748 		    x = item->bytes - sizeof(*item);
749 		    if (x > (int)bytes)
750 			x = (int)bytes;
751 		    bcopy(HCC_BINARYDATA(item), buf, x);
752 		    buf = (char *)buf + x;
753 		    bytes -= (size_t)x;
754 		    r += x;
755 		    break;
756 		}
757 	    }
758 	    if (x < n)
759 		break;
760 	}
761 	return(r);
762     } else {
763 	return(-1);
764     }
765 }
766 
767 static int
768 rc_read(hctransaction_t trans, struct HCHead *head)
769 {
770     struct HCLeaf *item;
771     int *fdp = NULL;
772     char buf[32768];
773     int bytes = -1;
774     int n;
775 
776     for (item = hcc_firstitem(head); item; item = hcc_nextitem(head, item)) {
777 	switch(item->leafid) {
778 	case LC_DESCRIPTOR:
779 	    fdp = hcc_get_descriptor(trans->hc, HCC_INT32(item), HC_DESC_FD);
780 	    break;
781 	case LC_BYTES:
782 	    bytes = HCC_INT32(item);
783 	    break;
784 	}
785     }
786     if (fdp == NULL)
787 	return(-2);
788     if (bytes < 0 || bytes > 32768)
789 	return(-2);
790     n = read(*fdp, buf, bytes);
791     if (n < 0) {
792 	head->error = errno;
793 	return(0);
794     }
795     hcc_leaf_data(trans, LC_DATA, buf, n);
796     return(0);
797 }
798 
799 /*
800  * WRITE
801  */
802 ssize_t
803 hc_write(struct HostConf *hc, int fd, const void *buf, size_t bytes)
804 {
805     hctransaction_t trans;
806     struct HCHead *head;
807     struct HCLeaf *item;
808     int *fdp;
809     int r;
810 
811     if (hc == NULL || hc->host == NULL)
812 	return(write(fd, buf, bytes));
813 
814     fdp = hcc_get_descriptor(hc, fd, HC_DESC_FD);
815     if (fdp) {
816 	r = 0;
817 	while (bytes) {
818 	    size_t limit = getiolimit();
819 	    int n = (bytes > limit) ? limit : bytes;
820 	    int x = 0;
821 
822 	    trans = hcc_start_command(hc, HC_WRITE);
823 	    hcc_leaf_int32(trans, LC_DESCRIPTOR, fd);
824 	    hcc_leaf_data(trans, LC_DATA, buf, n);
825 	    if ((head = hcc_finish_command(trans)) == NULL)
826 		return(-1);
827 	    if (head->error)
828 		return(-1);
829 	    for (item = hcc_firstitem(head); item; item = hcc_nextitem(head, item)) {
830 		switch(item->leafid) {
831 		case LC_BYTES:
832 		    x = HCC_INT32(item);
833 		    break;
834 		}
835 	    }
836 	    if (x < 0 || x > n)
837 		return(-1);
838 	    r += x;
839 	    buf = (const char *)buf + x;
840 	    bytes -= x;
841 	    if (x < n)
842 		break;
843 	}
844 	return(r);
845     } else {
846 	return(-1);
847     }
848 }
849 
850 static int
851 rc_write(hctransaction_t trans, struct HCHead *head)
852 {
853     struct HCLeaf *item;
854     int *fdp = NULL;
855     void *buf = NULL;
856     int n = -1;
857 
858     for (item = hcc_firstitem(head); item; item = hcc_nextitem(head, item)) {
859 	switch(item->leafid) {
860 	case LC_DESCRIPTOR:
861 	    fdp = hcc_get_descriptor(trans->hc, HCC_INT32(item), HC_DESC_FD);
862 	    break;
863 	case LC_DATA:
864 	    buf = HCC_BINARYDATA(item);
865 	    n = item->bytes - sizeof(*item);
866 	    break;
867 	}
868     }
869     if (fdp == NULL)
870 	return(-2);
871     if (n < 0 || n > 32768)
872 	return(-2);
873     n = write(*fdp, buf, n);
874     if (n < 0) {
875 	head->error = errno;
876     } else {
877 	hcc_leaf_int32(trans, LC_BYTES, n);
878     }
879     return(0);
880 }
881 
882 /*
883  * REMOVE
884  *
885  * NOTE: This function returns -errno if an error occured.
886  */
887 int
888 hc_remove(struct HostConf *hc, const char *path)
889 {
890     hctransaction_t trans;
891     struct HCHead *head;
892     int res;
893 
894     if (hc == NULL || hc->host == NULL) {
895 	res = remove(path);
896 	if (res < 0)
897 		res = -errno;
898 	return(res);
899     }
900 
901     trans = hcc_start_command(hc, HC_REMOVE);
902     hcc_leaf_string(trans, LC_PATH1, path);
903     if ((head = hcc_finish_command(trans)) == NULL)
904 	return(-EIO);
905     if (head->error)
906 	return(-(int)head->error);
907     return(0);
908 }
909 
910 static int
911 rc_remove(hctransaction_t trans __unused, struct HCHead *head)
912 {
913     struct HCLeaf *item;
914     const char *path = NULL;
915 
916     for (item = hcc_firstitem(head); item; item = hcc_nextitem(head, item)) {
917 	switch(item->leafid) {
918 	case LC_PATH1:
919 	    path = HCC_STRING(item);
920 	    break;
921 	}
922     }
923     if (path == NULL)
924 	return(-2);
925     return(remove(path));
926 }
927 
928 /*
929  * MKDIR
930  */
931 int
932 hc_mkdir(struct HostConf *hc __unused, const char *path, mode_t mode)
933 {
934     hctransaction_t trans;
935     struct HCHead *head;
936 
937     if (hc == NULL || hc->host == NULL)
938 	return(mkdir(path, mode));
939 
940     trans = hcc_start_command(hc, HC_MKDIR);
941     hcc_leaf_string(trans, LC_PATH1, path);
942     hcc_leaf_int32(trans, LC_MODE, mode);
943     if ((head = hcc_finish_command(trans)) == NULL)
944 	return(-1);
945     if (head->error)
946 	return(-1);
947     return(0);
948 }
949 
950 static int
951 rc_mkdir(hctransaction_t trans __unused, struct HCHead *head)
952 {
953     struct HCLeaf *item;
954     const char *path = NULL;
955     mode_t mode = 0777;
956 
957     for (item = hcc_firstitem(head); item; item = hcc_nextitem(head, item)) {
958 	switch(item->leafid) {
959 	case LC_PATH1:
960 	    path = HCC_STRING(item);
961 	    break;
962 	case LC_MODE:
963 	    mode = HCC_INT32(item);
964 	    break;
965 	}
966     }
967     if (path == NULL)
968 	return(-1);
969     return(mkdir(path, mode));
970 }
971 
972 /*
973  * RMDIR
974  */
975 int
976 hc_rmdir(struct HostConf *hc, const char *path)
977 {
978     hctransaction_t trans;
979     struct HCHead *head;
980 
981     if (hc == NULL || hc->host == NULL)
982 	return(rmdir(path));
983 
984     trans = hcc_start_command(hc, HC_RMDIR);
985     hcc_leaf_string(trans, LC_PATH1, path);
986     if ((head = hcc_finish_command(trans)) == NULL)
987 	return(-1);
988     if (head->error)
989 	return(-1);
990     return(0);
991 }
992 
993 static int
994 rc_rmdir(hctransaction_t trans __unused, struct HCHead *head)
995 {
996     struct HCLeaf *item;
997     const char *path = NULL;
998 
999     for (item = hcc_firstitem(head); item; item = hcc_nextitem(head, item)) {
1000 	switch(item->leafid) {
1001 	case LC_PATH1:
1002 	    path = HCC_STRING(item);
1003 	    break;
1004 	}
1005     }
1006     if (path == NULL)
1007 	return(-1);
1008     return(rmdir(path));
1009 }
1010 
1011 /*
1012  * CHOWN
1013  *
1014  * Almost silently ignore chowns that fail if we are not root.
1015  */
1016 int
1017 hc_chown(struct HostConf *hc, const char *path, uid_t owner, gid_t group)
1018 {
1019     hctransaction_t trans;
1020     struct HCHead *head;
1021     int rc;
1022 
1023     if (!DstRootPrivs)
1024 	owner = -1;
1025 
1026     if (hc == NULL || hc->host == NULL) {
1027 	rc = chown(path, owner, group);
1028 	if (rc < 0)
1029 	    rc = silentwarning(&chown_warning, "file ownership may differ\n");
1030 	return(rc);
1031     }
1032 
1033     trans = hcc_start_command(hc, HC_CHOWN);
1034     hcc_leaf_string(trans, LC_PATH1, path);
1035     hcc_leaf_int32(trans, LC_UID, owner);
1036     hcc_leaf_int32(trans, LC_GID, group);
1037     if ((head = hcc_finish_command(trans)) == NULL)
1038 	return(-1);
1039     if (head->error)
1040 	return(-1);
1041     return(0);
1042 }
1043 
1044 static int
1045 rc_chown(hctransaction_t trans __unused, struct HCHead *head)
1046 {
1047     struct HCLeaf *item;
1048     const char *path = NULL;
1049     uid_t uid = (uid_t)-1;
1050     gid_t gid = (gid_t)-1;
1051     int rc;
1052 
1053     for (item = hcc_firstitem(head); item; item = hcc_nextitem(head, item)) {
1054 	switch(item->leafid) {
1055 	case LC_PATH1:
1056 	    path = HCC_STRING(item);
1057 	    break;
1058 	case LC_UID:
1059 	    uid = HCC_INT32(item);
1060 	    break;
1061 	case LC_GID:
1062 	    gid = HCC_INT32(item);
1063 	    break;
1064 	}
1065     }
1066     if (path == NULL)
1067 	return(-1);
1068     rc = chown(path, uid, gid);
1069     if (rc < 0)
1070 	rc = silentwarning(&chown_warning, "file ownership may differ\n");
1071     return(rc);
1072 }
1073 
1074 /*
1075  * LCHOWN
1076  */
1077 int
1078 hc_lchown(struct HostConf *hc, const char *path, uid_t owner, gid_t group)
1079 {
1080     hctransaction_t trans;
1081     struct HCHead *head;
1082     int rc;
1083 
1084     if (!DstRootPrivs)
1085 	owner = -1;
1086 
1087     if (hc == NULL || hc->host == NULL) {
1088 	rc = lchown(path, owner, group);
1089 	if (rc < 0)
1090 	    rc = silentwarning(&chown_warning, "file ownership may differ\n");
1091 	return(rc);
1092     }
1093 
1094     trans = hcc_start_command(hc, HC_LCHOWN);
1095     hcc_leaf_string(trans, LC_PATH1, path);
1096     hcc_leaf_int32(trans, LC_UID, owner);
1097     hcc_leaf_int32(trans, LC_GID, group);
1098     if ((head = hcc_finish_command(trans)) == NULL)
1099 	return(-1);
1100     if (head->error)
1101 	return(-1);
1102     return(0);
1103 }
1104 
1105 static int
1106 rc_lchown(hctransaction_t trans __unused, struct HCHead *head)
1107 {
1108     struct HCLeaf *item;
1109     const char *path = NULL;
1110     uid_t uid = (uid_t)-1;
1111     gid_t gid = (gid_t)-1;
1112     int rc;
1113 
1114     for (item = hcc_firstitem(head); item; item = hcc_nextitem(head, item)) {
1115 	switch(item->leafid) {
1116 	case LC_PATH1:
1117 	    path = HCC_STRING(item);
1118 	    break;
1119 	case LC_UID:
1120 	    uid = HCC_INT32(item);
1121 	    break;
1122 	case LC_GID:
1123 	    gid = HCC_INT32(item);
1124 	    break;
1125 	}
1126     }
1127     if (path == NULL)
1128 	return(-1);
1129     rc = lchown(path, uid, gid);
1130     if (rc < 0)
1131 	rc = silentwarning(&chown_warning, "file ownership may differ\n");
1132     return(rc);
1133 }
1134 
1135 /*
1136  * CHMOD
1137  */
1138 int
1139 hc_chmod(struct HostConf *hc, const char *path, mode_t mode)
1140 {
1141     hctransaction_t trans;
1142     struct HCHead *head;
1143 
1144     if (hc == NULL || hc->host == NULL)
1145 	return(chmod(path, mode));
1146 
1147     trans = hcc_start_command(hc, HC_CHMOD);
1148     hcc_leaf_string(trans, LC_PATH1, path);
1149     hcc_leaf_int32(trans, LC_MODE, mode);
1150     if ((head = hcc_finish_command(trans)) == NULL)
1151 	return(-1);
1152     if (head->error)
1153 	return(-1);
1154     return(0);
1155 }
1156 
1157 static int
1158 rc_chmod(hctransaction_t trans __unused, struct HCHead *head)
1159 {
1160     struct HCLeaf *item;
1161     const char *path = NULL;
1162     mode_t mode = 0666;
1163 
1164     for (item = hcc_firstitem(head); item; item = hcc_nextitem(head, item)) {
1165 	switch(item->leafid) {
1166 	case LC_PATH1:
1167 	    path = HCC_STRING(item);
1168 	    break;
1169 	case LC_MODE:
1170 	    mode = HCC_INT32(item);
1171 	    break;
1172 	}
1173     }
1174     if (path == NULL)
1175 	return(-1);
1176     return(chmod(path, mode));
1177 }
1178 
1179 /*
1180  * MKNOD
1181  */
1182 int
1183 hc_mknod(struct HostConf *hc, const char *path, mode_t mode, dev_t rdev)
1184 {
1185     hctransaction_t trans;
1186     struct HCHead *head;
1187 
1188     if (!DstRootPrivs) {
1189 	/* mknod() requires root privs, so don't bother. */
1190 	errno = EPERM;
1191 	return (-1);
1192     }
1193 
1194     if (hc == NULL || hc->host == NULL)
1195 	return(mknod(path, mode, rdev));
1196 
1197     trans = hcc_start_command(hc, HC_MKNOD);
1198     hcc_leaf_string(trans, LC_PATH1, path);
1199     hcc_leaf_int32(trans, LC_MODE, mode);
1200     hcc_leaf_int32(trans, LC_RDEV, rdev);
1201     if ((head = hcc_finish_command(trans)) == NULL)
1202 	return(-1);
1203     if (head->error)
1204 	return(-1);
1205     return(0);
1206 }
1207 
1208 static int
1209 rc_mknod(hctransaction_t trans __unused, struct HCHead *head)
1210 {
1211     struct HCLeaf *item;
1212     const char *path = NULL;
1213     mode_t mode = 0666;
1214     dev_t rdev = 0;
1215 
1216     for (item = hcc_firstitem(head); item; item = hcc_nextitem(head, item)) {
1217 	switch(item->leafid) {
1218 	case LC_PATH1:
1219 	    path = HCC_STRING(item);
1220 	    break;
1221 	case LC_MODE:
1222 	    mode = HCC_INT32(item);
1223 	    break;
1224 	case LC_RDEV:
1225 	    rdev = HCC_INT32(item);
1226 	    break;
1227 	}
1228     }
1229     if (path == NULL)
1230 	return(-1);
1231     return(mknod(path, mode, rdev));
1232 }
1233 
1234 /*
1235  * LINK
1236  */
1237 int
1238 hc_link(struct HostConf *hc, const char *name1, const char *name2)
1239 {
1240     hctransaction_t trans;
1241     struct HCHead *head;
1242 
1243     if (hc == NULL || hc->host == NULL)
1244 	return(link(name1, name2));
1245 
1246     trans = hcc_start_command(hc, HC_LINK);
1247     hcc_leaf_string(trans, LC_PATH1, name1);
1248     hcc_leaf_string(trans, LC_PATH2, name2);
1249     if ((head = hcc_finish_command(trans)) == NULL)
1250 	return(-1);
1251     if (head->error)
1252 	return(-1);
1253     return(0);
1254 }
1255 
1256 static int
1257 rc_link(hctransaction_t trans __unused, struct HCHead *head)
1258 {
1259     struct HCLeaf *item;
1260     const char *name1 = NULL;
1261     const char *name2 = NULL;
1262 
1263     for (item = hcc_firstitem(head); item; item = hcc_nextitem(head, item)) {
1264 	switch(item->leafid) {
1265 	case LC_PATH1:
1266 	    name1 = HCC_STRING(item);
1267 	    break;
1268 	case LC_PATH2:
1269 	    name2 = HCC_STRING(item);
1270 	    break;
1271 	}
1272     }
1273     if (name1 == NULL || name2 == NULL)
1274 	return(-2);
1275     return(link(name1, name2));
1276 }
1277 
1278 #ifdef _ST_FLAGS_PRESENT_
1279 /*
1280  * CHFLAGS
1281  */
1282 int
1283 hc_chflags(struct HostConf *hc, const char *path, u_long flags)
1284 {
1285     hctransaction_t trans;
1286     struct HCHead *head;
1287     int rc;
1288 
1289     if (!DstRootPrivs)
1290 	flags &= UF_SETTABLE;
1291 
1292     if (hc == NULL || hc->host == NULL) {
1293 	if ((rc = chflags(path, flags)) < 0)
1294 	    rc = silentwarning(&chflags_warning, "file flags may differ\n");
1295 	return (rc);
1296     }
1297 
1298     trans = hcc_start_command(hc, HC_CHFLAGS);
1299     hcc_leaf_string(trans, LC_PATH1, path);
1300     hcc_leaf_int64(trans, LC_FILEFLAGS, flags);
1301     if ((head = hcc_finish_command(trans)) == NULL)
1302 	return(-1);
1303     if (head->error)
1304 	return(-1);
1305     return(0);
1306 }
1307 
1308 static int
1309 rc_chflags(hctransaction_t trans __unused, struct HCHead *head)
1310 {
1311     struct HCLeaf *item;
1312     const char *path = NULL;
1313     u_long flags = 0;
1314     int rc;
1315 
1316     for (item = hcc_firstitem(head); item; item = hcc_nextitem(head, item)) {
1317 	switch(item->leafid) {
1318 	case LC_PATH1:
1319 	    path = HCC_STRING(item);
1320 	    break;
1321 	case LC_FILEFLAGS:
1322 	    flags = (u_long)HCC_INT64(item);
1323 	    break;
1324 	}
1325     }
1326     if (path == NULL)
1327 	return(-2);
1328     if ((rc = chflags(path, flags)) < 0)
1329 	rc = silentwarning(&chflags_warning, "file flags may differ\n");
1330     return(rc);
1331 }
1332 
1333 #endif
1334 
1335 /*
1336  * READLINK
1337  */
1338 int
1339 hc_readlink(struct HostConf *hc, const char *path, char *buf, int bufsiz)
1340 {
1341     hctransaction_t trans;
1342     struct HCHead *head;
1343     struct HCLeaf *item;
1344     int r;
1345 
1346     if (hc == NULL || hc->host == NULL)
1347 	return(readlink(path, buf, bufsiz));
1348 
1349     trans = hcc_start_command(hc, HC_READLINK);
1350     hcc_leaf_string(trans, LC_PATH1, path);
1351     if ((head = hcc_finish_command(trans)) == NULL)
1352 	return(-1);
1353     if (head->error)
1354 	return(-1);
1355 
1356     r = 0;
1357     for (item = hcc_firstitem(head); item; item = hcc_nextitem(head, item)) {
1358 	switch(item->leafid) {
1359 	case LC_DATA:
1360 	    r = item->bytes - sizeof(*item);
1361 	    if (r < 0)
1362 		r = 0;
1363 	    if (r > bufsiz)
1364 		r = bufsiz;
1365 	    bcopy(HCC_BINARYDATA(item), buf, r);
1366 	    break;
1367 	}
1368     }
1369     return(r);
1370 }
1371 
1372 static int
1373 rc_readlink(hctransaction_t trans, struct HCHead *head)
1374 {
1375     struct HCLeaf *item;
1376     const char *path = NULL;
1377     char buf[1024];
1378     int r;
1379 
1380     for (item = hcc_firstitem(head); item; item = hcc_nextitem(head, item)) {
1381 	switch(item->leafid) {
1382 	case LC_PATH1:
1383 	    path = HCC_STRING(item);
1384 	    break;
1385 	}
1386     }
1387     if (path == NULL)
1388 	return(-2);
1389     r = readlink(path, buf, sizeof(buf));
1390     if (r < 0)
1391 	return(-1);
1392     hcc_leaf_data(trans, LC_DATA, buf, r);
1393     return(0);
1394 }
1395 
1396 /*
1397  * UMASK
1398  */
1399 mode_t
1400 hc_umask(struct HostConf *hc, mode_t numask)
1401 {
1402     hctransaction_t trans;
1403     struct HCHead *head;
1404     struct HCLeaf *item;
1405 
1406     if (hc == NULL || hc->host == NULL)
1407 	return(umask(numask));
1408 
1409     trans = hcc_start_command(hc, HC_UMASK);
1410     hcc_leaf_int32(trans, LC_MODE, numask);
1411     if ((head = hcc_finish_command(trans)) == NULL)
1412 	return((mode_t)-1);
1413     if (head->error)
1414 	return((mode_t)-1);
1415 
1416     numask = ~0666;
1417     for (item = hcc_firstitem(head); item; item = hcc_nextitem(head, item)) {
1418 	switch(item->leafid) {
1419 	case LC_MODE:
1420 	    numask = HCC_INT32(item);
1421 	    break;
1422 	}
1423     }
1424     return(numask);
1425 }
1426 
1427 static int
1428 rc_umask(hctransaction_t trans, struct HCHead *head)
1429 {
1430     struct HCLeaf *item;
1431     mode_t numask = ~0666;
1432 
1433     for (item = hcc_firstitem(head); item; item = hcc_nextitem(head, item)) {
1434 	switch(item->leafid) {
1435 	case LC_MODE:
1436 	    numask = HCC_INT32(item);
1437 	    break;
1438 	}
1439     }
1440     numask = umask(numask);
1441     hcc_leaf_int32(trans, LC_MODE, numask);
1442     return(0);
1443 }
1444 
1445 /*
1446  * SYMLINK
1447  */
1448 int
1449 hc_symlink(struct HostConf *hc, const char *name1, const char *name2)
1450 {
1451     hctransaction_t trans;
1452     struct HCHead *head;
1453 
1454     if (hc == NULL || hc->host == NULL)
1455 	return(symlink(name1, name2));
1456 
1457     trans = hcc_start_command(hc, HC_SYMLINK);
1458     hcc_leaf_string(trans, LC_PATH1, name1);
1459     hcc_leaf_string(trans, LC_PATH2, name2);
1460     if ((head = hcc_finish_command(trans)) == NULL)
1461 	return(-1);
1462     if (head->error)
1463 	return(-1);
1464     return(0);
1465 }
1466 
1467 static int
1468 rc_symlink(hctransaction_t trans __unused, struct HCHead *head)
1469 {
1470     struct HCLeaf *item;
1471     const char *name1 = NULL;
1472     const char *name2 = NULL;
1473 
1474     for (item = hcc_firstitem(head); item; item = hcc_nextitem(head, item)) {
1475 	switch(item->leafid) {
1476 	case LC_PATH1:
1477 	    name1 = HCC_STRING(item);
1478 	    break;
1479 	case LC_PATH2:
1480 	    name2 = HCC_STRING(item);
1481 	    break;
1482 	}
1483     }
1484     if (name1 == NULL || name2 == NULL)
1485 	return(-2);
1486     return(symlink(name1, name2));
1487 }
1488 
1489 /*
1490  * RENAME
1491  */
1492 int
1493 hc_rename(struct HostConf *hc, const char *name1, const char *name2)
1494 {
1495     hctransaction_t trans;
1496     struct HCHead *head;
1497 
1498     if (hc == NULL || hc->host == NULL)
1499 	return(rename(name1, name2));
1500 
1501     trans = hcc_start_command(hc, HC_RENAME);
1502     hcc_leaf_string(trans, LC_PATH1, name1);
1503     hcc_leaf_string(trans, LC_PATH2, name2);
1504     if ((head = hcc_finish_command(trans)) == NULL)
1505 	return(-1);
1506     if (head->error)
1507 	return(-1);
1508     return(0);
1509 }
1510 
1511 static int
1512 rc_rename(hctransaction_t trans __unused, struct HCHead *head)
1513 {
1514     struct HCLeaf *item;
1515     const char *name1 = NULL;
1516     const char *name2 = NULL;
1517 
1518     for (item = hcc_firstitem(head); item; item = hcc_nextitem(head, item)) {
1519 	switch(item->leafid) {
1520 	case LC_PATH1:
1521 	    name1 = HCC_STRING(item);
1522 	    break;
1523 	case LC_PATH2:
1524 	    name2 = HCC_STRING(item);
1525 	    break;
1526 	}
1527     }
1528     if (name1 == NULL || name2 == NULL)
1529 	return(-2);
1530     return(rename(name1, name2));
1531 }
1532 
1533 /*
1534  * UTIMES
1535  */
1536 int
1537 hc_utimes(struct HostConf *hc, const char *path, const struct timeval *times)
1538 {
1539     hctransaction_t trans;
1540     struct HCHead *head;
1541 
1542     if (hc == NULL || hc->host == NULL)
1543 	return(utimes(path, times));
1544 
1545     trans = hcc_start_command(hc, HC_UTIMES);
1546     hcc_leaf_string(trans, LC_PATH1, path);
1547     hcc_leaf_int64(trans, LC_ATIME, times[0].tv_sec);
1548     hcc_leaf_int64(trans, LC_MTIME, times[1].tv_sec);
1549     if ((head = hcc_finish_command(trans)) == NULL)
1550 	return(-1);
1551     if (head->error)
1552 	return(-1);
1553     return(0);
1554 }
1555 
1556 static int
1557 rc_utimes(hctransaction_t trans __unused, struct HCHead *head)
1558 {
1559     struct HCLeaf *item;
1560     struct timeval times[2];
1561     const char *path;
1562 
1563     bzero(times, sizeof(times));
1564     path = NULL;
1565 
1566     for (item = hcc_firstitem(head); item; item = hcc_nextitem(head, item)) {
1567 	switch(item->leafid) {
1568 	case LC_PATH1:
1569 	    path = HCC_STRING(item);
1570 	    break;
1571 	case LC_ATIME:
1572 	    times[0].tv_sec = HCC_INT64(item);
1573 	    break;
1574 	case LC_MTIME:
1575 	    times[1].tv_sec = HCC_INT64(item);
1576 	    break;
1577 	}
1578     }
1579     if (path == NULL)
1580 	return(-2);
1581     return(utimes(path, times));
1582 }
1583 
1584 uid_t
1585 hc_geteuid(struct HostConf *hc)
1586 {
1587     hctransaction_t trans;
1588     struct HCHead *head;
1589     struct HCLeaf *item;
1590 
1591     if (hc == NULL || hc->host == NULL)
1592 	return (geteuid());
1593 
1594     if (hc->version < 3) {
1595 	fprintf(stderr, "WARNING: Remote client uses old protocol version\n");
1596 	/* Return 0 on error, so the caller assumes root privileges. */
1597 	return (0);
1598     }
1599 
1600     trans = hcc_start_command(hc, HC_GETEUID);
1601     if ((head = hcc_finish_command(trans)) == NULL || head->error)
1602 	return(0);
1603     for (item = hcc_firstitem(head); item; item = hcc_nextitem(head, item)) {
1604 	if (item->leafid == LC_UID)
1605 	    return (HCC_INT32(item));
1606     }
1607     return(0); /* shouldn't happen */
1608 }
1609 
1610 static int
1611 rc_geteuid(hctransaction_t trans, struct HCHead *head __unused)
1612 {
1613     hcc_leaf_int32(trans, LC_UID, geteuid());
1614     return (0);
1615 }
1616 
1617 static int
1618 getmygroups(gid_t **gidlist)
1619 {
1620     int count;
1621 
1622     if ((count = getgroups(0, *gidlist)) > 0) {
1623 	if ((*gidlist = malloc(count * sizeof(gid_t))) != NULL) {
1624 	    if ((count = getgroups(count, *gidlist)) <= 0)
1625 		free(*gidlist);
1626 	}
1627 	else
1628 	    count = -1;
1629     }
1630     else
1631 	*gidlist = NULL;
1632     return (count);
1633 }
1634 
1635 int
1636 hc_getgroups(struct HostConf *hc, gid_t **gidlist)
1637 {
1638     int count, i;
1639     hctransaction_t trans;
1640     struct HCHead *head;
1641     struct HCLeaf *item;
1642 
1643     if (hc == NULL || hc->host == NULL)
1644 	return (getmygroups(gidlist));
1645 
1646     i = 0;
1647     count = 0;
1648     *gidlist = NULL;
1649 
1650     if (hc->version < 3) {
1651 	fprintf(stderr, "WARNING: Remote client uses old protocol version\n");
1652 	return (-1);
1653     }
1654 
1655     trans = hcc_start_command(hc, HC_GETGROUPS);
1656     if ((head = hcc_finish_command(trans)) == NULL || head->error)
1657 	return(-1);
1658     for (item = hcc_firstitem(head); item; item = hcc_nextitem(head, item)) {
1659 	switch(item->leafid) {
1660 	case LC_COUNT:
1661 	    count = HCC_INT32(item);
1662 	    if (*gidlist != NULL) { /* protocol error */
1663 		free(*gidlist);
1664 		*gidlist = NULL;
1665 		return (-1);
1666 	    }
1667 	    if ((*gidlist = malloc(count * sizeof(gid_t))) == NULL)
1668 		return (-1);
1669 	    break;
1670 	case LC_GID:
1671 	    if (*gidlist == NULL || i >= count) { /* protocol error */
1672 		if (*gidlist != NULL)
1673 		    free(*gidlist);
1674 		*gidlist = NULL;
1675 		return (-1);
1676 	    }
1677 	    (*gidlist)[i++] = HCC_INT32(item);
1678 	    break;
1679 	}
1680     }
1681     return (count);
1682 }
1683 
1684 static int
1685 rc_getgroups(hctransaction_t trans, struct HCHead *head __unused)
1686 {
1687     int count, i;
1688     gid_t *gidlist;
1689 
1690     if ((count = getmygroups(&gidlist)) < 0)
1691 	return (-1);
1692     hcc_leaf_int32(trans, LC_COUNT, count);
1693     for (i = 0; i < count; i++)
1694 	hcc_leaf_int32(trans, LC_GID, gidlist[i]);
1695     if (gidlist != NULL)
1696 	free(gidlist);
1697     return (0);
1698 }
1699