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