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 2006 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 122 for (mlp = mlist; mlp; mlp = mlp->mntl_next) { 123 struct mnttab *mnt = mlp->mntl_mnt; 124 125 if (mnt) 126 tsol_mnt_free(mnt); 127 free(mlp); 128 } 129 } 130 131 static struct mnttab * 132 mntdup(struct mnttab *mnt) 133 { 134 struct mnttab *new; 135 136 new = (struct mnttab *)malloc(sizeof (*new)); 137 if (new == NULL) 138 return (NULL); 139 140 new->mnt_special = NULL; 141 new->mnt_mountp = NULL; 142 new->mnt_fstype = NULL; 143 new->mnt_mntopts = NULL; 144 145 new->mnt_special = strdup(mnt->mnt_special); 146 if (new->mnt_special == NULL) { 147 tsol_mnt_free(new); 148 return (NULL); 149 } 150 new->mnt_mountp = strdup(mnt->mnt_mountp); 151 if (new->mnt_mountp == NULL) { 152 tsol_mnt_free(new); 153 return (NULL); 154 } 155 new->mnt_fstype = strdup(mnt->mnt_fstype); 156 if (new->mnt_fstype == NULL) { 157 tsol_mnt_free(new); 158 return (NULL); 159 } 160 new->mnt_mntopts = strdup(mnt->mnt_mntopts); 161 if (new->mnt_mntopts == NULL) { 162 tsol_mnt_free(new); 163 return (NULL); 164 } 165 return (new); 166 } 167 168 static struct mntlist * 169 tsol_mkmntlist(void) 170 { 171 FILE *mounted; 172 struct mntlist *mntl; 173 struct mntlist *mntst = NULL; 174 struct mnttab mnt; 175 176 if ((mounted = fopen(MNTTAB, "r")) == NULL) { 177 perror(MNTTAB); 178 return (NULL); 179 } 180 resetmnttab(mounted); 181 while (getmntent(mounted, &mnt) == NULL) { 182 mntl = (struct mntlist *)malloc(sizeof (*mntl)); 183 if (mntl == NULL) { 184 tsol_mlist_free(mntst); 185 mntst = NULL; 186 break; 187 } 188 mntl->mntl_mnt = mntdup((struct mnttab *)(&mnt)); 189 if (mntl->mntl_mnt == NULL) { 190 tsol_mlist_free(mntst); 191 mntst = NULL; 192 break; 193 } 194 mntl->mntl_next = mntst; 195 mntst = mntl; 196 } 197 (void) fclose(mounted); 198 return (mntst); 199 } 200 201 /* 202 * This function attempts to convert local zone NFS mounted pathnames 203 * into equivalent global zone NFS mounted pathnames. At present 204 * it only works for automounted filesystems. It depends on the 205 * assumption that both the local and global zone automounters 206 * share the same nameservices. It also assumes that any automount 207 * map used by a local zone is available to the global zone automounter. 208 * 209 * The algorithm used consists of three phases. 210 * 211 * 1. The local zone's mnttab is searched to find the automount map 212 * with the closest matching mountpath. 213 * 214 * 2. The matching autmount map name is looked up in the global zone's 215 * mnttab to determine the path where it should be mounted in the 216 * global zone. 217 * 218 * 3. A pathname covered by an appropiate autofs trigger mount in 219 * the global zone is generated as the resolved pathname 220 * 221 * Among the things that can go wrong is that global zone doesn't have 222 * a matching automount map or the mount was not done via the automounter. 223 * Either of these cases return a NULL path. 224 */ 225 #define ZONE_OPT "zone=" 226 static int 227 getnfspathbyautofs(struct mntlist *mlist, zoneid_t zoneid, 228 struct mnttab *autofs_mnt, char *globalpath, char *zonepath, int global_len) 229 { 230 struct mntlist *mlp; 231 char zonematch[ZONENAME_MAX + 20]; 232 char zonename[ZONENAME_MAX]; 233 int longestmatch; 234 struct mnttab *mountmatch; 235 236 if (autofs_mnt) { 237 mountmatch = autofs_mnt; 238 longestmatch = strlen(mountmatch->mnt_mountp); 239 } else { 240 /* 241 * First we need to get the zonename to look for 242 */ 243 if (zone_getattr(zoneid, ZONE_ATTR_NAME, zonename, 244 ZONENAME_MAX) == -1) { 245 return (0); 246 } 247 248 (void) strncpy(zonematch, ZONE_OPT, sizeof (zonematch)); 249 (void) strlcat(zonematch, zonename, sizeof (zonematch)); 250 251 /* 252 * Find the best match for an automount map that 253 * corresponds to the local zone's pathname 254 */ 255 longestmatch = 0; 256 for (mlp = mlist; mlp; mlp = mlp->mntl_next) { 257 struct mnttab *mnt = mlp->mntl_mnt; 258 int len; 259 int matchfound; 260 char *token; 261 char *lasts; 262 char mntopts[MAXPATHLEN]; 263 264 if (subpath(globalpath, mnt->mnt_mountp) != 0) 265 continue; 266 if (strcmp(mnt->mnt_fstype, MNTTYPE_AUTOFS)) 267 continue; 268 269 matchfound = 0; 270 (void) strncpy(mntopts, mnt->mnt_mntopts, MAXPATHLEN); 271 if ((token = strtok_r(mntopts, ",", &lasts)) != NULL) { 272 if (strcmp(token, zonematch) == 0) { 273 matchfound = 1; 274 } else while ((token = strtok_r(NULL, ",", 275 &lasts)) != NULL) { 276 if (strcmp(token, zonematch) == 0) { 277 matchfound = 1; 278 break; 279 } 280 } 281 } 282 if (matchfound) { 283 len = strlen(mnt->mnt_mountp); 284 if (len > longestmatch) { 285 mountmatch = mnt; 286 longestmatch = len; 287 } 288 } 289 } 290 } 291 if (longestmatch == 0) { 292 return (0); 293 } else { 294 /* 295 * Now we may have found the corresponding autofs mount 296 * Try to find the matching global zone autofs entry 297 */ 298 299 for (mlp = mlist; mlp; mlp = mlp->mntl_next) { 300 char p[MAXPATHLEN]; 301 size_t zp_len; 302 size_t mp_len; 303 304 struct mnttab *mnt = mlp->mntl_mnt; 305 306 if (strcmp(mountmatch->mnt_special, 307 mnt->mnt_special) != 0) 308 continue; 309 if (strcmp(mnt->mnt_fstype, MNTTYPE_AUTOFS)) 310 continue; 311 if (strstr(mnt->mnt_mntopts, ZONE_OPT) != NULL) 312 continue; 313 /* 314 * OK, we have a matching global zone automap 315 * so adjust the path for the global zone. 316 */ 317 zp_len = strlen(zonepath); 318 mp_len = strlen(mnt->mnt_mountp); 319 (void) strncpy(p, globalpath + zp_len, MAXPATHLEN); 320 /* 321 * If both global zone and zone-relative 322 * mountpoint match, just use the same pathname 323 */ 324 if (strncmp(mnt->mnt_mountp, p, mp_len) == 0) { 325 (void) strncpy(globalpath, p, global_len); 326 return (1); 327 } else { 328 (void) strncpy(p, globalpath, MAXPATHLEN); 329 (void) strncpy(globalpath, mnt->mnt_mountp, 330 global_len); 331 (void) strlcat(globalpath, 332 p + strlen(mountmatch->mnt_mountp), 333 global_len); 334 return (1); 335 } 336 } 337 return (0); 338 } 339 } 340 341 /* 342 * Find the pathname for the entry in mlist that corresponds to the 343 * file named by path (i.e., that names a mount table entry for the 344 * file system in which path lies). 345 * 346 * Return 0 is there an error. 347 */ 348 static int 349 getglobalpath(const char *path, zoneid_t zoneid, struct mntlist *mlist, 350 char *globalpath) 351 { 352 struct mntlist *mlp; 353 char lofspath[MAXPATHLEN]; 354 char zonepath[MAXPATHLEN]; 355 int longestmatch; 356 struct mnttab *mountmatch; 357 358 if (zoneid != GLOBAL_ZONEID) { 359 char *prefix; 360 361 if ((prefix = getzonerootbyid(zoneid)) == NULL) { 362 return (0); 363 } 364 (void) strncpy(zonepath, prefix, MAXPATHLEN); 365 (void) strlcpy(globalpath, prefix, MAXPATHLEN); 366 (void) strlcat(globalpath, path, MAXPATHLEN); 367 free(prefix); 368 } else { 369 (void) strlcpy(globalpath, path, MAXPATHLEN); 370 } 371 372 for (;;) { 373 longestmatch = 0; 374 for (mlp = mlist; mlp; mlp = mlp->mntl_next) { 375 struct mnttab *mnt = mlp->mntl_mnt; 376 int len; 377 378 if (subpath(globalpath, mnt->mnt_mountp) != 0) 379 continue; 380 len = strlen(mnt->mnt_mountp); 381 if (len > longestmatch) { 382 mountmatch = mnt; 383 longestmatch = len; 384 } 385 } 386 /* 387 * Handle interesting mounts. 388 */ 389 if ((strcmp(mountmatch->mnt_fstype, MNTTYPE_NFS) == 0) || 390 (strcmp(mountmatch->mnt_fstype, MNTTYPE_AUTOFS) == 0)) { 391 if (zoneid > GLOBAL_ZONEID) { 392 struct mnttab *m = NULL; 393 394 if (strcmp(mountmatch->mnt_fstype, 395 MNTTYPE_AUTOFS) == 0) 396 m = mountmatch; 397 if (getnfspathbyautofs(mlist, zoneid, m, 398 globalpath, zonepath, MAXPATHLEN) == 0) { 399 return (0); 400 } 401 } 402 break; 403 } else if (strcmp(mountmatch->mnt_fstype, MNTTYPE_LOFS) == 0) { 404 /* 405 * count up what's left 406 */ 407 int remainder; 408 409 remainder = strlen(globalpath) - longestmatch; 410 if (remainder > 0) { 411 path = pathsuffix(globalpath, 412 mountmatch->mnt_mountp); 413 (void) strlcpy(lofspath, path, MAXPATHLEN); 414 } 415 (void) strlcpy(globalpath, mountmatch->mnt_special, 416 MAXPATHLEN); 417 if (remainder > 0) { 418 (void) strlcat(globalpath, lofspath, 419 MAXPATHLEN); 420 } 421 } else { 422 if ((zoneid > GLOBAL_ZONEID) && 423 (strncmp(path, "/home/", strlen("/home/")) == 0)) { 424 char zonename[ZONENAME_MAX]; 425 426 /* 427 * If this is a cross-zone reference to 428 * a home directory, it must be corrected. 429 * We should only get here if the zone's 430 * automounter hasn't yet mounted its 431 * autofs trigger on /home. 432 * 433 * Since it is likely to do so in the 434 * future, we will assume that the global 435 * zone already has an equivalent autofs 436 * mount established. By convention, 437 * this should be mounted at the 438 * /zone/<zonename> 439 */ 440 441 if (zone_getattr(zoneid, ZONE_ATTR_NAME, 442 zonename, ZONENAME_MAX) == -1) { 443 return (0); 444 } else { 445 (void) snprintf(globalpath, MAXPATHLEN, 446 "/zone/%s%s", zonename, path); 447 } 448 } 449 break; 450 } 451 } 452 return (1); 453 } 454 455 456 /* 457 * This function is only useful for global zone callers 458 * It uses the global zone mnttab to translate local zone pathnames 459 * into global zone pathnames. 460 */ 461 char * 462 getpathbylabel(const char *path_name, char *resolved_path, size_t bufsize, 463 const bslabel_t *sl) { 464 char ret_path[MAXPATHLEN]; /* pathname to return */ 465 zoneid_t zoneid; 466 struct mntlist *mlist; 467 468 if (getzoneid() != GLOBAL_ZONEID) { 469 errno = EINVAL; 470 return (NULL); 471 } 472 473 if (path_name[0] != '/') { /* need absolute pathname */ 474 errno = EINVAL; 475 return (NULL); 476 } 477 478 if (resolved_path == NULL) { 479 errno = EINVAL; 480 return (NULL); 481 } 482 483 if ((zoneid = getzoneidbylabel(sl)) == -1) 484 return (NULL); 485 486 /* 487 * Construct the list of mounted file systems. 488 */ 489 490 if ((mlist = tsol_mkmntlist()) == NULL) { 491 return (NULL); 492 } 493 if (getglobalpath(path_name, zoneid, mlist, ret_path) == 0) { 494 tsol_mlist_free(mlist); 495 return (NULL); 496 } 497 tsol_mlist_free(mlist); 498 if (strlen(ret_path) >= bufsize) { 499 errno = EFAULT; 500 return (NULL); 501 } 502 return (strcpy(resolved_path, ret_path)); 503 } /* end getpathbylabel() */ 504