1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2011 Jason King.  All rights reserved.
14  * Copyright (c) 2018, Joyent, Inc.
15  */
16 
17 #include <stdlib.h>
18 #include <stdio.h>
19 #include <string.h>
20 #include <unistd.h>
21 #include <sys/types.h>
22 #include <sys/stat.h>
23 #include <sys/wait.h>
24 #include <fcntl.h>
25 #include <err.h>
26 #include <spawn.h>
27 
28 #define	MCS	"/usr/bin/mcs"
29 
30 #define	ELFLEN 4
31 static const char elf_signature[] = "\177ELF";
32 static posix_spawnattr_t attr;
33 static const char *cmd[] = { MCS, "-d", "-n", ".SUNW_ctf", NULL, NULL };
34 
35 extern char **environ;
36 
37 static boolean_t check_file(const char *, mode_t *);
38 static boolean_t fix_file(const char *, mode_t);
39 static void usage(const char *);
40 
41 int
main(int argc,const char ** argv)42 main(int argc, const char **argv)
43 {
44 	const char **p;
45 	int rc = 0;
46 	mode_t mode;
47 
48 	if (argc < 2)
49 		usage(argv[0]);
50 
51 	rc = posix_spawnattr_init(&attr);
52 	if (rc != 0) {
53 		errx(EXIT_FAILURE, "Spawn attribute initialization failed: %s",
54 		    strerror(rc));
55 	}
56 
57 	for (p = argv + 1; *p != NULL; p++) {
58 		if (!check_file(*p, &mode))
59 			continue;
60 		if (!fix_file(*p, mode))
61 			rc = 1;
62 	}
63 
64 	return (rc);
65 }
66 
67 static boolean_t
check_file(const char * filename,mode_t * mode)68 check_file(const char *filename, mode_t *mode)
69 {
70 	char elfbuf[4];
71 	struct stat sb;
72 	int fd;
73 
74 	fd = open(filename, O_RDONLY);
75 	if (fd == -1) {
76 		warn("Unable to open %s", filename);
77 		return (B_FALSE);
78 	}
79 
80 	if (fstat(fd, &sb) == -1) {
81 		warn("stat(2) failed on %s", filename);
82 		(void) close(fd);
83 		return (B_FALSE);
84 	}
85 
86 	if (!S_ISREG(sb.st_mode)) {
87 		warnx("%s is not a regular file", filename);
88 		(void) close(fd);
89 		return (B_FALSE);
90 	}
91 
92 	if (sb.st_size < ELFLEN) {
93 		warnx("%s is not an ELF file", filename);
94 		(void) close(fd);
95 		return (B_FALSE);
96 	}
97 
98 	if (read(fd, elfbuf, ELFLEN) != ELFLEN) {
99 		warn("Error reading %s", filename);
100 		(void) close(fd);
101 		return (B_FALSE);
102 	}
103 
104 	if (strncmp(elfbuf, elf_signature, ELFLEN) != 0) {
105 		warnx("%s is not an ELF file", filename);
106 		(void) close(fd);
107 		return (B_FALSE);
108 	}
109 
110 	*mode = sb.st_mode & S_IAMB;
111 	(void) close(fd);
112 	return (B_TRUE);
113 }
114 
115 static boolean_t
fix_file(const char * filename,mode_t mode)116 fix_file(const char *filename, mode_t mode)
117 {
118 	pid_t pid;
119 	int i, rc;
120 	int stat = 0;
121 
122 	if ((mode & S_IWUSR) == 0) {
123 		if (chmod(filename, mode | S_IWUSR) == -1) {
124 			warn("failed to make %s writable", filename);
125 			return (B_FALSE);
126 		}
127 	}
128 
129 	cmd[4] = filename;
130 	if ((rc = posix_spawn(&pid, MCS, NULL, &attr,
131 	    (char *const *)cmd, environ)) != 0) {
132 		warnx("could not exec mcs: %s", strerror(rc));
133 		return (B_FALSE);
134 	}
135 
136 	(void) waitpid(pid, &stat, 0);
137 	if (!WIFEXITED(stat) || WEXITSTATUS(stat) != 0) {
138 		warnx("Removing CTF information from %s failed", filename);
139 		return (B_FALSE);
140 	}
141 
142 	if ((mode & S_IWUSR) == 0) {
143 		if (chmod(filename, mode) == -1) {
144 			warn("could not reset permissions of %s", filename);
145 			return (B_FALSE);
146 		}
147 	}
148 
149 	return (B_TRUE);
150 }
151 
152 static void
usage(const char * name)153 usage(const char *name)
154 {
155 	(void) fprintf(stderr, "Usage: %s file...\n", name);
156 	exit(EXIT_FAILURE);
157 }
158