zsh-workers
 help / color / mirror / code / Atom feed
* pwd -r odd failure mode
@ 1996-12-23 19:59 Zefram
  1996-12-26  3:45 ` Zoltan Hidvegi
  0 siblings, 1 reply; 2+ messages in thread
From: Zefram @ 1996-12-23 19:59 UTC (permalink / raw)
  To: Z Shell workers mailing list

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

On some systems, pwd -r from a deleted directory can pick the first
pathname from the parent directory -- possibly a file -- and call that the
current directory.  The code responsible for this is a bit of zgetcwd()
that, if it fails to find the current directory's inode, reads the
directory again, accepting anything on the same device.  I don't see
how such an algorithm could ever produce a correct answer, and it isn't
commented, so just removing it seems sensible.  Does anyone know better?

 -zefram

      Index: Src/compat.c
      ===================================================================
      RCS file: /home/zefram/usr/cvsroot/zsh/Src/compat.c,v
      retrieving revision 1.1.1.11
      diff -c -r1.1.1.11 compat.c
      *** Src/compat.c	1996/12/22 01:13:20	1.1.1.11
      --- Src/compat.c	1996/12/22 22:45:25
      ***************
      *** 166,184 ****
        		    goto match;
        	    }
        	}
      - 	closedir(dir);
      - 	dir = opendir(".");
      - 	while ((de = readdir(dir))) {
      - 	    char *fn = de->d_name;
      - 	    /* Ignore `.' and `..'. */
      - 	    if (fn[0] == '.' &&
      - 		(fn[1] == '\0' ||
      - 		 (fn[1] == '.' && fn[2] == '\0')))
      - 		continue;
      - 	    lstat(fn, &sbuf);
      - 	    if (sbuf.st_dev == dev)
      - 		goto match;
      - 	}
        	noholdintr();
        	closedir(dir);
        	return ztrdup(".");
      --- 166,171 ----

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

iQCVAwUBMr27inD/+HJTpU/hAQHRQwP/VihCu3jo3QAZk41QG2UuooDakzMnFCoa
W4E1dhtEOt4J3PwjITlxPyzEcZLZZTjgVCEcSudCXPATzIiX35S54tOn3K6sJWid
j8vc+ssPQj1Ef9ZGlmNLRRD48CJ3qjXUxH++YvgYuobbtj+FEotmuMuVdB7rhCLh
DlY/IKgK5pQ=
=CGUm
-----END PGP SIGNATURE-----


^ permalink raw reply	[flat|nested] 2+ messages in thread

* Re: pwd -r odd failure mode
  1996-12-23 19:59 pwd -r odd failure mode Zefram
@ 1996-12-26  3:45 ` Zoltan Hidvegi
  0 siblings, 0 replies; 2+ messages in thread
From: Zoltan Hidvegi @ 1996-12-26  3:45 UTC (permalink / raw)
  To: Zefram; +Cc: zsh-workers

Zefram wrote:
> On some systems, pwd -r from a deleted directory can pick the first
> pathname from the parent directory -- possibly a file -- and call that the
> current directory.  The code responsible for this is a bit of zgetcwd()
> that, if it fails to find the current directory's inode, reads the
> directory again, accepting anything on the same device.  I don't see
> how such an algorithm could ever produce a correct answer, and it isn't
> commented, so just removing it seems sensible.  Does anyone know better?

That code was not completely useless, it was necessary when . and .. are on
different filesystems.  The right fix is to add check for the inode as
well.  Below I include a much bigger patch for the problem.  I do not
tested it much, I've just finished it as 4.44am so it may be buggy.

Before this patch zsh was unable to handle pathnames longer than PATH_MAX.
I also noticed that on Linux chdir accepts 4*PATH_MAX long strings.

With this patch zsh can handle arbitrary long pathnames in PWD and in the
output of pwd -r.  It completely rewrites the zgetcwd function and adds a
new zchdir function which is identical to the chdir system call but it
accepts arbitrary long pathnames.

If you applied Zefram's patch in article 2618, remove it before applying
this.

Zoltan


*** Src/builtin.c	1996/12/25 16:04:45	3.1.1.1
--- Src/builtin.c	1996/12/26 03:32:22
***************
*** 1105,1162 ****
  char *
  cd_try_chdir(char *pfix, char *dest)
  {
!     char buf[PATH_MAX], buf2[PATH_MAX];
!     char *s;
!     int dotsct;
  
      /* handle directory prefix */
      if (pfix && *pfix) {
! 	if (strlen(dest) + strlen(pfix) + 1 >= PATH_MAX)
! 	    return NULL;
! 	sprintf(buf, "%s/%s", (!strcmp("/", pfix)) ? "" : pfix, dest);
!     } else {
! 	if (strlen(dest) >= PATH_MAX)
! 	    return NULL;
! 	strcpy(buf, dest);
      }
      /* Normalise path.  See the definition of fixdir() for what this means. */
!     dotsct = fixdir(buf2, buf);
  
!     /* if the path is absolute, the test and return value are (relatively)
!     simple */
!     if (buf2[0] == '/')
! 	return (chdir(unmeta(buf)) == -1) ? NULL : ztrdup(buf2);
!     /* If the path is a simple `downward' relative path, the test is again
!     fairly simple.  The relative path must be added to the end of the current
!     directory. */
!     if (!dotsct) {
! 	if (chdir(unmeta(buf)) == -1)
! 	    return NULL;
! 	if (*buf2) {
! 	    if (strlen(pwd) + strlen(buf2) + 1 >= PATH_MAX)
! 		return NULL;
! 	    sprintf(buf, "%s/%s", (!strcmp("/", pwd)) ? "" : pwd, buf2);
! 	} else
! 	    strcpy(buf, pwd);
! 	return ztrdup(buf);
      }
!     /* There are one or more .. segments at the beginning of the relative path.
!     A corresponding number of segments must be removed from the end of the
!     current directory before the downward relative path is appended. */
!     strcpy(buf, pwd);
!     s = buf + strlen(buf) - 1;
!     while (dotsct--)
! 	while (s != buf)
! 	    if (*--s == '/')
! 		break;
!     if (s == buf || *buf2)
! 	s++;
!     strcpy(s, buf2);
!     /* For some reason, this chdir must be attempted with both the newly
!     created path and the original non-normalised version. */
!     if (chdir(unmeta(buf)) != -1 || chdir(unmeta(dest)) != -1)
! 	return ztrdup(buf);
!     return NULL;
  }
  
  /* do the extra processing associated with changing directory */
--- 1105,1146 ----
  char *
  cd_try_chdir(char *pfix, char *dest)
  {
!     char *buf;
  
      /* handle directory prefix */
      if (pfix && *pfix) {
! 	if (*pfix == '/')
! 	    buf = tricat(pfix, "/", dest);
! 	else {
! 	    int pwl = strlen(pwd);
! 	    int pfl = strlen(pfix);
! 
! 	    buf = zalloc(pwl + pfl + strlen(dest) + 3);
! 	    strcpy(buf, pwd);
! 	    buf[pwl] = '/';
! 	    strcpy(buf + pwl + 1, pfix);
! 	    buf[pwl + 1 + pfl] = '/';
! 	    strcpy(buf + pwl + pfl + 2, dest);
! 	}
!     } else if (*dest == '/')
! 	buf = ztrdup(dest);
!     else {
! 	int pwl = strlen(pwd);
! 
! 	buf = zalloc(pwl + strlen(dest) + 2);
! 	strcpy(buf, pwd);
! 	buf[pwl] = '/';
! 	strcpy(buf + pwl + 1, dest);
      }
+ 
      /* Normalise path.  See the definition of fixdir() for what this means. */
!     fixdir(buf);
  
!     if (zchdir(buf) == -1) {
! 	zsfree(buf);
! 	return NULL;
      }
!     return metafy(buf, -1, META_NOALLOC);
  }
  
  /* do the extra processing associated with changing directory */
***************
*** 1247,1262 ****
      putchar('\n');
  }
  
! /* Normalise a path.  Segments consisting of ., and foo/.. combinations, *
!  * are removed.  The number of .. segments at the beginning of the       *
!  * path is returned.  The normalised path, minus leading ..s, is copied  *
!  * to dest.                                                              */
  
  /**/
! int
! fixdir(char *dest, char *src)
  {
!     int ct = 0;
      char *d0 = dest;
  
  /*** if have RFS superroot directory ***/
--- 1231,1244 ----
      putchar('\n');
  }
  
! /* Normalise a path.  Segments consisting of ., and foo/.. *
!  * combinations, are removed and the path is unmetafied.   */
  
  /**/
! void
! fixdir(char *src)
  {
!     char *dest = src;
      char *d0 = dest;
  
  /*** if have RFS superroot directory ***/
***************
*** 1284,1300 ****
  	    while (dest > d0 + 1 && dest[-1] == '/')
  		dest--;
  	    *dest = '\0';
! 	    return ct;
  	}
! 	if (src[0] == '.' && src[1] == '.' &&
  	  (src[2] == '\0' || src[2] == '/')) {
! 	    /* remove a foo/.. combination, or increment ct, as appropriate */
! 	    if (dest > d0 + 1) {
! 		for (dest--; dest > d0 + 1 && dest[-1] != '/'; dest--);
! 		if (dest[-1] != '/')
! 		    dest--;
! 	    } else
! 		ct++;
  	    src++;
  	    while (*++src == '/');
  	} else if (src[0] == '.' && (src[1] == '/' || src[1] == '\0')) {
--- 1266,1279 ----
  	    while (dest > d0 + 1 && dest[-1] == '/')
  		dest--;
  	    *dest = '\0';
! 	    return;
  	}
! 	if (dest > d0 + 1 && src[0] == '.' && src[1] == '.' &&
  	  (src[2] == '\0' || src[2] == '/')) {
! 	    /* remove a foo/.. combination */
! 	    for (dest--; dest > d0 + 1 && dest[-1] != '/'; dest--);
! 	    if (dest[-1] != '/')
! 		dest--;
  	    src++;
  	    while (*++src == '/');
  	} else if (src[0] == '.' && (src[1] == '/' || src[1] == '\0')) {
***************
*** 1303,1309 ****
  	} else {
  	    /* copy a normal segment into the output */
  	    while (*src != '/' && *src != '\0')
! 		*dest++ = *src++;
  	}
      }
  }
--- 1282,1289 ----
  	} else {
  	    /* copy a normal segment into the output */
  	    while (*src != '/' && *src != '\0')
! 		if ((*dest++ = *src++) == Meta)
! 		    dest[-1] = *src++ ^ 32;
  	}
      }
  }
*** Src/compat.c	1996/12/21 02:35:32	3.1.1.0
--- Src/compat.c	1996/12/26 02:29:35
***************
*** 109,158 ****
  char *
  zgetcwd(void)
  {
!     static char buf0[PATH_MAX];
!     char *buf2 = buf0 + 1;
!     char buf3[PATH_MAX];
      struct stat sbuf;
      struct dirent *de;
      DIR *dir;
!     ino_t ino, rootino = (ino_t) ~ 0;
!     dev_t dev, rootdev = (dev_t) ~ 0;
  
      holdintr();
!     buf2[0] = '\0';
!     buf0[0] = '/';
  
!     if (stat(buf0, &sbuf) >= 0) {
! 	rootino = sbuf.st_ino;
! 	rootdev = sbuf.st_dev;
!     }
  
      for (;;) {
! 	if (stat(".", &sbuf) < 0) {
! 	    chdir(buf0);
! 	    noholdintr();
! 	    return ztrdup(".");
! 	}
! 	ino = sbuf.st_ino;
! 	dev = sbuf.st_dev;
! 	if (stat("..", &sbuf) < 0) {
! 	    chdir(buf0);
! 	    noholdintr();
! 	    return ztrdup(".");
! 	}
! 	if ((sbuf.st_ino == ino && sbuf.st_dev == dev) ||
! 	    (ino == rootino && dev == rootdev)) {
! 	    chdir(buf0);
! 	    noholdintr();
! 	    return ztrdup(buf0);
! 	}
! 	dir = opendir("..");
! 	if (!dir) {
! 	    chdir(buf0);
  	    noholdintr();
! 	    return ztrdup(".");
  	}
! 	chdir("..");
  	while ((de = readdir(dir))) {
  	    char *fn = de->d_name;
  	    /* Ignore `.' and `..'. */
--- 109,155 ----
  char *
  zgetcwd(void)
  {
!     char nbuf[PATH_MAX+3];
!     char *buf;
!     int bufsiz, pos, len;
      struct stat sbuf;
      struct dirent *de;
      DIR *dir;
!     ino_t ino, pino;
!     dev_t dev, pdev;
! 
!     if (stat(".", &sbuf) < 0)
! 	return(ztrdup("."));
  
      holdintr();
!     buf = halloc(bufsiz = PATH_MAX);
!     pos = bufsiz - 1;
!     buf[pos] = '\0';
!     strcpy(nbuf, "../");
  
!     pino = sbuf.st_ino;
!     pdev = sbuf.st_dev;
  
      for (;;) {
! 	if (stat("..", &sbuf) < 0)
! 	    break;
! 
! 	ino = pino;
! 	dev = pdev;
! 	pino = sbuf.st_ino;
! 	pdev = sbuf.st_dev;
! 
! 	if (ino == pino && dev == pdev) {
! 	    if (!buf[pos])
! 		buf[--pos] = '/';
! 	    zchdir(buf + pos);
  	    noholdintr();
! 	    return buf + pos;
  	}
! 
! 	if (!(dir = opendir("..")))
! 	    break;
! 
  	while ((de = readdir(dir))) {
  	    char *fn = de->d_name;
  	    /* Ignore `.' and `..'. */
***************
*** 160,194 ****
  		(fn[1] == '\0' ||
  		 (fn[1] == '.' && fn[2] == '\0')))
  		continue;
! 	    if ((ino_t) de->d_ino == ino) {
! 		lstat(fn, &sbuf);
! 		if (sbuf.st_dev == dev)
! 		    goto match;
  	    }
  	}
  	closedir(dir);
! 	dir = opendir(".");
! 	while ((de = readdir(dir))) {
! 	    char *fn = de->d_name;
! 	    /* Ignore `.' and `..'. */
! 	    if (fn[0] == '.' &&
! 		(fn[1] == '\0' ||
! 		 (fn[1] == '.' && fn[2] == '\0')))
! 		continue;
! 	    lstat(fn, &sbuf);
! 	    if (sbuf.st_dev == dev)
! 		goto match;
! 	}
! 	noholdintr();
! 	closedir(dir);
! 	return ztrdup(".");
!       match:
! 	strcpy(buf3, de->d_name);
! 	if (*buf2)
! 	    strcat(buf3, "/");
! 	strcat(buf3, buf2);
! 	strcpy(buf2, buf3);
! 	closedir(dir);
      }
  }
  
--- 157,223 ----
  		(fn[1] == '\0' ||
  		 (fn[1] == '.' && fn[2] == '\0')))
  		continue;
! 	    if (dev != pdev || (ino_t) de->d_ino == ino) {
! 		strncpy(nbuf + 3, fn, PATH_MAX);
! 		lstat(nbuf, &sbuf);
! 		if (sbuf.st_dev == dev && sbuf.st_ino == ino)
! 		    break;
  	    }
  	}
  	closedir(dir);
! 	if (!de)
! 	    break;
! 	len = strlen(nbuf + 2);
! 	pos -= len;
! 	while (pos <= 1) {
! 	    char *newbuf = halloc(2*bufsiz);
! 	    memcpy(newbuf + bufsiz, buf, bufsiz);
! 	    buf = newbuf;
! 	    pos += bufsiz;
! 	    bufsiz *= 2;
! 	}
! 	memcpy(buf + pos, nbuf + 2, len);
! 	if (chdir(".."))
! 	    break;
      }
+     if (*buf)
+ 	zchdir(buf + pos + 1);
+     noholdintr();
+     return ztrdup(".");
  }
  
+ /* chdir with arbitrary long pathname */
+ 
+ /**/
+ int
+ zchdir(char *dir)
+ {
+     char *s;
+     int currdir = -2;
+ 
+     for (;;) {
+ 	if (!*dir)
+ 	    return 0;
+ 	if (!chdir(dir))
+ 	    return 0;
+ 	if ((errno != ENAMETOOLONG && errno != ENOMEM) ||
+ 	    strlen(dir) < PATH_MAX)
+ 	    break;
+ 	for (s = dir + PATH_MAX - 1; s > dir && *s != '/'; s--);
+ 	if (s == dir)
+ 	    break;
+ 	if (currdir == -2)
+ 	    currdir = open(".", O_RDONLY);
+ 	*s = '\0';
+ 	if (chdir(dir)) {
+ 	    *s = '/';
+ 	    break;
+ 	}
+ 	*s = '/';
+ 	while (*++s == '/');
+ 	dir = s;
+     }
+     if (currdir >= 0)
+ 	fchdir(currdir);
+     return -1;
+ }
*** Src/utils.c	1996/12/25 16:04:45	3.1.1.2
--- Src/utils.c	1996/12/26 03:26:21
***************
*** 2624,2632 ****
  #endif
  
  /* Escape tokens and null characters.  Buf is the string which should be    *
!  * escaped.  len is the length of the string.  If len is -1, buf should     *
!  * be null terminated.  If len is non-zero and the third paramerer is not   *
!  * META_DUP buf should point to an at least len+1 long memory area.  The    *
   * return value points to the quoted string.  If the given string does not  *
   * contain any special character which should be quoted and the third       *
   * parameter is not META_DUP, buf is returned unchanged (a terminating null *
--- 2624,2632 ----
  #endif
  
  /* Escape tokens and null characters.  Buf is the string which should be    *
!  * escaped.  len is the length of the string.  If len is -1, buf should be  *
!  * null terminated.  If len is non-negative and the third paramerer is not  *
!  * META_DUP, buf should point to an at least len+1 long memory area.  The   *
   * return value points to the quoted string.  If the given string does not  *
   * contain any special character which should be quoted and the third       *
   * parameter is not META_DUP, buf is returned unchanged (a terminating null *
***************
*** 2744,2760 ****
  /* This function converts a zsh internal string to a form which can be *
   * passed to a system call as a filename.  The result is stored in a   *
   * single static area.  NULL returned if the result is longer than     *
!  * PATH_MAX.                                                           */
  
  /**/
  char *
  unmeta(const char *file_name)
  {
!     static char fn[PATH_MAX];
      char *p;
      const char *t;
  
!     for (t = file_name, p = fn; *t && p < fn + PATH_MAX - 1; p++)
  	if ((*p = *t++) == Meta)
  	    *p = *t++ ^ 32;
      if (*t)
--- 2744,2760 ----
  /* This function converts a zsh internal string to a form which can be *
   * passed to a system call as a filename.  The result is stored in a   *
   * single static area.  NULL returned if the result is longer than     *
!  * 4 * PATH_MAX.                                                       */
  
  /**/
  char *
  unmeta(const char *file_name)
  {
!     static char fn[4 * PATH_MAX];
      char *p;
      const char *t;
  
!     for (t = file_name, p = fn; *t && p < fn + 4 * PATH_MAX - 1; p++)
  	if ((*p = *t++) == Meta)
  	    *p = *t++ ^ 32;
      if (*t)


^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~1996-12-26  3:53 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
1996-12-23 19:59 pwd -r odd failure mode Zefram
1996-12-26  3:45 ` Zoltan Hidvegi

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).