/* * HCPROTO.C * * This module implements a simple remote control protocol */ #include "cpdup.h" #include "hclink.h" #include "hcproto.h" static int hc_decode_stat(hctransaction_t trans, struct stat *, struct HCHead *); static int hc_decode_stat_item(struct stat *st, struct HCLeaf *item); static int rc_encode_stat(hctransaction_t trans, struct stat *); static int rc_hello(hctransaction_t trans, struct HCHead *); static int rc_stat(hctransaction_t trans, struct HCHead *); static int rc_lstat(hctransaction_t trans, struct HCHead *); static int rc_opendir(hctransaction_t trans, struct HCHead *); static int rc_readdir(hctransaction_t trans, struct HCHead *); static int rc_closedir(hctransaction_t trans, struct HCHead *); static int rc_scandir(hctransaction_t trans, struct HCHead *); static int rc_open(hctransaction_t trans, struct HCHead *); static int rc_close(hctransaction_t trans, struct HCHead *); static int rc_read(hctransaction_t trans, struct HCHead *); static int rc_readfile(hctransaction_t trans, struct HCHead *); static int rc_write(hctransaction_t trans, struct HCHead *); static int rc_remove(hctransaction_t trans, struct HCHead *); static int rc_mkdir(hctransaction_t trans, struct HCHead *); static int rc_rmdir(hctransaction_t trans, struct HCHead *); static int rc_chown(hctransaction_t trans, struct HCHead *); static int rc_lchown(hctransaction_t trans, struct HCHead *); static int rc_chmod(hctransaction_t trans, struct HCHead *); static int rc_mknod(hctransaction_t trans, struct HCHead *); static int rc_link(hctransaction_t trans, struct HCHead *); #ifdef _ST_FLAGS_PRESENT_ static int rc_chflags(hctransaction_t trans, struct HCHead *); #endif static int rc_readlink(hctransaction_t trans, struct HCHead *); static int rc_umask(hctransaction_t trans, struct HCHead *); static int rc_symlink(hctransaction_t trans, struct HCHead *); static int rc_rename(hctransaction_t trans, struct HCHead *); static int rc_utimes(hctransaction_t trans, struct HCHead *); static int rc_geteuid(hctransaction_t trans, struct HCHead *); static int rc_getgroups(hctransaction_t trans, struct HCHead *); static int getmygroups(gid_t **gidlist); static int silentwarning(int *, const char *, ...) __printflike(2, 3); static struct HCDesc HCDispatchTable[] = { { HC_HELLO, rc_hello }, { HC_STAT, rc_stat }, { HC_LSTAT, rc_lstat }, { HC_OPENDIR, rc_opendir }, { HC_READDIR, rc_readdir }, { HC_CLOSEDIR, rc_closedir }, { HC_OPEN, rc_open }, { HC_CLOSE, rc_close }, { HC_READ, rc_read }, { HC_WRITE, rc_write }, { HC_REMOVE, rc_remove }, { HC_MKDIR, rc_mkdir }, { HC_RMDIR, rc_rmdir }, { HC_CHOWN, rc_chown }, { HC_LCHOWN, rc_lchown }, { HC_CHMOD, rc_chmod }, { HC_MKNOD, rc_mknod }, { HC_LINK, rc_link }, #ifdef _ST_FLAGS_PRESENT_ { HC_CHFLAGS, rc_chflags }, #endif { HC_READLINK, rc_readlink }, { HC_UMASK, rc_umask }, { HC_SYMLINK, rc_symlink }, { HC_RENAME, rc_rename }, { HC_UTIMES, rc_utimes }, { HC_GETEUID, rc_geteuid }, { HC_GETGROUPS, rc_getgroups }, { HC_SCANDIR, rc_scandir }, { HC_READFILE, rc_readfile }, { HC_LUTIMES, rc_utimes }, #ifdef _ST_FLAGS_PRESENT_ { HC_LCHFLAGS, rc_chflags }, #endif { HC_LCHMOD, rc_chmod }, }; static int chown_warning; #ifdef _ST_FLAGS_PRESENT_ static int chflags_warning; #endif /* * If not running as root generate a silent warning and return no error. * * If running as root return an error. */ static int silentwarning(int *didwarn, const char *ctl, ...) { va_list va; if (DstRootPrivs) return(-1); if (*didwarn == 0 && QuietOpt == 0) { *didwarn = 1; fprintf(stderr, "WARNING: Not running as root, "); va_start(va, ctl); vfprintf(stderr, ctl, va); va_end(va); } return(0); } int hc_connect(struct HostConf *hc, int readonly) { if (hcc_connect(hc, readonly) < 0) { fprintf(stderr, "Unable to connect to %s\n", hc->host); return(-1); } return(hc_hello(hc)); } void hc_slave(int fdin, int fdout) { hcc_slave(fdin, fdout, HCDispatchTable, sizeof(HCDispatchTable) / sizeof(HCDispatchTable[0])); } /* * A HELLO RPC is sent on the initial connect. */ int hc_hello(struct HostConf *hc) { struct HCHead *head; struct HCLeaf *item; hctransaction_t trans; char hostbuf[256]; int error; bzero(hostbuf, sizeof(hostbuf)); if (gethostname(hostbuf, sizeof(hostbuf) - 1) < 0) return(-1); if (hostbuf[0] == 0) hostbuf[0] = '?'; trans = hcc_start_command(hc, HC_HELLO); hcc_leaf_string(trans, LC_HELLOSTR, hostbuf); hcc_leaf_int32(trans, LC_VERSION, HCPROTO_VERSION); if (UseCpFile) hcc_leaf_string(trans, LC_PATH1, UseCpFile); if ((head = hcc_finish_command(trans)) == NULL) { fprintf(stderr, "Connected to %s but remote failed to complete hello\n", hc->host); return(-1); } if (head->error) { fprintf(stderr, "Connected to %s but remote returned error %d\n", hc->host, head->error); return(-1); } error = -1; FOR_EACH_ITEM(item, trans, head) { switch(item->leafid) { case LC_HELLOSTR: if (QuietOpt == 0) fprintf(stderr, "Handshaked with %s\n", HCC_STRING(item)); error = 0; break; case LC_VERSION: hc->version = HCC_INT32(item); break; } } if (hc->version < HCPROTO_VERSION_COMPAT) { fprintf(stderr, "Remote cpdup at %s has an incompatible version\n", hc->host); error = -1; } else if (hc->version < HCPROTO_VERSION && QuietOpt == 0) { fprintf(stderr, "WARNING: Remote cpdup at %s has a lower version,\n" "expect reduced speed and/or functionality\n", hc->host); } if (error < 0) fprintf(stderr, "Handshake failed with %s\n", hc->host); return (error); } static int rc_hello(hctransaction_t trans, struct HCHead *head) { struct HCLeaf *item; char hostbuf[256]; FOR_EACH_ITEM(item, trans, head) { if (item->leafid == LC_PATH1) UseCpFile = strdup(HCC_STRING(item)); } bzero(hostbuf, sizeof(hostbuf)); if (gethostname(hostbuf, sizeof(hostbuf) - 1) < 0) return(-1); if (hostbuf[0] == 0) hostbuf[0] = '?'; hcc_leaf_string(trans, LC_HELLOSTR, hostbuf); hcc_leaf_int32(trans, LC_VERSION, HCPROTO_VERSION); return(0); } /* * STAT, LSTAT */ int hc_stat(struct HostConf *hc, const char *path, struct stat *st) { struct HCHead *head; hctransaction_t trans; if (hc == NULL || hc->host == NULL) return(stat(path, st)); trans = hcc_start_command(hc, HC_STAT); hcc_leaf_string(trans, LC_PATH1, path); if ((head = hcc_finish_command(trans)) == NULL) return(-1); if (head->error) return(-1); return(hc_decode_stat(trans, st, head)); } int hc_lstat(struct HostConf *hc, const char *path, struct stat *st) { struct HCHead *head; hctransaction_t trans; if (hc == NULL || hc->host == NULL) return(lstat(path, st)); trans = hcc_start_command(hc, HC_LSTAT); hcc_leaf_string(trans, LC_PATH1, path); if ((head = hcc_finish_command(trans)) == NULL) return(-1); if (head->error) return(-1); return(hc_decode_stat(trans, st, head)); } static int hc_decode_stat(hctransaction_t trans, struct stat *st, struct HCHead *head) { struct HCLeaf *item; bzero(st, sizeof(*st)); FOR_EACH_ITEM(item, trans, head) hc_decode_stat_item(st, item); return(0); } static int hc_decode_stat_item(struct stat *st, struct HCLeaf *item) { switch(item->leafid) { case LC_DEV: st->st_dev = HCC_INT32(item); break; case LC_INO: st->st_ino = HCC_INT64(item); break; case LC_MODE: st->st_mode = HCC_INT32(item); break; case LC_NLINK: st->st_nlink = HCC_INT32(item); break; case LC_UID: st->st_uid = HCC_INT32(item); break; case LC_GID: st->st_gid = HCC_INT32(item); break; case LC_RDEV: st->st_rdev = HCC_INT32(item); break; case LC_ATIME: st->st_atime = (time_t)HCC_INT64(item); break; case LC_MTIME: st->st_mtime = (time_t)HCC_INT64(item); break; case LC_CTIME: st->st_ctime = (time_t)HCC_INT64(item); break; #if defined(st_atime) /* A macro, so very likely on modern POSIX */ case LC_ATIMENSEC: st->st_atim.tv_nsec = HCC_INT32(item); break; case LC_MTIMENSEC: st->st_mtim.tv_nsec = HCC_INT32(item); break; case LC_CTIMENSEC: st->st_ctim.tv_nsec = HCC_INT32(item); break; #endif case LC_FILESIZE: st->st_size = HCC_INT64(item); break; case LC_FILEBLKS: st->st_blocks = HCC_INT64(item); break; case LC_BLKSIZE: st->st_blksize = HCC_INT32(item); break; #ifdef _ST_FLAGS_PRESENT_ case LC_FILEFLAGS: st->st_flags = (uint32_t)HCC_INT64(item); break; #endif } return(0); } static int rc_stat(hctransaction_t trans, struct HCHead *head) { struct HCLeaf *item; struct stat st; const char *path = NULL; FOR_EACH_ITEM(item, trans, head) { if (item->leafid == LC_PATH1) path = HCC_STRING(item); } if (path == NULL) return(-2); if (stat(path, &st) < 0) return(-1); return (rc_encode_stat(trans, &st)); } static int rc_lstat(hctransaction_t trans, struct HCHead *head) { struct HCLeaf *item; struct stat st; const char *path = NULL; FOR_EACH_ITEM(item, trans, head) { if (item->leafid == LC_PATH1) path = HCC_STRING(item); } if (path == NULL) return(-2); if (lstat(path, &st) < 0) return(-1); return (rc_encode_stat(trans, &st)); } /* * Encode all entries of a stat structure. * * CAUTION: If you add any more entries here, be sure to * increase the STAT_MAX_NUM_ENTRIES value! */ #define STAT_MAX_NUM_ENTRIES 18 static int rc_encode_stat(hctransaction_t trans, struct stat *st) { hcc_leaf_int32(trans, LC_DEV, st->st_dev); hcc_leaf_int64(trans, LC_INO, st->st_ino); hcc_leaf_int32(trans, LC_MODE, st->st_mode); hcc_leaf_int32(trans, LC_NLINK, st->st_nlink); hcc_leaf_int32(trans, LC_UID, st->st_uid); hcc_leaf_int32(trans, LC_GID, st->st_gid); hcc_leaf_int32(trans, LC_RDEV, st->st_rdev); hcc_leaf_int64(trans, LC_ATIME, st->st_atime); hcc_leaf_int64(trans, LC_MTIME, st->st_mtime); hcc_leaf_int64(trans, LC_CTIME, st->st_ctime); #if defined(st_atime) hcc_leaf_int32(trans, LC_ATIMENSEC, st->st_atim.tv_nsec); hcc_leaf_int32(trans, LC_MTIMENSEC, st->st_mtim.tv_nsec); hcc_leaf_int32(trans, LC_CTIMENSEC, st->st_ctim.tv_nsec); #endif hcc_leaf_int64(trans, LC_FILESIZE, st->st_size); hcc_leaf_int64(trans, LC_FILEBLKS, st->st_blocks); hcc_leaf_int32(trans, LC_BLKSIZE, st->st_blksize); #ifdef _ST_FLAGS_PRESENT_ hcc_leaf_int64(trans, LC_FILEFLAGS, st->st_flags); #endif return(0); } /* * OPENDIR */ DIR * hc_opendir(struct HostConf *hc, const char *path) { hctransaction_t trans; struct HCHead *head; if (hc == NULL || hc->host == NULL) return(opendir(path)); if (hc->version <= 3) { /* compatibility: HC_SCANDIR not supported */ struct HCLeaf *item; struct HCDirEntry *den; intptr_t desc = 0; trans = hcc_start_command(hc, HC_OPENDIR); hcc_leaf_string(trans, LC_PATH1, path); if ((head = hcc_finish_command(trans)) == NULL) return (NULL); if (head->error) return (NULL); FOR_EACH_ITEM(item, trans, head) { if (item->leafid == LC_DESCRIPTOR) desc = HCC_INT32(item); } if (hcc_get_descriptor(hc, desc, HC_DESC_DIR)) { fprintf(stderr, "hc_opendir: remote reused active descriptor %jd\n", (intmax_t)desc); return (NULL); } den = malloc(sizeof(*den)); hcc_set_descriptor(hc, desc, den, HC_DESC_DIR); return ((void *)desc); } /* hc->version >= 4: use HC_SCANDIR */ trans = hcc_start_command(hc, HC_SCANDIR); hcc_leaf_string(trans, LC_PATH1, path); if ((head = hcc_finish_command(trans)) == NULL || head->error) return (NULL); return ((void *)head); } static int rc_opendir(hctransaction_t trans, struct HCHead *head) { struct HCLeaf *item; const char *path = NULL; DIR *dir; int desc; FOR_EACH_ITEM(item, trans, head) { if (item->leafid == LC_PATH1) path = HCC_STRING(item); } if (path == NULL) return(-2); if ((dir = opendir(path)) == NULL) { head->error = errno; } else { desc = hcc_alloc_descriptor(trans->hc, dir, HC_DESC_DIR); hcc_leaf_int32(trans, LC_DESCRIPTOR, desc); } return(0); } /* * READDIR */ struct HCDirEntry * hc_readdir(struct HostConf *hc, DIR *dir, struct stat **statpp) { int stat_ok = 0; struct HCHead *head; struct HCLeaf *item; static struct HCDirEntry denbuf; *statpp = NULL; if (hc == NULL || hc->host == NULL) { struct dirent *sysden; if ((sysden = readdir(dir)) == NULL) return (NULL); strlcpy(denbuf.d_name, sysden->d_name, MAXNAMLEN + 1); return (&denbuf); } if (hc->version <= 3) { /* compatibility: HC_SCANDIR not supported */ hctransaction_t trans; struct HCDirEntry *den; trans = hcc_start_command(hc, HC_READDIR); hcc_leaf_int32(trans, LC_DESCRIPTOR, (intptr_t)dir); if ((head = hcc_finish_command(trans)) == NULL) return (NULL); if (head->error) return (NULL); /* XXX errno */ den = hcc_get_descriptor(hc, (intptr_t)dir, HC_DESC_DIR); if (den == NULL) return (NULL); /* XXX errno */ den->d_name[0] = 0; FOR_EACH_ITEM(item, trans, head) { if (item->leafid == LC_PATH1) strlcpy(den->d_name, HCC_STRING(item), MAXNAMLEN + 1); } return (den->d_name[0] ? den : NULL); } /* hc->version >= 4: using HC_SCANDIR */ denbuf.d_name[0] = 0; head = (void *)dir; *statpp = malloc(sizeof(struct stat)); bzero(*statpp, sizeof(struct stat)); while ((item = hcc_nextchaineditem(hc, head)) != NULL) { if (item->leafid == LC_PATH1) { /* this must be the last item */ strlcpy(denbuf.d_name, HCC_STRING(item), MAXNAMLEN + 1); break; } else { stat_ok = 1; hc_decode_stat_item(*statpp, item); } } if (!stat_ok) { free(*statpp); *statpp = NULL; } if (hc->trans.state == HCT_FAIL) return NULL; return (denbuf.d_name[0] ? &denbuf : NULL); } static int rc_readdir(hctransaction_t trans, struct HCHead *head) { struct HCLeaf *item; struct dirent *den; DIR *dir = NULL; FOR_EACH_ITEM(item, trans, head) { if (item->leafid == LC_DESCRIPTOR) dir = hcc_get_descriptor(trans->hc, HCC_INT32(item), HC_DESC_DIR); } if (dir == NULL) return(-2); if ((den = readdir(dir)) != NULL) hcc_leaf_string(trans, LC_PATH1, den->d_name); return(0); } /* * CLOSEDIR * * XXX cpdup needs to check error code to avoid truncated dirs? */ int hc_closedir(struct HostConf *hc, DIR *dir) { struct HCHead *head; if (hc == NULL || hc->host == NULL) return(closedir(dir)); if (hc->version <= 3) { /* compatibility: HC_SCANDIR not supported */ hctransaction_t trans; struct dirent *den; if ((den = hcc_get_descriptor(hc, (intptr_t)dir, HC_DESC_DIR)) != NULL) { free(den); hcc_set_descriptor(hc, (intptr_t)dir, NULL, HC_DESC_DIR); trans = hcc_start_command(hc, HC_CLOSEDIR); hcc_leaf_int32(trans, LC_DESCRIPTOR, (intptr_t)dir); if ((head = hcc_finish_command(trans)) == NULL) return (-1); if (head->error) return (-1); /* XXX errno */ return (0); } else { /* errno */ return(-1); } } /* hc->version >= 4: using HC_SCANDIR */ head = (void *)dir; /* skip any remaining items if the directory is closed prematurely */ while (hcc_nextchaineditem(hc, head) != NULL) /*nothing*/ ; if (hc->trans.state == HCT_FAIL || head->error) return (-1); return (0); } static int rc_closedir(hctransaction_t trans, struct HCHead *head) { struct HCLeaf *item; DIR *dir = NULL; FOR_EACH_ITEM(item, trans, head) { if (item->leafid == LC_DESCRIPTOR) { dir = hcc_get_descriptor(trans->hc, HCC_INT32(item), HC_DESC_DIR); if (dir != NULL) { hcc_set_descriptor(trans->hc, HCC_INT32(item), NULL, HC_DESC_DIR); } } } if (dir == NULL) return(-2); return(closedir(dir)); } /* * SCANDIR */ static int rc_scandir(hctransaction_t trans, struct HCHead *head) { struct HCLeaf *item; const char *path = NULL; struct dirent *den; DIR *dir; char *fpath; struct stat st; FOR_EACH_ITEM(item, trans, head) { if (item->leafid == LC_PATH1) path = HCC_STRING(item); } if (path == NULL) return (-2); if ((dir = opendir(path)) == NULL) return (-1); while ((den = readdir(dir)) != NULL) { if (den->d_name[0] == '.' && (den->d_name[1] == '\0' || (den->d_name[1] == '.' && den->d_name[2] == '\0'))) continue; /* skip "." and ".." */ /* * Check if there's enough space left in the current packet. * We have at most STAT_MAX_NUM_ENTRIES pieces of data, of which * one is a string, so we use strlen() + 1 (terminating zero). * The remaining ones are numbers; we assume sizeof(int64_t) so * we're on the safe side. */ if (!hcc_check_space(trans, head, STAT_MAX_NUM_ENTRIES, (STAT_MAX_NUM_ENTRIES - 1) * sizeof(int64_t) + strlen(den->d_name) + 1)) { closedir(dir); return (-1); } fpath = mprintf("%s/%s", path, den->d_name); if (lstat(fpath, &st) == 0) rc_encode_stat(trans, &st); /* The name must be the last item! */ hcc_leaf_string(trans, LC_PATH1, den->d_name); free(fpath); } return (closedir(dir)); } /* * OPEN */ int hc_open(struct HostConf *hc, const char *path, int flags, mode_t mode) { hctransaction_t trans; struct HCHead *head; struct HCLeaf *item; int *fdp; int desc = 0; int nflags; if (NotForRealOpt && (flags & O_CREAT)) return(0x7FFFFFFF); if (hc == NULL || hc->host == NULL) { #ifdef O_LARGEFILE flags |= O_LARGEFILE; #endif return(open(path, flags, mode)); } if ((flags & (O_WRONLY | O_RDWR)) == 0 && hc->version >= 4) { trans = hcc_start_command(hc, HC_READFILE); hcc_leaf_string(trans, LC_PATH1, path); if ((head = hcc_finish_command(trans)) == NULL || head->error) return (-1); head->magic = 0; /* used to indicate offset within buffer */ return (1); /* dummy */ } nflags = flags & XO_NATIVEMASK; if (flags & O_CREAT) nflags |= XO_CREAT; if (flags & O_EXCL) nflags |= XO_EXCL; if (flags & O_TRUNC) nflags |= XO_TRUNC; trans = hcc_start_command(hc, HC_OPEN); hcc_leaf_string(trans, LC_PATH1, path); hcc_leaf_int32(trans, LC_OFLAGS, nflags); hcc_leaf_int32(trans, LC_MODE, mode); if ((head = hcc_finish_command(trans)) == NULL) return(-1); if (head->error) return(-1); FOR_EACH_ITEM(item, trans, head) { if (item->leafid == LC_DESCRIPTOR) desc = HCC_INT32(item); } if (hcc_get_descriptor(hc, desc, HC_DESC_FD)) { fprintf(stderr, "hc_open: remote reused active descriptor %d\n", desc); return(-1); } fdp = malloc(sizeof(int)); *fdp = desc; /* really just a dummy */ hcc_set_descriptor(hc, desc, fdp, HC_DESC_FD); return(desc); } static int rc_open(hctransaction_t trans, struct HCHead *head) { struct HCLeaf *item; const char *path = NULL; int nflags = 0; int flags; mode_t mode = 0666; int desc; int *fdp; int fd; FOR_EACH_ITEM(item, trans, head) { switch(item->leafid) { case LC_PATH1: path = HCC_STRING(item); break; case LC_OFLAGS: nflags = HCC_INT32(item); break; case LC_MODE: mode = HCC_INT32(item); break; } } if (path == NULL) return(-2); flags = nflags & XO_NATIVEMASK; if (nflags & XO_CREAT) flags |= O_CREAT; if (nflags & XO_EXCL) flags |= O_EXCL; if (nflags & XO_TRUNC) flags |= O_TRUNC; if (ReadOnlyOpt) { if (flags & (O_WRONLY | O_RDWR | O_CREAT | O_TRUNC)) { head->error = EACCES; return (0); } flags |= O_RDONLY; } #ifdef O_LARGEFILE flags |= O_LARGEFILE; #endif if ((fd = open(path, flags, mode)) < 0) return(-1); fdp = malloc(sizeof(int)); *fdp = fd; desc = hcc_alloc_descriptor(trans->hc, fdp, HC_DESC_FD); hcc_leaf_int32(trans, LC_DESCRIPTOR, desc); return(0); } /* * CLOSE */ int hc_close(struct HostConf *hc, int fd) { hctransaction_t trans; struct HCHead *head; int *fdp; if (NotForRealOpt && fd == 0x7FFFFFFF) return(0); if (hc == NULL || hc->host == NULL) return(close(fd)); if (fd == 1 && hc->version >= 4) { /* using HC_READFILE */ head = (void *)hc->trans.rbuf; /* skip any remaining items if the file is closed prematurely */ while (hcc_nextchaineditem(hc, head) != NULL) /*nothing*/ ; if (hc->trans.state == HCT_FAIL || head->error) return (-1); return (0); } fdp = hcc_get_descriptor(hc, fd, HC_DESC_FD); if (fdp) { free(fdp); hcc_set_descriptor(hc, fd, NULL, HC_DESC_FD); trans = hcc_start_command(hc, HC_CLOSE); hcc_leaf_int32(trans, LC_DESCRIPTOR, fd); if ((head = hcc_finish_command(trans)) == NULL) return(-1); if (head->error) return(-1); return(0); } else { return(-1); } } static int rc_close(hctransaction_t trans, struct HCHead *head) { struct HCLeaf *item; int *fdp = NULL; int fd; int desc = -1; FOR_EACH_ITEM(item, trans, head) { if (item->leafid == LC_DESCRIPTOR) desc = HCC_INT32(item); } if (desc < 0) return(-2); if ((fdp = hcc_get_descriptor(trans->hc, desc, HC_DESC_FD)) == NULL) return(-2); fd = *fdp; free(fdp); hcc_set_descriptor(trans->hc, desc, NULL, HC_DESC_FD); return(close(fd)); } static int getiolimit(void) { return(32768); } /* * READ */ ssize_t hc_read(struct HostConf *hc, int fd, void *buf, size_t bytes) { hctransaction_t trans; struct HCHead *head; struct HCLeaf *item; int *fdp; int offset; int r = 0; int x = 0; if (hc == NULL || hc->host == NULL) return(read(fd, buf, bytes)); if (fd == 1 && hc->version >= 4) { /* using HC_READFILE */ head = (void *)hc->trans.rbuf; while (bytes) { if ((offset = head->magic) != 0) { item = hcc_currentchaineditem(hc, head); } else { item = hcc_nextchaineditem(hc, head); } if (item == NULL) { if (hc->trans.state == HCT_FAIL) r = -1; return (r); } if (item->leafid != LC_DATA) return (-1); x = item->bytes - sizeof(*item) - offset; if (x > (int)bytes) { x = (int)bytes; head->magic += x; /* leave bytes in the buffer */ } else head->magic = 0; /* all bytes used up */ bcopy((char *)HCC_BINARYDATA(item) + offset, buf, x); buf = (char *)buf + x; bytes -= (size_t)x; r += x; } return (r); } fdp = hcc_get_descriptor(hc, fd, HC_DESC_FD); if (fdp) { while (bytes) { size_t limit = getiolimit(); int n = (bytes > limit) ? limit : bytes; trans = hcc_start_command(hc, HC_READ); hcc_leaf_int32(trans, LC_DESCRIPTOR, fd); hcc_leaf_int32(trans, LC_BYTES, n); if ((head = hcc_finish_command(trans)) == NULL) return(-1); if (head->error) return(-1); FOR_EACH_ITEM(item, trans, head) { if (item->leafid == LC_DATA) { x = item->bytes - sizeof(*item); if (x > (int)bytes) x = (int)bytes; bcopy(HCC_BINARYDATA(item), buf, x); buf = (char *)buf + x; bytes -= (size_t)x; r += x; } } if (x < n) break; } return(r); } else { return(-1); } } static int rc_read(hctransaction_t trans, struct HCHead *head) { struct HCLeaf *item; int *fdp = NULL; char buf[32768]; int bytes = -1; int n; FOR_EACH_ITEM(item, trans, head) { switch(item->leafid) { case LC_DESCRIPTOR: fdp = hcc_get_descriptor(trans->hc, HCC_INT32(item), HC_DESC_FD); break; case LC_BYTES: bytes = HCC_INT32(item); break; } } if (fdp == NULL) return(-2); if (bytes < 0 || bytes > 32768) return(-2); n = read(*fdp, buf, bytes); if (n < 0) return(-1); hcc_leaf_data(trans, LC_DATA, buf, n); return(0); } /* * READFILE */ static int rc_readfile(hctransaction_t trans, struct HCHead *head) { struct HCLeaf *item; const char *path = NULL; char buf[32768]; int n; int fd; FOR_EACH_ITEM(item, trans, head) { if (item->leafid == LC_PATH1) path = HCC_STRING(item); } if (path == NULL) return (-2); if ((fd = open(path, O_RDONLY)) < 0) return(-1); while ((n = read(fd, buf, 32768)) >= 0) { if (!hcc_check_space(trans, head, 1, n)) { close(fd); return (-1); } hcc_leaf_data(trans, LC_DATA, buf, n); if (n == 0) break; } if (n < 0) { close(fd); return (-1); } return (close(fd)); } /* * WRITE */ ssize_t hc_write(struct HostConf *hc, int fd, const void *buf, size_t bytes) { hctransaction_t trans; struct HCHead *head; struct HCLeaf *item; int *fdp; int r; if (NotForRealOpt) return(bytes); if (hc == NULL || hc->host == NULL) return(write(fd, buf, bytes)); fdp = hcc_get_descriptor(hc, fd, HC_DESC_FD); if (fdp) { r = 0; while (bytes) { size_t limit = getiolimit(); int n = (bytes > limit) ? limit : bytes; int x = 0; trans = hcc_start_command(hc, HC_WRITE); hcc_leaf_int32(trans, LC_DESCRIPTOR, fd); hcc_leaf_data(trans, LC_DATA, buf, n); if ((head = hcc_finish_command(trans)) == NULL) return(-1); if (head->error) return(-1); FOR_EACH_ITEM(item, trans, head) { if (item->leafid == LC_BYTES) x = HCC_INT32(item); } if (x < 0 || x > n) return(-1); r += x; buf = (const char *)buf + x; bytes -= x; if (x < n) break; } return(r); } else { return(-1); } } static int rc_write(hctransaction_t trans, struct HCHead *head) { struct HCLeaf *item; int *fdp = NULL; void *buf = NULL; int n = -1; FOR_EACH_ITEM(item, trans, head) { switch(item->leafid) { case LC_DESCRIPTOR: fdp = hcc_get_descriptor(trans->hc, HCC_INT32(item), HC_DESC_FD); break; case LC_DATA: buf = HCC_BINARYDATA(item); n = item->bytes - sizeof(*item); break; } } if (ReadOnlyOpt) { head->error = EACCES; return (0); } if (fdp == NULL) return(-2); if (n < 0 || n > 32768) return(-2); n = write(*fdp, buf, n); if (n < 0) return (-1); hcc_leaf_int32(trans, LC_BYTES, n); return(0); } /* * REMOVE * * NOTE: This function returns -errno if an error occured. */ int hc_remove(struct HostConf *hc, const char *path) { hctransaction_t trans; struct HCHead *head; int res; if (NotForRealOpt) return(0); if (hc == NULL || hc->host == NULL) { res = remove(path); if (res < 0) res = -errno; return(res); } trans = hcc_start_command(hc, HC_REMOVE); hcc_leaf_string(trans, LC_PATH1, path); if ((head = hcc_finish_command(trans)) == NULL) return(-EIO); if (head->error) return(-(int)head->error); return(0); } static int rc_remove(hctransaction_t trans, struct HCHead *head) { struct HCLeaf *item; const char *path = NULL; FOR_EACH_ITEM(item, trans, head) { if (item->leafid == LC_PATH1) path = HCC_STRING(item); } if (path == NULL) return(-2); if (ReadOnlyOpt) { head->error = EACCES; return (0); } return(remove(path)); } /* * MKDIR */ int hc_mkdir(struct HostConf *hc, const char *path, mode_t mode) { hctransaction_t trans; struct HCHead *head; if (NotForRealOpt) return(0); if (hc == NULL || hc->host == NULL) return(mkdir(path, mode)); trans = hcc_start_command(hc, HC_MKDIR); hcc_leaf_string(trans, LC_PATH1, path); hcc_leaf_int32(trans, LC_MODE, mode); if ((head = hcc_finish_command(trans)) == NULL) return(-1); if (head->error) return(-1); return(0); } static int rc_mkdir(hctransaction_t trans, struct HCHead *head) { struct HCLeaf *item; const char *path = NULL; mode_t mode = 0777; FOR_EACH_ITEM(item, trans, head) { switch(item->leafid) { case LC_PATH1: path = HCC_STRING(item); break; case LC_MODE: mode = HCC_INT32(item); break; } } if (ReadOnlyOpt) { head->error = EACCES; return (0); } if (path == NULL) return(-2); return(mkdir(path, mode)); } /* * RMDIR */ int hc_rmdir(struct HostConf *hc, const char *path) { hctransaction_t trans; struct HCHead *head; if (NotForRealOpt) return(0); if (hc == NULL || hc->host == NULL) return(rmdir(path)); trans = hcc_start_command(hc, HC_RMDIR); hcc_leaf_string(trans, LC_PATH1, path); if ((head = hcc_finish_command(trans)) == NULL) return(-1); if (head->error) return(-1); return(0); } static int rc_rmdir(hctransaction_t trans, struct HCHead *head) { struct HCLeaf *item; const char *path = NULL; FOR_EACH_ITEM(item, trans, head) { if (item->leafid == LC_PATH1) path = HCC_STRING(item); } if (ReadOnlyOpt) { head->error = EACCES; return (0); } if (path == NULL) return(-2); return(rmdir(path)); } /* * CHOWN * * Almost silently ignore chowns that fail if we are not root. */ int hc_chown(struct HostConf *hc, const char *path, uid_t owner, gid_t group) { hctransaction_t trans; struct HCHead *head; int rc; if (NotForRealOpt) return(0); if (!DstRootPrivs) owner = -1; if (hc == NULL || hc->host == NULL) { rc = chown(path, owner, group); if (rc < 0) rc = silentwarning(&chown_warning, "file ownership may differ\n"); return(rc); } trans = hcc_start_command(hc, HC_CHOWN); hcc_leaf_string(trans, LC_PATH1, path); hcc_leaf_int32(trans, LC_UID, owner); hcc_leaf_int32(trans, LC_GID, group); if ((head = hcc_finish_command(trans)) == NULL) return(-1); if (head->error) return(-1); return(0); } static int rc_chown(hctransaction_t trans, struct HCHead *head) { struct HCLeaf *item; const char *path = NULL; uid_t uid = (uid_t)-1; gid_t gid = (gid_t)-1; int rc; FOR_EACH_ITEM(item, trans, head) { switch(item->leafid) { case LC_PATH1: path = HCC_STRING(item); break; case LC_UID: uid = HCC_INT32(item); break; case LC_GID: gid = HCC_INT32(item); break; } } if (ReadOnlyOpt) { head->error = EACCES; return (0); } if (path == NULL) return(-2); rc = chown(path, uid, gid); if (rc < 0) rc = silentwarning(&chown_warning, "file ownership may differ\n"); return(rc); } /* * LCHOWN */ int hc_lchown(struct HostConf *hc, const char *path, uid_t owner, gid_t group) { hctransaction_t trans; struct HCHead *head; int rc; if (NotForRealOpt) return(0); if (!DstRootPrivs) owner = -1; if (hc == NULL || hc->host == NULL) { rc = lchown(path, owner, group); if (rc < 0) rc = silentwarning(&chown_warning, "file ownership may differ\n"); return(rc); } trans = hcc_start_command(hc, HC_LCHOWN); hcc_leaf_string(trans, LC_PATH1, path); hcc_leaf_int32(trans, LC_UID, owner); hcc_leaf_int32(trans, LC_GID, group); if ((head = hcc_finish_command(trans)) == NULL) return(-1); if (head->error) return(-1); return(0); } static int rc_lchown(hctransaction_t trans, struct HCHead *head) { struct HCLeaf *item; const char *path = NULL; uid_t uid = (uid_t)-1; gid_t gid = (gid_t)-1; int rc; FOR_EACH_ITEM(item, trans, head) { switch(item->leafid) { case LC_PATH1: path = HCC_STRING(item); break; case LC_UID: uid = HCC_INT32(item); break; case LC_GID: gid = HCC_INT32(item); break; } } if (ReadOnlyOpt) { head->error = EACCES; return (0); } if (path == NULL) return(-2); rc = lchown(path, uid, gid); if (rc < 0) rc = silentwarning(&chown_warning, "file ownership may differ\n"); return(rc); } /* * CHMOD */ int hc_chmod(struct HostConf *hc, const char *path, mode_t mode) { hctransaction_t trans; struct HCHead *head; if (NotForRealOpt) return(0); if (hc == NULL || hc->host == NULL) return(chmod(path, mode)); trans = hcc_start_command(hc, HC_CHMOD); hcc_leaf_string(trans, LC_PATH1, path); hcc_leaf_int32(trans, LC_MODE, mode); if ((head = hcc_finish_command(trans)) == NULL) return(-1); if (head->error) return(-1); return(0); } int hc_lchmod(struct HostConf *hc, const char *path, mode_t mode) { hctransaction_t trans; struct HCHead *head; if (NotForRealOpt) return(0); if (hc == NULL || hc->host == NULL) return(lchmod(path, mode)); trans = hcc_start_command(hc, HC_LCHMOD); hcc_leaf_string(trans, LC_PATH1, path); hcc_leaf_int32(trans, LC_MODE, mode); if ((head = hcc_finish_command(trans)) == NULL) return(-1); if (head->error) return(-1); return(0); } static int rc_chmod(hctransaction_t trans, struct HCHead *head) { struct HCLeaf *item; const char *path = NULL; mode_t mode = 0666; FOR_EACH_ITEM(item, trans, head) { switch(item->leafid) { case LC_PATH1: path = HCC_STRING(item); break; case LC_MODE: mode = HCC_INT32(item); break; } } if (ReadOnlyOpt) { head->error = EACCES; return (0); } if (path == NULL) return(-2); if (head->cmd == HC_LCHMOD) return(lchmod(path, mode)); else return(chmod(path, mode)); } /* * MKNOD */ int hc_mknod(struct HostConf *hc, const char *path, mode_t mode, dev_t rdev) { hctransaction_t trans; struct HCHead *head; if (NotForRealOpt) return(0); if (!DstRootPrivs) { /* mknod() requires root privs, so don't bother. */ errno = EPERM; return (-1); } if (hc == NULL || hc->host == NULL) return(mknod(path, mode, rdev)); trans = hcc_start_command(hc, HC_MKNOD); hcc_leaf_string(trans, LC_PATH1, path); hcc_leaf_int32(trans, LC_MODE, mode); hcc_leaf_int32(trans, LC_RDEV, rdev); if ((head = hcc_finish_command(trans)) == NULL) return(-1); if (head->error) return(-1); return(0); } static int rc_mknod(hctransaction_t trans, struct HCHead *head) { struct HCLeaf *item; const char *path = NULL; mode_t mode = 0666; dev_t rdev = 0; FOR_EACH_ITEM(item, trans, head) { switch(item->leafid) { case LC_PATH1: path = HCC_STRING(item); break; case LC_MODE: mode = HCC_INT32(item); break; case LC_RDEV: rdev = HCC_INT32(item); break; } } if (ReadOnlyOpt) { head->error = EACCES; return (0); } if (path == NULL) return(-2); return(mknod(path, mode, rdev)); } /* * LINK */ int hc_link(struct HostConf *hc, const char *name1, const char *name2) { hctransaction_t trans; struct HCHead *head; if (NotForRealOpt) return(0); if (hc == NULL || hc->host == NULL) return(link(name1, name2)); trans = hcc_start_command(hc, HC_LINK); hcc_leaf_string(trans, LC_PATH1, name1); hcc_leaf_string(trans, LC_PATH2, name2); if ((head = hcc_finish_command(trans)) == NULL) return(-1); if (head->error) return(-1); return(0); } static int rc_link(hctransaction_t trans, struct HCHead *head) { struct HCLeaf *item; const char *name1 = NULL; const char *name2 = NULL; FOR_EACH_ITEM(item, trans, head) { switch(item->leafid) { case LC_PATH1: name1 = HCC_STRING(item); break; case LC_PATH2: name2 = HCC_STRING(item); break; } } if (ReadOnlyOpt) { head->error = EACCES; return (-0); } if (name1 == NULL || name2 == NULL) return(-2); return(link(name1, name2)); } #ifdef _ST_FLAGS_PRESENT_ /* * CHFLAGS */ int hc_chflags(struct HostConf *hc, const char *path, u_long flags) { hctransaction_t trans; struct HCHead *head; int rc; if (NotForRealOpt) return(0); if (!DstRootPrivs) flags &= UF_SETTABLE; if (hc == NULL || hc->host == NULL) { if ((rc = chflags(path, flags)) < 0) rc = silentwarning(&chflags_warning, "file flags may differ\n"); return (rc); } trans = hcc_start_command(hc, HC_CHFLAGS); hcc_leaf_string(trans, LC_PATH1, path); hcc_leaf_int64(trans, LC_FILEFLAGS, flags); if ((head = hcc_finish_command(trans)) == NULL) return(-1); if (head->error) return(-1); return(0); } int hc_lchflags(struct HostConf *hc, const char *path, u_long flags) { hctransaction_t trans; struct HCHead *head; int rc; if (NotForRealOpt) return(0); if (!DstRootPrivs) flags &= UF_SETTABLE; if (hc == NULL || hc->host == NULL) { if ((rc = lchflags(path, flags)) < 0) rc = silentwarning(&chflags_warning, "file flags may differ\n"); return (rc); } trans = hcc_start_command(hc, HC_LCHFLAGS); hcc_leaf_string(trans, LC_PATH1, path); hcc_leaf_int64(trans, LC_FILEFLAGS, flags); if ((head = hcc_finish_command(trans)) == NULL) return(-1); if (head->error) return(-1); return(0); } static int rc_chflags(hctransaction_t trans, struct HCHead *head) { struct HCLeaf *item; const char *path = NULL; u_long flags = 0; int rc; FOR_EACH_ITEM(item, trans, head) { switch(item->leafid) { case LC_PATH1: path = HCC_STRING(item); break; case LC_FILEFLAGS: flags = (u_long)HCC_INT64(item); break; } } if (ReadOnlyOpt) { head->error = EACCES; return (0); } if (path == NULL) return(-2); if (head->cmd == HC_LCHFLAGS) rc = lchflags(path, flags); else rc = chflags(path, flags); if (rc < 0) rc = silentwarning(&chflags_warning, "file flags may differ\n"); return(rc); } #endif /* * READLINK */ int hc_readlink(struct HostConf *hc, const char *path, char *buf, int bufsiz) { hctransaction_t trans; struct HCHead *head; struct HCLeaf *item; int r; if (hc == NULL || hc->host == NULL) return(readlink(path, buf, bufsiz)); trans = hcc_start_command(hc, HC_READLINK); hcc_leaf_string(trans, LC_PATH1, path); if ((head = hcc_finish_command(trans)) == NULL) return(-1); if (head->error) return(-1); r = 0; FOR_EACH_ITEM(item, trans, head) { if (item->leafid == LC_DATA) { r = item->bytes - sizeof(*item); if (r < 0) r = 0; if (r > bufsiz) r = bufsiz; bcopy(HCC_BINARYDATA(item), buf, r); } } return(r); } static int rc_readlink(hctransaction_t trans, struct HCHead *head) { struct HCLeaf *item; const char *path = NULL; char buf[1024]; int r; FOR_EACH_ITEM(item, trans, head) { if (item->leafid == LC_PATH1) path = HCC_STRING(item); } if (path == NULL) return(-2); r = readlink(path, buf, sizeof(buf)); if (r < 0) return(-1); hcc_leaf_data(trans, LC_DATA, buf, r); return(0); } /* * UMASK */ mode_t hc_umask(struct HostConf *hc, mode_t numask) { hctransaction_t trans; struct HCHead *head; struct HCLeaf *item; if (NotForRealOpt) return(umask(numask)); if (hc == NULL || hc->host == NULL) return(umask(numask)); trans = hcc_start_command(hc, HC_UMASK); hcc_leaf_int32(trans, LC_MODE, numask); if ((head = hcc_finish_command(trans)) == NULL) return((mode_t)-1); if (head->error) return((mode_t)-1); numask = (mode_t) ~0666U; FOR_EACH_ITEM(item, trans, head) { if (item->leafid == LC_MODE) numask = HCC_INT32(item); } return(numask); } static int rc_umask(hctransaction_t trans, struct HCHead *head) { struct HCLeaf *item; mode_t numask = (mode_t) ~0666U; FOR_EACH_ITEM(item, trans, head) { if (item->leafid == LC_MODE) numask = HCC_INT32(item); } numask = umask(numask); hcc_leaf_int32(trans, LC_MODE, numask); return(0); } /* * SYMLINK */ int hc_symlink(struct HostConf *hc, const char *name1, const char *name2) { hctransaction_t trans; struct HCHead *head; if (NotForRealOpt) return(0); if (hc == NULL || hc->host == NULL) return(symlink(name1, name2)); trans = hcc_start_command(hc, HC_SYMLINK); hcc_leaf_string(trans, LC_PATH1, name1); hcc_leaf_string(trans, LC_PATH2, name2); if ((head = hcc_finish_command(trans)) == NULL) return(-1); if (head->error) return(-1); return(0); } static int rc_symlink(hctransaction_t trans, struct HCHead *head) { struct HCLeaf *item; const char *name1 = NULL; const char *name2 = NULL; FOR_EACH_ITEM(item, trans, head) { switch(item->leafid) { case LC_PATH1: name1 = HCC_STRING(item); break; case LC_PATH2: name2 = HCC_STRING(item); break; } } if (ReadOnlyOpt) { head->error = EACCES; return (0); } if (name1 == NULL || name2 == NULL) return(-2); return(symlink(name1, name2)); } /* * RENAME */ int hc_rename(struct HostConf *hc, const char *name1, const char *name2) { hctransaction_t trans; struct HCHead *head; if (NotForRealOpt) return(0); if (hc == NULL || hc->host == NULL) return(rename(name1, name2)); trans = hcc_start_command(hc, HC_RENAME); hcc_leaf_string(trans, LC_PATH1, name1); hcc_leaf_string(trans, LC_PATH2, name2); if ((head = hcc_finish_command(trans)) == NULL) return(-1); if (head->error) return(-1); return(0); } static int rc_rename(hctransaction_t trans, struct HCHead *head) { struct HCLeaf *item; const char *name1 = NULL; const char *name2 = NULL; FOR_EACH_ITEM(item, trans, head) { switch(item->leafid) { case LC_PATH1: name1 = HCC_STRING(item); break; case LC_PATH2: name2 = HCC_STRING(item); break; } } if (ReadOnlyOpt) { head->error = EACCES; return (0); } if (name1 == NULL || name2 == NULL) return(-2); return(rename(name1, name2)); } /* * UTIMES */ int hc_utimes(struct HostConf *hc, const char *path, const struct timeval *times) { hctransaction_t trans; struct HCHead *head; if (NotForRealOpt) return(0); if (hc == NULL || hc->host == NULL) { return(utimes(path, times)); } trans = hcc_start_command(hc, HC_UTIMES); hcc_leaf_string(trans, LC_PATH1, path); hcc_leaf_int64(trans, LC_ATIME, times[0].tv_sec); hcc_leaf_int64(trans, LC_MTIME, times[1].tv_sec); #if defined(st_atime) hcc_leaf_int32(trans, LC_ATIMENSEC, times[0].tv_usec * 1000); hcc_leaf_int32(trans, LC_MTIMENSEC, times[1].tv_usec * 1000); #endif if ((head = hcc_finish_command(trans)) == NULL) return(-1); if (head->error) return(-1); return(0); } int hc_lutimes(struct HostConf *hc, const char *path, const struct timeval *times) { hctransaction_t trans; struct HCHead *head; if (NotForRealOpt) return(0); if (hc == NULL || hc->host == NULL) { return(lutimes(path, times)); } trans = hcc_start_command(hc, HC_LUTIMES); hcc_leaf_string(trans, LC_PATH1, path); hcc_leaf_int64(trans, LC_ATIME, times[0].tv_sec); hcc_leaf_int64(trans, LC_MTIME, times[1].tv_sec); #if defined(st_atime) hcc_leaf_int32(trans, LC_ATIMENSEC, times[0].tv_usec * 1000); hcc_leaf_int32(trans, LC_MTIMENSEC, times[1].tv_usec * 1000); #endif if ((head = hcc_finish_command(trans)) == NULL) return(-1); if (head->error) return(-1); return(0); } static int rc_utimes(hctransaction_t trans, struct HCHead *head) { struct HCLeaf *item; struct timeval times[2]; const char *path; bzero(times, sizeof(times)); path = NULL; FOR_EACH_ITEM(item, trans, head) { switch(item->leafid) { case LC_PATH1: path = HCC_STRING(item); break; case LC_ATIME: times[0].tv_sec = HCC_INT64(item); break; case LC_MTIME: times[1].tv_sec = HCC_INT64(item); break; #if defined(st_atimespec) || defined(_STATBUF_ST_NSEC) case LC_ATIMENSEC: times[0].tv_usec = HCC_INT32(item) / 1000; break; case LC_MTIMENSEC: times[1].tv_usec = HCC_INT32(item) / 1000; break; #endif } } if (ReadOnlyOpt) { head->error = EACCES; return (0); } if (path == NULL) return(-2); if (head->cmd == HC_LUTIMES) return(lutimes(path, times)); else return(utimes(path, times)); } uid_t hc_geteuid(struct HostConf *hc) { hctransaction_t trans; struct HCHead *head; struct HCLeaf *item; if (hc == NULL || hc->host == NULL) return (geteuid()); if (hc->version < 3) { fprintf(stderr, "WARNING: Remote client uses old protocol version\n"); /* Return 0 on error, so the caller assumes root privileges. */ return (0); } trans = hcc_start_command(hc, HC_GETEUID); if ((head = hcc_finish_command(trans)) == NULL || head->error) return(0); FOR_EACH_ITEM(item, trans, head) { if (item->leafid == LC_UID) return (HCC_INT32(item)); } return(0); /* shouldn't happen */ } static int rc_geteuid(hctransaction_t trans, struct HCHead *head __unused) { hcc_leaf_int32(trans, LC_UID, geteuid()); return (0); } static int getmygroups(gid_t **gidlist) { int count; if ((count = getgroups(0, *gidlist)) > 0) { if ((*gidlist = malloc(count * sizeof(gid_t))) != NULL) { if ((count = getgroups(count, *gidlist)) <= 0) free(*gidlist); } else count = -1; } else *gidlist = NULL; return (count); } int hc_getgroups(struct HostConf *hc, gid_t **gidlist) { int count, i; hctransaction_t trans; struct HCHead *head; struct HCLeaf *item; if (hc == NULL || hc->host == NULL) return (getmygroups(gidlist)); i = 0; count = 0; *gidlist = NULL; if (hc->version < 3) { fprintf(stderr, "WARNING: Remote client uses old protocol version\n"); return (-1); } trans = hcc_start_command(hc, HC_GETGROUPS); if ((head = hcc_finish_command(trans)) == NULL || head->error) return(-1); FOR_EACH_ITEM(item, trans, head) { switch(item->leafid) { case LC_COUNT: count = HCC_INT32(item); if (*gidlist != NULL) { /* protocol error */ free(*gidlist); *gidlist = NULL; return (-1); } if ((*gidlist = malloc(count * sizeof(gid_t))) == NULL) return (-1); break; case LC_GID: if (*gidlist == NULL || i >= count) { /* protocol error */ if (*gidlist != NULL) free(*gidlist); *gidlist = NULL; return (-1); } (*gidlist)[i++] = HCC_INT32(item); break; } } return (count); } static int rc_getgroups(hctransaction_t trans, struct HCHead *head __unused) { int count, i; gid_t *gidlist; if ((count = getmygroups(&gidlist)) < 0) return (-1); hcc_leaf_int32(trans, LC_COUNT, count); for (i = 0; i < count; i++) hcc_leaf_int32(trans, LC_GID, gidlist[i]); if (gidlist != NULL) free(gidlist); return (0); }