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