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 "%Z%%M% %I% %E% 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 } 553