1da2e3ebdSchin /***********************************************************************
2da2e3ebdSchin * *
3da2e3ebdSchin * This software is part of the ast package *
4*3e14f97fSRoger A. Faulkner * Copyright (c) 1992-2010 AT&T Intellectual Property *
5da2e3ebdSchin * and is licensed under the *
6da2e3ebdSchin * Common Public License, Version 1.0 *
77c2fbfb3SApril Chin * by AT&T Intellectual Property *
8da2e3ebdSchin * *
9da2e3ebdSchin * A copy of the License is available at *
10da2e3ebdSchin * http://www.opensource.org/licenses/cpl1.0.txt *
11da2e3ebdSchin * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
12da2e3ebdSchin * *
13da2e3ebdSchin * Information and Software Systems Research *
14da2e3ebdSchin * AT&T Research *
15da2e3ebdSchin * Florham Park NJ *
16da2e3ebdSchin * *
17da2e3ebdSchin * Glenn Fowler <gsf@research.att.com> *
18da2e3ebdSchin * David Korn <dgk@research.att.com> *
19da2e3ebdSchin * *
20da2e3ebdSchin ***********************************************************************/
21da2e3ebdSchin #pragma prototyped
22da2e3ebdSchin /*
23da2e3ebdSchin * pathchk
24da2e3ebdSchin *
25da2e3ebdSchin * Written by David Korn
26da2e3ebdSchin */
27da2e3ebdSchin
28da2e3ebdSchin static const char usage[] =
2934f9b3eeSRoland Mainz "[-?\n@(#)$Id: pathchk (AT&T Research) 2009-07-24 $\n]"
30da2e3ebdSchin USAGE_LICENSE
31da2e3ebdSchin "[+NAME?pathchk - check pathnames for portability]"
3234f9b3eeSRoland Mainz "[+DESCRIPTION?\bpathchk\b checks each \apathname\a to see if it is "
3334f9b3eeSRoland Mainz "valid and/or portable. A \apathname\a is valid if it can be used to "
3434f9b3eeSRoland Mainz "access or create a file without causing syntax errors. A file is "
3534f9b3eeSRoland Mainz "portable if no truncation will result on any conforming POSIX.1 "
3634f9b3eeSRoland Mainz "implementation.]"
37da2e3ebdSchin "[+?By default \bpathchk\b checks each component of each \apathname\a "
3834f9b3eeSRoland Mainz "based on the underlying file system. A diagnostic is written to "
3934f9b3eeSRoland Mainz "standard error for each pathname that:]"
4034f9b3eeSRoland Mainz "{"
41da2e3ebdSchin "[+-?Is longer than \b$(getconf PATH_MAX)\b bytes.]"
4234f9b3eeSRoland Mainz "[+-?Contains any component longer than \b$(getconf NAME_MAX)\b "
4334f9b3eeSRoland Mainz "bytes.]"
4434f9b3eeSRoland Mainz "[+-?Contains any directory component in a directory that is not "
4534f9b3eeSRoland Mainz "searchable.]"
4634f9b3eeSRoland Mainz "[+-?Contains any character in any component that is not valid "
4734f9b3eeSRoland Mainz "in its containing directory.]"
48da2e3ebdSchin "[+-?Is empty.]"
49da2e3ebdSchin "}"
5034f9b3eeSRoland Mainz "[p:components?Instead of performing length checks on the underlying "
5134f9b3eeSRoland Mainz "file system, write a diagnostic for each pathname operand that:]"
5234f9b3eeSRoland Mainz "{"
53da2e3ebdSchin "[+-?Is longer than \b$(getconf _POSIX_PATH_MAX)\b bytes.]"
5434f9b3eeSRoland Mainz "[+-?Contains any component longer than \b$(getconf "
5534f9b3eeSRoland Mainz "_POSIX_NAME_MAX)\b bytes.]"
56da2e3ebdSchin "[+-?Contains any character in any component that is not in the "
57da2e3ebdSchin "portable filename character set.]"
5834f9b3eeSRoland Mainz "}"
5934f9b3eeSRoland Mainz "[P:path?Write a diagnostic for each pathname operand that:]"
6034f9b3eeSRoland Mainz "{"
61da2e3ebdSchin "[+-?Contains any component with \b-\b as the first character.]"
62da2e3ebdSchin "[+-?Is empty.]"
63da2e3ebdSchin "}"
6434f9b3eeSRoland Mainz "[a:all|portability?Equivalent to \b--components\b \b--path\b.]"
65da2e3ebdSchin "\n"
66da2e3ebdSchin "\npathname ...\n"
67da2e3ebdSchin "\n"
6834f9b3eeSRoland Mainz "[+EXIT STATUS?]"
6934f9b3eeSRoland Mainz "{"
70da2e3ebdSchin "[+0?All \apathname\a operands passed all of the checks.]"
71da2e3ebdSchin "[+>0?An error occurred.]"
72da2e3ebdSchin "}"
73da2e3ebdSchin "[+SEE ALSO?\bgetconf\b(1), \bcreat\b(2), \bpathchk\b(2)]"
74da2e3ebdSchin ;
75da2e3ebdSchin
76da2e3ebdSchin
77da2e3ebdSchin #include <cmd.h>
78da2e3ebdSchin #include <ls.h>
79da2e3ebdSchin
8034f9b3eeSRoland Mainz #define COMPONENTS 0x1
8134f9b3eeSRoland Mainz #define PATH 0x2
8234f9b3eeSRoland Mainz
83da2e3ebdSchin #define isport(c) (((c)>='a' && (c)<='z') || ((c)>='A' && (c)<='Z') || ((c)>='0' && (c)<='9') || (strchr("._-",(c))!=0) )
84da2e3ebdSchin
85da2e3ebdSchin /*
86da2e3ebdSchin * call pathconf and handle unlimited sizes
87da2e3ebdSchin */
mypathconf(const char * path,int op)88da2e3ebdSchin static long mypathconf(const char *path, int op)
89da2e3ebdSchin {
90da2e3ebdSchin register long r;
91da2e3ebdSchin
927c2fbfb3SApril Chin static const char* const ops[] = { "NAME_MAX", "PATH_MAX" };
93da2e3ebdSchin
94da2e3ebdSchin errno = 0;
9534f9b3eeSRoland Mainz if ((r = strtol(astconf(ops[op], path, NiL), NiL, 0)) < 0 && !errno)
9634f9b3eeSRoland Mainz return LONG_MAX;
9734f9b3eeSRoland Mainz return r;
98da2e3ebdSchin }
99da2e3ebdSchin
100da2e3ebdSchin /*
101da2e3ebdSchin * returns 1 if <path> passes test
102da2e3ebdSchin */
pathchk(char * path,int mode)103da2e3ebdSchin static int pathchk(char* path, int mode)
104da2e3ebdSchin {
105da2e3ebdSchin register char *cp=path, *cpold;
106da2e3ebdSchin register int c;
107da2e3ebdSchin register long r,name_max,path_max;
108da2e3ebdSchin char buf[2];
109da2e3ebdSchin
110da2e3ebdSchin if(!*path)
111da2e3ebdSchin {
11234f9b3eeSRoland Mainz if (mode & PATH)
113da2e3ebdSchin error(2,"path is empty");
11434f9b3eeSRoland Mainz return -1;
115da2e3ebdSchin }
11634f9b3eeSRoland Mainz if(mode & COMPONENTS)
117da2e3ebdSchin {
118da2e3ebdSchin name_max = _POSIX_NAME_MAX;
119da2e3ebdSchin path_max = _POSIX_PATH_MAX;
120da2e3ebdSchin }
121da2e3ebdSchin else
122da2e3ebdSchin {
123da2e3ebdSchin char tmp[2];
124da2e3ebdSchin name_max = path_max = 0;
125da2e3ebdSchin tmp[0] = (*cp=='/'? '/': '.');
126da2e3ebdSchin tmp[1] = 0;
127da2e3ebdSchin if((r=mypathconf(tmp, 0)) > _POSIX_NAME_MAX)
128da2e3ebdSchin name_max = r;
129da2e3ebdSchin if((r=mypathconf(tmp, 1)) > _POSIX_PATH_MAX)
130da2e3ebdSchin path_max = r;
131da2e3ebdSchin if(*cp!='/')
132da2e3ebdSchin {
133da2e3ebdSchin if(name_max==0||path_max==0)
134da2e3ebdSchin {
135da2e3ebdSchin if(!(cpold = getcwd((char*)0, 0)) && errno == EINVAL && (cpold = newof(0, char, PATH_MAX, 0)) && !getcwd(cpold, PATH_MAX))
136da2e3ebdSchin {
137da2e3ebdSchin free(cpold);
138da2e3ebdSchin cpold = 0;
139da2e3ebdSchin }
140da2e3ebdSchin if(cpold)
141da2e3ebdSchin {
142da2e3ebdSchin cp = cpold + strlen(cpold);
143da2e3ebdSchin while(name_max==0 || path_max==0)
144da2e3ebdSchin {
145da2e3ebdSchin if(cp>cpold)
146da2e3ebdSchin while(--cp>cpold && *cp=='/');
147da2e3ebdSchin *++cp = 0;
148da2e3ebdSchin if(name_max==0 && (r=mypathconf(cpold, 0)) > _POSIX_NAME_MAX)
149da2e3ebdSchin name_max = r;
150da2e3ebdSchin if(path_max==0 && (r=mypathconf(cpold, 1)) > _POSIX_PATH_MAX)
151da2e3ebdSchin path_max=r;
152da2e3ebdSchin if(--cp==cpold)
153da2e3ebdSchin {
154da2e3ebdSchin free(cpold);
155da2e3ebdSchin break;
156da2e3ebdSchin }
157da2e3ebdSchin while(*cp!='/')
158da2e3ebdSchin cp--;
159da2e3ebdSchin }
160da2e3ebdSchin cp=path;
161da2e3ebdSchin }
162da2e3ebdSchin }
163da2e3ebdSchin while(*cp=='/')
164da2e3ebdSchin cp++;
165da2e3ebdSchin }
166da2e3ebdSchin if(name_max==0)
167da2e3ebdSchin name_max=_POSIX_NAME_MAX;
168da2e3ebdSchin if(path_max==0)
169da2e3ebdSchin path_max=_POSIX_PATH_MAX;
170da2e3ebdSchin while(*(cpold=cp))
171da2e3ebdSchin {
172da2e3ebdSchin while((c= *cp++) && c!='/');
173da2e3ebdSchin if((cp-cpold) > name_max)
174da2e3ebdSchin goto err;
175da2e3ebdSchin errno=0;
176da2e3ebdSchin cp[-1] = 0;
177da2e3ebdSchin r = mypathconf(path, 0);
178da2e3ebdSchin if((cp[-1]=c)==0)
179da2e3ebdSchin cp--;
180da2e3ebdSchin else while(*cp=='/')
181da2e3ebdSchin cp++;
182da2e3ebdSchin if(r>=0)
183da2e3ebdSchin name_max=(r<_POSIX_NAME_MAX?_POSIX_NAME_MAX:r);
184da2e3ebdSchin else if(errno==EINVAL)
185da2e3ebdSchin continue;
186da2e3ebdSchin #ifdef ENAMETOOLONG
187da2e3ebdSchin else if(errno==ENAMETOOLONG)
188da2e3ebdSchin {
189da2e3ebdSchin error(2,"%s: pathname too long",path);
19034f9b3eeSRoland Mainz return -1;
191da2e3ebdSchin }
192da2e3ebdSchin #endif /*ENAMETOOLONG*/
193da2e3ebdSchin else
194da2e3ebdSchin break;
195da2e3ebdSchin }
196da2e3ebdSchin }
197da2e3ebdSchin while(*(cpold=cp))
198da2e3ebdSchin {
19934f9b3eeSRoland Mainz if((mode & PATH) && *cp == '-')
200da2e3ebdSchin {
201da2e3ebdSchin error(2,"%s: path component begins with '-'",path,fmtquote(buf, NiL, "'", 1, 0));
20234f9b3eeSRoland Mainz return -1;
203da2e3ebdSchin }
204da2e3ebdSchin while((c= *cp++) && c!='/')
20534f9b3eeSRoland Mainz if((mode & COMPONENTS) && !isport(c))
206da2e3ebdSchin {
207da2e3ebdSchin buf[0] = c;
208da2e3ebdSchin buf[1] = 0;
209da2e3ebdSchin error(2,"%s: '%s' not in portable character set",path,fmtquote(buf, NiL, "'", 1, 0));
21034f9b3eeSRoland Mainz return -1;
211da2e3ebdSchin }
212da2e3ebdSchin if((cp-cpold) > name_max)
213da2e3ebdSchin goto err;
214da2e3ebdSchin if(c==0)
215da2e3ebdSchin break;
216da2e3ebdSchin while(*cp=='/')
217da2e3ebdSchin cp++;
218da2e3ebdSchin }
219da2e3ebdSchin if((cp-path) >= path_max)
220da2e3ebdSchin {
221da2e3ebdSchin error(2, "%s: pathname too long", path);
22234f9b3eeSRoland Mainz return -1;
223da2e3ebdSchin }
22434f9b3eeSRoland Mainz return 0;
225da2e3ebdSchin err:
226da2e3ebdSchin error(2, "%s: component name %.*s too long", path, cp-cpold-1, cpold);
22734f9b3eeSRoland Mainz return -1;
228da2e3ebdSchin }
229da2e3ebdSchin
230da2e3ebdSchin int
b_pathchk(int argc,char ** argv,void * context)231da2e3ebdSchin b_pathchk(int argc, char** argv, void* context)
232da2e3ebdSchin {
23334f9b3eeSRoland Mainz register int mode = 0;
23434f9b3eeSRoland Mainz register char* s;
235da2e3ebdSchin
236da2e3ebdSchin cmdinit(argc, argv, context, ERROR_CATALOG, 0);
23734f9b3eeSRoland Mainz for (;;)
238da2e3ebdSchin {
23934f9b3eeSRoland Mainz switch (optget(argv, usage))
24034f9b3eeSRoland Mainz {
24134f9b3eeSRoland Mainz case 0:
242da2e3ebdSchin break;
24334f9b3eeSRoland Mainz case 'a':
24434f9b3eeSRoland Mainz mode |= COMPONENTS|PATH;
24534f9b3eeSRoland Mainz continue;
24634f9b3eeSRoland Mainz case 'p':
24734f9b3eeSRoland Mainz mode |= COMPONENTS;
24834f9b3eeSRoland Mainz continue;
24934f9b3eeSRoland Mainz case 'P':
25034f9b3eeSRoland Mainz mode |= PATH;
25134f9b3eeSRoland Mainz continue;
252da2e3ebdSchin case ':':
253da2e3ebdSchin error(2, "%s", opt_info.arg);
25434f9b3eeSRoland Mainz continue;
255da2e3ebdSchin case '?':
256da2e3ebdSchin error(ERROR_usage(2), "%s", opt_info.arg);
25734f9b3eeSRoland Mainz continue;
25834f9b3eeSRoland Mainz }
259da2e3ebdSchin break;
260da2e3ebdSchin }
261da2e3ebdSchin argv += opt_info.index;
26234f9b3eeSRoland Mainz if (!*argv || error_info.errors)
26334f9b3eeSRoland Mainz error(ERROR_usage(2),"%s", optusage(NiL));
26434f9b3eeSRoland Mainz while (s = *argv++)
26534f9b3eeSRoland Mainz pathchk(s, mode);
26634f9b3eeSRoland Mainz return error_info.errors != 0;
267da2e3ebdSchin }
268