version 1.17, 2003/05/06 22:10:11 |
version 1.18, 2003/05/14 01:34:35 |
|
|
* SUCH DAMAGE. |
* SUCH DAMAGE. |
*/ |
*/ |
|
|
|
#include "defs.h" |
|
#include "y.tab.h" |
|
|
#ifndef lint |
#ifndef lint |
#if 0 |
#if 0 |
static char RCSid[] = |
static char RCSid[] __attribute__((__unused__)) = |
"$From: client.c,v 6.80 1996/02/28 20:34:27 mcooper Exp $"; |
"$From: client.c,v 1.13 1999/11/01 00:22:14 christos Exp $"; |
#else |
#else |
static char RCSid[] = |
static char RCSid[] __attribute__((__unused__)) = |
"$OpenBSD$"; |
"$OpenBSD$"; |
#endif |
#endif |
|
|
static char sccsid[] = "@(#)client.c"; |
static char sccsid[] __attribute__((__unused__)) = |
|
"@(#)client.c"; |
|
|
static char copyright[] = |
static char copyright[] __attribute__((__unused__)) = |
"@(#) Copyright (c) 1983 Regents of the University of California.\n\ |
"@(#) Copyright (c) 1983 Regents of the University of California.\n\ |
All rights reserved.\n"; |
All rights reserved.\n"; |
#endif /* not lint */ |
#endif /* not lint */ |
|
|
* Routines used in client mode to communicate with remove server. |
* Routines used in client mode to communicate with remove server. |
*/ |
*/ |
|
|
#include "defs.h" |
|
#include "y.tab.h" |
|
|
|
/* |
/* |
* Update status |
* Update status |
|
|
#define US_NOENT 1 /* Entry does not exist */ |
#define US_NOENT 1 /* Entry does not exist */ |
#define US_OUTDATE 2 /* Entry is out of date */ |
#define US_OUTDATE 2 /* Entry is out of date */ |
#define US_DOCOMP 3 /* Do a binary comparison */ |
#define US_DOCOMP 3 /* Do a binary comparison */ |
#define US_MODE 4 /* Modes of file differ */ |
#define US_CHMOG 4 /* Modes or ownership of file differ */ |
|
|
struct linkbuf *ihead = NULL; /* list of files with more than one link */ |
struct linkbuf *ihead = NULL; /* list of files with more than one link */ |
char buf[BUFSIZ]; /* general purpose buffer */ |
char buf[BUFSIZ]; /* general purpose buffer */ |
|
|
char *Tdest; /* pointer to last T dest*/ |
char *Tdest; /* pointer to last T dest*/ |
struct namelist *updfilelist = NULL; /* List of updated files */ |
struct namelist *updfilelist = NULL; /* List of updated files */ |
|
|
static int sendit(); |
static void runspecial(char *, opt_t, char *, int); |
|
static void addcmdspecialfile(char *, char *, int); |
|
static void freecmdspecialfiles(void); |
|
static struct linkbuf *linkinfo(struct stat *); |
|
static int sendhardlink(opt_t, struct linkbuf *, char *, int); |
|
static int sendfile(char *, opt_t, struct stat *, char *, char *, int); |
|
static int rmchk(opt_t); |
|
static int senddir(char *, opt_t, struct stat *, char *, char *, int); |
|
static int sendlink(char *, opt_t, struct stat *, char *, char *, int); |
|
static int update(char *, opt_t, struct stat *); |
|
static int dostat(char *, struct stat *, opt_t); |
|
static int statupdate(int, char *, opt_t, char *, int, struct stat *, char *, char *); |
|
static int fullupdate(int, char *, opt_t, char *, int, struct stat *, char *, char *); |
|
static int sendit(char *, opt_t, int); |
|
|
/* |
/* |
* return remote file pathname (relative from target) |
* return remote file pathname (relative from target) |
*/ |
*/ |
char *remfilename(src, dest, path, rname, destdir) |
char * |
char *src, *dest, *path, *rname; |
remfilename(char *src, char *dest, char *path, char *rname, int destdir) |
int destdir; |
|
{ |
{ |
extern struct namelist *filelist; |
extern struct namelist *filelist; |
char *lname, *cp; |
char *lname, *cp; |
|
|
if (path && *path) { |
if (path && *path) { |
cp = strrchr(path, '/'); |
cp = strrchr(path, '/'); |
if (cp == NULL) |
if (cp == NULL) |
(void) snprintf(buff, sizeof buff, "%s/%s", dest, path); |
(void) snprintf(buff, sizeof(buff), "%s/%s", dest, path); |
else { |
else { |
srclen = strlen(src); |
srclen = strlen(src); |
pathlen = strlen(path); |
pathlen = strlen(path); |
|
|
} |
} |
} |
} |
if ((*cp != '/') && *cp) |
if ((*cp != '/') && *cp) |
(void) snprintf(buff, sizeof buff, |
(void) snprintf(buff, sizeof(buff), "%s/%s", |
"%s/%s", dest, cp); |
dest, cp); |
else |
else |
(void) snprintf(buff, sizeof buff, |
(void) snprintf(buff, sizeof(buff), "%s%s", |
"%s%s", dest, cp); |
dest, cp); |
} |
} |
} else |
} else |
strlcpy(lname, dest, buf + sizeof buff - lname); |
(void) strlcpy(lname, dest, buf + sizeof buff - lname); |
|
|
debugmsg(DM_MISC, "remfilename: remote filename=%s\n", lname); |
debugmsg(DM_MISC, "remfilename: remote filename=%s\n", lname); |
|
|
|
|
/* |
/* |
* Return true if name is in the list. |
* Return true if name is in the list. |
*/ |
*/ |
int inlist(list, file) |
int |
struct namelist *list; |
inlist(struct namelist *list, char *file) |
char *file; |
|
{ |
{ |
struct namelist *nl; |
struct namelist *nl; |
|
|
|
|
/* |
/* |
* Run any special commands for this file |
* Run any special commands for this file |
*/ |
*/ |
static void runspecial(starget, opts, rname, destdir) |
static void |
char *starget; |
runspecial(char *starget, opt_t opts, char *rname, int destdir) |
opt_t opts; |
|
char *rname; |
|
int destdir; |
|
{ |
{ |
struct subcmd *sc; |
struct subcmd *sc; |
extern struct subcmd *subcmds; |
extern struct subcmd *subcmds; |
|
|
* If we're doing a target with a "cmdspecial" in it, then |
* If we're doing a target with a "cmdspecial" in it, then |
* save the name of the file being updated for use with "cmdspecial". |
* save the name of the file being updated for use with "cmdspecial". |
*/ |
*/ |
static void addcmdspecialfile(starget, rname, destdir) |
static void |
char *starget; |
addcmdspecialfile(char *starget, char *rname, int destdir) |
char *rname; |
|
int destdir; |
|
{ |
{ |
char *rfile; |
char *rfile; |
struct namelist *new; |
struct namelist *new; |
|
|
/* |
/* |
* Free the file list |
* Free the file list |
*/ |
*/ |
static void freecmdspecialfiles() |
static void |
|
freecmdspecialfiles(void) |
{ |
{ |
struct namelist *ptr, *save; |
struct namelist *ptr, *save; |
|
|
|
|
/* |
/* |
* Run commands for an entire cmd |
* Run commands for an entire cmd |
*/ |
*/ |
extern void runcmdspecial(cmd, filev, opts) |
void |
struct cmd *cmd; |
runcmdspecial(struct cmd *cmd, opt_t opts) |
char **filev; |
|
opt_t opts; |
|
{ |
{ |
struct subcmd *sc; |
struct subcmd *sc; |
struct namelist *f; |
struct namelist *f; |
|
|
/* |
/* |
* For security, reject filenames that contains a newline |
* For security, reject filenames that contains a newline |
*/ |
*/ |
int checkfilename(name) |
int |
char *name; |
checkfilename(char *name) |
{ |
{ |
char *cp; |
char *cp; |
|
|
|
|
return(0); |
return(0); |
} |
} |
|
|
void freelinkinfo(lp) |
void |
struct linkbuf *lp; |
freelinkinfo(struct linkbuf *lp) |
{ |
{ |
if (lp->pathname) |
if (lp->pathname) |
free(lp->pathname); |
free(lp->pathname); |
|
|
/* |
/* |
* Save and retrieve hard link info |
* Save and retrieve hard link info |
*/ |
*/ |
static struct linkbuf *linkinfo(statp) |
static struct linkbuf * |
struct stat *statp; |
linkinfo(struct stat *statp) |
{ |
{ |
struct linkbuf *lp; |
struct linkbuf *lp; |
|
|
|
|
/* |
/* |
* Send a hardlink |
* Send a hardlink |
*/ |
*/ |
static int sendhardlink(opts, lp, rname, destdir) |
static int |
opt_t opts; |
sendhardlink(opt_t opts, struct linkbuf *lp, char *rname, int destdir) |
struct linkbuf *lp; |
|
char *rname; |
|
int destdir; |
|
{ |
{ |
static char buff[MAXPATHLEN]; |
static char buff[MAXPATHLEN]; |
|
char *lname; /* name of file to link to */ |
|
char ername[MAXPATHLEN*4], elname[MAXPATHLEN*4]; |
|
|
debugmsg(DM_MISC, |
debugmsg(DM_MISC, |
"sendhardlink: rname='%s' pathname='%s' src='%s' target='%s'\n", |
"sendhardlink: rname='%s' pathname='%s' src='%s' target='%s'\n", |
rname, lp->pathname ? lp->pathname : "(null)", lp->src |
rname, lp->pathname ? lp->pathname : "", |
? lp->src : "(null)", lp->target ? lp->target : "(null)"); |
lp->src ? lp->src : "", lp->target ? lp->target : ""); |
|
|
if (lp->target == NULL) |
if (lp->target == NULL) |
(void) sendcmd(C_RECVHARDLINK, "%o %s %s", |
lname = lp->pathname; |
opts, lp->pathname, rname); |
|
else { |
else { |
strlcpy(buff, remfilename(lp->src, lp->target, |
lname = buff; |
lp->pathname, rname, destdir), sizeof buff); |
strlcpy(lname, remfilename(lp->src, lp->target, |
debugmsg(DM_MISC, "sendhardlink: lname=%s\n", buff); |
lp->pathname, rname, |
(void) sendcmd(C_RECVHARDLINK, "%o %s %s", |
destdir), sizeof(buff)); |
opts, buff, rname); |
debugmsg(DM_MISC, "sendhardlink: lname=%s\n", lname); |
} |
} |
|
ENCODE(elname, lname); |
|
ENCODE(ername, rname); |
|
(void) sendcmd(C_RECVHARDLINK, "%o %s %s", |
|
opts, elname, ername); |
|
|
return(response()); |
return(response()); |
} |
} |
|
|
/* |
/* |
* Send a file |
* Send a file |
*/ |
*/ |
static int sendfile(rname, opts, stb, user, group, destdir) |
static int |
char *rname; |
sendfile(char *rname, opt_t opts, struct stat *stb, char *user, |
opt_t opts; |
char *group, int destdir) |
struct stat *stb; |
|
char *user, *group; |
|
int destdir; |
|
{ |
{ |
int goterr, f; |
int goterr, f; |
off_t i; |
off_t i; |
|
char ername[MAXPATHLEN*4]; |
|
|
if (stb->st_nlink > 1) { |
if (stb->st_nlink > 1) { |
struct linkbuf *lp; |
struct linkbuf *lp; |
|
|
/* |
/* |
* Send file info |
* Send file info |
*/ |
*/ |
|
ENCODE(ername, rname); |
|
|
(void) sendcmd(C_RECVREG, "%o %04o %ld %ld %ld %s %s %s", |
(void) sendcmd(C_RECVREG, "%o %04o %ld %ld %ld %s %s %s", |
opts, stb->st_mode & 07777, |
opts, stb->st_mode & 07777, (long) stb->st_size, |
(long) stb->st_size, |
|
stb->st_mtime, stb->st_atime, |
stb->st_mtime, stb->st_atime, |
user, group, rname); |
user, group, ername); |
if (response() < 0) { |
if (response() < 0) { |
(void) close(f); |
(void) close(f); |
return(-1); |
return(-1); |
} |
} |
|
|
debugmsg(DM_MISC, "Send file '%s' %d bytes\n", |
|
rname, (long) stb->st_size); |
|
|
|
|
debugmsg(DM_MISC, "Send file '%s' %ld bytes\n", rname, |
|
(long) stb->st_size); |
|
|
/* |
/* |
* Set remote time out alarm handler. |
* Set remote time out alarm handler. |
*/ |
*/ |
|
|
*/ |
*/ |
goterr = 0; |
goterr = 0; |
for (i = 0; i < stb->st_size; i += BUFSIZ) { |
for (i = 0; i < stb->st_size; i += BUFSIZ) { |
int amt = BUFSIZ; |
off_t amt = BUFSIZ; |
|
|
(void) alarm(rtimeout); |
(void) alarm(rtimeout); |
if (i + amt > stb->st_size) |
if (i + amt > stb->st_size) |
amt = stb->st_size - i; |
amt = stb->st_size - i; |
if (read(f, buf, amt) != amt) { |
if (read(f, buf, (size_t) amt) != (ssize_t) amt) { |
error("%s: File changed size", target); |
error("%s: File changed size", target); |
err(); |
err(); |
++goterr; |
++goterr; |
|
|
* this situation gracefully. |
* this situation gracefully. |
*/ |
*/ |
} |
} |
if (xwrite(rem_w, buf, amt) < 0) { |
if (xwrite(rem_w, buf, (size_t) amt) < 0) { |
error("%s: Error writing to client: %s", |
error("%s: Error writing to client: %s", |
target, SYSERR); |
target, SYSERR); |
err(); |
err(); |
++goterr; |
++goterr; |
|
|
* Return 0 if nothing happened. |
* Return 0 if nothing happened. |
* Return > 0 if anything is updated. |
* Return > 0 if anything is updated. |
*/ |
*/ |
static int rmchk(opts) |
static int |
opt_t opts; |
rmchk(opt_t opts) |
{ |
{ |
u_char *s; |
u_char *s; |
struct stat stb; |
struct stat stb; |
int didupdate = 0; |
int didupdate = 0; |
int n; |
int n; |
|
char targ[MAXPATHLEN*4]; |
|
|
debugmsg(DM_CALL, "rmchk()\n"); |
debugmsg(DM_CALL, "rmchk()\n"); |
|
|
|
|
* CC_NO -- file exists - DON'T remove. |
* CC_NO -- file exists - DON'T remove. |
* CC_YES -- file doesn't exist - REMOVE. |
* CC_YES -- file doesn't exist - REMOVE. |
*/ |
*/ |
snprintf(ptarget, target + sizeof(target) - ptarget, |
if (DECODE(targ, (char *) s) == -1) { |
"%s%s", (ptarget[-1] == '/' ? "" : "/"), s); |
error("rmchk: cannot decode file"); |
|
return(-1); |
|
} |
|
(void) snprintf(ptarget, |
|
sizeof(target) - (ptarget - target), |
|
"%s%s", |
|
(ptarget[-1] == '/' ? "" : "/"), |
|
targ); |
debugmsg(DM_MISC, "check %s\n", target); |
debugmsg(DM_MISC, "check %s\n", target); |
if (except(target)) |
if (except(target)) |
(void) sendcmd(CC_NO, NULL); |
(void) sendcmd(CC_NO, NULL); |
|
|
* Return 0 if nothing happened. |
* Return 0 if nothing happened. |
* Return > 0 if anything is updated. |
* Return > 0 if anything is updated. |
*/ |
*/ |
static int senddir(rname, opts, stb, user, group, destdir) |
static int |
char *rname; |
senddir(char *rname, opt_t opts, struct stat *stb, char *user, |
opt_t opts; |
char *group, int destdir) |
struct stat *stb; |
|
char *user, *group; |
|
int destdir; |
|
{ |
{ |
DIRENTRY *dp; |
DIRENTRY *dp; |
DIR *d; |
DIR *d; |
char *optarget, *cp; |
char *optarget, *cp; |
int len; |
int len; |
int didupdate = 0; |
int didupdate = 0; |
|
char ername[MAXPATHLEN*4]; |
|
|
/* |
/* |
* Don't descend into directory |
|
*/ |
|
if (IS_ON(opts, DO_NODESCEND)) |
|
return(0); |
|
|
|
if ((d = opendir(target)) == NULL) { |
|
error("%s: opendir failed: %s", target, SYSERR); |
|
return(-1); |
|
} |
|
|
|
/* |
|
* Send recvdir command in recvit() format. |
* Send recvdir command in recvit() format. |
*/ |
*/ |
|
ENCODE(ername, rname); |
(void) sendcmd(C_RECVDIR, "%o %04o 0 0 0 %s %s %s", |
(void) sendcmd(C_RECVDIR, "%o %04o 0 0 0 %s %s %s", |
opts, stb->st_mode & 07777, user, group, rname); |
opts, stb->st_mode & 07777, user, group, ername); |
if (response() < 0) |
if (response() < 0) |
return(-1); |
return(-1); |
|
|
|
/* |
|
* Don't descend into directory |
|
*/ |
|
if (IS_ON(opts, DO_NODESCEND)) |
|
return(0); |
|
|
if (IS_ON(opts, DO_REMOVE)) |
if (IS_ON(opts, DO_REMOVE)) |
if (rmchk(opts) > 0) |
if (rmchk(opts) > 0) |
++didupdate; |
++didupdate; |
|
|
|
if ((d = opendir(target)) == NULL) { |
|
error("%s: opendir failed: %s", target, SYSERR); |
|
return(-1); |
|
} |
|
|
optarget = ptarget; |
optarget = ptarget; |
len = ptarget - target; |
len = ptarget - target; |
while ((dp = readdir(d))) { |
while ((dp = readdir(d)) != NULL) { |
if (!strcmp(dp->d_name, ".") || |
if (!strcmp(dp->d_name, ".") || |
!strcmp(dp->d_name, "..")) |
!strcmp(dp->d_name, "..")) |
continue; |
continue; |
|
|
if (ptarget[-1] != '/') |
if (ptarget[-1] != '/') |
*ptarget++ = '/'; |
*ptarget++ = '/'; |
cp = dp->d_name; |
cp = dp->d_name; |
while ((*ptarget++ = *cp++)) |
while ((*ptarget++ = *cp++) != '\0') |
; |
continue; |
ptarget--; |
ptarget--; |
if (sendit(dp->d_name, opts, destdir) > 0) |
if (sendit(dp->d_name, opts, destdir) > 0) |
didupdate = 1; |
didupdate = 1; |
|
|
/* |
/* |
* Send a link |
* Send a link |
*/ |
*/ |
static int sendlink(rname, opts, stb, user, group, destdir) |
static int |
char *rname; |
sendlink(char *rname, opt_t opts, struct stat *stb, char *user, |
opt_t opts; |
char *group, int destdir) |
struct stat *stb; |
|
char *user; |
|
char *group; |
|
int destdir; |
|
{ |
{ |
int f, n; |
int f, n; |
static char tbuf[BUFSIZ]; |
static char tbuf[BUFSIZ]; |
char lbuf[MAXPATHLEN]; |
char lbuf[MAXPATHLEN]; |
u_char *s; |
u_char *s; |
|
char ername[MAXPATHLEN*4]; |
|
|
debugmsg(DM_CALL, "sendlink(%s, %x, stb, %d)\n", rname, opts, destdir); |
debugmsg(DM_CALL, "sendlink(%s, %x, stb, %d)\n", rname, opts, destdir); |
|
|
|
|
/* |
/* |
* Gather and send basic link info |
* Gather and send basic link info |
*/ |
*/ |
|
ENCODE(ername, rname); |
(void) sendcmd(C_RECVSYMLINK, "%o %04o %ld %ld %ld %s %s %s", |
(void) sendcmd(C_RECVSYMLINK, "%o %04o %ld %ld %ld %s %s %s", |
opts, stb->st_mode & 07777, |
opts, stb->st_mode & 07777, (long) stb->st_size, |
(long) stb->st_size, |
|
stb->st_mtime, stb->st_atime, |
stb->st_mtime, stb->st_atime, |
user, group, rname); |
user, group, ername); |
if (response() < 0) |
if (response() < 0) |
return(-1); |
return(-1); |
|
|
|
|
error("%s: readlink failed", target); |
error("%s: readlink failed", target); |
err(); |
err(); |
} |
} |
(void) snprintf(tbuf, sizeof tbuf, "%.*s", (int) stb->st_size, lbuf); |
(void) snprintf(tbuf, sizeof(tbuf), "%.*s", (int) stb->st_size, lbuf); |
(void) sendcmd(C_NONE, "%s\n", tbuf); |
ENCODE(ername, tbuf); |
|
(void) sendcmd(C_NONE, "%s\n", ername); |
|
|
if (n != stb->st_size) { |
if (n != stb->st_size) { |
error("%s: file changed size", target); |
error("%s: file changed size", target); |
|
|
* US_NOENT - remote doesn't exist |
* US_NOENT - remote doesn't exist |
* US_OUTDATE - out of date |
* US_OUTDATE - out of date |
* US_DOCOMP - comparing binaries to determine if out of date |
* US_DOCOMP - comparing binaries to determine if out of date |
* US_MODE - File modes do not match |
* US_CHMOG - File modes or ownership do not match |
*/ |
*/ |
static int update(rname, opts, statp) |
static int |
char *rname; |
update(char *rname, opt_t opts, struct stat *statp) |
opt_t opts; |
|
struct stat *statp; |
|
{ |
{ |
off_t size; |
off_t size; |
time_t mtime; |
time_t mtime; |
|
|
char *owner = NULL, *group = NULL; |
char *owner = NULL, *group = NULL; |
int done, n; |
int done, n; |
u_char *cp; |
u_char *cp; |
|
char ername[MAXPATHLEN*4]; |
|
|
debugmsg(DM_CALL, "update(%s, 0x%x, 0x%x)\n", rname, opts, statp); |
debugmsg(DM_CALL, "update(%s, 0x%x, 0x%x)\n", rname, opts, statp); |
|
|
|
|
/* |
/* |
* Check to see if the file exists on the remote machine. |
* Check to see if the file exists on the remote machine. |
*/ |
*/ |
(void) sendcmd(C_QUERY, "%s", rname); |
ENCODE(ername, rname); |
|
(void) sendcmd(C_QUERY, "%s", ername); |
|
|
for (done = 0; !done;) { |
for (done = 0; !done;) { |
n = remline(cp = respbuff, sizeof(respbuff), TRUE); |
n = remline(cp = respbuff, sizeof(respbuff), TRUE); |
|
|
/* Goto top of loop */ |
/* Goto top of loop */ |
|
|
default: |
default: |
error("update: unexpected response to query '%s'", cp); |
error("update: unexpected response to query '%s'", respbuff); |
return(US_NOTHING); |
return(US_NOTHING); |
} |
} |
} |
} |
|
|
/* |
/* |
* Parse size |
* Parse size |
*/ |
*/ |
size = strtol(cp, (char **)&cp, 10); |
size = (off_t) strtol(cp, (char **)&cp, 10); |
if (*cp++ != ' ') { |
if (*cp++ != ' ') { |
error("update: size not delimited"); |
error("update: size not delimited"); |
return(US_NOTHING); |
return(US_NOTHING); |
|
|
|
|
debugmsg(DM_MISC, "update(%s,) local mode %04o remote mode %04o\n", |
debugmsg(DM_MISC, "update(%s,) local mode %04o remote mode %04o\n", |
rname, lmode, rmode); |
rname, lmode, rmode); |
debugmsg(DM_MISC, "update(%s,) size %d mtime %d owner '%s' grp '%s'\n", |
debugmsg(DM_MISC, "update(%s,) size %ld mtime %d owner '%s' grp '%s'\n", |
rname, (int) size, mtime, owner, group); |
rname, (long) size, mtime, owner, group); |
|
|
if (statp->st_mtime != mtime) { |
if (statp->st_mtime != mtime) { |
if (statp->st_mtime < mtime && IS_ON(opts, DO_YOUNGER)) { |
if (statp->st_mtime < mtime && IS_ON(opts, DO_YOUNGER)) { |
|
|
return(US_OUTDATE); |
return(US_OUTDATE); |
} |
} |
|
|
/* |
if (statp->st_size != size) { |
* If the mode of a file does not match the local mode, the |
debugmsg(DM_MISC, "size does not match (%ld != %ld).\n", |
* whole file is updated. This is done both to insure that |
(long) statp->st_size, (long) size); |
* a bogus version of the file has not been installed and to |
return(US_OUTDATE); |
* avoid having to handle weird cases of chmod'ing symlinks |
} |
* and such. |
|
*/ |
|
if (!IS_ON(opts, DO_NOCHKMODE) && lmode != rmode) { |
if (!IS_ON(opts, DO_NOCHKMODE) && lmode != rmode) { |
debugmsg(DM_MISC, "modes do not match (%04o != %04o).\n", |
debugmsg(DM_MISC, "modes do not match (%04o != %04o).\n", |
lmode, rmode); |
lmode, rmode); |
return(US_OUTDATE); |
return(US_CHMOG); |
} |
} |
|
|
if (statp->st_size != size) { |
|
debugmsg(DM_MISC, "size does not match (%d != %d).\n", |
|
(int) statp->st_size, size); |
|
return(US_OUTDATE); |
|
} |
|
|
|
/* |
/* |
* Check ownership |
* Check ownership |
|
|
"owner does not match (%s != %s).\n", |
"owner does not match (%s != %s).\n", |
getusername(statp->st_uid, |
getusername(statp->st_uid, |
target, opts), owner); |
target, opts), owner); |
return(US_OUTDATE); |
return(US_CHMOG); |
} |
} |
} else { |
} else { |
/* |
/* |
* Check numerically. |
* Check numerically. |
* Allow negative numbers. |
* Allow negative numbers. |
*/ |
*/ |
while (*owner && !isdigit(*owner) && (*owner != '-')) |
while (*owner && !isdigit((unsigned char)*owner) && |
|
(*owner != '-')) |
++owner; |
++owner; |
if (owner && atoi(owner) != statp->st_uid) { |
if (owner && (UID_T) atoi(owner) != statp->st_uid) { |
debugmsg(DM_MISC, |
debugmsg(DM_MISC, |
"owner does not match (%d != %s).\n", |
"owner does not match (%d != %s).\n", |
statp->st_uid, owner); |
statp->st_uid, owner); |
return(US_OUTDATE); |
return(US_CHMOG); |
} |
} |
} |
} |
} |
} |
|
|
"group does not match (%s != %s).\n", |
"group does not match (%s != %s).\n", |
getgroupname(statp->st_gid, |
getgroupname(statp->st_gid, |
target, opts), group); |
target, opts), group); |
return(US_OUTDATE); |
return(US_CHMOG); |
} |
} |
} else { |
} else { |
/* Check numerically */ |
/* Check numerically */ |
/* Allow negative gid */ |
/* Allow negative gid */ |
while (*group && !isdigit(*group) && (*group != '-')) |
while (*group && !isdigit((unsigned char) *group) && |
|
(*group != '-')) |
++group; |
++group; |
if (group && atoi(group) != statp->st_gid) { |
if (group && (UID_T) atoi(group) != statp->st_gid) { |
debugmsg(DM_MISC, |
debugmsg(DM_MISC, |
"group does not match (%d != %s).\n", |
"group does not match (%d != %s).\n", |
statp->st_gid, group); |
statp->st_gid, group); |
return(US_OUTDATE); |
return(US_CHMOG); |
} |
} |
} |
} |
} |
} |
|
|
/* |
/* |
* Stat a file |
* Stat a file |
*/ |
*/ |
static int dostat(file, statbuf, opts) |
static int |
char *file; |
dostat(char *file, struct stat *statbuf, opt_t opts) |
struct stat *statbuf; |
|
opt_t opts; |
|
{ |
{ |
int s; |
int s; |
|
|
|
|
} |
} |
|
|
/* |
/* |
|
* We need to just change file info. |
|
*/ |
|
static int |
|
statupdate(int u, char *target, opt_t opts, char *rname, int destdir, |
|
struct stat *st, char *user, char *group) |
|
{ |
|
int rv = 0; |
|
char ername[MAXPATHLEN*4]; |
|
int lmode = st->st_mode & 07777; |
|
|
|
if (u == US_CHMOG) { |
|
if (IS_ON(opts, DO_VERIFY)) { |
|
message(MT_INFO, |
|
"%s: need to change to perm %04o, owner %s, group %s", |
|
target, lmode, user, group); |
|
runspecial(target, opts, rname, destdir); |
|
} |
|
else { |
|
message(MT_CHANGE, "%s: change to perm %04o, owner %s, group %s", |
|
target, lmode, user, group); |
|
ENCODE(ername, rname); |
|
(void) sendcmd(C_CHMOG, "%o %04o %s %s %s", |
|
opts, lmode, user, group, ername); |
|
(void) response(); |
|
} |
|
rv = 1; |
|
} |
|
return(rv); |
|
} |
|
|
|
|
|
/* |
|
* We need to install/update: |
|
*/ |
|
static int |
|
fullupdate(int u, char *target, opt_t opts, char *rname, int destdir, |
|
struct stat *st, char *user, char *group) |
|
{ |
|
/* |
|
* No entry - need to install |
|
*/ |
|
if (u == US_NOENT) { |
|
if (IS_ON(opts, DO_VERIFY)) { |
|
message(MT_INFO, "%s: need to install", target); |
|
runspecial(target, opts, rname, destdir); |
|
return(1); |
|
} |
|
if (!IS_ON(opts, DO_QUIET)) |
|
message(MT_CHANGE, "%s: installing", target); |
|
FLAG_OFF(opts, (DO_COMPARE|DO_REMOVE)); |
|
} |
|
|
|
/* |
|
* Handle special file types, including directories and symlinks |
|
*/ |
|
if (S_ISDIR(st->st_mode)) { |
|
if (senddir(rname, opts, st, user, group, destdir) > 0) |
|
return(1); |
|
return(0); |
|
} else if (S_ISLNK(st->st_mode)) { |
|
if (u == US_NOENT) |
|
FLAG_ON(opts, DO_COMPARE); |
|
/* |
|
* Since we always send link info to the server |
|
* so the server can determine if the remote link |
|
* is correct, we never get any acknowledge meant |
|
* from the server whether the link was really |
|
* updated or not. |
|
*/ |
|
(void) sendlink(rname, opts, st, user, group, destdir); |
|
return(0); |
|
} else if (S_ISREG(st->st_mode)) { |
|
if (u == US_OUTDATE) { |
|
if (IS_ON(opts, DO_VERIFY)) { |
|
message(MT_INFO, "%s: need to update", target); |
|
runspecial(target, opts, rname, destdir); |
|
return(1); |
|
} |
|
if (!IS_ON(opts, DO_QUIET)) |
|
message(MT_CHANGE, "%s: updating", target); |
|
} |
|
return (sendfile(rname, opts, st, user, group, destdir) == 0); |
|
} else { |
|
message(MT_INFO, "%s: unknown file type 0%o", target, |
|
st->st_mode); |
|
return(0); |
|
} |
|
} |
|
|
|
/* |
* Transfer the file or directory in target[]. |
* Transfer the file or directory in target[]. |
* rname is the name of the file on the remote host. |
* rname is the name of the file on the remote host. |
* |
* |
|
|
* Return 0 if nothing happened. |
* Return 0 if nothing happened. |
* Return > 0 if anything is updated. |
* Return > 0 if anything is updated. |
*/ |
*/ |
static int sendit(rname, opts, destdir) |
static int |
char *rname; |
sendit(char *rname, opt_t opts, int destdir) |
opt_t opts; |
|
int destdir; |
|
{ |
{ |
static struct stat stb; |
static struct stat stb; |
extern struct subcmd *subcmds; |
|
char *user, *group; |
char *user, *group; |
int u, len; |
int u, len; |
int didupdate = 0; |
|
|
|
/* |
/* |
* Remove possible accidental newline |
* Remove possible accidental newline |
|
|
return(0); |
return(0); |
} |
} |
|
|
/* |
|
* File mode needs changing |
|
*/ |
|
if (u == US_MODE) { |
|
if (IS_ON(opts, DO_VERIFY)) { |
|
message(MT_INFO, "%s: need to chmod to %04o", |
|
target, stb.st_mode & 07777); |
|
runspecial(target, opts, rname, destdir); |
|
return(1); |
|
} |
|
message(MT_CHANGE, "%s: chmod to %04o", |
|
target, stb.st_mode & 07777); |
|
(void) sendcmd(C_CHMOD, "%o %04o %s", |
|
opts, stb.st_mode & 07777, rname); |
|
(void) response(); |
|
return(1); |
|
} |
|
|
|
user = getusername(stb.st_uid, target, opts); |
user = getusername(stb.st_uid, target, opts); |
group = getgroupname(stb.st_gid, target, opts); |
group = getgroupname(stb.st_gid, target, opts); |
|
|
/* |
if (u == US_CHMOG && IS_OFF(opts, DO_UPDATEPERM)) |
* No entry - need to install |
u = US_OUTDATE; |
*/ |
|
if (u == US_NOENT) { |
|
if (IS_ON(opts, DO_VERIFY)) { |
|
message(MT_INFO, "%s: need to install", target); |
|
runspecial(target, opts, rname, destdir); |
|
return(1); |
|
} |
|
if (!IS_ON(opts, DO_QUIET)) |
|
message(MT_CHANGE, "%s: installing", target); |
|
FLAG_OFF(opts, (DO_COMPARE|DO_REMOVE)); |
|
} |
|
|
|
/* |
if (u == US_NOENT || u == US_OUTDATE || u == US_DOCOMP) |
* Handle special file types, including directories and symlinks |
return(fullupdate(u, target, opts, rname, destdir, &stb, |
*/ |
user, group)); |
if (S_ISDIR(stb.st_mode)) { |
|
if (senddir(rname, opts, &stb, user, group, destdir) > 0) |
|
didupdate = 1; |
|
} else if (S_ISLNK(stb.st_mode)) { |
|
if (u != US_NOENT) |
|
FLAG_ON(opts, DO_COMPARE); |
|
/* |
|
* Since we always send link info to the server |
|
* so the server can determine if the remote link |
|
* is correct, we never get any acknowledge meant |
|
* from the server whether the link was really |
|
* updated or not. |
|
*/ |
|
(void) sendlink(rname, opts, &stb, user, group, destdir); |
|
} else if (S_ISREG(stb.st_mode)) { |
|
if (u == US_OUTDATE) { |
|
if (IS_ON(opts, DO_VERIFY)) { |
|
message(MT_INFO, "%s: need to update", target); |
|
runspecial(target, opts, rname, destdir); |
|
return(1); |
|
} |
|
if (!IS_ON(opts, DO_QUIET)) |
|
message(MT_CHANGE, "%s: updating", target); |
|
} |
|
if (sendfile(rname, opts, &stb, user, group, destdir) == 0) |
|
didupdate = 1; |
|
} else |
|
error("%s: unknown file type", target); |
|
|
|
return(didupdate); |
if (u == US_CHMOG) |
|
return(statupdate(u, target, opts, rname, destdir, &stb, |
|
user, group)); |
|
|
|
return(0); |
} |
} |
|
|
/* |
/* |
* Remove temporary files and do any cleanup operations before exiting. |
* Remove temporary files and do any cleanup operations before exiting. |
*/ |
*/ |
extern void cleanup() |
void |
|
cleanup(int dummy) |
{ |
{ |
char *file; |
char *file; |
#ifdef USE_STATDB |
#ifdef USE_STATDB |
|
|
(void) unlink(statfile); |
(void) unlink(statfile); |
#endif |
#endif |
|
|
if ((file = getnotifyfile())) |
if ((file = getnotifyfile()) != NULL) |
(void) unlink(file); |
(void) unlink(file); |
} |
} |
|
|
|
|
* Return 0 if nothing updated. |
* Return 0 if nothing updated. |
* Return > 0 if something was updated. |
* Return > 0 if something was updated. |
*/ |
*/ |
extern int install(src, dest, ddir, destdir, opts) |
int |
char *src, *dest; |
install(char *src, char *dest, int ddir, int destdir, opt_t opts) |
int ddir, destdir; |
|
opt_t opts; |
|
{ |
{ |
static char destcopy[MAXPATHLEN]; |
static char destcopy[MAXPATHLEN]; |
char *rname; |
char *rname; |
int didupdate = 0; |
int didupdate = 0; |
|
char ername[MAXPATHLEN*4]; |
|
|
debugmsg(DM_CALL, |
debugmsg(DM_CALL, |
"install(src=%s,dest=%s,ddir=%d,destdir=%d,opts=%d) start\n", |
"install(src=%s,dest=%s,ddir=%d,destdir=%d,opts=%d) start\n", |
|
|
if (IS_ON(opts, DO_WHOLE)) |
if (IS_ON(opts, DO_WHOLE)) |
source[0] = CNULL; |
source[0] = CNULL; |
else |
else |
(void) strlcpy(source, src, sizeof source); |
(void) strlcpy(source, src, sizeof(source)); |
|
|
if (dest == NULL) { |
if (dest == NULL) { |
FLAG_OFF(opts, DO_WHOLE); /* WHOLE only useful if renaming */ |
FLAG_OFF(opts, DO_WHOLE); /* WHOLE only useful if renaming */ |
|
|
char *cp; |
char *cp; |
|
|
cp = getondistoptlist(opts); |
cp = getondistoptlist(opts); |
(void) snprintf(buff, sizeof buff, "%s%s%s %s %s", |
(void) snprintf(buff, sizeof(buff), "%s%s%s %s %s", |
IS_ON(opts, DO_VERIFY) ? "verify" : "install", |
IS_ON(opts, DO_VERIFY) ? "verify" : "install", |
(cp) ? " -o" : "", (cp) ? cp : "", |
(cp) ? " -o" : "", (cp) ? cp : "", |
src, dest); |
src, dest); |
|
|
/* |
/* |
* Pass the destination file/directory name to remote. |
* Pass the destination file/directory name to remote. |
*/ |
*/ |
|
ENCODE(ername, dest); |
if (ddir) |
if (ddir) |
(void) sendcmd(C_DIRTARGET, "%o %s", opts, dest); |
(void) sendcmd(C_DIRTARGET, "%o %s", opts, ername); |
else |
else |
(void) sendcmd(C_TARGET, "%o %s", opts, dest); |
(void) sendcmd(C_TARGET, "%o %s", opts, ername); |
if (response() < 0) |
if (response() < 0) |
return(-1); |
return(-1); |
|
|
|
|
* hardlink info. |
* hardlink info. |
*/ |
*/ |
if (destdir || (src && dest && strcmp(src, dest))) { |
if (destdir || (src && dest && strcmp(src, dest))) { |
(void) strlcpy(destcopy, dest, sizeof destcopy); |
(void) strlcpy(destcopy, dest, sizeof(destcopy)); |
Tdest = destcopy; |
Tdest = destcopy; |
} |
} |
|
|