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