1 |
|
|
/* $OpenBSD: client.c,v 1.36 2017/07/09 14:04:50 espie Exp $ */ |
2 |
|
|
|
3 |
|
|
/* |
4 |
|
|
* Copyright (c) 1983 Regents of the University of California. |
5 |
|
|
* All rights reserved. |
6 |
|
|
* |
7 |
|
|
* Redistribution and use in source and binary forms, with or without |
8 |
|
|
* modification, are permitted provided that the following conditions |
9 |
|
|
* are met: |
10 |
|
|
* 1. Redistributions of source code must retain the above copyright |
11 |
|
|
* notice, this list of conditions and the following disclaimer. |
12 |
|
|
* 2. Redistributions in binary form must reproduce the above copyright |
13 |
|
|
* notice, this list of conditions and the following disclaimer in the |
14 |
|
|
* documentation and/or other materials provided with the distribution. |
15 |
|
|
* 3. Neither the name of the University nor the names of its contributors |
16 |
|
|
* may be used to endorse or promote products derived from this software |
17 |
|
|
* without specific prior written permission. |
18 |
|
|
* |
19 |
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
20 |
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
21 |
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
22 |
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
23 |
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
24 |
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
25 |
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
26 |
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
27 |
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
28 |
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
29 |
|
|
* SUCH DAMAGE. |
30 |
|
|
*/ |
31 |
|
|
|
32 |
|
|
#include <ctype.h> |
33 |
|
|
#include <dirent.h> |
34 |
|
|
#include <errno.h> |
35 |
|
|
#include <fcntl.h> |
36 |
|
|
#include <limits.h> |
37 |
|
|
#include <stdlib.h> |
38 |
|
|
#include <string.h> |
39 |
|
|
#include <unistd.h> |
40 |
|
|
|
41 |
|
|
#include "client.h" |
42 |
|
|
#include "gram.h" |
43 |
|
|
|
44 |
|
|
/* |
45 |
|
|
* Routines used in client mode to communicate with remove server. |
46 |
|
|
*/ |
47 |
|
|
|
48 |
|
|
|
49 |
|
|
/* |
50 |
|
|
* Update status |
51 |
|
|
*/ |
52 |
|
|
#define US_NOTHING 0 /* No update needed */ |
53 |
|
|
#define US_NOENT 1 /* Entry does not exist */ |
54 |
|
|
#define US_OUTDATE 2 /* Entry is out of date */ |
55 |
|
|
#define US_DOCOMP 3 /* Do a binary comparison */ |
56 |
|
|
#define US_CHMOG 4 /* Modes or ownership of file differ */ |
57 |
|
|
|
58 |
|
|
struct linkbuf *ihead = NULL; /* list of files with more than one link */ |
59 |
|
|
char buf[BUFSIZ]; /* general purpose buffer */ |
60 |
|
|
u_char respbuff[BUFSIZ]; /* Response buffer */ |
61 |
|
|
char target[BUFSIZ]; /* target/source directory name */ |
62 |
|
|
char source[BUFSIZ]; /* source directory name */ |
63 |
|
|
char *ptarget; /* pointer to end of target name */ |
64 |
|
|
char *Tdest; /* pointer to last T dest*/ |
65 |
|
|
struct namelist *updfilelist = NULL; /* List of updated files */ |
66 |
|
|
|
67 |
|
|
static void runspecial(char *, opt_t, char *, int); |
68 |
|
|
static void addcmdspecialfile(char *, char *, int); |
69 |
|
|
static void freecmdspecialfiles(void); |
70 |
|
|
static struct linkbuf *linkinfo(struct stat *); |
71 |
|
|
static int sendhardlink(opt_t, struct linkbuf *, char *, int); |
72 |
|
|
static int sendfile(char *, opt_t, struct stat *, char *, char *, int); |
73 |
|
|
static int rmchk(opt_t); |
74 |
|
|
static int senddir(char *, opt_t, struct stat *, char *, char *, int); |
75 |
|
|
static int sendlink(char *, opt_t, struct stat *, char *, char *, int); |
76 |
|
|
static int update(char *, opt_t, struct stat *); |
77 |
|
|
static int dostat(char *, struct stat *, opt_t); |
78 |
|
|
static int statupdate(int, char *, opt_t, char *, int, struct stat *, char *, char *); |
79 |
|
|
static int fullupdate(int, char *, opt_t, char *, int, struct stat *, char *, char *); |
80 |
|
|
static int sendit(char *, opt_t, int); |
81 |
|
|
|
82 |
|
|
/* |
83 |
|
|
* return remote file pathname (relative from target) |
84 |
|
|
*/ |
85 |
|
|
char * |
86 |
|
|
remfilename(char *src, char *dest, char *path, char *rname, int destdir) |
87 |
|
|
{ |
88 |
|
|
char *lname, *cp; |
89 |
|
|
static char buff[BUFSIZ]; |
90 |
|
|
int srclen, pathlen; |
91 |
|
|
char *p; |
92 |
|
|
|
93 |
|
|
|
94 |
|
|
debugmsg(DM_MISC, |
95 |
|
|
"remfilename: src=%s dest=%s path=%s rname=%s destdir=%d\n", |
96 |
|
|
A(src), A(dest), A(path), A(rname), destdir); |
97 |
|
|
|
98 |
|
|
if (!dest) { |
99 |
|
|
debugmsg(DM_MISC, "remfilename: remote filename=%s\n", path); |
100 |
|
|
return(path); |
101 |
|
|
} |
102 |
|
|
|
103 |
|
|
if (!destdir) { |
104 |
|
|
debugmsg(DM_MISC, "remfilename: remote filename=%s\n", dest); |
105 |
|
|
return(dest); |
106 |
|
|
} |
107 |
|
|
|
108 |
|
|
buff[0] = CNULL; |
109 |
|
|
lname = buff; |
110 |
|
|
if (path && *path) { |
111 |
|
|
cp = strrchr(path, '/'); |
112 |
|
|
if (cp == NULL) |
113 |
|
|
(void) snprintf(buff, sizeof(buff), "%s/%s", dest, path); |
114 |
|
|
else { |
115 |
|
|
srclen = strlen(src); |
116 |
|
|
pathlen = strlen(path); |
117 |
|
|
if (srclen >= pathlen) |
118 |
|
|
cp++; /* xbasename(path) */ |
119 |
|
|
else { |
120 |
|
|
if (filelist && filelist->n_next == NULL) |
121 |
|
|
/* path relative to src */ |
122 |
|
|
cp = path + srclen; |
123 |
|
|
else { |
124 |
|
|
if ((p = strrchr(src, '/'))) |
125 |
|
|
cp = path + srclen - strlen(p); |
126 |
|
|
else |
127 |
|
|
cp = path; |
128 |
|
|
} |
129 |
|
|
} |
130 |
|
|
if ((*cp != '/') && *cp) |
131 |
|
|
(void) snprintf(buff, sizeof(buff), "%s/%s", |
132 |
|
|
dest, cp); |
133 |
|
|
else |
134 |
|
|
(void) snprintf(buff, sizeof(buff), "%s%s", |
135 |
|
|
dest, cp); |
136 |
|
|
} |
137 |
|
|
} else |
138 |
|
|
(void) strlcpy(lname, dest, buf + sizeof buff - lname); |
139 |
|
|
|
140 |
|
|
debugmsg(DM_MISC, "remfilename: remote filename=%s\n", lname); |
141 |
|
|
|
142 |
|
|
return(lname); |
143 |
|
|
} |
144 |
|
|
|
145 |
|
|
/* |
146 |
|
|
* Return true if name is in the list. |
147 |
|
|
*/ |
148 |
|
|
int |
149 |
|
|
inlist(struct namelist *list, char *file) |
150 |
|
|
{ |
151 |
|
|
struct namelist *nl; |
152 |
|
|
|
153 |
|
|
for (nl = list; nl != NULL; nl = nl->n_next) |
154 |
|
|
if (strcmp(file, nl->n_name) == 0) |
155 |
|
|
return(1); |
156 |
|
|
return(0); |
157 |
|
|
} |
158 |
|
|
|
159 |
|
|
/* |
160 |
|
|
* Run any special commands for this file |
161 |
|
|
*/ |
162 |
|
|
static void |
163 |
|
|
runspecial(char *starget, opt_t opts, char *rname, int destdir) |
164 |
|
|
{ |
165 |
|
|
struct subcmd *sc; |
166 |
|
|
char *rfile; |
167 |
|
|
|
168 |
|
|
rfile = remfilename(source, Tdest, target, rname, destdir); |
169 |
|
|
|
170 |
|
|
for (sc = subcmds; sc != NULL; sc = sc->sc_next) { |
171 |
|
|
if (sc->sc_type != SPECIAL) |
172 |
|
|
continue; |
173 |
|
|
if (sc->sc_args != NULL && !inlist(sc->sc_args, starget)) |
174 |
|
|
continue; |
175 |
|
|
message(MT_CHANGE, "special \"%s\"", sc->sc_name); |
176 |
|
|
if (IS_ON(opts, DO_VERIFY)) |
177 |
|
|
continue; |
178 |
|
|
(void) sendcmd(C_SPECIAL, |
179 |
|
|
"%s=%s;%s=%s;%s=%s;export %s %s %s;%s", |
180 |
|
|
E_LOCFILE, starget, |
181 |
|
|
E_REMFILE, rfile, |
182 |
|
|
E_BASEFILE, xbasename(rfile), |
183 |
|
|
E_LOCFILE, E_REMFILE, E_BASEFILE, |
184 |
|
|
sc->sc_name); |
185 |
|
|
while (response() > 0) |
186 |
|
|
; |
187 |
|
|
} |
188 |
|
|
} |
189 |
|
|
|
190 |
|
|
/* |
191 |
|
|
* If we're doing a target with a "cmdspecial" in it, then |
192 |
|
|
* save the name of the file being updated for use with "cmdspecial". |
193 |
|
|
*/ |
194 |
|
|
static void |
195 |
|
|
addcmdspecialfile(char *starget, char *rname, int destdir) |
196 |
|
|
{ |
197 |
|
|
char *rfile; |
198 |
|
|
struct namelist *new; |
199 |
|
|
struct subcmd *sc; |
200 |
|
|
int isokay = 0; |
201 |
|
|
|
202 |
|
|
rfile = remfilename(source, Tdest, target, rname, destdir); |
203 |
|
|
|
204 |
|
|
for (sc = subcmds; sc != NULL && !isokay; sc = sc->sc_next) { |
205 |
|
|
if (sc->sc_type != CMDSPECIAL) |
206 |
|
|
continue; |
207 |
|
|
if (sc->sc_args != NULL && !inlist(sc->sc_args, starget)) |
208 |
|
|
continue; |
209 |
|
|
isokay = TRUE; |
210 |
|
|
} |
211 |
|
|
|
212 |
|
|
if (isokay) { |
213 |
|
|
new = xmalloc(sizeof *new); |
214 |
|
|
new->n_name = xstrdup(rfile); |
215 |
|
|
new->n_regex = NULL; |
216 |
|
|
new->n_next = updfilelist; |
217 |
|
|
updfilelist = new; |
218 |
|
|
} |
219 |
|
|
} |
220 |
|
|
|
221 |
|
|
/* |
222 |
|
|
* Free the file list |
223 |
|
|
*/ |
224 |
|
|
static void |
225 |
|
|
freecmdspecialfiles(void) |
226 |
|
|
{ |
227 |
|
|
struct namelist *ptr, *save; |
228 |
|
|
|
229 |
|
|
for (ptr = updfilelist; ptr; ) { |
230 |
|
|
if (ptr->n_name) (void) free(ptr->n_name); |
231 |
|
|
save = ptr->n_next; |
232 |
|
|
(void) free(ptr); |
233 |
|
|
if (save) |
234 |
|
|
ptr = save->n_next; |
235 |
|
|
else |
236 |
|
|
ptr = NULL; |
237 |
|
|
} |
238 |
|
|
updfilelist = NULL; |
239 |
|
|
} |
240 |
|
|
|
241 |
|
|
/* |
242 |
|
|
* Run commands for an entire cmd |
243 |
|
|
*/ |
244 |
|
|
void |
245 |
|
|
runcmdspecial(struct cmd *cmd, opt_t opts) |
246 |
|
|
{ |
247 |
|
|
struct subcmd *sc; |
248 |
|
|
struct namelist *f; |
249 |
|
|
int first = TRUE; |
250 |
|
|
|
251 |
|
|
for (sc = cmd->c_cmds; sc != NULL; sc = sc->sc_next) { |
252 |
|
|
if (sc->sc_type != CMDSPECIAL) |
253 |
|
|
continue; |
254 |
|
|
message(MT_CHANGE, "cmdspecial \"%s\"", sc->sc_name); |
255 |
|
|
if (IS_ON(opts, DO_VERIFY)) |
256 |
|
|
continue; |
257 |
|
|
/* Send all the file names */ |
258 |
|
|
for (f = updfilelist; f != NULL; f = f->n_next) { |
259 |
|
|
if (first) { |
260 |
|
|
(void) sendcmd(C_CMDSPECIAL, NULL); |
261 |
|
|
if (response() < 0) |
262 |
|
|
return; |
263 |
|
|
first = FALSE; |
264 |
|
|
} |
265 |
|
|
(void) sendcmd(RC_FILE, "%s", f->n_name); |
266 |
|
|
if (response() < 0) |
267 |
|
|
return; |
268 |
|
|
} |
269 |
|
|
if (first) { |
270 |
|
|
(void) sendcmd(C_CMDSPECIAL, NULL); |
271 |
|
|
if (response() < 0) |
272 |
|
|
return; |
273 |
|
|
first = FALSE; |
274 |
|
|
} |
275 |
|
|
/* Send command to run and wait for it to complete */ |
276 |
|
|
(void) sendcmd(RC_COMMAND, "%s", sc->sc_name); |
277 |
|
|
while (response() > 0) |
278 |
|
|
; |
279 |
|
|
first = TRUE; /* Reset in case there are more CMDSPECIAL's */ |
280 |
|
|
} |
281 |
|
|
freecmdspecialfiles(); |
282 |
|
|
} |
283 |
|
|
|
284 |
|
|
/* |
285 |
|
|
* For security, reject filenames that contains a newline |
286 |
|
|
*/ |
287 |
|
|
int |
288 |
|
|
checkfilename(char *name) |
289 |
|
|
{ |
290 |
|
|
char *cp; |
291 |
|
|
|
292 |
|
|
if (strchr(name, '\n')) { |
293 |
|
|
for (cp = name; *cp; cp++) |
294 |
|
|
if (*cp == '\n') |
295 |
|
|
*cp = '?'; |
296 |
|
|
message(MT_NERROR, |
297 |
|
|
"Refuse to handle filename containing newline: %s", |
298 |
|
|
name); |
299 |
|
|
return(-1); |
300 |
|
|
} |
301 |
|
|
|
302 |
|
|
return(0); |
303 |
|
|
} |
304 |
|
|
|
305 |
|
|
void |
306 |
|
|
freelinkinfo(struct linkbuf *lp) |
307 |
|
|
{ |
308 |
|
|
free(lp->pathname); |
309 |
|
|
free(lp->src); |
310 |
|
|
free(lp->target); |
311 |
|
|
free(lp); |
312 |
|
|
} |
313 |
|
|
|
314 |
|
|
/* |
315 |
|
|
* Save and retrieve hard link info |
316 |
|
|
*/ |
317 |
|
|
static struct linkbuf * |
318 |
|
|
linkinfo(struct stat *statp) |
319 |
|
|
{ |
320 |
|
|
struct linkbuf *lp; |
321 |
|
|
|
322 |
|
|
/* XXX - linear search doesn't scale with many links */ |
323 |
|
|
for (lp = ihead; lp != NULL; lp = lp->nextp) |
324 |
|
|
if (lp->inum == statp->st_ino && lp->devnum == statp->st_dev) { |
325 |
|
|
lp->count--; |
326 |
|
|
return(lp); |
327 |
|
|
} |
328 |
|
|
|
329 |
|
|
lp = xmalloc(sizeof(*lp)); |
330 |
|
|
lp->nextp = ihead; |
331 |
|
|
ihead = lp; |
332 |
|
|
lp->inum = statp->st_ino; |
333 |
|
|
lp->devnum = statp->st_dev; |
334 |
|
|
lp->count = statp->st_nlink - 1; |
335 |
|
|
lp->pathname = xstrdup(target); |
336 |
|
|
lp->src = xstrdup(source); |
337 |
|
|
if (Tdest) |
338 |
|
|
lp->target = xstrdup(Tdest); |
339 |
|
|
else |
340 |
|
|
lp->target = NULL; |
341 |
|
|
|
342 |
|
|
return(NULL); |
343 |
|
|
} |
344 |
|
|
|
345 |
|
|
/* |
346 |
|
|
* Send a hardlink |
347 |
|
|
*/ |
348 |
|
|
static int |
349 |
|
|
sendhardlink(opt_t opts, struct linkbuf *lp, char *rname, int destdir) |
350 |
|
|
{ |
351 |
|
|
static char buff[PATH_MAX]; |
352 |
|
|
char *lname; /* name of file to link to */ |
353 |
|
|
char ername[PATH_MAX*4], elname[PATH_MAX*4]; |
354 |
|
|
|
355 |
|
|
debugmsg(DM_MISC, |
356 |
|
|
"sendhardlink: rname='%s' pathname='%s' src='%s' target='%s'\n", |
357 |
|
|
rname, lp->pathname ? lp->pathname : "", |
358 |
|
|
lp->src ? lp->src : "", lp->target ? lp->target : ""); |
359 |
|
|
|
360 |
|
|
if (lp->target == NULL) |
361 |
|
|
lname = lp->pathname; |
362 |
|
|
else { |
363 |
|
|
lname = buff; |
364 |
|
|
strlcpy(lname, remfilename(lp->src, lp->target, |
365 |
|
|
lp->pathname, rname, |
366 |
|
|
destdir), sizeof(buff)); |
367 |
|
|
debugmsg(DM_MISC, "sendhardlink: lname=%s\n", lname); |
368 |
|
|
} |
369 |
|
|
ENCODE(elname, lname); |
370 |
|
|
ENCODE(ername, rname); |
371 |
|
|
(void) sendcmd(C_RECVHARDLINK, "%o %s %s", opts, elname, ername); |
372 |
|
|
|
373 |
|
|
return(response()); |
374 |
|
|
} |
375 |
|
|
|
376 |
|
|
/* |
377 |
|
|
* Send a file |
378 |
|
|
*/ |
379 |
|
|
static int |
380 |
|
|
sendfile(char *rname, opt_t opts, struct stat *stb, char *user, |
381 |
|
|
char *group, int destdir) |
382 |
|
|
{ |
383 |
|
|
int goterr, f; |
384 |
|
|
off_t i; |
385 |
|
|
char ername[PATH_MAX*4]; |
386 |
|
|
|
387 |
|
|
if (stb->st_nlink > 1) { |
388 |
|
|
struct linkbuf *lp; |
389 |
|
|
|
390 |
|
|
if ((lp = linkinfo(stb)) != NULL) |
391 |
|
|
return(sendhardlink(opts, lp, rname, destdir)); |
392 |
|
|
} |
393 |
|
|
|
394 |
|
|
if ((f = open(target, O_RDONLY)) < 0) { |
395 |
|
|
error("%s: open for read failed: %s", target, SYSERR); |
396 |
|
|
return(-1); |
397 |
|
|
} |
398 |
|
|
|
399 |
|
|
/* |
400 |
|
|
* Send file info |
401 |
|
|
*/ |
402 |
|
|
ENCODE(ername, rname); |
403 |
|
|
|
404 |
|
|
(void) sendcmd(C_RECVREG, "%o %04o %lld %lld %lld %s %s %s", |
405 |
|
|
opts, stb->st_mode & 07777, (long long) stb->st_size, |
406 |
|
|
(long long)stb->st_mtime, (long long)stb->st_atime, |
407 |
|
|
user, group, ername); |
408 |
|
|
if (response() < 0) { |
409 |
|
|
(void) close(f); |
410 |
|
|
return(-1); |
411 |
|
|
} |
412 |
|
|
|
413 |
|
|
|
414 |
|
|
debugmsg(DM_MISC, "Send file '%s' %lld bytes\n", rname, |
415 |
|
|
(long long) stb->st_size); |
416 |
|
|
|
417 |
|
|
/* |
418 |
|
|
* Set remote time out alarm handler. |
419 |
|
|
*/ |
420 |
|
|
(void) signal(SIGALRM, sighandler); |
421 |
|
|
|
422 |
|
|
/* |
423 |
|
|
* Actually transfer the file |
424 |
|
|
*/ |
425 |
|
|
goterr = 0; |
426 |
|
|
for (i = 0; i < stb->st_size; i += BUFSIZ) { |
427 |
|
|
off_t amt = BUFSIZ; |
428 |
|
|
|
429 |
|
|
(void) alarm(rtimeout); |
430 |
|
|
if (i + amt > stb->st_size) |
431 |
|
|
amt = stb->st_size - i; |
432 |
|
|
if (read(f, buf, (size_t) amt) != (ssize_t) amt) { |
433 |
|
|
error("%s: File changed size", target); |
434 |
|
|
err(); |
435 |
|
|
++goterr; |
436 |
|
|
/* |
437 |
|
|
* XXX - We have to keep going because the |
438 |
|
|
* server expects to receive a fixed number |
439 |
|
|
* of bytes that we specified as the file size. |
440 |
|
|
* We need Out Of Band communication to handle |
441 |
|
|
* this situation gracefully. |
442 |
|
|
*/ |
443 |
|
|
} |
444 |
|
|
if (xwrite(rem_w, buf, (size_t) amt) < 0) { |
445 |
|
|
error("%s: Error writing to client: %s", |
446 |
|
|
target, SYSERR); |
447 |
|
|
err(); |
448 |
|
|
++goterr; |
449 |
|
|
break; |
450 |
|
|
} |
451 |
|
|
(void) alarm(0); |
452 |
|
|
} |
453 |
|
|
|
454 |
|
|
(void) alarm(0); /* Insure alarm is off */ |
455 |
|
|
(void) close(f); |
456 |
|
|
|
457 |
|
|
debugmsg(DM_MISC, "Send file '%s' %s.\n", |
458 |
|
|
(goterr) ? "failed" : "complete", rname); |
459 |
|
|
|
460 |
|
|
/* |
461 |
|
|
* Check for errors and end send |
462 |
|
|
*/ |
463 |
|
|
if (goterr) |
464 |
|
|
return(-1); |
465 |
|
|
else { |
466 |
|
|
ack(); |
467 |
|
|
f = response(); |
468 |
|
|
if (f < 0) |
469 |
|
|
return(-1); |
470 |
|
|
else if (f == 0 && IS_ON(opts, DO_COMPARE)) |
471 |
|
|
return(0); |
472 |
|
|
|
473 |
|
|
runspecial(target, opts, rname, destdir); |
474 |
|
|
addcmdspecialfile(target, rname, destdir); |
475 |
|
|
|
476 |
|
|
return(0); |
477 |
|
|
} |
478 |
|
|
} |
479 |
|
|
|
480 |
|
|
/* |
481 |
|
|
* Check for files on the machine being updated that are not on the master |
482 |
|
|
* machine and remove them. |
483 |
|
|
* |
484 |
|
|
* Return < 0 on error. |
485 |
|
|
* Return 0 if nothing happened. |
486 |
|
|
* Return > 0 if anything is updated. |
487 |
|
|
*/ |
488 |
|
|
static int |
489 |
|
|
rmchk(opt_t opts) |
490 |
|
|
{ |
491 |
|
|
u_char *s; |
492 |
|
|
struct stat stb; |
493 |
|
|
int didupdate = 0; |
494 |
|
|
int n; |
495 |
|
|
char targ[PATH_MAX*4]; |
496 |
|
|
|
497 |
|
|
debugmsg(DM_CALL, "rmchk()\n"); |
498 |
|
|
|
499 |
|
|
/* |
500 |
|
|
* Tell the remote to clean the files from the last directory sent. |
501 |
|
|
*/ |
502 |
|
|
(void) sendcmd(C_CLEAN, "%o", IS_ON(opts, DO_VERIFY)); |
503 |
|
|
if (response() < 0) |
504 |
|
|
return(-1); |
505 |
|
|
|
506 |
|
|
for ( ; ; ) { |
507 |
|
|
n = remline(s = respbuff, sizeof(respbuff), TRUE); |
508 |
|
|
if (n <= 0) { |
509 |
|
|
error("rmchk: unexpected control record"); |
510 |
|
|
return(didupdate); |
511 |
|
|
} |
512 |
|
|
|
513 |
|
|
switch (*s++) { |
514 |
|
|
case CC_QUERY: /* Query if file should be removed */ |
515 |
|
|
/* |
516 |
|
|
* Return the following codes to remove query. |
517 |
|
|
* CC_NO -- file exists - DON'T remove. |
518 |
|
|
* CC_YES -- file doesn't exist - REMOVE. |
519 |
|
|
*/ |
520 |
|
|
if (DECODE(targ, (char *) s) == -1) { |
521 |
|
|
error("rmchk: cannot decode file"); |
522 |
|
|
return(-1); |
523 |
|
|
} |
524 |
|
|
(void) snprintf(ptarget, |
525 |
|
|
sizeof(target) - (ptarget - target), |
526 |
|
|
"%s%s", |
527 |
|
|
(ptarget[-1] == '/' ? "" : "/"), |
528 |
|
|
targ); |
529 |
|
|
debugmsg(DM_MISC, "check %s\n", target); |
530 |
|
|
if (except(target)) |
531 |
|
|
(void) sendcmd(CC_NO, NULL); |
532 |
|
|
else if (lstat(target, &stb) < 0) { |
533 |
|
|
if (sendcmd(CC_YES, NULL) == 0) |
534 |
|
|
didupdate = 1; |
535 |
|
|
} else |
536 |
|
|
(void) sendcmd(CC_NO, NULL); |
537 |
|
|
break; |
538 |
|
|
|
539 |
|
|
case CC_END: |
540 |
|
|
*ptarget = CNULL; |
541 |
|
|
ack(); |
542 |
|
|
return(didupdate); |
543 |
|
|
|
544 |
|
|
case C_LOGMSG: |
545 |
|
|
if (n > 0) |
546 |
|
|
message(MT_INFO, "%s", s); |
547 |
|
|
break; |
548 |
|
|
|
549 |
|
|
case C_NOTEMSG: |
550 |
|
|
if (n > 0) |
551 |
|
|
message(MT_NOTICE, "%s", s); |
552 |
|
|
break; |
553 |
|
|
/* Goto top of loop */ |
554 |
|
|
|
555 |
|
|
case C_ERRMSG: |
556 |
|
|
message(MT_NERROR, "%s", s); |
557 |
|
|
return(didupdate); |
558 |
|
|
|
559 |
|
|
case C_FERRMSG: |
560 |
|
|
message(MT_FERROR, "%s", s); |
561 |
|
|
finish(); |
562 |
|
|
|
563 |
|
|
default: |
564 |
|
|
error("rmchk: unexpected response '%s'", respbuff); |
565 |
|
|
err(); |
566 |
|
|
} |
567 |
|
|
} |
568 |
|
|
/*NOTREACHED*/ |
569 |
|
|
} |
570 |
|
|
|
571 |
|
|
/* |
572 |
|
|
* Send a directory |
573 |
|
|
* |
574 |
|
|
* Return < 0 on error. |
575 |
|
|
* Return 0 if nothing happened. |
576 |
|
|
* Return > 0 if anything is updated. |
577 |
|
|
*/ |
578 |
|
|
static int |
579 |
|
|
senddir(char *rname, opt_t opts, struct stat *stb, char *user, |
580 |
|
|
char *group, int destdir) |
581 |
|
|
{ |
582 |
|
|
struct dirent *dp; |
583 |
|
|
DIR *d; |
584 |
|
|
char *optarget, *cp; |
585 |
|
|
int len; |
586 |
|
|
int didupdate = 0; |
587 |
|
|
char ername[PATH_MAX*4]; |
588 |
|
|
|
589 |
|
|
/* |
590 |
|
|
* Send recvdir command in recvit() format. |
591 |
|
|
*/ |
592 |
|
|
ENCODE(ername, rname); |
593 |
|
|
(void) sendcmd(C_RECVDIR, "%o %04o 0 0 0 %s %s %s", |
594 |
|
|
opts, stb->st_mode & 07777, user, group, ername); |
595 |
|
|
if (response() < 0) |
596 |
|
|
return(-1); |
597 |
|
|
|
598 |
|
|
optarget = ptarget; |
599 |
|
|
|
600 |
|
|
/* |
601 |
|
|
* Don't descend into directory |
602 |
|
|
*/ |
603 |
|
|
if (IS_ON(opts, DO_NODESCEND)) { |
604 |
|
|
didupdate = 0; |
605 |
|
|
goto out; |
606 |
|
|
} |
607 |
|
|
|
608 |
|
|
if (IS_ON(opts, DO_REMOVE)) |
609 |
|
|
if (rmchk(opts) > 0) |
610 |
|
|
++didupdate; |
611 |
|
|
|
612 |
|
|
if ((d = opendir(target)) == NULL) { |
613 |
|
|
error("%s: opendir failed: %s", target, SYSERR); |
614 |
|
|
didupdate = -1; |
615 |
|
|
goto out; |
616 |
|
|
} |
617 |
|
|
|
618 |
|
|
len = ptarget - target; |
619 |
|
|
while ((dp = readdir(d)) != NULL) { |
620 |
|
|
if (!strcmp(dp->d_name, ".") || |
621 |
|
|
!strcmp(dp->d_name, "..")) |
622 |
|
|
continue; |
623 |
|
|
if (len + 1 + (int) strlen(dp->d_name) >= PATH_MAX - 1) { |
624 |
|
|
error("%s/%s: Name too long", target, |
625 |
|
|
dp->d_name); |
626 |
|
|
continue; |
627 |
|
|
} |
628 |
|
|
ptarget = optarget; |
629 |
|
|
if (ptarget[-1] != '/') |
630 |
|
|
*ptarget++ = '/'; |
631 |
|
|
cp = dp->d_name; |
632 |
|
|
while ((*ptarget++ = *cp++) != '\0') |
633 |
|
|
continue; |
634 |
|
|
ptarget--; |
635 |
|
|
if (sendit(dp->d_name, opts, destdir) > 0) |
636 |
|
|
didupdate = 1; |
637 |
|
|
} |
638 |
|
|
(void) closedir(d); |
639 |
|
|
|
640 |
|
|
out: |
641 |
|
|
(void) sendcmd(C_END, NULL); |
642 |
|
|
(void) response(); |
643 |
|
|
|
644 |
|
|
ptarget = optarget; |
645 |
|
|
*ptarget = CNULL; |
646 |
|
|
|
647 |
|
|
return(didupdate); |
648 |
|
|
} |
649 |
|
|
|
650 |
|
|
/* |
651 |
|
|
* Send a link |
652 |
|
|
*/ |
653 |
|
|
static int |
654 |
|
|
sendlink(char *rname, opt_t opts, struct stat *stb, char *user, |
655 |
|
|
char *group, int destdir) |
656 |
|
|
{ |
657 |
|
|
int f, n; |
658 |
|
|
static char tbuf[BUFSIZ]; |
659 |
|
|
char lbuf[PATH_MAX]; |
660 |
|
|
u_char *s; |
661 |
|
|
char ername[PATH_MAX*4]; |
662 |
|
|
|
663 |
|
|
debugmsg(DM_CALL, "sendlink(%s, %#x, stb, %d)\n", rname, opts, destdir); |
664 |
|
|
|
665 |
|
|
if (stb->st_nlink > 1) { |
666 |
|
|
struct linkbuf *lp; |
667 |
|
|
|
668 |
|
|
if ((lp = linkinfo(stb)) != NULL) |
669 |
|
|
return(sendhardlink(opts, lp, rname, destdir)); |
670 |
|
|
} |
671 |
|
|
|
672 |
|
|
/* |
673 |
|
|
* Gather and send basic link info |
674 |
|
|
*/ |
675 |
|
|
ENCODE(ername, rname); |
676 |
|
|
(void) sendcmd(C_RECVSYMLINK, "%o %04o %lld %lld %lld %s %s %s", |
677 |
|
|
opts, stb->st_mode & 07777, (long long) stb->st_size, |
678 |
|
|
(long long)stb->st_mtime, (long long)stb->st_atime, |
679 |
|
|
user, group, ername); |
680 |
|
|
if (response() < 0) |
681 |
|
|
return(-1); |
682 |
|
|
|
683 |
|
|
/* |
684 |
|
|
* Gather and send additional link info |
685 |
|
|
*/ |
686 |
|
|
if ((n = readlink(target, lbuf, sizeof(lbuf)-1)) != -1) |
687 |
|
|
lbuf[n] = '\0'; |
688 |
|
|
else { |
689 |
|
|
error("%s: readlink failed", target); |
690 |
|
|
err(); |
691 |
|
|
} |
692 |
|
|
(void) snprintf(tbuf, sizeof(tbuf), "%.*s", (int) stb->st_size, lbuf); |
693 |
|
|
ENCODE(ername, tbuf); |
694 |
|
|
(void) sendcmd(C_NONE, "%s\n", ername); |
695 |
|
|
|
696 |
|
|
if (n != stb->st_size) { |
697 |
|
|
error("%s: file changed size", target); |
698 |
|
|
err(); |
699 |
|
|
} else |
700 |
|
|
ack(); |
701 |
|
|
|
702 |
|
|
/* |
703 |
|
|
* Check response |
704 |
|
|
*/ |
705 |
|
|
f = response(); |
706 |
|
|
if (f < 0) |
707 |
|
|
return(-1); |
708 |
|
|
else if (f == 0 && IS_ON(opts, DO_COMPARE)) |
709 |
|
|
return(0); |
710 |
|
|
|
711 |
|
|
/* |
712 |
|
|
* Read and process responses from server. |
713 |
|
|
* The server may send multiple messages regarding |
714 |
|
|
* file deletes if the remote target is a directory. |
715 |
|
|
*/ |
716 |
|
|
for (;;) { |
717 |
|
|
n = remline(s = respbuff, sizeof(respbuff), TRUE); |
718 |
|
|
if (n == -1) /* normal EOF */ |
719 |
|
|
return(0); |
720 |
|
|
if (n == 0) { |
721 |
|
|
error("expected control record"); |
722 |
|
|
continue; |
723 |
|
|
} |
724 |
|
|
|
725 |
|
|
switch (*s++) { |
726 |
|
|
case C_END: /* End of send operation */ |
727 |
|
|
*ptarget = CNULL; |
728 |
|
|
ack(); |
729 |
|
|
runspecial(target, opts, rname, destdir); |
730 |
|
|
addcmdspecialfile(target, rname, destdir); |
731 |
|
|
return(0); |
732 |
|
|
|
733 |
|
|
case C_LOGMSG: |
734 |
|
|
if (n > 0) |
735 |
|
|
message(MT_INFO, "%s", s); |
736 |
|
|
break; |
737 |
|
|
|
738 |
|
|
case C_NOTEMSG: |
739 |
|
|
if (n > 0) |
740 |
|
|
message(MT_NOTICE, "%s", s); |
741 |
|
|
break; |
742 |
|
|
/* Goto top of loop */ |
743 |
|
|
|
744 |
|
|
case C_ERRMSG: |
745 |
|
|
message(MT_NERROR, "%s", s); |
746 |
|
|
return(-1); |
747 |
|
|
|
748 |
|
|
case C_FERRMSG: |
749 |
|
|
message(MT_FERROR, "%s", s); |
750 |
|
|
finish(); |
751 |
|
|
|
752 |
|
|
default: |
753 |
|
|
error("install link: unexpected response '%s'", |
754 |
|
|
respbuff); |
755 |
|
|
err(); |
756 |
|
|
} |
757 |
|
|
} |
758 |
|
|
/*NOTREACHED*/ |
759 |
|
|
} |
760 |
|
|
|
761 |
|
|
/* |
762 |
|
|
* Check to see if file needs to be updated on the remote machine. |
763 |
|
|
* Returns: |
764 |
|
|
* US_NOTHING - no update |
765 |
|
|
* US_NOENT - remote doesn't exist |
766 |
|
|
* US_OUTDATE - out of date |
767 |
|
|
* US_DOCOMP - comparing binaries to determine if out of date |
768 |
|
|
* US_CHMOG - File modes or ownership do not match |
769 |
|
|
*/ |
770 |
|
|
static int |
771 |
|
|
update(char *rname, opt_t opts, struct stat *statp) |
772 |
|
|
{ |
773 |
|
|
off_t size; |
774 |
|
|
time_t mtime; |
775 |
|
|
unsigned short lmode; |
776 |
|
|
unsigned short rmode; |
777 |
|
|
char *owner = NULL, *group = NULL; |
778 |
|
|
int done, n; |
779 |
|
|
u_char *cp; |
780 |
|
|
char ername[PATH_MAX*4]; |
781 |
|
|
|
782 |
|
|
debugmsg(DM_CALL, "update(%s, %#x, %p)\n", rname, opts, statp); |
783 |
|
|
|
784 |
|
|
switch (statp->st_mode & S_IFMT) { |
785 |
|
|
case S_IFBLK: |
786 |
|
|
debugmsg(DM_MISC, "%s is a block special; skipping\n", target); |
787 |
|
|
return(US_NOTHING); |
788 |
|
|
case S_IFCHR: |
789 |
|
|
debugmsg(DM_MISC, "%s is a character special; skipping\n", |
790 |
|
|
target); |
791 |
|
|
return(US_NOTHING); |
792 |
|
|
case S_IFIFO: |
793 |
|
|
debugmsg(DM_MISC, "%s is a fifo; skipping\n", target); |
794 |
|
|
return(US_NOTHING); |
795 |
|
|
case S_IFSOCK: |
796 |
|
|
debugmsg(DM_MISC, "%s is a socket; skipping\n", target); |
797 |
|
|
return(US_NOTHING); |
798 |
|
|
} |
799 |
|
|
|
800 |
|
|
if (IS_ON(opts, DO_NOEXEC)) |
801 |
|
|
if (isexec(target, statp)) { |
802 |
|
|
debugmsg(DM_MISC, "%s is an executable\n", target); |
803 |
|
|
return(US_NOTHING); |
804 |
|
|
} |
805 |
|
|
|
806 |
|
|
/* |
807 |
|
|
* Check to see if the file exists on the remote machine. |
808 |
|
|
*/ |
809 |
|
|
ENCODE(ername, rname); |
810 |
|
|
(void) sendcmd(C_QUERY, "%s", ername); |
811 |
|
|
|
812 |
|
|
for (done = 0; !done;) { |
813 |
|
|
n = remline(cp = respbuff, sizeof(respbuff), TRUE); |
814 |
|
|
if (n <= 0) { |
815 |
|
|
error("update: unexpected control record in response to query"); |
816 |
|
|
return(US_NOTHING); |
817 |
|
|
} |
818 |
|
|
|
819 |
|
|
switch (*cp++) { |
820 |
|
|
case QC_ONNFS: /* Resides on a NFS */ |
821 |
|
|
debugmsg(DM_MISC, |
822 |
|
|
"update: %s is on a NFS. Skipping...\n", |
823 |
|
|
rname); |
824 |
|
|
return(US_NOTHING); |
825 |
|
|
|
826 |
|
|
case QC_SYM: /* Is a symbolic link */ |
827 |
|
|
debugmsg(DM_MISC, |
828 |
|
|
"update: %s is a symlink. Skipping...\n", |
829 |
|
|
rname); |
830 |
|
|
return(US_NOTHING); |
831 |
|
|
|
832 |
|
|
case QC_ONRO: /* Resides on a Read-Only fs */ |
833 |
|
|
debugmsg(DM_MISC, |
834 |
|
|
"update: %s is on a RO fs. Skipping...\n", |
835 |
|
|
rname); |
836 |
|
|
return(US_NOTHING); |
837 |
|
|
|
838 |
|
|
case QC_YES: |
839 |
|
|
done = 1; |
840 |
|
|
break; |
841 |
|
|
|
842 |
|
|
case QC_NO: /* file doesn't exist so install it */ |
843 |
|
|
return(US_NOENT); |
844 |
|
|
|
845 |
|
|
case C_ERRMSG: |
846 |
|
|
if (cp) |
847 |
|
|
message(MT_NERROR, "%s", cp); |
848 |
|
|
return(US_NOTHING); |
849 |
|
|
|
850 |
|
|
case C_FERRMSG: |
851 |
|
|
if (cp) |
852 |
|
|
message(MT_FERROR, "%s", cp); |
853 |
|
|
finish(); |
854 |
|
|
|
855 |
|
|
case C_NOTEMSG: |
856 |
|
|
if (cp) |
857 |
|
|
message(MT_NOTICE, "%s", cp); |
858 |
|
|
break; |
859 |
|
|
/* Goto top of loop */ |
860 |
|
|
|
861 |
|
|
default: |
862 |
|
|
error("update: unexpected response to query '%s'", respbuff); |
863 |
|
|
return(US_NOTHING); |
864 |
|
|
} |
865 |
|
|
} |
866 |
|
|
|
867 |
|
|
/* |
868 |
|
|
* Target exists, but no other info passed |
869 |
|
|
*/ |
870 |
|
|
if (n <= 1 || !S_ISREG(statp->st_mode)) |
871 |
|
|
return(US_OUTDATE); |
872 |
|
|
|
873 |
|
|
if (IS_ON(opts, DO_COMPARE)) |
874 |
|
|
return(US_DOCOMP); |
875 |
|
|
|
876 |
|
|
/* |
877 |
|
|
* Parse size |
878 |
|
|
*/ |
879 |
|
|
size = (off_t) strtoll(cp, (char **)&cp, 10); |
880 |
|
|
if (*cp++ != ' ') { |
881 |
|
|
error("update: size not delimited"); |
882 |
|
|
return(US_NOTHING); |
883 |
|
|
} |
884 |
|
|
|
885 |
|
|
/* |
886 |
|
|
* Parse mtime |
887 |
|
|
*/ |
888 |
|
|
mtime = strtol(cp, (char **)&cp, 10); |
889 |
|
|
if (*cp++ != ' ') { |
890 |
|
|
error("update: mtime not delimited"); |
891 |
|
|
return(US_NOTHING); |
892 |
|
|
} |
893 |
|
|
|
894 |
|
|
/* |
895 |
|
|
* Parse remote file mode |
896 |
|
|
*/ |
897 |
|
|
rmode = strtol(cp, (char **)&cp, 8); |
898 |
|
|
if (cp && *cp) |
899 |
|
|
++cp; |
900 |
|
|
|
901 |
|
|
/* |
902 |
|
|
* Be backwards compatible |
903 |
|
|
*/ |
904 |
|
|
if (cp && *cp != CNULL) { |
905 |
|
|
/* |
906 |
|
|
* Parse remote file owner |
907 |
|
|
*/ |
908 |
|
|
owner = strtok((char *)cp, " "); |
909 |
|
|
if (owner == NULL) { |
910 |
|
|
error("update: owner not delimited"); |
911 |
|
|
return(US_NOTHING); |
912 |
|
|
} |
913 |
|
|
|
914 |
|
|
/* |
915 |
|
|
* Parse remote file group |
916 |
|
|
*/ |
917 |
|
|
group = strtok(NULL, " "); |
918 |
|
|
if (group == NULL) { |
919 |
|
|
error("update: group not delimited"); |
920 |
|
|
return(US_NOTHING); |
921 |
|
|
} |
922 |
|
|
} |
923 |
|
|
|
924 |
|
|
/* |
925 |
|
|
* File needs to be updated? |
926 |
|
|
*/ |
927 |
|
|
lmode = statp->st_mode & 07777; |
928 |
|
|
|
929 |
|
|
debugmsg(DM_MISC, "update(%s,) local mode %#04o remote mode %#04o\n", |
930 |
|
|
rname, lmode, rmode); |
931 |
|
|
debugmsg(DM_MISC, "update(%s,) size %lld mtime %lld owner '%s' grp '%s'" |
932 |
|
|
"\n", rname, (long long) size, (long long)mtime, owner, group); |
933 |
|
|
|
934 |
|
|
if (statp->st_mtime != mtime) { |
935 |
|
|
if (statp->st_mtime < mtime && IS_ON(opts, DO_YOUNGER)) { |
936 |
|
|
message(MT_WARNING, |
937 |
|
|
"%s: Warning: remote copy is newer", |
938 |
|
|
target); |
939 |
|
|
return(US_NOTHING); |
940 |
|
|
} |
941 |
|
|
return(US_OUTDATE); |
942 |
|
|
} |
943 |
|
|
|
944 |
|
|
if (statp->st_size != size) { |
945 |
|
|
debugmsg(DM_MISC, "size does not match (%lld != %lld).\n", |
946 |
|
|
(long long) statp->st_size, (long long) size); |
947 |
|
|
return(US_OUTDATE); |
948 |
|
|
} |
949 |
|
|
|
950 |
|
|
if (!IS_ON(opts, DO_NOCHKMODE) && lmode != rmode) { |
951 |
|
|
debugmsg(DM_MISC, "modes do not match (%#04o != %#04o).\n", |
952 |
|
|
lmode, rmode); |
953 |
|
|
return(US_CHMOG); |
954 |
|
|
} |
955 |
|
|
|
956 |
|
|
|
957 |
|
|
/* |
958 |
|
|
* Check ownership |
959 |
|
|
*/ |
960 |
|
|
if (!IS_ON(opts, DO_NOCHKOWNER) && owner) { |
961 |
|
|
if (!IS_ON(opts, DO_NUMCHKOWNER)) { |
962 |
|
|
/* Check by string compare */ |
963 |
|
|
if (strcmp(owner, getusername(statp->st_uid, |
964 |
|
|
target, opts)) != 0) { |
965 |
|
|
debugmsg(DM_MISC, |
966 |
|
|
"owner does not match (%s != %s).\n", |
967 |
|
|
getusername(statp->st_uid, |
968 |
|
|
target, opts), owner); |
969 |
|
|
return(US_CHMOG); |
970 |
|
|
} |
971 |
|
|
} else { |
972 |
|
|
/* |
973 |
|
|
* Check numerically. |
974 |
|
|
* Allow negative numbers. |
975 |
|
|
*/ |
976 |
|
|
while (*owner && !isdigit((unsigned char)*owner) && |
977 |
|
|
(*owner != '-')) |
978 |
|
|
++owner; |
979 |
|
|
if (owner && (uid_t)atoi(owner) != statp->st_uid) { |
980 |
|
|
debugmsg(DM_MISC, |
981 |
|
|
"owner does not match (%d != %s).\n", |
982 |
|
|
statp->st_uid, owner); |
983 |
|
|
return(US_CHMOG); |
984 |
|
|
} |
985 |
|
|
} |
986 |
|
|
} |
987 |
|
|
|
988 |
|
|
if (!IS_ON(opts, DO_NOCHKGROUP) && group) { |
989 |
|
|
if (!IS_ON(opts, DO_NUMCHKGROUP)) { |
990 |
|
|
/* Check by string compare */ |
991 |
|
|
if (strcmp(group, getgroupname(statp->st_gid, |
992 |
|
|
target, opts)) != 0) { |
993 |
|
|
debugmsg(DM_MISC, |
994 |
|
|
"group does not match (%s != %s).\n", |
995 |
|
|
getgroupname(statp->st_gid, |
996 |
|
|
target, opts), group); |
997 |
|
|
return(US_CHMOG); |
998 |
|
|
} |
999 |
|
|
} else { |
1000 |
|
|
/* Check numerically */ |
1001 |
|
|
/* Allow negative gid */ |
1002 |
|
|
while (*group && !isdigit((unsigned char) *group) && |
1003 |
|
|
(*group != '-')) |
1004 |
|
|
++group; |
1005 |
|
|
if (group && (gid_t)atoi(group) != statp->st_gid) { |
1006 |
|
|
debugmsg(DM_MISC, |
1007 |
|
|
"group does not match (%d != %s).\n", |
1008 |
|
|
statp->st_gid, group); |
1009 |
|
|
return(US_CHMOG); |
1010 |
|
|
} |
1011 |
|
|
} |
1012 |
|
|
} |
1013 |
|
|
|
1014 |
|
|
return(US_NOTHING); |
1015 |
|
|
} |
1016 |
|
|
|
1017 |
|
|
/* |
1018 |
|
|
* Stat a file |
1019 |
|
|
*/ |
1020 |
|
|
static int |
1021 |
|
|
dostat(char *file, struct stat *statbuf, opt_t opts) |
1022 |
|
|
{ |
1023 |
|
|
int s; |
1024 |
|
|
|
1025 |
|
|
if (IS_ON(opts, DO_FOLLOW)) |
1026 |
|
|
s = stat(file, statbuf); |
1027 |
|
|
else |
1028 |
|
|
s = lstat(file, statbuf); |
1029 |
|
|
|
1030 |
|
|
if (s < 0) |
1031 |
|
|
error("%s: %s failed: %s", file, |
1032 |
|
|
IS_ON(opts, DO_FOLLOW) ? "stat" : "lstat", SYSERR); |
1033 |
|
|
return(s); |
1034 |
|
|
} |
1035 |
|
|
|
1036 |
|
|
/* |
1037 |
|
|
* We need to just change file info. |
1038 |
|
|
*/ |
1039 |
|
|
static int |
1040 |
|
|
statupdate(int u, char *starget, opt_t opts, char *rname, int destdir, |
1041 |
|
|
struct stat *st, char *user, char *group) |
1042 |
|
|
{ |
1043 |
|
|
int rv = 0; |
1044 |
|
|
char ername[PATH_MAX*4]; |
1045 |
|
|
int lmode = st->st_mode & 07777; |
1046 |
|
|
|
1047 |
|
|
if (u == US_CHMOG) { |
1048 |
|
|
if (IS_ON(opts, DO_VERIFY)) { |
1049 |
|
|
message(MT_INFO, |
1050 |
|
|
"%s: need to change to perm %#04o, owner %s, group %s", |
1051 |
|
|
starget, lmode, user, group); |
1052 |
|
|
runspecial(starget, opts, rname, destdir); |
1053 |
|
|
} |
1054 |
|
|
else { |
1055 |
|
|
message(MT_CHANGE, "%s: change to perm %#04o, owner %s, group %s", |
1056 |
|
|
starget, lmode, user, group); |
1057 |
|
|
ENCODE(ername, rname); |
1058 |
|
|
(void) sendcmd(C_CHMOG, "%o %04o %s %s %s", |
1059 |
|
|
opts, lmode, user, group, ername); |
1060 |
|
|
(void) response(); |
1061 |
|
|
} |
1062 |
|
|
rv = 1; |
1063 |
|
|
} |
1064 |
|
|
return(rv); |
1065 |
|
|
} |
1066 |
|
|
|
1067 |
|
|
|
1068 |
|
|
/* |
1069 |
|
|
* We need to install/update: |
1070 |
|
|
*/ |
1071 |
|
|
static int |
1072 |
|
|
fullupdate(int u, char *starget, opt_t opts, char *rname, int destdir, |
1073 |
|
|
struct stat *st, char *user, char *group) |
1074 |
|
|
{ |
1075 |
|
|
/* |
1076 |
|
|
* No entry - need to install |
1077 |
|
|
*/ |
1078 |
|
|
if (u == US_NOENT) { |
1079 |
|
|
if (IS_ON(opts, DO_VERIFY)) { |
1080 |
|
|
message(MT_INFO, "%s: need to install", starget); |
1081 |
|
|
runspecial(starget, opts, rname, destdir); |
1082 |
|
|
return(1); |
1083 |
|
|
} |
1084 |
|
|
if (!IS_ON(opts, DO_QUIET)) |
1085 |
|
|
message(MT_CHANGE, "%s: installing", starget); |
1086 |
|
|
FLAG_OFF(opts, (DO_COMPARE|DO_REMOVE)); |
1087 |
|
|
} |
1088 |
|
|
|
1089 |
|
|
/* |
1090 |
|
|
* Handle special file types, including directories and symlinks |
1091 |
|
|
*/ |
1092 |
|
|
if (S_ISDIR(st->st_mode)) { |
1093 |
|
|
if (senddir(rname, opts, st, user, group, destdir) > 0) |
1094 |
|
|
return(1); |
1095 |
|
|
return(0); |
1096 |
|
|
} else if (S_ISLNK(st->st_mode)) { |
1097 |
|
|
if (u == US_NOENT) |
1098 |
|
|
FLAG_ON(opts, DO_COMPARE); |
1099 |
|
|
/* |
1100 |
|
|
* Since we always send link info to the server |
1101 |
|
|
* so the server can determine if the remote link |
1102 |
|
|
* is correct, we never get any acknowledgement |
1103 |
|
|
* from the server whether the link was really |
1104 |
|
|
* updated or not. |
1105 |
|
|
*/ |
1106 |
|
|
(void) sendlink(rname, opts, st, user, group, destdir); |
1107 |
|
|
return(0); |
1108 |
|
|
} else if (S_ISREG(st->st_mode)) { |
1109 |
|
|
if (u == US_OUTDATE) { |
1110 |
|
|
if (IS_ON(opts, DO_VERIFY)) { |
1111 |
|
|
message(MT_INFO, "%s: need to update", starget); |
1112 |
|
|
runspecial(starget, opts, rname, destdir); |
1113 |
|
|
return(1); |
1114 |
|
|
} |
1115 |
|
|
if (!IS_ON(opts, DO_QUIET)) |
1116 |
|
|
message(MT_CHANGE, "%s: updating", starget); |
1117 |
|
|
} |
1118 |
|
|
return (sendfile(rname, opts, st, user, group, destdir) == 0); |
1119 |
|
|
} else { |
1120 |
|
|
message(MT_INFO, "%s: unknown file type %#o", starget, |
1121 |
|
|
st->st_mode); |
1122 |
|
|
return(0); |
1123 |
|
|
} |
1124 |
|
|
} |
1125 |
|
|
|
1126 |
|
|
/* |
1127 |
|
|
* Transfer the file or directory in target[]. |
1128 |
|
|
* rname is the name of the file on the remote host. |
1129 |
|
|
* |
1130 |
|
|
* Return < 0 on error. |
1131 |
|
|
* Return 0 if nothing happened. |
1132 |
|
|
* Return > 0 if anything is updated. |
1133 |
|
|
*/ |
1134 |
|
|
static int |
1135 |
|
|
sendit(char *rname, opt_t opts, int destdir) |
1136 |
|
|
{ |
1137 |
|
|
static struct stat stb; |
1138 |
|
|
char *user, *group; |
1139 |
|
|
int u, len; |
1140 |
|
|
|
1141 |
|
|
/* |
1142 |
|
|
* Remove possible accidental newline |
1143 |
|
|
*/ |
1144 |
|
|
len = strlen(rname); |
1145 |
|
|
if (len > 0 && rname[len-1] == '\n') |
1146 |
|
|
rname[len-1] = CNULL; |
1147 |
|
|
|
1148 |
|
|
if (checkfilename(rname) != 0) |
1149 |
|
|
return(-1); |
1150 |
|
|
|
1151 |
|
|
debugmsg(DM_CALL, "sendit(%s, %#x) called\n", rname, opts); |
1152 |
|
|
|
1153 |
|
|
if (except(target)) |
1154 |
|
|
return(0); |
1155 |
|
|
|
1156 |
|
|
if (dostat(target, &stb, opts) < 0) |
1157 |
|
|
return(-1); |
1158 |
|
|
|
1159 |
|
|
/* |
1160 |
|
|
* Does rname need updating? |
1161 |
|
|
*/ |
1162 |
|
|
u = update(rname, opts, &stb); |
1163 |
|
|
debugmsg(DM_MISC, "sendit(%s, %#x): update status of %s is %d\n", |
1164 |
|
|
rname, opts, target, u); |
1165 |
|
|
|
1166 |
|
|
/* |
1167 |
|
|
* Don't need to update the file, but we may need to save hardlink |
1168 |
|
|
* info. |
1169 |
|
|
*/ |
1170 |
|
|
if (u == US_NOTHING) { |
1171 |
|
|
if (S_ISREG(stb.st_mode) && stb.st_nlink > 1) |
1172 |
|
|
(void) linkinfo(&stb); |
1173 |
|
|
return(0); |
1174 |
|
|
} |
1175 |
|
|
|
1176 |
|
|
user = getusername(stb.st_uid, target, opts); |
1177 |
|
|
group = getgroupname(stb.st_gid, target, opts); |
1178 |
|
|
|
1179 |
|
|
if (u == US_CHMOG && IS_OFF(opts, DO_UPDATEPERM)) |
1180 |
|
|
u = US_OUTDATE; |
1181 |
|
|
|
1182 |
|
|
if (u == US_NOENT || u == US_OUTDATE || u == US_DOCOMP) |
1183 |
|
|
return(fullupdate(u, target, opts, rname, destdir, &stb, |
1184 |
|
|
user, group)); |
1185 |
|
|
|
1186 |
|
|
if (u == US_CHMOG) |
1187 |
|
|
return(statupdate(u, target, opts, rname, destdir, &stb, |
1188 |
|
|
user, group)); |
1189 |
|
|
|
1190 |
|
|
return(0); |
1191 |
|
|
} |
1192 |
|
|
|
1193 |
|
|
/* |
1194 |
|
|
* Remove temporary files and do any cleanup operations before exiting. |
1195 |
|
|
*/ |
1196 |
|
|
void |
1197 |
|
|
cleanup(int dummy) |
1198 |
|
|
{ |
1199 |
|
|
char *file; |
1200 |
|
|
|
1201 |
|
|
if ((file = getnotifyfile()) != NULL) |
1202 |
|
|
(void) unlink(file); |
1203 |
|
|
} |
1204 |
|
|
|
1205 |
|
|
/* |
1206 |
|
|
* Update the file(s) if they are different. |
1207 |
|
|
* destdir = 1 if destination should be a directory |
1208 |
|
|
* (i.e., more than one source is being copied to the same destination). |
1209 |
|
|
* |
1210 |
|
|
* Return < 0 on error. |
1211 |
|
|
* Return 0 if nothing updated. |
1212 |
|
|
* Return > 0 if something was updated. |
1213 |
|
|
*/ |
1214 |
|
|
int |
1215 |
|
|
install(char *src, char *dest, int ddir, int destdir, opt_t opts) |
1216 |
|
|
{ |
1217 |
|
|
static char destcopy[PATH_MAX]; |
1218 |
|
|
char *rname; |
1219 |
|
|
int didupdate = 0; |
1220 |
|
|
char ername[PATH_MAX*4]; |
1221 |
|
|
|
1222 |
|
|
debugmsg(DM_CALL, |
1223 |
|
|
"install(src=%s,dest=%s,ddir=%d,destdir=%d,opts=%#x) start\n", |
1224 |
|
|
(src?src:"NULL"), (dest?dest:"NULL"), ddir, destdir, opts); |
1225 |
|
|
/* |
1226 |
|
|
* Save source name |
1227 |
|
|
*/ |
1228 |
|
|
if (IS_ON(opts, DO_WHOLE)) |
1229 |
|
|
source[0] = CNULL; |
1230 |
|
|
else |
1231 |
|
|
(void) strlcpy(source, src, sizeof(source)); |
1232 |
|
|
|
1233 |
|
|
if (dest == NULL) { |
1234 |
|
|
FLAG_OFF(opts, DO_WHOLE); /* WHOLE only useful if renaming */ |
1235 |
|
|
dest = src; |
1236 |
|
|
} |
1237 |
|
|
|
1238 |
|
|
if (checkfilename(dest) != 0) |
1239 |
|
|
return(-1); |
1240 |
|
|
|
1241 |
|
|
if (nflag || debug) { |
1242 |
|
|
static char buff[BUFSIZ]; |
1243 |
|
|
char *cp; |
1244 |
|
|
|
1245 |
|
|
cp = getondistoptlist(opts); |
1246 |
|
|
(void) snprintf(buff, sizeof(buff), "%s%s%s %s %s", |
1247 |
|
|
IS_ON(opts, DO_VERIFY) ? "verify" : "install", |
1248 |
|
|
(cp) ? " -o" : "", (cp) ? cp : "", |
1249 |
|
|
src, dest); |
1250 |
|
|
if (nflag) { |
1251 |
|
|
printf("%s\n", buff); |
1252 |
|
|
return(0); |
1253 |
|
|
} else |
1254 |
|
|
debugmsg(DM_MISC, "%s\n", buff); |
1255 |
|
|
} |
1256 |
|
|
|
1257 |
|
|
rname = exptilde(target, src, sizeof(target)); |
1258 |
|
|
if (rname == NULL) |
1259 |
|
|
return(-1); |
1260 |
|
|
ptarget = target; |
1261 |
|
|
while (*ptarget) |
1262 |
|
|
ptarget++; |
1263 |
|
|
/* |
1264 |
|
|
* If we are renaming a directory and we want to preserve |
1265 |
|
|
* the directory hierarchy (-w), we must strip off the leading |
1266 |
|
|
* directory name and preserve the rest. |
1267 |
|
|
*/ |
1268 |
|
|
if (IS_ON(opts, DO_WHOLE)) { |
1269 |
|
|
while (*rname == '/') |
1270 |
|
|
rname++; |
1271 |
|
|
ddir = 1; |
1272 |
|
|
destdir = 1; |
1273 |
|
|
} else { |
1274 |
|
|
rname = strrchr(target, '/'); |
1275 |
|
|
/* Check if no '/' or target ends in '/' */ |
1276 |
|
|
if (rname == NULL || |
1277 |
|
|
rname+1 == NULL || |
1278 |
|
|
*(rname+1) == CNULL) |
1279 |
|
|
rname = target; |
1280 |
|
|
else |
1281 |
|
|
rname++; |
1282 |
|
|
} |
1283 |
|
|
|
1284 |
|
|
debugmsg(DM_MISC, |
1285 |
|
|
"install: target=%s src=%s rname=%s dest='%s' destdir=%d, ddir=%d\n", |
1286 |
|
|
target, source, rname, dest, destdir, ddir); |
1287 |
|
|
|
1288 |
|
|
/* |
1289 |
|
|
* Pass the destination file/directory name to remote. |
1290 |
|
|
*/ |
1291 |
|
|
ENCODE(ername, dest); |
1292 |
|
|
if (ddir) |
1293 |
|
|
(void) sendcmd(C_DIRTARGET, "%o %s", opts, ername); |
1294 |
|
|
else |
1295 |
|
|
(void) sendcmd(C_TARGET, "%o %s", opts, ername); |
1296 |
|
|
if (response() < 0) |
1297 |
|
|
return(-1); |
1298 |
|
|
|
1299 |
|
|
/* |
1300 |
|
|
* Save the name of the remote target destination if we are |
1301 |
|
|
* in WHOLE mode (destdir > 0) or if the source and destination |
1302 |
|
|
* are not the same. This info will be used later for maintaining |
1303 |
|
|
* hardlink info. |
1304 |
|
|
*/ |
1305 |
|
|
if (destdir || (src && dest && strcmp(src, dest))) { |
1306 |
|
|
(void) strlcpy(destcopy, dest, sizeof(destcopy)); |
1307 |
|
|
Tdest = destcopy; |
1308 |
|
|
} |
1309 |
|
|
|
1310 |
|
|
didupdate = sendit(rname, opts, destdir); |
1311 |
|
|
Tdest = 0; |
1312 |
|
|
|
1313 |
|
|
return(didupdate); |
1314 |
|
|
} |