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