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