xref: /titanic_50/usr/src/lib/libcmd/common/pathchk.c (revision da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968)
1*da2e3ebdSchin /***********************************************************************
2*da2e3ebdSchin *                                                                      *
3*da2e3ebdSchin *               This software is part of the ast package               *
4*da2e3ebdSchin *           Copyright (c) 1992-2007 AT&T Knowledge Ventures            *
5*da2e3ebdSchin *                      and is licensed under the                       *
6*da2e3ebdSchin *                  Common Public License, Version 1.0                  *
7*da2e3ebdSchin *                      by AT&T Knowledge Ventures                      *
8*da2e3ebdSchin *                                                                      *
9*da2e3ebdSchin *                A copy of the License is available at                 *
10*da2e3ebdSchin *            http://www.opensource.org/licenses/cpl1.0.txt             *
11*da2e3ebdSchin *         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
12*da2e3ebdSchin *                                                                      *
13*da2e3ebdSchin *              Information and Software Systems Research               *
14*da2e3ebdSchin *                            AT&T Research                             *
15*da2e3ebdSchin *                           Florham Park NJ                            *
16*da2e3ebdSchin *                                                                      *
17*da2e3ebdSchin *                 Glenn Fowler <gsf@research.att.com>                  *
18*da2e3ebdSchin *                  David Korn <dgk@research.att.com>                   *
19*da2e3ebdSchin *                                                                      *
20*da2e3ebdSchin ***********************************************************************/
21*da2e3ebdSchin #pragma prototyped
22*da2e3ebdSchin /*
23*da2e3ebdSchin  * pathchk
24*da2e3ebdSchin  *
25*da2e3ebdSchin  * Written by David Korn
26*da2e3ebdSchin  */
27*da2e3ebdSchin 
28*da2e3ebdSchin static const char usage[] =
29*da2e3ebdSchin "[-?\n@(#)$Id: pathchk (AT&T Research) 2006-09-19 $\n]"
30*da2e3ebdSchin USAGE_LICENSE
31*da2e3ebdSchin "[+NAME?pathchk - check pathnames for portability]"
32*da2e3ebdSchin "[+DESCRIPTION?\bpathchk\b checks each \apathname\a to see if it "
33*da2e3ebdSchin 	"is valid and/or portable.  A \apathname\a is valid if it "
34*da2e3ebdSchin 	"can be used to access or create a file without causing syntax "
35*da2e3ebdSchin 	"errors.  A file is portable, if no truncation will result on "
36*da2e3ebdSchin 	"any conforming POSIX.1 implementation.]"
37*da2e3ebdSchin "[+?By default \bpathchk\b checks each component of each \apathname\a "
38*da2e3ebdSchin 	"based on the underlying file system.  A diagnostic is written "
39*da2e3ebdSchin 	"to standard error for each pathname that:]{"
40*da2e3ebdSchin 	"[+-?Is longer than \b$(getconf PATH_MAX)\b bytes.]"
41*da2e3ebdSchin 	"[+-?Contains any component longer than \b$(getconf NAME_MAX)\b bytes.]"
42*da2e3ebdSchin 	"[+-?Contains any directory component in a directory that is "
43*da2e3ebdSchin 		"not searchable.]"
44*da2e3ebdSchin 	"[+-?Contains any character in any component that is not valid in "
45*da2e3ebdSchin 		"its containing directory.]"
46*da2e3ebdSchin 	"[+-?Is empty.]"
47*da2e3ebdSchin 	"}"
48*da2e3ebdSchin "[p:portability?Instead of performing length checks on the underlying "
49*da2e3ebdSchin 	"file system, write a diagnostic for each pathname operand that:]{"
50*da2e3ebdSchin 	"[+-?Is longer than \b$(getconf _POSIX_PATH_MAX)\b bytes.]"
51*da2e3ebdSchin 	"[+-?Contains any component longer than "
52*da2e3ebdSchin 		"\b$(getconf _POSIX_NAME_MAX)\b bytes.]"
53*da2e3ebdSchin         "[+-?Contains any character in any component that is not in the "
54*da2e3ebdSchin 		"portable filename character set.]"
55*da2e3ebdSchin #if 0
56*da2e3ebdSchin 	"[+-?Contains any component with \b-\b as the first character.]"
57*da2e3ebdSchin #endif
58*da2e3ebdSchin 	"[+-?Is empty.]"
59*da2e3ebdSchin 	"}"
60*da2e3ebdSchin "\n"
61*da2e3ebdSchin "\npathname ...\n"
62*da2e3ebdSchin "\n"
63*da2e3ebdSchin "[+EXIT STATUS?]{"
64*da2e3ebdSchin         "[+0?All \apathname\a operands passed all of the checks.]"
65*da2e3ebdSchin         "[+>0?An error occurred.]"
66*da2e3ebdSchin "}"
67*da2e3ebdSchin "[+SEE ALSO?\bgetconf\b(1), \bcreat\b(2), \bpathchk\b(2)]"
68*da2e3ebdSchin ;
69*da2e3ebdSchin 
70*da2e3ebdSchin 
71*da2e3ebdSchin #include	<cmd.h>
72*da2e3ebdSchin #include	<ls.h>
73*da2e3ebdSchin 
74*da2e3ebdSchin #define isport(c)	(((c)>='a' && (c)<='z') || ((c)>='A' && (c)<='Z') || ((c)>='0' && (c)<='9') || (strchr("._-",(c))!=0) )
75*da2e3ebdSchin 
76*da2e3ebdSchin /*
77*da2e3ebdSchin  * call pathconf and handle unlimited sizes
78*da2e3ebdSchin  */
79*da2e3ebdSchin static long mypathconf(const char *path, int op)
80*da2e3ebdSchin {
81*da2e3ebdSchin 	register long r;
82*da2e3ebdSchin 
83*da2e3ebdSchin 	static const char*	ops[] = { "NAME_MAX", "PATH_MAX" };
84*da2e3ebdSchin 
85*da2e3ebdSchin 	errno=0;
86*da2e3ebdSchin 	if((r=strtol(astconf(ops[op], path, NiL), NiL, 0))<0 && errno==0)
87*da2e3ebdSchin 		return(LONG_MAX);
88*da2e3ebdSchin 	return(r);
89*da2e3ebdSchin }
90*da2e3ebdSchin 
91*da2e3ebdSchin /*
92*da2e3ebdSchin  * returns 1 if <path> passes test
93*da2e3ebdSchin  */
94*da2e3ebdSchin static int pathchk(char* path, int mode)
95*da2e3ebdSchin {
96*da2e3ebdSchin 	register char *cp=path, *cpold;
97*da2e3ebdSchin 	register int c;
98*da2e3ebdSchin 	register long r,name_max,path_max;
99*da2e3ebdSchin 	char buf[2];
100*da2e3ebdSchin 
101*da2e3ebdSchin 	if(!*path)
102*da2e3ebdSchin 	{
103*da2e3ebdSchin 		error(2,"path is empty");
104*da2e3ebdSchin 		return(0);
105*da2e3ebdSchin 	}
106*da2e3ebdSchin 	if(mode)
107*da2e3ebdSchin 	{
108*da2e3ebdSchin 		name_max = _POSIX_NAME_MAX;
109*da2e3ebdSchin 		path_max = _POSIX_PATH_MAX;
110*da2e3ebdSchin 	}
111*da2e3ebdSchin 	else
112*da2e3ebdSchin 	{
113*da2e3ebdSchin 		char tmp[2];
114*da2e3ebdSchin 		name_max = path_max = 0;
115*da2e3ebdSchin 		tmp[0] = (*cp=='/'? '/': '.');
116*da2e3ebdSchin 		tmp[1] = 0;
117*da2e3ebdSchin 		if((r=mypathconf(tmp, 0)) > _POSIX_NAME_MAX)
118*da2e3ebdSchin 			name_max = r;
119*da2e3ebdSchin 		if((r=mypathconf(tmp, 1)) > _POSIX_PATH_MAX)
120*da2e3ebdSchin 			path_max = r;
121*da2e3ebdSchin 		if(*cp!='/')
122*da2e3ebdSchin 		{
123*da2e3ebdSchin 			if(name_max==0||path_max==0)
124*da2e3ebdSchin 			{
125*da2e3ebdSchin 				if(!(cpold = getcwd((char*)0, 0)) && errno == EINVAL && (cpold = newof(0, char, PATH_MAX, 0)) && !getcwd(cpold, PATH_MAX))
126*da2e3ebdSchin 				{
127*da2e3ebdSchin 					free(cpold);
128*da2e3ebdSchin 					cpold = 0;
129*da2e3ebdSchin 				}
130*da2e3ebdSchin 				if(cpold)
131*da2e3ebdSchin 				{
132*da2e3ebdSchin 					cp = cpold + strlen(cpold);
133*da2e3ebdSchin 					while(name_max==0 || path_max==0)
134*da2e3ebdSchin 					{
135*da2e3ebdSchin 						if(cp>cpold)
136*da2e3ebdSchin 							while(--cp>cpold && *cp=='/');
137*da2e3ebdSchin 						*++cp = 0;
138*da2e3ebdSchin 						if(name_max==0 && (r=mypathconf(cpold, 0)) > _POSIX_NAME_MAX)
139*da2e3ebdSchin 							name_max = r;
140*da2e3ebdSchin 						if(path_max==0 && (r=mypathconf(cpold, 1)) > _POSIX_PATH_MAX)
141*da2e3ebdSchin 							path_max=r;
142*da2e3ebdSchin 						if(--cp==cpold)
143*da2e3ebdSchin 						{
144*da2e3ebdSchin 							free(cpold);
145*da2e3ebdSchin 							break;
146*da2e3ebdSchin 						}
147*da2e3ebdSchin 						while(*cp!='/')
148*da2e3ebdSchin 							cp--;
149*da2e3ebdSchin 					}
150*da2e3ebdSchin 					cp=path;
151*da2e3ebdSchin 				}
152*da2e3ebdSchin 			}
153*da2e3ebdSchin 			while(*cp=='/')
154*da2e3ebdSchin 				cp++;
155*da2e3ebdSchin 		}
156*da2e3ebdSchin 		if(name_max==0)
157*da2e3ebdSchin 			name_max=_POSIX_NAME_MAX;
158*da2e3ebdSchin 		if(path_max==0)
159*da2e3ebdSchin 			path_max=_POSIX_PATH_MAX;
160*da2e3ebdSchin 		while(*(cpold=cp))
161*da2e3ebdSchin 		{
162*da2e3ebdSchin 			while((c= *cp++) && c!='/');
163*da2e3ebdSchin 			if((cp-cpold) > name_max)
164*da2e3ebdSchin 				goto err;
165*da2e3ebdSchin 			errno=0;
166*da2e3ebdSchin 			cp[-1] = 0;
167*da2e3ebdSchin 			r = mypathconf(path, 0);
168*da2e3ebdSchin 			if((cp[-1]=c)==0)
169*da2e3ebdSchin 				cp--;
170*da2e3ebdSchin 			else while(*cp=='/')
171*da2e3ebdSchin 				cp++;
172*da2e3ebdSchin 			if(r>=0)
173*da2e3ebdSchin 				name_max=(r<_POSIX_NAME_MAX?_POSIX_NAME_MAX:r);
174*da2e3ebdSchin 			else if(errno==EINVAL)
175*da2e3ebdSchin 				continue;
176*da2e3ebdSchin #ifdef ENAMETOOLONG
177*da2e3ebdSchin 			else if(errno==ENAMETOOLONG)
178*da2e3ebdSchin 			{
179*da2e3ebdSchin 				error(2,"%s: pathname too long",path);
180*da2e3ebdSchin 				return(0);
181*da2e3ebdSchin 			}
182*da2e3ebdSchin #endif /*ENAMETOOLONG*/
183*da2e3ebdSchin 			else
184*da2e3ebdSchin 				break;
185*da2e3ebdSchin 		}
186*da2e3ebdSchin 	}
187*da2e3ebdSchin 	while(*(cpold=cp))
188*da2e3ebdSchin 	{
189*da2e3ebdSchin 		if(mode && *cp == '-')
190*da2e3ebdSchin 		{
191*da2e3ebdSchin 			error(2,"%s: path component begins with '-'",path,fmtquote(buf, NiL, "'", 1, 0));
192*da2e3ebdSchin 			return(0);
193*da2e3ebdSchin 		}
194*da2e3ebdSchin 		while((c= *cp++) && c!='/')
195*da2e3ebdSchin 			if(mode && !isport(c))
196*da2e3ebdSchin 			{
197*da2e3ebdSchin 				buf[0] = c;
198*da2e3ebdSchin 				buf[1] = 0;
199*da2e3ebdSchin 				error(2,"%s: '%s' not in portable character set",path,fmtquote(buf, NiL, "'", 1, 0));
200*da2e3ebdSchin 				return(0);
201*da2e3ebdSchin 			}
202*da2e3ebdSchin 		if((cp-cpold) > name_max)
203*da2e3ebdSchin 			goto err;
204*da2e3ebdSchin 		if(c==0)
205*da2e3ebdSchin 			break;
206*da2e3ebdSchin 		while(*cp=='/')
207*da2e3ebdSchin 			cp++;
208*da2e3ebdSchin 	}
209*da2e3ebdSchin 	if((cp-path) >= path_max)
210*da2e3ebdSchin 	{
211*da2e3ebdSchin 		error(2,"%s: pathname too long",path);
212*da2e3ebdSchin 		return(0);
213*da2e3ebdSchin 	}
214*da2e3ebdSchin 	return(1);
215*da2e3ebdSchin err:
216*da2e3ebdSchin 	error(2,"%s: component name %.*s too long",path,cp-cpold-1,cpold);
217*da2e3ebdSchin 	return(0);
218*da2e3ebdSchin }
219*da2e3ebdSchin 
220*da2e3ebdSchin int
221*da2e3ebdSchin b_pathchk(int argc, char** argv, void* context)
222*da2e3ebdSchin {
223*da2e3ebdSchin 	register int n, mode=0;
224*da2e3ebdSchin 	register char *cp;
225*da2e3ebdSchin 
226*da2e3ebdSchin 	cmdinit(argc, argv, context, ERROR_CATALOG, 0);
227*da2e3ebdSchin 	while (n = optget(argv, usage)) switch (n)
228*da2e3ebdSchin 	{
229*da2e3ebdSchin   	    case 'p':
230*da2e3ebdSchin 		mode = 1;
231*da2e3ebdSchin 		break;
232*da2e3ebdSchin 	    case ':':
233*da2e3ebdSchin 		error(2, "%s", opt_info.arg);
234*da2e3ebdSchin 		break;
235*da2e3ebdSchin 	    case '?':
236*da2e3ebdSchin 		error(ERROR_usage(2), "%s", opt_info.arg);
237*da2e3ebdSchin 		break;
238*da2e3ebdSchin 	}
239*da2e3ebdSchin 	argv += opt_info.index;
240*da2e3ebdSchin 	if(*argv==0 || error_info.errors)
241*da2e3ebdSchin 		error(ERROR_usage(2),"%s", optusage((char*)0));
242*da2e3ebdSchin 	while(cp = *argv++)
243*da2e3ebdSchin 	{
244*da2e3ebdSchin 		if(!pathchk(cp,mode))
245*da2e3ebdSchin 			error_info.errors=1;
246*da2e3ebdSchin 	}
247*da2e3ebdSchin 	return(error_info.errors);
248*da2e3ebdSchin }
249