pax: don't overwrite destination if -r -w copy fails
Add more error handling to pax -r -w so that any failure during the copy to the temporary file (including a failed flush) prevents any existing destination file from being replaced with the partial (including possibly empty) temporary file. The partial temporary file is removed. pax still exists non-zero. Thanks to Michael van Elst (mlelstv@) for the analysis of the problem in the PR. Should fix PR misc/33753.
This commit is contained in:
parent
81f726b0e0
commit
d2813c5205
|
@ -1,4 +1,4 @@
|
|||
/* $NetBSD: ar_subs.c,v 1.57 2021/12/05 02:52:17 msaitoh Exp $ */
|
||||
/* $NetBSD: ar_subs.c,v 1.58 2023/05/28 21:42:40 lukem Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 1992 Keith Muller.
|
||||
|
@ -42,7 +42,7 @@
|
|||
#if 0
|
||||
static char sccsid[] = "@(#)ar_subs.c 8.2 (Berkeley) 4/18/94";
|
||||
#else
|
||||
__RCSID("$NetBSD: ar_subs.c,v 1.57 2021/12/05 02:52:17 msaitoh Exp $");
|
||||
__RCSID("$NetBSD: ar_subs.c,v 1.58 2023/05/28 21:42:40 lukem Exp $");
|
||||
#endif
|
||||
#endif /* not lint */
|
||||
|
||||
|
@ -1131,10 +1131,14 @@ copy(void)
|
|||
}
|
||||
|
||||
/*
|
||||
* copy source file data to the destination file
|
||||
* copy source file data to the destination file.
|
||||
* if there was a failure, remove the temporary file
|
||||
* and leave any existing destination file unmodified.
|
||||
*/
|
||||
cp_file(arcn, fdsrc, fddest);
|
||||
file_close(arcn, fddest);
|
||||
if (cp_file(arcn, fdsrc, fddest) < 0)
|
||||
file_cleanup(arcn, fddest);
|
||||
else
|
||||
file_close(arcn, fddest);
|
||||
rdfile_close(arcn, &fdsrc);
|
||||
|
||||
if (vflag && vfpart) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* $NetBSD: buf_subs.c,v 1.30 2022/05/28 10:36:21 andvar Exp $ */
|
||||
/* $NetBSD: buf_subs.c,v 1.31 2023/05/28 21:42:40 lukem Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 1992 Keith Muller.
|
||||
|
@ -42,7 +42,7 @@
|
|||
#if 0
|
||||
static char sccsid[] = "@(#)buf_subs.c 8.2 (Berkeley) 4/18/94";
|
||||
#else
|
||||
__RCSID("$NetBSD: buf_subs.c,v 1.30 2022/05/28 10:36:21 andvar Exp $");
|
||||
__RCSID("$NetBSD: buf_subs.c,v 1.31 2023/05/28 21:42:40 lukem Exp $");
|
||||
#endif
|
||||
#endif /* not lint */
|
||||
|
||||
|
@ -745,8 +745,11 @@ rd_wrfile(ARCHD *arcn, int ofd, off_t *left)
|
|||
* written. just closing with the file offset moved forward may not put
|
||||
* a hole at the end of the file.
|
||||
*/
|
||||
if (ofd >= 0 && isem && (arcn->sb.st_size > 0L))
|
||||
file_flush(ofd, fnm, isem);
|
||||
if (ofd >= 0 && isem && (arcn->sb.st_size > 0L)) {
|
||||
if (file_flush(ofd, fnm, isem) < 0) {
|
||||
/* write flush errors are not an error here */;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* if we failed from archive read, we do not want to skip
|
||||
|
@ -758,9 +761,11 @@ rd_wrfile(ARCHD *arcn, int ofd, off_t *left)
|
|||
* some formats record a crc on file data. If so, then we compare the
|
||||
* calculated crc to the crc stored in the archive
|
||||
*/
|
||||
if (docrc && (size == 0L) && (arcn->crc != crc))
|
||||
if (docrc && (size == 0L) && (arcn->crc != crc)) {
|
||||
tty_warn(1,"Actual crc does not match expected crc %s",
|
||||
arcn->name);
|
||||
/* crc warning is not an error */
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -769,9 +774,11 @@ rd_wrfile(ARCHD *arcn, int ofd, off_t *left)
|
|||
* copy the contents of one file to another. used during -rw phase of pax
|
||||
* just as in rd_wrfile() we use a special write function to write the
|
||||
* destination file so we can properly copy files with holes.
|
||||
* Return:
|
||||
* 0 if ok, -1 if any error.
|
||||
*/
|
||||
|
||||
void
|
||||
int
|
||||
cp_file(ARCHD *arcn, int fd1, int fd2)
|
||||
{
|
||||
int cnt;
|
||||
|
@ -783,6 +790,7 @@ cp_file(ARCHD *arcn, int fd1, int fd2)
|
|||
int rem;
|
||||
int sz = MINFBSZ;
|
||||
struct stat sb, origsb;
|
||||
int rv = 0;
|
||||
|
||||
/*
|
||||
* check for holes in the source file. If none, we will use regular
|
||||
|
@ -797,8 +805,10 @@ cp_file(ARCHD *arcn, int fd1, int fd2)
|
|||
* if Mflag is set, use the actual mtime instead.
|
||||
*/
|
||||
origsb = arcn->sb;
|
||||
if (Mflag && (fstat(fd1, &origsb) < 0))
|
||||
if (Mflag && (fstat(fd1, &origsb) < 0)) {
|
||||
syswarn(1, errno, "Failed stat on %s", arcn->org_name);
|
||||
rv = -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* pass the blocksize of the file being written to the write routine,
|
||||
|
@ -830,17 +840,22 @@ cp_file(ARCHD *arcn, int fd1, int fd2)
|
|||
/*
|
||||
* check to make sure the copy is valid.
|
||||
*/
|
||||
if (res < 0)
|
||||
if (res < 0) {
|
||||
syswarn(1, errno, "Failed write during copy of %s to %s",
|
||||
arcn->org_name, arcn->name);
|
||||
else if (cpcnt != arcn->sb.st_size)
|
||||
rv = -1;
|
||||
} else if (cpcnt != arcn->sb.st_size) {
|
||||
tty_warn(1, "File %s changed size during copy to %s",
|
||||
arcn->org_name, arcn->name);
|
||||
else if (fstat(fd1, &sb) < 0)
|
||||
rv = -1;
|
||||
} else if (fstat(fd1, &sb) < 0) {
|
||||
syswarn(1, errno, "Failed stat of %s", arcn->org_name);
|
||||
else if (origsb.st_mtime != sb.st_mtime)
|
||||
rv = -1;
|
||||
} else if (origsb.st_mtime != sb.st_mtime) {
|
||||
tty_warn(1, "File %s was modified during copy to %s",
|
||||
arcn->org_name, arcn->name);
|
||||
rv = -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* if the last block has a file hole (all zero), we must make sure this
|
||||
|
@ -848,9 +863,11 @@ cp_file(ARCHD *arcn, int fd1, int fd2)
|
|||
* written. just closing with the file offset moved forward may not put
|
||||
* a hole at the end of the file.
|
||||
*/
|
||||
if (!no_hole && isem && (arcn->sb.st_size > 0L))
|
||||
file_flush(fd2, fnm, isem);
|
||||
return;
|
||||
if (!no_hole && isem && (arcn->sb.st_size > 0L)) {
|
||||
if (file_flush(fd2, fnm, isem) < 0)
|
||||
rv = -1;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* $NetBSD: extern.h,v 1.60 2020/04/03 16:13:32 joerg Exp $ */
|
||||
/* $NetBSD: extern.h,v 1.61 2023/05/28 21:42:40 lukem Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 1992 Keith Muller.
|
||||
|
@ -115,7 +115,7 @@ int rd_wrbuf(char *, int);
|
|||
int wr_skip(off_t);
|
||||
int wr_rdfile(ARCHD *, int, off_t *);
|
||||
int rd_wrfile(ARCHD *, int, off_t *);
|
||||
void cp_file(ARCHD *, int, int);
|
||||
int cp_file(ARCHD *, int, int);
|
||||
int buf_fill(void);
|
||||
int buf_flush(int);
|
||||
|
||||
|
@ -150,6 +150,7 @@ extern char *gnu_name_string, *gnu_link_string;
|
|||
extern size_t gnu_name_length, gnu_link_length;
|
||||
extern char *xtmp_name;
|
||||
int file_creat(ARCHD *, int);
|
||||
void file_cleanup(ARCHD *, int);
|
||||
void file_close(ARCHD *, int);
|
||||
int lnk_creat(ARCHD *, int *);
|
||||
int cross_lnk(ARCHD *);
|
||||
|
@ -162,7 +163,7 @@ int set_ids(char *, uid_t, gid_t);
|
|||
void set_pmode(char *, mode_t);
|
||||
void set_chflags(char *fnm, u_int32_t flags);
|
||||
int file_write(int, char *, int, int *, int *, int, char *);
|
||||
void file_flush(int, char *, int);
|
||||
int file_flush(int, char *, int);
|
||||
void rdfile_close(ARCHD *, int *);
|
||||
int set_crc(ARCHD *, int);
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* $NetBSD: file_subs.c,v 1.64 2019/03/20 03:13:39 gutteridge Exp $ */
|
||||
/* $NetBSD: file_subs.c,v 1.65 2023/05/28 21:42:40 lukem Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 1992 Keith Muller.
|
||||
|
@ -42,7 +42,7 @@
|
|||
#if 0
|
||||
static char sccsid[] = "@(#)file_subs.c 8.1 (Berkeley) 5/31/93";
|
||||
#else
|
||||
__RCSID("$NetBSD: file_subs.c,v 1.64 2019/03/20 03:13:39 gutteridge Exp $");
|
||||
__RCSID("$NetBSD: file_subs.c,v 1.65 2023/05/28 21:42:40 lukem Exp $");
|
||||
#endif
|
||||
#endif /* not lint */
|
||||
|
||||
|
@ -178,12 +178,44 @@ file_creat(ARCHD *arcn, int write_to_hardlink)
|
|||
return fd;
|
||||
}
|
||||
|
||||
/*
|
||||
* file_cleanup()
|
||||
* Close file descriptor to a file just created by pax.
|
||||
* Unlinks any temporary file.
|
||||
*/
|
||||
|
||||
void
|
||||
file_cleanup(ARCHD *arcn, int fd)
|
||||
{
|
||||
char *tmp_name;
|
||||
|
||||
if (fd < 0)
|
||||
return;
|
||||
|
||||
tmp_name = (arcn->tmp_name != NULL) ? arcn->tmp_name : arcn->name;
|
||||
|
||||
if (close(fd) < 0)
|
||||
syswarn(0, errno, "Cannot close file descriptor on %s",
|
||||
tmp_name);
|
||||
|
||||
/* No temporary file to cleanup? */
|
||||
if (arcn->tmp_name == NULL)
|
||||
return;
|
||||
|
||||
/* Cleanup the temporary file. */
|
||||
if (unlink(arcn->tmp_name) < 0) {
|
||||
syswarn(0, errno, "Cannot unlink %s", arcn->tmp_name);
|
||||
}
|
||||
|
||||
free(arcn->tmp_name);
|
||||
arcn->tmp_name = NULL;
|
||||
xtmp_name = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* file_close()
|
||||
* Close file descriptor to a file just created by pax. Sets modes,
|
||||
* ownership and times as required.
|
||||
* Return:
|
||||
* 0 for success, -1 for failure
|
||||
*/
|
||||
|
||||
void
|
||||
|
@ -1039,9 +1071,11 @@ file_write(int fd, char *str, int cnt, int *rem, int *isempt, int sz,
|
|||
* when the last file block in a file is zero, many file systems will not
|
||||
* let us create a hole at the end. To get the last block with zeros, we
|
||||
* write the last BYTE with a zero (back up one byte and write a zero).
|
||||
* Return:
|
||||
* 0 if was able to flush the file, -1 otherwise
|
||||
*/
|
||||
|
||||
void
|
||||
int
|
||||
file_flush(int fd, char *fname, int isempt)
|
||||
{
|
||||
static char blnk[] = "\0";
|
||||
|
@ -1051,19 +1085,21 @@ file_flush(int fd, char *fname, int isempt)
|
|||
* filled with all zeros.
|
||||
*/
|
||||
if (!isempt)
|
||||
return;
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* move back one byte and write a zero
|
||||
*/
|
||||
if (lseek(fd, (off_t)-1, SEEK_CUR) < 0) {
|
||||
syswarn(1, errno, "Failed seek on file %s", fname);
|
||||
return;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (write_with_restart(fd, blnk, 1) < 0)
|
||||
if (write_with_restart(fd, blnk, 1) < 0) {
|
||||
syswarn(1, errno, "Failed write to file %s", fname);
|
||||
return;
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
Loading…
Reference in New Issue