Skip site navigation (1)Skip section navigation (2)
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>