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