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 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 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 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 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 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 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 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 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