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