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
rl_roll_log(char * bdev)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
cleanup(log_info_t * lip)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
is_mounted(log_info_t * lip,char * dev)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
make_mp(log_info_t * lip)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
rlflush(log_info_t * lip)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
rlmount(log_info_t * lip,int mntopt)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
rlumount(log_info_t * lip)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
rl_log_control(char * bdev,int request)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