xref: /illumos-gate/usr/src/cmd/fs.d/ufs/roll_log/roll_log.c (revision 948f2876ce2a3010558f4f6937e16086ebcd36f2)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * This file contains functions that allow applications to roll the log.
31  * It is intended for use by applications that open a raw device with the
32  * understanding that it contains a Unix File System.
33  */
34 
35 #include <errno.h>
36 #include <fcntl.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <strings.h>
41 #include <unistd.h>
42 #include <sys/filio.h>
43 #include <sys/mnttab.h>
44 #include <sys/mntent.h>
45 #include <sys/mount.h>
46 #include <sys/param.h>
47 #include <sys/types.h>
48 #include <sys/stat.h>
49 #include <sys/fs/ufs_mount.h>
50 #include <sys/fs/ufs_log.h>
51 #include <libintl.h>
52 #include "roll_log.h"
53 
54 /*
55  * The following is the template string passed to mktemp(3C).  This
56  * string is used as the name of a temporary mount point which is
57  * used to roll the log.
58  */
59 #define	RLG_TEMPLATE	".rlg.XXXXXX"
60 
61 #define	SYSERR		(-1)
62 
63 #define	RLM_RW		0
64 #define	RLM_RO		1
65 
66 /*
67  * Structure definitions:
68  */
69 
70 typedef struct log_info {
71 	char *li_blkname;	/* Path of block device. */
72 	char *li_mntpoint;	/* Path of mounted device. */
73 	char *li_tmpmp_parent;	/* Temporary parent directory of mount point */
74 	char *li_tmpmp;		/* Temporary mount point. */
75 } log_info_t;
76 
77 /*
78  * Static function declarations:
79  */
80 
81 static rl_result_t	is_mounted(log_info_t *lip, char *dev);
82 static void		cleanup(log_info_t *lip);
83 static rl_result_t	make_mp(log_info_t *lip);
84 static rl_result_t	rlflush(log_info_t *lip);
85 static rl_result_t	rlmount(log_info_t *lip, int mntopt);
86 static rl_result_t	rlumount(log_info_t *lip);
87 
88 /*
89  * NAME
90  *	rl_roll_log
91  *
92  * SYNOPSIS
93  *	rl_roll_log(block_dev)
94  *
95  * DESCRIPTION
96  *	Roll the log for the block device "block_dev".
97  */
98 
99 rl_result_t
100 rl_roll_log(char *bdev)
101 {
102 	log_info_t		li;
103 	rl_result_t		rv = RL_SUCCESS;
104 
105 	(void) memset((void *)&li, 0, (size_t)sizeof (li));
106 	if (is_mounted(&li, bdev) == RL_TRUE) {
107 		rv = rlflush(&li);
108 	} else {
109 		/*
110 		 * Device appears not to be mounted.
111 		 * We need to mount the device read only.
112 		 * This automatically causes the log to be rolled, then we can
113 		 * unmount the device again.  To do the mount, we need to
114 		 * create a temporary directory, and then remove it when we
115 		 * are done.
116 		 */
117 		rv = make_mp(&li);
118 		switch (rv) {
119 		case RL_CORRUPT:
120 			/* corrupt mnttab - the file sys really was mounted */
121 			rv = rlflush(&li);
122 			break;
123 		case RL_SUCCESS:
124 			rv = rlmount(&li, RLM_RO);
125 			if (rv == RL_SUCCESS) {
126 				rv = rlflush(&li);
127 				if (umount(li.li_blkname) == SYSERR) {
128 					(void) fprintf(stderr,
129 		"WARNING: rl_roll_log(): Can't unmount %s\n", li.li_blkname);
130 				}
131 
132 			}
133 			break;
134 		}
135 	}
136 	cleanup(&li);
137 	return (rv);
138 }
139 
140 /*
141  * Static function definitions:
142  */
143 
144 /*
145  * NAME
146  *	cleanup
147  *
148  * SYNOPSIS
149  *	cleanup(log_infop)
150  *
151  * DESCRIPTION
152  *	Remove the temporary mount directroy and free the dynamically
153  *	allocated memory that is pointed to by log_infop.
154  */
155 
156 static void
157 cleanup(log_info_t *lip)
158 {
159 	if (lip->li_blkname != (char *)NULL) {
160 		free(lip->li_blkname);
161 		lip->li_blkname = (char *)NULL;
162 	}
163 	if (lip->li_mntpoint != (char *)NULL) {
164 		free(lip->li_mntpoint);
165 		lip->li_mntpoint = (char *)NULL;
166 	}
167 	if (lip->li_tmpmp != (char *)NULL) {
168 		(void) rmdir(lip->li_tmpmp);
169 		free(lip->li_tmpmp);
170 		lip->li_tmpmp = (char *)NULL;
171 	}
172 	if (lip->li_tmpmp_parent != (char *)NULL) {
173 		(void) rmdir(lip->li_tmpmp_parent);
174 		free(lip->li_tmpmp_parent);
175 		lip->li_tmpmp_parent = (char *)NULL;
176 	}
177 }
178 
179 /*
180  * NAME
181  *	is_mounted
182  *
183  * SYNOPSIS
184  *	is_mounted(log_infop, dev)
185  *
186  * DESCRIPTION
187  *	Determine if device dev is mounted, and return RL_TRUE if it is.
188  *	As a side effect, li_blkname is set to point the the full path
189  *	names of the block device.  Memory for this path is dynamically
190  *	allocated and must be freed by the caller.
191  */
192 
193 extern char *getfullblkname(char *);
194 
195 static rl_result_t
196 is_mounted(log_info_t *lip, char *dev)
197 {
198 
199 	struct mnttab		mntbuf;
200 	FILE			*mnttable;
201 	rl_result_t		rv = RL_FALSE;
202 
203 	/* Make sure that we have the full path name. */
204 	lip->li_blkname = getfullblkname(dev);
205 	if (lip->li_blkname == NULL)
206 		lip->li_blkname = strdup(dev);
207 
208 	/* Search mnttab to see if it device is mounted. */
209 	if ((mnttable = fopen(MNTTAB, "r")) == NULL)
210 		return (rv);
211 	while (getmntent(mnttable, &mntbuf) == NULL) {
212 		if (strcmp(mntbuf.mnt_fstype, MNTTYPE_UFS) == 0) {
213 			/* Entry is UFS */
214 			if ((strcmp(mntbuf.mnt_mountp, dev) == 0) ||
215 			    (strcmp(mntbuf.mnt_special, lip->li_blkname)
216 				== 0) ||
217 			    (strcmp(mntbuf.mnt_special, dev) == 0)) {
218 				lip->li_mntpoint = strdup(mntbuf.mnt_mountp);
219 				rv = RL_TRUE;
220 				break;
221 			}
222 		}
223 	}
224 	(void) fclose(mnttable);
225 
226 
227 	return (rv);
228 }
229 
230 /*
231  * NAME
232  *	make_mp
233  *
234  * SYNOPSIS
235  *	make_mp(loginfop)
236  *
237  * DESCRIPTION
238  *	Create a temporary directory to be used as a mount point.  li_tmpmp
239  *	will be set to the path of the mount point. li_tmpmp_parent is the
240  *	parent directory of the mount point.  The parent directory is
241  *	created with restrictive permissions.   Memory pointed to by
242  *	li_tmpmp and li_tmpmp_parent should be freed by the caller.
243  */
244 
245 static rl_result_t
246 make_mp(log_info_t *lip)
247 {
248 	size_t			i;
249 	rl_result_t		rv = RL_FAIL;
250 	/*
251 	 * Note tmp_dir_list[] should all be directories in the
252 	 * original root file system.
253 	 */
254 	static const char 	*tmp_dir_list[] = {
255 							"/tmp/",
256 							"/var/tmp/",
257 							"/",
258 						};
259 	char			dirname[] = RLG_TEMPLATE;
260 	char			tmp_dir[MAXPATHLEN + 1];
261 	char			mountpt_dir[MAXPATHLEN + 1];
262 	static size_t		list_len = sizeof (tmp_dir_list) /
263 						sizeof (const char *);
264 	int			merr = 0;
265 
266 	/*
267 	 * Sequence of events:
268 	 * - Create a random name using mktemp(3C) (e.g., ".rlg.123456")
269 	 * - Cycle through tmp_dir_list to find a path where we can create
270 	 *   a temporary parent directory (e.g., /tmp/.rlg.123456) with
271 	 *   restrictive permissions.  This prevents any non-root processes,
272 	 *   such as a 'find', from wandering in where it doesn't belong.
273 	 * - Create the mount-point (/tmp/.rlg.123456/.rlg.123456).
274 	 */
275 	(void) mktemp(dirname);
276 	for (i = 0; i < list_len; i++) {
277 		/* Make the directory containing the mount-point */
278 		(void) snprintf(tmp_dir, sizeof (tmp_dir), "%s%s",
279 				tmp_dir_list[i], dirname);
280 		if (mkdir(tmp_dir, 0) == SYSERR) {
281 			merr = errno;
282 			continue;
283 		}
284 
285 		/* Now, make the mount-point */
286 		(void) snprintf(mountpt_dir, sizeof (mountpt_dir), "%s/%s",
287 				tmp_dir, dirname);
288 		if (mkdir(mountpt_dir, 0) == SYSERR) {
289 			merr = errno;
290 			continue;
291 		}
292 		lip->li_tmpmp = strdup(mountpt_dir);
293 		lip->li_tmpmp_parent = strdup(tmp_dir);
294 
295 		/* Make sure that the strdup()s both succeeded */
296 		if ((lip->li_tmpmp != NULL) && (lip->li_tmpmp_parent != NULL)) {
297 			rv = RL_SUCCESS;
298 		}
299 		break;
300 	}
301 
302 	/* Get some help if we cannot make the directory. */
303 	if (rv != RL_SUCCESS) {
304 		/*
305 		 * If we get a read only filesystem failure (EROFS)
306 		 * to make a directory in "/", then we must be fsck'ing
307 		 * at boot with a incorrect mnttab.
308 		 *
309 		 * Just return RL_CORRUPT to indicate it really
310 		 * was mounted.
311 		 */
312 		if (merr == EROFS) {
313 			lip->li_mntpoint = strdup("/");
314 			return (RL_CORRUPT);
315 		}
316 
317 		(void) fprintf(stderr, gettext(
318 			"Unable to create temporary "
319 			"directory in any of the directories listed "
320 			"below:\n"));
321 		for (i = 0; i < list_len; i++) {
322 			(void) fprintf(stderr, "\t%s\n", tmp_dir_list[i]);
323 		}
324 		(void) fprintf(stderr, gettext(
325 			"Please correct this problem "
326 			"and rerun the program.\n"));
327 	}
328 
329 	return (rv);
330 }
331 
332 /*
333  * NAME
334  *	rlflush
335  *
336  * SYNOPSIS
337  *	rlflush(log_infop)
338  *
339  * DESCRIPTION
340  *	Open the mount point of the file system (li_mntpoint) to get a
341  *	file descriptor.  Issue the _FIOFFS ioctl to flush the file system
342  *	and then close the device.
343  */
344 
345 static rl_result_t
346 rlflush(log_info_t *lip)
347 {
348 	int			fd;	/* File descriptor. */
349 	rl_result_t		rv = RL_SUCCESS;
350 
351 	if ((fd = open((lip->li_mntpoint ? lip->li_mntpoint : lip->li_tmpmp),
352 		O_RDONLY)) == SYSERR) {
353 		return (RL_SYSERR);
354 	}
355 	if (ioctl(fd, _FIOFFS, NULL) == SYSERR) {
356 		rv = RL_SYSERR;
357 	}
358 	(void) close(fd);
359 	return (rv);
360 }
361 
362 /*
363  * NAME
364  *	rlmount
365  *
366  * SYNOPSIS
367  *	rlmount(log_infop, mntopt)
368  *
369  * DESCRIPTION
370  *	Mount the device specified by li_blkname on li_tmpmp. mntopt specifies
371  *	whether it's mounted RLM_RO or RLM_RW.
372  */
373 
374 static rl_result_t
375 rlmount(log_info_t *lip, int mntopt)
376 {
377 	struct ufs_args		args;
378 	rl_result_t		rv = RL_SUCCESS;
379 	char			opt[MAX_MNTOPT_STR];
380 	char			*optstr;
381 	int			optflg;
382 
383 	args.flags = 0;	/* Initialize ufs_args */
384 
385 	/*
386 	 * Use a minimal restrictive set of mount options.  Make sure
387 	 * to use "largefiles" option otherwise mount() can fail w/EFBIG.
388 	 * (Although "nosub" isn't a currently supported option on UFS,
389 	 * it would be a good one to have if it ever is implemented
390 	 * since submounts would prevent a umount.)
391 	 */
392 	args.flags |= UFSMNT_LARGEFILES;
393 	switch (mntopt) {
394 	case RLM_RO:
395 		optstr = MNTOPT_RO;
396 		optflg = MS_RDONLY;
397 		break;
398 	case RLM_RW:
399 		optstr = MNTOPT_RW;
400 		optflg = 0;
401 		break;
402 	default:
403 		return (RL_FAIL);
404 	}
405 	(void) snprintf(opt, sizeof (opt), "%s,%s,%s",
406 		optstr, MNTOPT_NOSUID, MNTOPT_LARGEFILES);
407 	if (mount(lip->li_blkname, lip->li_tmpmp,
408 			optflg | MS_DATA | MS_OPTIONSTR,
409 			MNTTYPE_UFS, &args, sizeof (args),
410 			opt, MAX_MNTOPT_STR) == SYSERR) {
411 		rv = RL_SYSERR;
412 	}
413 	return (rv);
414 }
415 
416 /*
417  * NAME
418  *	rlumount
419  *
420  * SYNOPSIS
421  *	rlumount(log_infop)
422  *
423  * DESCRIPTION
424  *	Unmounts the device specified by li_blkname, printing an
425  *	error message on failure.
426  */
427 
428 static rl_result_t
429 rlumount(log_info_t *lip)
430 {
431 	rl_result_t		rv = RL_SUCCESS;
432 
433 	if (umount(lip->li_blkname) == SYSERR) {
434 		(void) fprintf(stderr, gettext(
435 		"WARNING: rlumount(): Can't unmount %s\n"),
436 			lip->li_blkname);
437 		rv = RL_SYSERR;
438 	}
439 	return (rv);
440 }
441 
442 /*
443  * NAME
444  *	rl_log_control
445  *
446  * SYNOPSIS
447  *	rl_log_control(block_dev, request)
448  *
449  * DESCRIPTION
450  *	Enable/disable logging for the block device "block_dev".
451  *	The request parameter should be set to _FIOLOGENABLE or
452  *	_FIOLOGDISABLE.
453  */
454 
455 rl_result_t
456 rl_log_control(char *bdev, int request)
457 {
458 	log_info_t	li;
459 	rl_result_t	rv = RL_SUCCESS;
460 	rl_result_t	alreadymounted;
461 	int		fd;
462 	fiolog_t	fl;
463 	int		logenabled = 0;
464 
465 	if ((request != _FIOLOGENABLE) && (request != _FIOLOGDISABLE))
466 		return (RL_FAIL);
467 
468 	(void) memset((void *)&li, '\0', (size_t)sizeof (li));
469 	if ((alreadymounted = is_mounted(&li, bdev)) != RL_TRUE) {
470 		/*
471 		 * Device is not mounted. Need to mount it rw to allow
472 		 * the log to be enabled/disabled. To do the mount, we need
473 		 * to create a temporary directory, and then remove it when
474 		 * we are done.
475 		 */
476 		if (make_mp(&li) != RL_SUCCESS) {
477 			cleanup(&li);
478 			return (RL_FAIL);
479 		}
480 		if (rlmount(&li, RLM_RW) != RL_SUCCESS) {
481 			cleanup(&li);
482 			return (RL_FAIL);
483 		}
484 	}
485 
486 	if (alreadymounted == RL_TRUE)
487 		fd = open(li.li_mntpoint, O_RDONLY);
488 	else
489 		fd = open(li.li_tmpmp, O_RDONLY);
490 	if (fd == SYSERR) {
491 		perror("open");
492 		rv = RL_SYSERR;
493 		goto out;
494 	}
495 
496 	fl.nbytes_requested = 0;
497 	fl.nbytes_actual = 0;
498 	fl.error = FIOLOG_ENONE;
499 
500 	if (ioctl(fd, request, &fl) == SYSERR) {
501 		perror("ioctl");
502 		(void) close(fd);
503 		rv = RL_SYSERR;
504 		goto out;
505 	}
506 	if (ioctl(fd, _FIOISLOG, &logenabled) == SYSERR) {
507 		perror("ioctl");
508 		(void) close(fd);
509 		rv = RL_SYSERR;
510 		goto out;
511 	}
512 	if (((request == _FIOLOGENABLE) && (!logenabled)) ||
513 	    ((request == _FIOLOGDISABLE) && logenabled))
514 		rv = RL_FAIL;
515 
516 	(void) close(fd);
517 out:
518 	if (alreadymounted != RL_TRUE)
519 		(void) rlumount(&li);
520 	cleanup(&li);
521 	return (rv);
522 }
523