/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" /* * This file contains functions that allow applications to roll the log. * It is intended for use by applications that open a raw device with the * understanding that it contains a Unix File System. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "roll_log.h" /* * The following is the template string passed to mktemp(3C). This * string is used as the name of a temporary mount point which is * used to roll the log. */ #define RLG_TEMPLATE ".rlg.XXXXXX" #define SYSERR (-1) #define RLM_RW 0 #define RLM_RO 1 /* * Structure definitions: */ typedef struct log_info { char *li_blkname; /* Path of block device. */ char *li_mntpoint; /* Path of mounted device. */ char *li_tmpmp_parent; /* Temporary parent directory of mount point */ char *li_tmpmp; /* Temporary mount point. */ } log_info_t; /* * Static function declarations: */ static rl_result_t is_mounted(log_info_t *lip, char *dev); static void cleanup(log_info_t *lip); static rl_result_t make_mp(log_info_t *lip); static rl_result_t rlflush(log_info_t *lip); static rl_result_t rlmount(log_info_t *lip, int mntopt); static rl_result_t rlumount(log_info_t *lip); /* * NAME * rl_roll_log * * SYNOPSIS * rl_roll_log(block_dev) * * DESCRIPTION * Roll the log for the block device "block_dev". */ rl_result_t rl_roll_log(char *bdev) { log_info_t li; rl_result_t rv = RL_SUCCESS; (void) memset((void *)&li, 0, (size_t)sizeof (li)); if (is_mounted(&li, bdev) == RL_TRUE) { rv = rlflush(&li); } else { /* * Device appears not to be mounted. * We need to mount the device read only. * This automatically causes the log to be rolled, then we can * unmount the device again. To do the mount, we need to * create a temporary directory, and then remove it when we * are done. */ rv = make_mp(&li); switch (rv) { case RL_CORRUPT: /* corrupt mnttab - the file sys really was mounted */ rv = rlflush(&li); break; case RL_SUCCESS: rv = rlmount(&li, RLM_RO); if (rv == RL_SUCCESS) { rv = rlflush(&li); if (umount(li.li_blkname) == SYSERR) { (void) fprintf(stderr, "WARNING: rl_roll_log(): Can't unmount %s\n", li.li_blkname); } } break; } } cleanup(&li); return (rv); } /* * Static function definitions: */ /* * NAME * cleanup * * SYNOPSIS * cleanup(log_infop) * * DESCRIPTION * Remove the temporary mount directroy and free the dynamically * allocated memory that is pointed to by log_infop. */ static void cleanup(log_info_t *lip) { if (lip->li_blkname != (char *)NULL) { free(lip->li_blkname); lip->li_blkname = (char *)NULL; } if (lip->li_mntpoint != (char *)NULL) { free(lip->li_mntpoint); lip->li_mntpoint = (char *)NULL; } if (lip->li_tmpmp != (char *)NULL) { (void) rmdir(lip->li_tmpmp); free(lip->li_tmpmp); lip->li_tmpmp = (char *)NULL; } if (lip->li_tmpmp_parent != (char *)NULL) { (void) rmdir(lip->li_tmpmp_parent); free(lip->li_tmpmp_parent); lip->li_tmpmp_parent = (char *)NULL; } } /* * NAME * is_mounted * * SYNOPSIS * is_mounted(log_infop, dev) * * DESCRIPTION * Determine if device dev is mounted, and return RL_TRUE if it is. * As a side effect, li_blkname is set to point the the full path * names of the block device. Memory for this path is dynamically * allocated and must be freed by the caller. */ extern char *getfullblkname(char *); static rl_result_t is_mounted(log_info_t *lip, char *dev) { struct mnttab mntbuf; FILE *mnttable; rl_result_t rv = RL_FALSE; /* Make sure that we have the full path name. */ lip->li_blkname = getfullblkname(dev); if (lip->li_blkname == NULL) lip->li_blkname = strdup(dev); /* Search mnttab to see if it device is mounted. */ if ((mnttable = fopen(MNTTAB, "r")) == NULL) return (rv); while (getmntent(mnttable, &mntbuf) == NULL) { if (strcmp(mntbuf.mnt_fstype, MNTTYPE_UFS) == 0) { /* Entry is UFS */ if ((strcmp(mntbuf.mnt_mountp, dev) == 0) || (strcmp(mntbuf.mnt_special, lip->li_blkname) == 0) || (strcmp(mntbuf.mnt_special, dev) == 0)) { lip->li_mntpoint = strdup(mntbuf.mnt_mountp); rv = RL_TRUE; break; } } } (void) fclose(mnttable); return (rv); } /* * NAME * make_mp * * SYNOPSIS * make_mp(loginfop) * * DESCRIPTION * Create a temporary directory to be used as a mount point. li_tmpmp * will be set to the path of the mount point. li_tmpmp_parent is the * parent directory of the mount point. The parent directory is * created with restrictive permissions. Memory pointed to by * li_tmpmp and li_tmpmp_parent should be freed by the caller. */ static rl_result_t make_mp(log_info_t *lip) { size_t i; rl_result_t rv = RL_FAIL; /* * Note tmp_dir_list[] should all be directories in the * original root file system. */ static const char *tmp_dir_list[] = { "/tmp/", "/var/tmp/", "/", }; char dirname[] = RLG_TEMPLATE; char tmp_dir[MAXPATHLEN + 1]; char mountpt_dir[MAXPATHLEN + 1]; static size_t list_len = sizeof (tmp_dir_list) / sizeof (const char *); int merr = 0; /* * Sequence of events: * - Create a random name using mktemp(3C) (e.g., ".rlg.123456") * - Cycle through tmp_dir_list to find a path where we can create * a temporary parent directory (e.g., /tmp/.rlg.123456) with * restrictive permissions. This prevents any non-root processes, * such as a 'find', from wandering in where it doesn't belong. * - Create the mount-point (/tmp/.rlg.123456/.rlg.123456). */ (void) mktemp(dirname); for (i = 0; i < list_len; i++) { /* Make the directory containing the mount-point */ (void) snprintf(tmp_dir, sizeof (tmp_dir), "%s%s", tmp_dir_list[i], dirname); if (mkdir(tmp_dir, 0) == SYSERR) { merr = errno; continue; } /* Now, make the mount-point */ (void) snprintf(mountpt_dir, sizeof (mountpt_dir), "%s/%s", tmp_dir, dirname); if (mkdir(mountpt_dir, 0) == SYSERR) { merr = errno; continue; } lip->li_tmpmp = strdup(mountpt_dir); lip->li_tmpmp_parent = strdup(tmp_dir); /* Make sure that the strdup()s both succeeded */ if ((lip->li_tmpmp != NULL) && (lip->li_tmpmp_parent != NULL)) { rv = RL_SUCCESS; } break; } /* Get some help if we cannot make the directory. */ if (rv != RL_SUCCESS) { /* * If we get a read only filesystem failure (EROFS) * to make a directory in "/", then we must be fsck'ing * at boot with a incorrect mnttab. * * Just return RL_CORRUPT to indicate it really * was mounted. */ if (merr == EROFS) { lip->li_mntpoint = strdup("/"); return (RL_CORRUPT); } (void) fprintf(stderr, gettext( "Unable to create temporary " "directory in any of the directories listed " "below:\n")); for (i = 0; i < list_len; i++) { (void) fprintf(stderr, "\t%s\n", tmp_dir_list[i]); } (void) fprintf(stderr, gettext( "Please correct this problem " "and rerun the program.\n")); } return (rv); } /* * NAME * rlflush * * SYNOPSIS * rlflush(log_infop) * * DESCRIPTION * Open the mount point of the file system (li_mntpoint) to get a * file descriptor. Issue the _FIOFFS ioctl to flush the file system * and then close the device. */ static rl_result_t rlflush(log_info_t *lip) { int fd; /* File descriptor. */ rl_result_t rv = RL_SUCCESS; if ((fd = open((lip->li_mntpoint ? lip->li_mntpoint : lip->li_tmpmp), O_RDONLY)) == SYSERR) { return (RL_SYSERR); } if (ioctl(fd, _FIOFFS, NULL) == SYSERR) { rv = RL_SYSERR; } (void) close(fd); return (rv); } /* * NAME * rlmount * * SYNOPSIS * rlmount(log_infop, mntopt) * * DESCRIPTION * Mount the device specified by li_blkname on li_tmpmp. mntopt specifies * whether it's mounted RLM_RO or RLM_RW. */ static rl_result_t rlmount(log_info_t *lip, int mntopt) { struct ufs_args args; rl_result_t rv = RL_SUCCESS; char opt[MAX_MNTOPT_STR]; char *optstr; int optflg; args.flags = 0; /* Initialize ufs_args */ /* * Use a minimal restrictive set of mount options. Make sure * to use "largefiles" option otherwise mount() can fail w/EFBIG. * (Although "nosub" isn't a currently supported option on UFS, * it would be a good one to have if it ever is implemented * since submounts would prevent a umount.) */ args.flags |= UFSMNT_LARGEFILES; switch (mntopt) { case RLM_RO: optstr = MNTOPT_RO; optflg = MS_RDONLY; break; case RLM_RW: optstr = MNTOPT_RW; optflg = 0; break; default: return (RL_FAIL); } (void) snprintf(opt, sizeof (opt), "%s,%s,%s", optstr, MNTOPT_NOSUID, MNTOPT_LARGEFILES); if (mount(lip->li_blkname, lip->li_tmpmp, optflg | MS_DATA | MS_OPTIONSTR, MNTTYPE_UFS, &args, sizeof (args), opt, MAX_MNTOPT_STR) == SYSERR) { rv = RL_SYSERR; } return (rv); } /* * NAME * rlumount * * SYNOPSIS * rlumount(log_infop) * * DESCRIPTION * Unmounts the device specified by li_blkname, printing an * error message on failure. */ static rl_result_t rlumount(log_info_t *lip) { rl_result_t rv = RL_SUCCESS; if (umount(lip->li_blkname) == SYSERR) { (void) fprintf(stderr, gettext( "WARNING: rlumount(): Can't unmount %s\n"), lip->li_blkname); rv = RL_SYSERR; } return (rv); } /* * NAME * rl_log_control * * SYNOPSIS * rl_log_control(block_dev, request) * * DESCRIPTION * Enable/disable logging for the block device "block_dev". * The request parameter should be set to _FIOLOGENABLE or * _FIOLOGDISABLE. */ rl_result_t rl_log_control(char *bdev, int request) { log_info_t li; rl_result_t rv = RL_SUCCESS; rl_result_t alreadymounted; int fd; fiolog_t fl; int logenabled = 0; if ((request != _FIOLOGENABLE) && (request != _FIOLOGDISABLE)) return (RL_FAIL); (void) memset((void *)&li, '\0', (size_t)sizeof (li)); if ((alreadymounted = is_mounted(&li, bdev)) != RL_TRUE) { /* * Device is not mounted. Need to mount it rw to allow * the log to be enabled/disabled. To do the mount, we need * to create a temporary directory, and then remove it when * we are done. */ if (make_mp(&li) != RL_SUCCESS) { cleanup(&li); return (RL_FAIL); } if (rlmount(&li, RLM_RW) != RL_SUCCESS) { cleanup(&li); return (RL_FAIL); } } if (alreadymounted == RL_TRUE) fd = open(li.li_mntpoint, O_RDONLY); else fd = open(li.li_tmpmp, O_RDONLY); if (fd == SYSERR) { perror("open"); rv = RL_SYSERR; goto out; } fl.nbytes_requested = 0; fl.nbytes_actual = 0; fl.error = FIOLOG_ENONE; if (ioctl(fd, request, &fl) == SYSERR) { perror("ioctl"); (void) close(fd); rv = RL_SYSERR; goto out; } if (ioctl(fd, _FIOISLOG, &logenabled) == SYSERR) { perror("ioctl"); (void) close(fd); rv = RL_SYSERR; goto out; } if (((request == _FIOLOGENABLE) && (!logenabled)) || ((request == _FIOLOGDISABLE) && logenabled)) rv = RL_FAIL; (void) close(fd); out: if (alreadymounted != RL_TRUE) (void) rlumount(&li); cleanup(&li); return (rv); }