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
main(int argc,char * argv[])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
output(char * path,int n,char * local)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
follow(char * path)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 *
scan_raw_ln(char * targ_name,char * link_name)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
findlink(struct cfent * ept,char * path,char * svpath)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
usage(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