1/*
2 * FreeBSD install - a package for the installation and maintenance
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$");
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 */
31void
32add_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 */
61void
62check_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
100static int
101trylink(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 */
135void
136copy_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