version 1.67, 2018/09/16 02:44:07 |
version 1.68, 2019/02/08 12:53:44 |
|
|
#define NOCHANGEBITS (UF_IMMUTABLE | UF_APPEND | SF_IMMUTABLE | SF_APPEND) |
#define NOCHANGEBITS (UF_IMMUTABLE | UF_APPEND | SF_IMMUTABLE | SF_APPEND) |
#define BACKUP_SUFFIX ".old" |
#define BACKUP_SUFFIX ".old" |
|
|
int dobackup, docompare, dodest, dodir, dopreserve, dostrip, safecopy; |
int dobackup, docompare, dodest, dodir, dopreserve, dostrip; |
int mode = S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH; |
int mode = S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH; |
char pathbuf[PATH_MAX], tempfile[PATH_MAX]; |
char pathbuf[PATH_MAX], tempfile[PATH_MAX]; |
char *suffix = BACKUP_SUFFIX; |
char *suffix = BACKUP_SUFFIX; |
|
|
void install_dir(char *, int); |
void install_dir(char *, int); |
void strip(char *); |
void strip(char *); |
void usage(void); |
void usage(void); |
int create_newfile(char *, struct stat *); |
|
int create_tempfile(char *, char *, size_t); |
int create_tempfile(char *, char *, size_t); |
int file_write(int, char *, size_t, int *, int *, int); |
int file_write(int, char *, size_t, int *, int *, int); |
void file_flush(int, int); |
void file_flush(int, int); |
|
|
docompare = dopreserve = 1; |
docompare = dopreserve = 1; |
break; |
break; |
case 'S': |
case 'S': |
safecopy = 1; |
/* For backwards compatibility. */ |
break; |
break; |
case 's': |
case 's': |
dostrip = 1; |
dostrip = 1; |
|
|
argv += optind; |
argv += optind; |
|
|
/* some options make no sense when creating directories */ |
/* some options make no sense when creating directories */ |
if ((safecopy || docompare || dostrip) && dodir) |
if ((docompare || dostrip) && dodir) |
usage(); |
usage(); |
|
|
/* must have at least two arguments, except when creating directories */ |
/* must have at least two arguments, except when creating directories */ |
if (argc < 2 && !dodir) |
if (argc < 2 && !dodir) |
usage(); |
usage(); |
|
|
/* need to make a temp copy so we can compare stripped version */ |
|
if (docompare && dostrip) |
|
safecopy = 1; |
|
|
|
/* get group and owner id's */ |
/* get group and owner id's */ |
if (group != NULL && gid_from_group(group, &gid) == -1) { |
if (group != NULL && gid_from_group(group, &gid) == -1) { |
gid = strtonum(group, 0, GID_MAX, &errstr); |
gid = strtonum(group, 0, GID_MAX, &errstr); |
|
|
err(1, "%s", from_name); |
err(1, "%s", from_name); |
} |
} |
|
|
if (safecopy) { |
to_fd = create_tempfile(to_name, tempfile, sizeof(tempfile)); |
to_fd = create_tempfile(to_name, tempfile, sizeof(tempfile)); |
if (to_fd < 0) |
if (to_fd < 0) |
err(1, "%s", tempfile); |
err(1, "%s", tempfile); |
|
} else if (docompare && !dostrip) { |
|
if ((to_fd = open(to_name, O_RDONLY, 0)) < 0) |
|
err(1, "%s", to_name); |
|
} else { |
|
if ((to_fd = create_newfile(to_name, &to_sb)) < 0) |
|
err(1, "%s", to_name); |
|
} |
|
|
|
if (!devnull) { |
if (!devnull) |
if (docompare && !safecopy) { |
copy(from_fd, from_name, to_fd, tempfile, from_sb.st_size, |
files_match = !(compare(from_fd, from_name, |
((off_t)from_sb.st_blocks * S_BLKSIZE < from_sb.st_size)); |
from_sb.st_size, to_fd, |
|
to_name, to_sb.st_size)); |
|
|
|
/* Truncate "to" file for copy unless we match */ |
|
if (!files_match) { |
|
(void)close(to_fd); |
|
if ((to_fd = create_newfile(to_name, &to_sb)) < 0) |
|
err(1, "%s", to_name); |
|
} |
|
} |
|
if (!files_match) |
|
copy(from_fd, from_name, to_fd, |
|
safecopy ? tempfile : to_name, from_sb.st_size, |
|
((off_t)from_sb.st_blocks * S_BLKSIZE < from_sb.st_size)); |
|
} |
|
|
|
if (dostrip) { |
if (dostrip) { |
strip(safecopy ? tempfile : to_name); |
strip(tempfile); |
|
|
/* |
/* |
* Re-open our fd on the target, in case we used a strip |
* Re-open our fd on the target, in case we used a strip |
* that does not work in-place -- like gnu binutils strip. |
* that does not work in-place -- like gnu binutils strip. |
*/ |
*/ |
close(to_fd); |
close(to_fd); |
if ((to_fd = open(safecopy ? tempfile : to_name, O_RDONLY, |
if ((to_fd = open(tempfile, O_RDONLY, 0)) < 0) |
0)) < 0) |
|
err(1, "stripping %s", to_name); |
err(1, "stripping %s", to_name); |
} |
} |
|
|
/* |
/* |
* Compare the (possibly stripped) temp file to the target. |
* Compare the (possibly stripped) temp file to the target. |
*/ |
*/ |
if (safecopy && docompare) { |
if (docompare) { |
int temp_fd = to_fd; |
int temp_fd = to_fd; |
struct stat temp_sb; |
struct stat temp_sb; |
|
|
|
|
if ((gid != (gid_t)-1 || uid != (uid_t)-1) && |
if ((gid != (gid_t)-1 || uid != (uid_t)-1) && |
fchown(to_fd, uid, gid)) { |
fchown(to_fd, uid, gid)) { |
serrno = errno; |
serrno = errno; |
(void)unlink(safecopy ? tempfile : to_name); |
(void)unlink(tempfile); |
errx(1, "%s: chown/chgrp: %s", |
errx(1, "%s: chown/chgrp: %s", tempfile, strerror(serrno)); |
safecopy ? tempfile : to_name, strerror(serrno)); |
|
} |
} |
if (fchmod(to_fd, mode)) { |
if (fchmod(to_fd, mode)) { |
serrno = errno; |
serrno = errno; |
(void)unlink(safecopy ? tempfile : to_name); |
(void)unlink(tempfile); |
errx(1, "%s: chmod: %s", safecopy ? tempfile : to_name, |
errx(1, "%s: chmod: %s", tempfile, strerror(serrno)); |
strerror(serrno)); |
|
} |
} |
|
|
/* |
/* |
|
|
if (fchflags(to_fd, |
if (fchflags(to_fd, |
flags & SETFLAGS ? fset : from_sb.st_flags & ~UF_NODUMP)) { |
flags & SETFLAGS ? fset : from_sb.st_flags & ~UF_NODUMP)) { |
if (errno != EOPNOTSUPP || (from_sb.st_flags & ~UF_NODUMP) != 0) |
if (errno != EOPNOTSUPP || (from_sb.st_flags & ~UF_NODUMP) != 0) |
warnx("%s: chflags: %s", |
warnx("%s: chflags: %s", tempfile, strerror(errno)); |
safecopy ? tempfile :to_name, strerror(errno)); |
|
} |
} |
|
|
if (flags & USEFSYNC) |
if (flags & USEFSYNC) |
|
|
(void)close(from_fd); |
(void)close(from_fd); |
|
|
/* |
/* |
* Move the new file into place if doing a safe copy |
* Move the new file into place if the files are different |
* and the files are different (or just not compared). |
* or were not compared. |
*/ |
*/ |
if (safecopy && !files_match) { |
if (!files_match) { |
/* Try to turn off the immutable bits. */ |
/* Try to turn off the immutable bits. */ |
if (to_sb.st_flags & (NOCHANGEBITS)) |
if (to_sb.st_flags & (NOCHANGEBITS)) |
(void)chflags(to_name, to_sb.st_flags & ~(NOCHANGEBITS)); |
(void)chflags(to_name, to_sb.st_flags & ~(NOCHANGEBITS)); |
|
|
strlcat(p, "INS@XXXXXXXXXX", tsize); |
strlcat(p, "INS@XXXXXXXXXX", tsize); |
|
|
return(mkstemp(temp)); |
return(mkstemp(temp)); |
} |
|
|
|
/* |
|
* create_newfile -- |
|
* create a new file, overwriting an existing one if necessary |
|
*/ |
|
int |
|
create_newfile(char *path, struct stat *sbp) |
|
{ |
|
char backup[PATH_MAX]; |
|
|
|
/* |
|
* Unlink now... avoid ETXTBSY errors later. Try and 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) { |
|
(void)snprintf(backup, PATH_MAX, "%s%s", path, suffix); |
|
/* It is ok for the target file not to exist. */ |
|
if (rename(path, backup) < 0 && errno != ENOENT) |
|
err(1, "rename: %s to %s (errno %d)", path, backup, errno); |
|
} else { |
|
if (unlink(path) < 0 && errno != ENOENT) |
|
err(1, "%s", path); |
|
} |
|
|
|
return(open(path, O_CREAT | O_RDWR | O_EXCL, S_IRUSR | S_IWUSR)); |
|
} |
} |
|
|
/* |
/* |