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>