#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <sys/filio.h>
#include <sys/mnttab.h>
#include <sys/mntent.h>
#include <sys/mount.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/fs/ufs_mount.h>
#include <sys/fs/ufs_log.h>
#include <libintl.h>
#include "roll_log.h"
#define RLG_TEMPLATE ".rlg.XXXXXX"
#define SYSERR (-1)
#define RLM_RW 0
#define RLM_RO 1
typedef struct log_info {
char *li_blkname;
char *li_mntpoint;
char *li_tmpmp_parent;
char *li_tmpmp;
} log_info_t;
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);
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 {
rv = make_mp(&li);
switch (rv) {
case RL_CORRUPT:
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 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;
}
}
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;
lip->li_blkname = getfullblkname(dev);
if (lip->li_blkname == NULL)
lip->li_blkname = strdup(dev);
if ((mnttable = fopen(MNTTAB, "r")) == NULL)
return (rv);
while (getmntent(mnttable, &mntbuf) == 0) {
if (strcmp(mntbuf.mnt_fstype, MNTTYPE_UFS) == 0) {
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);
}
static rl_result_t
make_mp(log_info_t *lip)
{
size_t i;
rl_result_t rv = RL_FAIL;
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;
(void) mktemp(dirname);
for (i = 0; i < list_len; i++) {
(void) snprintf(tmp_dir, sizeof (tmp_dir), "%s%s",
tmp_dir_list[i], dirname);
if (mkdir(tmp_dir, 0) == SYSERR) {
merr = errno;
continue;
}
(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);
if ((lip->li_tmpmp != NULL) && (lip->li_tmpmp_parent != NULL)) {
rv = RL_SUCCESS;
}
break;
}
if (rv != RL_SUCCESS) {
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);
}
static rl_result_t
rlflush(log_info_t *lip)
{
int fd;
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);
}
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;
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);
}
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);
}
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) {
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);
}