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