Example #1
0
int
try_xopen(const char* pathname, int flags, mode_t mode)
{
    struct cleanup* cl = cleanup_allocate();
    int fd = open(pathname, flags | O_CLOEXEC, mode);
    if (fd == -1) {
        cleanup_forget(cl);
        return -1;
    }

    assert_cloexec(fd);
    cleanup_commit_close_fd(cl, fd);
    return fd;
}
Example #2
0
static void
do_xfer_recv(const struct xfer_opts xfer_opts,
             const char* filename,
             const char* desired_basename,
             int from_peer)
{
    struct xfer_msg statm = recv_xfer_msg(from_peer);
    if (statm.type != XFER_MSG_STAT)
        die(ECOMM, "expected stat msg");

    struct cleanup* error_cl = cleanup_allocate();
    struct stat st;
    const char* parent_directory = NULL;
    const char* rename_to = NULL;
    const char* write_mode = NULL;
    int dest_fd;

    if (stat(filename, &st) == 0) {
        if (S_ISDIR(st.st_mode)) {
            if (desired_basename == NULL)
                die(EISDIR, "\"%s\" is a directory", filename);
            parent_directory = filename;
            filename = xaprintf("%s/%s",
                                parent_directory,
                                desired_basename);
        } else if (S_ISREG(st.st_mode)) {
            if (st.st_nlink > 1)
                write_mode = "inplace";
        } else {
            write_mode = "inplace";
        }
    }

    if (parent_directory == NULL)
        parent_directory = xdirname(filename);

    if (write_mode == NULL)
        write_mode = xfer_opts.write_mode;

    bool atomic;
    bool automatic_mode = false;
    if (write_mode == NULL) {
        automatic_mode = true;
        atomic = true;
    } else if (strcmp(write_mode, "atomic") == 0) {
        atomic = true;
    } else if (strcmp(write_mode, "inplace") == 0) {
        atomic = false;
    } else {
        die(EINVAL, "unknown write mode \"%s\"", write_mode);
    }

    bool regular_file = true;
    bool preallocated = false;
    bool chmod_explicit = false;
    mode_t chmod_explicit_modes = 0;

    if (xfer_opts.preserve) {
        chmod_explicit = true;
        chmod_explicit_modes = statm.u.stat.ugo_bits;
    }

    if (xfer_opts.mode) {
        char* endptr = NULL;
        errno = 0;
        unsigned long omode = strtoul(xfer_opts.mode, &endptr, 8);
        if (errno != 0 || *endptr != '\0' || (omode &~ 0777) != 0)
            die(EINVAL, "invalid mode bits: %s", xfer_opts.mode);
        chmod_explicit = true;
        chmod_explicit_modes = (mode_t) omode;
    }

    mode_t creat_mode = (chmod_explicit_modes ? 0200 : 0666);

    if (atomic) {
        rename_to = filename;
        filename =
            xaprintf("%s.fb-adb-%s",
                     filename,
                     gen_hex_random(ENOUGH_ENTROPY));
        dest_fd = try_xopen(
            filename,
            O_CREAT | O_WRONLY | O_EXCL,
            creat_mode);
        if (dest_fd == -1) {
            if (errno == EACCES && automatic_mode) {
                atomic = false;
                filename = rename_to;
                rename_to = NULL;
            } else {
                die_errno("open(\"%s\")", filename);
            }
        }
    }

    if (!atomic) {
        dest_fd = xopen(filename, O_WRONLY | O_CREAT | O_TRUNC, creat_mode);
        if (!S_ISREG(xfstat(dest_fd).st_mode))
            regular_file = false;
    }

    if (regular_file)
        cleanup_commit(error_cl, unlink_cleanup, filename);

    if (regular_file && statm.u.stat.size > 0)
        preallocated = fallocate_if_supported(
            dest_fd,
            statm.u.stat.size);

    uint64_t total_written = copy_loop_posix_recv(from_peer, dest_fd);

    if (preallocated && total_written < statm.u.stat.size)
        xftruncate(dest_fd, total_written);

    if (xfer_opts.preserve) {
        struct timeval times[2] = {
            { statm.u.stat.atime, statm.u.stat.atime_ns / 1000 },
            { statm.u.stat.mtime, statm.u.stat.mtime_ns / 1000 },
        };
#ifdef HAVE_FUTIMES
        if (futimes(dest_fd, times) == -1)
            die_errno("futimes");
#else
        if (utimes(filename, times) == -1)
            die_errno("times");
#endif
    }

    if (chmod_explicit)
        if (fchmod(dest_fd, chmod_explicit_modes) == -1)
            die_errno("fchmod");

    if (xfer_opts.sync)
        xfsync(dest_fd);

    if (rename_to)
        xrename(filename, rename_to);

    if (xfer_opts.sync)
        xfsync(xopen(parent_directory, O_DIRECTORY|O_RDONLY, 0));

    cleanup_forget(error_cl);
}