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.
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.
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]
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
38 #include <sys/types.h>
50 #define WRN_SCARYLINK "WARNING: <%s>, target of symlink <%s>, does not exist."
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"
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
);
75 static char *def_class
= "none";
77 static int errflg
= 0;
78 static int iflag
= 0; /* follow symlinks */
79 static int xflag
= 0; /* confirm contents of files */
81 static char construction
[PATH_MAX
], mylocal
[PATH_MAX
];
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);
89 main(int argc
, char *argv
[])
92 char *pt
, path
[PATH_MAX
];
97 (void) setlocale(LC_ALL
, "");
99 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
100 #define TEXT_DOMAIN "SYS_TEST"
102 (void) textdomain(TEXT_DOMAIN
);
104 (void) set_prog_name(argv
[0]);
106 while ((c
= getopt(argc
, argv
, "xnic:?")) != EOF
) {
108 case 'x': /* include content info */
116 case 'c': /* assign class */
118 /* validate that classname is acceptable */
119 if (strlen(def_class
) > (size_t)CLSSIZ
) {
120 progerr(gettext(ERR_CLASSLONG
));
123 for (pt
= def_class
; *pt
; pt
++) {
124 if (!isalpha(*pt
) && !isdigit(*pt
)) {
125 progerr(gettext(ERR_CLASSCHAR
));
131 case 'i': /* follow symlinks */
141 /* follow symlinks */
142 set_nonABI_symlinks();
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();
151 if (optind
== argc
) {
152 /* take path list from stdin */
153 while (fgets(path
, sizeof (path
), stdin
) != (char *)NULL
) {
154 output(path
, 0, NULL
);
157 while (optind
< argc
) {
158 follow(argv
[optind
++]);
162 return (errflg
? 1 : 0);
166 output(char *path
, int n
, char *local
)
168 char mypath
[PATH_MAX
];
174 * remove any trailing newline characters from the end of path
178 while ((len
> 0) && (path
[len
-1] == '\n')) {
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
));
195 if (cverify(0, &entry
.ftype
, path
, &entry
.cinfo
, 1)) {
197 logerr(gettext("ERROR: %s"), path
);
198 logerr(getErrbufAddr());
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.
210 if ((s
= averify(0, &entry
.ftype
, path
, &entry
.ainfo
)) == VE_EXIST
&&
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
)) {
220 /* It's a link to a file not in this package. */
221 ptext(stderr
, gettext(WRN_SCARYLINK
),
226 } else if (s
!= 0 && s
!= VE_CONT
)
230 logerr(gettext("ERROR: %s"), path
);
231 logerr(getErrbufAddr());
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
,
241 canonize(entry
.ainfo
.local
);
244 entry
.ainfo
.local
= mylocal
;
245 (void) strlcpy(entry
.path
, local
, PATH_MAX
);
246 (void) strcat(entry
.path
, path
+n
);
248 (void) strlcpy(entry
.path
,
249 (path
[n
] == '/') ? path
+n
+1 : path
+n
,
253 canonize(entry
.path
);
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
));
279 if (pt
= strchr(path
, '=')) {
281 n
= ((unsigned int)pt
- (unsigned int)path
- 1);
283 progerr(gettext(ERR_PATHLONG
));
291 (void) strlcpy(local
, pt
, sizeof (local
));
294 progerr(gettext(ERR_PATHLONG
));
303 if (stat(path
, &stbuf
)) {
304 progerr(gettext(ERR_STAT
), path
);
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
);
315 while (fscanf(pp
, "%[^\n]\n", newpath
) == 1)
316 output(newpath
, n
, local
);
318 progerr(gettext(ERR_PCLOSE
), cmd
);
322 output(path
, n
, local
);
326 * Scan a raw link for origination errors. Given
327 * targ_name = hlink/path/file1
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.
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.
341 scan_raw_ln(char *targ_name
, char *link_name
)
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 */
349 const_ptr
= targ_name
; /* Point to here 'til we know it's different. */
352 * If the link is absolute or it is in the current directory, no
353 * further testing necessary.
355 if (RELATIVE(targ_name
) &&
356 (file_name
= strrchr(link_name
, '/')) != NULL
) {
359 * This will be walked down to the highest directory
360 * not common to both the link and the target.
362 targ_ptr
= targ_name
;
365 * At this point targ_name is a relative path through at
366 * least one directory.
368 this_dir
= targ_ptr
; /* first directory in targ_name */
369 file_name
++; /* point to the name not the '/' */
372 * Scan across the pathname until we reach a different
373 * directory or the final file name.
378 next_dir
= strchr(targ_ptr
, '/');
380 next_dir
++; /* point to name not '/' */
381 else /* point to the end of the string */
382 next_dir
= targ_ptr
+strlen(targ_ptr
);
384 /* length to compare */
385 str_size
= ((ptrdiff_t)next_dir
- (ptrdiff_t)this_dir
);
388 * If both paths begin with the same directory, then
389 * skip that common directory in both the link and
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
);
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.
407 char *dptr
= link_name
;
409 /* Count the intermediate directories. */
410 while ((dptr
= strchr(dptr
, '/')) != NULL
) {
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
426 const_ptr
= tptr
= construction
;
432 (void) strlcpy(tptr
, targ_ptr
,
437 } while (link_name
!= file_name
); /* at file name */
444 findlink(struct cfent
*ept
, char *path
, char *svpath
)
447 struct link
*link
, *new;
451 if (lstat(path
, &statbuf
)) {
452 progerr(gettext(ERR_STAT
), path
);
455 if ((statbuf
.st_mode
& S_IFMT
) == S_IFLNK
) {
457 ept
->ainfo
.local
= mylocal
;
459 n
= readlink(path
, buf
, PATH_MAX
);
461 progerr(gettext(ERR_RDLINK
), path
);
463 (void) strlcpy(ept
->ainfo
.local
,
464 "unknown", PATH_MAX
);
466 (void) strncpy(ept
->ainfo
.local
, buf
, n
);
467 ept
->ainfo
.local
[n
] = '\0';
473 if (stat(path
, &statbuf
))
475 if (statbuf
.st_nlink
<= 1)
478 for (link
= firstlink
; link
; link
= link
->next
) {
479 if ((statbuf
.st_ino
== link
->ino
) &&
480 (statbuf
.st_dev
== link
->dev
)) {
482 ept
->ainfo
.local
= mylocal
;
483 (void) strlcpy(ept
->ainfo
.local
,
484 scan_raw_ln(link
->path
, ept
->path
),
489 if ((new = (struct link
*)calloc(1, sizeof (struct link
))) == NULL
) {
490 progerr(gettext(ERR_MEMORY
), errno
);
495 lastlink
->next
= new;
498 firstlink
= lastlink
= new;
500 new->path
= strdup(svpath
);
501 new->ino
= statbuf
.st_ino
;
502 new->dev
= statbuf
.st_dev
;
508 (void) fprintf(stderr
,
509 gettext("usage: %s [-i] [-c class] [path ...]\n"), get_prog_name());