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