From: Nicholas Clark Date: Mon, 27 Oct 2008 20:29:54 +0000 (+0000) Subject: Upgrade Cwd.xs to the Cwd.xs in PathTools 3.26_01: X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=c7304ea2604337d29c746515db4570520e6a66ca;p=p5sagit%2Fp5-mst-13.2.git Upgrade Cwd.xs to the Cwd.xs in PathTools 3.26_01: - abs_path() on Unix(ish) platforms has been upgraded to a much later version of the underlying C code from BSD. [Michael Schwern] - If strlcpy() and strlcat() aren't available on the user's system, we now use ppport.h to provide them, so our C code works. [Steve Peters] This unwinds change 32710 (consting from Robin Barker of the old code) p4raw-id: //depot/perl@34606 --- diff --git a/ext/Cwd/Cwd.xs b/ext/Cwd/Cwd.xs index 4864ba1..949b94c 100644 --- a/ext/Cwd/Cwd.xs +++ b/ext/Cwd/Cwd.xs @@ -64,148 +64,160 @@ */ static char * -bsd_realpath(const char *path, char *resolved) +bsd_realpath(const char *path, char resolved[MAXPATHLEN]) { #ifdef VMS dTHX; return Perl_rmsexpand(aTHX_ (char*)path, resolved, NULL, 0); #else - int rootd, serrno; - const char *p; - char *q, wbuf[MAXPATHLEN]; - int symlinks = 0; - - /* Save the starting point. */ -#ifdef HAS_FCHDIR - int fd; - - if ((fd = open(".", O_RDONLY)) < 0) { - (void)strcpy(resolved, "."); + char *p, *q, *s; + size_t left_len, resolved_len; + unsigned symlinks; + int serrno; + char left[MAXPATHLEN], next_token[MAXPATHLEN], symlink[MAXPATHLEN]; + + serrno = errno; + symlinks = 0; + if (path[0] == '/') { + resolved[0] = '/'; + resolved[1] = '\0'; + if (path[1] == '\0') + return (resolved); + resolved_len = 1; + left_len = my_strlcpy(left, path + 1, sizeof(left)); + } else { + if (getcwd(resolved, MAXPATHLEN) == NULL) { + my_strlcpy(resolved, ".", MAXPATHLEN); return (NULL); } -#else - char wd[MAXPATHLEN]; - - if (getcwd(wd, MAXPATHLEN - 1) == NULL) { - (void)strcpy(resolved, "."); + resolved_len = strlen(resolved); + left_len = my_strlcpy(left, path, sizeof(left)); + } + if (left_len >= sizeof(left) || resolved_len >= MAXPATHLEN) { + errno = ENAMETOOLONG; return (NULL); } -#endif /* - * Find the dirname and basename from the path to be resolved. - * Change directory to the dirname component. - * lstat the basename part. - * if it is a symlink, read in the value and loop. - * if it is a directory, then change to that directory. - * get the current directory name and append the basename. + * Iterate over path components in `left'. */ - (void)strncpy(resolved, path, MAXPATHLEN - 1); - resolved[MAXPATHLEN - 1] = '\0'; -loop: - q = strrchr(resolved, '/'); - if (q != NULL) { - const char *dir; - p = q + 1; - if (q == resolved) - dir = "/"; - else { - do { - --q; - } while (q > resolved && *q == '/'); - q[1] = '\0'; - dir = resolved; - } - if (chdir(dir) < 0) - goto err1; - } else - p = resolved; - -#if defined(HAS_LSTAT) && defined(HAS_READLINK) && defined(HAS_SYMLINK) - { - struct stat sb; - /* Deal with the last component. */ - if (lstat(p, &sb) == 0) { - if (S_ISLNK(sb.st_mode)) { - int n; - if (++symlinks > MAXSYMLINKS) { - errno = ELOOP; - goto err1; + while (left_len != 0) { + /* + * Extract the next path component and adjust `left' + * and its length. + */ + p = strchr(left, '/'); + s = p ? p : left + left_len; + if (s - left >= sizeof(next_token)) { + errno = ENAMETOOLONG; + return (NULL); } - n = readlink(p, resolved, MAXPATHLEN-1); - if (n < 0) - goto err1; - resolved[n] = '\0'; - goto loop; - } - if (S_ISDIR(sb.st_mode)) { - if (chdir(p) < 0) - goto err1; - p = ""; + memcpy(next_token, left, s - left); + next_token[s - left] = '\0'; + left_len -= s - left; + if (p != NULL) + memmove(left, s + 1, left_len + 1); + if (resolved[resolved_len - 1] != '/') { + if (resolved_len + 1 >= MAXPATHLEN) { + errno = ENAMETOOLONG; + return (NULL); } + resolved[resolved_len++] = '/'; + resolved[resolved_len] = '\0'; } + if (next_token[0] == '\0') + continue; + else if (strcmp(next_token, ".") == 0) + continue; + else if (strcmp(next_token, "..") == 0) { + /* + * Strip the last path component except when we have + * single "/" + */ + if (resolved_len > 1) { + resolved[resolved_len - 1] = '\0'; + q = strrchr(resolved, '/') + 1; + *q = '\0'; + resolved_len = q - resolved; + } + continue; } -#endif /* - * Save the last component name and get the full pathname of - * the current directory. + * Append the next path component and lstat() it. If + * lstat() fails we still can return successfully if + * there are no more path components left. */ - (void)strcpy(wbuf, p); - if (getcwd(resolved, MAXPATHLEN) == 0) - goto err1; + resolved_len = my_strlcat(resolved, next_token, MAXPATHLEN); + if (resolved_len >= MAXPATHLEN) { + errno = ENAMETOOLONG; + return (NULL); + } + #if defined(HAS_LSTAT) && defined(HAS_READLINK) && defined(HAS_SYMLINK) + { + struct stat sb; + if (lstat(resolved, &sb) != 0) { + if (errno == ENOENT && p == NULL) { + errno = serrno; + return (resolved); + } + return (NULL); + } + if (S_ISLNK(sb.st_mode)) { + int slen; + + if (symlinks++ > MAXSYMLINKS) { + errno = ELOOP; + return (NULL); + } + slen = readlink(resolved, symlink, sizeof(symlink) - 1); + if (slen < 0) + return (NULL); + symlink[slen] = '\0'; + if (symlink[0] == '/') { + resolved[1] = 0; + resolved_len = 1; + } else if (resolved_len > 1) { + /* Strip the last path component. */ + resolved[resolved_len - 1] = '\0'; + q = strrchr(resolved, '/') + 1; + *q = '\0'; + resolved_len = q - resolved; + } /* - * Join the two strings together, ensuring that the right thing - * happens if the last component is empty, or the dirname is root. + * If there are any path components left, then + * append them to symlink. The result is placed + * in `left'. */ - if (resolved[0] == '/' && resolved[1] == '\0') - rootd = 1; - else - rootd = 0; - - if (*wbuf) { - if (strlen(resolved) + strlen(wbuf) + (1 - rootd) + 1 > MAXPATHLEN) { + if (p != NULL) { + if (symlink[slen - 1] != '/') { + if (slen + 1 >= sizeof(symlink)) { errno = ENAMETOOLONG; - goto err1; + return (NULL); } - if (rootd == 0) - (void)strcat(resolved, "/"); - (void)strcat(resolved, wbuf); + symlink[slen] = '/'; + symlink[slen + 1] = 0; } - - /* Go back to where we came from. */ -#ifdef HAS_FCHDIR - if (fchdir(fd) < 0) { - serrno = errno; - goto err2; + left_len = my_strlcat(symlink, left, sizeof(left)); + if (left_len >= sizeof(left)) { + errno = ENAMETOOLONG; + return (NULL); } -#else - if (chdir(wd) < 0) { - serrno = errno; - goto err2; } -#endif + left_len = my_strlcpy(left, symlink, sizeof(left)); + } + } + #endif + } - /* It's okay if the close fails, what's an fd more or less? */ -#ifdef HAS_FCHDIR - (void)close(fd); -#endif + /* + * Remove trailing slash except when the resolved pathname + * is a single "/". + */ + if (resolved_len > 1 && resolved[resolved_len - 1] == '/') + resolved[resolved_len - 1] = '\0'; return (resolved); - -err1: serrno = errno; -#ifdef HAS_FCHDIR - (void)fchdir(fd); -#else - (void)chdir(wd); -#endif - -err2: -#ifdef HAS_FCHDIR - (void)close(fd); -#endif - errno = serrno; - return (NULL); #endif }