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