Initial import from FreeBSD RELENG_4:
[dragonfly.git] / usr.sbin / pkg_install / create / pl.c
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: src/usr.sbin/pkg_install/create/pl.c,v 1.13.2.8 2002/08/31 19:31:02 obrien Exp $");
23
24 #include "lib.h"
25 #include "create.h"
26 #include <errno.h>
27 #include <err.h>
28 #include <md5.h>
29
30 /* Check a list for files that require preconversion */
31 void
32 check_list(const char *home, Package *pkg)
33 {
34     const char *where = home;
35     const char *there = NULL;
36     char *cp, name[FILENAME_MAX], buf[33];
37     PackingList p;
38
39     for (p = pkg->head; p != NULL; p = p->next)
40         switch (p->type) {
41         case PLIST_CWD:
42             where = p->name;
43             break;
44
45         case PLIST_IGNORE:
46             p = p->next;
47             break;
48
49         case PLIST_SRC:
50             there = p->name;
51             break;
52
53         case PLIST_FILE:
54             cp = NULL;
55             sprintf(name, "%s/%s", there ? there : where, p->name);
56             if (issymlink(name)) {
57                 int len;
58                 char lnk[FILENAME_MAX];
59
60                 if ((len = readlink(name, lnk, FILENAME_MAX)) > 0)
61                     cp = MD5Data((unsigned char *)lnk, len, buf);
62             } else if (isfile(name)) {
63                 /* Don't record MD5 checksum for device nodes and such */
64                 cp = MD5File(name, buf);
65             }
66
67             if (cp != NULL) {
68                 PackingList tmp = new_plist_entry();
69
70                 tmp->name = copy_string(strconcat("MD5:", cp));
71                 tmp->type = PLIST_COMMENT;
72                 tmp->next = p->next;
73                 tmp->prev = p;
74                 p->next = tmp;
75                 if (pkg->tail == p)
76                     pkg->tail = tmp;
77                 p = tmp;
78             }
79             break;
80         default:
81             break;
82         }
83 }
84
85 static int
86 trylink(const char *from, const char *to)
87 {
88     if (link(from, to) == 0)
89         return 0;
90     if (errno == ENOENT) {
91         /* try making the container directory */
92         char *cp = strrchr(to, '/');
93         if (cp)
94             vsystem("mkdir -p %.*s", cp - to,
95                     to);
96         return link(from, to);
97     }
98     return -1;
99 }
100
101 #define STARTSTRING "tar cf -"
102 #define TOOBIG(str) (int)strlen(str) + 6 + (int)strlen(home) + where_count > maxargs
103 #define PUSHOUT() /* push out string */ \
104         if (where_count > (int)sizeof(STARTSTRING)-1) { \
105                     strcat(where_args, "|tar xpf -"); \
106                     if (system(where_args)) { \
107                         cleanup(0); \
108                         errx(2, "%s: can't invoke tar pipeline", __func__); \
109                     } \
110                     memset(where_args, 0, maxargs); \
111                     last_chdir = NULL; \
112                     strcpy(where_args, STARTSTRING); \
113                     where_count = sizeof(STARTSTRING)-1; \
114         }
115
116 /*
117  * Copy unmarked files in packing list to playpen - marked files
118  * have already been copied in an earlier pass through the list.
119  */
120 void
121 copy_plist(const char *home, Package *plist)
122 {
123     PackingList p = plist->head;
124     const char *where = home;
125     const char *there = NULL, *mythere;
126     char *where_args;
127     const char *last_chdir, *root = "/";
128     int maxargs, where_count = 0, add_count;
129     struct stat stb;
130     dev_t curdir;
131
132     maxargs = sysconf(_SC_ARG_MAX);
133     maxargs -= 64;                      /*
134                                          * Some slop for the tar cmd text,
135                                          * and sh -c
136                                          */
137     where_args = malloc(maxargs);
138     if (!where_args) {
139         cleanup(0);
140         errx(2, "%s: can't get argument list space", __func__);
141     }
142
143     memset(where_args, 0, maxargs);
144     strcpy(where_args, STARTSTRING);
145     where_count = sizeof(STARTSTRING)-1;
146     last_chdir = 0;
147
148     if (stat(".", &stb) == 0)
149         curdir = stb.st_dev;
150     else
151         curdir = (dev_t) -1;            /*
152                                          * It's ok if this is a valid dev_t;
153                                          * this is just a hint for an
154                                          * optimization.
155                                          */
156
157     while (p) {
158         if (p->type == PLIST_CWD)
159             where = p->name;
160         else if (p->type == PLIST_SRC)
161             there = p->name;
162         else if (p->type == PLIST_IGNORE)
163             p = p->next;
164         else if (p->type == PLIST_FILE && !p->marked) {
165             char fn[FILENAME_MAX];
166
167
168             /* First, look for it in the "home" dir */
169             sprintf(fn, "%s/%s", home, p->name);
170             if (fexists(fn)) {
171                 if (lstat(fn, &stb) == 0 && stb.st_dev == curdir &&
172                     S_ISREG(stb.st_mode)) {
173                     /*
174                      * If we can link it to the playpen, that avoids a copy
175                      * and saves time.
176                      */
177                     if (p->name[0] != '/') {
178                         /*
179                          * Don't link abspn stuff--it doesn't come from
180                          * local dir!
181                          */
182                         if (trylink(fn, p->name) == 0) {
183                             p = p->next;
184                             continue;
185                         }
186                     }
187                 }
188                 if (TOOBIG(fn)) {
189                     PUSHOUT();
190                 }
191                 if (p->name[0] == '/') {
192                     add_count = snprintf(&where_args[where_count],
193                                          maxargs - where_count,
194                                          " %s %s",
195                                          last_chdir == root ? "" : "-C /",
196                                          p->name);
197                     last_chdir = root;
198                 } else {
199                     add_count = snprintf(&where_args[where_count],
200                                          maxargs - where_count,
201                                          " %s%s %s",
202                                          last_chdir == home ? "" : "-C ",
203                                          last_chdir == home ? "" : home,
204                                          p->name);
205                     last_chdir = home;
206                 }
207                 if (add_count < 0 || add_count > maxargs - where_count) {
208                     cleanup(0);
209                     errx(2, "%s: oops, miscounted strings!", __func__);
210                 }
211                 where_count += add_count;
212             }
213             /*
214              * Otherwise, try along the actual extraction path..
215              */
216             else {
217                 if (p->name[0] == '/')
218                     mythere = root;
219                 else mythere = there;
220                 sprintf(fn, "%s/%s", mythere ? mythere : where, p->name);
221                 if (lstat(fn, &stb) == 0 && stb.st_dev == curdir &&
222                     S_ISREG(stb.st_mode)) {
223                     /*
224                      * If we can link it to the playpen, that avoids a copy
225                      * and saves time.
226                      */
227                     if (trylink(fn, p->name) == 0) {
228                         p = p->next;
229                         continue;
230                     }
231                 }
232                 if (TOOBIG(p->name)) {
233                     PUSHOUT();
234                 }
235                 if (last_chdir == (mythere ? mythere : where))
236                     add_count = snprintf(&where_args[where_count],
237                                          maxargs - where_count,
238                                          " %s", p->name);
239                 else
240                     add_count = snprintf(&where_args[where_count],
241                                          maxargs - where_count,
242                                          " -C %s %s",
243                                          mythere ? mythere : where,
244                                          p->name);
245                 if (add_count < 0 || add_count > maxargs - where_count) {
246                     cleanup(0);
247                     errx(2, "%s: oops, miscounted strings!", __func__);
248                 }
249                 where_count += add_count;
250                 last_chdir = (mythere ? mythere : where);
251             }
252         }
253         p = p->next;
254     }
255     PUSHOUT();
256     free(where_args);
257 }