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
50extern struct cfextra **extlist;
51extern char	pkgloc[];
52extern 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
73static void	warn(int type, char *name, fsblkcnt_t need, fsblkcnt_t avail,
74			fsblkcnt_t limit);
75static int	fsys_stat(int n);
76static int	readmap(int *error);
77static int	readspace(char *spacefile, int *error);
78
79int
80dockspace(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
124static void
125warn(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
137static int
138fsys_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 */
187static int
188readmap(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
352static int
353readspace(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