1 /*********************************************************************** 2 * * 3 * This software is part of the ast package * 4 * Copyright (c) 1992-2008 AT&T Intellectual Property * 5 * and is licensed under the * 6 * Common Public License, Version 1.0 * 7 * by AT&T Intellectual Property * 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* const 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