1 /*
2  * Copyright 2003-2012 Gentoo Foundation
3  * Distributed under the terms of the GNU General Public License v2
4  *
5  * Copyright 2005-2012 Ned Ludd        - <solar@gentoo.org>
6  * Copyright 2005-2012 Mike Frysinger  - <vapier@gentoo.org>
7  */
8 
9 /* stick common symbols here that are needed by paxinc.h */
10 
11 #define IN_paxinc
12 #include "paxinc.h"
13 
14 char do_reverse_endian;
15 
16 /* some of this ar code was taken from busybox */
17 
18 #define AR_MAGIC "!<arch>"
19 #define AR_MAGIC_SIZE (sizeof(AR_MAGIC)-1) /* dont count null byte */
ar_open_fd(const char * filename,int fd,bool verbose)20 archive_handle *ar_open_fd(const char *filename, int fd, bool verbose)
21 {
22 	static archive_handle ret;
23 	char buf[AR_MAGIC_SIZE];
24 
25 	ret.filename = filename;
26 	ret.fd = fd;
27 	ret.skip = 0;
28 	ret.extfn = NULL;
29 	ret.verbose = verbose;
30 
31 	if (read(ret.fd, buf, AR_MAGIC_SIZE) != AR_MAGIC_SIZE)
32 		return NULL;
33 	if (strncmp(buf, AR_MAGIC, AR_MAGIC_SIZE))
34 		return NULL;
35 
36 	return &ret;
37 }
ar_open(const char * filename,bool verbose)38 archive_handle *ar_open(const char *filename, bool verbose)
39 {
40 	int fd;
41 	archive_handle *ret;
42 
43 	if ((fd=open(filename, O_RDONLY)) == -1)
44 		errp("%s: could not open", filename);
45 
46 	ret = ar_open_fd(filename, fd, verbose);
47 	if (ret == NULL)
48 		close(fd);
49 
50 	return ret;
51 }
52 
ar_next(archive_handle * ar)53 archive_member *ar_next(archive_handle *ar)
54 {
55 	char *s;
56 	ssize_t len = 0;
57 	static archive_member ret;
58 
59 	if (ar->skip && lseek(ar->fd, ar->skip, SEEK_CUR) == -1) {
60 close_and_ret:
61 		free(ar->extfn);
62 		close(ar->fd);
63 		ar->extfn = NULL;
64 		ar->fd = -1;
65 		return NULL;
66 	}
67 
68 	if (read(ar->fd, ret.buf.raw, sizeof(ret.buf.raw)) != sizeof(ret.buf.raw))
69 		goto close_and_ret;
70 
71 	/* ar header starts on an even byte (2 byte aligned)
72 	 * '\n' is used for padding */
73 	if (ret.buf.raw[0] == '\n') {
74 		memmove(ret.buf.raw, ret.buf.raw+1, 59);
75 		if (read(ar->fd, ret.buf.raw+59, 1) != 1)
76 			goto close_and_ret;
77 	}
78 
79 	if ((ret.buf.formatted.magic[0] != '`') || (ret.buf.formatted.magic[1] != '\n')) {
80 		/* When dealing with corrupt or random embedded cross-compilers, they might
81 		 * be abusing the archive format; only complain when in verbose mode. */
82 		if (ar->verbose)
83 			warn("%s: invalid ar entry", ar->filename);
84 		goto close_and_ret;
85 	}
86 
87 	if (ret.buf.formatted.name[0] == '/' && ret.buf.formatted.name[1] == '/') {
88 		if (ar->extfn != NULL) {
89 			warn("%s: Duplicate GNU extended filename section", ar->filename);
90 			goto close_and_ret;
91 		}
92 		len = atoi(ret.buf.formatted.size);
93 		ar->extfn = xmalloc(sizeof(char) * (len + 1));
94 		if (read(ar->fd, ar->extfn, len) != len)
95 			goto close_and_ret;
96 		ar->extfn[len--] = '\0';
97 		for (; len > 0; len--)
98 			if (ar->extfn[len] == '\n')
99 				ar->extfn[len] = '\0';
100 		ar->skip = 0;
101 		return ar_next(ar);
102 	}
103 
104 	s = ret.buf.formatted.name;
105 	if (s[0] == '#' && s[1] == '1' && s[2] == '/') {
106 		/* BSD extended filename, always in use on Darwin */
107 		len = atoi(s + 3);
108 		if (len <= (ssize_t)sizeof(ret.buf.formatted.name)) {
109 			if (read(ar->fd, ret.buf.formatted.name, len) != len)
110 				goto close_and_ret;
111 		} else {
112 			s = alloca(sizeof(char) * len + 1);
113 			if (read(ar->fd, s, len) != len)
114 				goto close_and_ret;
115 			s[len] = '\0';
116 		}
117 	} else if (s[0] == '/' && s[1] >= '0' && s[1] <= '9') {
118 		/* GNU extended filename */
119 		if (ar->extfn == NULL) {
120 			warn("%s: GNU extended filename without special data section", ar->filename);
121 			goto close_and_ret;
122 		}
123 		s = ar->extfn + atoi(s + 1);
124 	}
125 
126 	snprintf(ret.name, sizeof(ret.name), "%s:%s", ar->filename, s);
127 	ret.name[sizeof(ret.name) - 1] = '\0';
128 	if ((s=strchr(ret.name+strlen(ar->filename), '/')) != NULL)
129 		*s = '\0';
130 	ret.date = atoi(ret.buf.formatted.date);
131 	ret.uid = atoi(ret.buf.formatted.uid);
132 	ret.gid = atoi(ret.buf.formatted.gid);
133 	ret.mode = strtol(ret.buf.formatted.mode, NULL, 8);
134 	ret.size = atoi(ret.buf.formatted.size);
135 	ar->skip = ret.size - len;
136 
137 	return &ret;
138 }
139 
140 /* Convert file perms into octal string */
strfileperms(const char * fname)141 const char *strfileperms(const char *fname)
142 {
143 	struct stat st;
144 	static char buf[8];
145 
146 	if (stat(fname, &st) == -1)
147 		return "";
148 
149 	snprintf(buf, sizeof(buf), "%o", st.st_mode);
150 
151 	return buf + 2;
152 }
153 
154 /* Color helpers */
155 #define COLOR(c,b) "\e[" c ";" b "m"
156 const char *NORM   = COLOR("00", "00");
157 const char *RED    = COLOR("31", "01");
158 const char *YELLOW = COLOR("33", "01");
159 
color_init(bool disable)160 void color_init(bool disable)
161 {
162 	if (!disable) {
163 		const char *nocolor = getenv("NOCOLOR");
164 		if (nocolor)
165 			disable = !strcmp(nocolor, "yes") || !strcmp(nocolor, "true");
166 	}
167 	if (disable)
168 		NORM = RED = YELLOW = "";
169 }
170 
171 /* File system helpers. */
172 int root_fd = AT_FDCWD;
173 
fopenat_r(int dir_fd,const char * path)174 FILE *fopenat_r(int dir_fd, const char *path)
175 {
176 	int fd = openat(dir_fd, path, O_RDONLY|O_CLOEXEC);
177 	if (fd == -1)
178 		return NULL;
179 	return fdopen(fd, "re");
180 }
181 
root_rel_path(const char * path)182 const char *root_rel_path(const char *path)
183 {
184 	/*
185 	 * openat() will ignore the dirfd if path starts with
186 	 * a /, so consume all of that noise
187 	 *
188 	 * XXX: we don't handle relative paths like ../ that
189 	 * break out of the --root option, but for now, just
190 	 * don't do that :P.
191 	 */
192 	if (root_fd != AT_FDCWD) {
193 		while (*path == '/')
194 			++path;
195 		if (*path == '\0')
196 			path = ".";
197 	}
198 
199 	return path;
200 }
201