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