Signed-off-by: Ilya Kuzmich <ilya.kuzm...@gmail.com> --- tests/pathchk.test | 85 ++++++++++++++++++++++++++++++++++++++++++++++ toys/posix/pathchk.c | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 180 insertions(+) create mode 100644 tests/pathchk.test create mode 100644 toys/posix/pathchk.c
diff --git a/tests/pathchk.test b/tests/pathchk.test new file mode 100644 index 0000000..4632399 --- /dev/null +++ b/tests/pathchk.test @@ -0,0 +1,85 @@ +#!/bin/bash + +[ -f testing.sh ] && . testing.sh + +#testing "name" "command" "result" "infile" "stdin" + +testing "zero arguments" "pathchk 2>/dev/null || echo ok" "ok\n" "" "" + +mkdir dir_a-rx +chmod a-rx dir_a-rx +testing "dir_a-rx/a" "pathchk dir_a-rx/a 2>/dev/null || echo ok" "ok\n" "" "" +rmdir dir_a-rx + +touch file +testing "file/a" "pathchk file/a 2>/dev/null || echo ok" "ok\n" "" "" +rm file + +mkcomponent() { + head -c "$1" /dev/zero | tr '\0' 'a' +} + +mkpath() { + to_fill=$1 + component_len=$2 + + while [ "$to_fill" -gt 0 ]; do + if [ $component_len -ge $to_fill ]; then + component_len=$((to_fill)) + fi + mkcomponent $component_len + to_fill=$((to_fill-component_len)) + if [ "$to_fill" -gt 0 ]; then + printf "/" + to_fill=$((to_fill-1)) + fi + done +} + +NAME_MAX="$(getconf NAME_MAX .)" +testing "NAME_MAX" "pathchk '$(mkcomponent $NAME_MAX )' \ + && echo ok" "ok\n" "" "" +testing "NAME_MAX+1" "pathchk '$(mkcomponent $((NAME_MAX+1)) )' \ + 2>/dev/null || echo ok" "ok\n" "" "" +testing "NAME_MAX/1" "pathchk '$(mkcomponent $NAME_MAX)/a' \ + 2>/dev/null && echo ok" "ok\n" "" "" +testing "NAME_MAX+1/1" "pathchk '$(mkcomponent $((NAME_MAX+1)))/a' \ + 2>/dev/null || echo ok" "ok\n" "" "" + +PATH_MAX="$(getconf PATH_MAX .)" +testing "PATH_MAX-1" "pathchk '$(mkpath $((PATH_MAX-1)) $NAME_MAX)' \ + && echo ok" "ok\n" "" "" +testing "PATH_MAX" "pathchk '$(mkpath $PATH_MAX $NAME_MAX)' \ + 2>/dev/null || echo ok" "ok\n" "" "" + +POSIX_NAME_MAX="14" +testing "-p _POSIX_NAME_MAX" "pathchk -p \ + '$(mkcomponent $POSIX_NAME_MAX)' && echo ok" "ok\n" "" "" +testing "-p _POSIX_NAME_MAX+1" "pathchk -p\ + '$(mkcomponent $((POSIX_NAME_MAX+1)))' 2>/dev/null || echo ok" \ + "ok\n" "" "" +testing "-p _POSIX_NAME_MAX/1" "pathchk -p \ + '$(mkcomponent $POSIX_NAME_MAX)/a' && echo ok" "ok\n" "" "" +testing "-p _POSIX_NAME_MAX+1/1" "pathchk -p\ + '$(mkcomponent $((POSIX_NAME_MAX+1)))/1' 2>/dev/null || echo ok" \ + "ok\n" "" "" + +POSIX_PATH_MAX="256" +testing "-p _POSIX_PATH_MAX-1" "pathchk -p \ + '$(mkpath $((POSIX_PATH_MAX-1)) $POSIX_NAME_MAX)' && echo ok" \ + "ok\n" "" "" +testing "-p _POSIX_PATH_MAX" "pathchk -p \ + '$(mkpath $POSIX_PATH_MAX $POSIX_NAME_MAX)' 2>/dev/null || \ + echo ok" "ok\n" "" "" + +# portable filename character set +testing "-p a=b" "pathchk -p a=b 2>/dev/null || echo ok" "ok\n" "" "" +testing "-p 'a$'" "pathchk -p 'a$' 2>/dev/null || echo ok" "ok\n" "" "" +testing "-p '+a'" "pathchk -p '+a' 2>/dev/null || echo ok" "ok\n" "" "" + +# empty path and leading '-' +testing "-P ''" "pathchk -P '' 2>/dev/null || echo ok" "ok\n" "" "" +testing "-P a/-b" "pathchk -P 'a/-b' 2>/dev/null || echo ok" "ok\n" "" "" + +# one error message per path +testing "-P '' '-'" "pathchk -P '' '-' 2>&1 | wc -l" "2\n" "" "" diff --git a/toys/posix/pathchk.c b/toys/posix/pathchk.c new file mode 100644 index 0000000..25d76a7 --- /dev/null +++ b/toys/posix/pathchk.c @@ -0,0 +1,95 @@ +/* pathchk.c - check pathnames + * + * Copyright 2017 Ilya Kuzmich <ilya.kuzm...@gmail.com> + * + * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/pathchk.html + +USE_PATHCHK(NEWTOY(pathchk, "<1pP", TOYFLAG_USR|TOYFLAG_BIN)) + +config PATHCHK + bool "pathchk" + default y + help + usage: pathchk [-p] [-P] [NAME...] + + Check that pathnames are portable and valid on the underlying file system + + -p check instead for POSIX portability + -P check for empty names and components with leading "-" +*/ + +#define FOR_pathchk +#include "toys.h" + +static long get_pathconf(char *path, int name, long fallback) +{ + long ret; + + errno = 0; + ret = pathconf(path, name); + if (ret == -1 && errno) + ret = fallback; + + return ret; +} + +static void do_check_path(char *path) +{ + long pathmax, namemax; + size_t namelen; + char *p = path; + + if (toys.optflags & FLAG_p) { + namemax = _POSIX_NAME_MAX; + pathmax = _POSIX_PATH_MAX; + + p += strspn(p, "/" + "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "0123456789._-"); + + if (*p) { + error_msg("%s: nonportable character %c in path", path, *p); + return; + } + } else { + struct stat st; + + namemax = get_pathconf(*path == '/' ? "/" : ".", _PC_NAME_MAX, NAME_MAX); + pathmax = get_pathconf(path, _PC_PATH_MAX, PATH_MAX); + + errno = 0; + if (stat(path, &st) && errno != ENOENT) + perror_msg("%s", path); + } + + for (namelen = 0, p = path; *p; p += namelen) { + p += strspn(p, "/"); + namelen = strcspn(p, "/"); + + if (namemax != -1 && namelen > namemax) { + error_msg("%s: component too long (limit %ld)", path, namemax); + return; + } + + if (toys.optflags & FLAG_P && *p == '-') { + error_msg("%s: leading '-' in a path component", path); + return; + } + } + + if (pathmax != -1 && strlen(path) >= (size_t)pathmax) { + error_msg("%s: path too long (limit %ld)", path, pathmax); + return; + } + + if (toys.optflags & FLAG_P && !*path) { + error_msg("path is empty"); + return; + } +} + +void pathchk_main(void) +{ + while (*toys.optargs) do_check_path(*toys.optargs++); +} -- 2.7.4 _______________________________________________ Toybox mailing list Toybox@lists.landley.net http://lists.landley.net/listinfo.cgi/toybox-landley.net