xref: /titanic_52/usr/src/contrib/ast/src/lib/libcmd/pathchk.c (revision 906afcb89d0412cc073b95c2d701a804a8cdb62c)
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   */
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   */
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
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