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