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#include <stdio.h>
29#include <stdlib.h>
30#include <string.h>
31#include <unistd.h>
32#include <libintl.h>
33#include <errno.h>
34#include <assert.h>
35#include <sys/types.h>
36#include <sys/mntent.h>
37#include <sys/mnttab.h>
38#include <sys/param.h>
39#include <libzonecfg.h>
40#include "zones_strings.h"
41#include "instzones_lib.h"
42
43#define	MNTTAB	"/etc/mnttab"
44
45#define	MNTTAB_HUNK	32
46
47static struct mnttab *mountTable;
48static size_t mountTableSize = 0;
49static boolean_t createdFlag = B_FALSE;
50
51/*
52 * Name		: z_createMountTable
53 * Description	: Populate the mountTable Array with mnttab entries
54 * Arguments	: void
55 * Returns	: int
56 *		  0: The Mount Table was succesfully initialized
57 *		 -1: There was an error during initialisation
58 */
59int
60z_createMountTable(void)
61{
62	FILE *fp;
63	struct mnttab ent;
64	struct mnttab *entp;
65
66	if (createdFlag) {
67		return (0);
68	}
69
70	fp = fopen(MNTTAB, "r");
71	if (fp == NULL) {
72		_z_program_error(ERR_OPEN_READ, MNTTAB, errno,
73		    strerror(errno));
74		return (-1);
75	}
76
77	/* Put the entries into the table */
78	mountTable = NULL;
79	mountTableSize = 0;
80	createdFlag = B_TRUE;
81	while (getmntent(fp, &ent) == 0) {
82		if (mountTableSize % MNTTAB_HUNK == 0) {
83			mountTable = _z_realloc(mountTable,
84			    (mountTableSize + MNTTAB_HUNK) * sizeof (ent));
85		}
86		entp = &mountTable[mountTableSize++];
87
88		/*
89		 * Zero out any fields we're not using.
90		 */
91		(void) memset(entp, 0, sizeof (*entp));
92
93		if (ent.mnt_special != NULL)
94			entp->mnt_special = _z_strdup(ent.mnt_special);
95		if (ent.mnt_mntopts != NULL)
96			entp->mnt_mntopts = _z_strdup(ent.mnt_mntopts);
97		entp->mnt_mountp = _z_strdup(ent.mnt_mountp);
98		entp->mnt_fstype = _z_strdup(ent.mnt_fstype);
99	}
100
101	(void) fclose(fp);
102	return (0);
103}
104
105/*
106 * Name		: findPathRWStatus
107 * Description	: Check whether the given path is an mnttab entry
108 * Arguments	: char * - The Path to be verified
109 * Returns	: int
110 *		  -1: The Path is NOT present in the table (mnttab)
111 *		   0: The Path is present in the table and is mounted read-only
112 *		   1: The Path is present in the table and is mounted read-write
113 */
114static int
115findPathRWStatus(const char *a_path)
116{
117	int i;
118
119	for (i = 0; i < mountTableSize; i++) {
120		if (strcmp(a_path, mountTable[i].mnt_mountp) == 0) {
121			if (hasmntopt(&mountTable[i], MNTOPT_RO) != NULL) {
122				return (0);
123			} else {
124				return (1);
125			}
126		}
127	}
128
129	return (-1);
130}
131
132
133/*
134 * Name		: z_isPathWritable
135 * Description	: Check if the given path is in a writable area
136 * Arguments	: char * - The Path to be verified
137 * Returns	: int
138 *		   0: The Path is under a read-only mount
139 *		   1: The Path is under a read-write mount
140 * NOTE		: This funcion automatically initialises
141 *		  the mountPoint table if needed.
142 */
143int
144z_isPathWritable(const char *a_str)
145{
146	int i, result, slen;
147	char a_path[MAXPATHLEN];
148
149	if (!createdFlag) {
150		if (z_createMountTable() != 0) {
151			return (1);
152		}
153	}
154
155	(void) strlcpy(a_path, a_str, sizeof (a_path));
156	slen = strlen(a_path);
157
158	/*
159	 * This for loop traverses Path backwards, incrementally removing the
160	 * basename of Path and looking for the resultant directory in the
161	 * mnttab.  Once found, it returns the rw status of that file system.
162	 */
163	for (i = slen; i > 0; i--) {
164		if ((a_path[i] == '/') || (a_path[i] == '\0')) {
165			a_path[i] = '\0';
166			result = findPathRWStatus(a_path);
167			if (result != -1) {
168				return (result);
169			}
170		}
171	}
172
173	return (1);
174}
175
176/*
177 * Name		: z_destroyMountTable
178 * Description	: Clear the entries in the mount table
179 * Arguments	: void
180 * Returns	: void
181 */
182void
183z_destroyMountTable(void)
184{
185	int i;
186
187	if (!createdFlag) {
188		return;
189	}
190
191	if (mountTable == NULL) {
192		return;
193	}
194
195	for (i = 0; i < mountTableSize; i++) {
196		free(mountTable[i].mnt_mountp);
197		free(mountTable[i].mnt_fstype);
198		free(mountTable[i].mnt_special);
199		free(mountTable[i].mnt_mntopts);
200		assert(mountTable[i].mnt_time == NULL);
201	}
202
203	free(mountTable);
204	mountTable = NULL;
205	mountTableSize = 0;
206	createdFlag = B_FALSE;
207}
208
209/*
210 * Name		: z_resolve_lofs
211 * Description	: Loop over potential loopback mounts and symlinks in a
212 *		  given path and resolve them all down to an absolute path.
213 * Arguments	: char * - path to resolve.  path is in writable storage.
214 *		  size_t - length of path storage.
215 * Returns	: void
216 */
217void
218z_resolve_lofs(char *path, size_t pathlen)
219{
220	int len, arlen, i;
221	const char *altroot;
222	char tmppath[MAXPATHLEN];
223	boolean_t outside_altroot;
224
225	if ((len = resolvepath(path, tmppath, sizeof (tmppath))) == -1)
226		return;
227
228	tmppath[len] = '\0';
229	(void) strlcpy(path, tmppath, pathlen);
230
231	if (z_createMountTable() == -1)
232		return;
233
234	altroot = zonecfg_get_root();
235	arlen = strlen(altroot);
236	outside_altroot = B_FALSE;
237	for (;;) {
238		struct mnttab *mnp;
239
240		/* Search in reverse order to find longest match */
241		for (i = mountTableSize; i > 0; i--) {
242			mnp = &mountTable[i - 1];
243			if (mnp->mnt_fstype == NULL ||
244			    mnp->mnt_mountp == NULL ||
245			    mnp->mnt_special == NULL)
246				continue;
247			len = strlen(mnp->mnt_mountp);
248			if (strncmp(mnp->mnt_mountp, path, len) == 0 &&
249			    (path[len] == '/' || path[len] == '\0'))
250				break;
251		}
252		if (i <= 0)
253			break;
254
255		/* If it's not a lofs then we're done */
256		if (strcmp(mnp->mnt_fstype, MNTTYPE_LOFS) != 0)
257			break;
258
259		if (outside_altroot) {
260			char *cp;
261			int olen = sizeof (MNTOPT_RO) - 1;
262
263			/*
264			 * If we run into a read-only mount outside of the
265			 * alternate root environment, then the user doesn't
266			 * want this path to be made read-write.
267			 */
268			if (mnp->mnt_mntopts != NULL &&
269			    (cp = strstr(mnp->mnt_mntopts, MNTOPT_RO)) !=
270			    NULL &&
271			    (cp == mnp->mnt_mntopts || cp[-1] == ',') &&
272			    (cp[olen] == '\0' || cp[olen] == ',')) {
273				break;
274			}
275		} else if (arlen > 0 &&
276		    (strncmp(mnp->mnt_special, altroot, arlen) != 0 ||
277		    (mnp->mnt_special[arlen] != '\0' &&
278		    mnp->mnt_special[arlen] != '/'))) {
279			outside_altroot = B_TRUE;
280		}
281		/* use temporary buffer because new path might be longer */
282		(void) snprintf(tmppath, sizeof (tmppath), "%s%s",
283		    mnp->mnt_special, path + len);
284		if ((len = resolvepath(tmppath, path, pathlen)) == -1)
285			break;
286		path[len] = '\0';
287	}
288}
289