xref: /illumos-gate/usr/src/cmd/svr4pkg/pkgproto/main.c (revision abb88ab1b9516b1ca12094db7f2cfb5d91e0a135)
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