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