=================================================================== RCS file: /cvs/mandoc/mandocdb.c,v retrieving revision 1.137 retrieving revision 1.140 diff -u -p -r1.137 -r1.140 --- mandoc/mandocdb.c 2014/04/16 21:36:18 1.137 +++ mandoc/mandocdb.c 2014/04/19 02:56:54 1.140 @@ -1,4 +1,4 @@ -/* $Id: mandocdb.c,v 1.137 2014/04/16 21:36:18 schwarze Exp $ */ +/* $Id: mandocdb.c,v 1.140 2014/04/19 02:56:54 schwarze Exp $ */ /* * Copyright (c) 2011, 2012 Kristaps Dzonsons * Copyright (c) 2011, 2012, 2013, 2014 Ingo Schwarze @@ -430,27 +430,39 @@ main(int argc, char *argv[]) ohash_init(&mlinks, 6, &mlinks_info); if (OP_UPDATE == op || OP_DELETE == op || OP_TEST == op) { - /* - * Force processing all files. - */ - use_all = 1; /* * All of these deal with a specific directory. - * Jump into that directory then collect files specified - * on the command-line. + * Jump into that directory first. */ if (0 == set_basedir(path_arg)) goto out; - for (i = 0; i < argc; i++) - filescan(argv[i]); - if (0 == dbopen(1)) - goto out; - if (OP_TEST != op) - dbprune(); + + if (dbopen(1)) { + /* + * The existing database is usable. Process + * all files specified on the command-line. + */ + use_all = 1; + for (i = 0; i < argc; i++) + filescan(argv[i]); + if (OP_TEST != op) + dbprune(); + } else { + /* + * Database missing or corrupt. + * Recreate from scratch. + */ + exitcode = (int)MANDOCLEVEL_OK; + op = OP_DEFAULT; + if (0 == treescan()) + goto out; + if (0 == dbopen(0)) + goto out; + } if (OP_DELETE != op) mpages_merge(mc, mp); - dbclose(1); + dbclose(OP_DEFAULT == op ? 0 : 1); } else { /* * If we have arguments, use them as our manpaths. @@ -549,6 +561,7 @@ usage: static int treescan(void) { + char buf[PATH_MAX]; FTS *f; FTSENT *ff; struct mlink *mlink; @@ -560,11 +573,8 @@ treescan(void) argv[0] = "."; argv[1] = (char *)NULL; - /* - * Walk through all components under the directory, using the - * logical descent of files. - */ - f = fts_open((char * const *)argv, FTS_LOGICAL, NULL); + f = fts_open((char * const *)argv, + FTS_PHYSICAL | FTS_NOCHDIR, NULL); if (NULL == f) { exitcode = (int)MANDOCLEVEL_SYSERR; say("", "&fts_open"); @@ -576,11 +586,36 @@ treescan(void) while (NULL != (ff = fts_read(f))) { path = ff->fts_path + 2; + switch (ff->fts_info) { + /* + * Symbolic links require various sanity checks, + * then get handled just like regular files. + */ + case (FTS_SL): + if (NULL == realpath(path, buf)) { + if (warnings) + say(path, "&realpath"); + continue; + } + if (strstr(buf, basedir) != buf) { + if (warnings) say("", + "%s: outside base directory", buf); + continue; + } + /* Use logical inode to avoid mpages dupe. */ + if (-1 == stat(path, ff->fts_statp)) { + if (warnings) + say(path, "&stat"); + continue; + } + /* FALLTHROUGH */ + + /* * If we're a regular file, add an mlink by using the * stored directory data and handling the filename. */ - if (FTS_F == ff->fts_info) { + case (FTS_F): if (0 == strcmp(path, MANDOC_DB)) continue; if ( ! use_all && ff->fts_level < 2) { @@ -636,8 +671,13 @@ treescan(void) mlink->gzip = gzip; mlink_add(mlink, ff->fts_statp); continue; - } else if (FTS_D != ff->fts_info && - FTS_DP != ff->fts_info) { + + case (FTS_D): + /* FALLTHROUGH */ + case (FTS_DP): + break; + + default: if (warnings) say(path, "Not a regular file"); continue; @@ -726,6 +766,27 @@ filescan(const char *file) if (0 == strncmp(file, "./", 2)) file += 2; + /* + * We have to do lstat(2) before realpath(3) loses + * the information whether this is a symbolic link. + * We need to know that because for symbolic links, + * we want to use the orginal file name, while for + * regular files, we want to use the real path. + */ + if (-1 == lstat(file, &st)) { + exitcode = (int)MANDOCLEVEL_BADARG; + say(file, "&lstat"); + return; + } else if (0 == ((S_IFREG | S_IFLNK) & st.st_mode)) { + exitcode = (int)MANDOCLEVEL_BADARG; + say(file, "Not a regular file"); + return; + } + + /* + * We have to resolve the file name to the real path + * in any case for the base directory check. + */ if (NULL == realpath(file, buf)) { exitcode = (int)MANDOCLEVEL_BADARG; say(file, "&realpath"); @@ -742,14 +803,24 @@ filescan(const char *file) return; } - if (-1 == stat(buf, &st)) { - exitcode = (int)MANDOCLEVEL_BADARG; - say(file, "&stat"); - return; - } else if ( ! (S_IFREG & st.st_mode)) { - exitcode = (int)MANDOCLEVEL_BADARG; - say(file, "Not a regular file"); - return; + /* + * 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 (S_IFLNK & st.st_mode) { + if (-1 == stat(buf, &st)) { + exitcode = (int)MANDOCLEVEL_BADARG; + say(file, "&stat"); + return; + } + strlcpy(buf, file, sizeof(buf)); + start = strstr(buf, basedir) == buf ? + buf + strlen(basedir) + 1 : buf; } mlink = mandoc_calloc(1, sizeof(struct mlink));