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