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