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 2004 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 #pragma ident "@(#)fslib.c 1.25 05/06/08 SMI"
28
29 #include <stdio.h>
30 #include <stdarg.h>
31 #include <stdlib.h>
32 #include <unistd.h>
33 #include <libintl.h>
34 #include <string.h>
35 #include <fcntl.h>
36 #include <errno.h>
37 #include <syslog.h>
38 #include <alloca.h>
39 #include <sys/vfstab.h>
40 #include <sys/mnttab.h>
41 #include <sys/mntent.h>
42 #include <sys/mount.h>
43 #include <sys/filio.h>
44 #include <sys/fs/ufs_filio.h>
45 #include <sys/stat.h>
46 #include <sys/param.h>
47 #include <zone.h>
48 #include <signal.h>
49 #include <strings.h>
50 #include "fslib.h"
51
52 /* LINTLIBRARY */
53
54 #define BUFLEN 256
55
56 #define TIME_MAX 16
57
58 /*
59 * Reads all of the entries from the in-kernel mnttab, and returns the
60 * linked list of the entries.
61 */
62 mntlist_t *
63 fsgetmntlist(void)
64 {
65 FILE *mfp;
66 mntlist_t *mntl;
67 char buf[BUFLEN];
68
69 if ((mfp = fopen(MNTTAB, "r")) == NULL) {
70 (void) snprintf(buf, BUFLEN, "fsgetmntlist: fopen %s", MNTTAB);
71 perror(buf);
72 return (NULL);
73 }
74
75 mntl = fsmkmntlist(mfp);
76
77 (void) fclose(mfp);
78 return (mntl);
79 }
80
81
82 static struct extmnttab zmnttab = { 0 };
83
84 struct extmnttab *
85 fsdupmnttab(struct extmnttab *mnt)
86 {
87 struct extmnttab *new;
88
89 new = (struct extmnttab *)malloc(sizeof (*new));
90 if (new == NULL)
91 goto alloc_failed;
92
93 *new = zmnttab;
94 /*
95 * Allocate an extra byte for the mountpoint
96 * name in case a space needs to be added.
97 */
98 new->mnt_mountp = (char *)malloc(strlen(mnt->mnt_mountp) + 2);
99 if (new->mnt_mountp == NULL)
100 goto alloc_failed;
101 (void) strcpy(new->mnt_mountp, mnt->mnt_mountp);
102
103 if ((new->mnt_special = strdup(mnt->mnt_special)) == NULL)
104 goto alloc_failed;
105
106 if ((new->mnt_fstype = strdup(mnt->mnt_fstype)) == NULL)
107 goto alloc_failed;
108
109 if (mnt->mnt_mntopts != NULL)
110 if ((new->mnt_mntopts = strdup(mnt->mnt_mntopts)) == NULL)
111 goto alloc_failed;
112
113 if (mnt->mnt_time != NULL)
114 if ((new->mnt_time = strdup(mnt->mnt_time)) == NULL)
115 goto alloc_failed;
116
117 new->mnt_major = mnt->mnt_major;
118 new->mnt_minor = mnt->mnt_minor;
119 return (new);
120
121 alloc_failed:
122 (void) fprintf(stderr, gettext("fsdupmnttab: Out of memory\n"));
123 fsfreemnttab(new);
124 return (NULL);
125 }
126
127 /*
128 * Free a single mnttab structure
129 */
130 void
131 fsfreemnttab(struct extmnttab *mnt)
132 {
133
134 if (mnt) {
135 if (mnt->mnt_special)
136 free(mnt->mnt_special);
137 if (mnt->mnt_mountp)
138 free(mnt->mnt_mountp);
139 if (mnt->mnt_fstype)
140 free(mnt->mnt_fstype);
141 if (mnt->mnt_mntopts)
142 free(mnt->mnt_mntopts);
143 if (mnt->mnt_time)
144 free(mnt->mnt_time);
145 free(mnt);
146 }
147 }
148
149 void
150 fsfreemntlist(mntlist_t *mntl)
151 {
152 mntlist_t *mntl_tmp;
153
154 while (mntl) {
155 fsfreemnttab(mntl->mntl_mnt);
156 mntl_tmp = mntl;
157 mntl = mntl->mntl_next;
158 free(mntl_tmp);
159 }
160 }
161
162 /*
163 * Read the mnttab file and return it as a list of mnttab structs.
164 * Returns NULL if there was a memory failure.
165 */
166 mntlist_t *
167 fsmkmntlist(FILE *mfp)
168 {
169 struct extmnttab mnt;
170 mntlist_t *mhead, *mtail;
171 int ret;
172
173 mhead = mtail = NULL;
174
175 resetmnttab(mfp);
176 while ((ret = getextmntent(mfp, &mnt, sizeof (struct extmnttab)))
177 != -1) {
178 mntlist_t *mp;
179
180 if (ret != 0) /* bad entry */
181 continue;
182
183 mp = (mntlist_t *)malloc(sizeof (*mp));
184 if (mp == NULL)
185 goto alloc_failed;
186 if (mhead == NULL)
187 mhead = mp;
188 else
189 mtail->mntl_next = mp;
190 mtail = mp;
191 mp->mntl_next = NULL;
192 mp->mntl_flags = 0;
193 if ((mp->mntl_mnt = fsdupmnttab(&mnt)) == NULL)
194 goto alloc_failed;
195 }
196 return (mhead);
197
198 alloc_failed:
199 fsfreemntlist(mhead);
200 return (NULL);
201 }
202
203 /*
204 * Return the last entry that matches mntin's special
205 * device and/or mountpt.
206 * Helps to be robust here, so we check for NULL pointers.
207 */
208 mntlist_t *
209 fsgetmlast(mntlist_t *ml, struct mnttab *mntin)
210 {
211 mntlist_t *delete = NULL;
212
213 for (; ml; ml = ml->mntl_next) {
214 if (mntin->mnt_mountp && mntin->mnt_special) {
215 /*
216 * match if and only if both are equal.
217 */
218 if ((strcmp(ml->mntl_mnt->mnt_mountp,
219 mntin->mnt_mountp) == 0) &&
220 (strcmp(ml->mntl_mnt->mnt_special,
221 mntin->mnt_special) == 0))
222 delete = ml;
223 } else if (mntin->mnt_mountp) {
224 if (strcmp(ml->mntl_mnt->mnt_mountp,
225 mntin->mnt_mountp) == 0)
226 delete = ml;
227 } else if (mntin->mnt_special) {
228 if (strcmp(ml->mntl_mnt->mnt_special,
229 mntin->mnt_special) == 0)
230 delete = ml;
231 }
232 }
233 return (delete);
234 }
235
236
237 /*
238 * Returns the mountlevel of the pathname in cp. As examples,
239 * / => 1, /bin => 2, /bin/ => 2, ////bin////ls => 3, sdf => 0, etc...
240 */
241 int
242 fsgetmlevel(char *cp)
243 {
244 int mlevel;
245 char *cp1;
246
247 if (cp == NULL || *cp == NULL || *cp != '/')
248 return (0); /* this should never happen */
249
250 mlevel = 1; /* root (/) is the minimal case */
251
252 for (cp1 = cp + 1; *cp1; cp++, cp1++)
253 if (*cp == '/' && *cp1 != '/') /* "///" counts as 1 */
254 mlevel++;
255
256 return (mlevel);
257 }
258
259 /*
260 * Returns non-zero if string s is a member of the strings in ps.
261 */
262 int
263 fsstrinlist(const char *s, const char **ps)
264 {
265 const char *cp;
266 cp = *ps;
267 while (cp) {
268 if (strcmp(s, cp) == 0)
269 return (1);
270 ps++;
271 cp = *ps;
272 }
273 return (0);
274 }
275
276 static char *empty_opt_vector[] = {
277 NULL
278 };
279 /*
280 * Compare the mount options that were requested by the caller to
281 * the options actually supported by the file system. If any requested
282 * options are not supported, print a warning message.
283 *
284 * WARNING: this function modifies the string pointed to by
285 * the requested_opts argument.
286 *
287 * Arguments:
288 * requested_opts - the string containing the requested options.
289 * actual_opts - the string returned by mount(2), which lists the
290 * options actually supported. It is normal for this
291 * string to contain more options than the requested options.
292 * (The actual options may contain the default options, which
293 * may not have been included in the requested options.)
294 * special - device being mounted (only used in error messages).
295 * mountp - mount point (only used in error messages).
296 */
297 void
298 cmp_requested_to_actual_options(char *requested_opts, char *actual_opts,
299 char *special, char *mountp)
300 {
301 char *option_ptr, *actopt, *equalptr;
302 int found;
303 char *actual_opt_hold, *bufp;
304
305 if (requested_opts == NULL)
306 return;
307
308 bufp = alloca(strlen(actual_opts) + 1);
309
310 while (*requested_opts != '\0') {
311 (void) getsubopt(&requested_opts, empty_opt_vector,
312 &option_ptr);
313
314 /*
315 * Truncate any "=<value>" string from the end of
316 * the option.
317 */
318 if ((equalptr = strchr(option_ptr, '=')) != NULL)
319 *equalptr = '\0';
320
321 if (*option_ptr == '\0')
322 continue;
323
324 /*
325 * Search for the requested option in the list of options
326 * actually supported.
327 */
328 found = 0;
329
330 /*
331 * Need to use a copy of actual_opts because getsubopt
332 * is destructive and we need to scan the actual_opts
333 * string more than once.
334 *
335 * We also need to reset actual_opt_hold to the
336 * beginning of the buffer because getsubopt changes
337 * actual_opt_hold (the pointer).
338 */
339 actual_opt_hold = bufp;
340 if (actual_opts != NULL)
341 (void) strcpy(actual_opt_hold, actual_opts);
342 else
343 *actual_opt_hold = '\0';
344
345 while (*actual_opt_hold != '\0') {
346 (void) getsubopt(&actual_opt_hold, empty_opt_vector,
347 &actopt);
348
349 /* Truncate the "=<value>", if any. */
350 if ((equalptr = strchr(actopt, '=')) != NULL)
351 *equalptr = '\0';
352
353 if ((strcmp(option_ptr, actopt)) == 0) {
354 found = 1;
355 break;
356 }
357 }
358
359 if (found == 0) {
360 /*
361 * That we're ignoring the option is always
362 * truthful; the old message that the option
363 * was unknown is often not correct.
364 */
365 (void) fprintf(stderr, gettext(
366 "mount: %s on %s - WARNING ignoring option "
367 "\"%s\"\n"), special, mountp, option_ptr);
368 }
369 }
370 }
371 /*
372 * FUNCTION: fsgetmaxphys(int *, int *)
373 *
374 * INPUT: int *maxphys - a pointer to an integer that will hold
375 * the value for the system maxphys value.
376 * int *error - 0 means completed successfully
377 * otherwise this indicates the errno value.
378 *
379 * RETURNS: int - 0 if maxphys not found
380 * - 1 if maxphys is found
381 */
382 int
383 fsgetmaxphys(int *maxphys, int *error) {
384
385 int gotit = 0;
386 int fp = open("/", O_RDONLY);
387
388 *error = 0;
389
390 /*
391 * For some reason cannot open root as read only. Need a valid file
392 * descriptor to call the ufs private ioctl. If this open failes,
393 * just assume we cannot get maxphys in this case.
394 */
395 if (fp == -1) {
396 return (gotit);
397 }
398
399 if (ioctl(fp, _FIOGETMAXPHYS, maxphys) == -1) {
400 *error = errno;
401 (void) close(fp);
402 return (gotit);
403 }
404
405 (void) close(fp);
406 gotit = 1;
407 return (gotit);
408
409 }
410
411 /*
412 * The below is limited support for zone-aware commands.
413 */
414 struct zone_summary {
415 zoneid_t zoneid;
416 char rootpath[MAXPATHLEN];
417 size_t rootpathlen;
418 };
419
420 struct zone_summary *
421 fs_get_zone_summaries(void)
422 {
423 uint_t numzones = 0, oldnumzones = 0;
424 uint_t i, j;
425 zoneid_t *ids = NULL;
426 struct zone_summary *summaries;
427 zoneid_t myzoneid = getzoneid();
428
429 for (;;) {
430 if (zone_list(ids, &numzones) < 0) {
431 perror("unable to retrieve list of zones");
432 if (ids != NULL)
433 free(ids);
434 return (NULL);
435 }
436 if (numzones <= oldnumzones)
437 break;
438 if (ids != NULL)
439 free(ids);
440 ids = malloc(numzones * sizeof (*ids));
441 if (ids == NULL) {
442 perror("malloc failed");
443 return (NULL);
444 }
445 oldnumzones = numzones;
446 }
447
448 summaries = malloc((numzones + 1) * sizeof (*summaries));
449 if (summaries == NULL) {
450 free(ids);
451 perror("malloc failed");
452 return (NULL);
453 }
454
455
456 for (i = 0, j = 0; i < numzones; i++) {
457 ssize_t len;
458
459 if (ids[i] == myzoneid)
460 continue;
461 len = zone_getattr(ids[i], ZONE_ATTR_ROOT,
462 summaries[j].rootpath, sizeof (summaries[j].rootpath));
463 if (len < 0) {
464 /*
465 * Zone must have gone away. Skip.
466 */
467 continue;
468 }
469 /*
470 * Adding a trailing '/' to the zone's rootpath allows us to
471 * use strncmp() to see if a given path resides within that
472 * zone.
473 *
474 * As an example, if the zone's rootpath is "/foo/root",
475 * "/foo/root/usr" resides within the zone, while
476 * "/foo/rootpath" doesn't.
477 */
478 (void) strlcat(summaries[j].rootpath, "/",
479 sizeof (summaries[j].rootpath));
480 summaries[j].rootpathlen = len;
481 summaries[j].zoneid = ids[i];
482 j++;
483 }
484 summaries[j].zoneid = -1;
485 free(ids);
486 return (summaries);
487 }
488
489 static zoneid_t
490 fs_find_zone(const struct zone_summary *summaries, const char *mntpt)
491 {
492 uint_t i;
493
494 for (i = 0; summaries[i].zoneid != -1; i++) {
495 if (strncmp(mntpt, summaries[i].rootpath,
496 summaries[i].rootpathlen) == 0)
497 return (summaries[i].zoneid);
498 }
499 /*
500 * (-1) is the special token we return to the caller if the mount
501 * wasn't found in any other mounts on the system. This means it's
502 * only visible to our zone.
503 *
504 * Odd choice of constant, I know, but it beats calling getzoneid() a
505 * million times.
506 */
507 return (-1);
508 }
509
510 boolean_t
511 fs_mount_in_other_zone(const struct zone_summary *summaries, const char *mntpt)
512 {
513 return (fs_find_zone(summaries, mntpt) != -1);
514 }
515
516 /*
517 * List of standard options.
518 */
519 static const char *stdopts[] = {
520 MNTOPT_RO, MNTOPT_RW,
521 MNTOPT_SUID, MNTOPT_NOSUID,
522 MNTOPT_DEVICES, MNTOPT_NODEVICES,
523 MNTOPT_SETUID, MNTOPT_NOSETUID,
524 MNTOPT_NBMAND, MNTOPT_NONBMAND,
525 MNTOPT_EXEC, MNTOPT_NOEXEC,
526 };
527
528 #define NSTDOPT (sizeof (stdopts) / sizeof (stdopts[0]))
529
530 static int
531 optindx(const char *opt)
532 {
533 int i;
534
535 for (i = 0; i < NSTDOPT; i++) {
536 if (strcmp(opt, stdopts[i]) == 0)
537 return (i);
538 }
539 return (-1);
540 }
541
542 /*
543 * INPUT: filesystem option not recognized by the fs specific option
544 * parsing code.
545 * OUTPUT: True if and only if the option is one of the standard VFS
546 * layer options.
547 */
548 boolean_t
549 fsisstdopt(const char *opt)
550 {
551 return (optindx(opt) != -1);
552 }