1 /*
2 * FreeBSD install - a package for the installation and maintainance
3 * of non-core utilities.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * Jordan K. Hubbard
15 * 18 July 1993
16 *
17 * Routines for dealing with the packing list.
18 *
19 */
20
21 #include <sys/cdefs.h>
22 __FBSDID("$FreeBSD: stable/9/usr.sbin/pkg_install/create/pl.c 252638 2013-07-03 22:57:20Z obrien $");
23
24 #include "lib.h"
25 #include "create.h"
26 #include <errno.h>
27 #include <err.h>
28 #include <md5.h>
29
30 /* Add an MD5 checksum entry for a file or link */
31 void
add_cksum(Package * pkg,PackingList p,const char * fname)32 add_cksum(Package *pkg, PackingList p, const char *fname)
33 {
34 char *cp = NULL, buf[33];
35
36 if (issymlink(fname)) {
37 int len;
38 char lnk[FILENAME_MAX];
39
40 if ((len = readlink(fname, lnk, FILENAME_MAX)) > 0)
41 cp = MD5Data((unsigned char *)lnk, len, buf);
42 } else if (isfile(fname)) {
43 /* Don't record MD5 checksum for device nodes and such */
44 cp = MD5File(fname, buf);
45 }
46
47 if (cp != NULL) {
48 PackingList tmp = new_plist_entry();
49
50 tmp->name = copy_string(strconcat("MD5:", cp));
51 tmp->type = PLIST_COMMENT;
52 tmp->next = p->next;
53 tmp->prev = p;
54 p->next = tmp;
55 if (pkg->tail == p)
56 pkg->tail = tmp;
57 }
58 }
59
60 /* Check a list for files that require preconversion */
61 void
check_list(const char * home,Package * pkg)62 check_list(const char *home, Package *pkg)
63 {
64 const char *where = home;
65 const char *there = NULL;
66 char name[FILENAME_MAX];
67 char *prefix = NULL;
68 PackingList p;
69
70 for (p = pkg->head; p != NULL; p = p->next)
71 switch (p->type) {
72 case PLIST_CWD:
73 if (!prefix)
74 prefix = p->name;
75 where = (p->name == NULL) ? prefix : p->name;
76 break;
77
78 case PLIST_IGNORE:
79 p = p->next;
80 break;
81
82 case PLIST_SRC:
83 there = p->name;
84 break;
85
86 case PLIST_FILE:
87 if (there)
88 snprintf(name, sizeof(name), "%s/%s", there, p->name);
89 else
90 snprintf(name, sizeof(name), "%s%s/%s",
91 BaseDir && where && where[0] == '/' ? BaseDir : "", where, p->name);
92
93 add_cksum(pkg, p, name);
94 break;
95 default:
96 break;
97 }
98 }
99
100 static int
trylink(const char * from,const char * to)101 trylink(const char *from, const char *to)
102 {
103 if (link(from, to) == 0)
104 return 0;
105 if (errno == ENOENT) {
106 /* try making the container directory */
107 char *cp = strrchr(to, '/');
108 if (cp)
109 vsystem("/bin/mkdir -p %.*s", cp - to,
110 to);
111 return link(from, to);
112 }
113 return -1;
114 }
115
116 #define STARTSTRING "/usr/bin/tar cf -"
117 #define TOOBIG(str) (int)strlen(str) + 6 + (int)strlen(home) + where_count > maxargs
118 #define PUSHOUT() /* push out string */ \
119 if (where_count > (int)sizeof(STARTSTRING)-1) { \
120 strcat(where_args, "|/usr/bin/tar xpf -"); \
121 if (system(where_args)) { \
122 cleanup(0); \
123 errx(2, "%s: can't invoke tar pipeline", __func__); \
124 } \
125 memset(where_args, 0, maxargs); \
126 last_chdir = NULL; \
127 strcpy(where_args, STARTSTRING); \
128 where_count = sizeof(STARTSTRING)-1; \
129 }
130
131 /*
132 * Copy unmarked files in packing list to playpen - marked files
133 * have already been copied in an earlier pass through the list.
134 */
135 void
copy_plist(const char * home,Package * plist)136 copy_plist(const char *home, Package *plist)
137 {
138 PackingList p = plist->head;
139 const char *where = home;
140 const char *there = NULL, *mythere;
141 char *where_args, *prefix = NULL;
142 const char *last_chdir, *root = "/";
143 long maxargs;
144 int where_count = 0, add_count;
145 struct stat stb;
146 dev_t curdir;
147
148 maxargs = sysconf(_SC_ARG_MAX);
149 maxargs -= 64; /*
150 * Some slop for the tar cmd text,
151 * and sh -c
152 */
153 where_args = malloc(maxargs);
154 if (!where_args) {
155 cleanup(0);
156 errx(2, "%s: can't get argument list space", __func__);
157 }
158
159 memset(where_args, 0, maxargs);
160 strcpy(where_args, STARTSTRING);
161 where_count = sizeof(STARTSTRING)-1;
162 last_chdir = 0;
163
164 if (stat(".", &stb) == 0)
165 curdir = stb.st_dev;
166 else
167 curdir = (dev_t) -1; /*
168 * It's ok if this is a valid dev_t;
169 * this is just a hint for an
170 * optimization.
171 */
172
173 while (p) {
174 if (p->type == PLIST_CWD)
175 {
176 if (!prefix)
177 prefix = p->name;
178 where = p->name == NULL ? prefix : p->name;
179 }
180 else if (p->type == PLIST_SRC)
181 there = p->name;
182 else if (p->type == PLIST_IGNORE)
183 p = p->next;
184 else if (p->type == PLIST_FILE && !p->marked) {
185 char fn[FILENAME_MAX];
186
187
188 /* First, look for it in the "home" dir */
189 sprintf(fn, "%s/%s", home, p->name);
190 if (fexists(fn)) {
191 if (lstat(fn, &stb) == 0 && stb.st_dev == curdir &&
192 S_ISREG(stb.st_mode)) {
193 /*
194 * If we can link it to the playpen, that avoids a copy
195 * and saves time.
196 */
197 if (p->name[0] != '/') {
198 /*
199 * Don't link abspn stuff--it doesn't come from
200 * local dir!
201 */
202 if (trylink(fn, p->name) == 0) {
203 p = p->next;
204 continue;
205 }
206 }
207 }
208 if (TOOBIG(fn)) {
209 PUSHOUT();
210 }
211 if (p->name[0] == '/') {
212 add_count = snprintf(&where_args[where_count],
213 maxargs - where_count,
214 " %s %s",
215 last_chdir == root ? "" : "-C /",
216 p->name);
217 last_chdir = root;
218 } else {
219 add_count = snprintf(&where_args[where_count],
220 maxargs - where_count,
221 " %s%s %s",
222 last_chdir == home ? "" : "-C ",
223 last_chdir == home ? "" : home,
224 p->name);
225 last_chdir = home;
226 }
227 if (add_count < 0 || add_count >= maxargs - where_count) {
228 cleanup(0);
229 errx(2, "%s: oops, miscounted strings!", __func__);
230 }
231 where_count += add_count;
232 }
233 /*
234 * Otherwise, try along the actual extraction path..
235 */
236 else {
237 if (p->name[0] == '/')
238 mythere = root;
239 else mythere = there;
240 if (mythere)
241 snprintf(fn, sizeof(fn), "%s/%s", mythere, p->name);
242 else
243 snprintf(fn, sizeof(fn), "%s%s/%s",
244 BaseDir && where && where[0] == '/' ? BaseDir : "", where, p->name);
245 if (lstat(fn, &stb) == 0 && stb.st_dev == curdir &&
246 S_ISREG(stb.st_mode)) {
247 /*
248 * If we can link it to the playpen, that avoids a copy
249 * and saves time.
250 */
251 if (trylink(fn, p->name) == 0) {
252 p = p->next;
253 continue;
254 }
255 }
256 if (TOOBIG(p->name)) {
257 PUSHOUT();
258 }
259 if (last_chdir == (mythere ? mythere : where))
260 add_count = snprintf(&where_args[where_count],
261 maxargs - where_count,
262 " %s", p->name);
263 else
264 add_count = snprintf(&where_args[where_count],
265 maxargs - where_count,
266 " -C %s %s",
267 mythere ? mythere : where,
268 p->name);
269 if (add_count < 0 || add_count >= maxargs - where_count) {
270 cleanup(0);
271 errx(2, "%s: oops, miscounted strings!", __func__);
272 }
273 where_count += add_count;
274 last_chdir = (mythere ? mythere : where);
275 }
276 }
277 p = p->next;
278 }
279 PUSHOUT();
280 free(where_args);
281 }
282