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 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* 27 * Name: getpathbylabel.c 28 * 29 * Description: Returns the global zone pathname corresponding 30 * to the specified label. The pathname does 31 * not need to match an existing file system object. 32 * 33 */ 34 #include <stdio.h> 35 #include <string.h> 36 #include <unistd.h> 37 #include <errno.h> 38 #include <sys/types.h> 39 #include <tsol/label.h> 40 #include <stdlib.h> 41 #include <zone.h> 42 #include <sys/mntent.h> 43 #include <sys/mnttab.h> 44 #include <stdarg.h> 45 46 /* 47 * This structure is used to chain mntent structures into a list 48 * and to cache stat information for each member of the list. 49 */ 50 struct mntlist { 51 struct mnttab *mntl_mnt; 52 struct mntlist *mntl_next; 53 }; 54 55 56 /* 57 * Return a pointer to the trailing suffix of full that follows the prefix 58 * given by pref. If pref isn't a prefix of full, return NULL. Apply 59 * pathname semantics to the prefix test, so that pref must match at a 60 * component boundary. 61 */ 62 static char * 63 pathsuffix(char *full, char *pref) 64 { 65 int preflen; 66 67 if (full == NULL || pref == NULL) 68 return (NULL); 69 70 preflen = strlen(pref); 71 if (strncmp(pref, full, preflen) != 0) 72 return (NULL); 73 74 /* 75 * pref is a substring of full. To be a subpath, it cannot cover a 76 * partial component of full. The last clause of the test handles the 77 * special case of the root. 78 */ 79 if (full[preflen] != '\0' && full[preflen] != '/' && preflen > 1) 80 return (NULL); 81 82 if (preflen == 1 && full[0] == '/') 83 return (full); 84 else 85 return (full + preflen); 86 } 87 88 /* 89 * Return zero iff the path named by sub is a leading subpath 90 * of the path named by full. 91 * 92 * Treat null paths as matching nothing. 93 */ 94 static int 95 subpath(char *full, char *sub) 96 { 97 return (pathsuffix(full, sub) == NULL); 98 } 99 100 static void 101 tsol_mnt_free(struct mnttab *mnt) 102 { 103 if (mnt->mnt_special) 104 free(mnt->mnt_special); 105 if (mnt->mnt_mountp) 106 free(mnt->mnt_mountp); 107 if (mnt->mnt_fstype) 108 free(mnt->mnt_fstype); 109 if (mnt->mnt_mntopts) 110 free(mnt->mnt_mntopts); 111 free(mnt); 112 } 113 114 static void 115 tsol_mlist_free(struct mntlist *mlist) 116 { 117 struct mntlist *mlp; 118 struct mntlist *oldmlp; 119 120 mlp = mlist; 121 while (mlp) { 122 struct mnttab *mnt = mlp->mntl_mnt; 123 124 if (mnt) 125 tsol_mnt_free(mnt); 126 oldmlp = mlp; 127 mlp = mlp->mntl_next; 128 free(oldmlp); 129 } 130 } 131 132 static struct mnttab * 133 mntdup(struct mnttab *mnt) 134 { 135 struct mnttab *new; 136 137 new = (struct mnttab *)malloc(sizeof (*new)); 138 if (new == NULL) 139 return (NULL); 140 141 new->mnt_special = NULL; 142 new->mnt_mountp = NULL; 143 new->mnt_fstype = NULL; 144 new->mnt_mntopts = NULL; 145 146 new->mnt_special = strdup(mnt->mnt_special); 147 if (new->mnt_special == NULL) { 148 tsol_mnt_free(new); 149 return (NULL); 150 } 151 new->mnt_mountp = strdup(mnt->mnt_mountp); 152 if (new->mnt_mountp == NULL) { 153 tsol_mnt_free(new); 154 return (NULL); 155 } 156 new->mnt_fstype = strdup(mnt->mnt_fstype); 157 if (new->mnt_fstype == NULL) { 158 tsol_mnt_free(new); 159 return (NULL); 160 } 161 new->mnt_mntopts = strdup(mnt->mnt_mntopts); 162 if (new->mnt_mntopts == NULL) { 163 tsol_mnt_free(new); 164 return (NULL); 165 } 166 return (new); 167 } 168 169 static struct mntlist * 170 tsol_mkmntlist(void) 171 { 172 FILE *mounted; 173 struct mntlist *mntl; 174 struct mntlist *mntst = NULL; 175 struct mnttab mnt; 176 177 if ((mounted = fopen(MNTTAB, "rF")) == NULL) { 178 perror(MNTTAB); 179 return (NULL); 180 } 181 resetmnttab(mounted); 182 while (getmntent(mounted, &mnt) == 0) { 183 mntl = (struct mntlist *)malloc(sizeof (*mntl)); 184 if (mntl == NULL) { 185 tsol_mlist_free(mntst); 186 mntst = NULL; 187 break; 188 } 189 mntl->mntl_mnt = mntdup((struct mnttab *)(&mnt)); 190 if (mntl->mntl_mnt == NULL) { 191 tsol_mlist_free(mntst); 192 mntst = NULL; 193 break; 194 } 195 mntl->mntl_next = mntst; 196 mntst = mntl; 197 } 198 (void) fclose(mounted); 199 return (mntst); 200 } 201 202 /* 203 * This function attempts to convert local zone NFS mounted pathnames 204 * into equivalent global zone NFS mounted pathnames. At present 205 * it only works for automounted filesystems. It depends on the 206 * assumption that both the local and global zone automounters 207 * share the same nameservices. It also assumes that any automount 208 * map used by a local zone is available to the global zone automounter. 209 * 210 * The algorithm used consists of three phases. 211 * 212 * 1. The local zone's mnttab is searched to find the automount map 213 * with the closest matching mountpath. 214 * 215 * 2. The matching autmount map name is looked up in the global zone's 216 * mnttab to determine the path where it should be mounted in the 217 * global zone. 218 * 219 * 3. A pathname covered by an appropiate autofs trigger mount in 220 * the global zone is generated as the resolved pathname 221 * 222 * Among the things that can go wrong is that global zone doesn't have 223 * a matching automount map or the mount was not done via the automounter. 224 * Either of these cases return a NULL path. 225 */ 226 #define ZONE_OPT "zone=" 227 static int 228 getnfspathbyautofs(struct mntlist *mlist, zoneid_t zoneid, 229 struct mnttab *autofs_mnt, char *globalpath, char *zonepath, int global_len) 230 { 231 struct mntlist *mlp; 232 char zonematch[ZONENAME_MAX + 20]; 233 char zonename[ZONENAME_MAX]; 234 int longestmatch; 235 struct mnttab *mountmatch; 236 237 if (autofs_mnt) { 238 mountmatch = autofs_mnt; 239 longestmatch = strlen(mountmatch->mnt_mountp); 240 } else { 241 /* 242 * First we need to get the zonename to look for 243 */ 244 if (zone_getattr(zoneid, ZONE_ATTR_NAME, zonename, 245 ZONENAME_MAX) == -1) { 246 return (0); 247 } 248 249 (void) strncpy(zonematch, ZONE_OPT, sizeof (zonematch)); 250 (void) strlcat(zonematch, zonename, sizeof (zonematch)); 251 252 /* 253 * Find the best match for an automount map that 254 * corresponds to the local zone's pathname 255 */ 256 longestmatch = 0; 257 for (mlp = mlist; mlp; mlp = mlp->mntl_next) { 258 struct mnttab *mnt = mlp->mntl_mnt; 259 int len; 260 int matchfound; 261 char *token; 262 char *lasts; 263 char mntopts[MAXPATHLEN]; 264 265 if (subpath(globalpath, mnt->mnt_mountp) != 0) 266 continue; 267 if (strcmp(mnt->mnt_fstype, MNTTYPE_AUTOFS)) 268 continue; 269 270 matchfound = 0; 271 (void) strncpy(mntopts, mnt->mnt_mntopts, MAXPATHLEN); 272 if ((token = strtok_r(mntopts, ",", &lasts)) != NULL) { 273 if (strcmp(token, zonematch) == 0) { 274 matchfound = 1; 275 } else while ((token = strtok_r(NULL, ",", 276 &lasts)) != NULL) { 277 if (strcmp(token, zonematch) == 0) { 278 matchfound = 1; 279 break; 280 } 281 } 282 } 283 if (matchfound) { 284 len = strlen(mnt->mnt_mountp); 285 if (len > longestmatch) { 286 mountmatch = mnt; 287 longestmatch = len; 288 } 289 } 290 } 291 } 292 if (longestmatch == 0) { 293 return (0); 294 } else { 295 /* 296 * Now we may have found the corresponding autofs mount 297 * Try to find the matching global zone autofs entry 298 */ 299 300 for (mlp = mlist; mlp; mlp = mlp->mntl_next) { 301 char p[MAXPATHLEN]; 302 size_t zp_len; 303 size_t mp_len; 304 305 struct mnttab *mnt = mlp->mntl_mnt; 306 307 if (strcmp(mountmatch->mnt_special, 308 mnt->mnt_special) != 0) 309 continue; 310 if (strcmp(mnt->mnt_fstype, MNTTYPE_AUTOFS)) 311 continue; 312 if (strstr(mnt->mnt_mntopts, ZONE_OPT) != NULL) 313 continue; 314 /* 315 * OK, we have a matching global zone automap 316 * so adjust the path for the global zone. 317 */ 318 zp_len = strlen(zonepath); 319 mp_len = strlen(mnt->mnt_mountp); 320 (void) strncpy(p, globalpath + zp_len, MAXPATHLEN); 321 /* 322 * If both global zone and zone-relative 323 * mountpoint match, just use the same pathname 324 */ 325 if (strncmp(mnt->mnt_mountp, p, mp_len) == 0) { 326 (void) strncpy(globalpath, p, global_len); 327 return (1); 328 } else { 329 (void) strncpy(p, globalpath, MAXPATHLEN); 330 (void) strncpy(globalpath, mnt->mnt_mountp, 331 global_len); 332 (void) strlcat(globalpath, 333 p + strlen(mountmatch->mnt_mountp), 334 global_len); 335 return (1); 336 } 337 } 338 return (0); 339 } 340 } 341 342 /* 343 * Find the pathname for the entry in mlist that corresponds to the 344 * file named by path (i.e., that names a mount table entry for the 345 * file system in which path lies). 346 * 347 * Return 0 is there an error. 348 */ 349 static int 350 getglobalpath(const char *path, zoneid_t zoneid, struct mntlist *mlist, 351 char *globalpath) 352 { 353 struct mntlist *mlp; 354 char lofspath[MAXPATHLEN]; 355 char zonepath[MAXPATHLEN]; 356 int longestmatch; 357 struct mnttab *mountmatch; 358 359 if (zoneid != GLOBAL_ZONEID) { 360 char *prefix; 361 362 if ((prefix = getzonerootbyid(zoneid)) == NULL) { 363 return (0); 364 } 365 (void) strncpy(zonepath, prefix, MAXPATHLEN); 366 (void) strlcpy(globalpath, prefix, MAXPATHLEN); 367 (void) strlcat(globalpath, path, MAXPATHLEN); 368 free(prefix); 369 } else { 370 (void) strlcpy(globalpath, path, MAXPATHLEN); 371 } 372 373 for (;;) { 374 longestmatch = 0; 375 for (mlp = mlist; mlp; mlp = mlp->mntl_next) { 376 struct mnttab *mnt = mlp->mntl_mnt; 377 int len; 378 379 if (subpath(globalpath, mnt->mnt_mountp) != 0) 380 continue; 381 len = strlen(mnt->mnt_mountp); 382 if (len > longestmatch) { 383 mountmatch = mnt; 384 longestmatch = len; 385 } 386 } 387 /* 388 * Handle interesting mounts. 389 */ 390 if ((strcmp(mountmatch->mnt_fstype, MNTTYPE_NFS) == 0) || 391 (strcmp(mountmatch->mnt_fstype, MNTTYPE_AUTOFS) == 0)) { 392 if (zoneid > GLOBAL_ZONEID) { 393 struct mnttab *m = NULL; 394 395 if (strcmp(mountmatch->mnt_fstype, 396 MNTTYPE_AUTOFS) == 0) 397 m = mountmatch; 398 if (getnfspathbyautofs(mlist, zoneid, m, 399 globalpath, zonepath, MAXPATHLEN) == 0) { 400 return (0); 401 } 402 } 403 break; 404 } else if (strcmp(mountmatch->mnt_fstype, MNTTYPE_LOFS) == 0) { 405 /* 406 * count up what's left 407 */ 408 int remainder; 409 410 remainder = strlen(globalpath) - longestmatch; 411 if (remainder > 0) { 412 path = pathsuffix(globalpath, 413 mountmatch->mnt_mountp); 414 (void) strlcpy(lofspath, path, MAXPATHLEN); 415 } 416 (void) strlcpy(globalpath, mountmatch->mnt_special, 417 MAXPATHLEN); 418 if (remainder > 0) { 419 (void) strlcat(globalpath, lofspath, 420 MAXPATHLEN); 421 } 422 } else { 423 if ((zoneid > GLOBAL_ZONEID) && 424 (strncmp(path, "/home/", strlen("/home/")) == 0)) { 425 char zonename[ZONENAME_MAX]; 426 427 /* 428 * If this is a cross-zone reference to 429 * a home directory, it must be corrected. 430 * We should only get here if the zone's 431 * automounter hasn't yet mounted its 432 * autofs trigger on /home. 433 * 434 * Since it is likely to do so in the 435 * future, we will assume that the global 436 * zone already has an equivalent autofs 437 * mount established. By convention, 438 * this should be mounted at the 439 * /zone/<zonename> 440 */ 441 442 if (zone_getattr(zoneid, ZONE_ATTR_NAME, 443 zonename, ZONENAME_MAX) == -1) { 444 return (0); 445 } else { 446 (void) snprintf(globalpath, MAXPATHLEN, 447 "/zone/%s%s", zonename, path); 448 } 449 } 450 break; 451 } 452 } 453 return (1); 454 } 455 456 457 /* 458 * This function is only useful for global zone callers 459 * It uses the global zone mnttab to translate local zone pathnames 460 * into global zone pathnames. 461 */ 462 char * 463 getpathbylabel(const char *path_name, char *resolved_path, size_t bufsize, 464 const bslabel_t *sl) 465 { 466 char ret_path[MAXPATHLEN]; /* pathname to return */ 467 zoneid_t zoneid; 468 struct mntlist *mlist; 469 470 if (getzoneid() != GLOBAL_ZONEID) { 471 errno = EINVAL; 472 return (NULL); 473 } 474 475 if (path_name[0] != '/') { /* need absolute pathname */ 476 errno = EINVAL; 477 return (NULL); 478 } 479 480 if (resolved_path == NULL) { 481 errno = EINVAL; 482 return (NULL); 483 } 484 485 if ((zoneid = getzoneidbylabel(sl)) == -1) 486 return (NULL); 487 488 /* 489 * Construct the list of mounted file systems. 490 */ 491 492 if ((mlist = tsol_mkmntlist()) == NULL) { 493 return (NULL); 494 } 495 if (getglobalpath(path_name, zoneid, mlist, ret_path) == 0) { 496 tsol_mlist_free(mlist); 497 return (NULL); 498 } 499 tsol_mlist_free(mlist); 500 if (strlen(ret_path) >= bufsize) { 501 errno = EFAULT; 502 return (NULL); 503 } 504 return (strcpy(resolved_path, ret_path)); 505 } /* end getpathbylabel() */ 506