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