summaryrefslogtreecommitdiff
path: root/usr/src/cmd/svr4pkg/pkginstall/dockspace.c
blob: 8d6bf9e0ceb6026782bc08c14565f4b383a9daa8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright (c) 2017 Peter Tribble.
 */

/*
 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */


#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <limits.h>
#include <locale.h>
#include <libintl.h>
#include <pkgstrct.h>
#include "install.h"
#include <pkglib.h>
#include "libadm.h"
#include "libinst.h"
#include "pkginstall.h"

extern struct cfextra **extlist;
extern char	pkgloc[];
extern char	instdir[];

#define	LSIZE		256
#define	LIM_BFREE	150LL
#define	LIM_FFREE	25LL

#define	WRN_STATVFS	"WARNING: unable to stat filesystem mounted on <%s>"

#define	WRN_NOBLKS	"The %s filesystem has %llu free blocks. The current " \
			"installation requires %llu blocks, which includes a " \
			"required %llu block buffer for open " \
			"deleted files. %llu more blocks are needed."

#define	WRN_NOFILES	"The %s filesystem has %llu free file nodes. The " \
			"current installation requires %llu file nodes, " \
			"which includes a required %llu file node buffer " \
			"for temporary files. %llu more file nodes " \
			"are needed."

#define	TYPE_BLCK	0
#define	TYPE_NODE	1
static void	warn(int type, char *name, fsblkcnt_t need, fsblkcnt_t avail,
			fsblkcnt_t limit);
static int	fsys_stat(int n);
static int	readmap(int *error);
static int	readspace(char *spacefile, int *error);

int
dockspace(char *spacefile)
{
	struct fstable *fs_tab;
	int	i, error;

	error = 0;

	/*
	 * Also, vanilla SVr4 code used the output from popen()
	 * on the "/etc/mount" command.  However, we need to get more
	 * information about mounted filesystems, so we use the C
	 * interfaces to the mount table, which also happens to be
	 * much faster than running another process.  Since several
	 * of the pkg commands need access to the mount table, this
	 * code is now in libinst.  However, mount table info is needed
	 * at the time the base directory is determined, so the call
	 * to get the mount table information is in main.c
	 */

	if (readmap(&error) || readspace(spacefile, &error))
		return (-1);

	for (i = 0; fs_tab = get_fs_entry(i); ++i) {
		if ((!fs_tab->fused) && (!fs_tab->bused))
			continue; /* not used by us */

		if (fs_tab->bfree < (LIM_BFREE + fs_tab->bused)) {
			warn(TYPE_BLCK, fs_tab->name, fs_tab->bused,
			    fs_tab->bfree, LIM_BFREE);
			error++;
		}

		/* bug id 1091292 */
		if ((long)fs_tab->ffree == -1L)
			continue;
		if (fs_tab->ffree < (LIM_FFREE + fs_tab->fused)) {
			warn(TYPE_NODE, fs_tab->name, fs_tab->fused,
			    fs_tab->ffree, LIM_FFREE);
			error++;
		}
	}
	return (error);
}

static void
warn(int type, char *name, fsblkcnt_t need, fsblkcnt_t avail, fsblkcnt_t limit)
{
	logerr(gettext("WARNING:"));
	if (type == TYPE_BLCK) {
		logerr(gettext(WRN_NOBLKS), name, avail, (need + limit), limit,
		    (need + limit - avail));
	} else {
		logerr(gettext(WRN_NOFILES), name, avail, (need + limit), limit,
		    (need + limit - avail));
	}
}

static int
fsys_stat(int n)
{
	struct statvfs64 svfsb;
	struct fstable *fs_tab;

	if (n == BADFSYS)
		return (1);

	fs_tab = get_fs_entry(n);

	/*
	 * At this point, we know we need information
	 * about a particular filesystem, so we can do the
	 * statvfs() now.  For performance reasons, we only want to
	 * stat the filesystem once, at the first time we need to,
	 * and so we can key on whether or not we have the
	 * block size for that filesystem.
	 */
	if (fs_tab->bsize != 0)
		return (0);

	if (statvfs64(fs_tab->name, &svfsb)) {
		logerr(gettext(WRN_STATVFS), fs_tab->name);
		return (1);
	}

	/*
	 * statvfs returns number of fragment size blocks
	 * so will change this to number of 512 byte blocks
	 */
	fs_tab->bsize  = svfsb.f_bsize;
	fs_tab->frsize = svfsb.f_frsize;
	fs_tab->bfree  = ((svfsb.f_frsize > 0) ?
	    howmany(svfsb.f_frsize, DEV_BSIZE) :
	    howmany(svfsb.f_bsize, DEV_BSIZE)) * svfsb.f_bavail;
	fs_tab->ffree  = (svfsb.f_favail > 0) ? svfsb.f_favail : svfsb.f_ffree;
	return (0);
}

/*
 * This function reads all of the package objects, maps them to their target
 * filesystems and adds up the amount of space used on each. Wherever you see
 * "fsys_value", that's the apparent filesystem which could be a temporary
 * loopback mount for the purpose of constructing the client filesystem. It
 * isn't necessarily the real target filesystem. Where you see "fsys_base"
 * that's the real filesystem to which fsys_value may just refer. If this is
 * installing to a standalone or a server, fsys_value will almost always be
 * the same as fsys_base.
 */
static int
readmap(int *error)
{
	struct fstable *fs_tab;
	struct cfextra *ext;
	struct cfent *ept;
	struct stat statbuf;
	char	tpath[PATH_MAX];
	fsblkcnt_t	blk;
	int	i;

	/*
	 * Handle the installation files (ftype i) that are in the
	 * pkgmap/eptlist.
	 */
	for (i = 0; (ext = extlist[i]) != NULL; i++) {
		ept = &(ext->cf_ent);

		if (ept->ftype != 'i')
			continue;

		/*
		 * These paths are treated differently from the others
		 * since their full pathnames are not included in the
		 * pkgmap.
		 */
		if (strcmp(ept->path, "pkginfo") == 0)
			(void) sprintf(tpath, "%s/%s", pkgloc, ept->path);
		else
			(void) sprintf(tpath, "%s/install/%s", pkgloc,
			    ept->path);

		/* If we haven't done an fsys() series, do one */
		if (ext->fsys_value == BADFSYS)
			ext->fsys_value = fsys(tpath);

		/*
		 * Now check if this is a base or apparent filesystem. If
		 * it's just apparent, get the resolved filesystem entry,
		 * otherwise, base and value are the same.
		 */
		if (use_srvr_map_n(ext->fsys_value))
			ext->fsys_base = resolved_fsys(tpath);
		else
			ext->fsys_base = ext->fsys_value;

		if (fsys_stat(ext->fsys_base)) {
			(*error)++;
			continue;
		}

		/*
		 * Don't accumulate space requirements on read-only
		 * remote filesystems.
		 */
		if (is_remote_fs_n(ext->fsys_value) &&
		    !is_fs_writeable_n(ext->fsys_value))
			continue;

		fs_tab = get_fs_entry(ext->fsys_base);

		fs_tab->fused++;
		if (ept->cinfo.size != BADCONT)
			blk = nblk(ept->cinfo.size,
			    fs_tab->bsize,
			    fs_tab->frsize);
		else
			blk = 0;
		fs_tab->bused += blk;
	}

	/*
	 * Handle the other files in the eptlist.
	 */
	for (i = 0; (ext = extlist[i]) != NULL; i++) {
		ept = &(extlist[i]->cf_ent);

		if (ept->ftype == 'i')
			continue;

		/*
		 * Don't recalculate package objects that are already in the
		 * table.
		 */
		if (ext->mstat.preloaded)
			continue;

		/*
		 * Don't accumulate space requirements on read-only
		 * remote filesystems.
		 */
		if (is_remote_fs(ept->path, &(ext->fsys_value)) &&
		    !is_fs_writeable(ept->path, &(ext->fsys_value)))
			continue;

		/*
		 * Now check if this is a base or apparent filesystem. If
		 * it's just apparent, get the resolved filesystem entry,
		 * otherwise, base and value are the same.
		 */
		if (use_srvr_map_n(ext->fsys_value))
			ext->fsys_base = resolved_fsys(tpath);
		else
			ext->fsys_base = ext->fsys_value;

		/* At this point we know we have a good fsys_base. */
		if (fsys_stat(ext->fsys_base)) {
			(*error)++;
			continue;
		}

		/*
		 * We have to stat this path based upon it's real location.
		 * If this is a server-remap, ept->path isn't the real
		 * location.
		 */
		if (use_srvr_map_n(ext->fsys_value))
			strcpy(tpath, server_map(ept->path, ext->fsys_value));
		else
			strcpy(tpath, ept->path);

		fs_tab = get_fs_entry(ext->fsys_base);
		if (stat(tpath, &statbuf)) {
			/* path cannot be accessed */
			fs_tab->fused++;
			if (strchr("dxs", ept->ftype))
				blk =
				    nblk(fs_tab->bsize,
				    fs_tab->bsize,
				    fs_tab->frsize);
			else if (ept->cinfo.size != BADCONT)
				blk = nblk(ept->cinfo.size,
				    fs_tab->bsize,
				    fs_tab->frsize);
			else
				blk = 0;
		} else {
			/* path already exists */
			if (strchr("dxs", ept->ftype))
				blk = 0;
			else if (ept->cinfo.size != BADCONT) {
				fsblkcnt_t new_size, old_size;
				new_size = nblk(ept->cinfo.size,
				    fs_tab->bsize,
				    fs_tab->frsize);
				old_size = nblk(statbuf.st_size,
				    fs_tab->bsize,
				    fs_tab->frsize);
				/*
				 * negative blocks show room freed, but since
				 * order of installation is uncertain show
				 * 0 blocks usage
				 */
				if (new_size < old_size)
					blk = 0;
				else
					blk = new_size - old_size;
			} else
				blk = 0;
		}
		fs_tab->bused += blk;
	}
	return (0);
}

static int
readspace(char *spacefile, int *error)
{
	FILE	*fp;
	char	line[LSIZE];
	long	blocks, nodes;
	int	n;

	if (spacefile == NULL)
		return (0);

	if ((fp = fopen(spacefile, "r")) == NULL) {
		progerr(gettext("unable to open spacefile %s"), spacefile);
		return (-1);
	}

	while (fgets(line, LSIZE, fp)) {
		struct fstable *fs_tab;
		char *pt, path[PATH_MAX];

		blocks = nodes = 0;
		for (pt = line; isspace(*pt); /* void */)
			pt++;
		if (*pt == '#' || *pt == '\0')
			continue;

		(void) sscanf(line, "%s %ld %ld", path, &blocks, &nodes);
		mappath(2, path);
		basepath(path, get_basedir(), get_inst_root());
		canonize(path);

		n = resolved_fsys(path);
		if (fsys_stat(n)) {
			(*error)++;
			continue;
		}

		/*
		 * Don't accumulate space requirements on read-only
		 * remote filesystems. NOTE: For some reason, this
		 * used to check for !remote && read only. If this
		 * blows up later, then maybe that was correct -- JST
		 */
		if (is_remote_fs_n(n) && !is_fs_writeable_n(n))
			continue;

		fs_tab = get_fs_entry(n);

		fs_tab->bused += blocks;
		fs_tab->fused += nodes;
	}
	(void) fclose(fp);
	return (0);
}