xref: /dragonfly/usr.bin/dsynth/mount.c (revision abf903a5)
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 	int n;
60 
61 	rc = 0;
62 	asprintf(&goodbuf, "%s/.template.good", BuildBase);
63 
64 	/*
65 	 * Conditionally create the template and discrete copies of certain
66 	 * directories if we think we are missing things.
67 	 */
68 	if (force == 0) {
69 		time_t ls_mtime;
70 
71 		asprintf(&buf, "%s/Template", BuildBase);
72 		if (stat(buf, &st) < 0)
73 			force = 1;
74 		free(buf);
75 
76 		/*
77 		 * Check to see if the worker count changed and some
78 		 * template dirs are missing or out of date, and also if
79 		 * a new world was installed (via /bin/ls mtime).
80 		 */
81 		asprintf(&buf, "%s/bin/ls", SystemPath);
82 		if (stat(buf, &st) < 0)
83 			dfatal_errno("Unable to locate %s", buf);
84 		free(buf);
85 		ls_mtime = st.st_mtime;
86 
87 		for (n = 0; n < MaxWorkers; ++n) {
88 			asprintf(&buf, "%s/bin.%03d/ls", BuildBase, n);
89 			if (stat(buf, &st) < 0 || st.st_mtime != ls_mtime)
90 				force = 1;
91 			free(buf);
92 		}
93 
94 		if (stat(goodbuf, &st) < 0)
95 			force = 1;
96 	}
97 
98 	dlog(DLOG_ALL, "Check Template: %s\n",
99 	     (force ? "Must-Create" : "Good"));
100 
101 	/*
102 	 * Create the template
103 	 */
104 	if (force) {
105 		remove(goodbuf);	/* ignore exit code */
106 
107 		rc = 0;
108 		asprintf(&buf, "%s/mktemplate %s %s/Template",
109 			 SCRIPTPATH(SCRIPTDIR), SystemPath, BuildBase);
110 		rc = system(buf);
111 		if (rc)
112 			dfatal("Command failed: %s\n", buf);
113 		dlog(DLOG_ALL | DLOG_FILTER,
114 		     "Template - rc=%d running %s\n", rc, buf);
115 		free(buf);
116 
117 		/*
118 		 * Make discrete copies of certain extremely heavily used
119 		 * but small directories.
120 		 */
121 		makeDiscreteCopies("$/bin", "/bin.%03d");
122 		makeDiscreteCopies("$/lib", "/lib.%03d");
123 		makeDiscreteCopies("$/libexec", "/libexec.%03d");
124 		makeDiscreteCopies("$/usr/bin", "/usr.bin.%03d");
125 
126 		/*
127 		 * Mark the template good... ah, do a sync() to really
128 		 * be sure that it can't get corrupted.
129 		 */
130 		sync();
131 		fd = open(goodbuf, O_RDWR|O_CREAT|O_TRUNC, 0644);
132 		dassert_errno(fd >= 0, "could not create %s", goodbuf);
133 		close(fd);
134 
135 		dlog(DLOG_ALL | DLOG_FILTER, "Template - done\n");
136 	}
137 	free(goodbuf);
138 
139 	return force;
140 }
141 
142 void
143 DoDestroyTemplate(void)
144 {
145 	struct stat st;
146 	char *path;
147 	char *buf;
148 	int rc;
149 
150 	/*
151 	 * NOTE: rm -rf safety, use a fixed name 'Template' to ensure we
152 	 *	 do not accidently blow something up.
153 	 */
154 	asprintf(&path, "%s/Template", BuildBase);
155 	if (stat(path, &st) == 0) {
156 		asprintf(&buf, "chflags -R noschg %s; /bin/rm -rf %s",
157 			 path, path);
158 		rc = system(buf);
159 		if (rc)
160 			dfatal("Command failed: %s (ignored)\n", buf);
161 		free(buf);
162 	}
163 	free(path);
164 }
165 
166 /*
167  * Called by the worker support thread to install a new worker
168  * filesystem topology.
169  */
170 void
171 DoWorkerMounts(worker_t *work)
172 {
173 	char *buf;
174 	int rc;
175 
176 	/*
177 	 * Generate required mounts, domount() will mkdir() the target
178 	 * directory if necessary and prefix spath with SystemPath if
179 	 * it starts with $/
180 	 */
181 	domount(work, TMPFS_RW, "dummy", "", NULL);
182 	asprintf(&buf, "%s/usr", work->basedir);
183 	if (mkdir(buf, 0755) != 0) {
184 		fprintf(stderr, "Command failed: mkdir %s\n", buf);
185 		++work->mount_error;
186 	}
187 	domount(work, NULLFS_RO, "$/boot", "/boot", NULL);
188 	domount(work, TMPFS_RW,  "dummy", "/boot/modules.local", NULL);
189 	domount(work, DEVFS_RW,  "dummy", "/dev", NULL);
190 	domount(work, PROCFS_RO, "dummy", "/proc", NULL);
191 	domount(work, NULLFS_RO, "$/bin", "/bin", "/bin.%03d");
192 	domount(work, NULLFS_RO, "$/sbin", "/sbin", NULL);
193 	domount(work, NULLFS_RO, "$/lib", "/lib", "/lib.%03d");
194 	domount(work, NULLFS_RO, "$/libexec", "/libexec", "/libexec.%03d");
195 	domount(work, NULLFS_RO, "$/usr/bin", "/usr/bin", "/usr.bin.%03d");
196 	domount(work, NULLFS_RO, "$/usr/include", "/usr/include", NULL);
197 	domount(work, NULLFS_RO, "$/usr/lib", "/usr/lib", NULL);
198 	domount(work, NULLFS_RO, "$/usr/libdata", "/usr/libdata", NULL);
199 	domount(work, NULLFS_RO, "$/usr/libexec", "/usr/libexec", NULL);
200 	domount(work, NULLFS_RO, "$/usr/sbin", "/usr/sbin", NULL);
201 	domount(work, NULLFS_RO, "$/usr/share", "/usr/share", NULL);
202 	domount(work, TMPFS_RW,  "dummy", "/usr/local", NULL);
203 	domount(work, NULLFS_RO, "$/usr/games", "/usr/games", NULL);
204 	if (UseUsrSrc)
205 		domount(work, NULLFS_RO, "$/usr/src", "/usr/src", NULL);
206 	domount(work, NULLFS_RO, DPortsPath, "/xports", NULL);
207 	domount(work, NULLFS_RW, OptionsPath, "/options", NULL);
208 	domount(work, NULLFS_RW, PackagesPath, "/packages", NULL);
209 	domount(work, NULLFS_RW, DistFilesPath, "/distfiles", NULL);
210 	domount(work, TMPFS_RW_BIG, "dummy", "/construction", NULL);
211 	if (UseCCache)
212 		domount(work, NULLFS_RW, CCachePath, "/ccache", NULL);
213 
214 	/*
215 	 * NOTE: Uses blah/. to prevent cp from creating 'Template' under
216 	 *	 work->basedir.  We want to start with the content.
217 	 */
218 	asprintf(&buf, "cp -Rp %s/Template/. %s", BuildBase, work->basedir);
219 	rc = system(buf);
220 	if (rc) {
221 		fprintf(stderr, "Command failed: %s\n", buf);
222 		++work->accum_error;
223 		snprintf(work->status, sizeof(work->status),
224 			 "Template copy failed");
225 	}
226 	free(buf);
227 }
228 
229 /*
230  * Called by the worker support thread to remove a worker
231  * filesystem topology.
232  *
233  * NOTE: No need to conditionalize UseUsrSrc, it doesn't hurt to
234  *	 issue the umount() if it isn't mounted and it ensures that
235  *	 everything is unmounted properly on cleanup if the state
236  *	 changes.
237  */
238 void
239 DoWorkerUnmounts(worker_t *work)
240 {
241 	int retries;
242 
243 	work->mount_error = 0;
244 	for (retries = 0; retries < 10; ++retries) {
245 		dounmount(work, "/proc");
246 		dounmount(work, "/dev");
247 		dounmount(work, "/usr/src");
248 		dounmount(work, "/usr/games");
249 		dounmount(work, "/boot/modules.local");
250 		dounmount(work, "/boot");
251 		dounmount(work, "/usr/local");
252 		dounmount(work, "/construction");
253 		dounmount(work, "/ccache");	/* in case of config change */
254 		dounmount(work, "/distfiles");
255 		dounmount(work, "/packages");
256 		dounmount(work, "/options");
257 		dounmount(work, "/xports");
258 		dounmount(work, "/usr/share");
259 		dounmount(work, "/usr/sbin");
260 		dounmount(work, "/usr/libexec");
261 		dounmount(work, "/usr/libdata");
262 		dounmount(work, "/usr/lib");
263 		dounmount(work, "/usr/include");
264 		dounmount(work, "/usr/bin");
265 		dounmount(work, "/libexec");
266 		dounmount(work, "/lib");
267 		dounmount(work, "/sbin");
268 		dounmount(work, "/bin");
269 		dounmount(work, "");
270 		if (work->mount_error == 0)
271 			break;
272 		sleep(5);
273 		work->mount_error = 0;
274 	}
275 	if (work->mount_error) {
276 		++work->accum_error;
277 		snprintf(work->status, sizeof(work->status),
278 			 "Unable to unmount slot");
279 	}
280 }
281 
282 static
283 void
284 domount(worker_t *work, int type, const char *spath, const char *dpath,
285 	const char *discretefmt)
286 {
287 	const char *prog;
288 	const char *sbase;
289 	const char *rwstr;
290 	const char *optstr;
291 	struct stat st;
292 	char *buf;
293 	char *tmp;
294 	int rc;
295 
296 	/*
297 	 * Make target directory if necessary.  This must occur in-order
298 	 * since directories may have to be created under prior mounts
299 	 * in the sequence.
300 	 */
301 	asprintf(&buf, "%s%s", work->basedir, dpath);
302 	if (stat(buf, &st) != 0) {
303 		if (mkdir(buf, 0755) != 0) {
304 			fprintf(stderr, "Command failed: mkdir %s\n", buf);
305 			++work->mount_error;
306 		}
307 	}
308 	free(buf);
309 
310 	/*
311 	 * Setup for mount arguments
312 	 */
313 	rwstr = (type & MOUNT_TYPE_RW) ? "rw" : "ro";
314 	optstr = "";
315 
316 	switch(type & MOUNT_TYPE_MASK) {
317 	case MOUNT_TYPE_TMPFS:
318 		prog = MOUNT_TMPFS_BINARY;
319 		if (type & MOUNT_TYPE_BIG)
320 			optstr = " -s 64g";
321 		else
322 			optstr = " -s 16g";
323 		break;
324 	case MOUNT_TYPE_NULLFS:
325 		prog = MOUNT_NULLFS_BINARY;
326 		break;
327 	case MOUNT_TYPE_DEVFS:
328 		prog = MOUNT_DEVFS_BINARY;
329 		break;
330 	case MOUNT_TYPE_PROCFS:
331 		prog = MOUNT_PROCFS_BINARY;
332 		break;
333 	default:
334 		dfatal("Illegal mount type: %08x", type);
335 		/* NOT REACHED */
336 		prog = "/bin/hell";
337 		break;
338 	}
339 
340 	/*
341 	 * Prefix spath
342 	 */
343 	if (discretefmt) {
344 		sbase = BuildBase;
345 		asprintf(&tmp, discretefmt, work->index);
346 		spath = tmp;
347 	} else {
348 		if (spath[0] == '$') {
349 			++spath;
350 			sbase = SystemPath;
351 			if (strcmp(sbase, "/") == 0)
352 				++sbase;
353 		} else {
354 			sbase = "";
355 		}
356 		tmp = NULL;
357 	}
358 	asprintf(&buf, "%s%s -o %s %s%s %s%s",
359 		 prog, optstr, rwstr,
360 		 sbase, spath, work->basedir, dpath);
361 	rc = system(buf);
362 	if (rc) {
363 		fprintf(stderr, "Command failed: %s\n", buf);
364 		++work->mount_error;
365 	}
366 	free(buf);
367 	if (tmp)
368 		free(tmp);
369 }
370 
371 static
372 void
373 dounmount(worker_t *work, const char *rpath)
374 {
375 	char *buf;
376 
377 	asprintf(&buf, "%s%s", work->basedir, rpath);
378 	if (unmount(buf, 0) < 0) {
379 		switch(errno) {
380 		case EPERM:	/* This is probably fatal later on in mount */
381 		case ENOENT:	/* Expected if mount already gone */
382 		case EINVAL:	/* Expected if mount already gone (maybe) */
383 			break;
384 		default:
385 			fprintf(stderr, "Cannot umount %s (%s)\n",
386 				buf, strerror(errno));
387 			++work->mount_error;
388 			break;
389 		}
390 	}
391 	free(buf);
392 }
393 
394 static
395 void
396 makeDiscreteCopies(const char *spath, const char *discretefmt)
397 {
398 	char *src;
399 	char *dst;
400 	char *buf;
401 	struct stat st;
402 	int i;
403 	int rc;
404 
405 	for (i = 0; i < MaxWorkers; ++i) {
406 		if (spath[0] == '$') {
407 			if (strcmp(SystemPath, "/") == 0)
408 				asprintf(&src, "%s%s",
409 					 SystemPath + 1, spath + 1);
410 			else
411 				asprintf(&src, "%s%s",
412 					 SystemPath, spath + 1);
413 		} else {
414 			src = strdup(spath);
415 		}
416 		asprintf(&buf, discretefmt, i);
417 		asprintf(&dst, "%s%s", BuildBase, buf);
418 		free(buf);
419 
420 		if (stat(dst, &st) < 0) {
421 			if (mkdir(dst, 0555) < 0) {
422 				dlog(DLOG_ALL, "Template - mkdir %s failed\n",
423 				     dst);
424 				dfatal_errno("Cannot mkdir %s:", dst);
425 			}
426 		}
427 		asprintf(&buf, "chflags -R noschg %s; "
428 			       "rm -rf %s; "
429 			       "cp -Rp %s/. %s",
430 			       dst, dst, src, dst);
431 		rc = system(buf);
432 		dlog(DLOG_ALL | DLOG_FILTER,
433 		     "Template - rc=%d running %s\n", rc, buf);
434 		if (rc)
435 			dfatal("Command failed: %s", buf);
436 		free(buf);
437 		free(src);
438 		free(dst);
439 	}
440 }
441