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