#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
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]));
}
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);
}
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)
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));
}
#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);
}
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) {
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);
}
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);
}
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) {
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);
den = hcc_get_descriptor(hc, (intptr_t)dir, HC_DESC_DIR);
if (den == NULL)
return (NULL);
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);
}
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) {
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);
}
int
hc_closedir(struct HostConf *hc, DIR *dir)
{
struct HCHead *head;
if (hc == NULL || hc->host == NULL)
return(closedir(dir));
if (hc->version <= 3) {
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);
return (0);
} else {
return(-1);
}
}
head = (void *)dir;
while (hcc_nextchaineditem(hc, head) != NULL)
;
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));
}
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;
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);
hcc_leaf_string(trans, LC_PATH1, den->d_name);
free(fpath);
}
return (closedir(dir));
}
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;
return (1);
}
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;
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);
}
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) {
head = (void *)hc->trans.rbuf;
while (hcc_nextchaineditem(hc, head) != NULL)
;
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);
}
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) {
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;
}
else
head->magic = 0;
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);
}
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));
}
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);
}
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));
}
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));
}
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));
}
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);
}
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);
}
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));
}
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) {
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));
}
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_
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
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);
}
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);
}
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));
}
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));
}
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);
}
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);
}
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) {
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) {
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);
}