inspect.c revision c13de8f6a88563211bd4432ca11ca38ed3bf0fc0
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Routines to add file and directory entries into the internal configuration
* information. This information is maintained in a number of hash tables which
* after completion of input file processing will be processed and written to
* the output configuration file.
*
* Each hash table is defined via a Hash_tbl structure. These are organized:
*
* c_strtbl contains a hash entry for every file, directory, pathname and
* alternative path (dldump(3dl) image) processed.
* c_strsize and c_objnum maintain the size and count of the
* strings added to this table and are used to size the output
* configuration file.
*
* c_inotbls contains a list of inode hash tables. Each element of the list
* identifies a unique device. Thus, for each file processed its
* st_dev and st_ino are used to assign its entry to the correct
* hash table.
*
* Each directory processed is assigned a unique id (c_dirnum)
* which insures each file also becomes uniquely identified.
*
* All file and directory additions come through the inspect() entry point.
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#include <_libelf.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>
#include "machdep.h"
#include "sgs.h"
#include "rtc.h"
#include "_crle.h"
#include "msg.h"
/*
* Add an alternative pathname for an object. Although a configuration file
* may contain several pathnames that resolve to the same real file, there can
* only be one real file. Consequently, there can only be one alternative.
* For multiple pathnames that resolve to the same real file, multiple alter-
* natives may be specified. Always take the alternative for the real file
* over any others.
*/
static int
enteralt(Crle_desc * crle, const char *path, const char *file, Half flags,
Hash_obj * obj)
{
const char *fmt;
char alter[PATH_MAX];
size_t altsz;
if (obj->o_alter) {
/*
* If an alternative has already been captured, only override
* it if the specified file is the real file.
*/
if (strcmp(path, obj->o_path))
return (1);
}
/*
* Create an alternative pathname from the file and object destination
* directory. If we're dumping an alternative don't allow it to
* override the original.
*/
if (flags & RTC_OBJ_DUMP) {
char _alter[PATH_MAX];
(void) strcpy(_alter, crle->c_objdir);
(void) realpath(_alter, _alter);
(void) snprintf(alter, PATH_MAX, MSG_ORIG(MSG_FMT_PATH),
_alter, file);
if (strcmp(alter, obj->o_path) == 0) {
(void) printf(MSG_INTL(MSG_ARG_ALT), crle->c_name,
obj->o_path);
return (0);
}
obj->o_flags |= RTC_OBJ_DUMP;
} else
(void) snprintf(alter, PATH_MAX, MSG_ORIG(MSG_FMT_PATH),
crle->c_objdir, file);
obj->o_flags |= RTC_OBJ_ALTER;
/*
* If we're overriding an existing alternative with the real path, free
* up any previous alternative.
*/
if (obj->o_alter) {
crle->c_strsize -= strlen(alter) + 1;
fmt = MSG_INTL(MSG_DIA_ALTUPDATE);
} else
fmt = MSG_INTL(MSG_DIA_ALTCREATE);
/*
* Allocate the new alternative and update the string table size.
*/
altsz = strlen(alter) + 1;
if ((obj->o_alter = malloc(altsz)) == 0)
return (0);
(void) strcpy(obj->o_alter, alter);
crle->c_strsize += altsz;
if (crle->c_flags & CRLE_VERBOSE)
(void) printf(fmt, alter, obj->o_path);
return (1);
}
/*
* Establish an inode hash entry, this is unique for each dev hash table, and
* establishes the unique object descriptor.
*/
static Hash_ent *
enterino(Crle_desc * crle, const char *name, struct stat *status, Half flags)
{
Hash_ent * ent;
Hash_obj * obj;
Hash_tbl * tbl;
Listnode * lnp = 0;
Addr ino = (Addr)status->st_ino;
ulong_t dev = status->st_dev;
Lword info;
/*
* For configuration file verification we retain information about the
* file or directory.
*/
if (flags & RTC_OBJ_DIRENT)
info = (Lword)status->st_mtime;
else
info = (Lword)status->st_size;
/*
* Determine the objects device number and establish a hash table for
* for this devices inodes.
*/
for (LIST_TRAVERSE(&crle->c_inotbls, lnp, tbl)) {
if (tbl->t_ident == dev)
break;
}
if (lnp == 0) {
if ((tbl = make_hash(crle->c_inobkts, HASH_INT, dev)) == 0)
return (0);
if (list_append(&crle->c_inotbls, tbl) == 0)
return (0);
}
/*
* Reuse or add this new object to the inode hash table.
*/
if ((ent = get_hash(tbl, ino, 0, (HASH_FND_ENT | HASH_ADD_ENT))) == 0)
return (0);
/*
* If an object descriptor doesn't yet exist create one.
*/
if ((obj = ent->e_obj) == 0) {
if ((obj = calloc(sizeof (Hash_obj), 1)) == 0)
return (0);
obj->o_tbl = tbl;
obj->o_flags = flags;
obj->o_info = info;
/*
* Reallocate the objects name, as it might have been composed
* and passed to us on the stack.
*/
if ((obj->o_path = strdup(name)) == 0)
return (0);
/*
* Assign this object to the original ino hash entry.
*/
ent->e_obj = obj;
}
return (ent);
}
/*
* Basic directory entry, establishes entry information, updated global counts
* and provides any diagnostics.
*/
static int
_enterdir(Crle_desc * crle, const char *dir, Hash_ent * ent, Hash_obj * obj)
{
size_t size = strlen(dir) + 1;
char *ndir;
/*
* Establish this hash entries key (which is the directory name itself),
* assign the next available directory number, and its object.
*/
if ((ndir = malloc(size)) == 0)
return (0);
(void) strcpy(ndir, dir);
ent->e_key = (Addr)ndir;
ent->e_id = crle->c_dirnum++;
ent->e_obj = obj;
/*
* Update string table information. We add a dummy filename for each
* real directory so as to have a null terminated file table array for
* this directory.
*/
crle->c_strsize += size;
crle->c_hashstrnum++;
crle->c_filenum++;
/*
* Provide any diagnostics.
*/
if (crle->c_flags & CRLE_VERBOSE) {
const char *fmt;
if (obj->o_flags & RTC_OBJ_NOEXIST)
fmt = MSG_INTL(MSG_DIA_NOEXIST);
else
fmt = MSG_INTL(MSG_DIA_DIR);
(void) printf(fmt, ent->e_id, dir);
}
return (1);
}
/*
* Establish a string hash entry for a directory.
*/
static Hash_ent *
enterdir(Crle_desc * crle, const char *odir, Half flags, struct stat *status)
{
Hash_tbl * stbl = crle->c_strtbl;
Hash_ent * ent;
Hash_obj * obj;
char rdir[PATH_MAX], * ndir;
/*
* Establish the directories real name, this is the name that will be
* recorded in the object identifier.
*/
if (realpath(odir, rdir) == 0)
return (0);
if (strcmp(odir, rdir))
ndir = rdir;
else
ndir = (char *)odir;
/*
* If we're not dealing with an all-entries directory (i.e., we're
* recording this directory because of its explicitly specified
* filename) leave off any filename specific attributes.
*/
if ((flags & RTC_OBJ_ALLENTS) == 0)
flags &= ~(RTC_OBJ_ALTER | RTC_OBJ_DUMP | RTC_OBJ_GROUP);
flags |= RTC_OBJ_DIRENT;
/*
* Establish a inode table entry, and the objects unique descriptor.
*/
if ((ent = enterino(crle, ndir, status, flags)) == 0)
return (0);
obj = ent->e_obj;
/*
* Create a string table entry for the real directory.
*/
if ((ent = get_hash(stbl, (Addr)ndir, 0,
(HASH_FND_ENT | HASH_ADD_ENT))) == 0)
return (0);
/*
* If this is a new entry reassign the directory name and assign a
* unique directory id.
*/
if (ent->e_id == 0) {
if (_enterdir(crle, ndir, ent, obj) == 0)
return (0);
}
/*
* If the directory name supplied is different than the real name we've
* just entered, continue to create an entry for it.
*/
if (ndir == odir)
return (ent);
/*
* Create a string table entry for this real directory.
*/
if ((ent = get_hash(stbl, (Addr)odir, 0,
(HASH_FND_ENT | HASH_ADD_ENT))) == 0)
return (0);
/*
* If this is a new entry reassign the directory name and assign a
* unique directory id.
*/
if (ent->e_id == 0) {
if (_enterdir(crle, odir, ent, obj) == 0)
return (0);
}
return (ent);
}
/*
* Establish a non-existent directory entry. There is no inode entry created
* for this, just a directory and its associated object.
*/
static Hash_ent *
enternoexistdir(Crle_desc * crle, const char *dir)
{
Hash_ent * ent;
/*
* Reuse or add this new non-existent directory to the string table.
*/
if ((ent = get_hash(crle->c_strtbl, (Addr)dir, 0,
(HASH_FND_ENT | HASH_ADD_ENT))) == 0)
return (0);
/*
* If this is a new entry, assign both the object and the directory
* entry information.
*/
if (ent->e_id == 0) {
Hash_obj * obj;
if ((obj = calloc(sizeof (Hash_obj), 1)) == 0)
return (0);
obj->o_flags = (RTC_OBJ_NOEXIST | RTC_OBJ_DIRENT);
if (_enterdir(crle, dir, ent, obj) == 0)
return (0);
}
return (ent);
}
/*
* Basic file entry, establishes entry information, updated global counts
* and provides any diagnostics.
*/
static int
_enterfile(Crle_desc * crle, const char *file, int off, Hash_ent * fent,
Hash_ent * rent, Hash_ent * dent, Hash_obj * obj)
{
size_t size = strlen(file) + 1;
char *nfile;
/*
* If this is a full file name reallocate it, as it might have been
* composed and passed to us on the stack. Otherwise reuse the original
* directory name to satisfy the filename, here we record the offset of
* the file in the directory name so that we can reduce the string table
* in the final configuration file.
*/
if (off == 0) {
if ((nfile = malloc(size)) == 0)
return (0);
(void) strcpy(nfile, file);
} else
nfile = (char *)file;
fent->e_key = (Addr)nfile;
fent->e_off = off;
/*
* Assign directory and directory id, and any real (full) path
* association.
*/
fent->e_dir = dent;
fent->e_id = dent->e_id;
fent->e_path = rent;
/*
* Increment the file count for this directory.
*/
dent->e_cnt++;
/*
* Assign this object to the new string hash entry.
*/
fent->e_obj = obj;
/*
* Update string table information.
*/
crle->c_strsize += size;
crle->c_hashstrnum++;
crle->c_filenum++;
/*
* Provide any diagnostics.
*/
if (crle->c_flags & CRLE_VERBOSE)
(void) printf(MSG_INTL(MSG_DIA_FILE), fent->e_id, nfile);
return (1);
}
/*
* Establish a non-existent file entry. There is no inode entry created for
* this, just the files full and simple name, and its associated object.
*/
static Hash_ent *
enternoexistfile(Crle_desc * crle, const char *path, const char *file,
Hash_ent * dent)
{
Hash_ent * rent, * ent;
Hash_obj * obj;
int off;
/*
* Create a string table entry for the full filename.
*/
if ((rent = get_hash(crle->c_strtbl, (Addr)path, 0,
(HASH_FND_ENT | HASH_ADD_ENT))) == 0)
return (0);
/*
* If this is a new entry, assign both the object and the full filename
* entry information.
*/
if (rent->e_id == 0) {
if ((obj = calloc(sizeof (Hash_obj), 1)) == 0)
return (0);
obj->o_flags = RTC_OBJ_NOEXIST;
if (_enterfile(crle, path, 0, rent, 0, dent, obj) == 0)
return (0);
}
obj = rent->e_obj;
if ((obj->o_path = strdup(path)) == 0)
return (0);
/*
* Express the filename in terms of the full pathname. By reusing the
* name within the full filename we can reduce the overall string table
* size in the output configuration file.
*/
off = file - path;
file = (char *)rent->e_key + off;
/*
* Create a entry for the individual file within this directory.
*/
if ((ent = get_hash(crle->c_strtbl, (Addr)file, dent->e_id,
(HASH_FND_ENT | HASH_ADD_ENT))) == 0)
return (0);
if (ent->e_id == 0) {
if (_enterfile(crle, file, off, ent, rent, dent, obj) == 0)
return (0);
}
return (ent);
}
/*
* Establish a string hash entry for a file.
*/
static Hash_ent *
enterfile(Crle_desc * crle, const char *opath, const char *ofile, Half flags,
Hash_ent * odent, struct stat *status)
{
Hash_tbl * stbl = crle->c_strtbl;
Hash_ent * ent, * rent, * ndent = odent;
Hash_obj * obj;
size_t size;
char rpath[PATH_MAX], * npath, * nfile;
int off;
/*
* Establish the files real name, this is the name that will be
* recorded in the object identifier.
*/
if (realpath(opath, rpath) == 0)
return (0);
if (strcmp(opath, rpath)) {
npath = rpath;
if (nfile = strrchr(npath, '/'))
nfile++;
else
nfile = npath;
/*
* Determine if the real pathname has a different directory to
* the original passed to us.
*/
size = nfile - npath;
if (strncmp(opath, npath, size)) {
char _npath[PATH_MAX];
struct stat _status;
(void) strncpy(_npath, npath, size);
_npath[size - 1] = '\0';
(void) stat(_npath, &_status);
if ((ndent = enterdir(crle, _npath, flags,
&_status)) == 0)
return (0);
}
} else {
npath = (char *)opath;
nfile = (char *)ofile;
}
/*
* Establish an inode table entry, and the objects unique descriptor.
*/
if ((ent = enterino(crle, npath, status, flags)) == 0)
return (0);
obj = ent->e_obj;
/*
* Create a string table entry for the full filename.
*/
if ((rent = get_hash(stbl, (Addr)npath, 0,
(HASH_FND_ENT | HASH_ADD_ENT))) == 0)
return (0);
if (rent->e_id == 0) {
if (_enterfile(crle, npath, 0, rent, 0, ndent, obj) == 0)
return (0);
}
/*
* Identify this entry and its directory as real paths. If dldump(3dl)
* processing is required this flag is checked, as we only need to dump
* the real pathname. Many other objects may point to the same
* alternative, but only one needs to be dumped. In addition, during
* ld.so.1 validation, only this directory and file need be checked.
*/
rent->e_flags |= RTC_OBJ_REALPTH;
ndent->e_flags |= RTC_OBJ_REALPTH;
/*
* Express the filename in terms of the full pathname. By reusing the
* name within the full filename we can reduce the overall string table
* size in the output configuration file.
*/
off = nfile - npath;
nfile = (char *)rent->e_key + off;
/*
* Create a entry for the individual file within this directory.
*/
if ((ent = get_hash(stbl, (Addr)nfile, ndent->e_id,
(HASH_FND_ENT | HASH_ADD_ENT))) == 0)
return (0);
if (ent->e_id == 0) {
if (_enterfile(crle, nfile, off, ent, rent, ndent, obj) == 0)
return (0);
}
/*
* If the original path name is not equivalent to the real path name,
* then we had an alias (typically it's a symlink). Add the path name
* to the string hash table and reference the object data structure.
*/
if (nfile == ofile)
return (ent);
/*
* Establish an inode table entry, and the objects unique descriptor.
*/
if ((ent = enterino(crle, opath, status, 0)) == 0)
return (0);
obj = ent->e_obj;
/*
* Create a string table entry for the full filename.
*/
if ((rent = get_hash(stbl, (Addr)opath, 0,
(HASH_FND_ENT | HASH_ADD_ENT))) == 0)
return (0);
if (rent->e_id == 0) {
if (_enterfile(crle, opath, 0, rent, 0, odent, obj) == 0)
return (0);
}
/*
* Express the filename in terms of the full pathname. By reusing the
* name within the full filename we can reduce the overall string table
* size in the output configuration file.
*/
off = ofile - opath;
ofile = (char *)rent->e_key + off;
/*
* Create a entry for the individual file within this directory.
*/
if ((ent = get_hash(stbl, (Addr)ofile, odent->e_id,
(HASH_FND_ENT | HASH_ADD_ENT))) == 0)
return (0);
if (ent->e_id == 0) {
if (_enterfile(crle, ofile, off, ent, rent, odent, obj) == 0)
return (0);
}
return (ent);
}
/*
* Add a file to configuration information.
*/
static int
inspect_file(Crle_desc * crle, const char *path, const char *file, Half flags,
Hash_ent * dent, struct stat *status, int error)
{
Hash_ent * ent;
Hash_obj * obj;
int fd;
Elf * elf;
GElf_Ehdr ehdr;
GElf_Xword dyflags = 0;
Listnode * lnp;
Hash_tbl * tbl;
Addr ino = (Addr)status->st_ino;
/*
* Determine whether this file (inode) has already been processed.
*/
for (LIST_TRAVERSE(&crle->c_inotbls, lnp, tbl)) {
if (tbl->t_ident != status->st_dev)
continue;
if ((ent = get_hash(tbl, ino, 0, HASH_FND_ENT)) == 0)
break;
/*
* This files inode object does exist, make sure it has a file
* entry for this directory.
*/
if ((ent = enterfile(crle, path, file, flags, dent,
status)) == 0)
return (error);
obj = ent->e_obj;
/*
* If an alternative has been asked for, and one has not yet
* been established, create one.
*/
if ((flags & RTC_OBJ_ALTER) &&
((obj->o_flags & RTC_OBJ_NOALTER) == 0)) {
if (enteralt(crle, path, file, flags, obj) == 0)
return (error);
}
return (0);
}
/*
* This is a new file, determine if it's a valid ELF file.
*/
if ((fd = open(path, O_RDONLY, 0)) == -1) {
if (error) {
int err = errno;
(void) fprintf(stderr, MSG_INTL(MSG_SYS_OPEN),
crle->c_name, path, strerror(err));
}
return (error);
}
/*
* Obtain an ELF descriptor and determine if we have a shared object.
*/
if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
if (error)
(void) fprintf(stderr, MSG_INTL(MSG_ELF_BEGIN),
crle->c_name, path, elf_errmsg(-1));
(void) close(fd);
return (error);
}
if ((elf_kind(elf) != ELF_K_ELF) ||
(gelf_getehdr(elf, &ehdr) == NULL) ||
(!((ehdr.e_type == ET_EXEC) || (ehdr.e_type == ET_DYN))) ||
(!((ehdr.e_ident[EI_CLASS] == M_CLASS) ||
(ehdr.e_machine == M_MACH)))) {
if (error)
(void) fprintf(stderr, MSG_INTL(MSG_ELF_TYPE),
crle->c_name, path);
(void) close(fd);
(void) elf_end(elf);
return (error);
}
(void) close(fd);
/*
* If we're generating alternative objects find this objects DT_FLAGS
* to insure it isn't marked as non-dumpable (libdl.so.1 falls into
* this category).
*/
if (flags & RTC_OBJ_DUMP)
dyflags = _gelf_getdyndtflags_1(elf);
/*
* Dynamic executables can be examined to determine their dependencies,
* dldump(3dl) their dependencies, and may even be dldump(3dl)'ed
* themselves.
*
* If we come across an executable while searching a directory
* (error == 0) it is ignored.
*/
if (ehdr.e_type == ET_EXEC) {
if (error == 0) {
(void) elf_end(elf);
return (0);
}
/*
* If we're not dumping the application itself, or we've not
* asked to gather its dependencies then its rather useless.
*/
if ((flags & (RTC_OBJ_GROUP | RTC_OBJ_DUMP)) == 0) {
(void) fprintf(stderr, MSG_INTL(MSG_GEN_INVFILE),
crle->c_name, path);
(void) elf_end(elf);
return (error);
}
/*
* If we're dumping the application under RTLD_REL_EXEC then the
* configuration file becomes specific to this application, so
* make sure we haven't been here before.
*/
if (crle->c_app && (flags & RTC_OBJ_DUMP) &&
(crle->c_dlflags & RTLD_REL_EXEC)) {
(void) fprintf(stderr, MSG_INTL(MSG_ARG_MODE),
crle->c_name, crle->c_app, path);
(void) elf_end(elf);
return (error);
}
}
/*
* Enter the file in the string hash table.
*/
if ((ent = enterfile(crle, path, file, flags, dent, status)) == 0) {
(void) elf_end(elf);
return (error);
}
obj = ent->e_obj;
if (flags & RTC_OBJ_ALTER) {
/*
* If this object is marked as non-dumpable make sure we don't
* create a dldump(3dl) alternative. A user requested
* alternative is acceptable.
*/
if ((flags & RTC_OBJ_DUMP) && (dyflags & DF_1_NODUMP)) {
obj->o_flags |= RTC_OBJ_NOALTER;
obj->o_flags &= ~(RTC_OBJ_ALTER | RTC_OBJ_DUMP);
} else {
if (enteralt(crle, path, file, flags, obj) == 0) {
(void) elf_end(elf);
return (error);
}
}
}
/*
* Executables are recorded in the configuration file either to allow
* for the configuration files update, or may indicate that the
* configuration file is specific to their use.
*/
if (ehdr.e_type == ET_EXEC) {
obj->o_flags |= RTC_OBJ_EXEC;
if ((flags & RTC_OBJ_DUMP) &&
(crle->c_dlflags & RTLD_REL_EXEC)) {
/*
* Get the reallocated pathname rather than using the
* original (the original might be from an existing
* configuration file being updated, in which case the
* pointer will be unmapped before we get to use it).
*/
ent = get_hash(crle->c_strtbl, (Addr)path, 0,
HASH_FND_ENT);
obj->o_flags |= RTC_OBJ_APP;
crle->c_app = (char *)ent->e_key;
}
}
/*
* If we've been asked to process this object as a group determine its
* dependencies.
*/
if (flags & RTC_OBJ_GROUP) {
if (depend(crle, path, flags, &ehdr)) {
(void) elf_end(elf);
return (error);
}
}
(void) elf_end(elf);
return (0);
}
/*
* Add a directory to configuration information.
*/
static int
inspect_dir(Crle_desc * crle, const char *name, Half flags, struct stat *status)
{
Hash_tbl * stbl = crle->c_strtbl;
DIR * dir;
struct dirent *dirent;
Hash_ent * ent;
int error = 0;
struct stat _status;
char path[PATH_MAX], * dst;
const char *src;
/*
* Determine whether we've already visited this directory to process
* all its entries.
*/
if ((ent = get_hash(stbl, (Addr)name, 0, HASH_FND_ENT)) != 0) {
if (ent->e_obj->o_flags & RTC_OBJ_ALLENTS)
return (0);
} else {
/*
* Create a directory hash entry.
*/
if ((ent = enterdir(crle, name, (flags | RTC_OBJ_ALLENTS),
status)) == 0)
return (1);
}
ent->e_obj->o_flags |= RTC_OBJ_ALLENTS;
/*
* Establish the pathname buffer.
*/
for (dst = path, dst--, src = name; *src; src++)
*++dst = *src;
if (*dst++ != '/')
*dst++ = '/';
/*
* Access the directory in preparation for reading its entries.
*/
if ((dir = opendir(name)) == 0)
return (1);
/*
* Read each entry from the directory looking for ELF files.
*/
while ((dirent = readdir(dir)) != NULL) {
const char *file = dirent->d_name;
char *_dst;
/*
* Ignore "." and ".." entries.
*/
if ((file[0] == '.') && ((file[1] == '\0') ||
((file[1] == '.') && (file[2] == '\0'))))
continue;
/*
* Complete full pathname, and reassign file to the new path.
*/
for (_dst = dst, src = file, file = dst; *src; _dst++, src++)
*_dst = *src;
*_dst = '\0';
if (stat(path, &_status) == -1)
continue;
if ((_status.st_mode & S_IFMT) != S_IFREG)
continue;
if (inspect_file(crle, path, file, flags, ent, &_status, 0)) {
error = 1;
break;
}
}
return (error);
}
/*
* Inspect a file/dir name. A stat(name) results in the following actions:
*
* The name doesn't exist:
* The name is assummed to be a non-existent directory and a directory
* cache entry is created to indicate this.
*
* The name is a directory:
* The directory is searched for appropriate files.
*
* The name is a file:
* The file is processed and added to the cache if appropriate.
*/
int
inspect(Crle_desc * crle, const char *name, Half flags)
{
Hash_ent * ent;
const char *file, * dir;
struct stat status;
char _name[PATH_MAX], _dir[PATH_MAX];
Half nflags = flags & ~RTC_OBJ_CMDLINE;
int noexist;
/*
* If this is the first time through here establish a string table
* cache.
*/
if (crle->c_dirnum == 0) {
if ((crle->c_strtbl = make_hash(crle->c_strbkts,
HASH_STR, 0)) == 0)
return (1);
crle->c_dirnum = 1;
}
if (crle->c_flags & CRLE_VERBOSE)
(void) printf(MSG_INTL(MSG_DIA_INSPECT), name);
/*
* Determine whether the name exists.
*/
if ((noexist = stat(name, &status)) != 0) {
if (errno != ENOENT) {
int err = errno;
(void) fprintf(stderr, MSG_INTL(MSG_SYS_STAT),
crle->c_name, name, strerror(err));
return (1);
} else {
/*
* If we've been asked to create an alternative object
* assume the object is a file and create a valid
* alternative entry. This allows the creation of
* alternatives for files that might not yet be
* installed.
*
* Otherwise we have no idea whether the name specified
* is a file or directory, so we assume a directory and
* establish an object descriptor to mark this as
* non-existent. This allows us to mark things like
* platform specific directories as non-existent.
*/
if ((flags & (RTC_OBJ_DUMP | RTC_OBJ_ALTER)) !=
RTC_OBJ_ALTER) {
if ((ent = enternoexistdir(crle, name)) == 0)
return (1);
ent->e_flags |= flags;
return (0);
}
}
}
/*
* Determine whether we're dealing with a directory or a file.
*/
if ((noexist == 0) && ((status.st_mode & S_IFMT) == S_IFDIR)) {
/*
* Process the directory name to collect its shared objects into
* the configuration file.
*/
if (inspect_dir(crle, name, nflags, &status))
return (1);
ent = get_hash(crle->c_strtbl, (Addr)name, 0, HASH_FND_ENT);
ent->e_flags |= flags;
return (0);
}
/*
* If this isn't a regular file we might as well bail now. Note that
* even if it is, we might still reject the file if it's not ELF later
* in inspect_file().
*/
if ((noexist == 0) && ((status.st_mode & S_IFMT) != S_IFREG)) {
(void) fprintf(stderr, MSG_INTL(MSG_GEN_INVFILE), crle->c_name,
name);
return (1);
}
/*
* Break the pathname into directory and filename components.
*/
if ((file = strrchr(name, '/')) == 0) {
dir = MSG_ORIG(MSG_DIR_DOT);
(void) strcpy(_name, MSG_ORIG(MSG_PTH_DOT));
(void) strcpy(&_name[MSG_PTH_DOT_SIZE], name);
name = (const char *)_name;
file = (const char *)&_name[MSG_PTH_DOT_SIZE];
} else {
size_t off = file - name;
if (file == name)
dir = MSG_ORIG(MSG_DIR_ROOT);
else {
(void) strncpy(_dir, name, off);
_dir[off] = '\0';
dir = (const char *)_dir;
}
file++;
}
/*
* Determine whether we've already visited this directory and if not
* create it.
*/
if ((ent = get_hash(crle->c_strtbl, (Addr)dir, 0, HASH_FND_ENT)) == 0) {
struct stat _status;
if (stat(dir, &_status) != 0) {
if (errno != ENOENT) {
int err = errno;
(void) fprintf(stderr, MSG_INTL(MSG_SYS_STAT),
crle->c_name, name, strerror(err));
return (1);
} else {
/*
* Note that this directory will be tagged as
* having an alternative - not that the
* directory does, but supposedly it contains
* a file that does.
*/
if ((ent = enternoexistdir(crle, dir)) == 0)
return (1);
ent->e_flags |= nflags;
}
} else {
if ((ent = enterdir(crle, dir, nflags, &_status)) == 0)
return (1);
}
}
/*
* Regardless of whether we've already processed this file (say from
* an RTC_OBJ_ALLENTS which we could determine from the above), continue
* to inspect the file. It may require alternatives or something that
* hadn't be specified from the directory entry.
*/
if (noexist) {
if ((ent = enternoexistfile(crle, name, file, ent)) == 0)
return (1);
ent->e_flags |= nflags;
if (enteralt(crle, name, file, flags, ent->e_obj) == 0)
return (1);
} else {
if (inspect_file(crle, name, file, nflags, ent, &status, 1))
return (1);
}
/*
* Make sure to propagate any RTC_OBJ_CMDLINE flag.
*/
if (ent = get_hash(crle->c_strtbl, (Addr)name, 0, HASH_FND_ENT))
ent->e_flags |= (flags & RTC_OBJ_CMDLINE);
return (0);
}