cpdup - Multiple additions, fixes, and one removal
[dragonfly.git] / bin / cpdup / hcproto.c
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 }