xref: /openbsd-src/usr.bin/rsync/uploader.c (revision 394aa39a7ef19d70e75e3c4a648921afbf99e288)
1*394aa39aSclaudio /*	$OpenBSD: uploader.c,v 1.36 2023/11/27 11:28:39 claudio Exp $ */
260a32ee9Sbenno /*
360a32ee9Sbenno  * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
4dbc83512Sflorian  * Copyright (c) 2019 Florian Obser <florian@openbsd.org>
560a32ee9Sbenno  *
660a32ee9Sbenno  * Permission to use, copy, modify, and distribute this software for any
760a32ee9Sbenno  * purpose with or without fee is hereby granted, provided that the above
860a32ee9Sbenno  * copyright notice and this permission notice appear in all copies.
960a32ee9Sbenno  *
1060a32ee9Sbenno  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1160a32ee9Sbenno  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1260a32ee9Sbenno  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1360a32ee9Sbenno  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1460a32ee9Sbenno  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1560a32ee9Sbenno  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1660a32ee9Sbenno  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1760a32ee9Sbenno  */
1860a32ee9Sbenno #include <sys/mman.h>
1960a32ee9Sbenno #include <sys/stat.h>
2060a32ee9Sbenno 
2160a32ee9Sbenno #include <assert.h>
22e397242dSclaudio #include <err.h>
2360a32ee9Sbenno #include <errno.h>
2460a32ee9Sbenno #include <fcntl.h>
2560a32ee9Sbenno #include <inttypes.h>
2660a32ee9Sbenno #include <math.h>
2760a32ee9Sbenno #include <stdio.h>
2860a32ee9Sbenno #include <stdlib.h>
2960a32ee9Sbenno #include <string.h>
3060a32ee9Sbenno #include <time.h>
3160a32ee9Sbenno #include <unistd.h>
3260a32ee9Sbenno 
3360a32ee9Sbenno #include "extern.h"
3460a32ee9Sbenno 
3560a32ee9Sbenno enum	uploadst {
3660a32ee9Sbenno 	UPLOAD_FIND_NEXT = 0, /* find next to upload to sender */
37a982ab77Sclaudio 	UPLOAD_WRITE, /* wait to write to sender */
3860a32ee9Sbenno 	UPLOAD_FINISHED /* nothing more to do in phase */
3960a32ee9Sbenno };
4060a32ee9Sbenno 
4160a32ee9Sbenno /*
4260a32ee9Sbenno  * Used to keep track of data flowing from the receiver to the sender.
4360a32ee9Sbenno  * This is managed by the receiver process.
4460a32ee9Sbenno  */
4560a32ee9Sbenno struct	upload {
4660a32ee9Sbenno 	enum uploadst	    state;
4760a32ee9Sbenno 	char		   *buf; /* if not NULL, pending upload */
4860a32ee9Sbenno 	size_t		    bufsz; /* size of buf */
4960a32ee9Sbenno 	size_t		    bufmax; /* maximum size of buf */
5060a32ee9Sbenno 	size_t		    bufpos; /* position in buf */
5160a32ee9Sbenno 	size_t		    idx; /* current transfer index */
5260a32ee9Sbenno 	mode_t		    oumask; /* umask for creating files */
53434f41cdSflorian 	char		   *root; /* destination directory path */
5460a32ee9Sbenno 	int		    rootfd; /* destination directory */
5560a32ee9Sbenno 	size_t		    csumlen; /* checksum length */
5660a32ee9Sbenno 	int		    fdout; /* write descriptor to sender */
5760a32ee9Sbenno 	const struct flist *fl; /* file list */
5860a32ee9Sbenno 	size_t		    flsz; /* size of file list */
5960a32ee9Sbenno 	int		   *newdir; /* non-zero if mkdir'd */
6060a32ee9Sbenno };
6160a32ee9Sbenno 
6260a32ee9Sbenno /*
6360a32ee9Sbenno  * Log a directory by emitting the file and a trailing slash, just to
6460a32ee9Sbenno  * show the operator that we're a directory.
6560a32ee9Sbenno  */
6660a32ee9Sbenno static void
log_dir(struct sess * sess,const struct flist * f)6760a32ee9Sbenno log_dir(struct sess *sess, const struct flist *f)
6860a32ee9Sbenno {
6960a32ee9Sbenno 	size_t	 sz;
7060a32ee9Sbenno 
7160a32ee9Sbenno 	if (sess->opts->server)
7260a32ee9Sbenno 		return;
7360a32ee9Sbenno 	sz = strlen(f->path);
7460a32ee9Sbenno 	assert(sz > 0);
75b2a7eac7Sbenno 	LOG1("%s%s", f->path, (f->path[sz - 1] == '/') ? "" : "/");
7660a32ee9Sbenno }
7760a32ee9Sbenno 
7860a32ee9Sbenno /*
7960a32ee9Sbenno  * Log a link by emitting the file and the target, just to show the
8060a32ee9Sbenno  * operator that we're a link.
8160a32ee9Sbenno  */
8260a32ee9Sbenno static void
log_symlink(struct sess * sess,const struct flist * f)83e107519eSclaudio log_symlink(struct sess *sess, const struct flist *f)
8460a32ee9Sbenno {
8560a32ee9Sbenno 
8660a32ee9Sbenno 	if (!sess->opts->server)
87b2a7eac7Sbenno 		LOG1("%s -> %s", f->path, f->link);
8860a32ee9Sbenno }
8960a32ee9Sbenno 
9060a32ee9Sbenno /*
9160a32ee9Sbenno  * Simply log the filename.
9260a32ee9Sbenno  */
9360a32ee9Sbenno static void
log_file(struct sess * sess,const struct flist * f)9460a32ee9Sbenno log_file(struct sess *sess, const struct flist *f)
9560a32ee9Sbenno {
9660a32ee9Sbenno 
9760a32ee9Sbenno 	if (!sess->opts->server)
98b2a7eac7Sbenno 		LOG1("%s", f->path);
9960a32ee9Sbenno }
10060a32ee9Sbenno 
10160a32ee9Sbenno /*
10260a32ee9Sbenno  * Prepare the overall block set's metadata.
10360a32ee9Sbenno  * We always have at least one block.
10460a32ee9Sbenno  * The block size is an important part of the algorithm.
10560a32ee9Sbenno  * I use the same heuristic as the reference rsync, but implemented in a
10660a32ee9Sbenno  * bit more of a straightforward way.
10760a32ee9Sbenno  * In general, the individual block length is the rounded square root of
10860a32ee9Sbenno  * the total file size.
10960a32ee9Sbenno  * The minimum block length is 700.
11060a32ee9Sbenno  */
11160a32ee9Sbenno static void
init_blkset(struct blkset * p,off_t sz)11260a32ee9Sbenno init_blkset(struct blkset *p, off_t sz)
11360a32ee9Sbenno {
11460a32ee9Sbenno 	double	 v;
11560a32ee9Sbenno 
11660a32ee9Sbenno 	if (sz >= (BLOCK_SIZE_MIN * BLOCK_SIZE_MIN)) {
11760a32ee9Sbenno 		/* Simple rounded-up integer square root. */
11860a32ee9Sbenno 
11960a32ee9Sbenno 		v = sqrt(sz);
12060a32ee9Sbenno 		p->len = ceil(v);
12160a32ee9Sbenno 
12260a32ee9Sbenno 		/*
12360a32ee9Sbenno 		 * Always be a multiple of eight.
12460a32ee9Sbenno 		 * There's no reason to do this, but rsync does.
12560a32ee9Sbenno 		 */
12660a32ee9Sbenno 
12760a32ee9Sbenno 		if ((p->len % 8) > 0)
12860a32ee9Sbenno 			p->len += 8 - (p->len % 8);
12960a32ee9Sbenno 	} else
13060a32ee9Sbenno 		p->len = BLOCK_SIZE_MIN;
13160a32ee9Sbenno 
13260a32ee9Sbenno 	p->size = sz;
133f1dcb30aSderaadt 	if ((p->blksz = sz / p->len) == 0)
13460a32ee9Sbenno 		p->rem = sz;
13560a32ee9Sbenno 	else
13660a32ee9Sbenno 		p->rem = sz % p->len;
13760a32ee9Sbenno 
13860a32ee9Sbenno 	/* If we have a remainder, then we need an extra block. */
13960a32ee9Sbenno 
14060a32ee9Sbenno 	if (p->rem)
14160a32ee9Sbenno 		p->blksz++;
14260a32ee9Sbenno }
14360a32ee9Sbenno 
14460a32ee9Sbenno /*
14560a32ee9Sbenno  * For each block, prepare the block's metadata.
14660a32ee9Sbenno  * We use the mapped "map" file to set our checksums.
14760a32ee9Sbenno  */
14860a32ee9Sbenno static void
init_blk(struct blk * p,const struct blkset * set,off_t offs,size_t idx,const void * map,const struct sess * sess)14960a32ee9Sbenno init_blk(struct blk *p, const struct blkset *set, off_t offs,
15060a32ee9Sbenno 	size_t idx, const void *map, const struct sess *sess)
15160a32ee9Sbenno {
15260a32ee9Sbenno 
15360a32ee9Sbenno 	p->idx = idx;
15417d693dbSclaudio 	/* Block length inherits for all but the last. */
15560a32ee9Sbenno 	p->len = idx < set->blksz - 1 ? set->len : set->rem;
15660a32ee9Sbenno 	p->offs = offs;
15760a32ee9Sbenno 
158c314c803Sbenno 	p->chksum_short = hash_fast(map, p->len);
159c314c803Sbenno 	hash_slow(map, p->len, p->chksum_long, sess);
16060a32ee9Sbenno }
16160a32ee9Sbenno 
16260a32ee9Sbenno /*
16355cb9f91Sbenno  * Handle a symbolic link.
16455cb9f91Sbenno  * If we encounter directories existing in the symbolic link's place,
16555cb9f91Sbenno  * then try to unlink the directory.
16655cb9f91Sbenno  * Otherwise, simply overwrite with the symbolic link by renaming.
16760a32ee9Sbenno  * Return <0 on failure 0 on success.
16860a32ee9Sbenno  */
16960a32ee9Sbenno static int
pre_symlink(struct upload * p,struct sess * sess)170e397242dSclaudio pre_symlink(struct upload *p, struct sess *sess)
17160a32ee9Sbenno {
17260a32ee9Sbenno 	struct stat		 st;
17360a32ee9Sbenno 	const struct flist	*f;
174dbed5971Sflorian 	int			 rc, newlink = 0, updatelink = 0;
175434f41cdSflorian 	char			*b, *temp = NULL;
17660a32ee9Sbenno 
17760a32ee9Sbenno 	f = &p->fl[p->idx];
17860a32ee9Sbenno 	assert(S_ISLNK(f->st.mode));
17960a32ee9Sbenno 
18060a32ee9Sbenno 	if (!sess->opts->preserve_links) {
181b2a7eac7Sbenno 		WARNX("%s: ignoring symlink", f->path);
18260a32ee9Sbenno 		return 0;
183a982ab77Sclaudio 	}
184a982ab77Sclaudio 	if (sess->opts->dry_run) {
185e107519eSclaudio 		log_symlink(sess, f);
18660a32ee9Sbenno 		return 0;
18760a32ee9Sbenno 	}
18860a32ee9Sbenno 
18955cb9f91Sbenno 	/*
19055cb9f91Sbenno 	 * See if the symlink already exists.
19155cb9f91Sbenno 	 * If it's a directory, then try to unlink the directory prior
19255cb9f91Sbenno 	 * to overwriting with a symbolic link.
19355cb9f91Sbenno 	 * If it's a non-directory, we just overwrite it.
19455cb9f91Sbenno 	 */
19560a32ee9Sbenno 
196f1dcb30aSderaadt 	assert(p->rootfd != -1);
19760a32ee9Sbenno 	rc = fstatat(p->rootfd, f->path, &st, AT_SYMLINK_NOFOLLOW);
198eab91beeSclaudio 
199eab91beeSclaudio 	if (rc == -1 && errno != ENOENT) {
200eab91beeSclaudio 		ERR("%s: fstatat", f->path);
201eab91beeSclaudio 		return -1;
202eab91beeSclaudio 	}
203f1dcb30aSderaadt 	if (rc != -1 && !S_ISLNK(st.st_mode)) {
20455cb9f91Sbenno 		if (S_ISDIR(st.st_mode) &&
20555cb9f91Sbenno 		    unlinkat(p->rootfd, f->path, AT_REMOVEDIR) == -1) {
206b2a7eac7Sbenno 			ERR("%s: unlinkat", f->path);
20760a32ee9Sbenno 			return -1;
208dbed5971Sflorian 		}
20955cb9f91Sbenno 		rc = -1;
21060a32ee9Sbenno 	}
21160a32ee9Sbenno 
21260a32ee9Sbenno 	/*
21360a32ee9Sbenno 	 * If the symbolic link already exists, then make sure that it
21460a32ee9Sbenno 	 * points to the correct place.
21560a32ee9Sbenno 	 */
21660a32ee9Sbenno 
217dbed5971Sflorian 	if (rc != -1) {
218ba617adaSbenno 		b = symlinkat_read(p->rootfd, f->path);
219f1dcb30aSderaadt 		if (b == NULL) {
220b2a7eac7Sbenno 			ERRX1("symlinkat_read");
22160a32ee9Sbenno 			return -1;
22260a32ee9Sbenno 		}
22360a32ee9Sbenno 		if (strcmp(f->link, b)) {
22460a32ee9Sbenno 			free(b);
22560a32ee9Sbenno 			b = NULL;
226b2a7eac7Sbenno 			LOG3("%s: updating symlink: %s", f->path, f->link);
227dbed5971Sflorian 			updatelink = 1;
228dbed5971Sflorian 		}
229dbed5971Sflorian 		free(b);
230dbed5971Sflorian 		b = NULL;
231dbed5971Sflorian 	}
232dbed5971Sflorian 
23355cb9f91Sbenno 	/*
23455cb9f91Sbenno 	 * Create the temporary file as a symbolic link, then rename the
23555cb9f91Sbenno 	 * temporary file as the real one, overwriting anything there.
23655cb9f91Sbenno 	 */
23755cb9f91Sbenno 
238dbed5971Sflorian 	if (rc == -1 || updatelink) {
239b2a7eac7Sbenno 		LOG3("%s: creating symlink: %s", f->path, f->link);
240ba617adaSbenno 		if (mktemplate(&temp, f->path, sess->opts->recursive) == -1) {
241b2a7eac7Sbenno 			ERRX1("mktemplate");
24260a32ee9Sbenno 			return -1;
24360a32ee9Sbenno 		}
244434f41cdSflorian 		if (mkstemplinkat(f->link, p->rootfd, temp) == NULL) {
245b2a7eac7Sbenno 			ERR("mkstemplinkat");
246434f41cdSflorian 			free(temp);
24760a32ee9Sbenno 			return -1;
24860a32ee9Sbenno 		}
24960a32ee9Sbenno 		newlink = 1;
25060a32ee9Sbenno 	}
25160a32ee9Sbenno 
25255cb9f91Sbenno 	rsync_set_metadata_at(sess, newlink,
25355cb9f91Sbenno 		p->rootfd, f, newlink ? temp : f->path);
25460a32ee9Sbenno 
255dbed5971Sflorian 	if (newlink) {
256434f41cdSflorian 		if (renameat(p->rootfd, temp, p->rootfd, f->path) == -1) {
257b2a7eac7Sbenno 			ERR("%s: renameat %s", temp, f->path);
258434f41cdSflorian 			(void)unlinkat(p->rootfd, temp, 0);
259434f41cdSflorian 			free(temp);
260dbed5971Sflorian 			return -1;
261dbed5971Sflorian 		}
262434f41cdSflorian 		free(temp);
263dbed5971Sflorian 	}
264dbed5971Sflorian 
265e107519eSclaudio 	log_symlink(sess, f);
26660a32ee9Sbenno 	return 0;
26760a32ee9Sbenno }
26860a32ee9Sbenno 
26960a32ee9Sbenno /*
270e397242dSclaudio  * See pre_symlink(), but for devices.
27155cb9f91Sbenno  * FIXME: this is very similar to the other pre_xxx() functions.
272434f41cdSflorian  * Return <0 on failure 0 on success.
273434f41cdSflorian  */
274434f41cdSflorian static int
pre_dev(struct upload * p,struct sess * sess)275434f41cdSflorian pre_dev(struct upload *p, struct sess *sess)
276434f41cdSflorian {
277434f41cdSflorian 	struct stat		 st;
278434f41cdSflorian 	const struct flist	*f;
279434f41cdSflorian 	int			 rc, newdev = 0, updatedev = 0;
280434f41cdSflorian 	char			*temp = NULL;
281434f41cdSflorian 
282434f41cdSflorian 	f = &p->fl[p->idx];
283434f41cdSflorian 	assert(S_ISBLK(f->st.mode) || S_ISCHR(f->st.mode));
284434f41cdSflorian 
285434f41cdSflorian 	if (!sess->opts->devices || getuid() != 0) {
286b2a7eac7Sbenno 		WARNX("skipping non-regular file %s", f->path);
287434f41cdSflorian 		return 0;
288a982ab77Sclaudio 	}
289a982ab77Sclaudio 	if (sess->opts->dry_run) {
290434f41cdSflorian 		log_file(sess, f);
291434f41cdSflorian 		return 0;
292434f41cdSflorian 	}
293434f41cdSflorian 
29455cb9f91Sbenno 	/*
29555cb9f91Sbenno 	 * See if the dev already exists.
29655cb9f91Sbenno 	 * If a non-device exists in its place, we'll replace that.
29755cb9f91Sbenno 	 * If it replaces a directory, remove the directory first.
29855cb9f91Sbenno 	 */
2996304135bSbenno 
30055cb9f91Sbenno 	assert(p->rootfd != -1);
301434f41cdSflorian 	rc = fstatat(p->rootfd, f->path, &st, AT_SYMLINK_NOFOLLOW);
302434f41cdSflorian 
303eab91beeSclaudio 	if (rc == -1 && errno != ENOENT) {
304eab91beeSclaudio 		ERR("%s: fstatat", f->path);
305eab91beeSclaudio 		return -1;
306eab91beeSclaudio 	}
307434f41cdSflorian 	if (rc != -1 && !(S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode))) {
30855cb9f91Sbenno 		if (S_ISDIR(st.st_mode) &&
30955cb9f91Sbenno 		    unlinkat(p->rootfd, f->path, AT_REMOVEDIR) == -1) {
310b2a7eac7Sbenno 			ERR("%s: unlinkat", f->path);
311434f41cdSflorian 			return -1;
312434f41cdSflorian 		}
31355cb9f91Sbenno 		rc = -1;
314434f41cdSflorian 	}
315434f41cdSflorian 
31655cb9f91Sbenno 	/* Make sure existing device is of the correct type. */
317434f41cdSflorian 
318434f41cdSflorian 	if (rc != -1) {
319434f41cdSflorian 		if ((f->st.mode & (S_IFCHR|S_IFBLK)) !=
32055cb9f91Sbenno 		    (st.st_mode & (S_IFCHR|S_IFBLK)) ||
32155cb9f91Sbenno 		    f->st.rdev != st.st_rdev) {
322b2a7eac7Sbenno 			LOG3("%s: updating device", f->path);
323434f41cdSflorian 			updatedev = 1;
324434f41cdSflorian 		}
325434f41cdSflorian 	}
326434f41cdSflorian 
327434f41cdSflorian 	if (rc == -1 || updatedev) {
328434f41cdSflorian 		newdev = 1;
329ba617adaSbenno 		if (mktemplate(&temp, f->path, sess->opts->recursive) == -1) {
330b2a7eac7Sbenno 			ERRX1("mktemplate");
331434f41cdSflorian 			return -1;
332434f41cdSflorian 		}
33355cb9f91Sbenno 		if (mkstempnodat(p->rootfd, temp,
33455cb9f91Sbenno 		    f->st.mode & (S_IFCHR|S_IFBLK), f->st.rdev) == NULL) {
335b2a7eac7Sbenno 			ERR("mkstempnodat");
336434f41cdSflorian 			free(temp);
337434f41cdSflorian 			return -1;
338434f41cdSflorian 		}
339434f41cdSflorian 	}
340434f41cdSflorian 
34155cb9f91Sbenno 	rsync_set_metadata_at(sess, newdev,
34255cb9f91Sbenno 	    p->rootfd, f, newdev ? temp : f->path);
343434f41cdSflorian 
344434f41cdSflorian 	if (newdev) {
345434f41cdSflorian 		if (renameat(p->rootfd, temp, p->rootfd, f->path) == -1) {
346b2a7eac7Sbenno 			ERR("%s: renameat %s", temp, f->path);
347434f41cdSflorian 			(void)unlinkat(p->rootfd, temp, 0);
348434f41cdSflorian 			free(temp);
349434f41cdSflorian 			return -1;
350434f41cdSflorian 		}
351434f41cdSflorian 		free(temp);
352434f41cdSflorian 	}
35355cb9f91Sbenno 
354434f41cdSflorian 	log_file(sess, f);
355434f41cdSflorian 	return 0;
356434f41cdSflorian }
357434f41cdSflorian 
358434f41cdSflorian /*
359e397242dSclaudio  * See pre_symlink(), but for FIFOs.
36055cb9f91Sbenno  * FIXME: this is very similar to the other pre_xxx() functions.
361434f41cdSflorian  * Return <0 on failure 0 on success.
362434f41cdSflorian  */
363434f41cdSflorian static int
pre_fifo(struct upload * p,struct sess * sess)364434f41cdSflorian pre_fifo(struct upload *p, struct sess *sess)
365434f41cdSflorian {
366434f41cdSflorian 	struct stat		 st;
367434f41cdSflorian 	const struct flist	*f;
368434f41cdSflorian 	int			 rc, newfifo = 0;
369434f41cdSflorian 	char			*temp = NULL;
370434f41cdSflorian 
371434f41cdSflorian 	f = &p->fl[p->idx];
372434f41cdSflorian 	assert(S_ISFIFO(f->st.mode));
373434f41cdSflorian 
374434f41cdSflorian 	if (!sess->opts->specials) {
375b2a7eac7Sbenno 		WARNX("skipping non-regular file %s", f->path);
376434f41cdSflorian 		return 0;
377a982ab77Sclaudio 	}
378a982ab77Sclaudio 	if (sess->opts->dry_run) {
379434f41cdSflorian 		log_file(sess, f);
380434f41cdSflorian 		return 0;
381434f41cdSflorian 	}
382434f41cdSflorian 
38355cb9f91Sbenno 	/*
38455cb9f91Sbenno 	 * See if the fifo already exists.
38555cb9f91Sbenno 	 * If it exists as a non-FIFO, unlink it (if a directory) then
38655cb9f91Sbenno 	 * mark it from replacement.
38755cb9f91Sbenno 	 */
3886304135bSbenno 
38955cb9f91Sbenno 	assert(p->rootfd != -1);
390434f41cdSflorian 	rc = fstatat(p->rootfd, f->path, &st, AT_SYMLINK_NOFOLLOW);
391434f41cdSflorian 
392eab91beeSclaudio 	if (rc == -1 && errno != ENOENT) {
393eab91beeSclaudio 		ERR("%s: fstatat", f->path);
394eab91beeSclaudio 		return -1;
395eab91beeSclaudio 	}
396434f41cdSflorian 	if (rc != -1 && !S_ISFIFO(st.st_mode)) {
39755cb9f91Sbenno 		if (S_ISDIR(st.st_mode) &&
39855cb9f91Sbenno 		    unlinkat(p->rootfd, f->path, AT_REMOVEDIR) == -1) {
399b2a7eac7Sbenno 			ERR("%s: unlinkat", f->path);
400434f41cdSflorian 			return -1;
401434f41cdSflorian 		}
40255cb9f91Sbenno 		rc = -1;
403434f41cdSflorian 	}
404434f41cdSflorian 
405434f41cdSflorian 	if (rc == -1) {
406434f41cdSflorian 		newfifo = 1;
407ba617adaSbenno 		if (mktemplate(&temp, f->path, sess->opts->recursive) == -1) {
408b2a7eac7Sbenno 			ERRX1("mktemplate");
409434f41cdSflorian 			return -1;
410434f41cdSflorian 		}
411434f41cdSflorian 		if (mkstempfifoat(p->rootfd, temp) == NULL) {
412b2a7eac7Sbenno 			ERR("mkstempfifoat");
413434f41cdSflorian 			free(temp);
414434f41cdSflorian 			return -1;
415434f41cdSflorian 		}
416434f41cdSflorian 	}
417434f41cdSflorian 
41855cb9f91Sbenno 	rsync_set_metadata_at(sess, newfifo,
41955cb9f91Sbenno 		p->rootfd, f, newfifo ? temp : f->path);
420434f41cdSflorian 
421434f41cdSflorian 	if (newfifo) {
422434f41cdSflorian 		if (renameat(p->rootfd, temp, p->rootfd, f->path) == -1) {
423b2a7eac7Sbenno 			ERR("%s: renameat %s", temp, f->path);
424434f41cdSflorian 			(void)unlinkat(p->rootfd, temp, 0);
425434f41cdSflorian 			free(temp);
426434f41cdSflorian 			return -1;
427434f41cdSflorian 		}
428434f41cdSflorian 		free(temp);
429434f41cdSflorian 	}
43055cb9f91Sbenno 
431434f41cdSflorian 	log_file(sess, f);
432434f41cdSflorian 	return 0;
433434f41cdSflorian }
434434f41cdSflorian 
435434f41cdSflorian /*
436e397242dSclaudio  * See pre_symlink(), but for socket files.
43755cb9f91Sbenno  * FIXME: this is very similar to the other pre_xxx() functions.
438434f41cdSflorian  * Return <0 on failure 0 on success.
439434f41cdSflorian  */
440434f41cdSflorian static int
pre_sock(struct upload * p,struct sess * sess)441434f41cdSflorian pre_sock(struct upload *p, struct sess *sess)
442434f41cdSflorian {
443434f41cdSflorian 	struct stat		 st;
444434f41cdSflorian 	const struct flist	*f;
445434f41cdSflorian 	int			 rc, newsock = 0;
446434f41cdSflorian 	char			*temp = NULL;
447434f41cdSflorian 
448434f41cdSflorian 	f = &p->fl[p->idx];
449434f41cdSflorian 	assert(S_ISSOCK(f->st.mode));
450434f41cdSflorian 
451434f41cdSflorian 	if (!sess->opts->specials) {
452b2a7eac7Sbenno 		WARNX("skipping non-regular file %s", f->path);
453434f41cdSflorian 		return 0;
454a982ab77Sclaudio 	}
455a982ab77Sclaudio 	if (sess->opts->dry_run) {
456434f41cdSflorian 		log_file(sess, f);
457434f41cdSflorian 		return 0;
458434f41cdSflorian 	}
459434f41cdSflorian 
46055cb9f91Sbenno 	/*
46155cb9f91Sbenno 	 * See if the fifo already exists.
46255cb9f91Sbenno 	 * If it exists as a non-FIFO, unlink it (if a directory) then
46355cb9f91Sbenno 	 * mark it from replacement.
46455cb9f91Sbenno 	 */
46555cb9f91Sbenno 
46608a7862dSderaadt 	assert(p->rootfd != -1);
467434f41cdSflorian 	rc = fstatat(p->rootfd, f->path, &st, AT_SYMLINK_NOFOLLOW);
468434f41cdSflorian 
469eab91beeSclaudio 	if (rc == -1 && errno != ENOENT) {
470eab91beeSclaudio 		ERR("%s: fstatat", f->path);
471eab91beeSclaudio 		return -1;
472eab91beeSclaudio 	}
473434f41cdSflorian 	if (rc != -1 && !S_ISSOCK(st.st_mode)) {
47455cb9f91Sbenno 		if (S_ISDIR(st.st_mode) &&
47555cb9f91Sbenno 		    unlinkat(p->rootfd, f->path, AT_REMOVEDIR) == -1) {
476b2a7eac7Sbenno 			ERR("%s: unlinkat", f->path);
477434f41cdSflorian 			return -1;
478434f41cdSflorian 		}
47955cb9f91Sbenno 		rc = -1;
480434f41cdSflorian 	}
481434f41cdSflorian 
482434f41cdSflorian 	if (rc == -1) {
483434f41cdSflorian 		newsock = 1;
484ba617adaSbenno 		if (mktemplate(&temp, f->path, sess->opts->recursive) == -1) {
485b2a7eac7Sbenno 			ERRX1("mktemplate");
486434f41cdSflorian 			return -1;
487434f41cdSflorian 		}
488434f41cdSflorian 		if (mkstempsock(p->root, temp) == NULL) {
489b2a7eac7Sbenno 			ERR("mkstempsock");
490434f41cdSflorian 			free(temp);
491434f41cdSflorian 			return -1;
492434f41cdSflorian 		}
493434f41cdSflorian 	}
494434f41cdSflorian 
49555cb9f91Sbenno 	rsync_set_metadata_at(sess, newsock,
49655cb9f91Sbenno 		p->rootfd, f, newsock ? temp : f->path);
497434f41cdSflorian 
498434f41cdSflorian 	if (newsock) {
499434f41cdSflorian 		if (renameat(p->rootfd, temp, p->rootfd, f->path) == -1) {
500b2a7eac7Sbenno 			ERR("%s: renameat %s", temp, f->path);
501434f41cdSflorian 			(void)unlinkat(p->rootfd, temp, 0);
502434f41cdSflorian 			free(temp);
503434f41cdSflorian 			return -1;
504434f41cdSflorian 		}
505434f41cdSflorian 		free(temp);
506434f41cdSflorian 	}
50755cb9f91Sbenno 
508434f41cdSflorian 	log_file(sess, f);
509434f41cdSflorian 	return 0;
510434f41cdSflorian }
511434f41cdSflorian 
512434f41cdSflorian /*
51360a32ee9Sbenno  * If not found, create the destination directory in prefix order.
51460a32ee9Sbenno  * Create directories using the existing umask.
51560a32ee9Sbenno  * Return <0 on failure 0 on success.
51660a32ee9Sbenno  */
51760a32ee9Sbenno static int
pre_dir(const struct upload * p,struct sess * sess)51860a32ee9Sbenno pre_dir(const struct upload *p, struct sess *sess)
51960a32ee9Sbenno {
52060a32ee9Sbenno 	struct stat	 st;
52160a32ee9Sbenno 	int		 rc;
52260a32ee9Sbenno 	const struct flist *f;
52360a32ee9Sbenno 
52460a32ee9Sbenno 	f = &p->fl[p->idx];
52560a32ee9Sbenno 	assert(S_ISDIR(f->st.mode));
52660a32ee9Sbenno 
52760a32ee9Sbenno 	if (!sess->opts->recursive) {
528b2a7eac7Sbenno 		WARNX("%s: ignoring directory", f->path);
52960a32ee9Sbenno 		return 0;
530a982ab77Sclaudio 	}
531a982ab77Sclaudio 	if (sess->opts->dry_run) {
53260a32ee9Sbenno 		log_dir(sess, f);
53360a32ee9Sbenno 		return 0;
53460a32ee9Sbenno 	}
53560a32ee9Sbenno 
536f1dcb30aSderaadt 	assert(p->rootfd != -1);
53760a32ee9Sbenno 	rc = fstatat(p->rootfd, f->path, &st, AT_SYMLINK_NOFOLLOW);
53855cb9f91Sbenno 
539f1dcb30aSderaadt 	if (rc == -1 && errno != ENOENT) {
540b2a7eac7Sbenno 		ERR("%s: fstatat", f->path);
54160a32ee9Sbenno 		return -1;
542eab91beeSclaudio 	}
543eab91beeSclaudio 	if (rc != -1 && !S_ISDIR(st.st_mode)) {
544b2a7eac7Sbenno 		ERRX("%s: not a directory", f->path);
54560a32ee9Sbenno 		return -1;
546f1dcb30aSderaadt 	} else if (rc != -1) {
54760a32ee9Sbenno 		/*
54860a32ee9Sbenno 		 * FIXME: we should fchmod the permissions here as well,
54960a32ee9Sbenno 		 * as we may locally have shut down writing into the
55060a32ee9Sbenno 		 * directory and that doesn't work.
55160a32ee9Sbenno 		 */
552b2a7eac7Sbenno 		LOG3("%s: updating directory", f->path);
55360a32ee9Sbenno 		return 0;
55460a32ee9Sbenno 	}
55560a32ee9Sbenno 
55660a32ee9Sbenno 	/*
55760a32ee9Sbenno 	 * We want to make the directory with default permissions (using
55860a32ee9Sbenno 	 * our old umask, which we've since unset), then adjust
55960a32ee9Sbenno 	 * permissions (assuming preserve_perms or new) afterward in
56060a32ee9Sbenno 	 * case it's u-w or something.
56160a32ee9Sbenno 	 */
56260a32ee9Sbenno 
563b2a7eac7Sbenno 	LOG3("%s: creating directory", f->path);
564f1dcb30aSderaadt 	if (mkdirat(p->rootfd, f->path, 0777 & ~p->oumask) == -1) {
565b2a7eac7Sbenno 		ERR("%s: mkdirat", f->path);
56660a32ee9Sbenno 		return -1;
56760a32ee9Sbenno 	}
56860a32ee9Sbenno 
56960a32ee9Sbenno 	p->newdir[p->idx] = 1;
57060a32ee9Sbenno 	log_dir(sess, f);
57160a32ee9Sbenno 	return 0;
57260a32ee9Sbenno }
57360a32ee9Sbenno 
57460a32ee9Sbenno /*
57560a32ee9Sbenno  * Process the directory time and mode for "idx" in the file list.
57660a32ee9Sbenno  * Returns zero on failure, non-zero on success.
57760a32ee9Sbenno  */
57860a32ee9Sbenno static int
post_dir(struct sess * sess,const struct upload * u,size_t idx)57960a32ee9Sbenno post_dir(struct sess *sess, const struct upload *u, size_t idx)
58060a32ee9Sbenno {
58160a32ee9Sbenno 	struct timespec	 tv[2];
58260a32ee9Sbenno 	int		 rc;
58360a32ee9Sbenno 	struct stat	 st;
58460a32ee9Sbenno 	const struct flist *f;
58560a32ee9Sbenno 
58660a32ee9Sbenno 	f = &u->fl[idx];
58760a32ee9Sbenno 	assert(S_ISDIR(f->st.mode));
58860a32ee9Sbenno 
58960a32ee9Sbenno 	/* We already warned about the directory in pre_process_dir(). */
59060a32ee9Sbenno 
59160a32ee9Sbenno 	if (!sess->opts->recursive)
59260a32ee9Sbenno 		return 1;
593a982ab77Sclaudio 	if (sess->opts->dry_run)
59460a32ee9Sbenno 		return 1;
59560a32ee9Sbenno 
596f1dcb30aSderaadt 	if (fstatat(u->rootfd, f->path, &st, AT_SYMLINK_NOFOLLOW) == -1) {
597b2a7eac7Sbenno 		ERR("%s: fstatat", f->path);
59860a32ee9Sbenno 		return 0;
599a982ab77Sclaudio 	}
600a982ab77Sclaudio 	if (!S_ISDIR(st.st_mode)) {
601b2a7eac7Sbenno 		WARNX("%s: not a directory", f->path);
60260a32ee9Sbenno 		return 0;
60360a32ee9Sbenno 	}
60460a32ee9Sbenno 
60560a32ee9Sbenno 	/*
60660a32ee9Sbenno 	 * Update the modification time if we're a new directory *or* if
60760a32ee9Sbenno 	 * we're preserving times and the time has changed.
608df1d9d84Sflorian 	 * FIXME: run rsync_set_metadata()?
60960a32ee9Sbenno 	 */
61060a32ee9Sbenno 
61197d9fd37Sjob 	if (!sess->opts->ignore_dir_times) {
61260a32ee9Sbenno 		if (u->newdir[idx] ||
61360a32ee9Sbenno 		    (sess->opts->preserve_times &&
61460a32ee9Sbenno 		     st.st_mtime != f->st.mtime)) {
61560a32ee9Sbenno 			tv[0].tv_sec = time(NULL);
61660a32ee9Sbenno 			tv[0].tv_nsec = 0;
61760a32ee9Sbenno 			tv[1].tv_sec = f->st.mtime;
61860a32ee9Sbenno 			tv[1].tv_nsec = 0;
61960a32ee9Sbenno 			rc = utimensat(u->rootfd, f->path, tv, 0);
620f1dcb30aSderaadt 			if (rc == -1) {
621b2a7eac7Sbenno 				ERR("%s: utimensat", f->path);
62260a32ee9Sbenno 				return 0;
62360a32ee9Sbenno 			}
624b2a7eac7Sbenno 			LOG4("%s: updated date", f->path);
62560a32ee9Sbenno 		}
62697d9fd37Sjob 	}
62760a32ee9Sbenno 
62860a32ee9Sbenno 	/*
62960a32ee9Sbenno 	 * Update the mode if we're a new directory *or* if we're
63060a32ee9Sbenno 	 * preserving modes and it has changed.
63160a32ee9Sbenno 	 */
63260a32ee9Sbenno 
63360a32ee9Sbenno 	if (u->newdir[idx] ||
6348a54c977Sderaadt 	    (sess->opts->preserve_perms && st.st_mode != f->st.mode)) {
63560a32ee9Sbenno 		rc = fchmodat(u->rootfd, f->path, f->st.mode, 0);
636f1dcb30aSderaadt 		if (rc == -1) {
637b2a7eac7Sbenno 			ERR("%s: fchmodat", f->path);
63860a32ee9Sbenno 			return 0;
63960a32ee9Sbenno 		}
640b2a7eac7Sbenno 		LOG4("%s: updated mode", f->path);
64160a32ee9Sbenno 	}
64260a32ee9Sbenno 
64360a32ee9Sbenno 	return 1;
64460a32ee9Sbenno }
64560a32ee9Sbenno 
64660a32ee9Sbenno /*
647e397242dSclaudio  * Check if file exists in the specified root directory.
648e397242dSclaudio  * Returns:
649e397242dSclaudio  *    -1 on error
650e397242dSclaudio  *     0 if file is considered the same
651e397242dSclaudio  *     1 if file exists and is possible match
652e397242dSclaudio  *     2 if file exists but quick check failed
653e397242dSclaudio  *     3 if file does not exist
654e397242dSclaudio  * The stat pointer st is only valid for 0, 1, and 2 returns.
655e397242dSclaudio  */
656e397242dSclaudio static int
check_file(int rootfd,const struct flist * f,struct stat * st,struct sess * sess)6578d16211cSclaudio check_file(int rootfd, const struct flist *f, struct stat *st,
6588d16211cSclaudio     struct sess *sess)
659e397242dSclaudio {
660e397242dSclaudio 	if (fstatat(rootfd, f->path, st, AT_SYMLINK_NOFOLLOW) == -1) {
661e397242dSclaudio 		if (errno == ENOENT)
662e397242dSclaudio 			return 3;
663e397242dSclaudio 
664e397242dSclaudio 		ERR("%s: fstatat", f->path);
665e397242dSclaudio 		return -1;
666e397242dSclaudio 	}
667e397242dSclaudio 
668e397242dSclaudio 	/* non-regular file needs attention */
669e397242dSclaudio 	if (!S_ISREG(st->st_mode))
670e397242dSclaudio 		return 2;
671e397242dSclaudio 
6728d16211cSclaudio 	/* TODO: add support for --checksum */
6738d16211cSclaudio 
6748d16211cSclaudio 	/* if ignore_times is on file needs attention */
6758d16211cSclaudio 	if (sess->opts->ignore_times)
6768d16211cSclaudio 		return 2;
6778d16211cSclaudio 
678e397242dSclaudio 	/* quick check if file is the same */
679e397242dSclaudio 	if (st->st_size == f->st.size) {
6808d16211cSclaudio 		if (sess->opts->size_only)
6818d16211cSclaudio 			return 0;
682e397242dSclaudio 		if (st->st_mtime == f->st.mtime)
683e397242dSclaudio 			return 0;
684e397242dSclaudio 		return 1;
685e397242dSclaudio 	}
686e397242dSclaudio 
687e397242dSclaudio 	/* file needs attention */
688e397242dSclaudio 	return 2;
689e397242dSclaudio }
690e397242dSclaudio 
691e397242dSclaudio /*
69260a32ee9Sbenno  * Try to open the file at the current index.
693a982ab77Sclaudio  * If the file does not exist, returns with >0.
69460a32ee9Sbenno  * Return <0 on failure, 0 on success w/nothing to be done, >0 on
69560a32ee9Sbenno  * success and the file needs attention.
69660a32ee9Sbenno  */
69760a32ee9Sbenno static int
pre_file(const struct upload * p,int * filefd,off_t * size,struct sess * sess)698e397242dSclaudio pre_file(const struct upload *p, int *filefd, off_t *size,
699a982ab77Sclaudio     struct sess *sess)
70060a32ee9Sbenno {
70160a32ee9Sbenno 	const struct flist *f;
702e397242dSclaudio 	struct stat st;
703e397242dSclaudio 	int i, rc, match = -1;
70460a32ee9Sbenno 
70560a32ee9Sbenno 	f = &p->fl[p->idx];
70660a32ee9Sbenno 	assert(S_ISREG(f->st.mode));
70760a32ee9Sbenno 
70860a32ee9Sbenno 	if (sess->opts->dry_run) {
70960a32ee9Sbenno 		log_file(sess, f);
71060a32ee9Sbenno 		if (!io_write_int(sess, p->fdout, p->idx)) {
711b2a7eac7Sbenno 			ERRX1("io_write_int");
71260a32ee9Sbenno 			return -1;
71360a32ee9Sbenno 		}
71460a32ee9Sbenno 		return 0;
71560a32ee9Sbenno 	}
71660a32ee9Sbenno 
71782ecafa1Sclaudio 	if (sess->opts->max_size >= 0 && f->st.size > sess->opts->max_size) {
71882ecafa1Sclaudio 		WARNX("skipping over max-size file %s", f->path);
71982ecafa1Sclaudio 		return 0;
72082ecafa1Sclaudio 	}
72182ecafa1Sclaudio 	if (sess->opts->min_size >= 0 && f->st.size < sess->opts->min_size) {
72282ecafa1Sclaudio 		WARNX("skipping under min-size file %s", f->path);
72382ecafa1Sclaudio 		return 0;
72482ecafa1Sclaudio 	}
72582ecafa1Sclaudio 
72660a32ee9Sbenno 	/*
72760a32ee9Sbenno 	 * For non dry-run cases, we'll write the acknowledgement later
728a982ab77Sclaudio 	 * in the rsync_uploader() function.
72960a32ee9Sbenno 	 */
73060a32ee9Sbenno 
731e397242dSclaudio 	*size = 0;
732e107519eSclaudio 	*filefd = -1;
733a982ab77Sclaudio 
7348d16211cSclaudio 	rc = check_file(p->rootfd, f, &st, sess);
735e397242dSclaudio 	if (rc == -1)
736a982ab77Sclaudio 		return -1;
737e397242dSclaudio 	if (rc == 2 && !S_ISREG(st.st_mode)) {
738e397242dSclaudio 		if (S_ISDIR(st.st_mode) &&
739e107519eSclaudio 		    unlinkat(p->rootfd, f->path, AT_REMOVEDIR) == -1) {
740e107519eSclaudio 			ERR("%s: unlinkat", f->path);
741a982ab77Sclaudio 			return -1;
742a982ab77Sclaudio 		}
743e107519eSclaudio 	}
744e397242dSclaudio 	if (rc == 0) {
745e107519eSclaudio 		if (!rsync_set_metadata_at(sess, 0, p->rootfd, f, f->path)) {
746a982ab77Sclaudio 			ERRX1("rsync_set_metadata");
747a982ab77Sclaudio 			return -1;
748a982ab77Sclaudio 		}
749e397242dSclaudio 		LOG3("%s: skipping: up to date", f->path);
750a982ab77Sclaudio 		return 0;
751a982ab77Sclaudio 	}
752a982ab77Sclaudio 
753e397242dSclaudio 	/* check alternative locations for better match */
754e397242dSclaudio 	for (i = 0; sess->opts->basedir[i] != NULL; i++) {
755e397242dSclaudio 		const char *root = sess->opts->basedir[i];
756e397242dSclaudio 		int dfd, x;
757e397242dSclaudio 
758b7041c07Sderaadt 		dfd = openat(p->rootfd, root, O_RDONLY | O_DIRECTORY);
759e397242dSclaudio 		if (dfd == -1)
760e397242dSclaudio 			err(ERR_FILE_IO, "%s: openat", root);
7618d16211cSclaudio 		x = check_file(dfd, f, &st, sess);
762e397242dSclaudio 		/* found a match */
763e397242dSclaudio 		if (x == 0) {
764e397242dSclaudio 			if (rc >= 0) {
765e397242dSclaudio 				/* found better match, delete file in rootfd */
766e397242dSclaudio 				if (unlinkat(p->rootfd, f->path, 0) == -1 &&
767e397242dSclaudio 				    errno != ENOENT) {
768e397242dSclaudio 					ERR("%s: unlinkat", f->path);
769e397242dSclaudio 					return -1;
770e397242dSclaudio 				}
771e397242dSclaudio 			}
772e397242dSclaudio 			LOG3("%s: skipping: up to date in %s", f->path, root);
773e397242dSclaudio 			/* TODO: depending on mode link or copy file */
774e397242dSclaudio 			close(dfd);
775e397242dSclaudio 			return 0;
776e397242dSclaudio 		} else if (x == 1 && match == -1) {
777e397242dSclaudio 			/* found a local file that is a close match */
778e397242dSclaudio 			match = i;
779e397242dSclaudio 		}
780e397242dSclaudio 		close(dfd);
781e397242dSclaudio 	}
782e397242dSclaudio 	if (match != -1) {
783e397242dSclaudio 		/* copy match from basedir into root as a start point */
784e397242dSclaudio 		copy_file(p->rootfd, sess->opts->basedir[match], f);
785e397242dSclaudio 		if (fstatat(p->rootfd, f->path, &st, AT_SYMLINK_NOFOLLOW) ==
786e397242dSclaudio 		    -1) {
787e397242dSclaudio 			ERR("%s: fstatat", f->path);
788e397242dSclaudio 			return -1;
789e397242dSclaudio 		}
790e397242dSclaudio 	}
791e397242dSclaudio 
792e397242dSclaudio 	*size = st.st_size;
793b7041c07Sderaadt 	*filefd = openat(p->rootfd, f->path, O_RDONLY | O_NOFOLLOW);
794e107519eSclaudio 	if (*filefd == -1 && errno != ENOENT) {
795e107519eSclaudio 		ERR("%s: openat", f->path);
796e107519eSclaudio 		return -1;
797e107519eSclaudio 	}
798e107519eSclaudio 
799a982ab77Sclaudio 	/* file needs attention */
800a982ab77Sclaudio 	return 1;
801a982ab77Sclaudio }
80260a32ee9Sbenno 
80360a32ee9Sbenno /*
80460a32ee9Sbenno  * Allocate an uploader object in the correct state to start.
80560a32ee9Sbenno  * Returns NULL on failure or the pointer otherwise.
80660a32ee9Sbenno  * On success, upload_free() must be called with the allocated pointer.
80760a32ee9Sbenno  */
80860a32ee9Sbenno struct upload *
upload_alloc(const char * root,int rootfd,int fdout,size_t clen,const struct flist * fl,size_t flsz,mode_t msk)809ba617adaSbenno upload_alloc(const char *root, int rootfd, int fdout,
81060a32ee9Sbenno 	size_t clen, const struct flist *fl, size_t flsz, mode_t msk)
81160a32ee9Sbenno {
81260a32ee9Sbenno 	struct upload	*p;
81360a32ee9Sbenno 
814f1dcb30aSderaadt 	if ((p = calloc(1, sizeof(struct upload))) == NULL) {
815b2a7eac7Sbenno 		ERR("calloc");
81660a32ee9Sbenno 		return NULL;
81760a32ee9Sbenno 	}
81860a32ee9Sbenno 
81960a32ee9Sbenno 	p->state = UPLOAD_FIND_NEXT;
82060a32ee9Sbenno 	p->oumask = msk;
821434f41cdSflorian 	p->root = strdup(root);
822434f41cdSflorian 	if (p->root == NULL) {
823b2a7eac7Sbenno 		ERR("strdup");
824434f41cdSflorian 		free(p);
825434f41cdSflorian 		return NULL;
826434f41cdSflorian 	}
82760a32ee9Sbenno 	p->rootfd = rootfd;
82860a32ee9Sbenno 	p->csumlen = clen;
82960a32ee9Sbenno 	p->fdout = fdout;
83060a32ee9Sbenno 	p->fl = fl;
83160a32ee9Sbenno 	p->flsz = flsz;
83260a32ee9Sbenno 	p->newdir = calloc(flsz, sizeof(int));
833f1dcb30aSderaadt 	if (p->newdir == NULL) {
834b2a7eac7Sbenno 		ERR("calloc");
835434f41cdSflorian 		free(p->root);
83660a32ee9Sbenno 		free(p);
83760a32ee9Sbenno 		return NULL;
83860a32ee9Sbenno 	}
83960a32ee9Sbenno 	return p;
84060a32ee9Sbenno }
84160a32ee9Sbenno 
84260a32ee9Sbenno /*
84360a32ee9Sbenno  * Perform all cleanups and free.
84460a32ee9Sbenno  * Passing a NULL to this function is ok.
84560a32ee9Sbenno  */
84660a32ee9Sbenno void
upload_free(struct upload * p)84760a32ee9Sbenno upload_free(struct upload *p)
84860a32ee9Sbenno {
84960a32ee9Sbenno 
850f1dcb30aSderaadt 	if (p == NULL)
85160a32ee9Sbenno 		return;
852434f41cdSflorian 	free(p->root);
85360a32ee9Sbenno 	free(p->newdir);
85460a32ee9Sbenno 	free(p->buf);
85560a32ee9Sbenno 	free(p);
85660a32ee9Sbenno }
85760a32ee9Sbenno 
85860a32ee9Sbenno /*
85960a32ee9Sbenno  * Iterates through all available files and conditionally gets the file
86060a32ee9Sbenno  * ready for processing to check whether it's up to date.
86160a32ee9Sbenno  * If not up to date or empty, sends file information to the sender.
86260a32ee9Sbenno  * If returns 0, we've processed all files there are to process.
86360a32ee9Sbenno  * If returns >0, we're waiting for POLLIN or POLLOUT data.
86460a32ee9Sbenno  * Otherwise returns <0, which is an error.
86560a32ee9Sbenno  */
86660a32ee9Sbenno int
rsync_uploader(struct upload * u,int * fileinfd,struct sess * sess,int * fileoutfd)86760a32ee9Sbenno rsync_uploader(struct upload *u, int *fileinfd,
86860a32ee9Sbenno 	struct sess *sess, int *fileoutfd)
86960a32ee9Sbenno {
87060a32ee9Sbenno 	struct blkset	    blk;
871c314c803Sbenno 	void		   *mbuf, *bufp;
872c314c803Sbenno 	ssize_t		    msz;
873c314c803Sbenno 	size_t		    i, pos, sz;
874e397242dSclaudio 	off_t		    offs, filesize;
87560a32ee9Sbenno 	int		    c;
87660a32ee9Sbenno 
877e107519eSclaudio 	/* Once finished this should never get called again. */
878f1dcb30aSderaadt 	assert(u->state != UPLOAD_FINISHED);
87960a32ee9Sbenno 
88060a32ee9Sbenno 	/*
88160a32ee9Sbenno 	 * If we have an upload in progress, then keep writing until the
88260a32ee9Sbenno 	 * buffer has been fully written.
88360a32ee9Sbenno 	 * We must only have the output file descriptor working and also
88460a32ee9Sbenno 	 * have a valid buffer to write.
88560a32ee9Sbenno 	 */
88660a32ee9Sbenno 
887a982ab77Sclaudio 	if (u->state == UPLOAD_WRITE) {
888ed5cc9fbSderaadt 		assert(u->buf != NULL);
889f1dcb30aSderaadt 		assert(*fileoutfd != -1);
890f1dcb30aSderaadt 		assert(*fileinfd == -1);
89160a32ee9Sbenno 
89260a32ee9Sbenno 		/*
89360a32ee9Sbenno 		 * Unfortunately, we need to chunk these: if we're
89460a32ee9Sbenno 		 * the server side of things, then we're multiplexing
89560a32ee9Sbenno 		 * output and need to wrap this in chunks.
89660a32ee9Sbenno 		 * This is a major deficiency of rsync.
89760a32ee9Sbenno 		 * FIXME: add a "fast-path" mode that simply dumps out
89860a32ee9Sbenno 		 * the buffer non-blocking if we're not mplexing.
89960a32ee9Sbenno 		 */
90060a32ee9Sbenno 
90160a32ee9Sbenno 		if (u->bufpos < u->bufsz) {
90260a32ee9Sbenno 			sz = MAX_CHUNK < (u->bufsz - u->bufpos) ?
90360a32ee9Sbenno 				MAX_CHUNK : (u->bufsz - u->bufpos);
90460a32ee9Sbenno 			c = io_write_buf(sess, u->fdout,
90560a32ee9Sbenno 				u->buf + u->bufpos, sz);
906f1dcb30aSderaadt 			if (c == 0) {
907b2a7eac7Sbenno 				ERRX1("io_write_nonblocking");
90860a32ee9Sbenno 				return -1;
90960a32ee9Sbenno 			}
91060a32ee9Sbenno 			u->bufpos += sz;
91160a32ee9Sbenno 			if (u->bufpos < u->bufsz)
91260a32ee9Sbenno 				return 1;
91360a32ee9Sbenno 		}
91460a32ee9Sbenno 
91560a32ee9Sbenno 		/*
91660a32ee9Sbenno 		 * Let the UPLOAD_FIND_NEXT state handle things if we
91760a32ee9Sbenno 		 * finish, as we'll need to write a POLLOUT message and
91860a32ee9Sbenno 		 * not have a writable descriptor yet.
91960a32ee9Sbenno 		 */
92060a32ee9Sbenno 
92160a32ee9Sbenno 		u->state = UPLOAD_FIND_NEXT;
92260a32ee9Sbenno 		u->idx++;
92360a32ee9Sbenno 		return 1;
92460a32ee9Sbenno 	}
92560a32ee9Sbenno 
92660a32ee9Sbenno 	/*
92760a32ee9Sbenno 	 * If we invoke the uploader without a file currently open, then
92860a32ee9Sbenno 	 * we iterate through til the next available regular file and
92960a32ee9Sbenno 	 * start the opening process.
93060a32ee9Sbenno 	 * This means we must have the output file descriptor working.
93160a32ee9Sbenno 	 */
93260a32ee9Sbenno 
933f1dcb30aSderaadt 	if (u->state == UPLOAD_FIND_NEXT) {
934f1dcb30aSderaadt 		assert(*fileinfd == -1);
935f1dcb30aSderaadt 		assert(*fileoutfd != -1);
93660a32ee9Sbenno 
93760a32ee9Sbenno 		for ( ; u->idx < u->flsz; u->idx++) {
93860a32ee9Sbenno 			if (S_ISDIR(u->fl[u->idx].st.mode))
93960a32ee9Sbenno 				c = pre_dir(u, sess);
94060a32ee9Sbenno 			else if (S_ISLNK(u->fl[u->idx].st.mode))
941e397242dSclaudio 				c = pre_symlink(u, sess);
94260a32ee9Sbenno 			else if (S_ISREG(u->fl[u->idx].st.mode))
943e397242dSclaudio 				c = pre_file(u, fileinfd, &filesize, sess);
944434f41cdSflorian 			else if (S_ISBLK(u->fl[u->idx].st.mode) ||
945434f41cdSflorian 			    S_ISCHR(u->fl[u->idx].st.mode))
946434f41cdSflorian 				c = pre_dev(u, sess);
947434f41cdSflorian 			else if (S_ISFIFO(u->fl[u->idx].st.mode))
948434f41cdSflorian 				c = pre_fifo(u, sess);
949434f41cdSflorian 			else if (S_ISSOCK(u->fl[u->idx].st.mode))
950434f41cdSflorian 				c = pre_sock(u, sess);
95160a32ee9Sbenno 			else
95260a32ee9Sbenno 				c = 0;
95360a32ee9Sbenno 
95460a32ee9Sbenno 			if (c < 0)
95560a32ee9Sbenno 				return -1;
95660a32ee9Sbenno 			else if (c > 0)
95760a32ee9Sbenno 				break;
95860a32ee9Sbenno 		}
95960a32ee9Sbenno 
96060a32ee9Sbenno 		/*
96160a32ee9Sbenno 		 * Whether we've finished writing files or not, we
96260a32ee9Sbenno 		 * disable polling on the output channel.
96360a32ee9Sbenno 		 */
96460a32ee9Sbenno 
96560a32ee9Sbenno 		*fileoutfd = -1;
96660a32ee9Sbenno 		if (u->idx == u->flsz) {
967f1dcb30aSderaadt 			assert(*fileinfd == -1);
96860a32ee9Sbenno 			if (!io_write_int(sess, u->fdout, -1)) {
969b2a7eac7Sbenno 				ERRX1("io_write_int");
97060a32ee9Sbenno 				return -1;
97160a32ee9Sbenno 			}
97260a32ee9Sbenno 			u->state = UPLOAD_FINISHED;
973b2a7eac7Sbenno 			LOG4("uploader: finished");
97460a32ee9Sbenno 			return 0;
97560a32ee9Sbenno 		}
97660a32ee9Sbenno 
97760a32ee9Sbenno 		/* Go back to the event loop, if necessary. */
97860a32ee9Sbenno 
979a982ab77Sclaudio 		u->state = UPLOAD_WRITE;
98060a32ee9Sbenno 	}
98160a32ee9Sbenno 
98260a32ee9Sbenno 	/* Initialies our blocks. */
98360a32ee9Sbenno 
984a982ab77Sclaudio 	assert(u->state == UPLOAD_WRITE);
98560a32ee9Sbenno 	memset(&blk, 0, sizeof(struct blkset));
98660a32ee9Sbenno 	blk.csum = u->csumlen;
98760a32ee9Sbenno 
988e397242dSclaudio 	if (*fileinfd != -1 && filesize > 0) {
989e397242dSclaudio 		init_blkset(&blk, filesize);
99060a32ee9Sbenno 		assert(blk.blksz);
99160a32ee9Sbenno 
99260a32ee9Sbenno 		blk.blks = calloc(blk.blksz, sizeof(struct blk));
993f1dcb30aSderaadt 		if (blk.blks == NULL) {
994b2a7eac7Sbenno 			ERR("calloc");
995c314c803Sbenno 			close(*fileinfd);
996c314c803Sbenno 			*fileinfd = -1;
997c314c803Sbenno 			return -1;
998c314c803Sbenno 		}
999c314c803Sbenno 
1000a982ab77Sclaudio 		if ((mbuf = malloc(blk.len)) == NULL) {
1001a982ab77Sclaudio 			ERR("malloc");
100260a32ee9Sbenno 			close(*fileinfd);
100360a32ee9Sbenno 			*fileinfd = -1;
10048da8f61eSclaudio 			free(blk.blks);
100560a32ee9Sbenno 			return -1;
100660a32ee9Sbenno 		}
100760a32ee9Sbenno 
100860a32ee9Sbenno 		offs = 0;
1009c314c803Sbenno 		i = 0;
1010c314c803Sbenno 		do {
1011c314c803Sbenno 			msz = pread(*fileinfd, mbuf, blk.len, offs);
1012a982ab77Sclaudio 			if ((size_t)msz != blk.len && (size_t)msz != blk.rem) {
1013c314c803Sbenno 				ERR("pread");
1014c314c803Sbenno 				close(*fileinfd);
1015c314c803Sbenno 				*fileinfd = -1;
1016a982ab77Sclaudio 				free(mbuf);
10178da8f61eSclaudio 				free(blk.blks);
1018c314c803Sbenno 				return -1;
101960a32ee9Sbenno 			}
1020c314c803Sbenno 			init_blk(&blk.blks[i], &blk, offs, i, mbuf, sess);
1021c314c803Sbenno 			offs += blk.len;
1022c314c803Sbenno 			LOG3(
1023c314c803Sbenno 			    "i=%ld, offs=%lld, msz=%ld, blk.len=%lu, blk.rem=%lu",
1024c314c803Sbenno 			    i, offs, msz, blk.len, blk.rem);
1025c314c803Sbenno 			i++;
1026c314c803Sbenno 		} while (i < blk.blksz);
102760a32ee9Sbenno 
1028a982ab77Sclaudio 		free(mbuf);
102960a32ee9Sbenno 		close(*fileinfd);
103060a32ee9Sbenno 		*fileinfd = -1;
1031b2a7eac7Sbenno 		LOG3("%s: mapped %jd B with %zu blocks",
103260a32ee9Sbenno 		    u->fl[u->idx].path, (intmax_t)blk.size,
103360a32ee9Sbenno 		    blk.blksz);
103460a32ee9Sbenno 	} else {
1035f1dcb30aSderaadt 		if (*fileinfd != -1) {
103660a32ee9Sbenno 			close(*fileinfd);
103760a32ee9Sbenno 			*fileinfd = -1;
103860a32ee9Sbenno 		}
103960a32ee9Sbenno 		blk.len = MAX_CHUNK; /* Doesn't matter. */
1040b2a7eac7Sbenno 		LOG3("%s: not mapped", u->fl[u->idx].path);
104160a32ee9Sbenno 	}
104260a32ee9Sbenno 
1043f1dcb30aSderaadt 	assert(*fileinfd == -1);
104460a32ee9Sbenno 
104560a32ee9Sbenno 	/* Make sure the block metadata buffer is big enough. */
104660a32ee9Sbenno 
104760a32ee9Sbenno 	u->bufsz =
104860a32ee9Sbenno 	    sizeof(int32_t) + /* identifier */
104960a32ee9Sbenno 	    sizeof(int32_t) + /* block count */
105060a32ee9Sbenno 	    sizeof(int32_t) + /* block length */
105160a32ee9Sbenno 	    sizeof(int32_t) + /* checksum length */
105260a32ee9Sbenno 	    sizeof(int32_t) + /* block remainder */
105360a32ee9Sbenno 	    blk.blksz *
105460a32ee9Sbenno 	    (sizeof(int32_t) + /* short checksum */
105560a32ee9Sbenno 	    blk.csum); /* long checksum */
105660a32ee9Sbenno 
105760a32ee9Sbenno 	if (u->bufsz > u->bufmax) {
1058f1dcb30aSderaadt 		if ((bufp = realloc(u->buf, u->bufsz)) == NULL) {
1059b2a7eac7Sbenno 			ERR("realloc");
10608da8f61eSclaudio 			free(blk.blks);
106160a32ee9Sbenno 			return -1;
106260a32ee9Sbenno 		}
106360a32ee9Sbenno 		u->buf = bufp;
106460a32ee9Sbenno 		u->bufmax = u->bufsz;
106560a32ee9Sbenno 	}
106660a32ee9Sbenno 
106760a32ee9Sbenno 	u->bufpos = pos = 0;
1068ba617adaSbenno 	io_buffer_int(u->buf, &pos, u->bufsz, u->idx);
1069ba617adaSbenno 	io_buffer_int(u->buf, &pos, u->bufsz, blk.blksz);
1070ba617adaSbenno 	io_buffer_int(u->buf, &pos, u->bufsz, blk.len);
1071ba617adaSbenno 	io_buffer_int(u->buf, &pos, u->bufsz, blk.csum);
1072ba617adaSbenno 	io_buffer_int(u->buf, &pos, u->bufsz, blk.rem);
107360a32ee9Sbenno 	for (i = 0; i < blk.blksz; i++) {
1074ba617adaSbenno 		io_buffer_int(u->buf, &pos, u->bufsz,
107560a32ee9Sbenno 			blk.blks[i].chksum_short);
1076ba617adaSbenno 		io_buffer_buf(u->buf, &pos, u->bufsz,
107760a32ee9Sbenno 			blk.blks[i].chksum_long, blk.csum);
107860a32ee9Sbenno 	}
107960a32ee9Sbenno 	assert(pos == u->bufsz);
108060a32ee9Sbenno 
108160a32ee9Sbenno 	/* Reenable the output poller and clean up. */
108260a32ee9Sbenno 
108360a32ee9Sbenno 	*fileoutfd = u->fdout;
108460a32ee9Sbenno 	free(blk.blks);
108560a32ee9Sbenno 	return 1;
108660a32ee9Sbenno }
108760a32ee9Sbenno 
108860a32ee9Sbenno /*
108960a32ee9Sbenno  * Fix up the directory permissions and times post-order.
109060a32ee9Sbenno  * We can't fix up directory permissions in place because the server may
109160a32ee9Sbenno  * want us to have overly-tight permissions---say, those that don't
109260a32ee9Sbenno  * allow writing into the directory.
109360a32ee9Sbenno  * We also need to do our directory times post-order because making
109460a32ee9Sbenno  * files within the directory will change modification times.
109560a32ee9Sbenno  * Returns zero on failure, non-zero on success.
109660a32ee9Sbenno  */
109760a32ee9Sbenno int
rsync_uploader_tail(struct upload * u,struct sess * sess)109860a32ee9Sbenno rsync_uploader_tail(struct upload *u, struct sess *sess)
109960a32ee9Sbenno {
110060a32ee9Sbenno 	size_t	 i;
110160a32ee9Sbenno 
1102*394aa39aSclaudio 	if ((!sess->opts->preserve_times || sess->opts->ignore_dir_times) &&
110360a32ee9Sbenno 	    !sess->opts->preserve_perms)
110460a32ee9Sbenno 		return 1;
110560a32ee9Sbenno 
1106b2a7eac7Sbenno 	LOG2("fixing up directory times and permissions");
110760a32ee9Sbenno 
110860a32ee9Sbenno 	for (i = 0; i < u->flsz; i++)
110960a32ee9Sbenno 		if (S_ISDIR(u->fl[i].st.mode))
111060a32ee9Sbenno 			if (!post_dir(sess, u, i))
111160a32ee9Sbenno 				return 0;
111260a32ee9Sbenno 
111360a32ee9Sbenno 	return 1;
111460a32ee9Sbenno }
1115