1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 28 /* All Rights Reserved */ 29 30 31 #include <stdio.h> 32 #include <ctype.h> 33 #include <dirent.h> 34 #include <limits.h> 35 #include <stdlib.h> 36 #include <unistd.h> 37 #include <string.h> 38 #include <sys/types.h> 39 #include <sys/stat.h> 40 #include <pkgstrct.h> 41 #include <errno.h> 42 #include <locale.h> 43 #include <libintl.h> 44 #include <pkglib.h> 45 #include "libadm.h" 46 #include "libinst.h" 47 48 extern int holdcinfo; 49 50 #define WRN_SCARYLINK "WARNING: <%s>, target of symlink <%s>, does not exist." 51 52 #define ERR_PATHLONG "path argument too long" 53 #define ERR_CLASSLONG "classname argument too long" 54 #define ERR_CLASSCHAR "bad character in classname" 55 #define ERR_STAT "unable to stat <%s>" 56 #define ERR_WRITE "write of entry failed" 57 #define ERR_POPEN "unable to create pipe to <%s>" 58 #define ERR_PCLOSE "unable to close pipe to <%s>" 59 #define ERR_RDLINK "unable to read link for <%s>" 60 #define ERR_MEMORY "memory allocation failure, errno=%d" 61 62 #define LINK 1 63 64 struct link { 65 char *path; 66 ino_t ino; 67 dev_t dev; 68 struct link *next; 69 }; 70 71 static struct link *firstlink = (struct link *)0; 72 static struct link *lastlink = (struct link *)0; 73 static char *scan_raw_ln(char *targ_name, char *link_name); 74 75 static char *def_class = "none"; 76 77 static int errflg = 0; 78 static int iflag = 0; /* follow symlinks */ 79 static int xflag = 0; /* confirm contents of files */ 80 static int nflag = 0; 81 static char construction[PATH_MAX], mylocal[PATH_MAX]; 82 83 static void findlink(struct cfent *ept, char *path, char *svpath); 84 static void follow(char *path); 85 static void output(char *path, int n, char *local); 86 static void usage(void); 87 88 int 89 main(int argc, char *argv[]) 90 { 91 int c; 92 char *pt, path[PATH_MAX]; 93 char *abi_sym_ptr; 94 extern char *optarg; 95 extern int optind; 96 97 (void) setlocale(LC_ALL, ""); 98 99 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ 100 #define TEXT_DOMAIN "SYS_TEST" 101 #endif 102 (void) textdomain(TEXT_DOMAIN); 103 104 (void) set_prog_name(argv[0]); 105 106 while ((c = getopt(argc, argv, "xnic:?")) != EOF) { 107 switch (c) { 108 case 'x': /* include content info */ 109 xflag++; 110 break; 111 112 case 'n': 113 nflag++; 114 break; 115 116 case 'c': /* assign class */ 117 def_class = optarg; 118 /* validate that classname is acceptable */ 119 if (strlen(def_class) > (size_t)CLSSIZ) { 120 progerr(gettext(ERR_CLASSLONG)); 121 exit(1); 122 } 123 for (pt = def_class; *pt; pt++) { 124 if (!isalpha(*pt) && !isdigit(*pt)) { 125 progerr(gettext(ERR_CLASSCHAR)); 126 exit(1); 127 } 128 } 129 break; 130 131 case 'i': /* follow symlinks */ 132 iflag++; 133 break; 134 135 default: 136 usage(); 137 } 138 } 139 140 if (iflag) { 141 /* follow symlinks */ 142 set_nonABI_symlinks(); 143 } else { 144 /* bug id 4244631, not ABI compliant */ 145 abi_sym_ptr = getenv("PKG_NONABI_SYMLINKS"); 146 if (abi_sym_ptr && strncasecmp(abi_sym_ptr, "TRUE", 4) == 0) { 147 set_nonABI_symlinks(); 148 } 149 } 150 holdcinfo = !xflag; 151 if (optind == argc) { 152 /* take path list from stdin */ 153 while (fgets(path, sizeof (path), stdin) != (char *)NULL) { 154 output(path, 0, NULL); 155 } 156 } else { 157 while (optind < argc) { 158 follow(argv[optind++]); 159 } 160 } 161 162 return (errflg ? 1 : 0); 163 } 164 165 static void 166 output(char *path, int n, char *local) 167 { 168 char mypath[PATH_MAX]; 169 int len; 170 int s; 171 struct cfent entry; 172 173 /* 174 * remove any trailing newline characters from the end of path 175 */ 176 177 len = strlen(path); 178 while ((len > 0) && (path[len-1] == '\n')) { 179 path[--len] = '\0'; 180 } 181 182 entry.volno = 0; 183 entry.ftype = '?'; 184 entry.path = mypath; 185 (void) strlcpy(entry.pkg_class, def_class, sizeof (entry.pkg_class)); 186 (void) strlcpy(entry.path, path, PATH_MAX); 187 entry.ainfo.local = NULL; 188 entry.ainfo.mode = BADMODE; 189 (void) strlcpy(entry.ainfo.owner, BADOWNER, sizeof (entry.ainfo.owner)); 190 (void) strlcpy(entry.ainfo.group, BADGROUP, sizeof (entry.ainfo.group)); 191 errflg = 0; 192 193 if (xflag) { 194 entry.ftype = '?'; 195 if (cverify(0, &entry.ftype, path, &entry.cinfo, 1)) { 196 errflg++; 197 logerr(gettext("ERROR: %s"), path); 198 logerr(getErrbufAddr()); 199 return; 200 } 201 } 202 203 /* 204 * Use averify to figure out the attributes. This has trouble 205 * divining the identity of a symlink which points to a 206 * non-existant target. For that reason, if it comes back as 207 * an existence problem, we fake in a symlink and see if averify 208 * likes that. If it does, all we have is a risky symlink. 209 */ 210 if ((s = averify(0, &entry.ftype, path, &entry.ainfo)) == VE_EXIST && 211 !iflag) { 212 entry.ftype = 's'; /* try again assuming symlink */ 213 /* try to read what it points to */ 214 if ((s = readlink(path, mylocal, PATH_MAX)) > 0) { 215 mylocal[s] = '\000'; /* terminate it */ 216 entry.ainfo.local = mylocal; 217 if (averify(0, &entry.ftype, path, &entry.ainfo)) { 218 errflg++; 219 } else 220 /* It's a link to a file not in this package. */ 221 ptext(stderr, gettext(WRN_SCARYLINK), 222 mylocal, path); 223 } else { 224 errflg++; 225 } 226 } else if (s != 0 && s != VE_CONT) 227 errflg++; 228 229 if (errflg) { 230 logerr(gettext("ERROR: %s"), path); 231 logerr(getErrbufAddr()); 232 return; 233 } 234 235 if (n) { 236 /* replace first n characters with 'local' */ 237 if (strchr("fev", entry.ftype)) { 238 entry.ainfo.local = mylocal; 239 (void) strlcpy(entry.ainfo.local, entry.path, 240 PATH_MAX); 241 canonize(entry.ainfo.local); 242 } 243 if (local[0]) { 244 entry.ainfo.local = mylocal; 245 (void) strlcpy(entry.path, local, PATH_MAX); 246 (void) strcat(entry.path, path+n); 247 } else 248 (void) strlcpy(entry.path, 249 (path[n] == '/') ? path+n+1 : path+n, 250 PATH_MAX); 251 } 252 253 canonize(entry.path); 254 if (entry.path[0]) { 255 findlink(&entry, path, entry.path); 256 if (strchr("dcbp", entry.ftype) || 257 (nflag && !strchr("sl", entry.ftype))) 258 entry.ainfo.local = NULL; 259 if (ppkgmap(&entry, stdout)) { 260 progerr(gettext(ERR_WRITE)); 261 exit(99); 262 } 263 } 264 } 265 266 static void 267 follow(char *path) 268 { 269 struct stat stbuf; 270 FILE *pp; 271 char *pt, 272 local[PATH_MAX], 273 newpath[PATH_MAX], 274 cmd[PATH_MAX+32]; 275 int n; 276 277 errflg = 0; 278 279 if (pt = strchr(path, '=')) { 280 *pt++ = '\0'; 281 n = ((unsigned int)pt - (unsigned int)path - 1); 282 if (n >= PATH_MAX) { 283 progerr(gettext(ERR_PATHLONG)); 284 errflg++; 285 return; 286 } 287 288 n = strlen(pt); 289 290 if (n < PATH_MAX) { 291 (void) strlcpy(local, pt, sizeof (local)); 292 n = strlen(path); 293 } else { 294 progerr(gettext(ERR_PATHLONG)); 295 errflg++; 296 return; 297 } 298 } else { 299 n = 0; 300 local[0] = '\0'; 301 } 302 303 if (stat(path, &stbuf)) { 304 progerr(gettext(ERR_STAT), path); 305 errflg++; 306 return; 307 } 308 309 if (stbuf.st_mode & S_IFDIR) { 310 (void) snprintf(cmd, sizeof (cmd), "find %s -print", path); 311 if ((pp = popen(cmd, "r")) == NULL) { 312 progerr(gettext(ERR_POPEN), cmd); 313 exit(1); 314 } 315 while (fscanf(pp, "%[^\n]\n", newpath) == 1) 316 output(newpath, n, local); 317 if (pclose(pp)) { 318 progerr(gettext(ERR_PCLOSE), cmd); 319 errflg++; 320 } 321 } else 322 output(path, n, local); 323 } 324 325 /* 326 * Scan a raw link for origination errors. Given 327 * targ_name = hlink/path/file1 328 * and 329 * link_name = hlink/path/file2 330 * we don't want the link to be verbatim since link_name must be relative 331 * to it's source. This functions checks for identical directory paths 332 * and if it's clearly a misplaced relative path, the duplicate 333 * directories are stripped. This is necessary because pkgadd is actually 334 * in the source directory (hlink/path) when it creates the link. 335 * 336 * NOTE : The buffer we get with targ_name is going to be used later 337 * and cannot be modified. That's why we have yet another PATH_MAX 338 * size buffer in this function. 339 */ 340 static char * 341 scan_raw_ln(char *targ_name, char *link_name) 342 { 343 char *const_ptr; /* what we return */ 344 char *file_name; /* name of the file in link_name */ 345 char *this_dir; /* current directory in targ_name */ 346 char *next_dir; /* next directory in targ_name */ 347 char *targ_ptr; /* current character in targ_name */ 348 349 const_ptr = targ_name; /* Point to here 'til we know it's different. */ 350 351 /* 352 * If the link is absolute or it is in the current directory, no 353 * further testing necessary. 354 */ 355 if (RELATIVE(targ_name) && 356 (file_name = strrchr(link_name, '/')) != NULL) { 357 358 /* 359 * This will be walked down to the highest directory 360 * not common to both the link and the target. 361 */ 362 targ_ptr = targ_name; 363 364 /* 365 * At this point targ_name is a relative path through at 366 * least one directory. 367 */ 368 this_dir = targ_ptr; /* first directory in targ_name */ 369 file_name++; /* point to the name not the '/' */ 370 371 /* 372 * Scan across the pathname until we reach a different 373 * directory or the final file name. 374 */ 375 do { 376 size_t str_size; 377 378 next_dir = strchr(targ_ptr, '/'); 379 if (next_dir) 380 next_dir++; /* point to name not '/' */ 381 else /* point to the end of the string */ 382 next_dir = targ_ptr+strlen(targ_ptr); 383 384 /* length to compare */ 385 str_size = ((ptrdiff_t)next_dir - (ptrdiff_t)this_dir); 386 387 /* 388 * If both paths begin with the same directory, then 389 * skip that common directory in both the link and 390 * the target. 391 */ 392 if (strncmp(this_dir, link_name, str_size) == 0) { 393 /* point to the target so far */ 394 const_ptr = this_dir = next_dir; 395 /* Skip past it in the target */ 396 targ_ptr = (char *)(targ_ptr+str_size); 397 /* Skip past it in the link */ 398 link_name = (char *)(link_name+str_size); 399 /* 400 * If these directories don't match then the 401 * directory above is the lowest common directory. We 402 * need to construct a relative path from the lowest 403 * child up to that directory. 404 */ 405 } else { 406 int d = 0; 407 char *dptr = link_name; 408 409 /* Count the intermediate directories. */ 410 while ((dptr = strchr(dptr, '/')) != NULL) { 411 dptr++; 412 d++; 413 } 414 /* 415 * Now targ_ptr is pointing to the fork in 416 * the path and dptr is pointing to the lowest 417 * child in the link. We now insert the 418 * appropriate number of "../'s" to get to 419 * the first common directory. We'll 420 * construct this in the construction 421 * buffer. 422 */ 423 if (d) { 424 char *tptr; 425 426 const_ptr = tptr = construction; 427 while (d--) { 428 (void) strlcpy(tptr, 429 "../", PATH_MAX); 430 tptr += 3; 431 } 432 (void) strlcpy(tptr, targ_ptr, 433 PATH_MAX); 434 } 435 break; /* done */ 436 } 437 } while (link_name != file_name); /* at file name */ 438 } 439 440 return (const_ptr); 441 } 442 443 static void 444 findlink(struct cfent *ept, char *path, char *svpath) 445 { 446 struct stat statbuf; 447 struct link *link, *new; 448 char buf[PATH_MAX]; 449 int n; 450 451 if (lstat(path, &statbuf)) { 452 progerr(gettext(ERR_STAT), path); 453 errflg++; 454 } 455 if ((statbuf.st_mode & S_IFMT) == S_IFLNK) { 456 if (!iflag) { 457 ept->ainfo.local = mylocal; 458 ept->ftype = 's'; 459 n = readlink(path, buf, PATH_MAX); 460 if (n <= 0) { 461 progerr(gettext(ERR_RDLINK), path); 462 errflg++; 463 (void) strlcpy(ept->ainfo.local, 464 "unknown", PATH_MAX); 465 } else { 466 (void) strncpy(ept->ainfo.local, buf, n); 467 ept->ainfo.local[n] = '\0'; 468 } 469 } 470 return; 471 } 472 473 if (stat(path, &statbuf)) 474 return; 475 if (statbuf.st_nlink <= 1) 476 return; 477 478 for (link = firstlink; link; link = link->next) { 479 if ((statbuf.st_ino == link->ino) && 480 (statbuf.st_dev == link->dev)) { 481 ept->ftype = 'l'; 482 ept->ainfo.local = mylocal; 483 (void) strlcpy(ept->ainfo.local, 484 scan_raw_ln(link->path, ept->path), 485 PATH_MAX); 486 return; 487 } 488 } 489 if ((new = (struct link *)calloc(1, sizeof (struct link))) == NULL) { 490 progerr(gettext(ERR_MEMORY), errno); 491 exit(1); 492 } 493 494 if (firstlink) { 495 lastlink->next = new; 496 lastlink = new; 497 } else 498 firstlink = lastlink = new; 499 500 new->path = strdup(svpath); 501 new->ino = statbuf.st_ino; 502 new->dev = statbuf.st_dev; 503 } 504 505 static void 506 usage(void) 507 { 508 (void) fprintf(stderr, 509 gettext("usage: %s [-i] [-c class] [path ...]\n"), get_prog_name()); 510 exit(1); 511 /*NOTREACHED*/ 512 } 513