Date: Tue, 25 Jun 2002 13:24:31 -0400 (EDT) From: "Michael C. Adler" <mad1@tapil.com> To: FreeBSD-gnats-submit@FreeBSD.org Subject: bin/39849: /sbin/restore fails to overwrite files with schg flag set Message-ID: <200206251724.g5PHOVTg010586@grumpy.tapil.com>
next in thread | raw e-mail | index | archive | help
>Number: 39849
>Category: bin
>Synopsis: /sbin/restore fails to overwrite files with schg flag set
>Confidential: no
>Severity: serious
>Priority: medium
>Responsible: freebsd-bugs
>State: open
>Quarter:
>Keywords:
>Date-Required:
>Class: sw-bug
>Submitter-Id: current-users
>Arrival-Date: Tue Jun 25 10:30:01 PDT 2002
>Closed-Date:
>Last-Modified:
>Originator: Michael C. Adler
>Release: FreeBSD 4.6-RELEASE i386
>Organization:
>Environment:
System: FreeBSD grumpy.tapil.com 4.6-RELEASE FreeBSD 4.6-RELEASE #0: Tue Jun 18 11:30:24 EDT 2002 madler@grumpy.tapil.com:/usr/obj/usr/src/sys/GRUMPY i386
>Description:
Incremental restore fails to overwrite an older file that has a flag
set making the file immutable. (E.g. schg.) This became painfully
obvious when I did a level 0 dump/restore. Updated from 4.5 to 4.6
and then attempted a level 1 dump/restore to the same source/target
disk pair.
>How-To-Repeat:
Create a test tree with a file having the schg flag set. dump/restore
to a new partition with restore having -r -u set. Clear the schg flag
in the original file, modify the file and set the schg flag again. dump
(level 1) and do another restore -r -u to the target. Note that the
file does not get updated.
>Fix:
The following patch fixes the simplest cases. It forces unlink() and
rmdir() to clear the flags first. It forces rename() to clear all flags
and restore them after the file is renamed.
There is a remaining, more difficult, case that is not fixed. Imagine
directory X is tagged schg. If its contents are modified for the level
1 dump, restore would have to clear the schg flag on the directory before
restoring the files in X. I have not fixed this. It requires walking
up the path until you find a directory without an immutable flag, walking
back down to clear them and then resetting them all after the operation
is complete.
*** extern.h.~1~ Fri Aug 27 20:14:05 1999
--- extern.h Tue Jun 25 12:33:32 2002
***************
*** 54,59 ****
--- 54,62 ----
int extractfile __P((char *));
void findunreflinks __P((void));
char *flagvalues __P((struct entry *));
+ int forcerename __P((char *, char *));
+ int forcermdir __P((char *));
+ int forceunlink __P((char *));
void freeentry __P((struct entry *));
void freename __P((char *));
int genliteraldir __P((char *, ino_t));
*** tape.c.~1~ Tue Jun 18 09:09:34 2002
--- tape.c Tue Jun 25 11:52:28 2002
***************
*** 600,606 ****
return (GOOD);
}
if (uflag && !Nflag)
! (void)unlink(name);
if (mkfifo(name, mode) < 0) {
fprintf(stderr, "%s: cannot create fifo: %s\n",
name, strerror(errno));
--- 600,606 ----
return (GOOD);
}
if (uflag && !Nflag)
! (void)forceunlink(name);
if (mkfifo(name, mode) < 0) {
fprintf(stderr, "%s: cannot create fifo: %s\n",
name, strerror(errno));
***************
*** 622,628 ****
return (GOOD);
}
if (uflag)
! (void)unlink(name);
if (mknod(name, mode, (int)curfile.dip->di_rdev) < 0) {
fprintf(stderr, "%s: cannot create special file: %s\n",
name, strerror(errno));
--- 622,628 ----
return (GOOD);
}
if (uflag)
! (void)forceunlink(name);
if (mknod(name, mode, (int)curfile.dip->di_rdev) < 0) {
fprintf(stderr, "%s: cannot create special file: %s\n",
name, strerror(errno));
***************
*** 643,649 ****
return (GOOD);
}
if (uflag)
! (void)unlink(name);
if ((ofile = open(name, O_WRONLY | O_CREAT | O_TRUNC,
0666)) < 0) {
fprintf(stderr, "%s: cannot create file: %s\n",
--- 643,649 ----
return (GOOD);
}
if (uflag)
! (void)forceunlink(name);
if ((ofile = open(name, O_WRONLY | O_CREAT | O_TRUNC,
0666)) < 0) {
fprintf(stderr, "%s: cannot create file: %s\n",
*** utilities.c.~1~ Fri Sep 21 16:05:22 2001
--- utilities.c Tue Jun 25 12:34:14 2002
***************
*** 129,135 ****
renameit(from, to)
char *from, *to;
{
! if (!Nflag && rename(from, to) < 0) {
fprintf(stderr, "warning: cannot rename %s to %s: %s\n",
from, to, strerror(errno));
return;
--- 129,135 ----
renameit(from, to)
char *from, *to;
{
! if (!Nflag && forcerename(from, to) < 0) {
fprintf(stderr, "warning: cannot rename %s to %s: %s\n",
from, to, strerror(errno));
return;
***************
*** 173,179 ****
ep->e_flags |= REMOVED;
ep->e_flags &= ~TMPNAME;
cp = myname(ep);
! if (!Nflag && rmdir(cp) < 0) {
fprintf(stderr, "warning: %s: %s\n", cp, strerror(errno));
return;
}
--- 173,179 ----
ep->e_flags |= REMOVED;
ep->e_flags &= ~TMPNAME;
cp = myname(ep);
! if (!Nflag && forcermdir(cp) < 0) {
fprintf(stderr, "warning: %s: %s\n", cp, strerror(errno));
return;
}
***************
*** 194,200 ****
ep->e_flags |= REMOVED;
ep->e_flags &= ~TMPNAME;
cp = myname(ep);
! if (!Nflag && unlink(cp) < 0) {
fprintf(stderr, "warning: %s: %s\n", cp, strerror(errno));
return;
}
--- 194,200 ----
ep->e_flags |= REMOVED;
ep->e_flags &= ~TMPNAME;
cp = myname(ep);
! if (!Nflag && forceunlink(cp) < 0) {
fprintf(stderr, "warning: %s: %s\n", cp, strerror(errno));
return;
}
***************
*** 250,255 ****
--- 250,339 ----
vprintf(stdout, "Create %s link %s->%s\n",
type == SYMLINK ? "symbolic" : "hard", new, existing);
return (GOOD);
+ }
+
+ /*
+ * Make rename() work even if system flags prohibit it. Don't print warning
+ * messages here since caller may be calling without testing if the
+ * file exists.
+ */
+
+ int
+ forcerename(from, to)
+ char *from, *to;
+ {
+ struct stat sb;
+
+ if (!Nflag) {
+ if (stat(from, &sb) < 0) {
+ return -1;
+ }
+ if (sb.st_flags) {
+ chflags(from, 0);
+ }
+ if (rename(from, to) < 0) {
+ return -1;
+ }
+ if (sb.st_flags) {
+ return chflags(to, sb.st_flags);
+ }
+ }
+
+ return 0;
+ }
+
+ /*
+ * Make rmdir() work even if system flags prohibit it. Don't print warning
+ * messages here since caller may be calling without testing if the
+ * directory exists.
+ */
+
+ int
+ forcermdir(name)
+ char *name;
+ {
+ struct stat sb;
+
+ if (!Nflag) {
+ if (stat(name, &sb) < 0) {
+ return -1;
+ }
+ if (sb.st_flags) {
+ chflags(name, 0);
+ }
+ if (rmdir(name) < 0) {
+ return -1;
+ }
+ }
+
+ return 0;
+ }
+
+ /*
+ * Make unlink() work even if system flags prohibit it. Don't print warning
+ * messages here since caller may be calling without testing if the
+ * file exists.
+ */
+
+ int
+ forceunlink(name)
+ char *name;
+ {
+ struct stat sb;
+
+ if (!Nflag) {
+ if (stat(name, &sb) < 0) {
+ return -1;
+ }
+ if (sb.st_flags) {
+ chflags(name, 0);
+ }
+ if (unlink(name) < 0) {
+ return -1;
+ }
+ }
+
+ return 0;
}
/*
>Release-Note:
>Audit-Trail:
>Unformatted:
To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-bugs" in the body of the message
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200206251724.g5PHOVTg010586>
