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 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 /*
27 * Name: getpathbylabel.c
28 *
29 * Description: Returns the global zone pathname corresponding
30 * to the specified label. The pathname does
31 * not need to match an existing file system object.
32 *
33 */
34 #include <stdio.h>
35 #include <string.h>
36 #include <unistd.h>
37 #include <errno.h>
38 #include <sys/types.h>
39 #include <tsol/label.h>
40 #include <stdlib.h>
41 #include <zone.h>
42 #include <sys/mntent.h>
43 #include <sys/mnttab.h>
44 #include <stdarg.h>
45
46 /*
47 * This structure is used to chain mntent structures into a list
48 * and to cache stat information for each member of the list.
49 */
50 struct mntlist {
51 struct mnttab *mntl_mnt;
52 struct mntlist *mntl_next;
53 };
54
55
56 /*
57 * Return a pointer to the trailing suffix of full that follows the prefix
58 * given by pref. If pref isn't a prefix of full, return NULL. Apply
59 * pathname semantics to the prefix test, so that pref must match at a
60 * component boundary.
61 */
62 static char *
pathsuffix(char * full,char * pref)63 pathsuffix(char *full, char *pref)
64 {
65 int preflen;
66
67 if (full == NULL || pref == NULL)
68 return (NULL);
69
70 preflen = strlen(pref);
71 if (strncmp(pref, full, preflen) != 0)
72 return (NULL);
73
74 /*
75 * pref is a substring of full. To be a subpath, it cannot cover a
76 * partial component of full. The last clause of the test handles the
77 * special case of the root.
78 */
79 if (full[preflen] != '\0' && full[preflen] != '/' && preflen > 1)
80 return (NULL);
81
82 if (preflen == 1 && full[0] == '/')
83 return (full);
84 else
85 return (full + preflen);
86 }
87
88 /*
89 * Return zero iff the path named by sub is a leading subpath
90 * of the path named by full.
91 *
92 * Treat null paths as matching nothing.
93 */
94 static int
subpath(char * full,char * sub)95 subpath(char *full, char *sub)
96 {
97 return (pathsuffix(full, sub) == NULL);
98 }
99
100 static void
tsol_mnt_free(struct mnttab * mnt)101 tsol_mnt_free(struct mnttab *mnt)
102 {
103 if (mnt->mnt_special)
104 free(mnt->mnt_special);
105 if (mnt->mnt_mountp)
106 free(mnt->mnt_mountp);
107 if (mnt->mnt_fstype)
108 free(mnt->mnt_fstype);
109 if (mnt->mnt_mntopts)
110 free(mnt->mnt_mntopts);
111 free(mnt);
112 }
113
114 static void
tsol_mlist_free(struct mntlist * mlist)115 tsol_mlist_free(struct mntlist *mlist)
116 {
117 struct mntlist *mlp;
118 struct mntlist *oldmlp;
119
120 mlp = mlist;
121 while (mlp) {
122 struct mnttab *mnt = mlp->mntl_mnt;
123
124 if (mnt)
125 tsol_mnt_free(mnt);
126 oldmlp = mlp;
127 mlp = mlp->mntl_next;
128 free(oldmlp);
129 }
130 }
131
132 static struct mnttab *
mntdup(struct mnttab * mnt)133 mntdup(struct mnttab *mnt)
134 {
135 struct mnttab *new;
136
137 new = (struct mnttab *)malloc(sizeof (*new));
138 if (new == NULL)
139 return (NULL);
140
141 new->mnt_special = NULL;
142 new->mnt_mountp = NULL;
143 new->mnt_fstype = NULL;
144 new->mnt_mntopts = NULL;
145
146 new->mnt_special = strdup(mnt->mnt_special);
147 if (new->mnt_special == NULL) {
148 tsol_mnt_free(new);
149 return (NULL);
150 }
151 new->mnt_mountp = strdup(mnt->mnt_mountp);
152 if (new->mnt_mountp == NULL) {
153 tsol_mnt_free(new);
154 return (NULL);
155 }
156 new->mnt_fstype = strdup(mnt->mnt_fstype);
157 if (new->mnt_fstype == NULL) {
158 tsol_mnt_free(new);
159 return (NULL);
160 }
161 new->mnt_mntopts = strdup(mnt->mnt_mntopts);
162 if (new->mnt_mntopts == NULL) {
163 tsol_mnt_free(new);
164 return (NULL);
165 }
166 return (new);
167 }
168
169 static struct mntlist *
tsol_mkmntlist(void)170 tsol_mkmntlist(void)
171 {
172 FILE *mounted;
173 struct mntlist *mntl;
174 struct mntlist *mntst = NULL;
175 struct mnttab mnt;
176
177 if ((mounted = fopen(MNTTAB, "rF")) == NULL) {
178 perror(MNTTAB);
179 return (NULL);
180 }
181 resetmnttab(mounted);
182 while (getmntent(mounted, &mnt) == 0) {
183 mntl = (struct mntlist *)malloc(sizeof (*mntl));
184 if (mntl == NULL) {
185 tsol_mlist_free(mntst);
186 mntst = NULL;
187 break;
188 }
189 mntl->mntl_mnt = mntdup((struct mnttab *)(&mnt));
190 if (mntl->mntl_mnt == NULL) {
191 tsol_mlist_free(mntst);
192 mntst = NULL;
193 break;
194 }
195 mntl->mntl_next = mntst;
196 mntst = mntl;
197 }
198 (void) fclose(mounted);
199 return (mntst);
200 }
201
202 /*
203 * This function attempts to convert local zone NFS mounted pathnames
204 * into equivalent global zone NFS mounted pathnames. At present
205 * it only works for automounted filesystems. It depends on the
206 * assumption that both the local and global zone automounters
207 * share the same nameservices. It also assumes that any automount
208 * map used by a local zone is available to the global zone automounter.
209 *
210 * The algorithm used consists of three phases.
211 *
212 * 1. The local zone's mnttab is searched to find the automount map
213 * with the closest matching mountpath.
214 *
215 * 2. The matching autmount map name is looked up in the global zone's
216 * mnttab to determine the path where it should be mounted in the
217 * global zone.
218 *
219 * 3. A pathname covered by an appropiate autofs trigger mount in
220 * the global zone is generated as the resolved pathname
221 *
222 * Among the things that can go wrong is that global zone doesn't have
223 * a matching automount map or the mount was not done via the automounter.
224 * Either of these cases return a NULL path.
225 */
226 #define ZONE_OPT "zone="
227 static int
getnfspathbyautofs(struct mntlist * mlist,zoneid_t zoneid,struct mnttab * autofs_mnt,char * globalpath,char * zonepath,int global_len)228 getnfspathbyautofs(struct mntlist *mlist, zoneid_t zoneid,
229 struct mnttab *autofs_mnt, char *globalpath, char *zonepath, int global_len)
230 {
231 struct mntlist *mlp;
232 char zonematch[ZONENAME_MAX + 20];
233 char zonename[ZONENAME_MAX];
234 int longestmatch;
235 struct mnttab *mountmatch;
236
237 if (autofs_mnt) {
238 mountmatch = autofs_mnt;
239 longestmatch = strlen(mountmatch->mnt_mountp);
240 } else {
241 /*
242 * First we need to get the zonename to look for
243 */
244 if (zone_getattr(zoneid, ZONE_ATTR_NAME, zonename,
245 ZONENAME_MAX) == -1) {
246 return (0);
247 }
248
249 (void) strncpy(zonematch, ZONE_OPT, sizeof (zonematch));
250 (void) strlcat(zonematch, zonename, sizeof (zonematch));
251
252 /*
253 * Find the best match for an automount map that
254 * corresponds to the local zone's pathname
255 */
256 longestmatch = 0;
257 for (mlp = mlist; mlp; mlp = mlp->mntl_next) {
258 struct mnttab *mnt = mlp->mntl_mnt;
259 int len;
260 int matchfound;
261 char *token;
262 char *lasts;
263 char mntopts[MAXPATHLEN];
264
265 if (subpath(globalpath, mnt->mnt_mountp) != 0)
266 continue;
267 if (strcmp(mnt->mnt_fstype, MNTTYPE_AUTOFS))
268 continue;
269
270 matchfound = 0;
271 (void) strncpy(mntopts, mnt->mnt_mntopts, MAXPATHLEN);
272 if ((token = strtok_r(mntopts, ",", &lasts)) != NULL) {
273 if (strcmp(token, zonematch) == 0) {
274 matchfound = 1;
275 } else while ((token = strtok_r(NULL, ",",
276 &lasts)) != NULL) {
277 if (strcmp(token, zonematch) == 0) {
278 matchfound = 1;
279 break;
280 }
281 }
282 }
283 if (matchfound) {
284 len = strlen(mnt->mnt_mountp);
285 if (len > longestmatch) {
286 mountmatch = mnt;
287 longestmatch = len;
288 }
289 }
290 }
291 }
292 if (longestmatch == 0) {
293 return (0);
294 } else {
295 /*
296 * Now we may have found the corresponding autofs mount
297 * Try to find the matching global zone autofs entry
298 */
299
300 for (mlp = mlist; mlp; mlp = mlp->mntl_next) {
301 char p[MAXPATHLEN];
302 size_t zp_len;
303 size_t mp_len;
304
305 struct mnttab *mnt = mlp->mntl_mnt;
306
307 if (strcmp(mountmatch->mnt_special,
308 mnt->mnt_special) != 0)
309 continue;
310 if (strcmp(mnt->mnt_fstype, MNTTYPE_AUTOFS))
311 continue;
312 if (strstr(mnt->mnt_mntopts, ZONE_OPT) != NULL)
313 continue;
314 /*
315 * OK, we have a matching global zone automap
316 * so adjust the path for the global zone.
317 */
318 zp_len = strlen(zonepath);
319 mp_len = strlen(mnt->mnt_mountp);
320 (void) strncpy(p, globalpath + zp_len, MAXPATHLEN);
321 /*
322 * If both global zone and zone-relative
323 * mountpoint match, just use the same pathname
324 */
325 if (strncmp(mnt->mnt_mountp, p, mp_len) == 0) {
326 (void) strncpy(globalpath, p, global_len);
327 return (1);
328 } else {
329 (void) strncpy(p, globalpath, MAXPATHLEN);
330 (void) strncpy(globalpath, mnt->mnt_mountp,
331 global_len);
332 (void) strlcat(globalpath,
333 p + strlen(mountmatch->mnt_mountp),
334 global_len);
335 return (1);
336 }
337 }
338 return (0);
339 }
340 }
341
342 /*
343 * Find the pathname for the entry in mlist that corresponds to the
344 * file named by path (i.e., that names a mount table entry for the
345 * file system in which path lies).
346 *
347 * Return 0 is there an error.
348 */
349 static int
getglobalpath(const char * path,zoneid_t zoneid,struct mntlist * mlist,char * globalpath)350 getglobalpath(const char *path, zoneid_t zoneid, struct mntlist *mlist,
351 char *globalpath)
352 {
353 struct mntlist *mlp;
354 char lofspath[MAXPATHLEN];
355 char zonepath[MAXPATHLEN];
356 int longestmatch;
357 struct mnttab *mountmatch;
358
359 if (zoneid != GLOBAL_ZONEID) {
360 char *prefix;
361
362 if ((prefix = getzonerootbyid(zoneid)) == NULL) {
363 return (0);
364 }
365 (void) strncpy(zonepath, prefix, MAXPATHLEN);
366 (void) strlcpy(globalpath, prefix, MAXPATHLEN);
367 (void) strlcat(globalpath, path, MAXPATHLEN);
368 free(prefix);
369 } else {
370 (void) strlcpy(globalpath, path, MAXPATHLEN);
371 }
372
373 for (;;) {
374 longestmatch = 0;
375 for (mlp = mlist; mlp; mlp = mlp->mntl_next) {
376 struct mnttab *mnt = mlp->mntl_mnt;
377 int len;
378
379 if (subpath(globalpath, mnt->mnt_mountp) != 0)
380 continue;
381 len = strlen(mnt->mnt_mountp);
382 if (len > longestmatch) {
383 mountmatch = mnt;
384 longestmatch = len;
385 }
386 }
387 /*
388 * Handle interesting mounts.
389 */
390 if ((strcmp(mountmatch->mnt_fstype, MNTTYPE_NFS) == 0) ||
391 (strcmp(mountmatch->mnt_fstype, MNTTYPE_AUTOFS) == 0)) {
392 if (zoneid > GLOBAL_ZONEID) {
393 struct mnttab *m = NULL;
394
395 if (strcmp(mountmatch->mnt_fstype,
396 MNTTYPE_AUTOFS) == 0)
397 m = mountmatch;
398 if (getnfspathbyautofs(mlist, zoneid, m,
399 globalpath, zonepath, MAXPATHLEN) == 0) {
400 return (0);
401 }
402 }
403 break;
404 } else if (strcmp(mountmatch->mnt_fstype, MNTTYPE_LOFS) == 0) {
405 /*
406 * count up what's left
407 */
408 int remainder;
409
410 remainder = strlen(globalpath) - longestmatch;
411 if (remainder > 0) {
412 path = pathsuffix(globalpath,
413 mountmatch->mnt_mountp);
414 (void) strlcpy(lofspath, path, MAXPATHLEN);
415 }
416 (void) strlcpy(globalpath, mountmatch->mnt_special,
417 MAXPATHLEN);
418 if (remainder > 0) {
419 (void) strlcat(globalpath, lofspath,
420 MAXPATHLEN);
421 }
422 } else {
423 if ((zoneid > GLOBAL_ZONEID) &&
424 (strncmp(path, "/home/", strlen("/home/")) == 0)) {
425 char zonename[ZONENAME_MAX];
426
427 /*
428 * If this is a cross-zone reference to
429 * a home directory, it must be corrected.
430 * We should only get here if the zone's
431 * automounter hasn't yet mounted its
432 * autofs trigger on /home.
433 *
434 * Since it is likely to do so in the
435 * future, we will assume that the global
436 * zone already has an equivalent autofs
437 * mount established. By convention,
438 * this should be mounted at the
439 * /zone/<zonename>
440 */
441
442 if (zone_getattr(zoneid, ZONE_ATTR_NAME,
443 zonename, ZONENAME_MAX) == -1) {
444 return (0);
445 } else {
446 (void) snprintf(globalpath, MAXPATHLEN,
447 "/zone/%s%s", zonename, path);
448 }
449 }
450 break;
451 }
452 }
453 return (1);
454 }
455
456
457 /*
458 * This function is only useful for global zone callers
459 * It uses the global zone mnttab to translate local zone pathnames
460 * into global zone pathnames.
461 */
462 char *
getpathbylabel(const char * path_name,char * resolved_path,size_t bufsize,const bslabel_t * sl)463 getpathbylabel(const char *path_name, char *resolved_path, size_t bufsize,
464 const bslabel_t *sl)
465 {
466 char ret_path[MAXPATHLEN]; /* pathname to return */
467 zoneid_t zoneid;
468 struct mntlist *mlist;
469
470 if (getzoneid() != GLOBAL_ZONEID) {
471 errno = EINVAL;
472 return (NULL);
473 }
474
475 if (path_name[0] != '/') { /* need absolute pathname */
476 errno = EINVAL;
477 return (NULL);
478 }
479
480 if (resolved_path == NULL) {
481 errno = EINVAL;
482 return (NULL);
483 }
484
485 if ((zoneid = getzoneidbylabel(sl)) == -1)
486 return (NULL);
487
488 /*
489 * Construct the list of mounted file systems.
490 */
491
492 if ((mlist = tsol_mkmntlist()) == NULL) {
493 return (NULL);
494 }
495 if (getglobalpath(path_name, zoneid, mlist, ret_path) == 0) {
496 tsol_mlist_free(mlist);
497 return (NULL);
498 }
499 tsol_mlist_free(mlist);
500 if (strlen(ret_path) >= bufsize) {
501 errno = EFAULT;
502 return (NULL);
503 }
504 return (strcpy(resolved_path, ret_path));
505 } /* end getpathbylabel() */
506