Applied to original from file_cmds-430.140.2 --- xinstall.c.orig 2024-08-20 16:07:42 +++ xinstall.c 2025-08-18 10:03:32 @@ -70,9 +70,9 @@ __used static const char rcsid[] = #ifdef __APPLE__ #include #include +#include +#include #endif /* __APPLE__ */ - -#include "pathnames.h" /* Bootstrap aid - this doesn't exist in most older releases */ #ifndef MAP_FAILED @@ -92,6 +92,8 @@ char *suffix = BACKUP_SUFFIX; mode_t mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; char *suffix = BACKUP_SUFFIX; +static int clone __P((const char *, const char *, int, char *, size_t)); +static void handle_existing_file __P((char *path, struct stat *sbp)); void copy __P((int, char *, int, char *, off_t)); int compare __P((int, const char *, size_t, int, const char *, size_t)); int create_newfile __P((char *, int, struct stat *)); @@ -100,6 +102,7 @@ void strip __P((char *)); void install_dir __P((char *)); u_long numeric_id __P((char *, char *)); void strip __P((char *)); +static void tempfile_template __P((const char *, char *, size_t)); int trymmap __P((int)); void usage __P((void)); @@ -276,9 +279,10 @@ install(char *from_name, char *to_name, { struct stat from_sb, temp_sb, to_sb; struct utimbuf utb; - int devnull, files_match, from_fd=0, serrno, target; - int tempcopy, temp_fd, to_fd=0; + int devnull, files_match, from_fd=-1, serrno, target; + int tempcopy, temp_fd=-1, to_fd=-1; char backup[MAXPATHLEN], *p, pathbuf[MAXPATHLEN], tempfile[MAXPATHLEN]; + const int tryclone = 1; files_match = 0; @@ -334,6 +338,20 @@ install(char *from_name, char *to_name, } if (!files_match) { + int done_clone = 0; + if (tryclone && !devnull && !dostrip) { + if (!tempcopy && target) { + handle_existing_file(to_name, &to_sb); + } + done_clone = (clone(from_name, to_name, tempcopy, tempfile, sizeof(tempfile)) == 0); + } + if (done_clone && + (((to_fd = open(tempcopy ? tempfile : to_name, O_RDONLY, 0)) < 0) + || (!tempcopy && fstat(to_fd, &to_sb) == -1))) { + (void)unlink(tempcopy ? tempfile : to_name); + done_clone = 0; + } + if (!done_clone) { if (tempcopy) { to_fd = create_tempfile(to_name, tempfile, sizeof(tempfile)); @@ -350,6 +368,7 @@ install(char *from_name, char *to_name, if (!devnull) copy(from_fd, from_name, to_fd, tempcopy ? tempfile : to_name, from_sb.st_size); + } /* !done_clone */ } if (dostrip) { @@ -592,11 +611,11 @@ compare(int from_fd, const char *from_name, size_t fro } /* - * create_tempfile -- - * create a temporary file based on path and open it + * tempfile_template -- + * prepare a template filename for use with mktemp/mkstemp. */ -int -create_tempfile(char *path, +static void +tempfile_template(const char *path, char *temp, size_t tsize) { @@ -610,7 +629,49 @@ create_tempfile(char *path, p = temp; (void)strncpy(p, "INS@XXXX", &temp[tsize - 1] - p); temp[tsize - 1] = '\0'; +} + +/* + * create_tempfile -- + * create a temporary file based on path and open it + */ +int +create_tempfile(char *path, + char *temp, + size_t tsize) +{ + tempfile_template(path, temp, tsize); return (mkstemp(temp)); +} + +/* unlink or move (backup) an existing file */ +static void +handle_existing_file(char *path, struct stat *sbp) +{ + char backup[MAXPATHLEN]; + /* + * Unlink now... avoid ETXTBSY errors later. Try to turn + * off the append/immutable bits -- if we fail, go ahead, + * it might work. + */ + if (sbp->st_flags & NOCHANGEBITS) + (void)chflags(path, sbp->st_flags & ~NOCHANGEBITS); + + if (dobackup) { + if (snprintf(backup, MAXPATHLEN, "%s%s", + path, suffix) != strlen(path) + strlen(suffix)) + errx(EX_OSERR, "%s: backup filename too long", + path); + (void)snprintf(backup, MAXPATHLEN, "%s%s", + path, suffix); + if (verbose) + (void)printf("install: %s -> %s\n", + path, backup); + if (rename(path, backup) < 0) + err(EX_OSERR, "rename: %s to %s", path, backup); + } else { + (void)unlink(path); + } } /* @@ -622,31 +683,8 @@ create_newfile(char *path, int target, struct stat *sbp) { - char backup[MAXPATHLEN]; - if (target) { - /* - * Unlink now... avoid ETXTBSY errors later. Try to turn - * off the append/immutable bits -- if we fail, go ahead, - * it might work. - */ - if (sbp->st_flags & NOCHANGEBITS) - (void)chflags(path, sbp->st_flags & ~NOCHANGEBITS); - - if (dobackup) { - if (snprintf(backup, MAXPATHLEN, "%s%s", - path, suffix) != strlen(path) + strlen(suffix)) - errx(EX_OSERR, "%s: backup filename too long", - path); - (void)snprintf(backup, MAXPATHLEN, "%s%s", - path, suffix); - if (verbose) - (void)printf("install: %s -> %s\n", - path, backup); - if (rename(path, backup) < 0) - err(EX_OSERR, "rename: %s to %s", path, backup); - } else - (void)unlink(path); + handle_existing_file(path, sbp); } return (open(path, O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR)); @@ -736,6 +774,22 @@ copy(int from_fd, char *from_name, } /* + * clone -- + * create a clone of a file + */ +static int +clone(const char *from_name, const char *to_name, + int use_temp, char *temp_name, size_t tsize) +{ + if (use_temp) { + tempfile_template(to_name, temp_name, tsize); + mktemp(temp_name); + to_name = temp_name; + } + return clonefile(from_name, to_name, CLONE_NOOWNERCOPY); +} + +/* * strip -- * use strip(1) to strip the target file */