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