zsh-workers
 help / color / mirror / code / Atom feed
From: Zefram <zefram@dcs.warwick.ac.uk>
To: zsh-workers@math.gatech.edu (Z Shell workers mailing list)
Subject: files module improvements
Date: Tue, 31 Dec 1996 12:51:43 +0000 (GMT)	[thread overview]
Message-ID: <29389.199612311251@stone.dcs.warwick.ac.uk> (raw)

-----BEGIN PGP SIGNED MESSAGE-----

This patch makes some improvements to the files module.  The static
buffers are removed, to make it reentrant.  rm -r is made safer, and
can now handle arbitrarily long pathnames.

There are some remaining problems:

* Interrupt signals are not handled.  I'll do another patch for this.

* rm -r can run out of file descriptors in deep trees.  Fixing this would
  require going back up the tree with chdir("..") and then checking that
  the tree wasn't moved while we were deleting it; it makes it easier for
  rm to fail.  I think this needs to be done, but I don't like it.

* rm -r can now lose the current directory.  This is particularly easy
  in unreadable directories.  There's no avoiding this, as it's a design
  flaw in Unix.  There is a need for zsh to cope with unexpected changes
  of directory: some failure modes of cd can already do a chdir("/")
  and leave $PWD as something else.

This patch adds a function lchdir() that does a paranoid directory change,
not following symlinks (or at least not without noticing).  It would be
nice to make this functionality available directly to the user, via an
option to cd (cd -l?).

 -zefram

      *** Src/utils.c	1996/12/30 15:13:52	1.48
      --- Src/utils.c	1996/12/31 05:42:56
      ***************
      *** 3308,3313 ****
      --- 3308,3371 ----
            return r;
        }
        
      + /* Change directory, without following symlinks.  Returns 0 on success, -1 *
      +  * on failure.  Sets errno to ENOTDIR if any symlinks are encountered.  If *
      +  * fchdir() fails, or the current directory is unreadable, we might end up *
      +  * in an unwanted directory in case of failure.                            */
      + 
      + /**/
      + 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), err;
      +     struct stat st1, st2;
      + 
      +     if(*path == '/')
      + 	chdir("/");
      +     for(;;) {
      + 	while(*path == '/')
      + 	    path++;
      + 	if(!*path) {
      + 	    close(olddir);
      + 	    return 0;
      + 	}
      + 	for(pptr = path; *++pptr && *pptr != '/'; ) ;
      + 	if(pptr - path > PATH_MAX) {
      + 	    err = ENAMETOOLONG;
      + 	    break;
      + 	}
      + 	for(ptr = buf; path != pptr; )
      + 	    *ptr++ = *path++;
      + 	*ptr = 0;
      + 	if(lstat(buf, &st1)) {
      + 	    err = errno;
      + 	    break;
      + 	}
      + 	if(!S_ISDIR(st1.st_mode)) {
      + 	    err = ENOTDIR;
      + 	    break;
      + 	}
      + 	if(chdir(buf) || lstat(".", &st2)) {
      + 	    err = errno;
      + 	    break;
      + 	}
      + 	if(st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino) {
      + 	    err = ENOTDIR;
      + 	    break;
      + 	}
      +     }
      +     fchdir(olddir);
      +     close(olddir);
      +     errno = err;
      +     return -1;
      + #endif /* HAVE_LSTAT */
      + }
      + 
        #ifdef DEBUG
        
        /**/
      *** Src/Modules/files.c	1996/12/24 17:07:33	1.6
      --- Src/Modules/files.c	1996/12/31 05:42:09
      ***************
      *** 35,43 ****
        
        #include "files.pro"
        
      - static char buf[PATH_MAX * 2];
      - static char rbuf[PATH_MAX];
      - 
        /**/
        static int
        ask(void)
      --- 35,40 ----
      ***************
      *** 132,138 ****
            if(p) {
        	struct stat st;
        
      ! 	if(!stat(rpath, &st) && S_ISDIR(st.st_mode))
        	    return 0;
            }
            oumask = umask(0);
      --- 129,135 ----
            if(p) {
        	struct stat st;
        
      ! 	if(!lstat(rpath, &st) && S_ISDIR(st.st_mode))
        	    return 0;
            }
            oumask = umask(0);
      ***************
      *** 189,194 ****
      --- 186,193 ----
            int flags, space, err = 0;
            char **a, *ptr, *rp;
            struct stat st;
      +     char buf[PATH_MAX * 2 + 1];
      + 
        
            if(func == BIN_MV) {
        	move = rename;
      ***************
      *** 229,239 ****
            havedir:
            strcpy(buf, *a);
            *a = NULL;
      !     space = PATH_MAX - 2 - ztrlen(buf);
            rp = strchr(buf, 0);
            *rp++ = '/';
            for(; *args; args++) {
      ! 	if(ztrlen(*args) > PATH_MAX - 1) {
        	    zwarnnam(nam, "%s: %e", *args, ENAMETOOLONG);
        	    err = 1;
        	    continue;
      --- 228,238 ----
            havedir:
            strcpy(buf, *a);
            *a = NULL;
      !     space = PATH_MAX - 1 - ztrlen(buf);
            rp = strchr(buf, 0);
            *rp++ = '/';
            for(; *args; args++) {
      ! 	if(ztrlen(*args) > PATH_MAX) {
        	    zwarnnam(nam, "%s: %e", *args, ENAMETOOLONG);
        	    err = 1;
        	    continue;
      ***************
      *** 260,270 ****
        {
            struct stat st;
            char *qbuf;
      !     strcpy(rbuf, unmeta(p));
            qbuf = unmeta(q);
            if(flags & MV_NODIRS) {
        	errno = EISDIR;
      ! 	if(lstat(rbuf, &st) || S_ISDIR(st.st_mode)) {
        	    zwarnnam(nam, "%s: %e", p, errno);
        	    return 1;
        	}
      --- 259,270 ----
        {
            struct stat st;
            char *qbuf;
      !     char pbuf[PATH_MAX + 1];
      !     strcpy(pbuf, unmeta(p));
            qbuf = unmeta(q);
            if(flags & MV_NODIRS) {
        	errno = EISDIR;
      ! 	if(lstat(pbuf, &st) || S_ISDIR(st.st_mode)) {
        	    zwarnnam(nam, "%s: %e", p, errno);
        	    return 1;
        	}
      ***************
      *** 293,299 ****
        	if(flags & MV_FORCE)
        	    unlink(qbuf);
            }
      !     if(move(rbuf, qbuf)) {
        	zwarnnam(nam, "%s: %e", p, errno);
        	return 1;
            }
      --- 293,299 ----
        	if(flags & MV_FORCE)
        	    unlink(qbuf);
            }
      !     if(move(pbuf, qbuf)) {
        	zwarnnam(nam, "%s: %e", p, errno);
        	return 1;
            }
      ***************
      *** 306,374 ****
        static int
        bin_rm(char *nam, char **args, char *ops, int func)
        {
      !     int err = 0;
        
      !     for(; *args; args++)
      ! 	err |= dorm(nam, *args, ops);
      !     return ops['f'] ? 0 : err;
        }
        
        /**/
        static int
      ! dorm(char *nam, char *arg, char *ops)
        {
            struct stat st;
      -     char *rp = unmeta(arg);
        
      !     if(!rp) {
      ! 	if(!ops['f'])
      ! 	    zwarnnam(nam, "%s: %e", arg, ENAMETOOLONG);
      ! 	return 1;
      !     }
      !     if((!ops['d'] || !ops['f']) && !stat(rp, &st)) {
        	if(!ops['d'] && S_ISDIR(st.st_mode)) {
      ! 	    if(ops['r']) {
      ! 		DIR *d;
      ! 		char *pos, *fn;
      ! 		int err = 0, space;
      ! 		d = opendir(rp);
      ! 		if(!d) {
      ! 		    if(!ops['f'])
      ! 			zwarnnam(nam, "%s: %e", arg, errno);
      ! 		    return 1;
      ! 		}
      ! 		if(arg != buf)
      ! 		    strcpy(buf, arg);
      ! 		space = PATH_MAX - 2 - ztrlen(buf);
      ! 		pos = strchr(buf, 0);
      ! 		*pos++ = '/';
      ! 		while((fn = zreaddir(d, 1))) {
      ! 		    if(ztrlen(fn) > space) {
      ! 			pos[-1] = 0;
      ! 			zwarnnam(nam, "%s: %e", buf, ENAMETOOLONG);
      ! 			err = 1;
      ! 			continue;
      ! 		    }
      ! 		    strcpy(pos, fn);
      ! 		    err |= dorm(nam, buf, ops);
      ! 		}
      ! 		closedir(d);
      ! 		pos[-1] = 0;
      ! 		if(!ops['f'] && ops['i']) {
      ! 		    nicezputs(nam, stderr);
      ! 		    fputs(": remove `", stderr);
      ! 		    nicezputs(buf, stderr);
      ! 		    fputs("'? ", stderr);
      ! 		    fflush(stderr);
      ! 		    if(!ask())
      ! 			return err;
      ! 		}
      ! 		if(!rmdir(unmeta(buf)))
      ! 		    return err;
      ! 		if(!ops['f'])
      ! 		    zwarnnam(nam, "%s: %e", buf, errno);
      ! 		return 1;
      ! 	    }
        	    if(!ops['f'])
        		zwarnnam(nam, "%s: %e", arg, EISDIR);
        	    return 1;
      --- 306,334 ----
        static int
        bin_rm(char *nam, char **args, char *ops, int func)
        {
      !     int err = 0, len;
      !     char *rp;
        
      !     for(; !(err & 2) && *args; args++) {
      ! 	rp = ztrdup(*args);
      ! 	len = strlen(rp + 1);
      ! 	unmetafy(rp, NULL);
      ! 	err |= dorm(nam, *args, rp, ops, 1);
      ! 	zfree(rp, len);
      !     }
      !     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;
      ***************
      *** 393,398 ****
      --- 353,415 ----
            }
            if(!unlink(rp))
        	return 0;
      +     if(!ops['f'])
      + 	zwarnnam(nam, "%s: %e", arg, errno);
      +     return 1;
      + }
      + 
      + /**/
      + static int
      + dormr(char *nam, char *arg, char *rp, char *ops, int first)
      + {
      +     char *fn;
      +     DIR *d;
      +     int err = 0;
      +     int pwd = open(".", O_RDONLY);
      + 
      +     if(first ? zchdir(rp) : lchdir(rp)) {
      + 	if(!ops['f'])
      + 	    zwarnnam(nam, "%s: %e", arg, errno);
      + 	return 1;
      +     }
      + 
      +     d = opendir(".");
      +     if(!d) {
      + 	if(!ops['f'])
      + 	    zwarnnam(nam, "%s: %e", arg, errno);
      + 	return 1 - fchdir(pwd);
      +     }
      +     while((fn = zreaddir(d, 1))) {
      + 	char *narg = tricat(arg, "/", fn);
      + 
      + 	err |= dorm(nam, narg, unmeta(fn), ops, 0);
      + 	zsfree(narg);
      + 	if(err & 2) {
      + 	    closedir(d);
      + 	    close(pwd);
      + 	    return 2;
      + 	}
      +     }
      +     closedir(d);
      +     if(fchdir(pwd)) {
      + 	close(pwd);
      + 	if(!ops['f'])
      + 	    zwarnnam(nam, "failed to return to previous directory: %e",
      + 		NULL, errno);
      + 	return 2;
      +     }
      +     close(pwd);
      +     if(!ops['f'] && ops['i']) {
      + 	nicezputs(nam, stderr);
      + 	fputs(": remove `", stderr);
      + 	nicezputs(arg, stderr);
      + 	fputs("'? ", stderr);
      + 	fflush(stderr);
      + 	if(!ask())
      + 	    return err;
      +     }
      +     if(!rmdir(rp))
      + 	return err;
            if(!ops['f'])
        	zwarnnam(nam, "%s: %e", arg, errno);
            return 1;

-----BEGIN PGP SIGNATURE-----
Version: 2.6.2

iQCVAwUBMsit9XD/+HJTpU/hAQE3ZQQAoa+URUNR6PouSElz1yJT4Ll2ggUnbqP1
Gb1AoKNmTtMwVyLCC0BpmyYFx8T2H8gEHoP6K+/hQQFoay2Vwe7/yu9aeLeaCeMK
f2QBrQ5kl2ZlFDuahRTyvrD3DsAmPuSbfCbbCjLAFZ0vbelPrZ/mftZfsE6Qe9sg
bjj348W3dzY=
=IKT+
-----END PGP SIGNATURE-----


             reply	other threads:[~1996-12-31 12:48 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
1996-12-31 12:51 Zefram [this message]
1997-01-02  3:52 ` Zoltan Hidvegi
1997-01-02 11:16   ` Zefram

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=29389.199612311251@stone.dcs.warwick.ac.uk \
    --to=zefram@dcs.warwick.ac.uk \
    --cc=zsh-workers@math.gatech.edu \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.vuxu.org/mirror/zsh/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).