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