diff --git a/usr.sbin/kldxref/kldxref.c b/usr.sbin/kldxref/kldxref.c --- a/usr.sbin/kldxref/kldxref.c +++ b/usr.sbin/kldxref/kldxref.c @@ -49,6 +49,7 @@ #include #include #include +#include #include #include #include @@ -134,7 +135,7 @@ int error; size_t len; u_char val; - + if (dflag) return (0); val = len = strlen(str); @@ -657,6 +658,43 @@ return (fdopen(fd, "w+")); } +/* + * Copy an existing file identified by its path to the path specified. The + * destination is first truncated if it already exists. On error, a warning + * is written to stderr with the details and the function returns false. + */ +static bool +copyfile(char *src, const char *dst) +{ + bool retval = true; + int ival, src_fd, dst_fd; + + src_fd = open(src, O_RDONLY); + if (src_fd < 0) { + warn("can't open %s", src); + return (1); + } + dst_fd = open(dst, O_WRONLY | O_CREAT | O_TRUNC); + if (dst_fd < 0) { + warn("can't create %s", dst); + close(src_fd); + return (1); + } + + while ((ival = copy_file_range(src_fd, NULL, dst_fd, + NULL, SSIZE_MAX, 0)) != 0) { + if (ival < 0) { + warn("can't copy %s to %s", src, dst); + retval = false; + break; + } + } + + close(src_fd); + close(dst_fd); + return (retval); +} + static char xrefname[MAXPATHLEN], tempname[MAXPATHLEN]; static void @@ -732,7 +770,16 @@ fclose(fxref); fxref = NULL; if (reccnt != 0) { - rename(tempname, xrefname); + if (rename(tempname, xrefname) != 0) { + /* handle src/dst being on a different filesystems */ + if (errno == EXDEV) { + /* copyfile reports its own errors */ + copyfile(tempname, xrefname); + } else { + warn("can't rename %s to %s", xrefname, tempname); + } + unlink(tempname); + } } else { /* didn't find any entry, ignore this file */ unlink(tempname);