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 * 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 * 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 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 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 * 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 * 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 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 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 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 bufp = alloca(strlen(actual_opts) + 1); 306 307 while (*requested_opts != '\0') { 308 (void) getsubopt(&requested_opts, empty_opt_vector, 309 &option_ptr); 310 311 /* 312 * Truncate any "=<value>" string from the end of 313 * the option. 314 */ 315 if ((equalptr = strchr(option_ptr, '=')) != NULL) 316 *equalptr = '\0'; 317 318 if (*option_ptr == '\0') 319 continue; 320 321 /* 322 * Whilst we don't need this option to perform a lofi 323 * mount, let's not be mendacious enough to complain 324 * about it. 325 */ 326 if (strcmp(option_ptr, "loop") == 0) 327 continue; 328 329 /* 330 * Search for the requested option in the list of options 331 * actually supported. 332 */ 333 found = 0; 334 335 /* 336 * Need to use a copy of actual_opts because getsubopt 337 * is destructive and we need to scan the actual_opts 338 * string more than once. 339 * 340 * We also need to reset actual_opt_hold to the 341 * beginning of the buffer because getsubopt changes 342 * actual_opt_hold (the pointer). 343 */ 344 actual_opt_hold = bufp; 345 if (actual_opts != NULL) 346 (void) strcpy(actual_opt_hold, actual_opts); 347 else 348 *actual_opt_hold = '\0'; 349 350 while (*actual_opt_hold != '\0') { 351 (void) getsubopt(&actual_opt_hold, empty_opt_vector, 352 &actopt); 353 354 /* Truncate the "=<value>", if any. */ 355 if ((equalptr = strchr(actopt, '=')) != NULL) 356 *equalptr = '\0'; 357 358 if ((strcmp(option_ptr, actopt)) == 0) { 359 found = 1; 360 break; 361 } 362 } 363 364 if (found == 0) { 365 /* 366 * That we're ignoring the option is always 367 * truthful; the old message that the option 368 * was unknown is often not correct. 369 */ 370 (void) fprintf(stderr, gettext( 371 "mount: %s on %s - WARNING ignoring option " 372 "\"%s\"\n"), special, mountp, option_ptr); 373 } 374 } 375 } 376 /* 377 * FUNCTION: fsgetmaxphys(int *, int *) 378 * 379 * INPUT: int *maxphys - a pointer to an integer that will hold 380 * the value for the system maxphys value. 381 * int *error - 0 means completed successfully 382 * otherwise this indicates the errno value. 383 * 384 * RETURNS: int - 0 if maxphys not found 385 * - 1 if maxphys is found 386 */ 387 int 388 fsgetmaxphys(int *maxphys, int *error) { 389 390 int gotit = 0; 391 int fp = open("/", O_RDONLY); 392 393 *error = 0; 394 395 /* 396 * For some reason cannot open root as read only. Need a valid file 397 * descriptor to call the ufs private ioctl. If this open failes, 398 * just assume we cannot get maxphys in this case. 399 */ 400 if (fp == -1) { 401 return (gotit); 402 } 403 404 if (ioctl(fp, _FIOGETMAXPHYS, maxphys) == -1) { 405 *error = errno; 406 (void) close(fp); 407 return (gotit); 408 } 409 410 (void) close(fp); 411 gotit = 1; 412 return (gotit); 413 414 } 415 416 /* 417 * The below is limited support for zone-aware commands. 418 */ 419 struct zone_summary { 420 zoneid_t zoneid; 421 char rootpath[MAXPATHLEN]; 422 size_t rootpathlen; 423 }; 424 425 struct zone_summary * 426 fs_get_zone_summaries(void) 427 { 428 uint_t numzones = 0, oldnumzones = 0; 429 uint_t i, j; 430 zoneid_t *ids = NULL; 431 struct zone_summary *summaries; 432 zoneid_t myzoneid = getzoneid(); 433 434 for (;;) { 435 if (zone_list(ids, &numzones) < 0) { 436 perror("unable to retrieve list of zones"); 437 if (ids != NULL) 438 free(ids); 439 return (NULL); 440 } 441 if (numzones <= oldnumzones) 442 break; 443 if (ids != NULL) 444 free(ids); 445 ids = malloc(numzones * sizeof (*ids)); 446 if (ids == NULL) { 447 perror("malloc failed"); 448 return (NULL); 449 } 450 oldnumzones = numzones; 451 } 452 453 summaries = malloc((numzones + 1) * sizeof (*summaries)); 454 if (summaries == NULL) { 455 free(ids); 456 perror("malloc failed"); 457 return (NULL); 458 } 459 460 461 for (i = 0, j = 0; i < numzones; i++) { 462 ssize_t len; 463 464 if (ids[i] == myzoneid) 465 continue; 466 len = zone_getattr(ids[i], ZONE_ATTR_ROOT, 467 summaries[j].rootpath, sizeof (summaries[j].rootpath)); 468 if (len < 0) { 469 /* 470 * Zone must have gone away. Skip. 471 */ 472 continue; 473 } 474 /* 475 * Adding a trailing '/' to the zone's rootpath allows us to 476 * use strncmp() to see if a given path resides within that 477 * zone. 478 * 479 * As an example, if the zone's rootpath is "/foo/root", 480 * "/foo/root/usr" resides within the zone, while 481 * "/foo/rootpath" doesn't. 482 */ 483 (void) strlcat(summaries[j].rootpath, "/", 484 sizeof (summaries[j].rootpath)); 485 summaries[j].rootpathlen = len; 486 summaries[j].zoneid = ids[i]; 487 j++; 488 } 489 summaries[j].zoneid = -1; 490 free(ids); 491 return (summaries); 492 } 493 494 static zoneid_t 495 fs_find_zone(const struct zone_summary *summaries, const char *mntpt) 496 { 497 uint_t i; 498 499 for (i = 0; summaries[i].zoneid != -1; i++) { 500 if (strncmp(mntpt, summaries[i].rootpath, 501 summaries[i].rootpathlen) == 0) 502 return (summaries[i].zoneid); 503 } 504 /* 505 * (-1) is the special token we return to the caller if the mount 506 * wasn't found in any other mounts on the system. This means it's 507 * only visible to our zone. 508 * 509 * Odd choice of constant, I know, but it beats calling getzoneid() a 510 * million times. 511 */ 512 return (-1); 513 } 514 515 boolean_t 516 fs_mount_in_other_zone(const struct zone_summary *summaries, const char *mntpt) 517 { 518 return (fs_find_zone(summaries, mntpt) != -1); 519 } 520 521 /* 522 * List of standard options. 523 */ 524 static const char *stdopts[] = { 525 MNTOPT_RO, MNTOPT_RW, 526 MNTOPT_SUID, MNTOPT_NOSUID, 527 MNTOPT_DEVICES, MNTOPT_NODEVICES, 528 MNTOPT_SETUID, MNTOPT_NOSETUID, 529 MNTOPT_NBMAND, MNTOPT_NONBMAND, 530 MNTOPT_EXEC, MNTOPT_NOEXEC, 531 }; 532 533 #define NSTDOPT (sizeof (stdopts) / sizeof (stdopts[0])) 534 535 static int 536 optindx(const char *opt) 537 { 538 int i; 539 540 for (i = 0; i < NSTDOPT; i++) { 541 if (strcmp(opt, stdopts[i]) == 0) 542 return (i); 543 } 544 return (-1); 545 } 546 547 /* 548 * INPUT: filesystem option not recognized by the fs specific option 549 * parsing code. 550 * OUTPUT: True if and only if the option is one of the standard VFS 551 * layer options. 552 */ 553 boolean_t 554 fsisstdopt(const char *opt) 555 { 556 return (optindx(opt) != -1); 557 } 558