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 * 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 * 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 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 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 * 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 * 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 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 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 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 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 * 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 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 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 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 557 fsisstdopt(const char *opt) 558 { 559 return (optindx(opt) != -1); 560 } 561