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