Date: Tue, 12 Feb 2002 17:03:03 -0500 From: Mike Barcroft <mike@FreeBSD.ORG> To: Chuck Rouillard <chuckr@opus.sandiegoca.ncr.com> Cc: FreeBSD-Standards <freebsd-standards@FreeBSD.ORG> Subject: Re: pathchk - review Message-ID: <20020212170303.B55750@espresso.q9media.com> In-Reply-To: <20020205232519.N7805-101000@opus.sandiegoca.ncr.com>; from chuckr@opus.sandiegoca.ncr.com on Tue, Feb 05, 2002 at 11:54:26PM -0800 References: <20020129210829.GC50337@madman.nectar.cc> <20020205232519.N7805-101000@opus.sandiegoca.ncr.com>
index | next in thread | previous in thread | raw e-mail
Chuck Rouillard <chuckr@opus.sandiegoca.ncr.com> writes:
> A revised `pathchk' is being submitted for review.
Sorry for the delay in my review.
> Makefile:
`# $FreeBSD$' is needed here.
> PROG= pathchk
>
> .include <bsd.prog.mk>
> pathchk.1:
> .\" Copyright (c) 2001, 2002 Chuck Rouillard
> .\" All rights reserved.
> .\"
> .\" Redistribution and use in source and binary forms, with or without
> .\" modification, are permitted provided that the following conditions
> .\" are met:
> .\" 1. Redistributions of source code must retain the above copyright
> .\" notice, this list of conditions and the following disclaimer.
> .\" 2. Redistributions in binary form must reproduce the above copyright
> .\" notice, this list of conditions and the following disclaimer in the
> .\" documentation and/or other materials provided with the distribution.
> .\" 3. The name of the author may not be used to endorse or promote
> .\" products derived from this software without specific prior written
> .\" permission.
> .\"
> .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
> .\" OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
> .\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
> .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
> .\" DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
> .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
> .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
> .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
> .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
> .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
> .\" SUCH DAMAGE.
> .\"
> .\"
This line should be `.\" $FreeBSD$'.
> .\"
> .Dd January 2, 2002
This could be refreshed. :)
> .Dt PATHCHK 1
> .Os
> .Sh NAME
> .Nm pathchk
> .Nd checks pathnames
> .Sh SYNOPSIS
> .Nm pathchk
> .Op Fl p
> .Ar pathname
> .Ar ...
> .Sh DESCRIPTION
> The
> .Nm pathchk
The `pathchk' argument here is redundent. I don't comment on this
problem below.
> utility verifies whether a specified
> .Ar pathname
> may be used to access or create a file without error. By default,
New sentences need to begin on new lines. I don't comment on this
problem below.
> the overall path, and each
> .Ar pathname
> component, is checked against the underlying filesystem. Any of the
> following errors will result in a message along with the offending
> .Ar pathname
> sent to stderr.
> .Pp
> .Bl -bullet
> .It
> The
> .Ar pathname
> is longer than the maximum defined by PATH_MAX for the underlying
> filesystem.
> .It
> The
> .Ar pathname
> contains a path component longer than the maximum defined by NAME_MAX
> for the underlying filesystem.
> .It
> A component of the
> .Ar pathname
> which is not searchable or inaccessable by the
> underlying filesystem.
> .It
> Contains any character(s) which are invalid for the containing directory.
> .El
> .Pp
> Any
> .Ar pathname
> containing a non-existent path component is not considered an error if
> none of the remaining components nor the overall path length exceed the
> maximums defined by the underlying filesystem.
> .Pp
> The options are as follows:
> .Bl -tag -width indent
> .It Fl p
> Verify each
> .Ar pathname
> using portability checks instead of the underlying filesystem. Report
> any of the following as errors to console.
> .Pp
> .Bl -bullet
> .It
> The
> .Ar pathname
> is longer than the maximum _POSIX_PATH_MAX.
> .It
> The
> .Ar pathname
> contains a path component longer than the maximum _POXIX_NAME_MAX.
> .It
> The
> .Ar pathname
> contains at least one character which is not in the set of A-Z, a-z, 0-9,
I think these should be listed in individual .Dq macros.
> `.'(period), `-'(hyphen), or `_'(underscore). The `-'(hyphen) may not be
.Sq and .Pq should be used to here to wrap.
> used as the first character of any path component.
> .El
> .El
> .Pp
`.Sh DIAGNOSTICS' needed here.
> .Nm pathchk
> exits 0 on success, 1 if an error occurred.
The .Ex macro should be used here.
> .Sh SEE ALSO
> .Xr stat 2 ,
> .Xr pathconf 2
Misordered references.
> .Sh STANDARDS
> The
> .Nm pathchk
> utility is expected to conform to
> .St -susv2
> and
> .St -p1003.2 .
I think this should be SUSv3 and not refer to POSIX.2.
You may want to have Ruslan review this too. I don't have a lot of
mdoc-fu.
> pathchk.c
> /*
> * Copyright (c) 2001, 2002 Chuck Rouillard
> * All rights reserved.
> *
> * Redistribution and use in source and binary forms, with or without
> * modification, are permitted provided that the following conditions
> * are met:
> * 1. Redistributions of source code must retain the above copyright
> * notice, this list of conditions and the following disclaimer.
> * 2. Redistributions in binary form must reproduce the above copyright
> * notice, this list of conditions and the following disclaimer in the
> * documentation and/or other materials provided with the distribution.
> * 3. The name of the author may not be used to endorse or promote
> * products derived from this software without specific prior written
> * permission.
> *
> * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
> * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
> * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
> * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
> * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
> * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
> * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
> * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
> * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
> * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
> * SUCH DAMAGE.
Needs a ` *' line and a ` * $FreeBSD$' line.
> */
> #include <sys/stat.h>
> #include <sys/types.h>
>
> #include <errno.h>
> #include <limits.h>
> #include <locale.h>
> #include <stdio.h>
> #include <stdlib.h>
> #include <string.h>
> #include <unistd.h>
>
> const char *illegalchar = "pathchk: Illegal character";
> const char *memoryerror = "pathchk: No memory to allocate";
> const char *nametoolong = "pathchk: Path component too long";
> const char *pathchkhelp = "usage: pathchk [-p] path ...\n";
> const char *pathtoolong = "pathchk: Pathname too long";
> const char *posix_chars = "0123456789._-"
> "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
> "abcdefghijklmnopqrstuvwxyz";
These should all be #define's, unless I missed somewhere that they are
changed.
> /*
> * Local function prototypes.
> */
> int posixpath(char *);
> int pe(const char *, int);
> int systempath(char *);
> void usage(void);
These should all have `static' keywords. That will eliminate the need
for the comment too.
>
> /*
This line needs to be `/*-' to follow KNF, since you do special
tabbing in this comment. See indent(1) for details. I don't comment
on this below.
> * Verify path portability by checking if the:
> * - complete path and filename <= _POSIX_PATH_MAX,
> * - each path component length <= _POSIX_NAME_MAX,
> * - and that each path component contains no
> * disallowed characters.
> * Return 0 for an accepted path, 1 otherwise.
> */
> int
> posixpath(char *pathname)
> {
> int namelen, pathlen, rval;
> char *name, *path;
> char *wpath, *wptr;
These two lines can be combined. I think pointers are generally
considered to be larger than integers, so it might be a good idea to
move the combined line to the top.
>
> pathlen = strlen(pathname);
> if (pathlen > _POSIX_PATH_MAX) {
> (void)fprintf(stderr, "%s\n%s\n", pathtoolong, pathname);
> return 1;
Return values need to be wrapped in parens. I don't comment on this
below.
> }
>
> /*
> * Create space in which to build a validated
Comment lines should be wrapped at 80 characters. I don't comment on
this below.
> * path. Upon any error, this path is shown
> * in its current state.
> */
> if ((path = malloc(pathlen + 1)) == NULL) {
> (void)fprintf(stderr, "%s\n", memoryerror);
> exit(1);
I thought this was mentioned in a earlier review, but this should use
err(1). Since the errno can provide a more traditional message or a
locale-specific message (if we had support for that :). I don't
comment on this below.
Please use <sysexists.h>-style exit levels everywhere.
> }
> *path = NULL;
>
> /*
> * Since `strsep' mangles the strings sent to it,
> * create a working copy of pathname to preserve
> * the original argument value. Note that an
> * additional working pointer is set for use
> * with `free'.
> */
> if ((wpath = strdup(pathname)) == NULL) {
> (void)fprintf(stderr, "%s\n", memoryerror);
> exit(1);
> }
> wptr = wpath;
>
> /*
> * Begin path validation by checking each path
> * component for any exceeded length and any
> * disallowed character(s).
> */
> rval = 0;
> while ((name = strsep(&wpath, "/")) != NULL) {
> if ((namelen = strlen(name)) != 0) {
> (void)strcat(path, name);
Presumably `name' is guaranteed to fit into `path'? (I didn't check.)
> if (namelen > _POSIX_NAME_MAX) {
> (void)fprintf(stderr, "%s\n%s\n",
> nametoolong, path);
Wrapped lines begin four spaces further than the line above. See
style(9) for details. I don't comment on this below.
> rval = 1;
> break;
> }
> if (*name == '-' ||
> strspn(name, posix_chars) != namelen) {
> (void)fprintf(stderr, "%s\n%s\n",
> illegalchar, path);
> rval = 1;
> break;
> }
> }
> if (wpath != NULL)
> (void)strcat(path, "/");
> }
> free(wptr);
> free(path);
> return rval;
> }
>
> /*
> * Assume all error types are path errors except in the
> * case where a path/file does not exist.
> */
> int
> pe(const char *path, int type)
> {
Need an extra line here. Specifically, functions that don't have
local variables still put a blank space here. I don't comment on this
below.
> switch (type) {
> case ENOENT:
> return 0;
> default:
> perror("pathchk");
> (void)fprintf(stderr, "%s\n", path);
> break;
> }
> return 1;
> }
>
> /*
> * Check the current pathname against the underlying
> * filesystem for access, overall path length, and
> * path component length.
> *
> * If the path component is a directory and is:
> * - the last component : return 0
> * - not the last component but searchable : return 0
> * - last component, but not searchable : return 1
> *
> * If the path component is a file and is:
> * - the last component: return 0
> * - not the last component : return 1
> *
> * If a path component (and conseqently the entire path
> * from that point) is found to not exist, then only the
> * overall path and each path component are checked for
> * excessive length.
> */
> int
> systempath(char *pathname)
> {
> struct stat statbuf;
> int namelen, namemax;
> int pathlen, pathmax;
> int pexist, rval;
These three lines can be combined.
> char *name, *path;
> char *wpath, *wptr;
These two lines can be combined. See comment above about pointers.
>
> /*
> * Do an initial check to determine any critical
> * errors and prime max values.
> */
> pexist = 1;
> path = *pathname == '/' ? "/" : ".";
> namemax = pathconf(path , _PC_NAME_MAX);
> pathmax = pathconf(path , _PC_PATH_MAX);
I think pathconf(2)'s return value should be checked for error. I
don't comment on this below.
> if (namemax == -1 || pathmax == -1) {
> if ((pexist = pe(pathname, errno)) == 1)
> return 1;
> }
>
> /*
> * Create space in which to build a validated
> * path. Upon any error, this path is shown
> * in its current state.
> */
> pathlen = strlen(pathname);
> if ((path = malloc(pathlen + 1)) == NULL) {
> (void)fprintf(stderr, "%s\n", memoryerror);
> exit(1);
> }
> *path = NULL;
>
> /*
> * Since `strsep' mangles the strings sent to it,
This should probably say `strsep(3)'.
> * create a working copy of pathname to preserve
> * the original argument value. Note that an
> * additional working pointer is set for use
> * with `free'.
> */
> if ((wpath = strdup(pathname)) == NULL) {
> (void)fprintf(stderr, "%s\n", memoryerror);
> exit(1);
> }
> wptr = wpath;
>
> /*
> * Begin path validation by checking each path
> * component for errors and reached maximums. Any
> * error (except for non-existing paths) will end
> * further testing and exit function.
> */
> rval = 0;
> while ((name = strsep(&wpath, "/")) != NULL) {
> if ((namelen = strlen(name)) != 0) {
> (void)strcat(path, name);
> if (pexist) {
> if (stat(path, &statbuf) == -1) {
> /*
> * Since stat failed, check errno and
> * and show error to console -except-
> * if the path doesn't exist (ENOENT).
> */
> if ((pexist = pe(path, errno)) == 1) {
> rval = 1;
> break;
> }
> } else {
> /*
> * As long as the path is still valid,
> * verify the allowable path length
> * and path component maximums for
> * the current filesystem.
> */
> namemax = pathconf(path, _PC_NAME_MAX);
> pathmax = pathconf(path, _PC_PATH_MAX);
> if (namemax == -1 || pathmax == -1) {
> if ((pexist =
> pe(path, errno)) == 1) {
> rval = 1;
> break;
> }
> }
> }
> } else {
> /*
> * If the path doesn't exist, the only
> * realistic checks are those based on
> * overal path length and path component
> * length. Maximums are based on last
> * successful call to pathconf.
> */
> if (namelen > namemax) {
> (void)fprintf(stderr, "%s\n%s\n",
> nametoolong, path);
> rval = 1;
> break;
> }
> if (pathlen > pathmax) {
> (void)fprintf(stderr, "%s\n%s\n",
> pathtoolong, path);
> rval = 1;
> break;
> }
> }
> }
> if (wpath != NULL)
> (void)strcat(path, "/");
> }
> free(wptr);
> free(path);
> return rval;
> }
>
> void
> usage(void)
> {
> (void)fprintf(stderr, "%s\n", pathchkhelp);
> exit(1);
> }
>
> int
> main(int argc, char **argv)
> {
> int (*fptr)();
> int c, pflag, rval;
>
> pflag = rval = 0;
> while ((c = getopt(argc, argv, "p")) != -1) {
> switch (c) {
> case 'p':
> pflag = 1;
> break;
> case '?':
> default:
> usage();
> }
> }
> argc -= optind;
> argv += optind;
> if (argc == 0)
> usage();
> fptr = (pflag == 0) ? systempath : posixpath;
> while (argc--)
> rval |= (fptr)(*argv++);
> exit(rval);
> }
Best regards,
Mike Barcroft
To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-standards" in the body of the message
home |
help
Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20020212170303.B55750>
