xref: /titanic_44/usr/src/lib/libcmd/common/pathchk.c (revision 3e14f97f673e8a630f076077de35afdd43dc1587)
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