=================================================================== RCS file: /cvs/mandoc/mandocdb.c,v retrieving revision 1.264 retrieving revision 1.267 diff -u -p -r1.264 -r1.267 --- mandoc/mandocdb.c 2020/01/25 22:59:22 1.264 +++ mandoc/mandocdb.c 2020/04/03 11:35:01 1.267 @@ -1,7 +1,7 @@ -/* $Id: mandocdb.c,v 1.264 2020/01/25 22:59:22 schwarze Exp $ */ +/* $Id: mandocdb.c,v 1.267 2020/04/03 11:35:01 schwarze Exp $ */ /* - * Copyright (c) 2011, 2012 Kristaps Dzonsons * Copyright (c) 2011-2020 Ingo Schwarze + * Copyright (c) 2011, 2012 Kristaps Dzonsons * Copyright (c) 2016 Ed Maste * * Permission to use, copy, modify, and distribute this software for any @@ -15,6 +15,8 @@ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Implementation of the makewhatis(8) program. */ #include "config.h" @@ -118,7 +120,7 @@ struct mdoc_handler { int mandocdb(int, char *[]); static void dbadd(struct dba *, struct mpage *); -static void dbadd_mlink(const struct mlink *mlink); +static void dbadd_mlink(const struct mlink *); static void dbprune(struct dba *); static void dbwrite(struct dba *); static void filescan(const char *); @@ -778,17 +780,17 @@ treescan(void) * See treescan() for the fts(3) version of this. */ static void -filescan(const char *file) +filescan(const char *infile) { - char buf[PATH_MAX]; struct stat st; struct mlink *mlink; - char *p, *start; + char *linkfile, *p, *realdir, *start, *usefile; + size_t realdir_len; assert(use_all); - if (strncmp(file, "./", 2) == 0) - file += 2; + if (strncmp(infile, "./", 2) == 0) + infile += 2; /* * We have to do lstat(2) before realpath(3) loses @@ -797,13 +799,13 @@ filescan(const char *file) * we want to use the orginal file name, while for * regular files, we want to use the real path. */ - if (lstat(file, &st) == -1) { + if (lstat(infile, &st) == -1) { exitcode = (int)MANDOCLEVEL_BADARG; - say(file, "&lstat"); + say(infile, "&lstat"); return; - } else if ((st.st_mode & (S_IFREG | S_IFLNK)) == 0) { + } else if (S_ISREG(st.st_mode) == 0 && S_ISLNK(st.st_mode) == 0) { exitcode = (int)MANDOCLEVEL_BADARG; - say(file, "Not a regular file"); + say(infile, "Not a regular file"); return; } @@ -811,23 +813,24 @@ filescan(const char *file) * We have to resolve the file name to the real path * in any case for the base directory check. */ - if (realpath(file, buf) == NULL) { + if ((usefile = realpath(infile, NULL)) == NULL) { exitcode = (int)MANDOCLEVEL_BADARG; - say(file, "&realpath"); + say(infile, "&realpath"); return; } if (op == OP_TEST) - start = buf; - else if (strncmp(buf, basedir, basedir_len) == 0) - start = buf + basedir_len; + start = usefile; + else if (strncmp(usefile, basedir, basedir_len) == 0) + start = usefile + basedir_len; #ifdef HOMEBREWDIR - else if (strncmp(buf, HOMEBREWDIR, strlen(HOMEBREWDIR)) == 0) - start = buf; + else if (strncmp(usefile, HOMEBREWDIR, strlen(HOMEBREWDIR)) == 0) + start = usefile; #endif else { exitcode = (int)MANDOCLEVEL_BADARG; - say("", "%s: outside base directory", buf); + say("", "%s: outside base directory", infile); + free(usefile); return; } @@ -835,32 +838,80 @@ filescan(const char *file) * Now we are sure the file is inside our tree. * If it is a symbolic link, ignore the real path * and use the original name. - * This implies passing stuff like "cat1/../man1/foo.1" - * on the command line won't work. So don't do that. - * Note the stat(2) can still fail if the link target - * doesn't exist. */ - if (st.st_mode & S_IFLNK) { - if (stat(buf, &st) == -1) { + do { + if (S_ISLNK(st.st_mode) == 0) + break; + + /* + * Some implementations of realpath(3) may succeed + * even if the target of the link does not exist, + * so check again for extra safety. + */ + if (stat(usefile, &st) == -1) { exitcode = (int)MANDOCLEVEL_BADARG; - say(file, "&stat"); + say(infile, "&stat"); + free(usefile); return; } - if (strlcpy(buf, file, sizeof(buf)) >= sizeof(buf)) { - say(file, "Filename too long"); - return; + linkfile = mandoc_strdup(infile); + if (op == OP_TEST) { + free(usefile); + start = usefile = linkfile; + break; } - start = buf; - if (op != OP_TEST && strncmp(buf, basedir, basedir_len) == 0) - start += basedir_len; - } + if (strncmp(infile, basedir, basedir_len) == 0) { + free(usefile); + usefile = linkfile; + start = usefile + basedir_len; + break; + } + /* + * This symbolic link points into the basedir + * from the outside. Let's see whether any of + * the parent directories resolve to the basedir. + */ + p = strchr(linkfile, '\0'); + do { + while (*--p != '/') + continue; + *p = '\0'; + if ((realdir = realpath(linkfile, NULL)) == NULL) { + exitcode = (int)MANDOCLEVEL_BADARG; + say(infile, "&realpath"); + free(linkfile); + free(usefile); + return; + } + realdir_len = strlen(realdir) + 1; + free(realdir); + *p = '/'; + } while (realdir_len > basedir_len); + + /* + * If one of the directories resolves to the basedir, + * use the rest of the original name. + * Otherwise, the best we can do + * is to use the filename pointed to. + */ + if (realdir_len == basedir_len) { + free(usefile); + usefile = linkfile; + start = p + 1; + } else { + free(linkfile); + start = usefile + basedir_len; + } + } while (/* CONSTCOND */ 0); + mlink = mandoc_calloc(1, sizeof(struct mlink)); mlink->dform = FORM_NONE; if (strlcpy(mlink->file, start, sizeof(mlink->file)) >= sizeof(mlink->file)) { say(start, "Filename too long"); free(mlink); + free(usefile); return; } @@ -869,13 +920,13 @@ filescan(const char *file) * but outside our tree, guess the base directory. */ - if (op == OP_TEST || (start == buf && *start == '/')) { - if (strncmp(buf, "man/", 4) == 0) - start = buf + 4; - else if ((start = strstr(buf, "/man/")) != NULL) + if (op == OP_TEST || (start == usefile && *start == '/')) { + if (strncmp(usefile, "man/", 4) == 0) + start = usefile + 4; + else if ((start = strstr(usefile, "/man/")) != NULL) start += 5; else - start = buf; + start = usefile; } /* @@ -925,6 +976,7 @@ filescan(const char *file) *p = '\0'; } mlink_add(mlink, &st); + free(usefile); } static void