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 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  * This module contains functions used for reading and writing the scratch zone
29  * translation files.  These files are used by Live Upgrade to keep track of
30  * mappings between actual kernel zone names and the zones in an alternate boot
31  * environment.
32  *
33  * The functions are MT-safe.
34  *
35  * The file format looks like this:
36  *
37  *	<zonename>	<kernel-zonename>	<alt-root>
38  *
39  * The expected usage model is:
40  *
41  *	fp = zonecfg_open_scratch("", B_TRUE);
42  *	zonecfg_lock_scratch(fp);
43  *	if (zonecfg_find_scratch(fp, zonename, altroot, NULL, 0) == 0) {
44  *		handle error; zone already mounted
45  *	}
46  *	mount zone here
47  *	zonecfg_add_scratch(fp, zonename, kernname, altroot);
48  *	zonecfg_close_scratch(fp);
49  *	fp = zonecfg_open_scratch(zoneroot, B_TRUE);
50  *	ftruncate(fileno(fp), 0);
51  *	zonecfg_add_scratch(fp, zonename, kernname, "/");
52  *	zonecfg_close_scratch(fp);
53  */
54 
55 #include <stdio.h>
56 #include <unistd.h>
57 #include <fcntl.h>
58 #include <errno.h>
59 #include <string.h>
60 #include <sys/types.h>
61 #include <sys/stat.h>
62 #include <sys/param.h>
63 #include <libzonecfg.h>
64 
65 #define	PATH_MAPFILE	"tmp/.alt.lu-zone-map"
66 
67 static int
lock_op(int fd,int type)68 lock_op(int fd, int type)
69 {
70 	struct flock lock;
71 
72 	lock.l_type = type;
73 	lock.l_whence = SEEK_SET;
74 	lock.l_start = 0;
75 	lock.l_len = 0;
76 
77 	return (fcntl(fd, F_SETLKW, &lock));
78 }
79 
80 FILE *
zonecfg_open_scratch(const char * rootpath,boolean_t createfile)81 zonecfg_open_scratch(const char *rootpath, boolean_t createfile)
82 {
83 	mode_t oldmask = umask(0);
84 	struct stat lbuf, fbuf;
85 	int fd, flags;
86 	FILE *fp;
87 	char mapfile[MAXPATHLEN];
88 
89 	(void) snprintf(mapfile, sizeof (mapfile), "%s/" PATH_MAPFILE,
90 	    rootpath);
91 
92 	flags = O_RDWR | O_NOFOLLOW | O_NOLINKS;
93 	if (createfile)
94 		flags |= O_EXCL | O_CREAT;
95 	if ((fd = open(mapfile, flags, 0644)) == -1) {
96 		if (!createfile) {
97 			errno = ENOENT;
98 			goto failure;
99 		}
100 		if (lstat(mapfile, &lbuf) == -1)
101 			goto failure;
102 		if (!S_ISREG(lbuf.st_mode) || lbuf.st_nlink != 1 ||
103 		    lbuf.st_uid != 0) {
104 			errno = EINVAL;
105 			goto failure;
106 		}
107 		fd = open(mapfile, O_RDWR);
108 		if (fd == -1)
109 			goto failure;
110 		if (fstat(fd, &fbuf) == -1)
111 			goto failure;
112 		if (lbuf.st_ino != fbuf.st_ino || lbuf.st_dev != fbuf.st_dev) {
113 			errno = EINVAL;
114 			goto failure;
115 		}
116 	}
117 	if (lock_op(fd, F_RDLCK) == -1)
118 		goto failure;
119 	(void) umask(oldmask);
120 	if ((fp = fdopen(fd, "r+")) == NULL)
121 		(void) close(fd);
122 	return (fp);
123 
124 failure:
125 	if (fd != -1)
126 		(void) close(fd);
127 	(void) umask(oldmask);
128 	return (NULL);
129 }
130 
131 int
zonecfg_lock_scratch(FILE * fp)132 zonecfg_lock_scratch(FILE *fp)
133 {
134 	if (fflush(fp) != 0)
135 		return (-1);
136 	return (lock_op(fileno(fp), F_WRLCK));
137 }
138 
139 void
zonecfg_close_scratch(FILE * fp)140 zonecfg_close_scratch(FILE *fp)
141 {
142 	(void) fclose(fp);
143 }
144 
145 int
zonecfg_get_scratch(FILE * fp,char * zonename,size_t namelen,char * kernname,size_t kernlen,char * altroot,size_t altlen)146 zonecfg_get_scratch(FILE *fp, char *zonename, size_t namelen, char *kernname,
147     size_t kernlen, char *altroot, size_t altlen)
148 {
149 	char line[2 * ZONENAME_MAX + MAXPATHLEN + 2];
150 	char *cp, *cp2;
151 
152 	/* We always hold at least a read lock on the file */
153 	for (;;) {
154 		if (fgets(line, sizeof (line), fp) == NULL)
155 			return (-1);
156 		if ((cp = strchr(line, '\n')) == NULL)
157 			return (-1);
158 		*cp = '\0';
159 		if ((cp = strchr(line, ' ')) == NULL)
160 			cp = line + strlen(line);
161 		else
162 			*cp++ = '\0';
163 		if (zonename != NULL &&
164 		    strlcpy(zonename, line, namelen) >= namelen)
165 			continue;
166 		if ((cp2 = strchr(cp, ' ')) == NULL)
167 			cp2 = cp + strlen(cp);
168 		else
169 			*cp2++ = '\0';
170 		if (kernname != NULL &&
171 		    strlcpy(kernname, cp, kernlen) >= kernlen)
172 			continue;
173 		if (altroot != NULL && strlcpy(altroot, cp2, altlen) >= altlen)
174 			continue;
175 		break;
176 	}
177 	return (0);
178 }
179 
180 int
zonecfg_find_scratch(FILE * fp,const char * zonename,const char * altroot,char * kernzone,size_t kernlen)181 zonecfg_find_scratch(FILE *fp, const char *zonename, const char *altroot,
182     char *kernzone, size_t kernlen)
183 {
184 	char zone[ZONENAME_MAX];
185 	char aroot[MAXPATHLEN];
186 
187 	rewind(fp);
188 	while (zonecfg_get_scratch(fp, zone, sizeof (zone), kernzone, kernlen,
189 	    aroot, sizeof (aroot)) == 0) {
190 		if (strcmp(zone, zonename) == 0 && strcmp(altroot, aroot) == 0)
191 			return (0);
192 	}
193 	return (-1);
194 }
195 
196 int
zonecfg_reverse_scratch(FILE * fp,const char * kernzone,char * zonename,size_t namelen,char * altroot,size_t altlen)197 zonecfg_reverse_scratch(FILE *fp, const char *kernzone, char *zonename,
198     size_t namelen, char *altroot, size_t altlen)
199 {
200 	char kzone[ZONENAME_MAX];
201 
202 	rewind(fp);
203 	while (zonecfg_get_scratch(fp, zonename, namelen, kzone,
204 	    sizeof (kzone), altroot, altlen) == 0) {
205 		if (strcmp(kzone, kernzone) == 0)
206 			return (0);
207 	}
208 	return (-1);
209 }
210 
211 int
zonecfg_add_scratch(FILE * fp,const char * zonename,const char * kernzone,const char * altroot)212 zonecfg_add_scratch(FILE *fp, const char *zonename, const char *kernzone,
213     const char *altroot)
214 {
215 	if (fseek(fp, 0, SEEK_END) == -1)
216 		return (-1);
217 	if (fprintf(fp, "%s %s %s\n", zonename, kernzone, altroot) == EOF)
218 		return (-1);
219 	if (fflush(fp) != 0)
220 		return (-1);
221 	return (0);
222 }
223 
224 int
zonecfg_delete_scratch(FILE * fp,const char * kernzone)225 zonecfg_delete_scratch(FILE *fp, const char *kernzone)
226 {
227 	char zone[ZONENAME_MAX];
228 	char kzone[ZONENAME_MAX];
229 	char aroot[MAXPATHLEN];
230 	long roffs, woffs;
231 
232 	/*
233 	 * The implementation here is intentionally quite simple.  We could
234 	 * allocate a buffer that's big enough to hold the data up to
235 	 * stat.st_size and then write back out the part we need to, but there
236 	 * seems to be little point.
237 	 */
238 	rewind(fp);
239 	roffs = 0;
240 	do {
241 		woffs = roffs;
242 		if (zonecfg_get_scratch(fp, NULL, 0, kzone, sizeof (kzone),
243 		    NULL, 0) != 0)
244 			return (-1);
245 		roffs = ftell(fp);
246 	} while (strcmp(kzone, kernzone) != 0);
247 	while (zonecfg_get_scratch(fp, zone, sizeof (zone), kzone,
248 	    sizeof (kzone), aroot, sizeof aroot) == 0) {
249 		roffs = ftell(fp);
250 		if (fseek(fp, woffs, SEEK_SET) == -1)
251 			break;
252 		if (fprintf(fp, "%s %s %s\n", zone, kzone, aroot) == EOF)
253 			break;
254 		woffs = ftell(fp);
255 		if (fseek(fp, roffs, SEEK_SET) == -1)
256 			break;
257 	}
258 	(void) ftruncate(fileno(fp), woffs);
259 	return (0);
260 }
261 
262 boolean_t
zonecfg_is_scratch(const char * kernzone)263 zonecfg_is_scratch(const char *kernzone)
264 {
265 	return (strncmp(kernzone, "SUNWlu", 6) == 0);
266 }
267