#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"
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 (strcmp(path, obj->o_path))
return (1);
}
if (flags & RTC_OBJ_DUMP) {
char _alter[PATH_MAX];
(void) strlcpy(alter, crle->c_objdir, sizeof (alter));
(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 (obj->o_alter) {
crle->c_strsize -= strlen(alter) + 1;
fmt = MSG_INTL(MSG_DIA_ALTUPDATE);
} else {
fmt = MSG_INTL(MSG_DIA_ALTCREATE);
}
altsz = strlen(alter) + 1;
if ((obj->o_alter = malloc(altsz)) == NULL)
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);
}
static Hash_ent *
enterino(Crle_desc *crle, const char *name, struct stat *status, Half flags)
{
Hash_ent *ent;
Hash_obj *obj;
Hash_tbl *tbl;
Aliste idx;
Addr ino = (Addr)status->st_ino;
ulong_t dev = status->st_dev;
Lword info;
int found = 0;
if (flags & RTC_OBJ_DIRENT)
info = (Lword)status->st_mtime;
else
info = (Lword)status->st_size;
for (APLIST_TRAVERSE(crle->c_inotbls, idx, tbl)) {
if (tbl->t_ident == dev) {
found = 1;
break;
}
}
if (found == 0) {
if ((tbl = make_hash(crle->c_inobkts, HASH_INT, dev)) == NULL)
return (NULL);
if (aplist_append(&crle->c_inotbls, tbl, AL_CNT_CRLE) == NULL)
return (NULL);
}
if ((ent = get_hash(tbl, ino, 0,
(HASH_FND_ENT | HASH_ADD_ENT))) == NULL)
return (NULL);
if ((obj = ent->e_obj) == NULL) {
if ((obj = calloc(1, sizeof (Hash_obj))) == NULL)
return (NULL);
obj->o_tbl = tbl;
obj->o_flags = flags;
obj->o_info = info;
if ((obj->o_path = strdup(name)) == NULL)
return (NULL);
ent->e_obj = obj;
}
return (ent);
}
static int
_enterdir(Crle_desc *crle, const char *dir, Hash_ent *ent, Hash_obj *obj)
{
size_t size = strlen(dir) + 1;
char *ndir;
if ((ndir = malloc(size)) == NULL)
return (0);
(void) strcpy(ndir, dir);
ent->e_key = (Addr)ndir;
ent->e_id = crle->c_dirnum++;
ent->e_obj = obj;
crle->c_strsize += size;
crle->c_hashstrnum++;
crle->c_filenum++;
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);
}
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;
if (realpath(odir, rdir) == NULL)
return (NULL);
if (strcmp(odir, rdir))
ndir = rdir;
else
ndir = (char *)odir;
if ((flags & RTC_OBJ_ALLENTS) == 0)
flags &= ~(RTC_OBJ_ALTER | RTC_OBJ_DUMP | RTC_OBJ_GROUP);
flags |= RTC_OBJ_DIRENT;
if ((ent = enterino(crle, ndir, status, flags)) == NULL)
return (NULL);
obj = ent->e_obj;
if ((ent = get_hash(stbl, (Addr)ndir, 0,
(HASH_FND_ENT | HASH_ADD_ENT))) == NULL)
return (NULL);
if (ent->e_id == 0) {
if (_enterdir(crle, ndir, ent, obj) == 0)
return (NULL);
}
if (ndir == odir)
return (ent);
if ((ent = get_hash(stbl, (Addr)odir, 0,
(HASH_FND_ENT | HASH_ADD_ENT))) == NULL)
return (NULL);
if (ent->e_id == 0) {
if (_enterdir(crle, odir, ent, obj) == 0)
return (NULL);
}
return (ent);
}
static Hash_ent *
enternoexistdir(Crle_desc *crle, const char *dir)
{
Hash_ent *ent;
if ((ent = get_hash(crle->c_strtbl, (Addr)dir, 0,
(HASH_FND_ENT | HASH_ADD_ENT))) == NULL)
return (NULL);
if (ent->e_id == 0) {
Hash_obj * obj;
if ((obj = calloc(1, sizeof (Hash_obj))) == NULL)
return (NULL);
obj->o_flags = (RTC_OBJ_NOEXIST | RTC_OBJ_DIRENT);
if (_enterdir(crle, dir, ent, obj) == 0)
return (NULL);
}
return (ent);
}
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 (off == 0) {
if ((nfile = malloc(size)) == NULL)
return (0);
(void) strcpy(nfile, file);
} else {
nfile = (char *)file;
}
fent->e_key = (Addr)nfile;
fent->e_off = off;
fent->e_dir = dent;
fent->e_id = dent->e_id;
fent->e_path = rent;
dent->e_cnt++;
fent->e_obj = obj;
crle->c_strsize += size;
crle->c_hashstrnum++;
crle->c_filenum++;
if (crle->c_flags & CRLE_VERBOSE)
(void) printf(MSG_INTL(MSG_DIA_FILE), fent->e_id, nfile);
return (1);
}
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;
if ((rent = get_hash(crle->c_strtbl, (Addr)path, 0,
(HASH_FND_ENT | HASH_ADD_ENT))) == NULL)
return (NULL);
if (rent->e_id == 0) {
if ((obj = calloc(1, sizeof (Hash_obj))) == NULL)
return (NULL);
obj->o_flags = RTC_OBJ_NOEXIST;
if (_enterfile(crle, path, 0, rent, 0, dent, obj) == 0)
return (NULL);
}
obj = rent->e_obj;
if ((obj->o_path = strdup(path)) == NULL)
return (NULL);
off = file - path;
file = (char *)rent->e_key + off;
if ((ent = get_hash(crle->c_strtbl, (Addr)file, dent->e_id,
(HASH_FND_ENT | HASH_ADD_ENT))) == NULL)
return (NULL);
if (ent->e_id == 0) {
if (_enterfile(crle, file, off, ent, rent, dent, obj) == 0)
return (NULL);
}
return (ent);
}
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;
if (realpath(opath, rpath) == NULL)
return (NULL);
if (strcmp(opath, rpath)) {
npath = rpath;
nfile = strrchr(npath, '/');
if (nfile != NULL)
nfile++;
else
nfile = npath;
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)) == NULL)
return (NULL);
}
} else {
npath = (char *)opath;
nfile = (char *)ofile;
}
if ((ent = enterino(crle, npath, status, flags)) == NULL)
return (NULL);
obj = ent->e_obj;
if ((rent = get_hash(stbl, (Addr)npath, 0,
(HASH_FND_ENT | HASH_ADD_ENT))) == NULL)
return (NULL);
if (rent->e_id == 0) {
if (_enterfile(crle, npath, 0, rent, 0, ndent, obj) == 0)
return (NULL);
}
rent->e_flags |= RTC_OBJ_REALPTH;
ndent->e_flags |= RTC_OBJ_REALPTH;
off = nfile - npath;
nfile = (char *)rent->e_key + off;
if ((ent = get_hash(stbl, (Addr)nfile, ndent->e_id,
(HASH_FND_ENT | HASH_ADD_ENT))) == NULL)
return (NULL);
if (ent->e_id == 0) {
if (_enterfile(crle, nfile, off, ent, rent, ndent, obj) == 0)
return (NULL);
}
if (nfile == ofile)
return (ent);
if ((ent = enterino(crle, opath, status, 0)) == NULL)
return (NULL);
obj = ent->e_obj;
if ((rent = get_hash(stbl, (Addr)opath, 0,
(HASH_FND_ENT | HASH_ADD_ENT))) == NULL)
return (NULL);
if (rent->e_id == 0) {
if (_enterfile(crle, opath, 0, rent, 0, odent, obj) == 0)
return (NULL);
}
off = ofile - opath;
ofile = (char *)rent->e_key + off;
if ((ent = get_hash(stbl, (Addr)ofile, odent->e_id,
(HASH_FND_ENT | HASH_ADD_ENT))) == NULL)
return (NULL);
if (ent->e_id == 0) {
if (_enterfile(crle, ofile, off, ent, rent, odent, obj) == 0)
return (NULL);
}
return (ent);
}
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;
Aliste idx;
Hash_tbl *tbl;
Addr ino = (Addr)status->st_ino;
for (APLIST_TRAVERSE(crle->c_inotbls, idx, tbl)) {
if (tbl->t_ident != status->st_dev)
continue;
if ((ent = get_hash(tbl, ino, 0, HASH_FND_ENT)) == NULL)
break;
if ((ent = enterfile(crle, path, file, flags, dent,
status)) == NULL)
return (error);
obj = ent->e_obj;
if ((flags & RTC_OBJ_ALTER) &&
((obj->o_flags & RTC_OBJ_NOALTER) == 0)) {
if (enteralt(crle, path, file, flags, obj) == 0)
return (error);
}
return (0);
}
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);
}
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 (flags & RTC_OBJ_DUMP)
dyflags = _gelf_getdyndtflags_1(elf);
if (ehdr.e_type == ET_EXEC) {
if (error == 0) {
(void) elf_end(elf);
return (0);
}
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 (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);
}
}
if ((ent = enterfile(crle, path, file, flags, dent, status)) == NULL) {
(void) elf_end(elf);
return (error);
}
obj = ent->e_obj;
if (flags & RTC_OBJ_ALTER) {
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);
}
}
}
if (ehdr.e_type == ET_EXEC) {
obj->o_flags |= RTC_OBJ_EXEC;
if ((flags & RTC_OBJ_DUMP) &&
(crle->c_dlflags & RTLD_REL_EXEC)) {
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 (flags & RTC_OBJ_GROUP) {
if (depend(crle, path, flags, &ehdr)) {
(void) elf_end(elf);
return (error);
}
}
(void) elf_end(elf);
return (0);
}
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;
if ((ent = get_hash(stbl, (Addr)name, 0, HASH_FND_ENT)) != NULL) {
if (ent->e_obj->o_flags & RTC_OBJ_ALLENTS)
return (0);
} else {
if ((ent = enterdir(crle, name, (flags | RTC_OBJ_ALLENTS),
status)) == NULL)
return (1);
}
ent->e_obj->o_flags |= RTC_OBJ_ALLENTS;
for (dst = path, dst--, src = name; *src; src++)
*++dst = *src;
if (*dst++ != '/')
*dst++ = '/';
if ((dir = opendir(name)) == NULL)
return (1);
while ((dirent = readdir(dir)) != NULL) {
const char *file = dirent->d_name;
char *_dst;
if ((file[0] == '.') && ((file[1] == '\0') ||
((file[1] == '.') && (file[2] == '\0'))))
continue;
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);
}
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 (crle->c_dirnum == 0) {
if ((crle->c_strtbl = make_hash(crle->c_strbkts,
HASH_STR, 0)) == NULL)
return (1);
crle->c_dirnum = 1;
}
if (crle->c_flags & CRLE_VERBOSE)
(void) printf(MSG_INTL(MSG_DIA_INSPECT), name);
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 ((flags & (RTC_OBJ_DUMP | RTC_OBJ_ALTER)) !=
RTC_OBJ_ALTER) {
if ((ent = enternoexistdir(crle, name)) == NULL)
return (1);
ent->e_flags |= flags;
return (0);
}
}
}
if ((noexist == 0) && ((status.st_mode & S_IFMT) == S_IFDIR)) {
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 ((noexist == 0) && ((status.st_mode & S_IFMT) != S_IFREG)) {
(void) fprintf(stderr, MSG_INTL(MSG_GEN_INVFILE), crle->c_name,
name);
return (1);
}
if ((file = strrchr(name, '/')) == NULL) {
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++;
}
if ((ent = get_hash(crle->c_strtbl,
(Addr)dir, 0, HASH_FND_ENT)) == NULL) {
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 {
if ((ent = enternoexistdir(crle, dir)) == NULL)
return (1);
ent->e_flags |= nflags;
}
} else {
if ((ent = enterdir(crle, dir, nflags,
&_status)) == NULL)
return (1);
}
}
if (noexist) {
if ((ent = enternoexistfile(crle, name, file, ent)) == NULL)
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);
}
ent = get_hash(crle->c_strtbl, (Addr)name, 0, HASH_FND_ENT);
if (ent != NULL)
ent->e_flags |= (flags & RTC_OBJ_CMDLINE);
return (0);
}