diff --git a/racket/src/racket/src/file.c b/racket/src/racket/src/file.c index 7191393017..e695e90e73 100644 --- a/racket/src/racket/src/file.c +++ b/racket/src/racket/src/file.c @@ -3472,6 +3472,18 @@ static Scheme_Object *copy_file(int argc, Scheme_Object **argv) ok = 1; } } + + if (ok) { + if (!rktio_copy_file_finish_permissions(scheme_rktio, cf)) { + rktio_copy_file_stop(scheme_rktio, cf); + scheme_raise_exn(MZEXN_FAIL_FILESYSTEM, + "copy-file: cannot set destination's permissions\n" + " source path: %q\n" + " destination path: %q", + filename_for_error(argv[0]), + filename_for_error(argv[1])); + } + } rktio_copy_file_stop(scheme_rktio, cf); if (ok) return scheme_void; diff --git a/racket/src/rktio/rktio.h b/racket/src/rktio/rktio.h index 5fd7935c8f..19319d9c8b 100644 --- a/racket/src/rktio/rktio.h +++ b/racket/src/rktio/rktio.h @@ -745,11 +745,25 @@ typedef struct rktio_file_copy_t rktio_file_copy_t; RKTIO_EXTERN rktio_file_copy_t *rktio_copy_file_start(rktio_t *rktio, const char *dest, const char *src, rktio_bool_t exists_ok); -/* Can report `RKTIO_ERROR_EXISTS`. */ +/* Starts a file copy. Depending on the OS, this step may perform the + whole copy, or it may just get started. Can report + `RKTIO_ERROR_EXISTS`. */ -RKTIO_EXTERN int rktio_copy_file_is_done(rktio_t *rktio, rktio_file_copy_t *fc); +RKTIO_EXTERN rktio_bool_t rktio_copy_file_is_done(rktio_t *rktio, rktio_file_copy_t *fc); RKTIO_EXTERN rktio_ok_t rktio_copy_file_step(rktio_t *rktio, rktio_file_copy_t *fc); +/* As long as the copy isn't done, call `rktio_copy_file_step` to make + a little progress. Use `rktio_copy_file_finish_permissions` (optionally) + and then `rktio_copy_file_stop` when done. */ + +RKTIO_EXTERN rktio_ok_t rktio_copy_file_finish_permissions(rktio_t *rktio, rktio_file_copy_t *fc); +/* Depending on the OS, copies permissions from the source to the + destination. This step can be performed at any time between the + start and stop. Reports success if this step isn't needed (e.g., + where a copy fully completes when it is started). */ + RKTIO_EXTERN void rktio_copy_file_stop(rktio_t *rktio, rktio_file_copy_t *fc); +/* Deallocates the copy process, interrupting it if the copy is not + complete. */ /*************************************************/ /* System paths */ diff --git a/racket/src/rktio/rktio_fs.c b/racket/src/rktio/rktio_fs.c index 84313b434b..2c2838e2ca 100644 --- a/racket/src/rktio/rktio_fs.c +++ b/racket/src/rktio/rktio_fs.c @@ -1509,6 +1509,9 @@ void rktio_directory_list_stop(rktio_t *rktio, rktio_directory_list_t *dl) struct rktio_file_copy_t { int done; rktio_fd_t *src_fd, *dest_fd; +#ifdef RKTIO_SYSTEM_UNIX + intptr_t mode; +#endif }; rktio_file_copy_t *rktio_copy_file_start(rktio_t *rktio, const char *dest, const char *src, int exists_ok) @@ -1552,6 +1555,7 @@ rktio_file_copy_t *rktio_copy_file_start(rktio_t *rktio, const char *dest, const fc->done = 0; fc->src_fd = src_fd; fc->dest_fd = dest_fd; + fc->mode = buf.st_mode; return fc; } @@ -1583,12 +1587,12 @@ rktio_file_copy_t *rktio_copy_file_start(rktio_t *rktio, const char *dest, const #endif } -int rktio_copy_file_is_done(rktio_t *rktio, rktio_file_copy_t *fc) +rktio_bool_t rktio_copy_file_is_done(rktio_t *rktio, rktio_file_copy_t *fc) { return fc->done; } -int rktio_copy_file_step(rktio_t *rktio, rktio_file_copy_t *fc) +rktio_ok_t rktio_copy_file_step(rktio_t *rktio, rktio_file_copy_t *fc) { #ifdef RKTIO_SYSTEM_UNIX char buffer[4096]; @@ -1620,6 +1624,23 @@ int rktio_copy_file_step(rktio_t *rktio, rktio_file_copy_t *fc) #endif } +rktio_ok_t rktio_copy_file_finish_permissions(rktio_t *rktio, rktio_file_copy_t *fc) +{ +#ifdef RKTIO_SYSTEM_UNIX + int err; + + do { + err = fchmod(rktio_fd_system_fd(rktio, fc->dest_fd), fc->mode); + } while ((err == -1) && (errno != EINTR)); + + if (err) { + get_posix_error(); + return 0; + } +#endif + return 1; +} + void rktio_copy_file_stop(rktio_t *rktio, rktio_file_copy_t *fc) { #ifdef RKTIO_SYSTEM_UNIX