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