14887Schin /***********************************************************************
24887Schin * *
34887Schin * This software is part of the ast package *
4*12068SRoger.Faulkner@Oracle.COM * Copyright (c) 1992-2010 AT&T Intellectual Property *
54887Schin * and is licensed under the *
64887Schin * Common Public License, Version 1.0 *
78462SApril.Chin@Sun.COM * by AT&T Intellectual Property *
84887Schin * *
94887Schin * A copy of the License is available at *
104887Schin * http://www.opensource.org/licenses/cpl1.0.txt *
114887Schin * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
124887Schin * *
134887Schin * Information and Software Systems Research *
144887Schin * AT&T Research *
154887Schin * Florham Park NJ *
164887Schin * *
174887Schin * Glenn Fowler <gsf@research.att.com> *
184887Schin * David Korn <dgk@research.att.com> *
194887Schin * *
204887Schin ***********************************************************************/
214887Schin #pragma prototyped
224887Schin /*
234887Schin * pathchk
244887Schin *
254887Schin * Written by David Korn
264887Schin */
274887Schin
284887Schin static const char usage[] =
2910898Sroland.mainz@nrubsig.org "[-?\n@(#)$Id: pathchk (AT&T Research) 2009-07-24 $\n]"
304887Schin USAGE_LICENSE
314887Schin "[+NAME?pathchk - check pathnames for portability]"
3210898Sroland.mainz@nrubsig.org "[+DESCRIPTION?\bpathchk\b checks each \apathname\a to see if it is "
3310898Sroland.mainz@nrubsig.org "valid and/or portable. A \apathname\a is valid if it can be used to "
3410898Sroland.mainz@nrubsig.org "access or create a file without causing syntax errors. A file is "
3510898Sroland.mainz@nrubsig.org "portable if no truncation will result on any conforming POSIX.1 "
3610898Sroland.mainz@nrubsig.org "implementation.]"
374887Schin "[+?By default \bpathchk\b checks each component of each \apathname\a "
3810898Sroland.mainz@nrubsig.org "based on the underlying file system. A diagnostic is written to "
3910898Sroland.mainz@nrubsig.org "standard error for each pathname that:]"
4010898Sroland.mainz@nrubsig.org "{"
4110898Sroland.mainz@nrubsig.org "[+-?Is longer than \b$(getconf PATH_MAX)\b bytes.]"
4210898Sroland.mainz@nrubsig.org "[+-?Contains any component longer than \b$(getconf NAME_MAX)\b "
4310898Sroland.mainz@nrubsig.org "bytes.]"
4410898Sroland.mainz@nrubsig.org "[+-?Contains any directory component in a directory that is not "
4510898Sroland.mainz@nrubsig.org "searchable.]"
4610898Sroland.mainz@nrubsig.org "[+-?Contains any character in any component that is not valid "
4710898Sroland.mainz@nrubsig.org "in its containing directory.]"
4810898Sroland.mainz@nrubsig.org "[+-?Is empty.]"
4910898Sroland.mainz@nrubsig.org "}"
5010898Sroland.mainz@nrubsig.org "[p:components?Instead of performing length checks on the underlying "
5110898Sroland.mainz@nrubsig.org "file system, write a diagnostic for each pathname operand that:]"
5210898Sroland.mainz@nrubsig.org "{"
5310898Sroland.mainz@nrubsig.org "[+-?Is longer than \b$(getconf _POSIX_PATH_MAX)\b bytes.]"
5410898Sroland.mainz@nrubsig.org "[+-?Contains any component longer than \b$(getconf "
5510898Sroland.mainz@nrubsig.org "_POSIX_NAME_MAX)\b bytes.]"
564887Schin "[+-?Contains any character in any component that is not in the "
5710898Sroland.mainz@nrubsig.org "portable filename character set.]"
5810898Sroland.mainz@nrubsig.org "}"
5910898Sroland.mainz@nrubsig.org "[P:path?Write a diagnostic for each pathname operand that:]"
6010898Sroland.mainz@nrubsig.org "{"
6110898Sroland.mainz@nrubsig.org "[+-?Contains any component with \b-\b as the first character.]"
6210898Sroland.mainz@nrubsig.org "[+-?Is empty.]"
6310898Sroland.mainz@nrubsig.org "}"
6410898Sroland.mainz@nrubsig.org "[a:all|portability?Equivalent to \b--components\b \b--path\b.]"
654887Schin "\n"
664887Schin "\npathname ...\n"
674887Schin "\n"
6810898Sroland.mainz@nrubsig.org "[+EXIT STATUS?]"
6910898Sroland.mainz@nrubsig.org "{"
704887Schin "[+0?All \apathname\a operands passed all of the checks.]"
714887Schin "[+>0?An error occurred.]"
7210898Sroland.mainz@nrubsig.org "}"
734887Schin "[+SEE ALSO?\bgetconf\b(1), \bcreat\b(2), \bpathchk\b(2)]"
744887Schin ;
754887Schin
764887Schin
774887Schin #include <cmd.h>
784887Schin #include <ls.h>
794887Schin
8010898Sroland.mainz@nrubsig.org #define COMPONENTS 0x1
8110898Sroland.mainz@nrubsig.org #define PATH 0x2
8210898Sroland.mainz@nrubsig.org
834887Schin #define isport(c) (((c)>='a' && (c)<='z') || ((c)>='A' && (c)<='Z') || ((c)>='0' && (c)<='9') || (strchr("._-",(c))!=0) )
844887Schin
854887Schin /*
864887Schin * call pathconf and handle unlimited sizes
874887Schin */
mypathconf(const char * path,int op)884887Schin static long mypathconf(const char *path, int op)
894887Schin {
908462SApril.Chin@Sun.COM register long r;
914887Schin
928462SApril.Chin@Sun.COM static const char* const ops[] = { "NAME_MAX", "PATH_MAX" };
934887Schin
9410898Sroland.mainz@nrubsig.org errno = 0;
9510898Sroland.mainz@nrubsig.org if ((r = strtol(astconf(ops[op], path, NiL), NiL, 0)) < 0 && !errno)
9610898Sroland.mainz@nrubsig.org return LONG_MAX;
9710898Sroland.mainz@nrubsig.org return r;
984887Schin }
994887Schin
1004887Schin /*
1014887Schin * returns 1 if <path> passes test
1024887Schin */
pathchk(char * path,int mode)1034887Schin static int pathchk(char* path, int mode)
1044887Schin {
1054887Schin register char *cp=path, *cpold;
1064887Schin register int c;
1074887Schin register long r,name_max,path_max;
1084887Schin char buf[2];
1094887Schin
1104887Schin if(!*path)
1114887Schin {
11210898Sroland.mainz@nrubsig.org if (mode & PATH)
11310898Sroland.mainz@nrubsig.org error(2,"path is empty");
11410898Sroland.mainz@nrubsig.org return -1;
1154887Schin }
11610898Sroland.mainz@nrubsig.org if(mode & COMPONENTS)
1174887Schin {
1184887Schin name_max = _POSIX_NAME_MAX;
1194887Schin path_max = _POSIX_PATH_MAX;
1204887Schin }
1214887Schin else
1224887Schin {
1234887Schin char tmp[2];
1244887Schin name_max = path_max = 0;
1254887Schin tmp[0] = (*cp=='/'? '/': '.');
1264887Schin tmp[1] = 0;
1274887Schin if((r=mypathconf(tmp, 0)) > _POSIX_NAME_MAX)
1284887Schin name_max = r;
1294887Schin if((r=mypathconf(tmp, 1)) > _POSIX_PATH_MAX)
1304887Schin path_max = r;
1314887Schin if(*cp!='/')
1324887Schin {
1334887Schin if(name_max==0||path_max==0)
1344887Schin {
1354887Schin if(!(cpold = getcwd((char*)0, 0)) && errno == EINVAL && (cpold = newof(0, char, PATH_MAX, 0)) && !getcwd(cpold, PATH_MAX))
1364887Schin {
1374887Schin free(cpold);
1384887Schin cpold = 0;
1394887Schin }
1404887Schin if(cpold)
1414887Schin {
1424887Schin cp = cpold + strlen(cpold);
1434887Schin while(name_max==0 || path_max==0)
1444887Schin {
1454887Schin if(cp>cpold)
1464887Schin while(--cp>cpold && *cp=='/');
1474887Schin *++cp = 0;
1484887Schin if(name_max==0 && (r=mypathconf(cpold, 0)) > _POSIX_NAME_MAX)
1494887Schin name_max = r;
1504887Schin if(path_max==0 && (r=mypathconf(cpold, 1)) > _POSIX_PATH_MAX)
1514887Schin path_max=r;
1524887Schin if(--cp==cpold)
1534887Schin {
1544887Schin free(cpold);
1554887Schin break;
1564887Schin }
1574887Schin while(*cp!='/')
1584887Schin cp--;
1594887Schin }
1604887Schin cp=path;
1614887Schin }
1624887Schin }
1634887Schin while(*cp=='/')
1644887Schin cp++;
1654887Schin }
1664887Schin if(name_max==0)
1674887Schin name_max=_POSIX_NAME_MAX;
1684887Schin if(path_max==0)
1694887Schin path_max=_POSIX_PATH_MAX;
1704887Schin while(*(cpold=cp))
1714887Schin {
1724887Schin while((c= *cp++) && c!='/');
1734887Schin if((cp-cpold) > name_max)
1744887Schin goto err;
1754887Schin errno=0;
1764887Schin cp[-1] = 0;
1774887Schin r = mypathconf(path, 0);
1784887Schin if((cp[-1]=c)==0)
1794887Schin cp--;
1804887Schin else while(*cp=='/')
1814887Schin cp++;
1824887Schin if(r>=0)
1834887Schin name_max=(r<_POSIX_NAME_MAX?_POSIX_NAME_MAX:r);
1844887Schin else if(errno==EINVAL)
1854887Schin continue;
1864887Schin #ifdef ENAMETOOLONG
1874887Schin else if(errno==ENAMETOOLONG)
1884887Schin {
1894887Schin error(2,"%s: pathname too long",path);
19010898Sroland.mainz@nrubsig.org return -1;
1914887Schin }
1924887Schin #endif /*ENAMETOOLONG*/
1934887Schin else
1944887Schin break;
1954887Schin }
1964887Schin }
1974887Schin while(*(cpold=cp))
1984887Schin {
19910898Sroland.mainz@nrubsig.org if((mode & PATH) && *cp == '-')
2004887Schin {
2014887Schin error(2,"%s: path component begins with '-'",path,fmtquote(buf, NiL, "'", 1, 0));
20210898Sroland.mainz@nrubsig.org return -1;
2034887Schin }
2044887Schin while((c= *cp++) && c!='/')
20510898Sroland.mainz@nrubsig.org if((mode & COMPONENTS) && !isport(c))
2064887Schin {
2074887Schin buf[0] = c;
2084887Schin buf[1] = 0;
2094887Schin error(2,"%s: '%s' not in portable character set",path,fmtquote(buf, NiL, "'", 1, 0));
21010898Sroland.mainz@nrubsig.org return -1;
2114887Schin }
2124887Schin if((cp-cpold) > name_max)
2134887Schin goto err;
2144887Schin if(c==0)
2154887Schin break;
2164887Schin while(*cp=='/')
2174887Schin cp++;
2184887Schin }
2194887Schin if((cp-path) >= path_max)
2204887Schin {
22110898Sroland.mainz@nrubsig.org error(2, "%s: pathname too long", path);
22210898Sroland.mainz@nrubsig.org return -1;
2234887Schin }
22410898Sroland.mainz@nrubsig.org return 0;
22510898Sroland.mainz@nrubsig.org err:
22610898Sroland.mainz@nrubsig.org error(2, "%s: component name %.*s too long", path, cp-cpold-1, cpold);
22710898Sroland.mainz@nrubsig.org return -1;
2284887Schin }
2294887Schin
2304887Schin int
b_pathchk(int argc,char ** argv,void * context)2314887Schin b_pathchk(int argc, char** argv, void* context)
2324887Schin {
23310898Sroland.mainz@nrubsig.org register int mode = 0;
23410898Sroland.mainz@nrubsig.org register char* s;
2354887Schin
2364887Schin cmdinit(argc, argv, context, ERROR_CATALOG, 0);
23710898Sroland.mainz@nrubsig.org for (;;)
2384887Schin {
23910898Sroland.mainz@nrubsig.org switch (optget(argv, usage))
24010898Sroland.mainz@nrubsig.org {
24110898Sroland.mainz@nrubsig.org case 0:
24210898Sroland.mainz@nrubsig.org break;
24310898Sroland.mainz@nrubsig.org case 'a':
24410898Sroland.mainz@nrubsig.org mode |= COMPONENTS|PATH;
24510898Sroland.mainz@nrubsig.org continue;
24610898Sroland.mainz@nrubsig.org case 'p':
24710898Sroland.mainz@nrubsig.org mode |= COMPONENTS;
24810898Sroland.mainz@nrubsig.org continue;
24910898Sroland.mainz@nrubsig.org case 'P':
25010898Sroland.mainz@nrubsig.org mode |= PATH;
25110898Sroland.mainz@nrubsig.org continue;
25210898Sroland.mainz@nrubsig.org case ':':
25310898Sroland.mainz@nrubsig.org error(2, "%s", opt_info.arg);
25410898Sroland.mainz@nrubsig.org continue;
25510898Sroland.mainz@nrubsig.org case '?':
25610898Sroland.mainz@nrubsig.org error(ERROR_usage(2), "%s", opt_info.arg);
25710898Sroland.mainz@nrubsig.org continue;
25810898Sroland.mainz@nrubsig.org }
2594887Schin break;
2604887Schin }
2614887Schin argv += opt_info.index;
26210898Sroland.mainz@nrubsig.org if (!*argv || error_info.errors)
26310898Sroland.mainz@nrubsig.org error(ERROR_usage(2),"%s", optusage(NiL));
26410898Sroland.mainz@nrubsig.org while (s = *argv++)
26510898Sroland.mainz@nrubsig.org pathchk(s, mode);
26610898Sroland.mainz@nrubsig.org return error_info.errors != 0;
2674887Schin }
268