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 /* 23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 28 29 /* 30 * System includes 31 */ 32 33 #include <stdio.h> 34 #include <limits.h> 35 #include <stdlib.h> 36 #include <unistd.h> 37 #include <libgen.h> 38 #include <errno.h> 39 #include <string.h> 40 #include <fcntl.h> 41 #include <sys/types.h> 42 #include <sys/stat.h> 43 #include <signal.h> 44 #include <assert.h> 45 #include <locale.h> 46 #include <libintl.h> 47 48 /* 49 * local includes 50 */ 51 52 #include "instzones_lib.h" 53 #include "zones_strings.h" 54 55 #define isdot(x) ((x[0] == '.') && (!x[1] || (x[1] == '/'))) 56 #define isdotdot(x) ((x[0] == '.') && (x[1] == '.') && \ 57 (!x[2] || (x[2] == '/'))) 58 59 /* 60 * forward declarations 61 */ 62 63 static char **inheritedFileSystems = (char **)NULL; 64 static size_t *inheritedFileSystemsLen = (size_t *)NULL; 65 static int numInheritedFileSystems = 0; 66 67 /* 68 * ***************************************************************************** 69 * global external (public) functions 70 * ***************************************************************************** 71 */ 72 73 /* 74 * Name: z_get_inherited_file_systems 75 * Description: Return list of file systems inherited from the global zone; 76 * These file systems are entered into the list when the function 77 * pkgAddInheritedFileSystem() is called. 78 * Arguments: void 79 * Returns: char ** 80 * - pointer to array of character pointers, each pointer 81 * being a pointer to a string representing a file 82 * system that is inherited from the global zone 83 * the last entry will be (char *)NULL 84 * - (char **)NULL - no file systems inherited 85 * 86 */ 87 88 char ** 89 z_get_inherited_file_systems(void) 90 { 91 return (inheritedFileSystems); 92 } 93 94 /* 95 * Name: z_add_inherited_file_system 96 * Description: Add specified package to internal list of inherited file systems 97 * Arguments: a_inheritedFileSystem - absolute path to file systen "inherited" 98 * 99 * This function is called to register a directory (or 100 * file system) as being inherited from the global zone 101 * into the non-global zone being operated on. The 102 * inherited directory must be specified relative to the 103 * root file system ("/"). For example, if "/usr" is 104 * inherited, then the path specified would be "/usr". 105 * 106 * Any path subsequently checked for being present in a 107 * directory inherited read-only from the global zone: 108 * 109 * -- will NOT have $PKG_INSTALL_ROOT prepended to it 110 * -- if $PKG_INSTALL_ROOT is set and $BASEDIR is not set. 111 * -- WILL have $BASEDIR prepended to it (if set). 112 * -- $BASEDIR always has $PKG_INSTALL_ROOT included in it. 113 * -- For example, if $PKG_INSTALL_ROOT is set to /a, and 114 * -- the base install directory is set to "/opt", then the 115 * -- $BASEDIR variable will be set to "/a/opt". 116 * 117 * Any path that is checked for being present in an inherited 118 * directory will be specified relative to the root file system 119 * of the non-global zone in which the path is located. 120 * 121 * When a path to update is checked for being present in 122 * an inherited directory, $PKG_INSTALL_ROOT is stripped 123 * off the path before it is checked. 124 * 125 * If the non-global zone is not running, the scratch zone 126 * is used to access the non-global zone. In this case, 127 * $PKG_INSTALL_ROOT will be set to "/a" and both the 128 * non-global zone's root file system and all inherited 129 * directories will be mounted on "/a". When a path is checked 130 * for being inherited, it will have $PKG_INSTALL_ROOT stripped 131 * from the beginning, so any inherited directories must be 132 * specified relative to "/" and not $PKG_INSTALL_ROOT. 133 * 134 * If the non-global zone is running, the non-global zone 135 * is used directly. In this case, $PKG_INSTALL_ROOT will 136 * be set to "/" and both the non-global zone's root file 137 * system and all inherited directories will be mounted on 138 * "/". $PKG_INSTALL_ROOT is set to "/" so the path is unchanged 139 * before being checked against the list of inherited directories. 140 * 141 * Returns: boolean_t 142 * B_TRUE - file system successfully added to list 143 * B_FALSE - failed to add file system to list 144 */ 145 146 boolean_t 147 z_add_inherited_file_system(char *a_inheritedFileSystem) 148 { 149 #define IPSLOP 2 /* for trailing '/' and '\0' */ 150 #define IPMAX ((sizeof (rp))-IPSLOP) 151 152 char rp[PATH_MAX+1+IPSLOP] = {'\0'}; 153 int n; 154 155 /* file system cannot be empty */ 156 157 if (a_inheritedFileSystem == NULL || *a_inheritedFileSystem == '\0') { 158 _z_program_error(ERR_INHERITED_PATH_NULL); 159 return (B_FALSE); 160 } 161 162 /* file system must be absolute */ 163 164 if (*a_inheritedFileSystem != '/') { 165 _z_program_error(ERR_INHERITED_PATH_NOT_ABSOLUTE, 166 a_inheritedFileSystem); 167 return (B_FALSE); 168 } 169 170 /* make a local copy of the path and canonize it */ 171 172 n = strlcpy(rp, a_inheritedFileSystem, IPMAX); 173 if (n > IPMAX) { 174 _z_program_error(ERR_INHERITED_PATH_TOO_LONG, 175 strlen(a_inheritedFileSystem), IPMAX, 176 a_inheritedFileSystem); 177 return (B_FALSE); 178 } 179 180 assert(n > 0); /* path must have at least 1 byte in it */ 181 182 z_path_canonize(rp); /* remove duplicate "/"s, ./, etc */ 183 184 /* add trailing "/" if it's not already there */ 185 n = strlen(rp); 186 if (rp[n-1] != '/') { 187 rp[n++] = '/'; 188 } 189 190 /* null terminate the string */ 191 192 rp[n] = '\0'; 193 194 /* add file system to internal list */ 195 196 if (inheritedFileSystems == (char **)NULL) { 197 inheritedFileSystems = (char **)_z_calloc( 198 2 * (sizeof (char **))); 199 inheritedFileSystemsLen = 200 (size_t *)_z_calloc(2 * (sizeof (size_t *))); 201 } else { 202 inheritedFileSystems = (char **)_z_realloc(inheritedFileSystems, 203 sizeof (char **)*(numInheritedFileSystems+2)); 204 inheritedFileSystemsLen = (size_t *)_z_realloc( 205 inheritedFileSystemsLen, 206 sizeof (size_t *)*(numInheritedFileSystems+2)); 207 } 208 209 /* add this entry to the end of the list */ 210 211 inheritedFileSystemsLen[numInheritedFileSystems] = strlen(rp); 212 inheritedFileSystems[numInheritedFileSystems] = _z_strdup(rp); 213 214 numInheritedFileSystems++; 215 216 /* make sure end of the list is properly terminated */ 217 218 inheritedFileSystemsLen[numInheritedFileSystems] = 0; 219 inheritedFileSystems[numInheritedFileSystems] = (char *)NULL; 220 221 /* exit debugging info */ 222 223 _z_echoDebug(DBG_PATHS_ADD_FS, numInheritedFileSystems, 224 inheritedFileSystems[numInheritedFileSystems-1]); 225 226 return (B_TRUE); 227 } 228 229 /* 230 * Name: z_path_is_inherited 231 * Description: Determine if the specified path is in a file system that is 232 * in the internal list of inherited file systems 233 * Arguments: a_path - pointer to string representing path to verify 234 * a_ftype - file "type" if known otherwise '\0' 235 * Type can be "f" (file), or "d" (directory) 236 * a_rootDir - pointer to string representing root directory where 237 * a_path is relative to - typically this would either be 238 * "/" or the path specified as an alternative root to -R 239 * Returns: boolean_t 240 * B_TRUE - the path is in inherited file system space 241 * B_FALSE - the path is NOT in inherited file system space 242 */ 243 244 boolean_t 245 z_path_is_inherited(char *a_path, char a_ftype, char *a_rootDir) 246 { 247 int n; 248 char *cp, *path2use; 249 char real_path[PATH_MAX]; 250 char path_copy[PATH_MAX]; 251 boolean_t found = B_FALSE; 252 253 /* entry assertions */ 254 255 assert(a_path != (char *)NULL); 256 assert(*a_path != '\0'); 257 258 /* if no inherited file systems, there can be no match */ 259 260 if (numInheritedFileSystems == 0) { 261 _z_echoDebug(DBG_PATHS_NOT_INHERITED, a_path); 262 return (B_FALSE); 263 } 264 265 /* normalize root directory */ 266 267 if ((a_rootDir == (char *)NULL) || (*a_rootDir == '\0')) { 268 a_rootDir = "/"; 269 } 270 271 /* 272 * The loop below represents our best effort to identify real path of 273 * a file, which doesn't need to exist. realpath() returns error for 274 * nonexistent path, therefore we need to cut off trailing components 275 * of path until we get path which exists and can be resolved by 276 * realpath(). Lookup of "/dir/symlink/nonexistent-file" would fail 277 * to resolve symlink without this. 278 */ 279 (void) strlcpy(path_copy, a_path, PATH_MAX); 280 for (cp = dirname(path_copy); strlen(cp) > 1; cp = dirname(cp)) { 281 if (realpath(cp, real_path) != NULL) { 282 found = B_TRUE; 283 break; 284 } else if (errno != ENOENT) 285 break; 286 } 287 if (found) { 288 /* 289 * In the loop above we always strip trailing path component, 290 * so the type of real_path is always 'd'. 291 */ 292 a_ftype = 'd'; 293 path2use = real_path; 294 } else { 295 path2use = a_path; 296 } 297 298 /* 299 * if path resides on an inherited filesystem then 300 * it must be read-only. 301 */ 302 303 if (z_isPathWritable(path2use) != 0) { 304 return (B_FALSE); 305 } 306 307 /* 308 * remove the root path from the target path before comparing: 309 * Example 1: 310 * -- path is "/export/zone1/root/usr/test" 311 * -- root path is "/export/zone1/root" 312 * --- final path should be "/usr/test" 313 * Example 2: 314 * -- path is "/usr/test" 315 * -- root path is "/" 316 * --- final path should be "/usr/test" 317 */ 318 319 /* advance past given root directory if path begins with it */ 320 321 n = strlen(a_rootDir); 322 if (strncmp(a_rootDir, path2use, n) == 0) { 323 char *p; 324 325 /* advance past the root path */ 326 327 p = path2use + n; 328 329 /* go back to the first occurance of the path separator */ 330 331 while ((*p != '/') && (p > path2use)) { 332 p--; 333 } 334 335 /* use this location in the path to compare */ 336 337 path2use = p; 338 } 339 340 /* 341 * see if this path is in any inherited file system path 342 * note that all paths in the inherited list are directories 343 * so they end in "/" to prevent a partial match, such as 344 * comparing "/usr/libx" with "/usr/lib" - by making the comparison 345 * "/usr/libx" with "/usr/lib/" the partial false positive will not 346 * occur. This complicates matters when the object to compare is a 347 * directory - in this case, comparing "/usr" with "/usr/" will fail, 348 * so if the object is a directory, compare one less byte from the 349 * inherited file system so that the trailing "/" is ignored. 350 */ 351 352 for (n = 0; n < numInheritedFileSystems; n++) { 353 int fslen; 354 355 /* get target fs len; adjust -1 if directory */ 356 357 fslen = inheritedFileSystemsLen[n]; 358 if ((a_ftype == 'd') && (fslen > 1)) { 359 fslen--; 360 } 361 362 if (strncmp(path2use, inheritedFileSystems[n], fslen) == 0) { 363 _z_echoDebug(DBG_PATHS_IS_INHERITED, a_path, 364 inheritedFileSystems[n]); 365 return (B_TRUE); 366 } 367 } 368 369 /* path is not in inherited file system space */ 370 371 _z_echoDebug(DBG_PATHS_IS_NOT_INHERITED, a_path, a_rootDir); 372 373 return (B_FALSE); 374 } 375 376 /* 377 * Name: z_make_zone_root 378 * Description: Given its zonepath, generate a string representing the 379 * mountpoint of where the root path for a nonglobal zone is 380 * mounted. The zone is mounted using 'zoneadm', which mounts 381 * the zone's filesystems wrt <zonepath>/lu/a 382 * Arguments: zone_path - non-NULL pointer to string representing zonepath 383 * Returns: char * - pointer to string representing zonepath of zone 384 * NULL - if zone_path is NULL. 385 * Notes: The string returned is in static storage and should not be 386 * free()ed by the caller. 387 */ 388 char * 389 z_make_zone_root(char *zone_path) 390 { 391 static char zone_root_buf[MAXPATHLEN]; 392 393 if (zone_path == NULL) 394 return (NULL); 395 396 (void) snprintf(zone_root_buf, MAXPATHLEN, "%s%slu/a", zone_path, 397 (zone_path[0] != '\0' && 398 zone_path[strlen(zone_path) - 1] == '/') ? "" : "/"); 399 400 return (zone_root_buf); 401 } 402 403 void 404 z_path_canonize(char *a_file) 405 { 406 char *pt; 407 char *last; 408 int level; 409 410 /* remove references such as "./" and "../" and "//" */ 411 for (pt = a_file; *pt; /* void */) { 412 if (isdot(pt)) { 413 (void) strcpy(pt, pt[1] ? pt+2 : pt+1); 414 } else if (isdotdot(pt)) { 415 level = 0; 416 last = pt; 417 do { 418 level++; 419 last += 2; 420 if (*last) { 421 last++; 422 } 423 } while (isdotdot(last)); 424 --pt; /* point to previous '/' */ 425 while (level--) { 426 if (pt <= a_file) { 427 return; 428 } 429 while ((*--pt != '/') && (pt > a_file)) 430 ; 431 } 432 if (*pt == '/') { 433 pt++; 434 } 435 (void) strcpy(pt, last); 436 } else { 437 while (*pt && (*pt != '/')) { 438 pt++; 439 } 440 if (*pt == '/') { 441 while (pt[1] == '/') { 442 (void) strcpy(pt, pt+1); 443 } 444 pt++; 445 } 446 } 447 } 448 449 if ((--pt > a_file) && (*pt == '/')) { 450 *pt = '\0'; 451 } 452 } 453 454 void 455 z_canoninplace(char *src) 456 { 457 char *dst; 458 char *src_start; 459 460 /* keep a ptr to the beginning of the src string */ 461 src_start = src; 462 463 dst = src; 464 while (*src) { 465 if (*src == '/') { 466 *dst++ = '/'; 467 while (*src == '/') 468 src++; 469 } else 470 *dst++ = *src++; 471 } 472 473 /* 474 * remove any trailing slashes, unless the whole string is just "/". 475 * If the whole string is "/" (i.e. if the last '/' cahr in dst 476 * in the beginning of the original string), just terminate it 477 * and return "/". 478 */ 479 if ((*(dst - 1) == '/') && ((dst - 1) != src_start)) 480 dst--; 481 *dst = '\0'; 482 } 483 484 void 485 z_free_inherited_file_systems(void) 486 { 487 int i; 488 489 for (i = 0; i < numInheritedFileSystems; i++) { 490 free(inheritedFileSystems[i]); 491 } 492 free(inheritedFileSystems); 493 inheritedFileSystems = NULL; 494 free(inheritedFileSystemsLen); 495 inheritedFileSystemsLen = NULL; 496 numInheritedFileSystems = 0; 497 } 498