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