Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

files module: safe rm



This patch adds the -s option to rm which will not follow symbolic links.
It also adds the necessary checks after chdir("..");

For example rm -s /l/foo will fail if /l is a symlink.

The -s option can also be used with cd to ignore symlinks.

I tried to optimise the number of system calls in rm -s (but it's still
far from optimal).

Zoltan


*** Src/builtin.c	1997/01/02 18:58:06	3.1.1.4
--- Src/builtin.c	1997/01/04 23:59:52
***************
*** 885,890 ****
--- 885,892 ----
      struct stat st1, st2;
  
      doprintdir = (doprintdir == -1);
+     if (**argv == '-' && (*argv)[1] == 's' && (*argv)[2] == '\0')
+ 	ops['s'] = 1, argv++;
  
      PERMALLOC {
  	pushnode(dirstack, ztrdup(pwd));
***************
*** 985,991 ****
      if (!dir) {
  	dir = firstnode(dirstack);
      }
!     if (!(dest = cd_do_chdir(nam, getdata(dir)))) {
  	if (!target)
  	    zsfree(getlinknode(dirstack));
  	if (func == BIN_POPD)
--- 987,993 ----
      if (!dir) {
  	dir = firstnode(dirstack);
      }
!     if (!(dest = cd_do_chdir(nam, getdata(dir), ops['s']))) {
  	if (!target)
  	    zsfree(getlinknode(dirstack));
  	if (func == BIN_POPD)
***************
*** 1007,1013 ****
  
  /**/
  char *
! cd_do_chdir(char *cnam, char *dest)
  {
      char **pp, *ret;
      int hasdot = 0, eno = ENOENT;
--- 1009,1015 ----
  
  /**/
  char *
! cd_do_chdir(char *cnam, char *dest, int hard)
  {
      char **pp, *ret;
      int hasdot = 0, eno = ENOENT;
***************
*** 1020,1026 ****
  
      /* if we have an absolute path, use it as-is only */
      if (*dest == '/') {
! 	if ((ret = cd_try_chdir(NULL, dest)))
  	    return ret;
  	zwarnnam(cnam, "%e: %s", dest, errno);
  	return NULL;
--- 1022,1028 ----
  
      /* if we have an absolute path, use it as-is only */
      if (*dest == '/') {
! 	if ((ret = cd_try_chdir(NULL, dest, hard)))
  	    return ret;
  	zwarnnam(cnam, "%e: %s", dest, errno);
  	return NULL;
***************
*** 1034,1040 ****
      /* if there is no . in cdpath (or it is not being used), try the directory
      as-is (i.e. from .) */
      if (!hasdot) {
! 	if ((ret = cd_try_chdir(NULL, dest)))
  	    return ret;
  	if (errno != ENOENT)
  	    eno = errno;
--- 1036,1042 ----
      /* if there is no . in cdpath (or it is not being used), try the directory
      as-is (i.e. from .) */
      if (!hasdot) {
! 	if ((ret = cd_try_chdir(NULL, dest, hard)))
  	    return ret;
  	if (errno != ENOENT)
  	    eno = errno;
***************
*** 1043,1049 ****
      cdpath in turn */
      if (!nocdpath)
  	for (pp = cdpath; *pp; pp++) {
! 	    if ((ret = cd_try_chdir(*pp, dest))) {
  		if (strcmp(*pp, ".")) {
  		    doprintdir++;
  		}
--- 1045,1051 ----
      cdpath in turn */
      if (!nocdpath)
  	for (pp = cdpath; *pp; pp++) {
! 	    if ((ret = cd_try_chdir(*pp, dest, hard))) {
  		if (strcmp(*pp, ".")) {
  		    doprintdir++;
  		}
***************
*** 1055,1061 ****
  
      /* handle the CDABLEVARS option */
      if ((ret = cd_able_vars(dest))) {
! 	if ((ret = cd_try_chdir(NULL, ret))) {
  	    doprintdir++;
  	    return ret;
  	}
--- 1057,1063 ----
  
      /* handle the CDABLEVARS option */
      if ((ret = cd_able_vars(dest))) {
! 	if ((ret = cd_try_chdir(NULL, ret,hard))) {
  	    doprintdir++;
  	    return ret;
  	}
***************
*** 1103,1109 ****
  
  /**/
  char *
! cd_try_chdir(char *pfix, char *dest)
  {
      char *buf;
  
--- 1105,1111 ----
  
  /**/
  char *
! cd_try_chdir(char *pfix, char *dest, int hard)
  {
      char *buf;
  
***************
*** 1136,1142 ****
      /* Normalise path.  See the definition of fixdir() for what this means. */
      fixdir(buf);
  
!     if (zchdir(buf)) {
  	zsfree(buf);
  	return NULL;
      }
--- 1138,1144 ----
      /* Normalise path.  See the definition of fixdir() for what this means. */
      fixdir(buf);
  
!     if (lchdir(buf, NULL, hard)) {
  	zsfree(buf);
  	return NULL;
      }
*** Src/utils.c	1997/01/03 01:49:22	3.1.1.12
--- Src/utils.c	1997/01/04 23:59:44
***************
*** 3354,3366 ****
  {
      char buf[PATH_MAX];
      char *s;
  
      while (n > 0) {
  	for (s = buf; s < buf + PATH_MAX - 4 && n--; )
  	    *s++ = '.', *s++ = '.', *s++ = '/';
  	s[-1] = '\0';
  	if (chdir(buf))
! 	    return -1;
      }
      return 0;
  }
--- 3354,3368 ----
  {
      char buf[PATH_MAX];
      char *s;
+     int err = -1;
  
      while (n > 0) {
  	for (s = buf; s < buf + PATH_MAX - 4 && n--; )
  	    *s++ = '.', *s++ = '.', *s++ = '/';
  	s[-1] = '\0';
  	if (chdir(buf))
! 	    return err;
! 	err = -2;
      }
      return 0;
  }
***************
*** 3372,3397 ****
  
  /**/
  int
! lchdir(char const *path)
  {
- #ifndef HAVE_LSTAT
-     return zchdir(path);
- #else /* HAVE_LSTAT */
-     char buf[PATH_MAX + 1], *ptr;
      char const *pptr;
!     int olddir = open(".", O_RDONLY), level, err;
!     struct stat st1, st2;
  
!     if(*path == '/') {
! 	chdir("/");
  	level = -1;
!     } else
  	level = 0;
      for(;;) {
  	while(*path == '/')
  	    path++;
  	if(!*path) {
! 	    close(olddir);
  	    return 0;
  	}
  	for(pptr = path; *++pptr && *pptr != '/'; ) ;
--- 3374,3440 ----
  
  /**/
  int
! lchdir(char const *path, struct dirsav *d, int hard)
  {
      char const *pptr;
!     int level;
!     struct stat st1;
!     struct dirsav ds;
! #ifdef HAVE_LSTAT
!     char buf[PATH_MAX + 1], *ptr;
!     int err;
!     struct stat st2;
! #endif
  
!     if (!d) {
! 	ds.ino = ds.dev = 0;
! 	ds.dirname = NULL;
! 	ds.dirfd = -1;
! 	d = &ds;
!     }
! #ifdef HAVE_LSTAT
!     if (*path == '/' || !hard) {
! #else
!     if (*path == '/') {
! #endif
  	level = -1;
! 	if (d->dirfd < 0 && (d->dirfd = open(".", O_RDONLY)) < 0 &&
! 	    zgetdir(d) && *d->dirname != '/')
! 	    d->dirfd = open("..", O_RDONLY);
!     } else {
  	level = 0;
+ 	if (!d->dev && !d->ino) {
+ 	    stat(".", &st1);
+ 	    d->dev = st1.st_dev;
+ 	    d->ino = st1.st_ino;
+ 	}
+     }
+ 
+ #ifdef HAVE_LSTAT
+     if (!hard)
+ #endif
+     {
+ 	if (d == &ds)
+ 	    zsfree(ds.dirname);
+ 	else {
+ 	    for (pptr = path; *pptr; level++) {
+ 		while (*pptr && *pptr++ != '/');
+ 		while (*pptr == '/')
+ 		    pptr++;
+ 	    }
+ 	    d->level = level;
+ 	}
+ 	return zchdir((char *) path);
+     }
+ #ifdef HAVE_LSTAT
      for(;;) {
  	while(*path == '/')
  	    path++;
  	if(!*path) {
! 	    if (d == &ds)
! 		zsfree(ds.dirname);
! 	    else
! 		d->level = level;
  	    return 0;
  	}
  	for(pptr = path; *++pptr && *pptr != '/'; ) ;
***************
*** 3425,3438 ****
  	    break;
  	}
      }
!     if (olddir >= 0) {
! 	fchdir(olddir);
! 	close(olddir);
!     } else
! 	upchdir(level);
      errno = err;
      return -1;
  #endif /* HAVE_LSTAT */
  }
  
  #ifdef DEBUG
--- 3468,3518 ----
  	    break;
  	}
      }
!     if (restoredir(d)) {
! 	if (d == &ds)
! 	    zsfree(ds.dirname);
! 	errno = err;
! 	return -2;
!     }
!     if (d == &ds)
! 	zsfree(ds.dirname);
      errno = err;
      return -1;
  #endif /* HAVE_LSTAT */
+ }
+ 
+ /**/
+ int
+ restoredir(struct dirsav *d)
+ {
+     int err = 0;
+     struct stat sbuf;
+ 
+     if (d->dirname && *d->dirname == '/')
+ 	return chdir(d->dirname);
+     if (d->dirfd >= 0) {
+ 	if (!fchdir(d->dirfd)) {
+ 	    if (!d->dirname) {
+ 		return 0;
+ 	    } else if (chdir(d->dirname)) {
+ 		close(d->dirfd);
+ 		d->dirfd = -1;
+ 		err = -2;
+ 	    }
+ 	} else {
+ 	    close(d->dirfd);
+ 	    d->dirfd = err = -1;
+ 	}
+     } else if (d->level > 0)
+ 	err = upchdir(d->level);
+     else if (d->level < 0)
+ 	err = -1;
+     if (d->dev || d->ino) {
+ 	stat(".", &sbuf);
+ 	if (sbuf.st_ino != d->ino || sbuf.st_dev != d->dev)
+ 	    err = -2;
+     }
+     return err;
  }
  
  #ifdef DEBUG
*** Src/Modules/files.c	1997/01/02 03:53:56	3.1.1.3
--- Src/Modules/files.c	1997/01/04 23:38:02
***************
*** 307,333 ****
  bin_rm(char *nam, char **args, char *ops, int func)
  {
      int err = 0, len;
!     char *rp;
  
      for(; !(err & 2) && *args; args++) {
  	rp = ztrdup(*args);
  	unmetafy(rp, &len);
! 	err |= dorm(nam, *args, rp, ops, 1);
  	zfree(rp, len + 1);
      }
      return ops['f'] ? 0 : !!err;
  }
  
  /**/
  static int
! dorm(char *nam, char *arg, char *rp, char *ops, int first)
  {
      struct stat st;
  
      if((!ops['d'] || !ops['f']) && !lstat(rp, &st)) {
  	if(!ops['d'] && S_ISDIR(st.st_mode)) {
  	    if(ops['r'])
! 		return dormr(nam, arg, rp, ops, first);
  	    if(!ops['f'])
  		zwarnnam(nam, "%s: %e", arg, EISDIR);
  	    return 1;
--- 307,378 ----
  bin_rm(char *nam, char **args, char *ops, int func)
  {
      int err = 0, len;
!     char *rp, *s;
!     struct dirsav ds;
  
+     ds.ino = ds.dev = 0;
+     ds.dirname = NULL;
+     ds.dirfd = ds.level = -1;
+     if (ops['r'] || ops['s']) {
+ 	if ((ds.dirfd = open(".", O_RDONLY)) < 0 &&
+ 	    zgetdir(&ds) && *ds.dirname != '/')
+ 	    ds.dirfd = open("..", O_RDONLY);
+     }
      for(; !(err & 2) && *args; args++) {
  	rp = ztrdup(*args);
  	unmetafy(rp, &len);
! 	if (ops['s']) {
! 	    s = strrchr(rp, '/');
! 	    if (s && !s[1]) {
! 		while (*s == '/' && s > rp)
! 		    *s-- = '\0';
! 		while (*s != '/' && s > rp)
! 		    s--;
! 	    }
! 	    if (s && s[1]) {
! 		int e;
! 
! 		*s = '\0';
! 		e = lchdir(s, &ds, 1);
! 		err |= -e;
! 		if (!e) {
! 		    struct dirsav d;
! 
! 		    d.ino = d.dev = 0;
! 		    d.dirname = NULL;
! 		    d.dirfd = d.level = -1;
! 		    err |= dorm(nam, *args, s + 1, ops, &d, 0);
! 		    zsfree(d.dirname);
! 		    if (restoredir(&ds))
! 			err |= 2;
! 		}
! 	    } else
! 		err |= dorm(nam, *args, rp, ops, &ds, 0);
! 	} else
! 	    err |= dorm(nam, *args, rp, ops, &ds, 1);
  	zfree(rp, len + 1);
      }
+     if ((err & 2) && ds.dirfd >= 0 && restoredir(&ds) && zchdir(pwd)) {
+ 	zsfree(pwd);
+ 	pwd = ztrdup("/");
+ 	chdir(pwd);
+     }
+     if (ds.dirfd >= 0)
+ 	close(ds.dirfd);
+     zsfree(ds.dirname);
      return ops['f'] ? 0 : !!err;
  }
  
  /**/
  static int
! dorm(char *nam, char *arg, char *rp, char *ops, struct dirsav *ds, int first)
  {
      struct stat st;
  
      if((!ops['d'] || !ops['f']) && !lstat(rp, &st)) {
  	if(!ops['d'] && S_ISDIR(st.st_mode)) {
  	    if(ops['r'])
! 		return dormr(nam, arg, rp, ops, ds, first);
  	    if(!ops['f'])
  		zwarnnam(nam, "%s: %e", arg, EISDIR);
  	    return 1;
***************
*** 359,377 ****
  
  /**/
  static int
! dormr(char *nam, char *arg, char *rp, char *ops, int first)
  {
      char *fn;
      DIR *d;
!     int err = 0;
!     int pwd = *rp == '/' ? open(".", O_RDONLY) : -1;
  
!     if(first ? zchdir(rp) : lchdir(rp)) {
! 	if(!ops['f'])
  	    zwarnnam(nam, "%s: %e", arg, errno);
! 	return 1;
      }
  
      d = opendir(".");
      if(!d) {
  	if(!ops['f'])
--- 404,426 ----
  
  /**/
  static int
! dormr(char *nam, char *arg, char *rp, char *ops, struct dirsav *ds, int first)
  {
      char *fn;
      DIR *d;
!     int err;
!     struct dirsav dsav;
  
!     err = -lchdir(rp, ds, !first);
!     if (err) {
! 	if (!ops['f'])
  	    zwarnnam(nam, "%s: %e", arg, errno);
! 	return err;
      }
  
+     dsav.ino = dsav.dev = 0;
+     dsav.dirname = NULL;
+     dsav.dirfd = dsav.level = -1;
      d = opendir(".");
      if(!d) {
  	if(!ops['f'])
***************
*** 383,420 ****
  	    int len;
  
  	    fn = ztrdup(unmetafy(fn, &len));
! 	    err |= dorm(nam, narg, fn, ops, 0);
  	    zsfree(narg);
  	    zfree(fn, len + 1);
  	    if(err & 2) {
  		closedir(d);
! 		close(pwd);
  		return 2;
  	    }
  	}
  	closedir(d);
      }
!     if (pwd < 0 || fchdir(pwd)) {
! 	int level = 0;
! 
! 	if (pwd >= 0)
! 	    close(pwd);
! 	if (*rp != '/') {
! 	    for (fn = rp; *fn; level++) {
! 		while (*fn && *fn++ != '/');
! 		while (*fn == '/')
! 		    fn++;
! 	    }
! 	    level = !upchdir(level);
! 	}
! 	if (!level) {
! 	    if(!ops['f'])
! 		zwarnnam(nam, "failed to return to previous directory: %e",
! 			 NULL, errno);
! 	    return 2;
! 	}
!     } else
! 	close(pwd);
      if(!ops['f'] && ops['i']) {
  	nicezputs(nam, stderr);
  	fputs(": remove `", stderr);
--- 432,455 ----
  	    int len;
  
  	    fn = ztrdup(unmetafy(fn, &len));
! 	    err |= dorm(nam, narg, fn, ops, &dsav, 0);
  	    zsfree(narg);
  	    zfree(fn, len + 1);
  	    if(err & 2) {
  		closedir(d);
! 		zsfree(dsav.dirname);
  		return 2;
  	    }
  	}
  	closedir(d);
      }
!     zsfree(dsav.dirname);
!     if (restoredir(ds)) {
! 	if(!ops['f'])
! 	    zwarnnam(nam, "failed to return to previous directory: %e",
! 		     NULL, errno);
! 	return 2;
!     }
      if(!ops['f'] && ops['i']) {
  	nicezputs(nam, stderr);
  	fputs(": remove `", stderr);
***************
*** 435,449 ****
  
  static struct binlist bintab[] = {
  #ifdef HAVE_LSTAT
!     { "ln",    0, bin_ln,    1, -1, BIN_LN, "dfis", NULL, 0 },
  #else
!     { "ln",    0, bin_ln,    1, -1, BIN_LN, "dfi",  NULL, 0 },
  #endif
!     { "mkdir", 0, bin_mkdir, 1, -1, 0,      "pm",   NULL, 0 },
!     { "mv",    0, bin_ln,    2, -1, BIN_MV, "fi",   NULL, 0 },
!     { "rm",    0, bin_rm,    1, -1, 0,      "dfir", NULL, 0 },
!     { "rmdir", 0, bin_rmdir, 1, -1, 0,      NULL,   NULL, 0 },
!     { "sync",  0, bin_sync,  0,  0, 0,      NULL,   NULL, 0 },
  };
  
  /**/
--- 470,484 ----
  
  static struct binlist bintab[] = {
  #ifdef HAVE_LSTAT
!     { "ln",    0, bin_ln,    1, -1, BIN_LN, "dfis",  NULL, 0 },
  #else
!     { "ln",    0, bin_ln,    1, -1, BIN_LN, "dfi",   NULL, 0 },
  #endif
!     { "mkdir", 0, bin_mkdir, 1, -1, 0,      "pm",    NULL, 0 },
!     { "mv",    0, bin_ln,    2, -1, BIN_MV, "fi",    NULL, 0 },
!     { "rm",    0, bin_rm,    1, -1, 0,      "dfirs", NULL, 0 },
!     { "rmdir", 0, bin_rmdir, 1, -1, 0,      NULL,    NULL, 0 },
!     { "sync",  0, bin_sync,  0,  0, 0,      NULL,    NULL, 0 },
  };
  
  /**/



Messages sorted by: Reverse Date, Date, Thread, Author