xref: /dragonfly/usr.bin/dsynth/mount.c (revision 3d33658b)
1 /*
2  * Copyright (c) 2019 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Matthew Dillon <dillon@backplane.com>
6  *
7  * This code uses concepts and configuration based on 'synth', by
8  * John R. Marino <draco@marino.st>, which was written in ada.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  *
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in
18  *    the documentation and/or other materials provided with the
19  *    distribution.
20  * 3. Neither the name of The DragonFly Project nor the names of its
21  *    contributors may be used to endorse or promote products derived
22  *    from this software without specific, prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
27  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
28  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
29  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
30  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
31  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
32  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
33  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
34  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35  * SUCH DAMAGE.
36  */
37 #include "dsynth.h"
38 
39 static void domount(worker_t *work, int type,
40 			const char *spath, const char *dpath,
41 			const char *discretefmt);
42 static void dounmount(worker_t *work, const char *rpath);
43 static void makeDiscreteCopies(const char *spath, const char *discretefmt);
44 
45 /*
46  * Called by the frontend to create a template which will be cpdup'd
47  * into fresh workers.
48  *
49  * Template must have been previously destroyed.  Errors are fatal
50  */
51 int
52 DoCreateTemplate(int force)
53 {
54 	struct stat st;
55 	char *goodbuf;
56 	char *buf;
57 	int rc;
58 	int fd;
59 
60 	rc = 0;
61 	asprintf(&goodbuf, "%s/.template.good", BuildBase);
62 
63 	/*
64 	 * Conditionally create the template and discrete copies of certain
65 	 * directories if we think we are missing things.
66 	 */
67 	if (force == 0) {
68 		asprintf(&buf, "%s/Template", BuildBase);
69 		if (stat(buf, &st) < 0)
70 			force = 1;
71 		free(buf);
72 
73 		if (stat(goodbuf, &st) < 0)
74 			force = 1;
75 	}
76 
77 	dlog(DLOG_ALL, "Check Template: %s\n",
78 	     (force ? "Must-Create" : "Good"));
79 
80 	/*
81 	 * Create the template
82 	 */
83 	if (force) {
84 		remove(goodbuf);	/* ignore exit code */
85 
86 		rc = 0;
87 		asprintf(&buf, "%s/mktemplate %s %s/Template",
88 			 SCRIPTPATH(SCRIPTDIR), SystemPath, BuildBase);
89 		rc = system(buf);
90 		if (rc)
91 			dfatal("Command failed: %s\n", buf);
92 		dlog(DLOG_ALL | DLOG_FILTER,
93 		     "Template - rc=%d running %s\n", rc, buf);
94 		free(buf);
95 
96 		/*
97 		 * Make discrete copies of certain extremely heavily used
98 		 * but small directories.
99 		 */
100 		if (force) {
101 			makeDiscreteCopies("$/bin", "/bin.%03d");
102 			makeDiscreteCopies("$/lib", "/lib.%03d");
103 			makeDiscreteCopies("$/libexec", "/libexec.%03d");
104 			makeDiscreteCopies("$/usr/bin", "/usr.bin.%03d");
105 		}
106 
107 		/*
108 		 * Mark the template good... ah, do a sync() to really
109 		 * be sure that it can't get corrupted.
110 		 */
111 		sync();
112 		fd = open(goodbuf, O_RDWR|O_CREAT|O_TRUNC, 0644);
113 		dassert_errno(fd >= 0, "could not create %s", goodbuf);
114 		close(fd);
115 
116 		dlog(DLOG_ALL | DLOG_FILTER, "Template - done\n");
117 	}
118 	free(goodbuf);
119 
120 	return force;
121 }
122 
123 void
124 DoDestroyTemplate(void)
125 {
126 	struct stat st;
127 	char *path;
128 	char *buf;
129 	int rc;
130 
131 	/*
132 	 * NOTE: rm -rf safety, use a fixed name 'Template' to ensure we
133 	 *	 do not accidently blow something up.
134 	 */
135 	asprintf(&path, "%s/Template", BuildBase);
136 	if (stat(path, &st) == 0) {
137 		asprintf(&buf, "chflags -R noschg %s; /bin/rm -rf %s",
138 			 path, path);
139 		rc = system(buf);
140 		if (rc)
141 			dfatal("Command failed: %s (ignored)\n", buf);
142 		free(buf);
143 	}
144 	free(path);
145 }
146 
147 /*
148  * Called by the worker support thread to install a new worker
149  * filesystem topology.
150  */
151 void
152 DoWorkerMounts(worker_t *work)
153 {
154 	char *buf;
155 	int rc;
156 
157 	/*
158 	 * Generate required mounts, domount() will mkdir() the target
159 	 * directory if necessary and prefix spath with SystemPath if
160 	 * it starts with $/
161 	 */
162 	domount(work, TMPFS_RW, "dummy", "", NULL);
163 	asprintf(&buf, "%s/usr", work->basedir);
164 	if (mkdir(buf, 0755) != 0) {
165 		fprintf(stderr, "Command failed: mkdir %s\n", buf);
166 		++work->mount_error;
167 	}
168 	domount(work, NULLFS_RO, "$/boot", "/boot", NULL);
169 	domount(work, TMPFS_RW,  "dummy", "/boot/modules.local", NULL);
170 	domount(work, DEVFS_RW,  "dummy", "/dev", NULL);
171 	domount(work, PROCFS_RO, "dummy", "/proc", NULL);
172 	domount(work, NULLFS_RO, "$/bin", "/bin", "/bin.%03d");
173 	domount(work, NULLFS_RO, "$/sbin", "/sbin", NULL);
174 	domount(work, NULLFS_RO, "$/lib", "/lib", "/lib.%03d");
175 	domount(work, NULLFS_RO, "$/libexec", "/libexec", "/libexec.%03d");
176 	domount(work, NULLFS_RO, "$/usr/bin", "/usr/bin", "/usr.bin.%03d");
177 	domount(work, NULLFS_RO, "$/usr/include", "/usr/include", NULL);
178 	domount(work, NULLFS_RO, "$/usr/lib", "/usr/lib", NULL);
179 	domount(work, NULLFS_RO, "$/usr/libdata", "/usr/libdata", NULL);
180 	domount(work, NULLFS_RO, "$/usr/libexec", "/usr/libexec", NULL);
181 	domount(work, NULLFS_RO, "$/usr/sbin", "/usr/sbin", NULL);
182 	domount(work, NULLFS_RO, "$/usr/share", "/usr/share", NULL);
183 	domount(work, TMPFS_RW,  "dummy", "/usr/local", NULL);
184 	domount(work, NULLFS_RO, "$/usr/games", "/usr/games", NULL);
185 	if (UseUsrSrc)
186 		domount(work, NULLFS_RO, "$/usr/src", "/usr/src", NULL);
187 	domount(work, NULLFS_RO, DPortsPath, "/xports", NULL);
188 	domount(work, NULLFS_RW, OptionsPath, "/options", NULL);
189 	domount(work, NULLFS_RW, PackagesPath, "/packages", NULL);
190 	domount(work, NULLFS_RW, DistFilesPath, "/distfiles", NULL);
191 	domount(work, TMPFS_RW_BIG, "dummy", "/construction", NULL);
192 	if (UseCCache)
193 		domount(work, NULLFS_RW, CCachePath, "/ccache", NULL);
194 
195 	/*
196 	 * NOTE: Uses blah/. to prevent cp from creating 'Template' under
197 	 *	 work->basedir.  We want to start with the content.
198 	 */
199 	asprintf(&buf, "cp -Rp %s/Template/. %s", BuildBase, work->basedir);
200 	rc = system(buf);
201 	if (rc) {
202 		fprintf(stderr, "Command failed: %s\n", buf);
203 		++work->accum_error;
204 		snprintf(work->status, sizeof(work->status),
205 			 "Template copy failed");
206 	}
207 	free(buf);
208 }
209 
210 /*
211  * Called by the worker support thread to remove a worker
212  * filesystem topology.
213  *
214  * NOTE: No need to conditionalize UseUsrSrc, it doesn't hurt to
215  *	 issue the umount() if it isn't mounted and it ensures that
216  *	 everything is unmounted properly on cleanup if the state
217  *	 changes.
218  */
219 void
220 DoWorkerUnmounts(worker_t *work)
221 {
222 	int retries;
223 
224 	work->mount_error = 0;
225 	for (retries = 0; retries < 10; ++retries) {
226 		dounmount(work, "/proc");
227 		dounmount(work, "/dev");
228 		dounmount(work, "/usr/src");
229 		dounmount(work, "/usr/games");
230 		dounmount(work, "/boot/modules.local");
231 		dounmount(work, "/boot");
232 		dounmount(work, "/usr/local");
233 		dounmount(work, "/construction");
234 		dounmount(work, "/ccache");	/* in case of config change */
235 		dounmount(work, "/distfiles");
236 		dounmount(work, "/packages");
237 		dounmount(work, "/options");
238 		dounmount(work, "/xports");
239 		dounmount(work, "/usr/share");
240 		dounmount(work, "/usr/sbin");
241 		dounmount(work, "/usr/libexec");
242 		dounmount(work, "/usr/libdata");
243 		dounmount(work, "/usr/lib");
244 		dounmount(work, "/usr/include");
245 		dounmount(work, "/usr/bin");
246 		dounmount(work, "/libexec");
247 		dounmount(work, "/lib");
248 		dounmount(work, "/sbin");
249 		dounmount(work, "/bin");
250 		dounmount(work, "");
251 		if (work->mount_error == 0)
252 			break;
253 		sleep(5);
254 		work->mount_error = 0;
255 	}
256 	if (work->mount_error) {
257 		++work->accum_error;
258 		snprintf(work->status, sizeof(work->status),
259 			 "Unable to unmount slot");
260 	}
261 }
262 
263 static
264 void
265 domount(worker_t *work, int type, const char *spath, const char *dpath,
266 	const char *discretefmt)
267 {
268 	const char *prog;
269 	const char *sbase;
270 	const char *rwstr;
271 	const char *optstr;
272 	struct stat st;
273 	char *buf;
274 	char *tmp;
275 	int rc;
276 
277 	/*
278 	 * Make target directory if necessary.  This must occur in-order
279 	 * since directories may have to be created under prior mounts
280 	 * in the sequence.
281 	 */
282 	asprintf(&buf, "%s%s", work->basedir, dpath);
283 	if (stat(buf, &st) != 0) {
284 		if (mkdir(buf, 0755) != 0) {
285 			fprintf(stderr, "Command failed: mkdir %s\n", buf);
286 			++work->mount_error;
287 		}
288 	}
289 	free(buf);
290 
291 	/*
292 	 * Setup for mount arguments
293 	 */
294 	rwstr = (type & MOUNT_TYPE_RW) ? "rw" : "ro";
295 	optstr = "";
296 
297 	switch(type & MOUNT_TYPE_MASK) {
298 	case MOUNT_TYPE_TMPFS:
299 		prog = MOUNT_TMPFS_BINARY;
300 		if (type & MOUNT_TYPE_BIG)
301 			optstr = " -s 64g";
302 		else
303 			optstr = " -s 16g";
304 		break;
305 	case MOUNT_TYPE_NULLFS:
306 		prog = MOUNT_NULLFS_BINARY;
307 		break;
308 	case MOUNT_TYPE_DEVFS:
309 		prog = MOUNT_DEVFS_BINARY;
310 		break;
311 	case MOUNT_TYPE_PROCFS:
312 		prog = MOUNT_PROCFS_BINARY;
313 		break;
314 	default:
315 		dfatal("Illegal mount type: %08x", type);
316 		/* NOT REACHED */
317 		prog = "/bin/hell";
318 		break;
319 	}
320 
321 	/*
322 	 * Prefix spath
323 	 */
324 	if (discretefmt) {
325 		sbase = BuildBase;
326 		asprintf(&tmp, discretefmt, work->index);
327 		spath = tmp;
328 	} else {
329 		if (spath[0] == '$') {
330 			++spath;
331 			sbase = SystemPath;
332 			if (strcmp(sbase, "/") == 0)
333 				++sbase;
334 		} else {
335 			sbase = "";
336 		}
337 		tmp = NULL;
338 	}
339 	asprintf(&buf, "%s%s -o %s %s%s %s%s",
340 		 prog, optstr, rwstr,
341 		 sbase, spath, work->basedir, dpath);
342 	rc = system(buf);
343 	if (rc) {
344 		fprintf(stderr, "Command failed: %s\n", buf);
345 		++work->mount_error;
346 	}
347 	free(buf);
348 	if (tmp)
349 		free(tmp);
350 }
351 
352 static
353 void
354 dounmount(worker_t *work, const char *rpath)
355 {
356 	char *buf;
357 
358 	asprintf(&buf, "%s%s", work->basedir, rpath);
359 	if (unmount(buf, 0) < 0) {
360 		switch(errno) {
361 		case EPERM:	/* This is probably fatal later on in mount */
362 		case ENOENT:	/* Expected if mount already gone */
363 		case EINVAL:	/* Expected if mount already gone (maybe) */
364 			break;
365 		default:
366 			fprintf(stderr, "Cannot umount %s (%s)\n",
367 				buf, strerror(errno));
368 			++work->mount_error;
369 			break;
370 		}
371 	}
372 	free(buf);
373 }
374 
375 static
376 void
377 makeDiscreteCopies(const char *spath, const char *discretefmt)
378 {
379 	char *src;
380 	char *dst;
381 	char *buf;
382 	struct stat st;
383 	int i;
384 	int rc;
385 
386 	for (i = 0; i < MaxWorkers; ++i) {
387 		if (spath[0] == '$') {
388 			if (strcmp(SystemPath, "/") == 0)
389 				asprintf(&src, "%s%s",
390 					 SystemPath + 1, spath + 1);
391 			else
392 				asprintf(&src, "%s%s",
393 					 SystemPath, spath + 1);
394 		} else {
395 			src = strdup(spath);
396 		}
397 		asprintf(&buf, discretefmt, i);
398 		asprintf(&dst, "%s%s", BuildBase, buf);
399 		free(buf);
400 
401 		if (stat(dst, &st) < 0) {
402 			if (mkdir(dst, 0555) < 0) {
403 				dlog(DLOG_ALL, "Template - mkdir %s failed\n",
404 				     dst);
405 				dfatal_errno("Cannot mkdir %s:", dst);
406 			}
407 		}
408 		asprintf(&buf, "chflags -R noschg %s; "
409 			       "rm -rf %s; "
410 			       "cp -Rp %s/. %s",
411 			       dst, dst, src, dst);
412 		rc = system(buf);
413 		dlog(DLOG_ALL | DLOG_FILTER,
414 		     "Template - rc=%d running %s\n", rc, buf);
415 		if (rc)
416 			dfatal("Command failed: %s", buf);
417 		free(buf);
418 		free(src);
419 		free(dst);
420 	}
421 }
422