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 2008 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 28 /* All Rights Reserved */ 29 30 31 #include <stdio.h> 32 #include <ctype.h> 33 #include <string.h> 34 #include <stdlib.h> 35 #include <unistd.h> 36 #include <sys/types.h> 37 #include <sys/param.h> 38 #include <sys/stat.h> 39 #include <sys/statvfs.h> 40 #include <limits.h> 41 #include <locale.h> 42 #include <libintl.h> 43 #include <pkgstrct.h> 44 #include "install.h" 45 #include <pkglib.h> 46 #include "libadm.h" 47 #include "libinst.h" 48 #include "pkginstall.h" 49 50 extern struct cfextra **extlist; 51 extern char pkgloc[]; 52 extern char instdir[]; 53 54 #define LSIZE 256 55 #define LIM_BFREE 150LL 56 #define LIM_FFREE 25LL 57 58 #define WRN_STATVFS "WARNING: unable to stat filesystem mounted on <%s>" 59 60 #define WRN_NOBLKS "The %s filesystem has %llu free blocks. The current " \ 61 "installation requires %llu blocks, which includes a " \ 62 "required %llu block buffer for open " \ 63 "deleted files. %llu more blocks are needed." 64 65 #define WRN_NOFILES "The %s filesystem has %llu free file nodes. The " \ 66 "current installation requires %llu file nodes, " \ 67 "which includes a required %llu file node buffer " \ 68 "for temporary files. %llu more file nodes " \ 69 "are needed." 70 71 #define TYPE_BLCK 0 72 #define TYPE_NODE 1 73 static void warn(int type, char *name, fsblkcnt_t need, fsblkcnt_t avail, 74 fsblkcnt_t limit); 75 static int fsys_stat(int n); 76 static int readmap(int *error); 77 static int readspace(char *spacefile, int *error); 78 79 int 80 dockspace(char *spacefile) 81 { 82 struct fstable *fs_tab; 83 int i, error; 84 85 error = 0; 86 87 /* 88 * Also, vanilla SVr4 code used the output from popen() 89 * on the "/etc/mount" command. However, we need to get more 90 * information about mounted filesystems, so we use the C 91 * interfaces to the mount table, which also happens to be 92 * much faster than running another process. Since several 93 * of the pkg commands need access to the mount table, this 94 * code is now in libinst. However, mount table info is needed 95 * at the time the base directory is determined, so the call 96 * to get the mount table information is in main.c 97 */ 98 99 if (readmap(&error) || readspace(spacefile, &error)) 100 return (-1); 101 102 for (i = 0; fs_tab = get_fs_entry(i); ++i) { 103 if ((!fs_tab->fused) && (!fs_tab->bused)) 104 continue; /* not used by us */ 105 106 if (fs_tab->bfree < (LIM_BFREE + fs_tab->bused)) { 107 warn(TYPE_BLCK, fs_tab->name, fs_tab->bused, 108 fs_tab->bfree, LIM_BFREE); 109 error++; 110 } 111 112 /* bug id 1091292 */ 113 if ((long)fs_tab->ffree == -1L) 114 continue; 115 if (fs_tab->ffree < (LIM_FFREE + fs_tab->fused)) { 116 warn(TYPE_NODE, fs_tab->name, fs_tab->fused, 117 fs_tab->ffree, LIM_FFREE); 118 error++; 119 } 120 } 121 return (error); 122 } 123 124 static void 125 warn(int type, char *name, fsblkcnt_t need, fsblkcnt_t avail, fsblkcnt_t limit) 126 { 127 logerr(gettext("WARNING:")); 128 if (type == TYPE_BLCK) { 129 logerr(gettext(WRN_NOBLKS), name, avail, (need + limit), limit, 130 (need + limit - avail)); 131 } else { 132 logerr(gettext(WRN_NOFILES), name, avail, (need + limit), limit, 133 (need + limit - avail)); 134 } 135 } 136 137 static int 138 fsys_stat(int n) 139 { 140 struct statvfs64 svfsb; 141 struct fstable *fs_tab; 142 143 if (n == BADFSYS) 144 return (1); 145 146 fs_tab = get_fs_entry(n); 147 148 /* 149 * At this point, we know we need information 150 * about a particular filesystem, so we can do the 151 * statvfs() now. For performance reasons, we only want to 152 * stat the filesystem once, at the first time we need to, 153 * and so we can key on whether or not we have the 154 * block size for that filesystem. 155 */ 156 if (fs_tab->bsize != 0) 157 return (0); 158 159 if (statvfs64(fs_tab->name, &svfsb)) { 160 logerr(gettext(WRN_STATVFS), fs_tab->name); 161 return (1); 162 } 163 164 /* 165 * statvfs returns number of fragment size blocks 166 * so will change this to number of 512 byte blocks 167 */ 168 fs_tab->bsize = svfsb.f_bsize; 169 fs_tab->frsize = svfsb.f_frsize; 170 fs_tab->bfree = ((svfsb.f_frsize > 0) ? 171 howmany(svfsb.f_frsize, DEV_BSIZE) : 172 howmany(svfsb.f_bsize, DEV_BSIZE)) * svfsb.f_bavail; 173 fs_tab->ffree = (svfsb.f_favail > 0) ? svfsb.f_favail : svfsb.f_ffree; 174 return (0); 175 } 176 177 /* 178 * This function reads all of the package objects, maps them to their target 179 * filesystems and adds up the amount of space used on each. Wherever you see 180 * "fsys_value", that's the apparent filesystem which could be a temporary 181 * loopback mount for the purpose of constructing the client filesystem. It 182 * isn't necessarily the real target filesystem. Where you see "fsys_base" 183 * that's the real filesystem to which fsys_value may just refer. If this is 184 * installing to a standalone or a server, fsys_value will almost always be 185 * the same as fsys_base. 186 */ 187 static int 188 readmap(int *error) 189 { 190 struct fstable *fs_tab; 191 struct cfextra *ext; 192 struct cfent *ept; 193 struct stat statbuf; 194 char tpath[PATH_MAX]; 195 fsblkcnt_t blk; 196 int i, n; 197 198 /* 199 * Handle the installation files (ftype i) that are in the 200 * pkgmap/eptlist. 201 */ 202 for (i = 0; (ext = extlist[i]) != NULL; i++) { 203 ept = &(ext->cf_ent); 204 205 if (ept->ftype != 'i') 206 continue; 207 208 /* 209 * These paths are treated differently from the others 210 * since their full pathnames are not included in the 211 * pkgmap. 212 */ 213 if (strcmp(ept->path, "pkginfo") == 0) 214 (void) sprintf(tpath, "%s/%s", pkgloc, ept->path); 215 else 216 (void) sprintf(tpath, "%s/install/%s", pkgloc, 217 ept->path); 218 219 /* If we haven't done an fsys() series, do one */ 220 if (ext->fsys_value == BADFSYS) 221 ext->fsys_value = fsys(tpath); 222 223 /* 224 * Now check if this is a base or apparent filesystem. If 225 * it's just apparent, get the resolved filesystem entry, 226 * otherwise, base and value are the same. 227 */ 228 if (use_srvr_map_n(ext->fsys_value)) 229 ext->fsys_base = resolved_fsys(tpath); 230 else 231 ext->fsys_base = ext->fsys_value; 232 233 if (fsys_stat(ext->fsys_base)) { 234 (*error)++; 235 continue; 236 } 237 238 /* 239 * Don't accumulate space requirements on read-only 240 * remote filesystems. 241 */ 242 if (is_remote_fs_n(ext->fsys_value) && 243 !is_fs_writeable_n(ext->fsys_value)) 244 continue; 245 246 fs_tab = get_fs_entry(ext->fsys_base); 247 248 fs_tab->fused++; 249 if (ept->cinfo.size != BADCONT) 250 blk = nblk(ept->cinfo.size, 251 fs_tab->bsize, 252 fs_tab->frsize); 253 else 254 blk = 0; 255 fs_tab->bused += blk; 256 } 257 258 /* 259 * Handle the other files in the eptlist. 260 */ 261 for (i = 0; (ext = extlist[i]) != NULL; i++) { 262 ept = &(extlist[i]->cf_ent); 263 264 if (ept->ftype == 'i') 265 continue; 266 267 /* 268 * Don't recalculate package objects that are already in the 269 * table. 270 */ 271 if (ext->mstat.preloaded) 272 continue; 273 274 /* 275 * Don't accumulate space requirements on read-only 276 * remote filesystems. 277 */ 278 if (is_remote_fs(ept->path, &(ext->fsys_value)) && 279 !is_fs_writeable(ept->path, &(ext->fsys_value))) 280 continue; 281 282 /* 283 * Now check if this is a base or apparent filesystem. If 284 * it's just apparent, get the resolved filesystem entry, 285 * otherwise, base and value are the same. 286 */ 287 if (use_srvr_map_n(ext->fsys_value)) 288 ext->fsys_base = resolved_fsys(tpath); 289 else 290 ext->fsys_base = ext->fsys_value; 291 292 /* At this point we know we have a good fsys_base. */ 293 if (fsys_stat(ext->fsys_base)) { 294 (*error)++; 295 continue; 296 } 297 298 /* 299 * We have to stat this path based upon it's real location. 300 * If this is a server-remap, ept->path isn't the real 301 * location. 302 */ 303 if (use_srvr_map_n(ext->fsys_value)) 304 strcpy(tpath, server_map(ept->path, ext->fsys_value)); 305 else 306 strcpy(tpath, ept->path); 307 308 fs_tab = get_fs_entry(ext->fsys_base); 309 if (stat(tpath, &statbuf)) { 310 /* path cannot be accessed */ 311 fs_tab->fused++; 312 if (strchr("dxs", ept->ftype)) 313 blk = 314 nblk(fs_tab->bsize, 315 fs_tab->bsize, 316 fs_tab->frsize); 317 else if (ept->cinfo.size != BADCONT) 318 blk = nblk(ept->cinfo.size, 319 fs_tab->bsize, 320 fs_tab->frsize); 321 else 322 blk = 0; 323 } else { 324 /* path already exists */ 325 if (strchr("dxs", ept->ftype)) 326 blk = 0; 327 else if (ept->cinfo.size != BADCONT) { 328 fsblkcnt_t new_size, old_size; 329 new_size = nblk(ept->cinfo.size, 330 fs_tab->bsize, 331 fs_tab->frsize); 332 old_size = nblk(statbuf.st_size, 333 fs_tab->bsize, 334 fs_tab->frsize); 335 /* 336 * negative blocks show room freed, but since 337 * order of installation is uncertain show 338 * 0 blocks usage 339 */ 340 if (new_size < old_size) 341 blk = 0; 342 else 343 blk = new_size - old_size; 344 } else 345 blk = 0; 346 } 347 fs_tab->bused += blk; 348 } 349 return (0); 350 } 351 352 static int 353 readspace(char *spacefile, int *error) 354 { 355 FILE *fp; 356 char line[LSIZE]; 357 long blocks, nodes; 358 int n; 359 360 if (spacefile == NULL) 361 return (0); 362 363 if ((fp = fopen(spacefile, "r")) == NULL) { 364 progerr(gettext("unable to open spacefile %s"), spacefile); 365 return (-1); 366 } 367 368 while (fgets(line, LSIZE, fp)) { 369 struct fstable *fs_tab; 370 char *pt, path[PATH_MAX]; 371 372 blocks = nodes = 0; 373 for (pt = line; isspace(*pt); /* void */) 374 pt++; 375 if (*pt == '#' || *pt == '\0') 376 continue; 377 378 (void) sscanf(line, "%s %ld %ld", path, &blocks, &nodes); 379 mappath(2, path); 380 basepath(path, get_basedir(), get_inst_root()); 381 canonize(path); 382 383 n = resolved_fsys(path); 384 if (fsys_stat(n)) { 385 (*error)++; 386 continue; 387 } 388 389 /* 390 * Don't accumulate space requirements on read-only 391 * remote filesystems. NOTE: For some reason, this 392 * used to check for !remote && read only. If this 393 * blows up later, then maybe that was correct -- JST 394 */ 395 if (is_remote_fs_n(n) && !is_fs_writeable_n(n)) 396 continue; 397 398 fs_tab = get_fs_entry(n); 399 400 fs_tab->bused += blocks; 401 fs_tab->fused += nodes; 402 } 403 (void) fclose(fp); 404 return (0); 405 } 406