# # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright (c) 1992, 2010, Oracle and/or its affiliates. All rights reserved. # Copyright 2015 RackTop Systems. # Copyright (c) 2018, Joyent, Inc. # FSTYPE= autofs AUTO= automount MOUNT= mount VERS= .2 DFSHARES= dfshares SHARE= share UNSHARE= unshare LIBPROG= $(AUTO) $(MOUNT) $(DFSHARES) $(SHARE) $(UNSHARE) TYPEPROG= automountd LINKINSTALL= $(ROOTUSRSBIN)/$(AUTO) LINKVALUE= ../lib/fs/$(FSTYPE)/$(AUTO) MAPS= auto_master auto_home MAPINSTALL= $(MAPS:%=$(ROOTETC)/%) AUTOFS= autofs DEFAULTFILES= autofs.dfl SMFMANIFEST= autofs.xml SMFMETHOD= svc-autofs MFSTINSTALL= $(SMFMANIFEST:%=$(ROOTSVCSYSTEM)/filesystem/%) METHODINSTALL= $(SMFMETHOD:%=$(ROOTLIBSVCMETHOD)/%) OTHERINSTALL= $(MAPINSTALL) $(LINKINSTALL) $(ROOTETCDEFAULTFILES) \ $(MFSTINSTALL) $(METHODINSTALL) UNCHECKED_HDRS= webnfs.h MANIFEST= autofs.xml SVCMETHOD= svc-autofs include ../Makefile.fstype $(MAPINSTALL) : FILEMODE= 0644 $(MFSTINSTALL) : FILEMODE = 0444 # Hammerhead: ns_ldap.o removed (libsldap chain removal, Phase 1). REAL_COMMON= debug_alloc.o COMMON= ns_generic.o ns_files.o ns_nis.o \ auto_mnttab.o auto_subr.o $(REAL_COMMON) AUTOOBJS= automount.o $(COMMON) $(FSLIB) smfcfg.o MOUNTOBJS= mount.o $(FSLIB) $(REAL_COMMON) LOCAL= autod_main.o \ autod_parse.o autod_mount.o autod_nfs.o nfs_cast.o \ autod_autofs.o autod_xdr.o autod_readdir.o autod_lookup.o \ smfcfg.o TYPEOBJS= $(LOCAL) $(COMMON) replica.o nfs_sec.o nfs_resolve.o nfs_subr.o \ $(FSLIB) webnfs_xdr.o webnfs_client.o selfcheck.o SHAREOBJS= $(SHARE:%=%.o) UNSHAREOBJS= $(UNSHARE:%=%.o) POFILE= autofs.po $(AUTO) : LDLIBS += -lnsl -lscf $(MOUNT): LDLIBS += -lscf $(TYPEPROG) : LDLIBS += -lrpcsvc -lsocket -lnsl -lkstat -lscf CSTD= $(CSTD_GNU99) CFLAGS += $(CCVERBOSE) -D_FILE_OFFSET_BITS=64 CPPFLAGS= -I. -I.. -I../nfs/lib $(CPPFLAGS.master) -D_REENTRANT \ $(MALLOC_DEBUG) CERRWARN += -Wno-parentheses CERRWARN += -Wno-unused-variable CERRWARN += -Wno-switch CERRWARN += $(CNOWARN_UNINIT) CERRWARN += -Wno-unused-function # Hammerhead: Suppress pointer/int cast warnings in legacy autofs code CERRWARN += -Wno-pointer-to-int-cast CERRWARN += -Wno-int-to-pointer-cast # not linted SMATCH=off OBJS= $(AUTOOBJS) $(MOUNTOBJS) $(TYPEOBJS) \ $(SHAREOBJS) $(UNSHAREOBJS) nfs_sec.o : CPPFLAGS += -DWNFS_SEC_NEGO $(AUTO): $(AUTOOBJS) $(LINK.c) -o $@ $(AUTOOBJS) $(LDLIBS) $(POST_PROCESS) $(MOUNT): $(MOUNTOBJS) $(LINK.c) -o $@ $(MOUNTOBJS) $(LDLIBS) $(POST_PROCESS) $(TYPEPROG): webnfs.h $(TYPEOBJS) $(LINK.c) -o $@ $(TYPEOBJS) $(LDLIBS) $(POST_PROCESS) $(SHARE): $(SHAREOBJS) $(LINK.c) -o $@ $(SHAREOBJS) $(LDLIBS) $(POST_PROCESS) $(UNSHARE): $(UNSHAREOBJS) $(LINK.c) -o $@ $(UNSHAREOBJS) $(LDLIBS) $(POST_PROCESS) $(ROOTSVCSYSTEM)/filesystem/%: % $(INS.file) DUMP_PROG=malloc_dump BUILDDIR=tmp DUMP_PROTO=$(BUILDDIR)/$(DUMP_PROG)_client XFILE=$(BUILDDIR)/malloc_dump.x XLINE= "program DUMP { version VERS \ { void DUMP_IT(void) = 1000000; } = 2; } = 100099;" $(XFILE): $(BUILDDIR) $(RM) $(XFILE) $(ECHO) $(XLINE) > $(XFILE) $(BUILDDIR): $(RM) -r $(BUILDDIR) $(MKDIR) $(BUILDDIR) $(DUMP_PROTO): $(BUILDDIR) $(XFILE) cd $(BUILDDIR); $(RM) *.[ch] makefile.malloc_dump ; rpcgen -a `basename $(XFILE) cd $(BUILDDIR); $(MAKE) -f makefile.malloc_dump `basename $@` $(DUMP_PROG): $(DUMP_PROTO) $(RM) $@ $(CP) $(DUMP_PROTO) $@ clean_dump: $(RM) -r $(BUILDDIR) $(RM) $(DUMP_PROG) $(ROOTUSRSBIN)/$(AUTO): $(RM) $@; $(SYMLINK) $(LINKVALUE) $@ replica.o: ../nfs/lib/replica.c $(COMPILE.c) ../nfs/lib/replica.c nfs_sec.o: ../nfs/lib/nfs_sec.c $(COMPILE.c) ../nfs/lib/nfs_sec.c nfs_subr.o: ../nfs/lib/nfs_subr.c $(COMPILE.c) ../nfs/lib/nfs_subr.c selfcheck.o: ../nfs/lib/selfcheck.c $(COMPILE.c) ../nfs/lib/selfcheck.c smfcfg.o: ../nfs/lib/smfcfg.c $(COMPILE.c) ../nfs/lib/smfcfg.c nfs_resolve.o: ../nfs/lib/nfs_resolve.c $(COMPILE.c) ../nfs/lib/nfs_resolve.c # Hammerhead: pre-generated (rpcgen + GNU cpp truncation bug) # webnfs.h, webnfs_xdr.c, webnfs_client.c are now committed source files. webnfs.x: ../nfs/lib/webnfs.x $(RM) webnfs.x $(CP) ../nfs/lib/webnfs.x . catalog: $(POFILE) $(POFILE): $(RM) messages.po $(SED) -e 's/pr_msg/gettext/' `$(EGREP) -l "pr_msg|gettext" *.[ch]` | \ $(XGETTEXT) $(XGETFLAGS) - $(SED) -e '/^# msg/d' -e '/^domain/d' < messages.po > $@ $(RM) messages.po clean: clean_dump $(RM) $(OBJS) webnfs.x @# Hammerhead: do not delete pre-generated webnfs_xdr.c webnfs_client.c webnfs.h check: $(CHKMANIFEST) # # Copyright 2005 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License, Version 1.0 only # (the "License"). You may not use this file except in compliance # with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # ident "%Z%%M% %I% %E% SMI" # # Home directory map for automounter # +auto_home # # Copyright 2005 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License, Version 1.0 only # (the "License"). You may not use this file except in compliance # with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # ident "%Z%%M% %I% %E% SMI" # # Master map for automounter # +auto_master /net -hosts -nosuid,nobrowse /home auto_home -nobrowse /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * auto_mnttab.c * * Copyright (c) 1988-1999 Sun Microsystems Inc * All Rights Reserved. */ #include #include #include #include "automount.h" static mutex_t mnttab_lock = DEFAULTMUTEX; /* * XXX - Serialize calls to fsgetmntlist, so that threads can get consistent * snapshots of the in-kernel mnttab. This function should be removed * once getextmntent is made MT-safe and callers can invoke fsgetmntlist * directly. */ struct mntlist * getmntlist() { struct mntlist *mntl; (void) mutex_lock(&mnttab_lock); mntl = fsgetmntlist(); (void) mutex_unlock(&mnttab_lock); return (mntl); } /* * Return device number from extmnttab struct */ dev_t get_devid(mnt) struct extmnttab *mnt; { return (makedev(mnt->mnt_major, mnt->mnt_minor)); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "automount.h" #include #include #include #include #include #include #include "smfcfg.h" static char *check_hier(char *); static int arch(char *, size_t, bool_t); static int cpu(char *, size_t); static int natisa(char *, size_t); static int platform(char *, size_t); struct mntlist *current_mounts; static bool_t nodirect_map = FALSE; /* * If the system is labeled then we need to * have a uniquely-named auto_home map for each zone. * The maps are made unique by appending the zonename. * The home directory is made unique by prepending /zone/ * for each zone that is dominated by the current zone. * The current zone's home directory mount point is not changed. * * For each auto_home_ a default template map is created * only if it doesn't exist yet. The default entry is used to declare * local home directories created within each zone. For example: * * +auto_home_public * * -fstype=lofs :/zone/public/export/home/& */ static void loadzone_maps(char *mntpnt, char *map, char *opts, char **stack, char ***stkptr) { zoneid_t *zids = NULL; zoneid_t my_zoneid; uint_t nzents_saved; uint_t nzents; int i; if (!priv_ineffect(PRIV_SYS_MOUNT)) return; if (zone_list(NULL, &nzents) != 0) { return; } my_zoneid = getzoneid(); again: if (nzents == 0) return; zids = malloc(nzents * sizeof (zoneid_t)); nzents_saved = nzents; if (zone_list(zids, &nzents) != 0) { free(zids); return; } if (nzents != nzents_saved) { /* list changed, try again */ free(zids); goto again; } for (i = 0; i < nzents; i++) { char zonename[ZONENAME_MAX]; char zoneroot[MAXPATHLEN]; if (getzonenamebyid(zids[i], zonename, ZONENAME_MAX) != -1) { char appended_map[MAXPATHLEN]; char prepended_mntpnt[MAXPATHLEN]; char map_path[MAXPATHLEN]; int fd; (void) snprintf(appended_map, sizeof (appended_map), "%s_%s", map, zonename); /* for current zone, leave mntpnt alone */ if (zids[i] != my_zoneid) { (void) snprintf(prepended_mntpnt, sizeof (prepended_mntpnt), "/zone/%s%s", zonename, mntpnt); if (zone_getattr(zids[i], ZONE_ATTR_ROOT, zoneroot, sizeof (zoneroot)) == -1) continue; } else { (void) strcpy(prepended_mntpnt, mntpnt); zoneroot[0] = '\0'; } dirinit(prepended_mntpnt, appended_map, opts, 0, stack, stkptr); /* * Next create auto_home_ maps for each zone */ (void) snprintf(map_path, sizeof (map_path), "/etc/%s", appended_map); /* * If the map file doesn't exist create a template */ if ((fd = open(map_path, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR | S_IRGRP| S_IROTH)) != -1) { int len; char map_rec[MAXPATHLEN]; len = snprintf(map_rec, sizeof (map_rec), "+%s\n*\t-fstype=lofs\t:%s/export/home/&\n", appended_map, zoneroot); if (len <= sizeof (map_rec)) (void) write(fd, map_rec, len); (void) close(fd); } } } free(zids); } void dirinit(char *mntpnt, char *map, char *opts, int direct, char **stack, char ***stkptr) { struct autodir *dir; char *p; if (strcmp(map, "-null") == 0) { if (strcmp(mntpnt, "/-") == 0) nodirect_map = TRUE; goto enter; } p = mntpnt + (strlen(mntpnt) - 1); if (*p == '/') *p = '\0'; /* trim trailing / */ if (*mntpnt != '/') { pr_msg("dir %s must start with '/'", mntpnt); return; } if (p = check_hier(mntpnt)) { pr_msg("hierarchical mountpoint: %s and %s", p, mntpnt); return; } /* * If it's a direct map then call dirinit * for every map entry. */ if ((strcmp(mntpnt, "/-") == 0) && !(nodirect_map)) { (void) loaddirect_map(map, map, opts, stack, stkptr); return; } /* * Home directories are polyinstantiated on * labeled systems. */ if (is_system_labeled() && (strcmp(mntpnt, "/home") == 0) && (strcmp(map, "auto_home") == 0)) { (void) loadzone_maps(mntpnt, map, opts, stack, stkptr); return; } enter: dir = (struct autodir *)malloc(sizeof (*dir)); if (dir == NULL) goto alloc_failed; dir->dir_name = strdup(mntpnt); if (dir->dir_name == NULL) goto alloc_failed; dir->dir_map = strdup(map); if (dir->dir_map == NULL) goto alloc_failed; dir->dir_opts = strdup(opts); if (dir->dir_opts == NULL) goto alloc_failed; dir->dir_direct = direct; dir->dir_remount = 0; dir->dir_next = NULL; /* * Append to dir chain */ if (dir_head == NULL) dir_head = dir; else dir_tail->dir_next = dir; dir->dir_prev = dir_tail; dir_tail = dir; return; alloc_failed: if (dir != NULL) { if (dir->dir_opts) free(dir->dir_opts); if (dir->dir_map) free(dir->dir_map); if (dir->dir_name) free(dir->dir_name); free(dir); } pr_msg("dirinit: memory allocation failed"); } /* * Check whether the mount point is a * subdirectory or a parent directory * of any previously mounted automount * mount point. */ static char * check_hier(mntpnt) char *mntpnt; { register struct autodir *dir; register char *p, *q; for (dir = dir_head; dir; dir = dir->dir_next) { p = dir->dir_name; q = mntpnt; for (; *p == *q; p++, q++) if (*p == '\0') break; if (*p == '/' && *q == '\0') return (dir->dir_name); if (*p == '\0' && *q == '/') return (dir->dir_name); if (*p == '\0' && *q == '\0') return (NULL); } return (NULL); /* it's not a subdir or parent */ } /* * Gets the next token from the string "p" and copies it into "w". The "wq" is * a quote vector for "w" and is derived from "pq", which is a quote vector for * "p". Delim is the character to be used as a delimiter for the scan. A space * means "whitespace". The call to getword must provide buffers w and wq of size * at least wordsz. getword() will pass strings of maximum length (wordsz-1), * since it needs to null terminate the string. * Returns 0 on ok and -1 on error. */ int getword(char *w, char *wq, char **p, char **pq, char delim, int wordsz) { char *tmp = w; char *tmpq = wq; int count = wordsz; if (wordsz <= 0) { if (verbose) syslog(LOG_ERR, "getword: input word size %d must be > 0", wordsz); return (-1); } while ((delim == ' ' ? isspace(**p) : **p == delim) && **pq == ' ') (*p)++, (*pq)++; while (**p && !((delim == ' ' ? isspace(**p) : **p == delim) && **pq == ' ')) { if (--count <= 0) { *tmp = '\0'; *tmpq = '\0'; syslog(LOG_ERR, "maximum word length (%d) exceeded", wordsz); return (-1); } *w++ = *(*p)++; *wq++ = *(*pq)++; } *w = '\0'; *wq = '\0'; return (0); } /* * get_line attempts to get a line from the map, upto LINESZ. A line in * the map is a concatenation of lines if the continuation symbol '\' * is used at the end of the line. Returns line on success, a NULL on * EOF, and an empty string on lines > linesz. */ char * get_line(FILE *fp, char *map, char *line, int linesz) { register char *p = line; register int len; int excess = 0; *p = '\0'; for (;;) { if (fgets(p, linesz - (p-line), fp) == NULL) { return (*line ? line : NULL); /* EOF */ } len = strlen(line); if (len <= 0) { p = line; continue; } p = &line[len - 1]; /* * Is input line too long? */ if (*p != '\n') { excess = 1; /* * Perhaps last char read was '\'. Reinsert it * into the stream to ease the parsing when we * read the rest of the line to discard. */ (void) ungetc(*p, fp); break; } trim: /* trim trailing white space */ while (p >= line && isspace(*(uchar_t *)p)) *p-- = '\0'; if (p < line) { /* empty line */ p = line; continue; } if (*p == '\\') { /* continuation */ *p = '\0'; continue; } /* * Ignore comments. Comments start with '#' * which must be preceded by a whitespace, unless * if '#' is the first character in the line. */ p = line; while (p = strchr(p, '#')) { if (p == line || isspace(*(p-1))) { *p-- = '\0'; goto trim; } p++; } break; } if (excess) { int c; /* * discard rest of line and return an empty string. * done to set the stream to the correct place when * we are done with this line. */ while ((c = getc(fp)) != EOF) { *p = c; if (*p == '\n') /* end of the long line */ break; else if (*p == '\\') { /* continuation */ if (getc(fp) == EOF) /* ignore next char */ break; } } syslog(LOG_ERR, "map %s: line too long (max %d chars)", map, linesz-1); *line = '\0'; } return (line); } /* * Gets the retry=n entry from opts. * Returns 0 if retry=n is not present in option string, * retry=n is invalid, or when option string is NULL. */ int get_retry(char *opts) { int retry = 0; char buf[MAXOPTSLEN]; char *p, *pb, *lasts; if (opts == NULL) return (retry); (void) strcpy(buf, opts); pb = buf; while (p = (char *)strtok_r(pb, ",", &lasts)) { pb = NULL; if (strncmp(p, "retry=", 6) == 0) retry = atoi(p+6); } return (retry > 0 ? retry : 0); } /* * Returns zero if "opt" is found in mnt->mnt_opts, setting * *sval to whatever follows the equal sign after "opt". * str_opt allocates a string long enough to store the value of * "opt" plus a terminating null character and returns it as *sval. * It is the responsability of the caller to deallocate *sval. * *sval will be equal to NULL upon return if either "opt=" is not found, * or "opt=" has no value associated with it. * * stropt will return -1 on error. */ int str_opt(struct mnttab *mnt, char *opt, char **sval) { char *str, *comma; /* * is "opt" in the options field? */ if (str = hasmntopt(mnt, opt)) { str += strlen(opt); if (*str++ != '=' || (*str == ',' || *str == '\0')) { syslog(LOG_ERR, "Bad option field"); return (-1); } comma = strchr(str, ','); if (comma != NULL) *comma = '\0'; *sval = strdup(str); if (comma != NULL) *comma = ','; if (*sval == NULL) return (-1); } else *sval = NULL; return (0); } /* * Performs text expansions in the string "pline". * "plineq" is the quote vector for "pline". * An identifier prefixed by "$" is replaced by the * corresponding environment variable string. A "&" * is replaced by the key string for the map entry. * * This routine will return an error (non-zero) if *size* would be * exceeded after expansion, indicating that the macro_expand failed. * This is to prevent writing past the end of pline and plineq. * Both pline and plineq are left untouched in such error case. */ int macro_expand(key, pline, plineq, size) char *key, *pline, *plineq; int size; { register char *p, *q; register char *bp, *bq; register char *s; char buffp[LINESZ], buffq[LINESZ]; char namebuf[64], *pn; int expand = 0; struct utsname name; char procbuf[SYS_NMLN]; char isaname[64]; p = pline; q = plineq; bp = buffp; bq = buffq; while (*p) { if (*p == '&' && *q == ' ') { /* insert key */ /* * make sure we don't overflow buffer */ if ((int)((bp - buffp) + strlen(key)) < size) { for (s = key; *s; s++) { *bp++ = *s; *bq++ = ' '; } expand++; p++; q++; continue; } else { /* * line too long... */ return (1); } } if (*p == '$' && *q == ' ') { /* insert env var */ p++; q++; pn = namebuf; if (*p == '{') { p++; q++; while (*p && *p != '}') { *pn++ = *p++; q++; } if (*p) { p++; q++; } } else { while (*p && (*p == '_' || isalnum(*p))) { *pn++ = *p++; q++; } } *pn = '\0'; s = getenv(namebuf); if (!s) { /* not found in env */ if (strcmp(namebuf, "ARCH") == 0) { if (arch(procbuf, sizeof (procbuf), FALSE)) s = procbuf; } else if (strcmp(namebuf, "CPU") == 0) { if (cpu(procbuf, sizeof (procbuf))) s = procbuf; } else if (strcmp(namebuf, "HOST") == 0) { (void) uname(&name); s = name.nodename; } else if (strcmp(namebuf, "KARCH") == 0) { if (arch(procbuf, sizeof (procbuf), TRUE)) s = procbuf; } else if (strcmp(namebuf, "OSREL") == 0) { (void) uname(&name); s = name.release; } else if (strcmp(namebuf, "OSNAME") == 0) { (void) uname(&name); s = name.sysname; } else if (strcmp(namebuf, "OSVERS") == 0) { (void) uname(&name); s = name.version; } else if (strcmp(namebuf, "NATISA") == 0) { if (natisa(isaname, sizeof (isaname))) s = isaname; } else if (strcmp(namebuf, "PLATFORM") == 0) { if (platform(procbuf, sizeof (procbuf))) s = procbuf; } } if (s) { if ((int)((bp - buffp) + strlen(s)) < size) { while (*s) { *bp++ = *s++; *bq++ = ' '; } } else { /* * line too long... */ return (1); } } expand++; continue; } /* * Since buffp needs to be null terminated, we need to * check that there's still room in the buffer to * place at least two more characters, *p and the * terminating null. */ if (bp - buffp == size - 1) { /* * There was not enough room for at least two more * characters, return with an error. */ return (1); } /* * The total number of characters so far better be less * than the size of buffer passed in. */ *bp++ = *p++; *bq++ = *q++; } if (!expand) return (0); *bp = '\0'; *bq = '\0'; /* * We know buffp/buffq will fit in pline/plineq since we * processed at most size characters. */ (void) strcpy(pline, buffp); (void) strcpy(plineq, buffq); return (0); } /* * Removes backslashes, quotes and brackets from the string "str" * and returns the quoting information in "qbuf". Character is * considered escaped when it is * * preceded with '\' e.g. \a * within quotes e.g. "string" * a ':' in brackets e.g. [an:ip:6::ad::d:re:s:s] * * original str: 'the "brown" f\ox said: [fe80::20a:e4ff:fe35:8b0d]' * unquoted str: 'the brown fox said: [fe80::20a:e4ff:fe35:8b0d]' * and the qbuf: ' ^^^^^ ^ ^^ ^ ^ ^ ' */ void unquote(str, qbuf) char *str, *qbuf; { register int escaped, inquote, inbracket, quoted; register char *ip, *bp, *qp; char buf[LINESZ]; escaped = inquote = inbracket = quoted = 0; for (ip = str, bp = buf, qp = qbuf; *ip; ip++) { if (!escaped) { if (*ip == '\\') { escaped = 1; quoted++; continue; } else if (*ip == '"') { inquote = !inquote; quoted++; continue; } else if (*ip == '[') { inbracket++; quoted++; } else if (*ip == ']') { if (inbracket > 0) inbracket--; } } *bp++ = *ip; *qp++ = (inquote || escaped) ? '^' : ((inbracket && (*ip == ':')) ? '^' : ' '); escaped = 0; } *bp = '\0'; *qp = '\0'; if (quoted) (void) strcpy(str, buf); } /* * If str is enclosed in [brackets], trim them off. */ void unbracket(s) char **s; { char *b = *s + strlen(*s) - 1; if (*b == ']') *b = '\0'; if (**s == '[') (*s)++; } /* * Removes trailing spaces from string "s". */ void trim(s) char *s; { char *p = &s[strlen(s) - 1]; while (p >= s && isspace(*(uchar_t *)p)) *p-- = '\0'; } /* * try to allocate memory using malloc, if malloc fails, then flush the * rddir caches, and retry. If the second allocation after the readdir * caches have been flushed fails too, then return NULL to indicate * memory could not be allocated. */ char * auto_rddir_malloc(unsigned nbytes) { char *p; int again = 0; if ((p = malloc(nbytes)) == NULL) { /* * No memory, free rddir caches and try again */ mutex_lock(&cleanup_lock); cond_signal(&cleanup_start_cv); if (cond_wait(&cleanup_done_cv, &cleanup_lock)) { mutex_unlock(&cleanup_lock); syslog(LOG_ERR, "auto_rddir_malloc interrupted\n"); } else { mutex_unlock(&cleanup_lock); again = 1; } } if (again) p = malloc(nbytes); return (p); } /* * try to strdup a string, if it fails, then flush the rddir caches, * and retry. If the second strdup fails, return NULL to indicate failure. */ char * auto_rddir_strdup(const char *s1) { char *s2; int again = 0; if ((s2 = strdup(s1)) == NULL) { /* * No memory, free rddir caches and try again */ mutex_lock(&cleanup_lock); cond_signal(&cleanup_start_cv); if (cond_wait(&cleanup_done_cv, &cleanup_lock)) { mutex_unlock(&cleanup_lock); syslog(LOG_ERR, "auto_rddir_strdup interrupted\n"); } else { mutex_unlock(&cleanup_lock); again = 1; } } if (again) s2 = strdup(s1); return (s2); } /* * Returns a pointer to the entry corresponding to 'name' if found, * otherwise it returns NULL. */ struct dir_entry * btree_lookup(struct dir_entry *head, char *name) { register struct dir_entry *p; register int direction; for (p = head; p != NULL; ) { direction = strcmp(name, p->name); if (direction == 0) return (p); if (direction > 0) p = p->right; else p = p->left; } return (NULL); } /* * Add entry to binary tree * Duplicate entries are not added */ void btree_enter(struct dir_entry **head, struct dir_entry *ent) { register struct dir_entry *p, *prev = NULL; register int direction; ent->right = ent->left = NULL; if (*head == NULL) { *head = ent; return; } for (p = *head; p != NULL; ) { prev = p; direction = strcmp(ent->name, p->name); if (direction == 0) { /* * entry already in btree */ return; } if (direction > 0) p = p->right; else p = p->left; } assert(prev != NULL); if (direction > 0) prev->right = ent; else prev->left = ent; } /* * If entry doesn't exist already, add it to the linear list * after '*last' and to the binary tree list. * If '*last == NULL' then the list is walked till the end. * *last is always set to the new element after successful completion. * if entry already exists '*last' is only updated if not previously * provided. */ int add_dir_entry(char *name, struct dir_entry **list, struct dir_entry **last) { struct dir_entry *e, *l; if ((*list != NULL) && (*last == NULL)) { /* * walk the list to find last element */ for (l = *list; l != NULL; l = l->next) *last = l; } if (btree_lookup(*list, name) == NULL) { /* * not a duplicate, add it to list */ /* LINTED pointer alignment */ e = (struct dir_entry *) auto_rddir_malloc(sizeof (struct dir_entry)); if (e == NULL) return (ENOMEM); (void) memset((char *)e, 0, sizeof (*e)); e->name = auto_rddir_strdup(name); if (e->name == NULL) { free(e); return (ENOMEM); } e->next = NULL; if (*list == NULL) { /* * list is empty */ *list = *last = e; } else { /* * append to end of list */ assert(*last != NULL); (*last)->next = e; *last = e; } /* * add to binary tree */ btree_enter(list, e); } return (0); } /* * Print trace output. * Like fprintf(stderr, fmt, ...) except that if "id" is nonzero, the output * is preceeded by the ID of the calling thread. */ #define FMT_BUFSIZ 1024 void trace_prt(int id, char *fmt, ...) { va_list args; char buf[FMT_BUFSIZ]; if (id) { (void) sprintf(buf, "t%u\t%s", thr_self(), fmt); fmt = buf; } va_start(args, fmt); (void) vfprintf(stderr, fmt, args); va_end(args); } /* * Extract the isalist(7) for userland from the kernel. */ static char * isalist(void) { char *buf; size_t bufsize = BUFSIZ; /* wild guess */ long ret; buf = malloc(bufsize); do { ret = sysinfo(SI_ISALIST, buf, bufsize); if (ret == -1l) return (NULL); if (ret > bufsize) { bufsize = ret; buf = realloc(buf, bufsize); } else break; } while (buf != NULL); return (buf); } /* * Classify isa's as to bitness of the corresponding ABIs. * isa's which have no "official" system ABI are returned * unrecognised i.e. zero bits. */ static int bitness(char *isaname) { if (strcmp(isaname, "sparc") == 0 || strcmp(isaname, "i386") == 0) return (32); if (strcmp(isaname, "sparcv9") == 0 || strcmp(isaname, "amd64") == 0) return (64); return (0); } /* * Determine the application architecture (derived from uname -m) to expand * the $ARCH and $KARCH macros. * * Like arch(1), we need to substitute "sun4" for "sun4u", "sun4v", ... for * backward compatibility. When kflag is set (like arch -k), the unmodifed * value is returned instead. */ static int arch(char *buf, size_t bufsize, bool_t karch) { long ret; ret = sysinfo(SI_MACHINE, buf, bufsize); if (ret == -1L) return (0); if (!karch && strncmp(buf, "sun4", 4) == 0) (void) strlcpy(buf, "sun4", bufsize); return (1); } /* * Determine the basic ISA (uname -p) to expand the $CPU macro. */ static int cpu(char *buf, size_t bufsize) { long ret; ret = sysinfo(SI_ARCHITECTURE, buf, bufsize); if (ret == -1L) return (0); else return (1); } /* * Find the left-most element in the isalist that matches our idea of a * system ABI. * * On machines with only one ABI, this is usually the same as uname -p. */ static int natisa(char *buf, size_t bufsize) { int bits; char *isa, *list; char *lasts; if ((list = isalist()) == NULL) return (0); for (isa = strtok_r(list, " ", &lasts); isa; isa = strtok_r(0, " ", &lasts)) if ((bits = bitness(isa)) != 0) break; /* ignore "extension" architectures */ if (isa == 0 || bits == 0) { free(list); return (0); /* can't figure it out :( */ } (void) strncpy(buf, isa, bufsize); free(list); return (1); } /* * Determine the platform (uname -i) to expand the $PLATFORM macro. */ static int platform(char *buf, size_t bufsize) { long ret; ret = sysinfo(SI_PLATFORM, buf, bufsize); if (ret == -1L) return (0); else return (1); } /* * Set environment variables */ void put_automountd_env(void) { char defval[PATH_MAX], *p, *a, *c; int ret = 0, bufsz = PATH_MAX; ret = autofs_smf_get_prop("environment", defval, DEFAULT_INSTANCE, SCF_TYPE_ASTRING, AUTOMOUNTD, &bufsz); if (ret == SA_OK) { a = c = defval; if (*a == '\0') return; /* * Environment variables can have more than one value * seperated by a comma and there can be multiple * environment variables. * a=b\,c,d=e. For multiple * valued environment variable, values are seperated * with an escape character. */ while ((p = strchr(c, ',')) != NULL) { if (*(p - 1) == '\\') { c = p + 1; continue; } *p = '\0'; if ((c = strchr(a, '=')) != NULL) putenv(strdup(a)); a = c = p + 1; } if (*a != '\0') { if ((c = strchr(a, '=')) != NULL) putenv(strdup(a)); } } } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * autod_autofs.c * * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "automount.h" static int process_opts(char *options, int *directp, int *sawnestp); void netbuf_free(struct netbuf *); int mount_autofs( struct mapent *me, char *mntpnt, action_list *alp, char *rootp, char *subdir, char *key ) { int mntflags = 0; struct utsname utsname; autofs_args *fnip = NULL; int mount_timeout = AUTOFS_MOUNT_TIMEOUT; int sawnest, len, error = 0; char *buf, rel_mntpnt[MAXPATHLEN]; if (trace > 1) trace_prt(1, " mount_autofs %s on %s\n", me->map_fs->mfs_dir, mntpnt); if (strcmp(mntpnt, "/-") == 0) { syslog(LOG_ERR, "invalid mountpoint: /-"); return (ENOENT); } /* * get relative mountpoint */ sprintf(rel_mntpnt, ".%s", mntpnt+strlen(rootp)); if (trace > 2) trace_prt(1, "rel_mntpnt = %s\n", rel_mntpnt); if (uname(&utsname) < 0) { error = errno; syslog(LOG_ERR, "uname %s", strerror(error)); return (error); } if ((fnip = (autofs_args *) malloc(sizeof (autofs_args))) == NULL) { goto free_mem; } (void) memset((void *) fnip, 0, sizeof (*fnip)); if ((fnip->addr.buf = (char *)malloc(MAXADDRLEN)) == NULL) goto free_mem; (void) strcpy(fnip->addr.buf, utsname.nodename); (void) strcat(fnip->addr.buf, ".autofs"); if ((fnip->opts = malloc(MAX_MNTOPT_STR)) == NULL) goto free_mem; strcpy(fnip->opts, me->map_mntopts); if (process_opts(fnip->opts, &fnip->direct, &sawnest) != 0) goto free_mem; fnip->addr.len = strlen(fnip->addr.buf); fnip->addr.maxlen = fnip->addr.len; /* * get absolute mountpoint */ if ((fnip->path = strdup(mntpnt)) == NULL) goto free_mem; if ((fnip->map = strdup(me->map_fs->mfs_dir)) == NULL) goto free_mem; if ((fnip->subdir = strdup(subdir)) == NULL) goto free_mem; /* * This timeout is really ignored by autofs, it uses the * parent directory's timeout since it's really the one * specified/inherited from the original mount by 'automount' */ fnip->mount_to = mount_timeout; /* IGNORED */ fnip->rpc_to = AUTOFS_RPC_TIMEOUT; if (fnip->direct) { if (me->map_modified == TRUE || me->map_faked == TRUE) { if ((fnip->key = strdup(key)) == NULL) goto free_mem; } else { /* wierd case of a direct map pointer in another map */ if ((fnip->key = strdup(fnip->path)) == NULL) goto free_mem; } } else { fnip->key = NULL; } /* * Fill out action list. */ alp->action.action = AUTOFS_MOUNT_RQ; if ((alp->action.action_list_entry_u.mounta.spec = strdup(me->map_fs->mfs_dir)) == NULL) goto free_mem; if ((alp->action.action_list_entry_u.mounta.dir = strdup(rel_mntpnt)) == NULL) goto free_mem; len = strlen(fnip->opts); /* * Get a buffer for the option string, it holds the map options plus * space for "nest" if it isn't already in the option string */ if ((buf = (char *)malloc(MAX_MNTOPT_STR)) == NULL) goto free_mem; strcpy(buf, fnip->opts); if (!sawnest) { if (len + strlen(",nest") + 1 > MAX_MNTOPT_STR) goto free_mem; if (len) (void) strcat(buf, ","); (void) strcat(buf, "nest"); } alp->action.action_list_entry_u.mounta.optptr = buf; alp->action.action_list_entry_u.mounta.optlen = strlen(buf) + 1; alp->action.action_list_entry_u.mounta.flags = mntflags | MS_DATA | MS_OPTIONSTR; if ((alp->action.action_list_entry_u.mounta.fstype = strdup(MNTTYPE_AUTOFS)) == NULL) goto free_mem; alp->action.action_list_entry_u.mounta.dataptr = (char *)fnip; alp->action.action_list_entry_u.mounta.datalen = sizeof (*fnip); return (0); free_mem: /* * We got an error, free the memory we allocated. */ syslog(LOG_ERR, "mount_autofs: memory allocation failure"); free_autofs_args(fnip); alp->action.action_list_entry_u.mounta.dataptr = NULL; alp->action.action_list_entry_u.mounta.datalen = 0; free_mounta(&alp->action.action_list_entry_u.mounta); return (error ? error : ENOMEM); } /* * Set *directp to 1 if "direct" is found, and 0 otherwise * (mounts are indirect by default). If both "direct" and "indirect" are * found, the last one wins. */ static int process_opts(char *options, int *directp, int *sawnestp) { char *opt, *opts, *lasts; char buf[AUTOFS_MAXOPTSLEN]; assert(strlen(options)+1 < AUTOFS_MAXOPTSLEN); *sawnestp = 0; strcpy(buf, options); opts = buf; options[0] = '\0'; *directp = 0; while ((opt = strtok_r(opts, ",", &lasts)) != NULL) { opts = NULL; while (isspace(*opt)) { opt++; } if (strcmp(opt, "direct") == 0) { *directp = 1; } else if (strcmp(opt, "indirect") == 0) { *directp = 0; } else if (strcmp(opt, "ignore") != 0) { if (strcmp(opt, "nest") == 0) { *sawnestp = 1; } if (options[0] != '\0') { (void) strcat(options, ","); } (void) strcat(options, opt); } }; return (0); } /* * Free autofs_args structure */ void free_autofs_args(autofs_args *p) { if (p == NULL) return; if (p->addr.buf) free(p->addr.buf); if (p->path) free(p->path); if (p->opts) free(p->opts); if (p->map) free(p->map); if (p->subdir) free(p->subdir); if (p->key) free(p->key); free(p); } /* * free mounta structure. Assumes that m->dataptr has * been freed already */ void free_mounta(struct mounta *m) { if (m == NULL) return; if (m->spec) free(m->spec); if (m->dir) free(m->dir); if (m->fstype) free(m->fstype); if (m->optptr) free(m->optptr); assert(m->dataptr == NULL); assert(m->datalen == 0); /* * no need to free 'm' since it is part of the * action_list_entry structure. */ } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * autod_lookup.c * * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include #include #include "automount.h" int do_lookup1( char *mapname, char *key, char *subdir, char *mapopts, char *path, uint_t isdirect, uid_t uid, autofs_action_t *action, struct linka *linkp) { struct mapline ml; struct mapent *mapents = NULL; int err; struct autofs_rddir_cache *rdcp; int found = 0; bool_t iswildcard = FALSE; bool_t isrestricted = hasrestrictopt(mapopts); char *stack[STACKSIZ]; char **stkptr = stack; /* * Default action is for no work to be done by kernel AUTOFS. */ *action = AUTOFS_NONE; /* * Is there a cache for this map? */ rw_rdlock(&autofs_rddir_cache_lock); err = autofs_rddir_cache_lookup(mapname, &rdcp); if (!err && rdcp->full) { rw_unlock(&autofs_rddir_cache_lock); /* * Try to lock readdir cache entry for reading, if * the entry can not be locked, then avoid blocking * and go to the name service. I'm assuming it is * faster to go to the name service than to wait for * the cache to be populated. */ if (rw_tryrdlock(&rdcp->rwlock) == 0) { found = (rddir_entry_lookup(key, rdcp->entp) != NULL); rw_unlock(&rdcp->rwlock); } } else rw_unlock(&autofs_rddir_cache_lock); if (!err) { /* * release reference on cache entry */ mutex_lock(&rdcp->lock); rdcp->in_use--; assert(rdcp->in_use >= 0); mutex_unlock(&rdcp->lock); } if (found) return (0); /* * entry not found in cache, try the name service now */ err = 0; /* initialize the stack of open files for this thread */ stack_op(INIT, NULL, stack, &stkptr); err = getmapent(key, mapname, &ml, stack, &stkptr, &iswildcard, isrestricted); if (err == 0) /* call parser w default mount_access = TRUE */ mapents = parse_entry(key, mapname, mapopts, &ml, subdir, isdirect, TRUE); /* * Now we indulge in a bit of hanky-panky. * If the entry isn't found in the map and the * name begins with an "=" then we assume that * the name is an undocumented control message * for the daemon. This is accessible only * to superusers. */ if (mapents == NULL && *action == AUTOFS_NONE) { if (*key == '=' && uid == 0) { if (isdigit(*(key+1))) { /* * If next character is a digit * then set the trace level. */ trace = atoi(key+1); trace_prt(1, "Automountd: trace level = %d\n", trace); } else if (*(key+1) == 'v') { /* * If it's a "v" then * toggle verbose mode. */ verbose = !verbose; trace_prt(1, "Automountd: verbose %s\n", verbose ? "on" : "off"); } } err = ENOENT; goto done; } /* * Each mapent in the list describes a mount to be done. * Since I'm only doing a lookup, I only care whether a mapentry * was found or not. The mount will be done on a later RPC to * do_mount1. */ if (mapents == NULL && *action == AUTOFS_NONE) err = ENOENT; done: if (mapents) free_mapent(mapents); if (*action == AUTOFS_NONE && (iswildcard == TRUE)) { *action = AUTOFS_MOUNT_RQ; } if (trace > 1) { trace_prt(1, " do_lookup1: action=%d wildcard=%s error=%d\n", *action, iswildcard ? "TRUE" : "FALSE", err); } return (err); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "automount.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "smfcfg.h" static void autofs_doorfunc(void *, char *, size_t, door_desc_t *, uint_t); static void autofs_setdoor(int); static void autofs_mntinfo_1_r(autofs_lookupargs *, autofs_mountres *); static void autofs_mount_1_free_r(struct autofs_mountres *); static void autofs_lookup_1_r(autofs_lookupargs *, autofs_lookupres *); static void autofs_lookup_1_free_args(autofs_lookupargs *); static void autofs_unmount_1_r(umntrequest *, umntres *); static void autofs_unmount_1_free_args(umntrequest *); static void autofs_readdir_1_r(autofs_rddirargs *, autofs_rddirres *); static void autofs_readdir_1_free_r(struct autofs_rddirres *); static int decode_args(xdrproc_t, autofs_door_args_t *, caddr_t *, int); static bool_t encode_res(xdrproc_t, autofs_door_res_t **, caddr_t, int *); static void usage(); static void warn_hup(int); static void free_action_list(); static int start_autofs_svcs(); static void automountd_wait_for_cleanup(pid_t); /* * Private autofs system call */ extern int _autofssys(int, void *); #define CTIME_BUF_LEN 26 #define RESOURCE_FACTOR 8 #ifdef DEBUG #define AUTOFS_DOOR "/var/run/autofs_door" #endif /* DEBUG */ static thread_key_t s_thr_key; struct autodir *dir_head; struct autodir *dir_tail; char self[64]; time_t timenow; int verbose = 0; int trace = 0; int automountd_nobrowse = 0; int did_fork_exec; int main(argc, argv) int argc; char *argv[]; { pid_t pid; int c, error; struct rlimit rlset; char defval[6]; int ret = 0, bufsz; if (geteuid() != 0) { (void) fprintf(stderr, "%s must be run as root\n", argv[0]); exit(1); } /* * Read in the values from SMF first before we check * commandline options so the options override the file. */ bufsz = 6; ret = autofs_smf_get_prop("automountd_verbose", defval, DEFAULT_INSTANCE, SCF_TYPE_BOOLEAN, AUTOMOUNTD, &bufsz); if (ret == SA_OK) { if (strncasecmp("true", defval, 4) == 0) verbose = TRUE; else verbose = FALSE; } bufsz = 6; ret = autofs_smf_get_prop("nobrowse", defval, DEFAULT_INSTANCE, SCF_TYPE_BOOLEAN, AUTOMOUNTD, &bufsz); if (ret == SA_OK) { if (strncasecmp("true", defval, 4) == 0) automountd_nobrowse = TRUE; else automountd_nobrowse = FALSE; } bufsz = 6; ret = autofs_smf_get_prop("trace", defval, DEFAULT_INSTANCE, SCF_TYPE_INTEGER, AUTOMOUNTD, &bufsz); if (ret == SA_OK) { errno = 0; trace = strtol(defval, (char **)NULL, 10); if (errno != 0) trace = 0; } put_automountd_env(); while ((c = getopt(argc, argv, "vnTD:")) != EOF) { switch (c) { case 'v': verbose++; break; case 'n': automountd_nobrowse++; break; case 'T': trace++; break; case 'D': (void) putenv(optarg); break; default: usage(); } } if (sysinfo(SI_HOSTNAME, self, sizeof (self)) == -1) { error = errno; (void) fprintf(stderr, "automountd: can't determine hostname, error: %d\n", error); exit(1); } #ifndef DEBUG pid = fork(); if (pid < 0) { perror("cannot fork"); exit(1); } if (pid) exit(0); #endif (void) setsid(); openlog("automountd", LOG_PID, LOG_DAEMON); (void) setlocale(LC_ALL, ""); /* * Create the door_servers to manage fork/exec requests for * mounts and executable automount maps */ if ((did_fork_exec = door_create(automountd_do_fork_exec, NULL, 0)) == -1) { syslog(LOG_ERR, "door_create failed: %m, Exiting."); exit(errno); } if ((did_exec_map = door_create(automountd_do_exec_map, NULL, 0)) == -1) { syslog(LOG_ERR, "door_create failed: %m, Exiting."); if (door_revoke(did_fork_exec) == -1) { syslog(LOG_ERR, "failed to door_revoke(%d) %m", did_fork_exec); } exit(errno); } /* * Before we become multithreaded we fork allowing the parent * to become a door server to handle all mount and unmount * requests. This works around a potential hang in using * fork1() within a multithreaded environment */ pid = fork1(); if (pid < 0) { syslog(LOG_ERR, "can't fork the automountd mount process %m"); if (door_revoke(did_fork_exec) == -1) { syslog(LOG_ERR, "failed to door_revoke(%d) %m", did_fork_exec); } if (door_revoke(did_exec_map) == -1) { syslog(LOG_ERR, "failed to door_revoke(%d) %m", did_exec_map); } exit(1); } else if (pid > 0) { /* this is the door server process */ automountd_wait_for_cleanup(pid); } (void) rwlock_init(&cache_lock, USYNC_THREAD, NULL); (void) rwlock_init(&autofs_rddir_cache_lock, USYNC_THREAD, NULL); /* * initialize the name services, use NULL arguments to ensure * we don't initialize the stack of files used in file service */ (void) ns_setup(NULL, NULL); /* * we're using doors and its thread management now so we need to * make sure we have more than the default of 256 file descriptors * available. */ rlset.rlim_cur = RLIM_INFINITY; rlset.rlim_max = RLIM_INFINITY; if (setrlimit(RLIMIT_NOFILE, &rlset) == -1) syslog(LOG_ERR, "setrlimit failed for %s: %s", AUTOMOUNTD, strerror(errno)); (void) enable_extended_FILE_stdio(-1, -1); /* * establish our lock on the lock file and write our pid to it. * exit if some other process holds the lock, or if there's any * error in writing/locking the file. */ pid = _enter_daemon_lock(AUTOMOUNTD); switch (pid) { case 0: break; case -1: syslog(LOG_ERR, "error locking for %s: %m", AUTOMOUNTD); exit(2); default: /* daemon was already running */ exit(0); } /* * If we coredump it'll be /core. */ if (chdir("/") < 0) syslog(LOG_ERR, "chdir /: %m"); /* * Create cache_cleanup thread */ if (thr_create(NULL, 0, (void *(*)(void *))cache_cleanup, NULL, THR_DETACHED | THR_DAEMON | THR_NEW_LWP, NULL)) { syslog(LOG_ERR, "unable to create cache_cleanup thread"); exit(1); } /* other initializations */ (void) rwlock_init(&portmap_cache_lock, USYNC_THREAD, NULL); /* * On a labeled system, allow read-down nfs mounts if privileged * (PRIV_NET_MAC_AWARE) to do so. Otherwise, ignore the error * and "mount equal label only" behavior will result. */ if (is_system_labeled()) { (void) setpflags(NET_MAC_AWARE, 1); (void) setpflags(NET_MAC_AWARE_INHERIT, 1); } (void) signal(SIGHUP, warn_hup); /* start services */ return (start_autofs_svcs()); } /* * The old automounter supported a SIGHUP * to allow it to resynchronize internal * state with the /etc/mnttab. * This is no longer relevant, but we * need to catch the signal and warn * the user. */ /* ARGSUSED */ static void warn_hup(i) int i; { syslog(LOG_ERR, "SIGHUP received: ignored"); (void) signal(SIGHUP, warn_hup); } static void usage() { (void) fprintf(stderr, "Usage: automountd\n" "\t[-T]\t\t(trace requests)\n" "\t[-v]\t\t(verbose error msgs)\n" "\t[-D n=s]\t(define env variable)\n"); exit(1); /* NOTREACHED */ } static void autofs_readdir_1_r( autofs_rddirargs *req, autofs_rddirres *res) { if (trace > 0) trace_prt(1, "READDIR REQUEST : %s @ %ld\n", req->rda_map, req->rda_offset); do_readdir(req, res); if (trace > 0) trace_prt(1, "READDIR REPLY : status=%d\n", res->rd_status); } static void autofs_readdir_1_free_r(struct autofs_rddirres *res) { if (res->rd_status == AUTOFS_OK) { if (res->rd_rddir.rddir_entries) free(res->rd_rddir.rddir_entries); } } /* ARGSUSED */ static void autofs_unmount_1_r( umntrequest *m, umntres *res) { struct umntrequest *ul; if (trace > 0) { char ctime_buf[CTIME_BUF_LEN]; if (ctime_r(&timenow, ctime_buf, CTIME_BUF_LEN) == NULL) ctime_buf[0] = '\0'; trace_prt(1, "UNMOUNT REQUEST: %s", ctime_buf); for (ul = m; ul; ul = ul->next) trace_prt(1, " resource=%s fstype=%s mntpnt=%s" " mntopts=%s %s\n", ul->mntresource, ul->fstype, ul->mntpnt, ul->mntopts, ul->isdirect ? "direct" : "indirect"); } res->status = do_unmount1(m); if (trace > 0) trace_prt(1, "UNMOUNT REPLY: status=%d\n", res->status); } static void autofs_lookup_1_r( autofs_lookupargs *m, autofs_lookupres *res) { autofs_action_t action; struct linka link; int status; if (trace > 0) { char ctime_buf[CTIME_BUF_LEN]; if (ctime_r(&timenow, ctime_buf, CTIME_BUF_LEN) == NULL) ctime_buf[0] = '\0'; trace_prt(1, "LOOKUP REQUEST: %s", ctime_buf); trace_prt(1, " name=%s[%s] map=%s opts=%s path=%s direct=%d\n", m->name, m->subdir, m->map, m->opts, m->path, m->isdirect); } bzero(&link, sizeof (struct linka)); status = do_lookup1(m->map, m->name, m->subdir, m->opts, m->path, (uint_t)m->isdirect, m->uid, &action, &link); if (status == 0) { /* * Return action list to kernel. */ res->lu_res = AUTOFS_OK; if ((res->lu_type.action = action) == AUTOFS_LINK_RQ) { res->lu_type.lookup_result_type_u.lt_linka = link; } } else { /* * Entry not found */ res->lu_res = AUTOFS_NOENT; } res->lu_verbose = verbose; if (trace > 0) trace_prt(1, "LOOKUP REPLY : status=%d\n", res->lu_res); } static void autofs_mntinfo_1_r( autofs_lookupargs *m, autofs_mountres *res) { int status; action_list *alp = NULL; if (trace > 0) { char ctime_buf[CTIME_BUF_LEN]; if (ctime_r(&timenow, ctime_buf, CTIME_BUF_LEN) == NULL) ctime_buf[0] = '\0'; trace_prt(1, "MOUNT REQUEST: %s", ctime_buf); trace_prt(1, " name=%s[%s] map=%s opts=%s path=%s direct=%d\n", m->name, m->subdir, m->map, m->opts, m->path, m->isdirect); } status = do_mount1(m->map, m->name, m->subdir, m->opts, m->path, (uint_t)m->isdirect, m->uid, &alp, DOMOUNT_USER); if (status != 0) { /* * An error occurred, free action list if allocated. */ if (alp != NULL) { free_action_list(alp); alp = NULL; } } if (alp != NULL) { /* * Return action list to kernel. */ res->mr_type.status = AUTOFS_ACTION; res->mr_type.mount_result_type_u.list = alp; } else { /* * No work to do left for the kernel */ res->mr_type.status = AUTOFS_DONE; res->mr_type.mount_result_type_u.error = status; } if (trace > 0) { switch (res->mr_type.status) { case AUTOFS_ACTION: trace_prt(1, "MOUNT REPLY : status=%d, AUTOFS_ACTION\n", status); break; case AUTOFS_DONE: trace_prt(1, "MOUNT REPLY : status=%d, AUTOFS_DONE\n", status); break; default: trace_prt(1, "MOUNT REPLY : status=%d, UNKNOWN\n", status); } } if (status && verbose) { if (m->isdirect) { /* direct mount */ syslog(LOG_ERR, "mount of %s failed", m->path); } else { /* indirect mount */ syslog(LOG_ERR, "mount of %s/%s failed", m->path, m->name); } } } static void autofs_mount_1_free_r(struct autofs_mountres *res) { if (res->mr_type.status == AUTOFS_ACTION) { if (trace > 2) trace_prt(1, "freeing action list\n"); free_action_list(res->mr_type.mount_result_type_u.list); } } /* * Used for reporting messages from code shared with automount command. * Formats message into a buffer and calls syslog. * * Print an error. Works like printf (fmt string and variable args) * except that it will subsititute an error message for a "%m" string * (like syslog). */ void pr_msg(const char *fmt, ...) { va_list ap; char fmtbuff[BUFSIZ], buff[BUFSIZ]; const char *p1; char *p2; p2 = fmtbuff; fmt = gettext(fmt); for (p1 = fmt; *p1; p1++) { if (*p1 == '%' && *(p1 + 1) == 'm') { (void) strcpy(p2, strerror(errno)); p2 += strlen(p2); p1++; } else { *p2++ = *p1; } } if (p2 > fmtbuff && *(p2-1) != '\n') *p2++ = '\n'; *p2 = '\0'; va_start(ap, fmt); (void) vsprintf(buff, fmtbuff, ap); va_end(ap); syslog(LOG_ERR, buff); } static void free_action_list(action_list *alp) { action_list *p, *next = NULL; struct mounta *mp; for (p = alp; p != NULL; p = next) { switch (p->action.action) { case AUTOFS_MOUNT_RQ: mp = &(p->action.action_list_entry_u.mounta); /* LINTED pointer alignment */ if (mp->fstype) { if (strcmp(mp->fstype, "autofs") == 0) { free_autofs_args((autofs_args *) mp->dataptr); } else if (strncmp(mp->fstype, "nfs", 3) == 0) { free_nfs_args((struct nfs_args *) mp->dataptr); } } mp->dataptr = NULL; mp->datalen = 0; free_mounta(mp); break; case AUTOFS_LINK_RQ: syslog(LOG_ERR, "non AUTOFS_MOUNT_RQ requests not implemented\n"); break; default: syslog(LOG_ERR, "non AUTOFS_MOUNT_RQ requests not implemented\n"); break; } next = p->next; free(p); } } static void autofs_lookup_1_free_args(autofs_lookupargs *args) { if (args->map) free(args->map); if (args->path) free(args->path); if (args->name) free(args->name); if (args->subdir) free(args->subdir); if (args->opts) free(args->opts); } static void autofs_unmount_1_free_args(umntrequest *args) { if (args->mntresource) free(args->mntresource); if (args->mntpnt) free(args->mntpnt); if (args->fstype) free(args->fstype); if (args->mntopts) free(args->mntopts); if (args->next) autofs_unmount_1_free_args(args->next); } static void autofs_setdoor(int did) { if (did < 0) { did = 0; } (void) _autofssys(AUTOFS_SETDOOR, &did); } void * autofs_get_buffer(size_t size) { autofs_tsd_t *tsd = NULL; /* * Make sure the buffer size is aligned */ (void) thr_getspecific(s_thr_key, (void **)&tsd); if (tsd == NULL) { tsd = (autofs_tsd_t *)malloc(sizeof (autofs_tsd_t)); if (tsd == NULL) { return (NULL); } tsd->atsd_buf = malloc(size); if (tsd->atsd_buf != NULL) tsd->atsd_len = size; else tsd->atsd_len = 0; (void) thr_setspecific(s_thr_key, tsd); } else { if (tsd->atsd_buf && (tsd->atsd_len < size)) { free(tsd->atsd_buf); tsd->atsd_buf = malloc(size); if (tsd->atsd_buf != NULL) tsd->atsd_len = size; else { tsd->atsd_len = 0; } } } if (tsd->atsd_buf) { bzero(tsd->atsd_buf, size); return (tsd->atsd_buf); } else { syslog(LOG_ERR, gettext("Can't Allocate tsd buffer, size %d"), size); return (NULL); } } /* * Each request will automatically spawn a new thread with this * as its entry point. */ /* ARGUSED */ static void autofs_doorfunc( void *cookie, char *argp, size_t arg_size, door_desc_t *dp, uint_t n_desc) { char *res; int res_size; int which; int error = 0; int srsz = 0; autofs_lookupargs *xdrargs; autofs_lookupres lookup_res; autofs_rddirargs *rddir_args; autofs_rddirres rddir_res; autofs_mountres mount_res; umntrequest *umnt_args; umntres umount_res; autofs_door_res_t *door_res; autofs_door_res_t failed_res; if (arg_size < sizeof (autofs_door_args_t)) { failed_res.res_status = EINVAL; error = door_return((char *)&failed_res, sizeof (autofs_door_res_t), NULL, 0); /* * If we got here the door_return() failed. */ syslog(LOG_ERR, "Bad argument, door_return failure %d", error); return; } timenow = time((time_t *)NULL); which = ((autofs_door_args_t *)argp)->cmd; switch (which) { case AUTOFS_LOOKUP: if (error = decode_args(xdr_autofs_lookupargs, (autofs_door_args_t *)argp, (caddr_t *)&xdrargs, sizeof (autofs_lookupargs))) { syslog(LOG_ERR, "error allocating lookup arguments buffer"); failed_res.res_status = error; failed_res.xdr_len = 0; res = (caddr_t)&failed_res; res_size = 0; break; } bzero(&lookup_res, sizeof (autofs_lookupres)); autofs_lookup_1_r(xdrargs, &lookup_res); autofs_lookup_1_free_args(xdrargs); free(xdrargs); if (!encode_res(xdr_autofs_lookupres, &door_res, (caddr_t)&lookup_res, &res_size)) { syslog(LOG_ERR, "error allocating lookup results buffer"); failed_res.res_status = EINVAL; failed_res.xdr_len = 0; res = (caddr_t)&failed_res; } else { door_res->res_status = 0; res = (caddr_t)door_res; } break; case AUTOFS_MNTINFO: if (error = decode_args(xdr_autofs_lookupargs, (autofs_door_args_t *)argp, (caddr_t *)&xdrargs, sizeof (autofs_lookupargs))) { syslog(LOG_ERR, "error allocating lookup arguments buffer"); failed_res.res_status = error; failed_res.xdr_len = 0; res = (caddr_t)&failed_res; res_size = 0; break; } autofs_mntinfo_1_r((autofs_lookupargs *)xdrargs, &mount_res); autofs_lookup_1_free_args(xdrargs); free(xdrargs); /* * Only reason we would get a NULL res is because * we could not allocate a results buffer. Use * a local one to return the error EAGAIN as has * always been done when memory allocations fail. */ if (!encode_res(xdr_autofs_mountres, &door_res, (caddr_t)&mount_res, &res_size)) { syslog(LOG_ERR, "error allocating mount results buffer"); failed_res.res_status = EAGAIN; failed_res.xdr_len = 0; res = (caddr_t)&failed_res; } else { door_res->res_status = 0; res = (caddr_t)door_res; } autofs_mount_1_free_r(&mount_res); break; case AUTOFS_UNMOUNT: if (error = decode_args(xdr_umntrequest, (autofs_door_args_t *)argp, (caddr_t *)&umnt_args, sizeof (umntrequest))) { syslog(LOG_ERR, "error allocating unmount argument buffer"); failed_res.res_status = error; failed_res.xdr_len = 0; res = (caddr_t)&failed_res; res_size = sizeof (autofs_door_res_t); break; } autofs_unmount_1_r(umnt_args, &umount_res); error = umount_res.status; autofs_unmount_1_free_args(umnt_args); free(umnt_args); if (!encode_res(xdr_umntres, &door_res, (caddr_t)&umount_res, &res_size)) { syslog(LOG_ERR, "error allocating unmount results buffer"); failed_res.res_status = EINVAL; failed_res.xdr_len = 0; res = (caddr_t)&failed_res; res_size = sizeof (autofs_door_res_t); } else { door_res->res_status = 0; res = (caddr_t)door_res; } break; case AUTOFS_READDIR: if (error = decode_args(xdr_autofs_rddirargs, (autofs_door_args_t *)argp, (caddr_t *)&rddir_args, sizeof (autofs_rddirargs))) { syslog(LOG_ERR, "error allocating readdir argument buffer"); failed_res.res_status = error; failed_res.xdr_len = 0; res = (caddr_t)&failed_res; res_size = sizeof (autofs_door_res_t); break; } autofs_readdir_1_r(rddir_args, &rddir_res); free(rddir_args->rda_map); free(rddir_args); if (!encode_res(xdr_autofs_rddirres, &door_res, (caddr_t)&rddir_res, &res_size)) { syslog(LOG_ERR, "error allocating readdir results buffer"); failed_res.res_status = ENOMEM; failed_res.xdr_len = 0; res = (caddr_t)&failed_res; res_size = sizeof (autofs_door_res_t); } else { door_res->res_status = 0; res = (caddr_t)door_res; } autofs_readdir_1_free_r(&rddir_res); break; #ifdef MALLOC_DEBUG case AUTOFS_DUMP_DEBUG: check_leaks("/var/tmp/automountd.leak"); error = door_return(NULL, 0, NULL, 0); /* * If we got here, door_return() failed */ syslog(LOG_ERR, "dump debug door_return failure %d", error); return; #endif case NULLPROC: res = NULL; res_size = 0; break; default: failed_res.res_status = EINVAL; res = (char *)&failed_res; res_size = sizeof (autofs_door_res_t); break; } srsz = res_size; errno = 0; error = door_return(res, res_size, NULL, 0); if (errno == E2BIG) { /* * Failed due to encoded results being bigger than the * kernel expected bufsize. Passing actual results size * back down to kernel. */ failed_res.res_status = EOVERFLOW; failed_res.xdr_len = srsz; res = (caddr_t)&failed_res; res_size = sizeof (autofs_door_res_t); } else { syslog(LOG_ERR, "door_return failed %d, buffer %p, " "buffer size %d", error, (void *)res, res_size); res = NULL; res_size = 0; } (void) door_return(res, res_size, NULL, 0); /* NOTREACHED */ } static int start_autofs_svcs(void) { int doorfd; #ifdef DEBUG int dfd; #endif if ((doorfd = door_create(autofs_doorfunc, NULL, DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) == -1) { syslog(LOG_ERR, gettext("Unable to create door\n")); return (1); } #ifdef DEBUG /* * Create a file system path for the door */ if ((dfd = open(AUTOFS_DOOR, O_RDWR|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) == -1) { syslog(LOG_ERR, "Unable to open %s: %m\n", AUTOFS_DOOR); (void) close(doorfd); return (1); } /* * stale associations clean up */ (void) fdetach(AUTOFS_DOOR); /* * Register in the namespace to the kernel to door_ki_open. */ if (fattach(doorfd, AUTOFS_DOOR) == -1) { syslog(LOG_ERR, "Unable to fattach door %m\n", AUTOFS_DOOR); (void) close(dfd); (void) close(doorfd); return (1); } #endif /* DEBUG */ /* * Pass door name to kernel for door_ki_open */ autofs_setdoor(doorfd); (void) thr_keycreate(&s_thr_key, NULL); /* * Wait for incoming calls */ /*CONSTCOND*/ while (1) (void) pause(); /* NOTREACHED */ syslog(LOG_ERR, gettext("Door server exited")); return (10); } static int decode_args( xdrproc_t xdrfunc, autofs_door_args_t *argp, caddr_t *xdrargs, int size) { XDR xdrs; caddr_t tmpargs = (caddr_t)&((autofs_door_args_t *)argp)->xdr_arg; size_t arg_size = ((autofs_door_args_t *)argp)->xdr_len; xdrmem_create(&xdrs, tmpargs, arg_size, XDR_DECODE); *xdrargs = malloc(size); if (*xdrargs == NULL) { syslog(LOG_ERR, "error allocating arguments buffer"); return (ENOMEM); } bzero(*xdrargs, size); if (!(*xdrfunc)(&xdrs, *xdrargs)) { free(*xdrargs); *xdrargs = NULL; syslog(LOG_ERR, "error decoding arguments"); return (EINVAL); } return (0); } static bool_t encode_res( xdrproc_t xdrfunc, autofs_door_res_t **results, caddr_t resp, int *size) { XDR xdrs; *size = xdr_sizeof((*xdrfunc), resp); *results = autofs_get_buffer( sizeof (autofs_door_res_t) + *size); if (*results == NULL) { (*results)->res_status = ENOMEM; return (FALSE); } (*results)->xdr_len = *size; *size = sizeof (autofs_door_res_t) + (*results)->xdr_len; xdrmem_create(&xdrs, (caddr_t)((*results)->xdr_res), (*results)->xdr_len, XDR_ENCODE); if (!(*xdrfunc)(&xdrs, resp)) { (*results)->res_status = EINVAL; syslog(LOG_ERR, "error encoding results"); return (FALSE); } (*results)->res_status = 0; return (TRUE); } static void automountd_wait_for_cleanup(pid_t pid) { int status; int child_exitval; /* * Wait for the main automountd process to exit so we cleanup */ (void) waitpid(pid, &status, 0); child_exitval = WEXITSTATUS(status); /* * Shutdown the door server for mounting and unmounting * filesystems */ if (door_revoke(did_fork_exec) == -1) { syslog(LOG_ERR, "failed to door_revoke(%d) %m", did_fork_exec); } if (door_revoke(did_exec_map) == -1) { syslog(LOG_ERR, "failed to door_revoke(%d) %m", did_exec_map); } exit(child_exitval); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * autod_mount.c * * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "automount.h" #include "replica.h" static int unmount_mntpnt(struct mnttab *); static int call_fork_exec(char *, char *, char **, int); static void remove_browse_options(char *); static int inherit_options(char *, char **); int do_mount1( char *mapname, char *key, char *subdir, char *mapopts, char *path, uint_t isdirect, uid_t uid, action_list **alpp, int flags) { struct mapline ml; struct mapent *me, *mapents = NULL; char mntpnt[MAXPATHLEN]; char spec_mntpnt[MAXPATHLEN]; int err = 0; char *private; /* fs specific data. eg prevhost in case of nfs */ int mount_ok = 0; ssize_t len; action_list *alp, *prev, *tmp; char root[MAXPATHLEN]; int overlay = 1; char next_subdir[MAXPATHLEN]; bool_t mount_access = TRUE; bool_t iswildcard; bool_t isrestricted = hasrestrictopt(mapopts); char *stack[STACKSIZ]; char **stkptr = stack; retry: iswildcard = FALSE; /* initialize the stack of open files for this thread */ stack_op(INIT, NULL, stack, &stkptr); err = getmapent(key, mapname, &ml, stack, &stkptr, &iswildcard, isrestricted); if (err == 0) { mapents = parse_entry(key, mapname, mapopts, &ml, subdir, isdirect, mount_access); } if (trace) { struct mapfs *mfs; trace_prt(1, " do_mount1:\n"); for (me = mapents; me; me = me->map_next) { trace_prt(1, " (%s,%s)\t%s%s%s\n", me->map_fstype ? me->map_fstype : "", me->map_mounter ? me->map_mounter : "", path ? path : "", me->map_root ? me->map_root : "", me->map_mntpnt ? me->map_mntpnt : ""); trace_prt(0, "\t\t-%s\n", me->map_mntopts ? me->map_mntopts : ""); for (mfs = me->map_fs; mfs; mfs = mfs->mfs_next) trace_prt(0, "\t\t%s:%s\tpenalty=%d\n", mfs->mfs_host ? mfs->mfs_host: "", mfs->mfs_dir ? mfs->mfs_dir : "", mfs->mfs_penalty); } } *alpp = NULL; /* * Each mapent in the list describes a mount to be done. * Normally there's just a single entry, though in the * case of /net mounts there may be many entries, that * must be mounted as a hierarchy. For each mount the * automountd must make sure the required mountpoint * exists and invoke the appropriate mount command for * the fstype. */ private = ""; for (me = mapents; me && !err; me = me->map_next) { len = snprintf(mntpnt, sizeof (mntpnt), "%s%s%s", path, mapents->map_root, me->map_mntpnt); if (len >= sizeof (mntpnt)) { free_mapent(mapents); return (ENAMETOOLONG); } /* * remove trailing /'s from mountpoint to avoid problems * stating a directory with two or more trailing slashes. * This will let us mount directories from machines * which export with two or more slashes (apollo for instance). */ len -= 1; while (mntpnt[len] == '/') mntpnt[len--] = '\0'; (void) strcpy(spec_mntpnt, mntpnt); if (isrestricted && inherit_options(mapopts, &me->map_mntopts) != 0) { syslog(LOG_ERR, "malloc of options failed"); free_mapent(mapents); return (EAGAIN); } if (strcmp(me->map_fstype, MNTTYPE_NFS) == 0) { remove_browse_options(me->map_mntopts); if (flags == DOMOUNT_KERNEL) { alp = (action_list *)malloc( sizeof (action_list)); if (alp == NULL) { syslog(LOG_ERR, "malloc of alp failed"); continue; } memset(alp, 0, sizeof (action_list)); } else alp = NULL; err = mount_nfs(me, spec_mntpnt, private, overlay, uid, &alp); /* * We must retry if we don't have access to the * root file system and there are other * following mapents. The reason we can't * continue because the rest of the mapent list * depends on whether mount_access is TRUE or FALSE. */ if (err == NFSERR_ACCES && me->map_next != NULL) { /* * don't expect mount_access to be * FALSE here, but we do a check * anyway. */ if (mount_access == TRUE) { mount_access = FALSE; err = 0; free_mapent(mapents); if (alp) { free(alp); alp = NULL; } goto retry; } } if (alp) { if (*alpp == NULL) *alpp = alp; else { for (tmp = *alpp; tmp != NULL; tmp = tmp->next) prev = tmp; prev->next = alp; } } mount_ok = !err; } else if (strcmp(me->map_fstype, MNTTYPE_AUTOFS) == 0) { if (isdirect) { len = strlcpy(root, path, sizeof (root)); } else { len = snprintf(root, sizeof (root), "%s/%s", path, key); } if (len >= sizeof (root)) { free_mapent(mapents); return (ENAMETOOLONG); } alp = (action_list *)malloc(sizeof (action_list)); if (alp == NULL) { syslog(LOG_ERR, "malloc of alp failed"); continue; } memset(alp, 0, sizeof (action_list)); /* * get the next subidr, but only if its a modified * or faked autofs mount */ if (me->map_modified || me->map_faked) { len = snprintf(next_subdir, sizeof (next_subdir), "%s%s", subdir, me->map_mntpnt); } else { next_subdir[0] = '\0'; len = 0; } if (trace > 2) trace_prt(1, " root=%s\t next_subdir=%s\n", root, next_subdir); if (len < sizeof (next_subdir)) { err = mount_autofs(me, spec_mntpnt, alp, root, next_subdir, key); } else { err = ENAMETOOLONG; } if (err == 0) { /* * append to action list */ mount_ok++; if (*alpp == NULL) *alpp = alp; else { for (tmp = *alpp; tmp != NULL; tmp = tmp->next) prev = tmp; prev->next = alp; } } else { free(alp); mount_ok = 0; } } else if (strcmp(me->map_fstype, MNTTYPE_LOFS) == 0) { remove_browse_options(me->map_mntopts); err = loopbackmount(me->map_fs->mfs_dir, spec_mntpnt, me->map_mntopts, overlay); mount_ok = !err; } else { remove_browse_options(me->map_mntopts); err = mount_generic(me->map_fs->mfs_dir, me->map_fstype, me->map_mntopts, spec_mntpnt, overlay); mount_ok = !err; } } if (mapents) free_mapent(mapents); /* * If an error occurred, * the filesystem doesn't exist, or could not be * mounted. Return EACCES to autofs indicating that * the mountpoint can not be accessed if this is not * a wildcard access. If it is a wildcard access we * return ENOENT since the lookup that triggered * this mount request will fail and the entry will not * be available. */ if (mount_ok) { /* * No error occurred, return 0 to indicate success. */ err = 0; } else { /* * The filesystem does not exist or could not be mounted. * Return ENOENT if the lookup was triggered by a wildcard * access. Wildcard entries only exist if they can be * mounted. They can not be listed otherwise (through * a readdir(3C)). * Return EACCES if the lookup was not triggered by a * wildcard access. Map entries that are explicitly defined * in maps are visible via readdir(3C), therefore we return * EACCES to indicate that the entry exists, but the directory * can not be opened. This is the same behavior of a Unix * directory that exists, but has its execute bit turned off. * The directory is there, but the user does not have access * to it. */ if (iswildcard) err = ENOENT; else err = EACCES; } return (err); } #define ARGV_MAX 16 #define VFS_PATH "/usr/lib/fs" int mount_generic(special, fstype, opts, mntpnt, overlay) char *special, *fstype, *opts, *mntpnt; int overlay; { struct mnttab m; struct stat stbuf; int i, res; char *newargv[ARGV_MAX]; if (trace > 1) { trace_prt(1, " mount: %s %s %s %s\n", special, mntpnt, fstype, opts); } if (stat(mntpnt, &stbuf) < 0) { syslog(LOG_ERR, "Couldn't stat %s: %m", mntpnt); return (ENOENT); } i = 2; if (overlay) newargv[i++] = "-O"; /* * Use "quiet" option to suppress warnings about unsupported * mount options. */ newargv[i++] = "-q"; if (opts && *opts) { m.mnt_mntopts = opts; if (hasmntopt(&m, MNTOPT_RO) != NULL) newargv[i++] = "-r"; newargv[i++] = "-o"; newargv[i++] = opts; } newargv[i++] = "--"; newargv[i++] = special; newargv[i++] = mntpnt; newargv[i] = NULL; res = call_fork_exec(fstype, "mount", newargv, verbose); if (res == 0 && trace > 1) { if (stat(mntpnt, &stbuf) == 0) { trace_prt(1, " mount of %s dev=%x rdev=%x OK\n", mntpnt, stbuf.st_dev, stbuf.st_rdev); } else { trace_prt(1, " failed to stat %s\n", mntpnt); } } return (res); } void automountd_do_fork_exec(void *cookie, char *argp, size_t arg_size, door_desc_t *dfd, uint_t n_desc) { int stat_loc; int fd = 0; struct stat stbuf; int res; int child_pid; command_t *command; char *newargv[ARGV_MAX]; int i; command = (command_t *)argp; if (sizeof (*command) != arg_size) { res = EINVAL; door_return((char *)&res, sizeof (res), NULL, 0); } switch ((child_pid = fork1())) { case -1: syslog(LOG_ERR, "Cannot fork: %m"); res = errno; break; case 0: /* * Child */ (void) setsid(); fd = open(command->console ? "/dev/console" : "/dev/null", O_WRONLY); if (fd != -1) { (void) dup2(fd, 1); (void) dup2(fd, 2); (void) close(fd); } for (i = 0; *command->argv[i]; i++) { newargv[i] = strdup(command->argv[i]); if (newargv[i] == (char *)NULL) { syslog(LOG_ERR, "failed to copy argument '%s'" " of %s: %m", command->argv[i], command->file); _exit(errno); } } newargv[i] = NULL; (void) execv(command->file, newargv); if (errno == EACCES) syslog(LOG_ERR, "exec %s: %m", command->file); _exit(errno); default: /* * Parent */ (void) waitpid(child_pid, &stat_loc, WUNTRACED); if (WIFEXITED(stat_loc)) { if (trace > 1) { trace_prt(1, " fork_exec: returns exit status %d\n", WEXITSTATUS(stat_loc)); } res = WEXITSTATUS(stat_loc); } else if (WIFSIGNALED(stat_loc)) { if (trace > 1) trace_prt(1, " fork_exec: returns signal status %d\n", WTERMSIG(stat_loc)); res = 1; } else { if (trace > 1) trace_prt(1, " fork_exec: returns unknown status\n"); res = 1; } } door_return((char *)&res, sizeof (res), NULL, 0); trace_prt(1, "automountd_do_fork_exec, door return failed %s, %s\n", command->file, strerror(errno)); door_return(NULL, 0, NULL, 0); } int do_unmount1(ur) umntrequest *ur; { struct mnttab m; int res = 0; m.mnt_special = ur->mntresource; m.mnt_mountp = ur->mntpnt; m.mnt_fstype = ur->fstype; m.mnt_mntopts = ur->mntopts; /* * Special case for NFS mounts. * Don't want to attempt unmounts from * a dead server. If any member of a * hierarchy belongs to a dead server * give up (try later). */ if (strcmp(ur->fstype, MNTTYPE_NFS) == 0) { struct replica *list; int i, n; bool_t pubopt = FALSE; int nfs_port; int got_port; /* * See if a port number was specified. If one was * specified that is too large to fit in 16 bits, truncate * the high-order bits (for historical compatibility). Use * zero to indicate "no port specified". */ got_port = nopt(&m, MNTOPT_PORT, &nfs_port); if (!got_port) nfs_port = 0; nfs_port &= USHRT_MAX; if (hasmntopt(&m, MNTOPT_PUBLIC)) pubopt = TRUE; list = parse_replica(ur->mntresource, &n); if (list == NULL) { if (n >= 0) syslog(LOG_ERR, "Memory allocation failed: %m"); res = 1; goto done; } for (i = 0; i < n; i++) { if (pingnfs(list[i].host, 1, NULL, 0, nfs_port, pubopt, list[i].path, NULL) != RPC_SUCCESS) { res = 1; free_replica(list, n); goto done; } } free_replica(list, n); } res = unmount_mntpnt(&m); done: return (res); } static int unmount_mntpnt(mnt) struct mnttab *mnt; { char *fstype = mnt->mnt_fstype; char *mountp = mnt->mnt_mountp; char *newargv[ARGV_MAX]; int res; if (strcmp(fstype, MNTTYPE_NFS) == 0) { res = nfsunmount(mnt); } else if (strcmp(fstype, MNTTYPE_LOFS) == 0) { if ((res = umount(mountp)) < 0) res = errno; } else { newargv[2] = mountp; newargv[3] = NULL; res = call_fork_exec(fstype, "umount", newargv, verbose); if (res == ENOENT) { /* * filesystem specific unmount command not found */ if ((res = umount(mountp)) < 0) res = errno; } } if (trace > 1) trace_prt(1, " unmount %s %s\n", mountp, res ? "failed" : "OK"); return (res); } /* * Remove the autofs specific options 'browse', 'nobrowse' and * 'restrict' from 'opts'. */ static void remove_browse_options(char *opts) { char *p, *pb; char buf[MAXOPTSLEN], new[MAXOPTSLEN]; char *placeholder; new[0] = '\0'; (void) strcpy(buf, opts); pb = buf; while (p = (char *)strtok_r(pb, ",", &placeholder)) { pb = NULL; if (strcmp(p, MNTOPT_NOBROWSE) != 0 && strcmp(p, MNTOPT_BROWSE) != 0 && strcmp(p, MNTOPT_RESTRICT) != 0) { if (new[0] != '\0') (void) strcat(new, ","); (void) strcat(new, p); } } (void) strcpy(opts, new); } static const char *restropts[] = { RESTRICTED_MNTOPTS }; #define NROPTS (sizeof (restropts)/sizeof (restropts[0])) static int inherit_options(char *opts, char **mapentopts) { int i; char *new; struct mnttab mtmap; struct mnttab mtopt; size_t len = strlen(*mapentopts); for (i = 0; i < NROPTS; i++) len += strlen(restropts[i]); /* "," for each new option plus the trailing NUL */ len += NROPTS + 1; new = malloc(len); if (new == 0) return (-1); (void) strcpy(new, *mapentopts); mtmap.mnt_mntopts = *mapentopts; mtopt.mnt_mntopts = opts; for (i = 0; i < NROPTS; i++) { if (hasmntopt(&mtopt, (char *)restropts[i]) != NULL && hasmntopt(&mtmap, (char *)restropts[i]) == NULL) { if (*new != '\0') (void) strcat(new, ","); (void) strcat(new, restropts[i]); } } free(*mapentopts); *mapentopts = new; return (0); } bool_t hasrestrictopt(char *opts) { struct mnttab mt; mt.mnt_mntopts = opts; return (hasmntopt(&mt, MNTOPT_RESTRICT) != NULL); } static int call_fork_exec(fstype, cmd, newargv, console) char *fstype; char *cmd; char **newargv; int console; { command_t command; door_arg_t darg; char path[MAXPATHLEN]; struct stat stbuf; int ret; int sz; int status; int i; bzero(&command, sizeof (command)); /* build the full path name of the fstype dependent command */ (void) snprintf(path, MAXPATHLEN, "%s/%s/%s", VFS_PATH, fstype, cmd); if (stat(path, &stbuf) != 0) { ret = errno; return (ret); } strlcpy(command.file, path, MAXPATHLEN); strlcpy(command.argv[0], path, MAXOPTSLEN); for (i = 2; newargv[i]; i++) { strlcpy(command.argv[i-1], newargv[i], MAXOPTSLEN); } if (trace > 1) { trace_prt(1, " call_fork_exec: %s ", command.file); for (i = 0; *command.argv[i]; i++) trace_prt(0, "%s ", command.argv[i]); trace_prt(0, "\n"); } command.console = console; darg.data_ptr = (char *)&command; darg.data_size = sizeof (command); darg.desc_ptr = NULL; darg.desc_num = 0; darg.rbuf = (char *)&status; darg.rsize = sizeof (status); ret = door_call(did_fork_exec, &darg); if (trace > 1) { trace_prt(1, " call_fork_exec: door_call failed %d\n", ret); } return (status); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2015 Nexenta Systems, Inc. All rights reserved. * Copyright (c) 1992, 2010, Oracle and/or its affiliates. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define NFSCLIENT #include #include #include #include #include #include #include #include #include #include /* for NSS_BUFLEN_HOSTS */ #include #include #include #include #include #include #include #define NO_RDDIR_CACHE #include "automount.h" #include "replica.h" #include "nfs_subr.h" #include "webnfs.h" #include "nfs_resolve.h" #include #include #include #include #include #include #include #include #include #include #include "smfcfg.h" extern void set_nfsv4_ephemeral_mount_to(void); extern char *nfs_get_qop_name(); extern AUTH *nfs_create_ah(); extern enum snego_stat nfs_sec_nego(); #define MAXHOSTS 512 /* * host cache states */ #define NOHOST 0 #define GOODHOST 1 #define DEADHOST 2 #define NFS_ARGS_EXTB_secdata(args, secdata) \ { (args).nfs_args_ext = NFS_ARGS_EXTB, \ (args).nfs_ext_u.nfs_extB.secdata = secdata; } struct cache_entry { struct cache_entry *cache_next; char *cache_host; time_t cache_time; int cache_state; rpcvers_t cache_reqvers; rpcvers_t cache_outvers; char *cache_proto; }; struct mfs_snego_t { int sec_opt; bool_t snego_done; char *nfs_flavor; seconfig_t nfs_sec; }; typedef struct mfs_snego_t mfs_snego_t; static struct cache_entry *cache_head = NULL; rwlock_t cache_lock; /* protect the cache chain */ static enum nfsstat nfsmount(struct mapfs *, char *, char *, int, uid_t, action_list *); static int is_nfs_port(char *); static void netbuf_free(struct netbuf *); static int get_pathconf(CLIENT *, char *, char *, struct pathcnf **, int); static struct mapfs *enum_servers(struct mapent *, char *); static struct mapfs *get_mysubnet_servers(struct mapfs *); static int subnet_test(int af, struct sioc_addrreq *); static struct netbuf *get_addr(char *, rpcprog_t, rpcvers_t, struct netconfig **, char *, ushort_t, struct t_info *); static struct netbuf *get_pubfh(char *, rpcvers_t, mfs_snego_t *, struct netconfig **, char *, ushort_t, struct t_info *, caddr_t *, bool_t, char *); static int create_homedir(const char *, const char *); enum type_of_stuff { SERVER_ADDR = 0, SERVER_PING = 1, SERVER_FH = 2 }; static void *get_server_netinfo(enum type_of_stuff, char *, rpcprog_t, rpcvers_t, mfs_snego_t *, struct netconfig **, char *, ushort_t, struct t_info *, caddr_t *, bool_t, char *, enum clnt_stat *); static void *get_netconfig_info(enum type_of_stuff, char *, rpcprog_t, rpcvers_t, struct netconfig *, ushort_t, struct t_info *, struct t_bind *, caddr_t *, bool_t, char *, enum clnt_stat *, mfs_snego_t *); static void *get_server_addrorping(char *, rpcprog_t, rpcvers_t, struct netconfig *, ushort_t, struct t_info *, struct t_bind *, caddr_t *, bool_t, char *, enum clnt_stat *, int); static void *get_server_fh(char *, rpcprog_t, rpcvers_t, mfs_snego_t *, struct netconfig *, ushort_t, struct t_info *, struct t_bind *, caddr_t *, bool_t, char *, enum clnt_stat *); struct mapfs *add_mfs(struct mapfs *, int, struct mapfs **, struct mapfs **); void free_mfs(struct mapfs *); static void dump_mfs(struct mapfs *, char *, int); static char *dump_distance(struct mapfs *); static void cache_free(struct cache_entry *); static int cache_check(char *, rpcvers_t *, char *); static void cache_enter(char *, rpcvers_t, rpcvers_t, char *, int); void destroy_auth_client_handle(CLIENT *cl); #ifdef CACHE_DEBUG static void trace_host_cache(void); static void trace_portmap_cache(void); #endif /* CACHE_DEBUG */ static int rpc_timeout = 20; #ifdef CACHE_DEBUG /* * host cache counters. These variables do not need to be protected * by mutex's. They have been added to measure the utility of the * goodhost/deadhost cache in the lazy hierarchical mounting scheme. */ static int host_cache_accesses = 0; static int host_cache_lookups = 0; static int deadhost_cache_hits = 0; static int goodhost_cache_hits = 0; /* * portmap cache counters. These variables do not need to be protected * by mutex's. They have been added to measure the utility of the portmap * cache in the lazy hierarchical mounting scheme. */ static int portmap_cache_accesses = 0; static int portmap_cache_lookups = 0; static int portmap_cache_hits = 0; #endif /* CACHE_DEBUG */ /* * There are the defaults (range) for the client when determining * which NFS version to use when probing the server (see above). * These will only be used when the vers mount option is not used and * these may be reset if /etc/default/nfs is configured to do so. */ static rpcvers_t vers_max_default = NFS_VERSMAX_DEFAULT; static rpcvers_t vers_min_default = NFS_VERSMIN_DEFAULT; /* * list of support services needed */ static char *service_list[] = { STATD, LOCKD, NULL }; static char *service_list_v4[] = { STATD, LOCKD, NFS4CBD, NFSMAPID, NULL }; static void read_default_nfs(void); static int is_v4_mount(char *); static void start_nfs4cbd(void); int mount_nfs(struct mapent *me, char *mntpnt, char *prevhost, int overlay, uid_t uid, action_list **alpp) { struct mapfs *mfs, *mp; int err = -1; action_list *alp; char *dir; alp = *alpp; read_default_nfs(); mfs = enum_servers(me, prevhost); if (mfs == NULL) return (ENOENT); /* * Try loopback if we have something on localhost; if nothing * works, we will fall back to NFS */ if (is_nfs_port(me->map_mntopts)) { for (mp = mfs; mp; mp = mp->mfs_next) { if (self_check(mp->mfs_host)) { err = loopbackmount(mp->mfs_dir, mntpnt, me->map_mntopts, overlay); if (err) { mp->mfs_ignore = 1; } else { /* * Free action_list if there * is one as it is not needed. * Make sure to set alpp to null * so caller doesn't try to free it * again. */ if (*alpp) { free(*alpp); *alpp = NULL; } break; } } } } if (err) { dir = strdup(mfs->mfs_dir); err = nfsmount(mfs, mntpnt, me->map_mntopts, overlay, uid, alp); if (err && trace > 1) { trace_prt(1, " Couldn't mount %s:%s, err=%d\n", mfs->mfs_host ? mfs->mfs_host : "", mfs->mfs_dir ? mfs->mfs_dir : dir, err); } free(dir); } free_mfs(mfs); return (err); } /* * Using the new ioctl SIOCTONLINK to determine if a host is on the same * subnet. Remove the old network, subnet check. */ static struct mapfs * get_mysubnet_servers(struct mapfs *mfs_in) { int s; struct mapfs *mfs, *p, *mfs_head = NULL, *mfs_tail = NULL; struct netconfig *nconf; NCONF_HANDLE *nc = NULL; struct nd_hostserv hs; struct nd_addrlist *retaddrs; struct netbuf *nb; struct sioc_addrreq areq; int res; int af; int i; int sa_size; hs.h_serv = "rpcbind"; for (mfs = mfs_in; mfs; mfs = mfs->mfs_next) { nc = setnetconfig(); while (nconf = getnetconfig(nc)) { /* * Care about INET family only. proto_done flag * indicates if we have already covered this * protocol family. If so skip it */ if (((strcmp(nconf->nc_protofmly, NC_INET6) == 0) || (strcmp(nconf->nc_protofmly, NC_INET) == 0)) && (nconf->nc_semantics == NC_TPI_CLTS)) { } else continue; hs.h_host = mfs->mfs_host; if (netdir_getbyname(nconf, &hs, &retaddrs) != ND_OK) continue; /* * For each host address see if it's on our * local subnet. */ if (strcmp(nconf->nc_protofmly, NC_INET6) == 0) af = AF_INET6; else af = AF_INET; nb = retaddrs->n_addrs; for (i = 0; i < retaddrs->n_cnt; i++, nb++) { memset(&areq.sa_addr, 0, sizeof (areq.sa_addr)); memcpy(&areq.sa_addr, nb->buf, MIN(nb->len, sizeof (areq.sa_addr))); if (res = subnet_test(af, &areq)) { p = add_mfs(mfs, DIST_MYNET, &mfs_head, &mfs_tail); if (!p) { netdir_free(retaddrs, ND_ADDRLIST); endnetconfig(nc); return (NULL); } break; } } /* end of every host */ if (trace > 2) { trace_prt(1, "get_mysubnet_servers: host=%s " "netid=%s res=%s\n", mfs->mfs_host, nconf->nc_netid, res == 1?"SUC":"FAIL"); } netdir_free(retaddrs, ND_ADDRLIST); } /* end of while */ endnetconfig(nc); } /* end of every map */ return (mfs_head); } int subnet_test(int af, struct sioc_addrreq *areq) { int s; if ((s = socket(af, SOCK_DGRAM, 0)) < 0) { return (0); } areq->sa_res = -1; if (ioctl(s, SIOCTONLINK, (caddr_t)areq) < 0) { syslog(LOG_ERR, "subnet_test:SIOCTONLINK failed"); return (0); } close(s); if (areq->sa_res == 1) return (1); else return (0); } /* * ping a bunch of hosts at once and sort by who responds first */ static struct mapfs * sort_servers(struct mapfs *mfs_in, int timeout) { struct mapfs *m1 = NULL; enum clnt_stat clnt_stat; if (!mfs_in) return (NULL); clnt_stat = nfs_cast(mfs_in, &m1, timeout); if (!m1) { char buff[2048] = {'\0'}; for (m1 = mfs_in; m1; m1 = m1->mfs_next) { (void) strcat(buff, m1->mfs_host); if (m1->mfs_next) (void) strcat(buff, ","); } syslog(LOG_ERR, "servers %s not responding: %s", buff, clnt_sperrno(clnt_stat)); } return (m1); } /* * Add a mapfs entry to the list described by *mfs_head and *mfs_tail, * provided it is not marked "ignored" and isn't a dupe of ones we've * already seen. */ struct mapfs * add_mfs(struct mapfs *mfs, int distance, struct mapfs **mfs_head, struct mapfs **mfs_tail) { struct mapfs *tmp, *new; for (tmp = *mfs_head; tmp; tmp = tmp->mfs_next) if ((strcmp(tmp->mfs_host, mfs->mfs_host) == 0 && strcmp(tmp->mfs_dir, mfs->mfs_dir) == 0) || mfs->mfs_ignore) return (*mfs_head); new = (struct mapfs *)malloc(sizeof (struct mapfs)); if (!new) { syslog(LOG_ERR, "Memory allocation failed: %m"); return (NULL); } bcopy(mfs, new, sizeof (struct mapfs)); new->mfs_next = NULL; if (distance) new->mfs_distance = distance; if (!*mfs_head) *mfs_tail = *mfs_head = new; else { (*mfs_tail)->mfs_next = new; *mfs_tail = new; } return (*mfs_head); } static void dump_mfs(struct mapfs *mfs, char *message, int level) { struct mapfs *m1; if (trace <= level) return; trace_prt(1, "%s", message); if (!mfs) { trace_prt(0, "mfs is null\n"); return; } for (m1 = mfs; m1; m1 = m1->mfs_next) trace_prt(0, "%s[%s] ", m1->mfs_host, dump_distance(m1)); trace_prt(0, "\n"); } static char * dump_distance(struct mapfs *mfs) { switch (mfs->mfs_distance) { case 0: return ("zero"); case DIST_SELF: return ("self"); case DIST_MYSUB: return ("mysub"); case DIST_MYNET: return ("mynet"); case DIST_OTHER: return ("other"); default: return ("other"); } } /* * Walk linked list "raw", building a new list consisting of members * NOT found in list "filter", returning the result. */ static struct mapfs * filter_mfs(struct mapfs *raw, struct mapfs *filter) { struct mapfs *mfs, *p, *mfs_head = NULL, *mfs_tail = NULL; int skip; if (!raw) return (NULL); for (mfs = raw; mfs; mfs = mfs->mfs_next) { for (skip = 0, p = filter; p; p = p->mfs_next) { if (strcmp(p->mfs_host, mfs->mfs_host) == 0 && strcmp(p->mfs_dir, mfs->mfs_dir) == 0) { skip = 1; break; } } if (skip) continue; p = add_mfs(mfs, 0, &mfs_head, &mfs_tail); if (!p) return (NULL); } return (mfs_head); } /* * Walk a linked list of mapfs structs, freeing each member. */ void free_mfs(struct mapfs *mfs) { struct mapfs *tmp; while (mfs) { tmp = mfs->mfs_next; free(mfs); mfs = tmp; } } /* * New code for NFS client failover: we need to carry and sort * lists of server possibilities rather than return a single * entry. It preserves previous behaviour of sorting first by * locality (loopback-or-preferred/subnet/net/other) and then * by ping times. We'll short-circuit this process when we * have ENOUGH or more entries. */ static struct mapfs * enum_servers(struct mapent *me, char *preferred) { struct mapfs *p, *m1, *m2, *mfs_head = NULL, *mfs_tail = NULL; /* * Short-circuit for simple cases. */ if (!me->map_fs->mfs_next) { p = add_mfs(me->map_fs, DIST_OTHER, &mfs_head, &mfs_tail); if (!p) return (NULL); return (mfs_head); } dump_mfs(me->map_fs, " enum_servers: mapent: ", 2); /* * get addresses & see if any are myself * or were mounted from previously in a * hierarchical mount. */ if (trace > 2) trace_prt(1, " enum_servers: looking for pref/self\n"); for (m1 = me->map_fs; m1; m1 = m1->mfs_next) { if (m1->mfs_ignore) continue; if (self_check(m1->mfs_host) || strcmp(m1->mfs_host, preferred) == 0) { p = add_mfs(m1, DIST_SELF, &mfs_head, &mfs_tail); if (!p) return (NULL); } } if (trace > 2 && m1) trace_prt(1, " enum_servers: pref/self found, %s\n", m1->mfs_host); /* * look for entries on this subnet */ dump_mfs(m1, " enum_servers: input of get_mysubnet_servers: ", 2); m1 = get_mysubnet_servers(me->map_fs); dump_mfs(m1, " enum_servers: output of get_mysubnet_servers: ", 3); if (m1 && m1->mfs_next) { m2 = sort_servers(m1, rpc_timeout / 2); dump_mfs(m2, " enum_servers: output of sort_servers: ", 3); free_mfs(m1); m1 = m2; } for (m2 = m1; m2; m2 = m2->mfs_next) { p = add_mfs(m2, 0, &mfs_head, &mfs_tail); if (!p) return (NULL); } if (m1) free_mfs(m1); /* * add the rest of the entries at the end */ m1 = filter_mfs(me->map_fs, mfs_head); dump_mfs(m1, " enum_servers: etc: output of filter_mfs: ", 3); m2 = sort_servers(m1, rpc_timeout / 2); dump_mfs(m2, " enum_servers: etc: output of sort_servers: ", 3); if (m1) free_mfs(m1); m1 = m2; for (m2 = m1; m2; m2 = m2->mfs_next) { p = add_mfs(m2, DIST_OTHER, &mfs_head, &mfs_tail); if (!p) return (NULL); } if (m1) free_mfs(m1); dump_mfs(mfs_head, " enum_servers: output: ", 1); return (mfs_head); } static enum nfsstat nfsmount( struct mapfs *mfs_in, char *mntpnt, char *opts, int overlay, uid_t uid, action_list *alp) { CLIENT *cl; char remname[MAXPATHLEN], *mnttabtext = NULL; char mopts[MAX_MNTOPT_STR]; char netname[MAXNETNAMELEN+1]; char *mntopts = NULL; int mnttabcnt = 0; int loglevel; struct mnttab m; struct nfs_args *argp = NULL, *head = NULL, *tail = NULL, *prevhead, *prevtail; int flags; struct fhstatus fhs; struct timeval timeout; enum clnt_stat rpc_stat; enum nfsstat status; struct stat stbuf; struct netconfig *nconf; rpcvers_t vers, versmin; /* used to negotiate nfs version in pingnfs */ /* and mount version with mountd */ rpcvers_t outvers; /* final version to be used during mount() */ rpcvers_t nfsvers; /* version in map options, 0 if not there */ rpcvers_t mountversmax; /* tracks the max mountvers during retries */ /* used to negotiate nfs version using webnfs */ rpcvers_t pubvers, pubversmin, pubversmax; int posix; struct nd_addrlist *retaddrs; struct mountres3 res3; nfs_fh3 fh3; char *fstype; int count, i; char scerror_msg[MAXMSGLEN]; int *auths; int delay; int retries; char *nfs_proto = NULL; uint_t nfs_port = 0; char *p, *host, *rhost, *dir; struct mapfs *mfs = NULL; int error, last_error = 0; int replicated; int entries = 0; int v2cnt = 0, v3cnt = 0, v4cnt = 0; int v2near = 0, v3near = 0, v4near = 0; int skipentry = 0; char *nfs_flavor; seconfig_t nfs_sec; int sec_opt, scerror; struct sec_data *secdata; int secflags; struct netbuf *syncaddr; bool_t use_pubfh; ushort_t thisport; int got_val; mfs_snego_t mfssnego_init, mfssnego; dump_mfs(mfs_in, " nfsmount: input: ", 2); replicated = (mfs_in->mfs_next != NULL); m.mnt_mntopts = opts; if (replicated && hasmntopt(&m, MNTOPT_SOFT)) { if (verbose) syslog(LOG_WARNING, "mount on %s is soft and will not be replicated.", mntpnt); replicated = 0; } if (replicated && !hasmntopt(&m, MNTOPT_RO)) { if (verbose) syslog(LOG_WARNING, "mount on %s is not read-only and will not be replicated.", mntpnt); replicated = 0; } if (replicated) loglevel = LOG_WARNING; else loglevel = LOG_ERR; if (trace > 1) { if (replicated) trace_prt(1, " nfsmount: replicated mount on %s %s:\n", mntpnt, opts); else trace_prt(1, " nfsmount: standard mount on %s %s:\n", mntpnt, opts); for (mfs = mfs_in; mfs; mfs = mfs->mfs_next) trace_prt(1, " %s:%s\n", mfs->mfs_host, mfs->mfs_dir); } /* * Make sure mountpoint is safe to mount on */ if (lstat(mntpnt, &stbuf) < 0) { syslog(LOG_ERR, "Couldn't stat %s: %m", mntpnt); return (NFSERR_NOENT); } /* * Get protocol specified in options list, if any. */ if ((str_opt(&m, "proto", &nfs_proto)) == -1) { return (NFSERR_NOENT); } /* * Get port specified in options list, if any. */ got_val = nopt(&m, MNTOPT_PORT, (int *)&nfs_port); if (!got_val) nfs_port = 0; /* "unspecified" */ if (nfs_port > USHRT_MAX) { syslog(LOG_ERR, "%s: invalid port number %d", mntpnt, nfs_port); return (NFSERR_NOENT); } /* * Set mount(2) flags here, outside of the loop. */ flags = MS_OPTIONSTR; flags |= (hasmntopt(&m, MNTOPT_RO) == NULL) ? 0 : MS_RDONLY; flags |= (hasmntopt(&m, MNTOPT_NOSUID) == NULL) ? 0 : MS_NOSUID; flags |= overlay ? MS_OVERLAY : 0; if (mntpnt[strlen(mntpnt) - 1] != ' ') /* direct mount point without offsets */ flags |= MS_OVERLAY; use_pubfh = (hasmntopt(&m, MNTOPT_PUBLIC) == NULL) ? FALSE : TRUE; (void) memset(&mfssnego_init, 0, sizeof (mfs_snego_t)); if (hasmntopt(&m, MNTOPT_SECURE) != NULL) { if (++mfssnego_init.sec_opt > 1) { syslog(loglevel, "conflicting security options"); return (NFSERR_IO); } if (nfs_getseconfig_byname("dh", &mfssnego_init.nfs_sec)) { syslog(loglevel, "error getting dh information from %s", NFSSEC_CONF); return (NFSERR_IO); } } if (hasmntopt(&m, MNTOPT_SEC) != NULL) { if ((str_opt(&m, MNTOPT_SEC, &mfssnego_init.nfs_flavor)) == -1) { syslog(LOG_ERR, "nfsmount: no memory"); return (NFSERR_IO); } } if (mfssnego_init.nfs_flavor) { if (++mfssnego_init.sec_opt > 1) { syslog(loglevel, "conflicting security options"); free(mfssnego_init.nfs_flavor); return (NFSERR_IO); } if (nfs_getseconfig_byname(mfssnego_init.nfs_flavor, &mfssnego_init.nfs_sec)) { syslog(loglevel, "error getting %s information from %s", mfssnego_init.nfs_flavor, NFSSEC_CONF); free(mfssnego_init.nfs_flavor); return (NFSERR_IO); } free(mfssnego_init.nfs_flavor); } nextentry: skipentry = 0; got_val = nopt(&m, MNTOPT_VERS, (int *)&nfsvers); if (!got_val) nfsvers = 0; /* "unspecified" */ if (set_versrange(nfsvers, &vers, &versmin) != 0) { syslog(LOG_ERR, "Incorrect NFS version specified for %s", mntpnt); last_error = NFSERR_NOENT; goto ret; } if (nfsvers != 0) { pubversmax = pubversmin = nfsvers; } else { pubversmax = vers; pubversmin = versmin; } /* * Walk the whole list, pinging and collecting version * info so that we can make sure the mount will be * homogeneous with respect to version. * * If we have a version preference, this is easy; we'll * just reject anything that doesn't match. * * If not, we want to try to provide the best compromise * that considers proximity, preference for a higher version, * sorted order, and number of replicas. We will count * the number of V2 and V3 replicas and also the number * which are "near", i.e. the localhost or on the same * subnet. */ for (mfs = mfs_in; mfs; mfs = mfs->mfs_next) { if (mfs->mfs_ignore) continue; /* * If the host is '[a:d:d:r:e:s:s'], * only use 'a:d:d:r:e:s:s' for communication */ host = strdup(mfs->mfs_host); if (host == NULL) { syslog(LOG_ERR, "nfsmount: no memory"); last_error = NFSERR_IO; goto out; } unbracket(&host); (void) memcpy(&mfssnego, &mfssnego_init, sizeof (mfs_snego_t)); if (use_pubfh == TRUE || mfs->mfs_flags & MFS_URL) { char *path; if (nfs_port != 0 && mfs->mfs_port != 0 && nfs_port != mfs->mfs_port) { syslog(LOG_ERR, "nfsmount: port (%u) in nfs URL" " not the same as port (%d) in port " "option\n", mfs->mfs_port, nfs_port); last_error = NFSERR_IO; goto out; } else if (nfs_port != 0) thisport = nfs_port; else thisport = mfs->mfs_port; dir = mfs->mfs_dir; if ((mfs->mfs_flags & MFS_URL) == 0) { path = malloc(strlen(dir) + 2); if (path == NULL) { syslog(LOG_ERR, "nfsmount: no memory"); last_error = NFSERR_IO; goto out; } path[0] = (char)WNL_NATIVEPATH; (void) strcpy(&path[1], dir); } else { path = dir; } argp = (struct nfs_args *) malloc(sizeof (struct nfs_args)); if (!argp) { if (path != dir) free(path); syslog(LOG_ERR, "nfsmount: no memory"); last_error = NFSERR_IO; goto out; } (void) memset(argp, 0, sizeof (*argp)); /* * RDMA support * By now Mount argument struct has been allocated, * either a pub_fh path will be taken or the regular * one. So here if a protocol was specified and it * was not rdma we let it be, else we set DO_RDMA. * If no proto was there we advise on trying RDMA. */ if (nfs_proto) { if (strcmp(nfs_proto, "rdma") == 0) { free(nfs_proto); nfs_proto = NULL; argp->flags |= NFSMNT_DORDMA; } } else argp->flags |= NFSMNT_TRYRDMA; for (pubvers = pubversmax; pubvers >= pubversmin; pubvers--) { nconf = NULL; argp->addr = get_pubfh(host, pubvers, &mfssnego, &nconf, nfs_proto, thisport, NULL, &argp->fh, TRUE, path); if (argp->addr != NULL) break; if (nconf != NULL) freenetconfigent(nconf); } if (path != dir) free(path); if (argp->addr != NULL) { /* * The use of llock option for NFSv4 * mounts is not required since file * locking is included within the protocol */ if (pubvers != NFS_V4) argp->flags |= NFSMNT_LLOCK; argp->flags |= NFSMNT_PUBLIC; vers = pubvers; mfs->mfs_args = argp; mfs->mfs_version = pubvers; mfs->mfs_nconf = nconf; mfs->mfs_flags |= MFS_FH_VIA_WEBNFS; } else { free(argp); /* * If -public was specified, give up * on this entry now. */ if (use_pubfh == TRUE) { syslog(loglevel, "%s: no public file handle support", host); last_error = NFSERR_NOENT; mfs->mfs_ignore = 1; continue; } /* * Back off to a conventional mount. * * URL's can contain escape characters. Get * rid of them. */ path = malloc(strlen(dir) + 2); if (path == NULL) { syslog(LOG_ERR, "nfsmount: no memory"); last_error = NFSERR_IO; goto out; } strcpy(path, dir); URLparse(path); mfs->mfs_dir = path; mfs->mfs_flags |= MFS_ALLOC_DIR; mfs->mfs_flags &= ~MFS_URL; } } if ((mfs->mfs_flags & MFS_FH_VIA_WEBNFS) == 0) { i = pingnfs(host, get_retry(opts) + 1, &vers, versmin, 0, FALSE, NULL, nfs_proto); if (i != RPC_SUCCESS) { if (i == RPC_PROGVERSMISMATCH) { syslog(loglevel, "server %s: NFS " "protocol version mismatch", host); } else { syslog(loglevel, "server %s not " "responding", host); } mfs->mfs_ignore = 1; last_error = NFSERR_NOENT; continue; } if (nfsvers != 0 && nfsvers != vers) { if (nfs_proto == NULL) syslog(loglevel, "NFS version %d " "not supported by %s", nfsvers, host); else syslog(loglevel, "NFS version %d " "with proto %s " "not supported by %s", nfsvers, nfs_proto, host); mfs->mfs_ignore = 1; last_error = NFSERR_NOENT; continue; } } free(host); switch (vers) { case NFS_V4: v4cnt++; break; case NFS_V3: v3cnt++; break; case NFS_VERSION: v2cnt++; break; default: break; } /* * It's not clear how useful this stuff is if * we are using webnfs across the internet, but it * can't hurt. */ if (mfs->mfs_distance && mfs->mfs_distance <= DIST_MYSUB) { switch (vers) { case NFS_V4: v4near++; break; case NFS_V3: v3near++; break; case NFS_VERSION: v2near++; break; default: break; } } /* * If the mount is not replicated, we don't want to * ping every entry, so we'll stop here. This means * that we may have to go back to "nextentry" above * to consider another entry if we can't get * all the way to mount(2) with this one. */ if (!replicated) break; } if (nfsvers == 0) { /* * Choose the NFS version. * We prefer higher versions, but will choose a one- * version downgrade in service if we can use a local * network interface and avoid a router. */ if (v4cnt && v4cnt >= v3cnt && (v4near || !v3near)) nfsvers = NFS_V4; else if (v3cnt && v3cnt >= v2cnt && (v3near || !v2near)) nfsvers = NFS_V3; else nfsvers = NFS_VERSION; if (trace > 2) trace_prt(1, " nfsmount: v4=%d[%d]v3=%d[%d],v2=%d[%d] => v%d.\n", v4cnt, v4near, v3cnt, v3near, v2cnt, v2near, nfsvers); } /* * Since we don't support different NFS versions in replicated * mounts, set fstype now. * Also take the opportunity to set * the mount protocol version as appropriate. */ switch (nfsvers) { case NFS_V4: fstype = MNTTYPE_NFS4; break; case NFS_V3: fstype = MNTTYPE_NFS3; if (use_pubfh == FALSE) { mountversmax = MOUNTVERS3; versmin = MOUNTVERS3; } break; case NFS_VERSION: fstype = MNTTYPE_NFS; if (use_pubfh == FALSE) { mountversmax = MOUNTVERS_POSIX; versmin = MOUNTVERS; } break; } /* * Our goal here is to evaluate each of several possible * replicas and try to come up with a list we can hand * to mount(2). If we don't have a valid "head" at the * end of this process, it means we have rejected all * potential server:/path tuples. We will fail quietly * in front of mount(2), and will have printed errors * where we found them. * XXX - do option work outside loop w careful design * XXX - use macro for error condition free handling */ for (mfs = mfs_in; mfs; mfs = mfs->mfs_next) { /* * Initialize retry and delay values on a per-server basis. */ retries = get_retry(opts); delay = INITDELAY; retry: if (mfs->mfs_ignore) continue; /* * If we don't have a fh yet, and if this is not a replicated * mount, we haven't done a pingnfs() on the next entry, * so we don't know if the next entry is up or if it * supports an NFS version we like. So if we had a problem * with an entry, we need to go back and run through some new * code. */ if ((mfs->mfs_flags & MFS_FH_VIA_WEBNFS) == 0 && !replicated && skipentry) goto nextentry; vers = mountversmax; host = mfs->mfs_host; dir = mfs->mfs_dir; /* * Remember the possible '[a:d:d:r:e:s:s]' as the address to be * later passed to mount(2) and used in the mnttab line, but * only use 'a:d:d:r:e:s:s' for communication */ rhost = strdup(host); if (rhost == NULL) { syslog(LOG_ERR, "nfsmount: no memory"); last_error = NFSERR_IO; goto out; } unbracket(&host); (void) sprintf(remname, "%s:%s", rhost, dir); if (trace > 4 && replicated) trace_prt(1, " nfsmount: examining %s\n", remname); if (mfs->mfs_args == NULL) { /* * Allocate nfs_args structure */ argp = (struct nfs_args *) malloc(sizeof (struct nfs_args)); if (!argp) { syslog(LOG_ERR, "nfsmount: no memory"); last_error = NFSERR_IO; goto out; } (void) memset(argp, 0, sizeof (*argp)); /* * RDMA support * By now Mount argument struct has been allocated, * either a pub_fh path will be taken or the regular * one. So here if a protocol was specified and it * was not rdma we let it be, else we set DO_RDMA. * If no proto was there we advise on trying RDMA. */ if (nfs_proto) { if (strcmp(nfs_proto, "rdma") == 0) { free(nfs_proto); nfs_proto = NULL; argp->flags |= NFSMNT_DORDMA; } } else argp->flags |= NFSMNT_TRYRDMA; } else { argp = mfs->mfs_args; mfs->mfs_args = NULL; /* * Skip entry if we already have file handle but the * NFS version is wrong. */ if ((mfs->mfs_flags & MFS_FH_VIA_WEBNFS) && mfs->mfs_version != nfsvers) { free(argp); skipentry = 1; mfs->mfs_ignore = 1; continue; } } prevhead = head; prevtail = tail; if (!head) head = tail = argp; else tail = tail->nfs_ext_u.nfs_extB.next = argp; /* * WebNFS and NFSv4 behave similarly in that they * don't use the mount protocol. Therefore, avoid * mount protocol like things when version 4 is being * used. */ if ((mfs->mfs_flags & MFS_FH_VIA_WEBNFS) == 0 && nfsvers != NFS_V4) { timeout.tv_usec = 0; timeout.tv_sec = rpc_timeout; rpc_stat = RPC_TIMEDOUT; /* Create the client handle. */ if (trace > 1) { trace_prt(1, " nfsmount: Get mount version: request " "vers=%d min=%d\n", vers, versmin); } while ((cl = clnt_create_vers(host, MOUNTPROG, &outvers, versmin, vers, "udp")) == NULL) { if (trace > 4) { trace_prt(1, " nfsmount: Can't get mount " "version: rpcerr=%d\n", rpc_createerr.cf_stat); } if (rpc_createerr.cf_stat == RPC_UNKNOWNHOST || rpc_createerr.cf_stat == RPC_TIMEDOUT) break; /* * backoff and return lower version to retry the ping. * XXX we should be more careful and handle * RPC_PROGVERSMISMATCH here, because that error * is handled in clnt_create_vers(). It's not done to * stay in sync with the nfs mount command. */ vers--; if (vers < versmin) break; if (trace > 4) { trace_prt(1, " nfsmount: Try version=%d\n", vers); } } if (cl == NULL) { free(argp); head = prevhead; tail = prevtail; if (tail) tail->nfs_ext_u.nfs_extB.next = NULL; last_error = NFSERR_NOENT; if (rpc_createerr.cf_stat != RPC_UNKNOWNHOST && rpc_createerr.cf_stat != RPC_PROGVERSMISMATCH && retries-- > 0) { DELAY(delay); goto retry; } syslog(loglevel, "%s %s", host, clnt_spcreateerror( "server not responding")); skipentry = 1; mfs->mfs_ignore = 1; continue; } if (trace > 1) { trace_prt(1, " nfsmount: mount version=%d\n", outvers); } #ifdef MALLOC_DEBUG add_alloc("CLNT_HANDLE", cl, 0, __FILE__, __LINE__); add_alloc("AUTH_HANDLE", cl->cl_auth, 0, __FILE__, __LINE__); #endif if (__clnt_bindresvport(cl) < 0) { free(argp); head = prevhead; tail = prevtail; if (tail) tail->nfs_ext_u.nfs_extB.next = NULL; last_error = NFSERR_NOENT; if (retries-- > 0) { destroy_auth_client_handle(cl); DELAY(delay); goto retry; } syslog(loglevel, "mount %s: %s", host, "Couldn't bind to reserved port"); destroy_auth_client_handle(cl); skipentry = 1; mfs->mfs_ignore = 1; continue; } #ifdef MALLOC_DEBUG drop_alloc("AUTH_HANDLE", cl->cl_auth, __FILE__, __LINE__); #endif AUTH_DESTROY(cl->cl_auth); if ((cl->cl_auth = authsys_create_default()) == NULL) { free(argp); head = prevhead; tail = prevtail; if (tail) tail->nfs_ext_u.nfs_extB.next = NULL; last_error = NFSERR_NOENT; if (retries-- > 0) { destroy_auth_client_handle(cl); DELAY(delay); goto retry; } syslog(loglevel, "mount %s: %s", host, "Failed creating default auth handle"); destroy_auth_client_handle(cl); skipentry = 1; mfs->mfs_ignore = 1; continue; } #ifdef MALLOC_DEBUG add_alloc("AUTH_HANDLE", cl->cl_auth, 0, __FILE__, __LINE__); #endif } else cl = NULL; /* * set security options */ sec_opt = 0; (void) memset(&nfs_sec, 0, sizeof (nfs_sec)); if (hasmntopt(&m, MNTOPT_SECURE) != NULL) { if (++sec_opt > 1) { syslog(loglevel, "conflicting security options for %s", remname); free(argp); head = prevhead; tail = prevtail; if (tail) tail->nfs_ext_u.nfs_extB.next = NULL; last_error = NFSERR_IO; destroy_auth_client_handle(cl); skipentry = 1; mfs->mfs_ignore = 1; continue; } if (nfs_getseconfig_byname("dh", &nfs_sec)) { syslog(loglevel, "error getting dh information from %s", NFSSEC_CONF); free(argp); head = prevhead; tail = prevtail; if (tail) tail->nfs_ext_u.nfs_extB.next = NULL; last_error = NFSERR_IO; destroy_auth_client_handle(cl); skipentry = 1; mfs->mfs_ignore = 1; continue; } } nfs_flavor = NULL; if (hasmntopt(&m, MNTOPT_SEC) != NULL) { if ((str_opt(&m, MNTOPT_SEC, &nfs_flavor)) == -1) { syslog(LOG_ERR, "nfsmount: no memory"); last_error = NFSERR_IO; destroy_auth_client_handle(cl); goto out; } } if (nfs_flavor) { if (++sec_opt > 1) { syslog(loglevel, "conflicting security options for %s", remname); free(nfs_flavor); free(argp); head = prevhead; tail = prevtail; if (tail) tail->nfs_ext_u.nfs_extB.next = NULL; last_error = NFSERR_IO; destroy_auth_client_handle(cl); skipentry = 1; mfs->mfs_ignore = 1; continue; } if (nfs_getseconfig_byname(nfs_flavor, &nfs_sec)) { syslog(loglevel, "error getting %s information from %s", nfs_flavor, NFSSEC_CONF); free(nfs_flavor); free(argp); head = prevhead; tail = prevtail; if (tail) tail->nfs_ext_u.nfs_extB.next = NULL; last_error = NFSERR_IO; destroy_auth_client_handle(cl); skipentry = 1; mfs->mfs_ignore = 1; continue; } free(nfs_flavor); } posix = (nfsvers != NFS_V4 && hasmntopt(&m, MNTOPT_POSIX) != NULL) ? 1 : 0; if ((mfs->mfs_flags & MFS_FH_VIA_WEBNFS) == 0 && nfsvers != NFS_V4) { bool_t give_up_on_mnt; bool_t got_mnt_error; /* * If we started with a URL, if first byte of path is not "/", * then the mount will likely fail, so we should try again * with a prepended "/". */ if (mfs->mfs_flags & MFS_ALLOC_DIR && *dir != '/') give_up_on_mnt = FALSE; else give_up_on_mnt = TRUE; got_mnt_error = FALSE; try_mnt_slash: if (got_mnt_error == TRUE) { int i, l; give_up_on_mnt = TRUE; l = strlen(dir); /* * Insert a "/" to front of mfs_dir. */ for (i = l; i > 0; i--) dir[i] = dir[i-1]; dir[0] = '/'; } /* Get fhandle of remote path from server's mountd */ switch (outvers) { case MOUNTVERS: if (posix) { free(argp); head = prevhead; tail = prevtail; if (tail) tail->nfs_ext_u.nfs_extB.next = NULL; last_error = NFSERR_NOENT; syslog(loglevel, "can't get posix info for %s", host); destroy_auth_client_handle(cl); skipentry = 1; mfs->mfs_ignore = 1; continue; } /* FALLTHRU */ case MOUNTVERS_POSIX: if (nfsvers == NFS_V3) { free(argp); head = prevhead; tail = prevtail; if (tail) tail->nfs_ext_u.nfs_extB.next = NULL; last_error = NFSERR_NOENT; syslog(loglevel, "%s doesn't support NFS Version 3", host); destroy_auth_client_handle(cl); skipentry = 1; mfs->mfs_ignore = 1; continue; } rpc_stat = clnt_call(cl, MOUNTPROC_MNT, xdr_dirpath, (caddr_t)&dir, xdr_fhstatus, (caddr_t)&fhs, timeout); if (rpc_stat != RPC_SUCCESS) { if (give_up_on_mnt == FALSE) { got_mnt_error = TRUE; goto try_mnt_slash; } /* * Given the way "clnt_sperror" works, the "%s" * immediately following the "not responding" * is correct. */ free(argp); head = prevhead; tail = prevtail; if (tail) tail->nfs_ext_u.nfs_extB.next = NULL; last_error = NFSERR_NOENT; if (retries-- > 0) { destroy_auth_client_handle(cl); DELAY(delay); goto retry; } if (trace > 3) { trace_prt(1, " nfsmount: mount RPC " "failed for %s\n", host); } syslog(loglevel, "%s server not responding%s", host, clnt_sperror(cl, "")); destroy_auth_client_handle(cl); skipentry = 1; mfs->mfs_ignore = 1; continue; } if ((errno = fhs.fhs_status) != MNT_OK) { if (give_up_on_mnt == FALSE) { got_mnt_error = TRUE; goto try_mnt_slash; } free(argp); head = prevhead; tail = prevtail; if (tail) tail->nfs_ext_u.nfs_extB.next = NULL; if (errno == EACCES) { status = NFSERR_ACCES; } else { syslog(loglevel, "%s: %m", host); status = NFSERR_IO; } if (trace > 3) { trace_prt(1, " nfsmount: mount RPC gave" " %d for %s:%s\n", errno, host, dir); } last_error = status; destroy_auth_client_handle(cl); skipentry = 1; mfs->mfs_ignore = 1; continue; } argp->fh = malloc((sizeof (fhandle))); if (!argp->fh) { syslog(LOG_ERR, "nfsmount: no memory"); last_error = NFSERR_IO; destroy_auth_client_handle(cl); goto out; } (void) memcpy(argp->fh, &fhs.fhstatus_u.fhs_fhandle, sizeof (fhandle)); break; case MOUNTVERS3: posix = 0; (void) memset((char *)&res3, '\0', sizeof (res3)); rpc_stat = clnt_call(cl, MOUNTPROC_MNT, xdr_dirpath, (caddr_t)&dir, xdr_mountres3, (caddr_t)&res3, timeout); if (rpc_stat != RPC_SUCCESS) { if (give_up_on_mnt == FALSE) { got_mnt_error = TRUE; goto try_mnt_slash; } /* * Given the way "clnt_sperror" works, the "%s" * immediately following the "not responding" * is correct. */ free(argp); head = prevhead; tail = prevtail; if (tail) tail->nfs_ext_u.nfs_extB.next = NULL; last_error = NFSERR_NOENT; if (retries-- > 0) { destroy_auth_client_handle(cl); DELAY(delay); goto retry; } if (trace > 3) { trace_prt(1, " nfsmount: mount RPC " "failed for %s\n", host); } syslog(loglevel, "%s server not responding%s", remname, clnt_sperror(cl, "")); destroy_auth_client_handle(cl); skipentry = 1; mfs->mfs_ignore = 1; continue; } if ((errno = res3.fhs_status) != MNT_OK) { if (give_up_on_mnt == FALSE) { got_mnt_error = TRUE; goto try_mnt_slash; } free(argp); head = prevhead; tail = prevtail; if (tail) tail->nfs_ext_u.nfs_extB.next = NULL; if (errno == EACCES) { status = NFSERR_ACCES; } else { syslog(loglevel, "%s: %m", remname); status = NFSERR_IO; } if (trace > 3) { trace_prt(1, " nfsmount: mount RPC gave" " %d for %s:%s\n", errno, host, dir); } last_error = status; destroy_auth_client_handle(cl); skipentry = 1; mfs->mfs_ignore = 1; continue; } /* * Negotiate the security flavor for nfs_mount */ auths = res3.mountres3_u.mountinfo. auth_flavors.auth_flavors_val; count = res3.mountres3_u.mountinfo. auth_flavors.auth_flavors_len; if (sec_opt) { for (i = 0; i < count; i++) if (auths[i] == nfs_sec.sc_nfsnum) { break; } if (i >= count) { syslog(LOG_ERR, "%s: does not support " "security \"%s\"\n", remname, nfs_sec.sc_name); clnt_freeres(cl, xdr_mountres3, (caddr_t)&res3); free(argp); head = prevhead; tail = prevtail; if (tail) tail->nfs_ext_u. nfs_extB.next = NULL; last_error = NFSERR_IO; destroy_auth_client_handle(cl); skipentry = 1; mfs->mfs_ignore = 1; continue; } } else if (count > 0) { for (i = 0; i < count; i++) { if (!(scerror = nfs_getseconfig_bynumber( auths[i], &nfs_sec))) { sec_opt++; break; } } if (i >= count) { if (nfs_syslog_scerr(scerror, scerror_msg) != -1) { syslog(LOG_ERR, "%s cannot be " "mounted because it" " is shared with " "security flavor %d" " which %s", remname, auths[i-1], scerror_msg); } clnt_freeres(cl, xdr_mountres3, (caddr_t)&res3); free(argp); head = prevhead; tail = prevtail; if (tail) tail->nfs_ext_u. nfs_extB.next = NULL; last_error = NFSERR_IO; destroy_auth_client_handle(cl); skipentry = 1; mfs->mfs_ignore = 1; continue; } } fh3.fh3_length = res3.mountres3_u.mountinfo.fhandle. fhandle3_len; (void) memcpy(fh3.fh3_u.data, res3.mountres3_u.mountinfo.fhandle. fhandle3_val, fh3.fh3_length); clnt_freeres(cl, xdr_mountres3, (caddr_t)&res3); argp->fh = malloc(sizeof (nfs_fh3)); if (!argp->fh) { syslog(LOG_ERR, "nfsmount: no memory"); last_error = NFSERR_IO; destroy_auth_client_handle(cl); goto out; } (void) memcpy(argp->fh, &fh3, sizeof (nfs_fh3)); break; default: free(argp); head = prevhead; tail = prevtail; if (tail) tail->nfs_ext_u.nfs_extB.next = NULL; last_error = NFSERR_NOENT; syslog(loglevel, "unknown MOUNT version %ld on %s", vers, remname); destroy_auth_client_handle(cl); skipentry = 1; mfs->mfs_ignore = 1; continue; } /* switch */ } if (nfsvers == NFS_V4) { argp->fh = strdup(dir); if (argp->fh == NULL) { syslog(LOG_ERR, "nfsmount: no memory"); last_error = NFSERR_IO; goto out; } } if (trace > 4) trace_prt(1, " nfsmount: have %s filehandle for %s\n", fstype, remname); argp->flags |= NFSMNT_NEWARGS; argp->flags |= NFSMNT_INT; /* default is "intr" */ argp->flags |= NFSMNT_HOSTNAME; argp->hostname = strdup(host); if (argp->hostname == NULL) { syslog(LOG_ERR, "nfsmount: no memory"); last_error = NFSERR_IO; goto out; } /* * In this case, we want NFSv4 to behave like * non-WebNFS so that we get the server address. */ if ((mfs->mfs_flags & MFS_FH_VIA_WEBNFS) == 0) { nconf = NULL; if (nfs_port != 0) thisport = nfs_port; else thisport = mfs->mfs_port; /* * For NFSv4, we want to avoid rpcbind, so call * get_server_netinfo() directly to tell it that * we want to go "direct_to_server". Otherwise, * do what has always been done. */ if (nfsvers == NFS_V4) { enum clnt_stat cstat; argp->addr = get_server_netinfo(SERVER_ADDR, host, NFS_PROGRAM, nfsvers, NULL, &nconf, nfs_proto, thisport, NULL, NULL, TRUE, NULL, &cstat); } else { argp->addr = get_addr(host, NFS_PROGRAM, nfsvers, &nconf, nfs_proto, thisport, NULL); } if (argp->addr == NULL) { if (argp->hostname) free(argp->hostname); free(argp->fh); free(argp); head = prevhead; tail = prevtail; if (tail) tail->nfs_ext_u.nfs_extB.next = NULL; last_error = NFSERR_NOENT; if (retries-- > 0) { destroy_auth_client_handle(cl); DELAY(delay); goto retry; } syslog(loglevel, "%s: no NFS service", host); destroy_auth_client_handle(cl); skipentry = 1; mfs->mfs_ignore = 1; continue; } if (trace > 4) trace_prt(1, "\tnfsmount: have net address for %s\n", remname); } else { nconf = mfs->mfs_nconf; mfs->mfs_nconf = NULL; } argp->flags |= NFSMNT_KNCONF; argp->knconf = get_knconf(nconf); if (argp->knconf == NULL) { netbuf_free(argp->addr); freenetconfigent(nconf); if (argp->hostname) free(argp->hostname); free(argp->fh); free(argp); head = prevhead; tail = prevtail; if (tail) tail->nfs_ext_u.nfs_extB.next = NULL; last_error = NFSERR_NOSPC; destroy_auth_client_handle(cl); skipentry = 1; mfs->mfs_ignore = 1; continue; } if (trace > 4) trace_prt(1, "\tnfsmount: have net config for %s\n", remname); if (hasmntopt(&m, MNTOPT_SOFT) != NULL) { argp->flags |= NFSMNT_SOFT; } if (hasmntopt(&m, MNTOPT_NOINTR) != NULL) { argp->flags &= ~(NFSMNT_INT); } if (hasmntopt(&m, MNTOPT_NOAC) != NULL) { argp->flags |= NFSMNT_NOAC; } if (hasmntopt(&m, MNTOPT_NOCTO) != NULL) { argp->flags |= NFSMNT_NOCTO; } if (hasmntopt(&m, MNTOPT_FORCEDIRECTIO) != NULL) { argp->flags |= NFSMNT_DIRECTIO; } if (hasmntopt(&m, MNTOPT_NOFORCEDIRECTIO) != NULL) { argp->flags &= ~(NFSMNT_DIRECTIO); } /* * Set up security data for argp->nfs_ext_u.nfs_extB.secdata. */ if (mfssnego.snego_done) { memcpy(&nfs_sec, &mfssnego.nfs_sec, sizeof (seconfig_t)); } else if (!sec_opt) { /* * Get default security mode. */ if (nfs_getseconfig_default(&nfs_sec)) { syslog(loglevel, "error getting default security entry\n"); free_knconf(argp->knconf); netbuf_free(argp->addr); freenetconfigent(nconf); if (argp->hostname) free(argp->hostname); free(argp->fh); free(argp); head = prevhead; tail = prevtail; if (tail) tail->nfs_ext_u.nfs_extB.next = NULL; last_error = NFSERR_NOSPC; destroy_auth_client_handle(cl); skipentry = 1; mfs->mfs_ignore = 1; continue; } argp->flags |= NFSMNT_SECDEFAULT; } /* * For AUTH_DH * get the network address for the time service on * the server. If an RPC based time service is * not available then try the IP time service. * * Eventurally, we want to move this code to nfs_clnt_secdata() * when autod_nfs.c and mount.c can share the same * get_the_addr/get_netconfig_info routine. */ secflags = 0; syncaddr = NULL; retaddrs = NULL; if (nfs_sec.sc_rpcnum == AUTH_DH || nfsvers == NFS_V4) { /* * If not using the public fh and not NFS_V4, we can try * talking RPCBIND. Otherwise, assume that firewalls * prevent us from doing that. */ if ((mfs->mfs_flags & MFS_FH_VIA_WEBNFS) == 0 && nfsvers != NFS_V4) { enum clnt_stat cstat; syncaddr = get_server_netinfo(SERVER_ADDR, host, RPCBPROG, RPCBVERS, NULL, &nconf, NULL, 0, NULL, NULL, FALSE, NULL, &cstat); } if (syncaddr != NULL) { /* for flags in sec_data */ secflags |= AUTH_F_RPCTIMESYNC; } else { struct nd_hostserv hs; int error; hs.h_host = host; hs.h_serv = "timserver"; error = netdir_getbyname(nconf, &hs, &retaddrs); if (error != ND_OK && nfs_sec.sc_rpcnum == AUTH_DH) { syslog(loglevel, "%s: secure: no time service\n", host); free_knconf(argp->knconf); netbuf_free(argp->addr); freenetconfigent(nconf); if (argp->hostname) free(argp->hostname); free(argp->fh); free(argp); head = prevhead; tail = prevtail; if (tail) tail->nfs_ext_u.nfs_extB.next = NULL; last_error = NFSERR_IO; destroy_auth_client_handle(cl); skipentry = 1; mfs->mfs_ignore = 1; continue; } if (error == ND_OK) syncaddr = retaddrs->n_addrs; /* * For potential usage by NFS V4 when AUTH_DH * is negotiated via SECINFO in the kernel. */ if (nfsvers == NFS_V4 && syncaddr && host2netname(netname, host, NULL)) { argp->syncaddr = malloc(sizeof (struct netbuf)); argp->syncaddr->buf = malloc(syncaddr->len); (void) memcpy(argp->syncaddr->buf, syncaddr->buf, syncaddr->len); argp->syncaddr->len = syncaddr->len; argp->syncaddr->maxlen = syncaddr->maxlen; argp->netname = strdup(netname); argp->flags |= NFSMNT_SECURE; } } /* syncaddr */ } /* AUTH_DH */ /* * TSOL notes: automountd in tsol extension * has "read down" capability, i.e. we allow * a user to trigger an nfs mount into a lower * labeled zone. We achieve this by always having * root issue the mount request so that the * lookup ops can go past /zone/ * on the server side. */ if (is_system_labeled()) nfs_sec.sc_uid = (uid_t)0; else nfs_sec.sc_uid = uid; /* * If AUTH_DH is a chosen flavor now, its data will be stored * in the sec_data structure via nfs_clnt_secdata(). */ if (!(secdata = nfs_clnt_secdata(&nfs_sec, host, argp->knconf, syncaddr, secflags))) { syslog(LOG_ERR, "errors constructing security related data\n"); if (secflags & AUTH_F_RPCTIMESYNC) netbuf_free(syncaddr); else if (retaddrs) netdir_free(retaddrs, ND_ADDRLIST); if (argp->syncaddr) netbuf_free(argp->syncaddr); if (argp->netname) free(argp->netname); if (argp->hostname) free(argp->hostname); free_knconf(argp->knconf); netbuf_free(argp->addr); freenetconfigent(nconf); free(argp->fh); free(argp); head = prevhead; tail = prevtail; if (tail) tail->nfs_ext_u.nfs_extB.next = NULL; last_error = NFSERR_IO; destroy_auth_client_handle(cl); skipentry = 1; mfs->mfs_ignore = 1; continue; } NFS_ARGS_EXTB_secdata(*argp, secdata); /* end of security stuff */ if (trace > 4) trace_prt(1, " nfsmount: have secure info for %s\n", remname); if (hasmntopt(&m, MNTOPT_GRPID) != NULL) { argp->flags |= NFSMNT_GRPID; } if (nopt(&m, MNTOPT_RSIZE, &argp->rsize)) { argp->flags |= NFSMNT_RSIZE; } if (nopt(&m, MNTOPT_WSIZE, &argp->wsize)) { argp->flags |= NFSMNT_WSIZE; } if (nopt(&m, MNTOPT_TIMEO, &argp->timeo)) { argp->flags |= NFSMNT_TIMEO; } if (nopt(&m, MNTOPT_RETRANS, &argp->retrans)) { argp->flags |= NFSMNT_RETRANS; } if (nopt(&m, MNTOPT_ACTIMEO, &argp->acregmax)) { argp->flags |= NFSMNT_ACREGMAX; argp->flags |= NFSMNT_ACDIRMAX; argp->flags |= NFSMNT_ACDIRMIN; argp->flags |= NFSMNT_ACREGMIN; argp->acdirmin = argp->acregmin = argp->acdirmax = argp->acregmax; } else { if (nopt(&m, MNTOPT_ACREGMIN, &argp->acregmin)) { argp->flags |= NFSMNT_ACREGMIN; } if (nopt(&m, MNTOPT_ACREGMAX, &argp->acregmax)) { argp->flags |= NFSMNT_ACREGMAX; } if (nopt(&m, MNTOPT_ACDIRMIN, &argp->acdirmin)) { argp->flags |= NFSMNT_ACDIRMIN; } if (nopt(&m, MNTOPT_ACDIRMAX, &argp->acdirmax)) { argp->flags |= NFSMNT_ACDIRMAX; } } if (posix) { argp->pathconf = NULL; if (error = get_pathconf(cl, dir, remname, &argp->pathconf, retries)) { if (secflags & AUTH_F_RPCTIMESYNC) netbuf_free(syncaddr); else if (retaddrs) netdir_free(retaddrs, ND_ADDRLIST); free_knconf(argp->knconf); netbuf_free(argp->addr); freenetconfigent(nconf); nfs_free_secdata( argp->nfs_ext_u.nfs_extB.secdata); if (argp->syncaddr) netbuf_free(argp->syncaddr); if (argp->netname) free(argp->netname); if (argp->hostname) free(argp->hostname); free(argp->fh); free(argp); head = prevhead; tail = prevtail; if (tail) tail->nfs_ext_u.nfs_extB.next = NULL; last_error = NFSERR_IO; if (error == RET_RETRY && retries-- > 0) { destroy_auth_client_handle(cl); DELAY(delay); goto retry; } destroy_auth_client_handle(cl); skipentry = 1; mfs->mfs_ignore = 1; continue; } argp->flags |= NFSMNT_POSIX; if (trace > 4) trace_prt(1, " nfsmount: have pathconf for %s\n", remname); } /* * free loop-specific data structures */ destroy_auth_client_handle(cl); freenetconfigent(nconf); if (secflags & AUTH_F_RPCTIMESYNC) netbuf_free(syncaddr); else if (retaddrs) netdir_free(retaddrs, ND_ADDRLIST); /* * Decide whether to use remote host's lockd or local locking. * If we are using the public fh, we've already turned * LLOCK on. */ if (hasmntopt(&m, MNTOPT_LLOCK)) argp->flags |= NFSMNT_LLOCK; if (!(argp->flags & NFSMNT_LLOCK) && nfsvers == NFS_VERSION && remote_lock(host, argp->fh)) { syslog(loglevel, "No network locking on %s : " "contact admin to install server change", host); argp->flags |= NFSMNT_LLOCK; } /* * Build a string for /etc/mnttab. * If possible, coalesce strings with same 'dir' info. */ if ((mfs->mfs_flags & MFS_URL) == 0) { char *tmp; if (mnttabcnt) { p = strrchr(mnttabtext, (int)':'); if (!p || strcmp(p+1, dir) != 0) { mnttabcnt += strlen(remname) + 2; } else { *p = '\0'; mnttabcnt += strlen(rhost) + 2; } if ((tmp = realloc(mnttabtext, mnttabcnt)) != NULL) { mnttabtext = tmp; strcat(mnttabtext, ","); } else { free(mnttabtext); mnttabtext = NULL; } } else { mnttabcnt = strlen(remname) + 1; if ((mnttabtext = malloc(mnttabcnt)) != NULL) mnttabtext[0] = '\0'; } if (mnttabtext != NULL) strcat(mnttabtext, remname); } else { char *tmp; int more_cnt = 0; char sport[16]; more_cnt += strlen("nfs://"); more_cnt += strlen(mfs->mfs_host); if (mfs->mfs_port != 0) { (void) sprintf(sport, ":%u", mfs->mfs_port); } else sport[0] = '\0'; more_cnt += strlen(sport); more_cnt += 1; /* "/" */ more_cnt += strlen(mfs->mfs_dir); if (mnttabcnt) { more_cnt += 1; /* "," */ mnttabcnt += more_cnt; if ((tmp = realloc(mnttabtext, mnttabcnt)) != NULL) { mnttabtext = tmp; strcat(mnttabtext, ","); } else { free(mnttabtext); mnttabtext = NULL; } } else { mnttabcnt = more_cnt + 1; if ((mnttabtext = malloc(mnttabcnt)) != NULL) mnttabtext[0] = '\0'; } if (mnttabtext != NULL) { strcat(mnttabtext, "nfs://"); strcat(mnttabtext, mfs->mfs_host); strcat(mnttabtext, sport); strcat(mnttabtext, "/"); strcat(mnttabtext, mfs->mfs_dir); } } if (!mnttabtext) { syslog(LOG_ERR, "nfsmount: no memory"); last_error = NFSERR_IO; goto out; } /* * At least one entry, can call mount(2). */ entries++; /* * If replication was defeated, don't do more work */ if (!replicated) break; } /* * Did we get through all possibilities without success? */ if (!entries) goto out; /* Make "xattr" the default if "noxattr" is not specified. */ strcpy(mopts, opts); if (!hasmntopt(&m, MNTOPT_NOXATTR) && !hasmntopt(&m, MNTOPT_XATTR)) { if (strlen(mopts) > 0) strcat(mopts, ","); strcat(mopts, "xattr"); } /* * enable services as needed. */ { char **sl; if (strcmp(fstype, MNTTYPE_NFS4) == 0) sl = service_list_v4; else sl = service_list; (void) _check_services(sl); } /* * Whew; do the mount, at last. */ if (trace > 1) { trace_prt(1, " mount %s %s (%s)\n", mnttabtext, mntpnt, mopts); } /* * About to do a nfs mount, make sure the mount_to is set for * potential ephemeral mounts with NFSv4. */ set_nfsv4_ephemeral_mount_to(); /* * If no action list pointer then do the mount, otherwise * build the actions list pointer with the mount information. * so the mount can be done in the kernel. */ if (alp == NULL) { if (mount(mnttabtext, mntpnt, flags | MS_DATA, fstype, head, sizeof (*head), mopts, MAX_MNTOPT_STR) < 0) { if (trace > 1) trace_prt(1, " Mount of %s on %s: %d\n", mnttabtext, mntpnt, errno); if (errno != EBUSY || verbose) syslog(LOG_ERR, "Mount of %s on %s: %m", mnttabtext, mntpnt); last_error = NFSERR_IO; goto out; } last_error = NFS_OK; if (stat(mntpnt, &stbuf) == 0) { if (trace > 1) { trace_prt(1, " mount %s dev=%x rdev=%x OK\n", mnttabtext, stbuf.st_dev, stbuf.st_rdev); } } else { if (trace > 1) { trace_prt(1, " mount %s OK\n", mnttabtext); trace_prt(1, " stat of %s failed\n", mntpnt); } } } else { alp->action.action = AUTOFS_MOUNT_RQ; alp->action.action_list_entry_u.mounta.spec = strdup(mnttabtext); alp->action.action_list_entry_u.mounta.dir = strdup(mntpnt); alp->action.action_list_entry_u.mounta.flags = flags | MS_DATA; alp->action.action_list_entry_u.mounta.fstype = strdup(fstype); alp->action.action_list_entry_u.mounta.dataptr = (char *)head; alp->action.action_list_entry_u.mounta.datalen = sizeof (*head); mntopts = malloc(strlen(mopts) + 1); strcpy(mntopts, mopts); mntopts[strlen(mopts)] = '\0'; alp->action.action_list_entry_u.mounta.optptr = mntopts; alp->action.action_list_entry_u.mounta.optlen = strlen(mntopts) + 1; last_error = NFS_OK; goto ret; } out: argp = head; while (argp) { if (argp->pathconf) free(argp->pathconf); free_knconf(argp->knconf); netbuf_free(argp->addr); if (argp->syncaddr) netbuf_free(argp->syncaddr); if (argp->netname) { free(argp->netname); } if (argp->hostname) free(argp->hostname); nfs_free_secdata(argp->nfs_ext_u.nfs_extB.secdata); free(argp->fh); head = argp; argp = argp->nfs_ext_u.nfs_extB.next; free(head); } ret: if (nfs_proto) free(nfs_proto); if (mnttabtext) free(mnttabtext); for (mfs = mfs_in; mfs; mfs = mfs->mfs_next) { if (mfs->mfs_flags & MFS_ALLOC_DIR) { free(mfs->mfs_dir); mfs->mfs_dir = NULL; mfs->mfs_flags &= ~MFS_ALLOC_DIR; } if (mfs->mfs_args != NULL && alp == NULL) { free(mfs->mfs_args); mfs->mfs_args = NULL; } if (mfs->mfs_nconf != NULL) { freenetconfigent(mfs->mfs_nconf); mfs->mfs_nconf = NULL; } } return (last_error); } /* * get_pathconf(cl, path, fsname, pcnf, cretries) * ugliness that requires that ppathcnf and pathcnf stay consistent * cretries is a copy of retries used to determine when to syslog * on retry situations. */ static int get_pathconf(CLIENT *cl, char *path, char *fsname, struct pathcnf **pcnf, int cretries) { struct ppathcnf *p = NULL; enum clnt_stat rpc_stat; struct timeval timeout; p = (struct ppathcnf *)malloc(sizeof (struct ppathcnf)); if (p == NULL) { syslog(LOG_ERR, "get_pathconf: Out of memory"); return (RET_ERR); } memset((caddr_t)p, 0, sizeof (struct ppathcnf)); timeout.tv_sec = 10; timeout.tv_usec = 0; rpc_stat = clnt_call(cl, MOUNTPROC_PATHCONF, xdr_dirpath, (caddr_t)&path, xdr_ppathcnf, (caddr_t)p, timeout); if (rpc_stat != RPC_SUCCESS) { if (cretries-- <= 0) { syslog(LOG_ERR, "get_pathconf: %s: server not responding: %s", fsname, clnt_sperror(cl, "")); } free(p); return (RET_RETRY); } if (_PC_ISSET(_PC_ERROR, p->pc_mask)) { syslog(LOG_ERR, "get_pathconf: no info for %s", fsname); free(p); return (RET_ERR); } *pcnf = (struct pathcnf *)p; return (RET_OK); } void netbuf_free(struct netbuf *nb) { if (nb == NULL) return; free(nb->buf); free(nb); } #define SMALL_HOSTNAME 20 #define SMALL_PROTONAME 10 #define SMALL_PROTOFMLYNAME 10 struct portmap_cache { int cache_prog; int cache_vers; time_t cache_time; char cache_small_hosts[SMALL_HOSTNAME + 1]; char *cache_hostname; char *cache_proto; char *cache_protofmly; char cache_small_protofmly[SMALL_PROTOFMLYNAME + 1]; char cache_small_proto[SMALL_PROTONAME + 1]; struct netbuf cache_srv_addr; struct portmap_cache *cache_prev, *cache_next; }; rwlock_t portmap_cache_lock; static int portmap_cache_valid_time = 30; struct portmap_cache *portmap_cache_head, *portmap_cache_tail; #ifdef MALLOC_DEBUG void portmap_cache_flush(void) { struct portmap_cache *next = NULL, *cp; (void) rw_wrlock(&portmap_cache_lock); for (cp = portmap_cache_head; cp; cp = cp->cache_next) { if (cp->cache_hostname != NULL && cp->cache_hostname != cp->cache_small_hosts) free(cp->cache_hostname); if (cp->cache_proto != NULL && cp->cache_proto != cp->cache_small_proto) free(cp->cache_proto); if (cp->cache_srv_addr.buf != NULL) free(cp->cache_srv_addr.buf); next = cp->cache_next; free(cp); } portmap_cache_head = NULL; portmap_cache_tail = NULL; (void) rw_unlock(&portmap_cache_lock); } #endif /* * Returns 1 if the entry is found in the cache, 0 otherwise. */ static int portmap_cache_lookup(char *hostname, rpcprog_t prog, rpcvers_t vers, struct netconfig *nconf, struct netbuf *addrp) { struct portmap_cache *cachep, *prev, *next = NULL, *cp; int retval = 0; timenow = time(NULL); (void) rw_rdlock(&portmap_cache_lock); /* * Increment the portmap cache counters for # accesses and lookups * Use a smaller factor (100 vs 1000 for the host cache) since * initial analysis shows this cache is looked up 10% that of the * host cache. */ #ifdef CACHE_DEBUG portmap_cache_accesses++; portmap_cache_lookups++; if ((portmap_cache_lookups%100) == 0) trace_portmap_cache(); #endif /* CACHE_DEBUG */ for (cachep = portmap_cache_head; cachep; cachep = cachep->cache_next) { if (timenow > cachep->cache_time) { /* * We stumbled across an entry in the cache which * has timed out. Free up all the entries that * were added before it, which will positionally * be after this entry. And adjust neighboring * pointers. * When we drop the lock and re-acquire it, we * need to start from the beginning. */ (void) rw_unlock(&portmap_cache_lock); (void) rw_wrlock(&portmap_cache_lock); for (cp = portmap_cache_head; cp && (cp->cache_time >= timenow); cp = cp->cache_next) ; if (cp == NULL) goto done; /* * Adjust the link of the predecessor. * Make the tail point to the new last entry. */ prev = cp->cache_prev; if (prev == NULL) { portmap_cache_head = NULL; portmap_cache_tail = NULL; } else { prev->cache_next = NULL; portmap_cache_tail = prev; } for (; cp; cp = next) { if (cp->cache_hostname != NULL && cp->cache_hostname != cp->cache_small_hosts) free(cp->cache_hostname); if (cp->cache_proto != NULL && cp->cache_proto != cp->cache_small_proto) free(cp->cache_proto); if (cp->cache_srv_addr.buf != NULL) free(cp->cache_srv_addr.buf); next = cp->cache_next; free(cp); } goto done; } if (cachep->cache_hostname == NULL || prog != cachep->cache_prog || vers != cachep->cache_vers || strcmp(nconf->nc_proto, cachep->cache_proto) != 0 || strcmp(nconf->nc_protofmly, cachep->cache_protofmly) != 0 || strcmp(hostname, cachep->cache_hostname) != 0) continue; /* * Cache Hit. */ #ifdef CACHE_DEBUG portmap_cache_hits++; /* up portmap cache hit counter */ #endif /* CACHE_DEBUG */ addrp->len = cachep->cache_srv_addr.len; memcpy(addrp->buf, cachep->cache_srv_addr.buf, addrp->len); retval = 1; break; } done: (void) rw_unlock(&portmap_cache_lock); return (retval); } static void portmap_cache_enter(char *hostname, rpcprog_t prog, rpcvers_t vers, struct netconfig *nconf, struct netbuf *addrp) { struct portmap_cache *cachep; int protofmlylen; int protolen, hostnamelen; timenow = time(NULL); cachep = malloc(sizeof (struct portmap_cache)); if (cachep == NULL) return; memset((char *)cachep, 0, sizeof (*cachep)); hostnamelen = strlen(hostname); if (hostnamelen <= SMALL_HOSTNAME) cachep->cache_hostname = cachep->cache_small_hosts; else { cachep->cache_hostname = malloc(hostnamelen + 1); if (cachep->cache_hostname == NULL) goto nomem; } strcpy(cachep->cache_hostname, hostname); protolen = strlen(nconf->nc_proto); if (protolen <= SMALL_PROTONAME) cachep->cache_proto = cachep->cache_small_proto; else { cachep->cache_proto = malloc(protolen + 1); if (cachep->cache_proto == NULL) goto nomem; } protofmlylen = strlen(nconf->nc_protofmly); if (protofmlylen <= SMALL_PROTOFMLYNAME) cachep->cache_protofmly = cachep->cache_small_protofmly; else { cachep->cache_protofmly = malloc(protofmlylen + 1); if (cachep->cache_protofmly == NULL) goto nomem; } strcpy(cachep->cache_proto, nconf->nc_proto); cachep->cache_prog = prog; cachep->cache_vers = vers; cachep->cache_time = timenow + portmap_cache_valid_time; cachep->cache_srv_addr.len = addrp->len; cachep->cache_srv_addr.buf = malloc(addrp->len); if (cachep->cache_srv_addr.buf == NULL) goto nomem; memcpy(cachep->cache_srv_addr.buf, addrp->buf, addrp->maxlen); cachep->cache_prev = NULL; (void) rw_wrlock(&portmap_cache_lock); /* * There's a window in which we could have multiple threads making * the same cache entry. This can be avoided by walking the cache * once again here to check and see if there are duplicate entries * (after grabbing the write lock). This isn't fatal and I'm not * going to bother with this. */ #ifdef CACHE_DEBUG portmap_cache_accesses++; /* up portmap cache access counter */ #endif /* CACHE_DEBUG */ cachep->cache_next = portmap_cache_head; if (portmap_cache_head != NULL) portmap_cache_head->cache_prev = cachep; portmap_cache_head = cachep; (void) rw_unlock(&portmap_cache_lock); return; nomem: syslog(LOG_ERR, "portmap_cache_enter: Memory allocation failed"); if (cachep->cache_srv_addr.buf) free(cachep->cache_srv_addr.buf); if (cachep->cache_proto && protolen > SMALL_PROTONAME) free(cachep->cache_proto); if (cachep->cache_hostname && hostnamelen > SMALL_HOSTNAME) free(cachep->cache_hostname); if (cachep->cache_protofmly && protofmlylen > SMALL_PROTOFMLYNAME) free(cachep->cache_protofmly); if (cachep) free(cachep); cachep = NULL; } static int get_cached_srv_addr(char *hostname, rpcprog_t prog, rpcvers_t vers, struct netconfig *nconf, struct netbuf *addrp) { if (portmap_cache_lookup(hostname, prog, vers, nconf, addrp)) return (1); if (rpcb_getaddr(prog, vers, nconf, addrp, hostname) == 0) return (0); portmap_cache_enter(hostname, prog, vers, nconf, addrp); return (1); } /* * Get a network address on "hostname" for program "prog" * with version "vers". If the port number is specified (non zero) * then try for a TCP/UDP transport and set the port number of the * resulting IP address. * * If the address of a netconfig pointer was passed and * if it's not null, use it as the netconfig otherwise * assign the address of the netconfig that was used to * establish contact with the service. * * tinfo argument is for matching the get_addr() defined in * ../nfs/mount/mount.c */ static struct netbuf * get_addr(char *hostname, rpcprog_t prog, rpcvers_t vers, struct netconfig **nconfp, char *proto, ushort_t port, struct t_info *tinfo) { enum clnt_stat cstat; return (get_server_netinfo(SERVER_ADDR, hostname, prog, vers, NULL, nconfp, proto, port, tinfo, NULL, FALSE, NULL, &cstat)); } static struct netbuf * get_pubfh(char *hostname, rpcvers_t vers, mfs_snego_t *mfssnego, struct netconfig **nconfp, char *proto, ushort_t port, struct t_info *tinfo, caddr_t *fhp, bool_t get_pubfh, char *fspath) { enum clnt_stat cstat; return (get_server_netinfo(SERVER_FH, hostname, NFS_PROGRAM, vers, mfssnego, nconfp, proto, port, tinfo, fhp, get_pubfh, fspath, &cstat)); } static enum clnt_stat get_ping(char *hostname, rpcprog_t prog, rpcvers_t vers, struct netconfig **nconfp, ushort_t port, bool_t direct_to_server) { enum clnt_stat cstat; (void) get_server_netinfo(SERVER_PING, hostname, prog, vers, NULL, nconfp, NULL, port, NULL, NULL, direct_to_server, NULL, &cstat); return (cstat); } void * get_server_netinfo( enum type_of_stuff type_of_stuff, char *hostname, rpcprog_t prog, rpcvers_t vers, mfs_snego_t *mfssnego, struct netconfig **nconfp, char *proto, ushort_t port, /* may be zero */ struct t_info *tinfo, caddr_t *fhp, bool_t direct_to_server, char *fspath, enum clnt_stat *cstatp) { struct netbuf *nb = NULL; struct netconfig *nconf = NULL; NCONF_HANDLE *nc = NULL; int error = 0; int fd = 0; struct t_bind *tbind = NULL; int nthtry = FIRST_TRY; if (nconfp && *nconfp) { return (get_netconfig_info(type_of_stuff, hostname, prog, vers, nconf, port, tinfo, tbind, fhp, direct_to_server, fspath, cstatp, mfssnego)); } /* * No nconf passed in. * * Try to get a nconf from /etc/netconfig. * First choice is COTS, second is CLTS unless proto * is specified. When we retry, we reset the * netconfig list, so that we search the whole list * for the next choice. */ if ((nc = setnetpath()) == NULL) goto done; /* * If proto is specified, then only search for the match, * otherwise try COTS first, if failed, then try CLTS. */ if (proto) { while ((nconf = getnetpath(nc)) != NULL) { if (strcmp(nconf->nc_proto, proto)) continue; /* * If the port number is specified then TCP/UDP * is needed. Otherwise any cots/clts will do. */ if (port) { if ((strcmp(nconf->nc_protofmly, NC_INET) && strcmp(nconf->nc_protofmly, NC_INET6)) || (strcmp(nconf->nc_proto, NC_TCP) && strcmp(nconf->nc_proto, NC_UDP))) continue; } nb = get_netconfig_info(type_of_stuff, hostname, prog, vers, nconf, port, tinfo, tbind, fhp, direct_to_server, fspath, cstatp, mfssnego); if (*cstatp == RPC_SUCCESS) break; assert(nb == NULL); } if (nconf == NULL) goto done; } else { retry: while ((nconf = getnetpath(nc)) != NULL) { if (nconf->nc_flag & NC_VISIBLE) { if (nthtry == FIRST_TRY) { if ((nconf->nc_semantics == NC_TPI_COTS_ORD) || (nconf->nc_semantics == NC_TPI_COTS)) { if (port == 0) break; if ((strcmp(nconf->nc_protofmly, NC_INET) == 0 || strcmp(nconf->nc_protofmly, NC_INET6) == 0) && (strcmp(nconf->nc_proto, NC_TCP) == 0)) break; } } if (nthtry == SECOND_TRY) { if (nconf->nc_semantics == NC_TPI_CLTS) { if (port == 0) break; if ((strcmp(nconf->nc_protofmly, NC_INET) == 0 || strcmp(nconf->nc_protofmly, NC_INET6) == 0) && (strcmp(nconf->nc_proto, NC_UDP) == 0)) break; } } } } if (nconf == NULL) { if (++nthtry <= MNT_PREF_LISTLEN) { endnetpath(nc); if ((nc = setnetpath()) == NULL) goto done; goto retry; } else goto done; } else { nb = get_netconfig_info(type_of_stuff, hostname, prog, vers, nconf, port, tinfo, tbind, fhp, direct_to_server, fspath, cstatp, mfssnego); if (*cstatp != RPC_SUCCESS) /* * Continue the same search path in the * netconfig db until no more matched nconf * (nconf == NULL). */ goto retry; } } /* * Got nconf and nb. Now dup the netconfig structure (nconf) * and return it thru nconfp. */ if (nconf != NULL) { if ((*nconfp = getnetconfigent(nconf->nc_netid)) == NULL) { syslog(LOG_ERR, "no memory\n"); free(nb); nb = NULL; } } else { *nconfp = NULL; } done: if (nc) endnetpath(nc); return (nb); } void * get_server_fh(char *hostname, rpcprog_t prog, rpcvers_t vers, mfs_snego_t *mfssnego, struct netconfig *nconf, ushort_t port, struct t_info *tinfo, struct t_bind *tbind, caddr_t *fhp, bool_t direct_to_server, char *fspath, enum clnt_stat *cstat) { AUTH *ah = NULL; AUTH *new_ah = NULL; struct snego_t snego; enum clnt_stat cs = RPC_TIMEDOUT; struct timeval tv; bool_t file_handle = 1; enum snego_stat sec; CLIENT *cl = NULL; int fd = -1; struct netbuf *nb = NULL; if (direct_to_server != TRUE) return (NULL); if (prog == NFS_PROGRAM && vers == NFS_V4) if (strncasecmp(nconf->nc_proto, NC_UDP, strlen(NC_UDP)) == 0) goto done; if ((fd = t_open(nconf->nc_device, O_RDWR, tinfo)) < 0) goto done; /* LINTED pointer alignment */ if ((tbind = (struct t_bind *)t_alloc(fd, T_BIND, T_ADDR)) == NULL) goto done; if (setup_nb_parms(nconf, tbind, tinfo, hostname, fd, direct_to_server, port, prog, vers, file_handle) < 0) { goto done; } cl = clnt_tli_create(fd, nconf, &tbind->addr, prog, vers, 0, 0); if (cl == NULL) goto done; ah = authsys_create_default(); if (ah != NULL) { #ifdef MALLOC_DEBUG drop_alloc("AUTH_HANDLE", cl->cl_auth, __FILE__, __LINE__); #endif AUTH_DESTROY(cl->cl_auth); cl->cl_auth = ah; #ifdef MALLOC_DEBUG add_alloc("AUTH_HANDLE", cl->cl_auth, 0, __FILE__, __LINE__); #endif } if (!mfssnego->snego_done && vers != NFS_V4) { /* * negotiate sec flavor. */ snego.cnt = 0; if ((sec = nfs_sec_nego(vers, cl, fspath, &snego)) == SNEGO_SUCCESS) { int jj; /* * check if server supports the one * specified in the sec= option. */ if (mfssnego->sec_opt) { for (jj = 0; jj < snego.cnt; jj++) { if (snego.array[jj] == mfssnego->nfs_sec.sc_nfsnum) { mfssnego->snego_done = TRUE; break; } } } /* * find a common sec flavor */ if (!mfssnego->snego_done) { for (jj = 0; jj < snego.cnt; jj++) { if (!nfs_getseconfig_bynumber( snego.array[jj], &mfssnego->nfs_sec)) { mfssnego->snego_done = TRUE; break; } } } if (!mfssnego->snego_done) goto done; /* * Now that the flavor has been * negotiated, get the fh. * * First, create an auth handle using the negotiated * sec flavor in the next lookup to * fetch the filehandle. */ new_ah = nfs_create_ah(cl, hostname, &mfssnego->nfs_sec); if (new_ah == NULL) goto done; #ifdef MALLOC_DEBUG drop_alloc("AUTH_HANDLE", cl->cl_auth, __FILE__, __LINE__); #endif AUTH_DESTROY(cl->cl_auth); cl->cl_auth = new_ah; #ifdef MALLOC_DEBUG add_alloc("AUTH_HANDLE", cl->cl_auth, 0, __FILE__, __LINE__); #endif } else if (sec == SNEGO_ARRAY_TOO_SMALL || sec == SNEGO_FAILURE) { goto done; } } switch (vers) { case NFS_VERSION: { wnl_diropargs arg; wnl_diropres res; memset((char *)&arg.dir, 0, sizeof (wnl_fh)); memset((char *)&res, 0, sizeof (wnl_diropres)); arg.name = fspath; if (wnlproc_lookup_2(&arg, &res, cl) != RPC_SUCCESS || res.status != WNL_OK) goto done; *fhp = malloc(sizeof (wnl_fh)); if (*fhp == NULL) { syslog(LOG_ERR, "no memory\n"); goto done; } memcpy((char *)*fhp, (char *)&res.wnl_diropres_u.wnl_diropres.file, sizeof (wnl_fh)); cs = RPC_SUCCESS; } break; case NFS_V3: { WNL_LOOKUP3args arg; WNL_LOOKUP3res res; nfs_fh3 *fh3p; memset((char *)&arg.what.dir, 0, sizeof (wnl_fh3)); memset((char *)&res, 0, sizeof (WNL_LOOKUP3res)); arg.what.name = fspath; if (wnlproc3_lookup_3(&arg, &res, cl) != RPC_SUCCESS || res.status != WNL3_OK) goto done; fh3p = (nfs_fh3 *)malloc(sizeof (*fh3p)); if (fh3p == NULL) { syslog(LOG_ERR, "no memory\n"); goto done; } fh3p->fh3_length = res.WNL_LOOKUP3res_u.res_ok.object.data.data_len; memcpy(fh3p->fh3_u.data, res.WNL_LOOKUP3res_u.res_ok.object.data.data_val, fh3p->fh3_length); *fhp = (caddr_t)fh3p; cs = RPC_SUCCESS; } break; case NFS_V4: tv.tv_sec = 10; tv.tv_usec = 0; cs = clnt_call(cl, NULLPROC, xdr_void, 0, xdr_void, 0, tv); if (cs != RPC_SUCCESS) goto done; *fhp = strdup(fspath); if (fhp == NULL) { cs = RPC_SYSTEMERROR; goto done; } break; } nb = (struct netbuf *)malloc(sizeof (struct netbuf)); if (nb == NULL) { syslog(LOG_ERR, "no memory\n"); cs = RPC_SYSTEMERROR; goto done; } nb->buf = (char *)malloc(tbind->addr.maxlen); if (nb->buf == NULL) { syslog(LOG_ERR, "no memory\n"); free(nb); nb = NULL; cs = RPC_SYSTEMERROR; goto done; } (void) memcpy(nb->buf, tbind->addr.buf, tbind->addr.len); nb->len = tbind->addr.len; nb->maxlen = tbind->addr.maxlen; done: if (cstat != NULL) *cstat = cs; destroy_auth_client_handle(cl); cleanup_tli_parms(tbind, fd); return (nb); } /* * Sends a null call to the remote host's (NFS program, versp). versp * may be "NULL" in which case the default maximum version is used. * Upon return, versp contains the maximum version supported iff versp!= NULL. */ enum clnt_stat pingnfs( char *hostpart, int attempts, rpcvers_t *versp, rpcvers_t versmin, ushort_t port, /* may be zero */ bool_t usepub, char *path, char *proto) { CLIENT *cl = NULL; struct timeval rpc_to_new = {15, 0}; static struct timeval rpc_rtrans_new = {-1, -1}; enum clnt_stat clnt_stat; int i, j; rpcvers_t versmax; /* maximum version to try against server */ rpcvers_t outvers; /* version supported by host on last call */ rpcvers_t vers_to_try; /* to try different versions against host */ char *hostname; struct netconfig *nconf; hostname = strdup(hostpart); if (hostname == NULL) { return (RPC_SYSTEMERROR); } unbracket(&hostname); if (path != NULL && strcmp(hostname, "nfs") == 0 && strncmp(path, "//", 2) == 0) { char *sport; hostname = strdup(path+2); if (hostname == NULL) return (RPC_SYSTEMERROR); path = strchr(hostname, '/'); /* * This cannot happen. If it does, give up * on the ping as this is obviously a corrupt * entry. */ if (path == NULL) { free(hostname); return (RPC_SUCCESS); } /* * Probable end point of host string. */ *path = '\0'; sport = strchr(hostname, ':'); if (sport != NULL && sport < path) { /* * Actual end point of host string. */ *sport = '\0'; port = htons((ushort_t)atoi(sport+1)); } usepub = TRUE; } /* Pick up the default versions and then set them appropriately */ if (versp) { versmax = *versp; /* use versmin passed in */ } else { read_default_nfs(); set_versrange(0, &versmax, &versmin); } if (proto && strncasecmp(proto, NC_UDP, strlen(NC_UDP)) == 0 && versmax == NFS_V4) { if (versmin == NFS_V4) { if (versp) { *versp = versmax - 1; return (RPC_SUCCESS); } return (RPC_PROGUNAVAIL); } else { versmax--; } } if (versp) *versp = versmax; switch (cache_check(hostname, versp, proto)) { case GOODHOST: if (hostname != hostpart) free(hostname); return (RPC_SUCCESS); case DEADHOST: if (hostname != hostpart) free(hostname); return (RPC_TIMEDOUT); case NOHOST: default: break; } /* * XXX The retransmission time rpcbrmttime is a global defined * in the rpc library (rpcb_clnt.c). We use (and like) the default * value of 15 sec in the rpc library. The code below is to protect * us in case it changes. This need not be done under a lock since * any # of threads entering this function will get the same * retransmission value. */ if (rpc_rtrans_new.tv_sec == -1 && rpc_rtrans_new.tv_usec == -1) { __rpc_control(CLCR_GET_RPCB_RMTTIME, (char *)&rpc_rtrans_new); if (rpc_rtrans_new.tv_sec != 15 && rpc_rtrans_new.tv_sec != 0) if (trace > 1) trace_prt(1, "RPC library rttimer changed\n"); } /* * XXX Manipulate the total timeout to get the number of * desired retransmissions. This code is heavily dependant on * the RPC backoff mechanism in clnt_dg_call (clnt_dg.c). */ for (i = 0, j = rpc_rtrans_new.tv_sec; i < attempts-1; i++) { if (j < RPC_MAX_BACKOFF) j *= 2; else j = RPC_MAX_BACKOFF; rpc_to_new.tv_sec += j; } vers_to_try = versmax; /* * check the host's version within the timeout */ if (trace > 1) trace_prt(1, " ping: %s timeout=%ld request vers=%d min=%d\n", hostname, rpc_to_new.tv_sec, versmax, versmin); if (usepub == FALSE) { do { /* * If NFSv4, then we do the same thing as is used * for public filehandles so that we avoid rpcbind */ if (vers_to_try == NFS_V4) { if (trace > 4) { trace_prt(1, " pingnfs: Trying ping via " "\"circuit_v\"\n"); } cl = clnt_create_service_timed(hostname, "nfs", NFS_PROGRAM, vers_to_try, port, "circuit_v", &rpc_to_new); if (cl != NULL) { outvers = vers_to_try; break; } if (trace > 4) { trace_prt(1, " pingnfs: Can't ping via " "\"circuit_v\" %s: RPC error=%d\n", hostname, rpc_createerr.cf_stat); } } else { cl = clnt_create_vers_timed(hostname, NFS_PROGRAM, &outvers, versmin, vers_to_try, "datagram_v", &rpc_to_new); if (cl != NULL) break; if (trace > 4) { trace_prt(1, " pingnfs: Can't ping via " "\"datagram_v\"%s: RPC error=%d\n", hostname, rpc_createerr.cf_stat); } if (rpc_createerr.cf_stat == RPC_UNKNOWNHOST || rpc_createerr.cf_stat == RPC_TIMEDOUT) break; if (rpc_createerr.cf_stat == RPC_PROGNOTREGISTERED) { if (trace > 4) { trace_prt(1, " pingnfs: Trying ping " "via \"circuit_v\"\n"); } cl = clnt_create_vers_timed(hostname, NFS_PROGRAM, &outvers, versmin, vers_to_try, "circuit_v", &rpc_to_new); if (cl != NULL) break; if (trace > 4) { trace_prt(1, " pingnfs: Can't ping " "via \"circuit_v\" %s: " "RPC error=%d\n", hostname, rpc_createerr.cf_stat); } } } /* * backoff and return lower version to retry the ping. * XXX we should be more careful and handle * RPC_PROGVERSMISMATCH here, because that error is handled * in clnt_create_vers(). It's not done to stay in sync * with the nfs mount command. */ vers_to_try--; if (vers_to_try < versmin) break; if (versp != NULL) { /* recheck the cache */ *versp = vers_to_try; if (trace > 4) { trace_prt(1, " pingnfs: check cache: vers=%d\n", *versp); } switch (cache_check(hostname, versp, proto)) { case GOODHOST: if (hostname != hostpart) free(hostname); return (RPC_SUCCESS); case DEADHOST: if (hostname != hostpart) free(hostname); return (RPC_TIMEDOUT); case NOHOST: default: break; } } if (trace > 4) { trace_prt(1, " pingnfs: Try version=%d\n", vers_to_try); } } while (cl == NULL); if (cl == NULL) { if (verbose) syslog(LOG_ERR, "pingnfs: %s%s", hostname, clnt_spcreateerror("")); clnt_stat = rpc_createerr.cf_stat; } else { clnt_destroy(cl); clnt_stat = RPC_SUCCESS; } } else { for (vers_to_try = versmax; vers_to_try >= versmin; vers_to_try--) { nconf = NULL; if (trace > 4) { trace_prt(1, " pingnfs: Try version=%d " "using get_ping()\n", vers_to_try); } clnt_stat = get_ping(hostname, NFS_PROGRAM, vers_to_try, &nconf, port, TRUE); if (nconf != NULL) freenetconfigent(nconf); if (clnt_stat == RPC_SUCCESS) { outvers = vers_to_try; break; } } } if (trace > 1) clnt_stat == RPC_SUCCESS ? trace_prt(1, " pingnfs OK: nfs version=%d\n", outvers): trace_prt(1, " pingnfs FAIL: can't get nfs version\n"); if (clnt_stat == RPC_SUCCESS) { cache_enter(hostname, versmax, outvers, proto, GOODHOST); if (versp != NULL) *versp = outvers; } else cache_enter(hostname, versmax, versmax, proto, DEADHOST); if (hostpart != hostname) free(hostname); return (clnt_stat); } #define MNTTYPE_LOFS "lofs" int loopbackmount(char *fsname, char *dir, char *mntopts, int overlay) { struct mnttab mnt; int flags = 0; char fstype[] = MNTTYPE_LOFS; int dirlen; struct stat st; char optbuf[MAX_MNTOPT_STR]; dirlen = strlen(dir); if (dir[dirlen-1] == ' ') dirlen--; if (dirlen == strlen(fsname) && strncmp(fsname, dir, dirlen) == 0) { syslog(LOG_ERR, "Mount of %s on %s would result in deadlock, aborted\n", fsname, dir); return (RET_ERR); } mnt.mnt_mntopts = mntopts; if (hasmntopt(&mnt, MNTOPT_RO) != NULL) flags |= MS_RDONLY; (void) strlcpy(optbuf, mntopts, sizeof (optbuf)); if (overlay) flags |= MS_OVERLAY; if (trace > 1) { trace_prt(1, " loopbackmount: fsname=%s, dir=%s, flags=%d\n", fsname, dir, flags); } if (is_system_labeled()) { if (create_homedir((const char *)fsname, (const char *)dir) == 0) { return (NFSERR_NOENT); } } if (mount(fsname, dir, flags | MS_DATA | MS_OPTIONSTR, fstype, NULL, 0, optbuf, sizeof (optbuf)) < 0) { syslog(LOG_ERR, "Mount of %s on %s: %m", fsname, dir); return (RET_ERR); } if (stat(dir, &st) == 0) { if (trace > 1) { trace_prt(1, " loopbackmount of %s on %s dev=%x rdev=%x OK\n", fsname, dir, st.st_dev, st.st_rdev); } } else { if (trace > 1) { trace_prt(1, " loopbackmount of %s on %s OK\n", fsname, dir); trace_prt(1, " stat of %s failed\n", dir); } } return (0); } /* * Look for the value of a numeric option of the form foo=x. If found, set * *valp to the value and return non-zero. If not found or the option is * malformed, return zero. */ int nopt(struct mnttab *mnt, char *opt, int *valp) { char *equal; char *str; /* * We should never get a null pointer, but if we do, it's better to * ignore the option than to dump core. */ if (valp == NULL) { syslog(LOG_DEBUG, "null pointer for %s option", opt); return (0); } if (str = hasmntopt(mnt, opt)) { if (equal = strchr(str, '=')) { *valp = atoi(&equal[1]); return (1); } else { syslog(LOG_ERR, "Bad numeric option '%s'", str); } } return (0); } int nfsunmount(struct mnttab *mnt) { struct timeval timeout; CLIENT *cl; enum clnt_stat rpc_stat; char *host, *path; struct replica *list; int i, count = 0; int isv4mount = is_v4_mount(mnt->mnt_mountp); if (trace > 1) trace_prt(1, " nfsunmount: umount %s\n", mnt->mnt_mountp); if (umount(mnt->mnt_mountp) < 0) { if (trace > 1) { trace_prt(1, " nfsunmount: umount %s FAILED\n", mnt->mnt_mountp); } if (errno) return (errno); } /* * If this is a NFSv4 mount, the mount protocol was not used * so we just return. */ if (isv4mount) { if (trace > 1) { trace_prt(1, " nfsunmount: umount %s OK\n", mnt->mnt_mountp); } return (0); } /* * If mounted with -o public, then no need to contact server * because mount protocol was not used. */ if (hasmntopt(mnt, MNTOPT_PUBLIC) != NULL) { return (0); } /* * The rest of this code is advisory to the server. * If it fails return success anyway. */ list = parse_replica(mnt->mnt_special, &count); if (!list) { if (count >= 0) syslog(LOG_ERR, "Memory allocation failed: %m"); return (ENOMEM); } for (i = 0; i < count; i++) { host = list[i].host; path = list[i].path; /* * Skip file systems mounted using WebNFS, because mount * protocol was not used. */ if (strcmp(host, "nfs") == 0 && strncmp(path, "//", 2) == 0) continue; cl = clnt_create(host, MOUNTPROG, MOUNTVERS, "datagram_v"); if (cl == NULL) break; #ifdef MALLOC_DEBUG add_alloc("CLNT_HANDLE", cl, 0, __FILE__, __LINE__); add_alloc("AUTH_HANDLE", cl->cl_auth, 0, __FILE__, __LINE__); #endif if (__clnt_bindresvport(cl) < 0) { if (verbose) { syslog(LOG_ERR, "umount %s:%s: %s", host, path, "Couldn't bind to reserved port"); } destroy_auth_client_handle(cl); continue; } #ifdef MALLOC_DEBUG drop_alloc("AUTH_HANDLE", cl->cl_auth, __FILE__, __LINE__); #endif AUTH_DESTROY(cl->cl_auth); if ((cl->cl_auth = authsys_create_default()) == NULL) { if (verbose) { syslog(LOG_ERR, "umount %s:%s: %s", host, path, "Failed creating default auth handle"); } destroy_auth_client_handle(cl); continue; } #ifdef MALLOC_DEBUG add_alloc("AUTH_HANDLE", cl->cl_auth, 0, __FILE__, __LINE__); #endif timeout.tv_usec = 0; timeout.tv_sec = 5; rpc_stat = clnt_call(cl, MOUNTPROC_UMNT, xdr_dirpath, (caddr_t)&path, xdr_void, (char *)NULL, timeout); if (verbose && rpc_stat != RPC_SUCCESS) { syslog(LOG_ERR, "%s: %s", host, clnt_sperror(cl, "unmount")); } destroy_auth_client_handle(cl); } free_replica(list, count); if (trace > 1) trace_prt(1, " nfsunmount: umount %s OK\n", mnt->mnt_mountp); return (0); } /* * Put a new entry in the cache chain by prepending it to the front. * If there isn't enough memory then just give up. */ static void cache_enter(char *host, rpcvers_t reqvers, rpcvers_t outvers, char *proto, int state) { struct cache_entry *entry; int cache_time = 30; /* sec */ timenow = time(NULL); entry = (struct cache_entry *)malloc(sizeof (struct cache_entry)); if (entry == NULL) return; (void) memset((caddr_t)entry, 0, sizeof (struct cache_entry)); entry->cache_host = strdup(host); if (entry->cache_host == NULL) { cache_free(entry); return; } entry->cache_reqvers = reqvers; entry->cache_outvers = outvers; entry->cache_proto = (proto == NULL ? NULL : strdup(proto)); entry->cache_state = state; entry->cache_time = timenow + cache_time; (void) rw_wrlock(&cache_lock); #ifdef CACHE_DEBUG host_cache_accesses++; /* up host cache access counter */ #endif /* CACHE DEBUG */ entry->cache_next = cache_head; cache_head = entry; (void) rw_unlock(&cache_lock); } static int cache_check(char *host, rpcvers_t *versp, char *proto) { int state = NOHOST; struct cache_entry *ce, *prev; timenow = time(NULL); (void) rw_rdlock(&cache_lock); #ifdef CACHE_DEBUG /* Increment the lookup and access counters for the host cache */ host_cache_accesses++; host_cache_lookups++; if ((host_cache_lookups%1000) == 0) trace_host_cache(); #endif /* CACHE DEBUG */ for (ce = cache_head; ce; ce = ce->cache_next) { if (timenow > ce->cache_time) { (void) rw_unlock(&cache_lock); (void) rw_wrlock(&cache_lock); for (prev = NULL, ce = cache_head; ce; prev = ce, ce = ce->cache_next) { if (timenow > ce->cache_time) { cache_free(ce); if (prev) prev->cache_next = NULL; else cache_head = NULL; break; } } (void) rw_unlock(&cache_lock); return (state); } if (strcmp(host, ce->cache_host) != 0) continue; if ((proto == NULL && ce->cache_proto != NULL) || (proto != NULL && ce->cache_proto == NULL)) continue; if (proto != NULL && strcmp(proto, ce->cache_proto) != 0) continue; if (versp == NULL || (versp != NULL && *versp == ce->cache_reqvers) || (versp != NULL && *versp == ce->cache_outvers)) { if (versp != NULL) *versp = ce->cache_outvers; state = ce->cache_state; /* increment the host cache hit counters */ #ifdef CACHE_DEBUG if (state == GOODHOST) goodhost_cache_hits++; if (state == DEADHOST) deadhost_cache_hits++; #endif /* CACHE_DEBUG */ (void) rw_unlock(&cache_lock); return (state); } } (void) rw_unlock(&cache_lock); return (state); } /* * Free a cache entry and all entries * further down the chain since they * will also be expired. */ static void cache_free(struct cache_entry *entry) { struct cache_entry *ce, *next = NULL; for (ce = entry; ce; ce = next) { if (ce->cache_host) free(ce->cache_host); if (ce->cache_proto) free(ce->cache_proto); next = ce->cache_next; free(ce); } } #ifdef MALLOC_DEBUG void cache_flush(void) { (void) rw_wrlock(&cache_lock); cache_free(cache_head); cache_head = NULL; (void) rw_unlock(&cache_lock); } void flush_caches(void) { mutex_lock(&cleanup_lock); cond_signal(&cleanup_start_cv); (void) cond_wait(&cleanup_done_cv, &cleanup_lock); mutex_unlock(&cleanup_lock); cache_flush(); portmap_cache_flush(); } #endif /* * Returns 1, if port option is NFS_PORT or * nfsd is running on the port given * Returns 0, if both port is not NFS_PORT and nfsd is not * running on the port. */ static int is_nfs_port(char *opts) { struct mnttab m; uint_t nfs_port = 0; struct servent sv; char buf[256]; int got_port; m.mnt_mntopts = opts; /* * Get port specified in options list, if any. */ got_port = nopt(&m, MNTOPT_PORT, (int *)&nfs_port); /* * if no port specified or it is same as NFS_PORT return nfs * To use any other daemon the port number should be different */ if (!got_port || nfs_port == NFS_PORT) return (1); /* * If daemon is nfsd, return nfs */ if (getservbyport_r(nfs_port, NULL, &sv, buf, 256) == &sv && strcmp(sv.s_name, "nfsd") == 0) return (1); /* * daemon is not nfs */ return (0); } /* * destroy_auth_client_handle(cl) * destroys the created client handle */ void destroy_auth_client_handle(CLIENT *cl) { if (cl) { if (cl->cl_auth) { #ifdef MALLOC_DEBUG drop_alloc("AUTH_HANDLE", cl->cl_auth, __FILE__, __LINE__); #endif AUTH_DESTROY(cl->cl_auth); cl->cl_auth = NULL; } #ifdef MALLOC_DEBUG drop_alloc("CLNT_HANDLE", cl, __FILE__, __LINE__); #endif clnt_destroy(cl); } } /* * Attempt to figure out which version of NFS to use in pingnfs(). If * the version number was specified (i.e., non-zero), then use it. * Otherwise, default to the compiled-in default or the default as set * by the /etc/default/nfs configuration (as read by read_default(). */ int set_versrange(rpcvers_t nfsvers, rpcvers_t *vers, rpcvers_t *versmin) { switch (nfsvers) { case 0: *vers = vers_max_default; *versmin = vers_min_default; break; case NFS_V4: *vers = NFS_V4; *versmin = NFS_V4; break; case NFS_V3: *vers = NFS_V3; *versmin = NFS_V3; break; case NFS_VERSION: *vers = NFS_VERSION; /* version 2 */ *versmin = NFS_VERSMIN; /* version 2 */ break; default: return (-1); } return (0); } #ifdef CACHE_DEBUG /* * trace_portmap_cache() * traces the portmap cache values at desired points */ static void trace_portmap_cache(void) { syslog(LOG_ERR, "portmap_cache: accesses=%d lookups=%d hits=%d\n", portmap_cache_accesses, portmap_cache_lookups, portmap_cache_hits); } /* * trace_host_cache() * traces the host cache values at desired points */ static void trace_host_cache(void) { syslog(LOG_ERR, "host_cache: accesses=%d lookups=%d deadhits=%d goodhits=%d\n", host_cache_accesses, host_cache_lookups, deadhost_cache_hits, goodhost_cache_hits); } #endif /* CACHE_DEBUG */ /* * Read the NFS SMF properties to determine if the * client has been configured for a new min/max for the NFS version to * use. */ #define SVC_NFS_CLIENT "svc:/network/nfs/client" static void read_default_nfs(void) { struct stat buf; char defval[4]; int errno, bufsz; int tmp, ret = 0; bufsz = 4; ret = nfs_smf_get_prop("client_versmin", defval, DEFAULT_INSTANCE, SCF_TYPE_INTEGER, SVC_NFS_CLIENT, &bufsz); if (ret == SA_OK) { errno = 0; tmp = strtol(defval, NULL, 10); if (errno == 0) { vers_min_default = tmp; } } bufsz = 4; ret = nfs_smf_get_prop("client_versmax", defval, DEFAULT_INSTANCE, SCF_TYPE_INTEGER, SVC_NFS_CLIENT, &bufsz); if (ret == SA_OK) { errno = 0; tmp = strtol(defval, NULL, 10); if (errno == 0) { vers_max_default = tmp; } } /* * Quick sanity check on the values picked up from the * defaults file. Make sure that a mistake wasn't * made that will confuse things later on. * If so, reset to compiled-in defaults */ if (vers_min_default > vers_max_default || vers_min_default < NFS_VERSMIN || vers_max_default > NFS_VERSMAX) { if (trace > 1) { trace_prt(1, " read_default: version minimum/maximum incorrectly configured\n"); trace_prt(1, " read_default: config is min=%d, max%d. Resetting to min=%d, max%d\n", vers_min_default, vers_max_default, NFS_VERSMIN_DEFAULT, NFS_VERSMAX_DEFAULT); } vers_min_default = NFS_VERSMIN_DEFAULT; vers_max_default = NFS_VERSMAX_DEFAULT; } } /* * Find the mnttab entry that corresponds to "name". * We're not sure what the name represents: either * a mountpoint name, or a special name (server:/path). * Return the last entry in the file that matches. */ static struct extmnttab * mnttab_find(char *dirname) { FILE *fp; struct extmnttab mnt; struct extmnttab *res = NULL; fp = fopen(MNTTAB, "r"); if (fp == NULL) { if (trace > 1) trace_prt(1, " mnttab_find: unable to open mnttab\n"); return (NULL); } while (getextmntent(fp, &mnt, sizeof (struct extmnttab)) == 0) { if (strcmp(mnt.mnt_mountp, dirname) == 0 || strcmp(mnt.mnt_special, dirname) == 0) { if (res) fsfreemnttab(res); res = fsdupmnttab(&mnt); } } resetmnttab(fp); fclose(fp); if (res == NULL) { if (trace > 1) { trace_prt(1, " mnttab_find: unable to find %s\n", dirname); } } return (res); } /* * This function's behavior is taken from nfsstat. * Trying to determine what NFS version was used for the mount. */ static int is_v4_mount(char *mntpath) { kstat_ctl_t *kc = NULL; /* libkstat cookie */ kstat_t *ksp; ulong_t fsid; struct mntinfo_kstat mik; struct extmnttab *mntp; uint_t mnt_minor; if ((mntp = mnttab_find(mntpath)) == NULL) return (FALSE); /* save the minor number and free the struct so we don't forget */ mnt_minor = mntp->mnt_minor; fsfreemnttab(mntp); if ((kc = kstat_open()) == NULL) return (FALSE); for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) { if (ksp->ks_type != KSTAT_TYPE_RAW) continue; if (strcmp(ksp->ks_module, "nfs") != 0) continue; if (strcmp(ksp->ks_name, "mntinfo") != 0) continue; if (mnt_minor != ksp->ks_instance) continue; if (kstat_read(kc, ksp, &mik) == -1) continue; (void) kstat_close(kc); if (mik.mik_vers == 4) return (TRUE); else return (FALSE); } (void) kstat_close(kc); return (FALSE); } static int create_homedir(const char *src, const char *dst) { struct stat stbuf; char *dst_username; struct passwd *pwd, pwds; char buf_pwd[NSS_BUFLEN_PASSWD]; int homedir_len; int dst_dir_len; int src_dir_len; if (trace > 1) trace_prt(1, "entered create_homedir\n"); if (stat(src, &stbuf) == 0) { if (trace > 1) trace_prt(1, "src exists\n"); return (1); } dst_username = strrchr(dst, '/'); if (dst_username) { dst_username++; /* Skip over slash */ pwd = getpwnam_r(dst_username, &pwds, buf_pwd, sizeof (buf_pwd)); if (pwd == NULL) { return (0); } } else { return (0); } homedir_len = strlen(pwd->pw_dir); dst_dir_len = strlen(dst) - homedir_len; src_dir_len = strlen(src) - homedir_len; /* Check that the paths are in the same zone */ if (src_dir_len < dst_dir_len || (strncmp(dst, src, dst_dir_len) != 0)) { if (trace > 1) trace_prt(1, " paths don't match\n"); return (0); } /* Check that mountpoint is an auto_home entry */ if (dst_dir_len < 0 || (strcmp(pwd->pw_dir, dst + dst_dir_len) != 0)) { return (0); } /* Check that source is an home directory entry */ if (src_dir_len < 0 || (strcmp(pwd->pw_dir, src + src_dir_len) != 0)) { if (trace > 1) { trace_prt(1, " homedir (2) doesn't match %s\n", src+src_dir_len); } return (0); } if (mkdir(src, S_IRUSR | S_IWUSR | S_IXUSR | S_IXGRP | S_IXOTH) == -1) { if (trace > 1) { trace_prt(1, " Couldn't mkdir %s\n", src); } return (0); } if (chown(src, pwd->pw_uid, pwd->pw_gid) == -1) { unlink(src); return (0); } /* Created new home directory for the user */ return (1); } void free_nfs_args(struct nfs_args *argp) { struct nfs_args *oldp; while (argp) { if (argp->pathconf) free(argp->pathconf); if (argp->knconf) free_knconf(argp->knconf); if (argp->addr) netbuf_free(argp->addr); if (argp->syncaddr) netbuf_free(argp->syncaddr); if (argp->netname) free(argp->netname); if (argp->hostname) free(argp->hostname); if (argp->nfs_ext_u.nfs_extB.secdata) nfs_free_secdata(argp->nfs_ext_u.nfs_extB.secdata); if (argp->fh) free(argp->fh); if (argp->nfs_ext_u.nfs_extA.secdata) { sec_data_t *sd; sd = argp->nfs_ext_u.nfs_extA.secdata; if (sd == NULL) break; switch (sd->rpcflavor) { case AUTH_NONE: case AUTH_UNIX: case AUTH_LOOPBACK: break; case AUTH_DES: { dh_k4_clntdata_t *dhk4; dhk4 = (dh_k4_clntdata_t *)sd->data; if (dhk4 == NULL) break; if (dhk4->syncaddr.buf) free(dhk4->syncaddr.buf); if (dhk4->knconf->knc_protofmly) free(dhk4->knconf->knc_protofmly); if (dhk4->knconf->knc_proto) free(dhk4->knconf->knc_proto); if (dhk4->knconf) free(dhk4->knconf); if (dhk4->netname) free(dhk4->netname); free(dhk4); break; } case RPCSEC_GSS: { gss_clntdata_t *gss; gss = (gss_clntdata_t *)sd->data; if (gss == NULL) break; if (gss->mechanism.elements) free(gss->mechanism.elements); free(gss); break; } } } oldp = argp; if (argp->nfs_args_ext == NFS_ARGS_EXTB) argp = argp->nfs_ext_u.nfs_extB.next; else argp = NULL; free(oldp); } } void * get_netconfig_info(enum type_of_stuff type_of_stuff, char *hostname, rpcprog_t prog, rpcvers_t vers, struct netconfig *nconf, ushort_t port, struct t_info *tinfo, struct t_bind *tbind, caddr_t *fhp, bool_t direct_to_server, char *fspath, enum clnt_stat *cstat, mfs_snego_t *mfssnego) { struct netconfig *nb = NULL; int ping_server = 0; if (nconf == NULL) return (NULL); switch (type_of_stuff) { case SERVER_FH: nb = get_server_fh(hostname, prog, vers, mfssnego, nconf, port, tinfo, tbind, fhp, direct_to_server, fspath, cstat); break; case SERVER_PING: ping_server = 1; /* FALLTHROUGH */ case SERVER_ADDR: nb = get_server_addrorping(hostname, prog, vers, nconf, port, tinfo, tbind, fhp, direct_to_server, fspath, cstat, ping_server); break; default: assert(nb != NULL); } return (nb); } /* * Get the server address or can we ping it or not. * Check the portmap cache first for server address. * If no entries there, ping the server with a NULLPROC rpc. */ void * get_server_addrorping(char *hostname, rpcprog_t prog, rpcvers_t vers, struct netconfig *nconf, ushort_t port, struct t_info *tinfo, struct t_bind *tbind, caddr_t *fhp, bool_t direct_to_server, char *fspath, enum clnt_stat *cstat, int ping_server) { struct timeval tv; enum clnt_stat cs = RPC_TIMEDOUT; struct netbuf *nb = NULL; CLIENT *cl = NULL; int fd = -1; if (prog == NFS_PROGRAM && vers == NFS_V4) if (strncasecmp(nconf->nc_proto, NC_UDP, strlen(NC_UDP)) == 0) goto done; if ((fd = t_open(nconf->nc_device, O_RDWR, tinfo)) < 0) { goto done; } /* LINTED pointer alignment */ if ((tbind = (struct t_bind *)t_alloc(fd, T_BIND, T_ADDR)) == NULL) { goto done; } if (direct_to_server != TRUE) { if (!ping_server) { if (get_cached_srv_addr(hostname, prog, vers, nconf, &tbind->addr) == 0) goto done; } else { if (port == 0) goto done; } } if (setup_nb_parms(nconf, tbind, tinfo, hostname, fd, direct_to_server, port, prog, vers, 0) < 0) goto done; if (port || (direct_to_server == TRUE)) { tv.tv_sec = 10; tv.tv_usec = 0; cl = clnt_tli_create(fd, nconf, &tbind->addr, prog, vers, 0, 0); if (cl == NULL) goto done; cs = clnt_call(cl, NULLPROC, xdr_void, 0, xdr_void, 0, tv); if (cs != RPC_SUCCESS) { syslog(LOG_ERR, "error is %d", cs); goto done; } } if (!ping_server) { nb = (struct netbuf *)malloc(sizeof (struct netbuf)); if (nb == NULL) { syslog(LOG_ERR, "no memory\n"); goto done; } nb->buf = (char *)malloc(tbind->addr.maxlen); if (nb->buf == NULL) { syslog(LOG_ERR, "no memory\n"); free(nb); nb = NULL; goto done; } (void) memcpy(nb->buf, tbind->addr.buf, tbind->addr.len); nb->len = tbind->addr.len; nb->maxlen = tbind->addr.maxlen; cs = RPC_SUCCESS; } done: destroy_auth_client_handle(cl); cleanup_tli_parms(tbind, fd); *cstat = cs; return (nb); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * autod_parse.c * * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * Copyright 2015 Nexenta Systems, Inc. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "automount.h" /* * This structure is used to determine the hierarchical * relationship between directories */ typedef struct _hiernode { char dirname[MAXFILENAMELEN+1]; struct _hiernode *subdir; struct _hiernode *leveldir; struct mapent *mapent; } hiernode; void free_mapent(struct mapent *); static int mapline_to_mapent(struct mapent **, struct mapline *, char *, char *, char *, char *, uint_t); static int hierarchical_sort(struct mapent *, hiernode **, char *, char *); static int push_options(hiernode *, char *, char *, int); static int set_mapent_opts(struct mapent *, char *, char *, char *); static void get_opts(char *, char *, char *, bool_t *); static int fstype_opts(struct mapent *, char *, char *, char *); static int modify_mapents(struct mapent **, char *, char *, char *, hiernode *, char *, uint_t, bool_t); static int set_and_fake_mapent_mntlevel(hiernode *, char *, char *, char *, struct mapent **, uint_t, char *, bool_t); static int mark_level1_root(hiernode *, char *); static int mark_and_fake_level1_noroot(hiernode *, char *, char *, char *, struct mapent **, uint_t i, char *); static int convert_mapent_to_automount(struct mapent *, char *, char *); static int automount_opts(char **, char *); static int parse_fsinfo(char *, struct mapent *); static int parse_nfs(char *, struct mapent *, char *, char *, char **, char **, int); static int parse_special(struct mapent *, char *, char *, char **, char **, int); static int get_dir_from_path(char *, char **, int); static int alloc_hiernode(hiernode **, char *); static void free_hiernode(hiernode *); static void trace_mapents(char *, struct mapent *); static void trace_hierarchy(hiernode *, int); static struct mapent *do_mapent_hosts(char *, char *, uint_t); static void freeex_ent(struct exportnode *); static void freeex(struct exportnode *); static void dump_mapent_err(struct mapent *, char *, char *); #define PARSE_OK 0 #define PARSE_ERROR -1 #define MAX_FSLEN 32 /* * mapentry error type defininitions */ #define MAPENT_NOERR 0 #define MAPENT_UATFS 1 /* * parse_entry(char *key, char *mapname, char *mapopts, struct mapline *ml, * char *subdir, uint_t isdirect, bool_t mount_access) * Parses the data in ml to build a mapentry list containing the information * for the mounts/lookups to be performed. Builds an intermediate mapentry list * by processing ml, hierarchically sorts (builds a tree of) the list according * to mountpoint. Then pushes options down the hierarchy, and fills in the mount * file system. Finally, modifies the intermediate list depending on how far * in the hierarchy the current request is (uses subdir). Deals with special * case of /net map parsing. * Returns a pointer to the head of the mapentry list. */ struct mapent * parse_entry(char *key, char *mapname, char *mapopts, struct mapline *ml, char *subdir, uint_t isdirect, bool_t mount_access) { char *p; char defaultopts[AUTOFS_MAXOPTSLEN]; struct mapent *mapents = NULL; hiernode *rootnode = NULL; char *lp = ml->linebuf; if (trace > 1) trace_prt(1, " mapline: %s\n", ml->linebuf); /* * Assure the key is only one token long. * This prevents options from sneaking in through the * command line or corruption of /etc/mnttab. */ for (p = key; *p != '\0'; p++) { if (isspace(*p)) { syslog(LOG_ERR, "parse_entry: bad key in map %s: %s", mapname, key); return ((struct mapent *)NULL); } } /* * select the appropriate parser, and build the mapentry list */ if (strcmp(lp, "-hosts") == 0) { /* * the /net parser - uses do_mapent_hosts to build mapents. * The mapopts are considered default for every entry, so we * don't push options down hierarchies. */ mapents = do_mapent_hosts(mapopts, key, isdirect); if (mapents == NULL) /* nothing to free */ return (mapents); if (trace > 3) trace_mapents("do_mapent_hosts:(return)", mapents); if (hierarchical_sort(mapents, &rootnode, key, mapname) != PARSE_OK) goto parse_error; } else { /* * all other parsing */ if (mapline_to_mapent(&mapents, ml, key, mapname, mapopts, defaultopts, isdirect) != PARSE_OK) goto parse_error; if (mapents == NULL) return (mapents); if (hierarchical_sort(mapents, &rootnode, key, mapname) != PARSE_OK) goto parse_error; if (push_options(rootnode, defaultopts, mapopts, MAPENT_NOERR) != PARSE_OK) goto parse_error; if (trace > 3) { trace_prt(1, "\n\tpush_options (return)\n"); trace_prt(0, "\tdefault options=%s\n", defaultopts); trace_hierarchy(rootnode, 0); }; if (parse_fsinfo(mapname, mapents) != PARSE_OK) goto parse_error; } /* * Modify the mapentry list. We *must* do this only after * the mapentry list is completely built (since we need to * have parse_fsinfo called first). */ if (modify_mapents(&mapents, mapname, mapopts, subdir, rootnode, key, isdirect, mount_access) != PARSE_OK) goto parse_error; /* * XXX: its dangerous to use rootnode after modify mapents as * it may be pointing to mapents that have been freed */ if (rootnode != NULL) free_hiernode(rootnode); return (mapents); parse_error: syslog(LOG_ERR, "parse_entry: mapentry parse error: map=%s key=%s", mapname, key); free_mapent(mapents); if (rootnode != NULL) free_hiernode(rootnode); return ((struct mapent *)NULL); } /* * mapline_to_mapent(struct mapent **mapents, struct mapline *ml, * char *key, char *mapname, char *mapopts, char *defaultopts, * uint_t isdirect) * Parses the mapline information in ml word by word to build an intermediate * mapentry list, which is passed back to the caller. The mapentries may have * holes (example no options), as they are completed only later. The logic is * awkward, but needed to provide the supported flexibility in the map entries. * (especially the first line). Note that the key is the full pathname of the * directory to be mounted in a direct map, and ml is the mapentry beyond key. * Returns PARSE_OK or an appropriate error value. */ static int mapline_to_mapent(struct mapent **mapents, struct mapline *ml, char *key, char *mapname, char *mapopts, char *defaultopts, uint_t isdirect) { struct mapent *me = NULL; struct mapent *mp; char w[MAXPATHLEN]; char wq[MAXPATHLEN]; char w1[MAXPATHLEN]; int implied; char *lp = ml->linebuf; char *lq = ml->lineqbuf; /* do any macro expansions that are required to complete ml */ if (macro_expand(key, lp, lq, LINESZ)) { syslog(LOG_ERR, "mapline_to_mapent: map %s: line too long (max %d chars)", mapname, LINESZ - 1); return (PARSE_ERROR); } if (trace > 3 && (strcmp(ml->linebuf, lp) != 0)) trace_prt(1, " mapline_to_mapent: (expanded) mapline (%s,%s)\n", ml->linebuf, ml->lineqbuf); /* init the head of mapentry list to null */ *mapents = NULL; /* * Get the first word - its either a '-' if default options provided, * a '/', if the mountroot is implicitly provided, or a mount filesystem * if the mountroot is implicit. Note that if the first word begins with * a '-' then the second must be read and it must be a mountpoint or a * mount filesystem. Use mapopts if no default opts are provided. */ if (getword(w, wq, &lp, &lq, ' ', sizeof (w)) == -1) return (PARSE_ERROR); if (*w == '-') { strcpy(defaultopts, w); if (getword(w, wq, &lp, &lq, ' ', sizeof (w)) == -1) return (PARSE_ERROR); } else strcpy(defaultopts, mapopts); /* * implied is true if there is no '/' * We need the same code path if we have an smbfs mount. */ implied = (*w != '/') || (strstr(defaultopts, "fstype=smbfs") != NULL); while (*w == '/' || implied) { mp = me; if ((me = (struct mapent *)malloc(sizeof (*me))) == NULL) goto alloc_failed; (void) memset((char *)me, 0, sizeof (*me)); if (*mapents == NULL) /* special case of head */ *mapents = me; else mp->map_next = me; /* * direct maps get an empty string as root - to be filled * by the entire path later. Indirect maps get /key as the * map root. Note that xfn maps don't care about the root * - they override it in getmapent_fn(). */ if (isdirect) { *w1 = '\0'; } else { strcpy(w1, "/"); strcat(w1, key); } if ((me->map_root = strdup(w1)) == NULL) goto alloc_failed; /* mntpnt is empty for the mount root */ if (strcmp(w, "/") == 0 || implied) me->map_mntpnt = strdup(""); else me->map_mntpnt = strdup(w); if (me->map_mntpnt == NULL) goto alloc_failed; /* * If implied, the word must be a mount filesystem, * and its already read in; also turn off implied - its * not applicable except for the mount root. Else, * read another (or two) words depending on if there's * an option. */ if (implied) /* must be a mount filesystem */ implied = 0; else { if (getword(w, wq, &lp, &lq, ' ', sizeof (w)) == -1) return (PARSE_ERROR); if (w[0] == '-') { /* mount options */ if ((me->map_mntopts = strdup(w)) == NULL) goto alloc_failed; if (getword(w, wq, &lp, &lq, ' ', sizeof (w)) == -1) return (PARSE_ERROR); } } /* * must be a mount filesystem or a set of filesystems at * this point. */ if (w[0] == '\0' || w[0] == '-') { syslog(LOG_ERR, "mapline_to_mapent: bad location=%s map=%s key=%s", w, mapname, key); return (PARSE_ERROR); } /* * map_fsw and map_fswq hold information which will be * used to determine filesystem information at a later * point. This is required since we can only find out * about the mount file system after the directories * are hierarchically sorted and options have been pushed * down the hierarchies. */ if (((me->map_fsw = strdup(w)) == NULL) || ((me->map_fswq = strdup(wq)) == NULL)) goto alloc_failed; /* * the next word, if any, is either another mount point or a * mount filesystem if more than one server is listed. */ if (getword(w, wq, &lp, &lq, ' ', sizeof (w)) == -1) return (PARSE_ERROR); while (*w && *w != '/') { /* more than 1 server listed */ int len; char *fsw, *fswq; len = strlen(me->map_fsw) + strlen(w) + 4; if ((fsw = (char *)malloc(len)) == NULL) goto alloc_failed; sprintf(fsw, "%s %s", me->map_fsw, w); free(me->map_fsw); me->map_fsw = fsw; len = strlen(me->map_fswq) + strlen(wq) + 4; if ((fswq = (char *)malloc(len)) == NULL) goto alloc_failed; sprintf(fswq, "%s %s", me->map_fswq, wq); free(me->map_fswq); me->map_fswq = fswq; if (getword(w, wq, &lp, &lq, ' ', sizeof (w)) == -1) return (PARSE_ERROR); } /* initialize flags */ me->map_mntlevel = -1; me->map_modified = FALSE; me->map_faked = FALSE; me->map_err = MAPENT_NOERR; me->map_next = NULL; } if (*mapents == NULL || w[0] != '\0') { /* sanity check */ if (verbose) { if (*mapents == NULL) syslog(LOG_ERR, "mapline_to_mapent: parsed with null mapents"); else syslog(LOG_ERR, "mapline_to_mapent: parsed nononempty w=%s", w); } return (PARSE_ERROR); } if (trace > 3) trace_mapents("mapline_to_mapent:", *mapents); return (PARSE_OK); alloc_failed: syslog(LOG_ERR, "mapline_to_mapent: Memory allocation failed"); return (ENOMEM); } /* * hierarchical_sort(struct mapent *mapents, hiernode **rootnode, char *key * char *mapname) * sorts the mntpnts in each mapent to build a hierarchy of nodes, with * with the rootnode being the mount root. The hierarchy is setup as * levels, and subdirs below each level. Provides a link from node to * the relevant mapentry. * Returns PARSE_OK or appropriate error value */ static int hierarchical_sort(struct mapent *mapents, hiernode **rootnode, char *key, char *mapname) { hiernode *prevnode, *currnode, *newnode; char *path; char dirname[MAXFILENAMELEN]; int rc = PARSE_OK; struct mapent *me = mapents; /* allocate the rootnode with a default path of "" */ *rootnode = NULL; if ((rc = alloc_hiernode(rootnode, "")) != PARSE_OK) return (rc); /* * walk through mapents - for each mapent, locate the position * within the hierarchy by walking across leveldirs, and * subdirs of matched leveldirs. Starts one level below * the root (assumes an implicit match with rootnode). * XXX - this could probably be done more cleanly using recursion. */ while (me != NULL) { path = me->map_mntpnt; if ((rc = get_dir_from_path(dirname, &path, sizeof (dirname))) != PARSE_OK) return (rc); prevnode = *rootnode; currnode = (*rootnode)->subdir; while (dirname[0] != '\0') { if (currnode != NULL) { if (strcmp(currnode->dirname, dirname) == 0) { /* * match found - mntpnt is a child of * this node */ prevnode = currnode; currnode = currnode->subdir; } else { prevnode = currnode; currnode = currnode->leveldir; if (currnode == NULL) { /* * No more leveldirs to match. * Add a new one */ if ((rc = alloc_hiernode (&newnode, dirname)) != PARSE_OK) return (rc); prevnode->leveldir = newnode; prevnode = newnode; currnode = newnode->subdir; } else { /* try this leveldir */ continue; } } } else { /* no more subdirs to match. Add a new one */ if ((rc = alloc_hiernode(&newnode, dirname)) != PARSE_OK) return (rc); prevnode->subdir = newnode; prevnode = newnode; currnode = newnode->subdir; } if ((rc = get_dir_from_path(dirname, &path, sizeof (dirname))) != PARSE_OK) return (rc); } if (prevnode->mapent != NULL) { /* duplicate mntpoint found */ syslog(LOG_ERR, "hierarchical_sort: duplicate mntpnt map=%s key=%s", mapname, key); return (PARSE_ERROR); } /* provide a pointer from node to mapent */ prevnode->mapent = me; me = me->map_next; } if (trace > 3) { trace_prt(1, "\n\thierarchical_sort:\n"); trace_hierarchy(*rootnode, 0); /* 0 is rootnode's level */ } return (rc); } /* * push_options(hiernode *node, char *opts, char *mapopts, int err) * Pushes the options down a hierarchical structure. Works recursively from the * root, which is passed in on the first call. Uses a replacement policy. * If a node points to a mapentry, and it has an option, then thats the option * for that mapentry. Else, the node's mapent inherits the option from the * default (which may be the global option for the entry or mapopts). * err is useful in flagging entries with errors in pushing options. * returns PARSE_OK or appropriate error value. */ static int push_options(hiernode *node, char *defaultopts, char *mapopts, int err) { int rc = PARSE_OK; struct mapent *me = NULL; /* ensure that all the dirs at a level are passed the default options */ while (node != NULL) { me = node->mapent; if (me != NULL) { /* not all nodes point to a mapentry */ me->map_err = err; if ((rc = set_mapent_opts(me, me->map_mntopts, defaultopts, mapopts)) != PARSE_OK) return (rc); } /* push the options to subdirs */ if (node->subdir != NULL) { if (node->mapent && strcmp(node->mapent->map_fstype, MNTTYPE_AUTOFS) == 0) err = MAPENT_UATFS; if ((rc = push_options(node->subdir, defaultopts, mapopts, err)) != PARSE_OK) return (rc); } node = node->leveldir; } return (rc); } #define FSTYPE "fstype" #define FSTYPE_EQ "fstype=" #define NO_OPTS "" /* * set_mapent_opts(struct mapent *me, char *opts, char *defaultopts, * char *mapopts) * sets the mapentry's options, fstype and mounter fields by separating * out the fstype part from the opts. Use default options if opts is NULL. * Note taht defaultopts may be the same as mapopts. * Returns PARSE_OK or appropriate error value. */ static int set_mapent_opts(struct mapent *me, char *opts, char *defaultopts, char *mapopts) { char entryopts[AUTOFS_MAXOPTSLEN]; char fstype[MAX_FSLEN], mounter[MAX_FSLEN]; int rc = PARSE_OK; bool_t fstype_opt = FALSE; strcpy(fstype, MNTTYPE_NFS); /* default */ /* set options to default options, if none exist for this entry */ if (opts == NULL) { opts = defaultopts; if (defaultopts == NULL) { /* NULL opts for entry */ strcpy(mounter, fstype); goto done; } } if (*opts == '-') opts++; /* separate opts into fstype and (other) entrypopts */ get_opts(opts, entryopts, fstype, &fstype_opt); /* replace any existing opts */ if (me->map_mntopts != NULL) free(me->map_mntopts); if ((me->map_mntopts = strdup(entryopts)) == NULL) return (ENOMEM); strcpy(mounter, fstype); /* * child options are exactly fstype = somefs, we need to do some * more option pushing work. */ if (fstype_opt == TRUE && (strcmp(me->map_mntopts, NO_OPTS) == 0)) { free(me->map_mntopts); if ((rc = fstype_opts(me, opts, defaultopts, mapopts)) != PARSE_OK) return (rc); } done: if (((me->map_fstype = strdup(fstype)) == NULL) || ((me->map_mounter = strdup(mounter)) == NULL)) { if (me->map_fstype != NULL) free(me->map_fstype); syslog(LOG_ERR, "set_mapent_opts: No memory"); return (ENOMEM); } return (rc); } /* * Check the option string for an "fstype" * option. If found, return the fstype * and the option string with the fstype * option removed, e.g. * * input: "fstype=nfs,ro,nosuid" * opts: "ro,nosuid" * fstype: "nfs" * * Also indicates if the fstype option was present * by setting a flag, if the pointer to the flag * is not NULL. */ static void get_opts(char *input, char *opts, char *fstype, bool_t *fstype_opt) { char *p, *pb; char buf[MAXOPTSLEN]; char *placeholder; *opts = '\0'; (void) strcpy(buf, input); pb = buf; while (p = (char *)strtok_r(pb, ",", &placeholder)) { pb = NULL; if (strncmp(p, FSTYPE_EQ, 7) == 0) { if (fstype_opt != NULL) *fstype_opt = TRUE; (void) strcpy(fstype, p + 7); } else { if (*opts) (void) strcat(opts, ","); (void) strcat(opts, p); } } } /* * fstype_opts(struct mapent *me, char *opts, char *defaultopts, * char *mapopts) * We need to push global options to the child entry if it is exactly * fstype=somefs. */ static int fstype_opts(struct mapent *me, char *opts, char *defaultopts, char *mapopts) { char pushentryopts[AUTOFS_MAXOPTSLEN]; char pushfstype[MAX_FSLEN]; if (defaultopts && *defaultopts == '-') defaultopts++; /* * the options to push are the global defaults for the entry, * if they exist, or mapopts, if the global defaults for the * entry does not exist. */ if (strcmp(defaultopts, opts) == 0) { if (*mapopts == '-') mapopts++; get_opts(mapopts, pushentryopts, pushfstype, NULL); } else { get_opts(defaultopts, pushentryopts, pushfstype, NULL); } me->map_mntopts = strdup(pushentryopts); if (!me->map_mntopts) { syslog(LOG_ERR, "fstype_opts: No memory"); return (ENOMEM); } return (PARSE_OK); } /* * modify_mapents(struct mapent **mapents, char *mapname, * char *mapopts, char *subdir, hiernode *rootnode, * char *key, uint_t isdirect, bool_t mount_access) * modifies the intermediate mapentry list into the final one, and passes * back a pointer to it. The final list may contain faked mapentries for * hiernodes that do not point to a mapentry, or converted mapentries, if * hiernodes that point to a mapentry need to be converted from nfs to autofs. * mounts. Entries that are not directly 1 level below the subdir are removed. * Returns PARSE_OK or PARSE_ERROR */ static int modify_mapents(struct mapent **mapents, char *mapname, char *mapopts, char *subdir, hiernode *rootnode, char *key, uint_t isdirect, bool_t mount_access) { struct mapent *mp = NULL; char w[MAXPATHLEN]; struct mapent *me; int rc = PARSE_OK; struct mapent *faked_mapents = NULL; /* * correct the mapentry mntlevel from default -1 to level depending on * position in hierarchy, and build any faked mapentries, if required * at one level below the rootnode given by subdir. */ if ((rc = set_and_fake_mapent_mntlevel(rootnode, subdir, key, mapname, &faked_mapents, isdirect, mapopts, mount_access)) != PARSE_OK) return (rc); /* * attaches faked mapents to real mapents list. Assumes mapents * is not NULL. */ me = *mapents; while (me->map_next != NULL) me = me->map_next; me->map_next = faked_mapents; /* * get rid of nodes marked at level -1 */ me = *mapents; while (me != NULL) { if ((me->map_mntlevel == -1) || (me->map_err) || (mount_access == FALSE && me->map_mntlevel == 0)) { /* * syslog any errors and free entry */ if (me->map_err) dump_mapent_err(me, key, mapname); if (me == (*mapents)) { /* special case when head has to be freed */ *mapents = me->map_next; if ((*mapents) == NULL) { /* something wierd happened */ if (verbose) syslog(LOG_ERR, "modify_mapents: level error"); return (PARSE_ERROR); } /* separate out the node */ me->map_next = NULL; free_mapent(me); me = *mapents; } else { mp->map_next = me->map_next; me->map_next = NULL; free_mapent(me); me = mp->map_next; } continue; } /* * convert level 1 mapents that are not already autonodes * to autonodes */ if (me->map_mntlevel == 1 && (strcmp(me->map_fstype, MNTTYPE_AUTOFS) != 0) && (me->map_faked != TRUE)) { if ((rc = convert_mapent_to_automount(me, mapname, mapopts)) != PARSE_OK) return (rc); } strcpy(w, (me->map_mntpnt+strlen(subdir))); strcpy(me->map_mntpnt, w); mp = me; me = me->map_next; } if (trace > 3) trace_mapents("modify_mapents:", *mapents); return (PARSE_OK); } /* * set_and_fake_mapent_mntlevel(hiernode *rootnode, char *subdir, char *key, * char *mapname, struct mapent **faked_mapents, * uint_t isdirect, char *mapopts, bool_t mount_access) * sets the mapentry mount levels (depths) with respect to the subdir. * Assigns a value of 0 to the new root. Finds the level1 directories by * calling mark_*_level1_*(). Also cleans off extra /'s in level0 and * level1 map_mntpnts. Note that one level below the new root is an existing * mapentry if there's a mapentry (nfs mount) corresponding to the root, * and the direct subdir set for the root, if there's no mapentry corresponding * to the root (we install autodirs). Returns PARSE_OK or error value. */ static int set_and_fake_mapent_mntlevel(hiernode *rootnode, char *subdir, char *key, char *mapname, struct mapent **faked_mapents, uint_t isdirect, char *mapopts, bool_t mount_access) { char dirname[MAXFILENAMELEN]; char traversed_path[MAXPATHLEN]; /* used in building fake mapentries */ char *subdir_child = subdir; hiernode *prevnode = rootnode; hiernode *currnode = rootnode->subdir; int rc = PARSE_OK; traversed_path[0] = '\0'; /* * find and mark the root by tracing down subdir. Use traversed_path * to keep track of how far we go, while guaranteeing that it * contains no '/' at the end. Took some mucking to get that right. */ if ((rc = get_dir_from_path(dirname, &subdir_child, sizeof (dirname))) != PARSE_OK) return (rc); if (dirname[0] != '\0') { if (strlcat(traversed_path, "/", sizeof (traversed_path)) >= sizeof (traversed_path)) return (PARSE_ERROR); if (strlcat(traversed_path, dirname, sizeof (traversed_path)) >= sizeof (traversed_path)) return (PARSE_ERROR); } prevnode = rootnode; currnode = rootnode->subdir; while (dirname[0] != '\0' && currnode != NULL) { if (strcmp(currnode->dirname, dirname) == 0) { /* subdir is a child of currnode */ prevnode = currnode; currnode = currnode->subdir; if ((rc = get_dir_from_path(dirname, &subdir_child, sizeof (dirname))) != PARSE_OK) return (rc); if (dirname[0] != '\0') { if (strlcat(traversed_path, "/", sizeof (traversed_path)) >= sizeof (traversed_path)) return (PARSE_ERROR); if (strlcat(traversed_path, dirname, sizeof (traversed_path)) >= sizeof (traversed_path)) return (PARSE_ERROR); } } else { /* try next leveldir */ prevnode = currnode; currnode = currnode->leveldir; } } if (dirname[0] != '\0') { if (verbose) syslog(LOG_ERR, "set_and_fake_mapent_mntlevel: subdir=%s error: map=%s", subdir, mapname); return (PARSE_ERROR); } /* * see if level of root really points to a mapent and if * have access to that filessystem - call appropriate * routine to mark level 1 nodes, and build faked entries */ if (prevnode->mapent != NULL && mount_access == TRUE) { if (trace > 3) trace_prt(1, " node mountpoint %s\t travpath=%s\n", prevnode->mapent->map_mntpnt, traversed_path); /* * Copy traversed path map_mntpnt to get rid of any extra * '/' the map entry may contain. */ if (strlen(prevnode->mapent->map_mntpnt) < strlen(traversed_path)) { /* sanity check */ if (verbose) syslog(LOG_ERR, "set_and_fake_mapent_mntlevel: path=%s error", traversed_path); return (PARSE_ERROR); } if (strcmp(prevnode->mapent->map_mntpnt, traversed_path) != 0) strcpy(prevnode->mapent->map_mntpnt, traversed_path); prevnode->mapent->map_mntlevel = 0; /* root level is 0 */ if (currnode != NULL) { if ((rc = mark_level1_root(currnode, traversed_path)) != PARSE_OK) return (rc); } } else if (currnode != NULL) { if (trace > 3) trace_prt(1, " No rootnode, travpath=%s\n", traversed_path); if ((rc = mark_and_fake_level1_noroot(currnode, traversed_path, key, mapname, faked_mapents, isdirect, mapopts)) != PARSE_OK) return (rc); } if (trace > 3) { trace_prt(1, "\n\tset_and_fake_mapent_mntlevel\n"); trace_hierarchy(rootnode, 0); } return (rc); } /* * mark_level1_root(hiernode *node, char *traversed_path) * marks nodes upto one level below the rootnode given by subdir * recursively. Called if rootnode points to a mapent. * In this routine, a level 1 node is considered to be the 1st existing * mapentry below the root node, so there's no faking involved. * Returns PARSE_OK or error value */ static int mark_level1_root(hiernode *node, char *traversed_path) { /* ensure we touch all leveldirs */ while (node) { /* * mark node level as 1, if one exists - else walk down * subdirs until we find one. */ if (node->mapent == NULL) { char w[MAXPATHLEN]; if (node->subdir != NULL) { sprintf(w, "%s/%s", traversed_path, node->dirname); if (mark_level1_root(node->subdir, w) == PARSE_ERROR) return (PARSE_ERROR); } else { if (verbose) { syslog(LOG_ERR, "mark_level1_root: hierarchy error"); } return (PARSE_ERROR); } } else { char w[MAXPATHLEN]; sprintf(w, "%s/%s", traversed_path, node->dirname); if (trace > 3) trace_prt(1, " node mntpnt %s\t travpath %s\n", node->mapent->map_mntpnt, w); /* replace mntpnt with travpath to clean extra '/' */ if (strlen(node->mapent->map_mntpnt) < strlen(w)) { if (verbose) { syslog(LOG_ERR, "mark_level1_root: path=%s error", traversed_path); } return (PARSE_ERROR); } if (strcmp(node->mapent->map_mntpnt, w) != 0) strcpy(node->mapent->map_mntpnt, w); node->mapent->map_mntlevel = 1; } node = node->leveldir; } return (PARSE_OK); } /* * mark_and_fake_level1_noroot(hiernode *node, char *traversed_path, * char *key,char *mapname, struct mapent **faked_mapents, * uint_t isdirect, char *mapopts) * Called if the root of the hierarchy does not point to a mapent. marks nodes * upto one physical level below the rootnode given by subdir. checks if * there's a real mapentry. If not, it builds a faked one (autonode) at that * point. The faked autonode is direct, with the map being the same as the * original one from which the call originated. Options are same as that of * the map and assigned in automount_opts(). Returns PARSE_OK or error value. */ static int mark_and_fake_level1_noroot(hiernode *node, char *traversed_path, char *key, char *mapname, struct mapent **faked_mapents, uint_t isdirect, char *mapopts) { struct mapent *me; int rc = 0; char faked_map_mntpnt[MAXPATHLEN]; char w1[MAXPATHLEN]; char w[MAXPATHLEN]; while (node != NULL) { if (node->mapent != NULL) { /* * existing mapentry at level 1 - copy travpath to * get rid of extra '/' in mntpnt */ sprintf(w, "%s/%s", traversed_path, node->dirname); if (trace > 3) trace_prt(1, " node mntpnt=%s\t travpath=%s\n", node->mapent->map_mntpnt, w); if (strlen(node->mapent->map_mntpnt) < strlen(w)) { /* sanity check */ if (verbose) syslog(LOG_ERR, "mark_fake_level1_noroot:path=%s error", traversed_path); return (PARSE_ERROR); } if (strcmp(node->mapent->map_mntpnt, w) != 0) strcpy(node->mapent->map_mntpnt, w); node->mapent->map_mntlevel = 1; } else { /* * build the faked autonode */ if ((me = (struct mapent *)malloc(sizeof (*me))) == NULL) { syslog(LOG_ERR, "mark_and_fake_level1_noroot: out of memory"); return (ENOMEM); } (void) memset((char *)me, 0, sizeof (*me)); if ((me->map_fs = (struct mapfs *) malloc(sizeof (struct mapfs))) == NULL) return (ENOMEM); (void) memset(me->map_fs, 0, sizeof (struct mapfs)); if (isdirect) { *w1 = '\0'; } else { strcpy(w1, "/"); strcat(w1, key); } me->map_root = strdup(w1); sprintf(faked_map_mntpnt, "%s/%s", traversed_path, node->dirname); me->map_mntpnt = strdup(faked_map_mntpnt); me->map_fstype = strdup(MNTTYPE_AUTOFS); me->map_mounter = strdup(MNTTYPE_AUTOFS); /* set options */ if ((rc = automount_opts(&me->map_mntopts, mapopts)) != PARSE_OK) return (rc); me->map_fs->mfs_dir = strdup(mapname); me->map_mntlevel = 1; me->map_modified = FALSE; me->map_faked = TRUE; /* mark as faked */ if (me->map_root == NULL || me->map_mntpnt == NULL || me->map_fstype == NULL || me->map_mounter == NULL || me->map_mntopts == NULL || me->map_fs->mfs_dir == NULL) { syslog(LOG_ERR, "mark_and_fake_level1_noroot: out of memory"); free_mapent(*faked_mapents); return (ENOMEM); } if (*faked_mapents == NULL) *faked_mapents = me; else { /* attach to the head */ me->map_next = *faked_mapents; *faked_mapents = me; } node->mapent = me; } node = node->leveldir; } return (rc); } /* * convert_mapent_to_automount(struct mapent *me, char *mapname, * char *mapopts) * change the mapentry me to an automount - free fields first and NULL them * to avoid freeing again, while freeing the mapentry at a later stage. * Could have avoided freeing entries here as we don't really look at them. * Give the converted mapent entry the options that came with the map using * automount_opts(). Returns PARSE_OK or appropriate error value. */ static int convert_mapent_to_automount(struct mapent *me, char *mapname, char *mapopts) { struct mapfs *mfs = me->map_fs; /* assumes it exists */ int rc = PARSE_OK; /* free relevant entries */ if (mfs->mfs_host) { free(mfs->mfs_host); mfs->mfs_host = NULL; } while (me->map_fs->mfs_next != NULL) { mfs = me->map_fs->mfs_next; if (mfs->mfs_host) free(mfs->mfs_host); if (mfs->mfs_dir) free(mfs->mfs_dir); me->map_fs->mfs_next = mfs->mfs_next; /* nulls eventually */ free((void*)mfs); } /* replace relevant entries */ if (me->map_fstype) free(me->map_fstype); if ((me->map_fstype = strdup(MNTTYPE_AUTOFS)) == NULL) goto alloc_failed; if (me->map_mounter) free(me->map_mounter); if ((me->map_mounter = strdup(me->map_fstype)) == NULL) goto alloc_failed; if (me->map_fs->mfs_dir) free(me->map_fs->mfs_dir); if ((me->map_fs->mfs_dir = strdup(mapname)) == NULL) goto alloc_failed; /* set options */ if (me->map_mntopts) free(me->map_mntopts); if ((rc = automount_opts(&me->map_mntopts, mapopts)) != PARSE_OK) return (rc); /* mucked with this entry, set the map_modified field to TRUE */ me->map_modified = TRUE; return (rc); alloc_failed: syslog(LOG_ERR, "convert_mapent_to_automount: Memory allocation failed"); return (ENOMEM); } /* * automount_opts(char **map_mntopts, char *mapopts) * modifies automount opts - gets rid of all "indirect" and "direct" strings * if they exist, and then adds a direct string to force a direct automount. * Rest of the mapopts stay intact. Returns PARSE_OK or appropriate error. */ static int automount_opts(char **map_mntopts, char *mapopts) { char *opts; char *opt; int len; char *placeholder; char buf[AUTOFS_MAXOPTSLEN]; char *addopt = "direct"; len = strlen(mapopts)+ strlen(addopt)+2; /* +2 for ",", '\0' */ if (len > AUTOFS_MAXOPTSLEN) { syslog(LOG_ERR, "option string %s too long (max=%d)", mapopts, AUTOFS_MAXOPTSLEN-8); return (PARSE_ERROR); } if (((*map_mntopts) = ((char *)malloc(len))) == NULL) { syslog(LOG_ERR, "automount_opts: Memory allocation failed"); return (ENOMEM); } memset(*map_mntopts, 0, len); strcpy(buf, mapopts); opts = buf; while ((opt = strtok_r(opts, ",", &placeholder)) != NULL) { opts = NULL; /* remove trailing and leading spaces */ while (isspace(*opt)) opt++; len = strlen(opt)-1; while (isspace(opt[len])) opt[len--] = '\0'; /* * if direct or indirect found, get rid of it, else put it * back */ if ((strcmp(opt, "indirect") == 0) || (strcmp(opt, "direct") == 0)) continue; if (*map_mntopts[0] != '\0') strcat(*map_mntopts, ","); strcat(*map_mntopts, opt); } /* add the direct string at the end */ if (*map_mntopts[0] != '\0') strcat(*map_mntopts, ","); strcat(*map_mntopts, addopt); return (PARSE_OK); } /* * parse_fsinfo(char *mapname, struct mapent *mapents) * parses the filesystem information stored in me->map_fsw and me->map_fswq * and calls appropriate filesystem parser. * Returns PARSE_OK or an appropriate error value. */ static int parse_fsinfo(char *mapname, struct mapent *mapents) { struct mapent *me = mapents; char *bufp; char *bufq; int wordsz = MAXPATHLEN; int err = 0; while (me != NULL) { bufp = ""; bufq = ""; if (strcmp(me->map_fstype, MNTTYPE_NFS) == 0) { err = parse_nfs(mapname, me, me->map_fsw, me->map_fswq, &bufp, &bufq, wordsz); } else { err = parse_special(me, me->map_fsw, me->map_fswq, &bufp, &bufq, wordsz); } if (err != PARSE_OK || *me->map_fsw != '\0' || *me->map_fswq != '\0') { /* sanity check */ if (verbose) syslog(LOG_ERR, "parse_fsinfo: mount location error %s", me->map_fsw); return (PARSE_ERROR); } me = me->map_next; } if (trace > 3) { trace_mapents("parse_fsinfo:", mapents); } return (PARSE_OK); } /* * This function parses the map entry for a nfs type file system * The input is the string lp (and lq) which can be one of the * following forms: * a) host[(penalty)][,host[(penalty)]]... :/directory * b) host[(penalty)]:/directory[ host[(penalty)]:/directory]... * This routine constructs a mapfs link-list for each of * the hosts and the corresponding file system. The list * is then attatched to the mapent struct passed in. */ int parse_nfs(char *mapname, struct mapent *me, char *fsw, char *fswq, char **lp, char **lq, int wsize) { struct mapfs *mfs, **mfsp; char *wlp, *wlq; char *hl, hostlist[1024], *hlq, hostlistq[1024]; char hostname_and_penalty[MXHOSTNAMELEN+5]; char *hn, *hnq, hostname[MXHOSTNAMELEN+1]; char dirname[MAXPATHLEN+1], subdir[MAXPATHLEN+1]; char qbuff[MAXPATHLEN+1], qbuff1[MAXPATHLEN+1]; char pbuff[10], pbuffq[10]; int penalty; char w[MAXPATHLEN]; char wq[MAXPATHLEN]; int host_cnt; mfsp = &me->map_fs; *mfsp = NULL; /* * there may be more than one entry in the map list. Get the * first one. Use temps to handle the word information and * copy back into fsw and fswq fields when done. */ *lp = fsw; *lq = fswq; if (getword(w, wq, lp, lq, ' ', wsize) == -1) return (PARSE_ERROR); while (*w && *w != '/') { bool_t maybe_url; maybe_url = TRUE; wlp = w; wlq = wq; if (getword(hostlist, hostlistq, &wlp, &wlq, ':', sizeof (hostlist)) == -1) return (PARSE_ERROR); if (!*hostlist) goto bad_entry; if (strcmp(hostlist, "nfs") != 0) maybe_url = FALSE; if (getword(dirname, qbuff, &wlp, &wlq, ':', sizeof (dirname)) == -1) return (PARSE_ERROR); if (*dirname == '\0') goto bad_entry; if (maybe_url == TRUE && strncmp(dirname, "//", 2) != 0) maybe_url = FALSE; /* * See the next block comment ("Once upon a time ...") to * understand this. It turns the deprecated concept * of "subdir mounts" produced some useful code for handling * the possibility of a ":port#" in the URL. */ if (maybe_url == FALSE) *subdir = '/'; else *subdir = ':'; *qbuff = ' '; /* * Once upon time, before autofs, there was support for * "subdir mounts". The idea was to "economize" the * number of mounts, so if you had a number of entries * all referring to a common subdirectory, e.g. * * carol seasons:/export/home11/carol * ted seasons:/export/home11/ted * alice seasons:/export/home11/alice * * then you could tell the automounter to mount a * common mountpoint which was delimited by the second * colon: * * carol seasons:/export/home11:carol * ted seasons:/export/home11:ted * alice seasons:/export/home11:alice * * The automounter would mount seasons:/export/home11 * then for any other map entry that referenced the same * directory it would build a symbolic link that * appended the remainder of the path after the second * colon, i.e. once the common subdir was mounted, then * other directories could be accessed just by link * building - no further mounts required. * * In theory the "mount saving" idea sounded good. In * practice the saving didn't amount to much and the * symbolic links confused people because the common * mountpoint had to have a pseudonym. * * To remain backward compatible with the existing * maps, we interpret a second colon as a slash. */ if (getword(subdir+1, qbuff+1, &wlp, &wlq, ':', sizeof (subdir)) == -1) return (PARSE_ERROR); if (*(subdir+1)) (void) strcat(dirname, subdir); hl = hostlist; hlq = hostlistq; host_cnt = 0; for (;;) { if (getword(hostname_and_penalty, qbuff, &hl, &hlq, ',', sizeof (hostname_and_penalty)) == -1) return (PARSE_ERROR); if (!*hostname_and_penalty) break; host_cnt++; if (host_cnt > 1) maybe_url = FALSE; hn = hostname_and_penalty; hnq = qbuff; if (getword(hostname, qbuff1, &hn, &hnq, '(', sizeof (hostname)) == -1) return (PARSE_ERROR); if (hostname[0] == '\0') goto bad_entry; if (strcmp(hostname, hostname_and_penalty) == 0) { penalty = 0; } else { maybe_url = FALSE; hn++; hnq++; if (getword(pbuff, pbuffq, &hn, &hnq, ')', sizeof (pbuff)) == -1) return (PARSE_ERROR); if (!*pbuff) penalty = 0; else penalty = atoi(pbuff); } mfs = (struct mapfs *)malloc(sizeof (*mfs)); if (mfs == NULL) { syslog(LOG_ERR, "parse_nfs: Memory allocation failed"); return (PARSE_ERROR); } (void) memset(mfs, 0, sizeof (*mfs)); *mfsp = mfs; mfsp = &mfs->mfs_next; if (maybe_url == TRUE) { char *host; char *path; char *sport; host = dirname+2; path = strchr(host, '/'); if (path == NULL) { syslog(LOG_ERR, "parse_nfs: illegal " "nfs url syntax: %s", host); return (PARSE_ERROR); } *path = '\0'; sport = strchr(host, ':'); if (sport != NULL && sport < path) { *sport = '\0'; mfs->mfs_port = atoi(sport+1); if (mfs->mfs_port > USHRT_MAX) { syslog(LOG_ERR, "parse_nfs: invalid " "port number (%d) in " "NFS URL", mfs->mfs_port); return (PARSE_ERROR); } } path++; if (*path == '\0') path = "."; mfs->mfs_flags |= MFS_URL; mfs->mfs_host = strdup(host); mfs->mfs_dir = strdup(path); } else { mfs->mfs_host = strdup(hostname); mfs->mfs_dir = strdup(dirname); } mfs->mfs_penalty = penalty; if (mfs->mfs_host == NULL || mfs->mfs_dir == NULL) { syslog(LOG_ERR, "parse_nfs: Memory allocation failed"); return (PARSE_ERROR); } } /* * We check host_cnt to make sure we haven't parsed an entry * with no host information. */ if (host_cnt == 0) { syslog(LOG_ERR, "parse_nfs: invalid host specified - bad entry " "in map %s \"%s\"", mapname, w); return (PARSE_ERROR); } if (getword(w, wq, lp, lq, ' ', wsize) == -1) return (PARSE_ERROR); } strcpy(fsw, w); strcpy(fswq, wq); return (PARSE_OK); bad_entry: syslog(LOG_ERR, "parse_nfs: bad entry in map %s \"%s\"", mapname, w); return (PARSE_ERROR); } static int parse_special(struct mapent *me, char *w, char *wq, char **lp, char **lq, int wsize) { char devname[MAXPATHLEN + 1], qbuf[MAXPATHLEN + 1]; char *wlp, *wlq; struct mapfs *mfs; wlp = w; wlq = wq; if (getword(devname, qbuf, &wlp, &wlq, ' ', sizeof (devname)) == -1) return (PARSE_ERROR); if (devname[0] == '\0') return (PARSE_ERROR); mfs = malloc(sizeof (struct mapfs)); if (mfs == NULL) return (PARSE_ERROR); (void) memset(mfs, 0, sizeof (*mfs)); /* * A device name that begins with a slash could * be confused with a mountpoint path, hence use * a colon to escape a device string that begins * with a slash, e.g. * * foo -ro /bar foo:/bar * and * foo -ro /dev/sr0 * * would confuse the parser. The second instance * must use a colon: * * foo -ro :/dev/sr0 */ mfs->mfs_dir = strdup(&devname[devname[0] == ':']); if (mfs->mfs_dir == NULL) return (PARSE_ERROR); me->map_fs = mfs; if (getword(w, wq, lp, lq, ' ', wsize) == -1) return (PARSE_ERROR); return (0); } /* * get_dir_from_path(char *dir, char **path, int dirsz) * gets the directory name dir from path for max string of length dirsz. * A modification of the getword routine. Assumes the delimiter is '/' * and that excess /'s are redundant. * Returns PARSE_OK or PARSE_ERROR */ static int get_dir_from_path(char *dir, char **path, int dirsz) { char *tmp = dir; int count = dirsz; if (dirsz <= 0) { if (verbose) syslog(LOG_ERR, "get_dir_from_path: invalid directory size %d", dirsz); return (PARSE_ERROR); } /* get rid of leading /'s in path */ while (**path == '/') (*path)++; /* now at a word or at the end of path */ while ((**path) && ((**path) != '/')) { if (--count <= 0) { *tmp = '\0'; syslog(LOG_ERR, "get_dir_from_path: max pathlength exceeded %d", dirsz); return (PARSE_ERROR); } *dir++ = *(*path)++; } *dir = '\0'; /* get rid of trailing /'s in path */ while (**path == '/') (*path)++; return (PARSE_OK); } /* * alloc_hiernode(hiernode **newnode, char *dirname) * allocates a new hiernode corresponding to a new directory entry * in the hierarchical structure, and passes a pointer to it back * to the calling program. * Returns PARSE_OK or appropriate error value. */ static int alloc_hiernode(hiernode **newnode, char *dirname) { if ((*newnode = (hiernode *)malloc(sizeof (hiernode))) == NULL) { syslog(LOG_ERR, "alloc_hiernode: Memory allocation failed"); return (ENOMEM); } memset(((char *)*newnode), 0, sizeof (hiernode)); strcpy(((*newnode)->dirname), dirname); return (PARSE_OK); } /* * free_hiernode(hiernode *node) * frees the allocated hiernode given the head of the structure * recursively calls itself until it frees entire structure. * Returns nothing. */ static void free_hiernode(hiernode *node) { hiernode *currnode = node; hiernode *prevnode = NULL; while (currnode != NULL) { if (currnode->subdir != NULL) free_hiernode(currnode->subdir); prevnode = currnode; currnode = currnode->leveldir; free((void*)prevnode); } } /* * free_mapent(struct mapent *) * free the mapentry and its fields */ void free_mapent(struct mapent *me) { struct mapfs *mfs; struct mapent *m; while (me) { while (me->map_fs) { mfs = me->map_fs; if (mfs->mfs_host) free(mfs->mfs_host); if (mfs->mfs_dir) free(mfs->mfs_dir); if (mfs->mfs_args) free(mfs->mfs_args); if (mfs->mfs_nconf) freenetconfigent(mfs->mfs_nconf); me->map_fs = mfs->mfs_next; free(mfs); } if (me->map_root) free(me->map_root); if (me->map_mntpnt) free(me->map_mntpnt); if (me->map_mntopts) free(me->map_mntopts); if (me->map_fstype) free(me->map_fstype); if (me->map_mounter) free(me->map_mounter); if (me->map_fsw) free(me->map_fsw); if (me->map_fswq) free(me->map_fswq); m = me; me = me->map_next; free(m); } } /* * trace_mapents(struct mapent *mapents) * traces through the mapentry structure and prints it element by element * returns nothing */ static void trace_mapents(char *s, struct mapent *mapents) { struct mapfs *mfs; struct mapent *me; trace_prt(1, "\n\t%s\n", s); for (me = mapents; me; me = me->map_next) { trace_prt(1, " (%s,%s)\t %s%s -%s\n", me->map_fstype ? me->map_fstype : "", me->map_mounter ? me->map_mounter : "", me->map_root ? me->map_root : "", me->map_mntpnt ? me->map_mntpnt : "", me->map_mntopts ? me->map_mntopts : ""); for (mfs = me->map_fs; mfs; mfs = mfs->mfs_next) trace_prt(0, "\t\t%s:%s\n", mfs->mfs_host ? mfs->mfs_host: "", mfs->mfs_dir ? mfs->mfs_dir : ""); trace_prt(1, "\tme->map_fsw=%s\n", me->map_fsw ? me->map_fsw:"", me->map_fswq ? me->map_fsw:""); trace_prt(1, "\t mntlevel=%d\t%s\t%s err=%d\n", me->map_mntlevel, me->map_modified ? "modify=TRUE":"modify=FALSE", me->map_faked ? "faked=TRUE":"faked=FALSE", me->map_err); } } /* * trace_hierarchy(hiernode *node) * traces the allocated hiernode given the head of the structure * recursively calls itself until it traces entire structure. * the first call made at the root is made with a zero level. * nodelevel is simply used to print tab and make the tracing clean. * Returns nothing. */ static void trace_hierarchy(hiernode *node, int nodelevel) { hiernode *currnode = node; int i; while (currnode != NULL) { if (currnode->subdir != NULL) { for (i = 0; i < nodelevel; i++) trace_prt(0, "\t"); trace_prt(0, "\t(%s, ", currnode->dirname); if (currnode->mapent) { trace_prt(0, "%d, %s)\n", currnode->mapent->map_mntlevel, currnode->mapent->map_mntopts ? currnode->mapent->map_mntopts:""); } else trace_prt(0, " ,)\n"); nodelevel++; trace_hierarchy(currnode->subdir, nodelevel); } else { for (i = 0; i < nodelevel; i++) trace_prt(0, "\t"); trace_prt(0, "\t(%s, ", currnode->dirname); if (currnode->mapent) { trace_prt(0, "%d, %s)\n", currnode->mapent->map_mntlevel, currnode->mapent->map_mntopts ? currnode->mapent->map_mntopts:""); } else trace_prt(0, ", )\n"); } currnode = currnode->leveldir; } } struct mapent * do_mapent_hosts(char *mapopts, char *host, uint_t isdirect) { CLIENT *cl; struct mapent *me, *ms, *mp; struct mapfs *mfs; struct exportnode *ex = NULL; struct exportnode *exlist, *texlist, **texp, *exnext; struct timeval timeout; enum clnt_stat clnt_stat; char name[MAXPATHLEN]; char entryopts[MAXOPTSLEN]; char fstype[32], mounter[32]; int exlen, duplicate; struct mnttab mb; /* needed for hasmntopt() to get nfs version */ rpcvers_t nfsvers; /* version in map options, 0 if not there */ rpcvers_t vers, versmin; /* used to negotiate nfs vers in pingnfs() */ int retries, delay; int foundvers; if (trace > 1) trace_prt(1, " do_mapent_hosts: host %s\n", host); /* check for special case: host is me */ if (self_check(host)) { ms = (struct mapent *)malloc(sizeof (*ms)); if (ms == NULL) goto alloc_failed; (void) memset((char *)ms, 0, sizeof (*ms)); (void) strcpy(fstype, MNTTYPE_NFS); get_opts(mapopts, entryopts, fstype, NULL); ms->map_mntopts = strdup(entryopts); if (ms->map_mntopts == NULL) goto alloc_failed; ms->map_mounter = strdup(fstype); if (ms->map_mounter == NULL) goto alloc_failed; ms->map_fstype = strdup(MNTTYPE_NFS); if (ms->map_fstype == NULL) goto alloc_failed; if (isdirect) name[0] = '\0'; else { (void) strcpy(name, "/"); (void) strcat(name, host); } ms->map_root = strdup(name); if (ms->map_root == NULL) goto alloc_failed; ms->map_mntpnt = strdup(""); if (ms->map_mntpnt == NULL) goto alloc_failed; mfs = (struct mapfs *)malloc(sizeof (*mfs)); if (mfs == NULL) goto alloc_failed; (void) memset((char *)mfs, 0, sizeof (*mfs)); ms->map_fs = mfs; mfs->mfs_host = strdup(host); if (mfs->mfs_host == NULL) goto alloc_failed; mfs->mfs_dir = strdup("/"); if (mfs->mfs_dir == NULL) goto alloc_failed; /* initialize mntlevel and modify */ ms->map_mntlevel = -1; ms->map_modified = FALSE; ms->map_faked = FALSE; if (trace > 1) trace_prt(1, " do_mapent_hosts: self-host %s OK\n", host); return (ms); } /* * Call pingnfs. Note that we can't have replicated hosts in /net. * XXX - we would like to avoid duplicating the across the wire calls * made here in nfsmount(). The pingnfs cache should help avoid it. */ mb.mnt_mntopts = mapopts; foundvers = nopt(&mb, MNTOPT_VERS, (int *)&nfsvers); if (!foundvers) nfsvers = 0; if (set_versrange(nfsvers, &vers, &versmin) != 0) { syslog(LOG_ERR, "Incorrect NFS version specified for %s", host); return (NULL); } if (pingnfs(host, get_retry(mapopts) + 1, &vers, versmin, 0, FALSE, NULL, NULL) != RPC_SUCCESS) return (NULL); retries = get_retry(mapopts); delay = INITDELAY; retry: /* get export list of host */ cl = clnt_create(host, MOUNTPROG, MOUNTVERS, "circuit_v"); if (cl == NULL) { cl = clnt_create(host, MOUNTPROG, MOUNTVERS, "datagram_v"); if (cl == NULL) { syslog(LOG_ERR, "do_mapent_hosts: %s %s", host, clnt_spcreateerror("")); return (NULL); } } #ifdef MALLOC_DEBUG add_alloc("CLNT_HANDLE", cl, 0, __FILE__, __LINE__); add_alloc("AUTH_HANDLE", cl->cl_auth, 0, __FILE__, __LINE__); #endif timeout.tv_usec = 0; timeout.tv_sec = 25; if (clnt_stat = clnt_call(cl, MOUNTPROC_EXPORT, xdr_void, 0, xdr_exports, (caddr_t)&ex, timeout)) { if (retries-- > 0) { clnt_destroy(cl); DELAY(delay); goto retry; } syslog(LOG_ERR, "do_mapent_hosts: %s: export list: %s", host, clnt_sperrno(clnt_stat)); #ifdef MALLOC_DEBUG drop_alloc("CLNT_HANDLE", cl, __FILE__, __LINE__); drop_alloc("AUTH_HANDLE", cl->cl_auth, __FILE__, __LINE__); #endif clnt_destroy(cl); return ((struct mapent *)NULL); } #ifdef MALLOC_DEBUG drop_alloc("CLNT_HANDLE", cl, __FILE__, __LINE__); drop_alloc("AUTH_HANDLE", cl->cl_auth, __FILE__, __LINE__); #endif clnt_destroy(cl); if (ex == NULL) { if (trace > 1) trace_prt(1, gettext(" getmapent_hosts: null export list\n")); return ((struct mapent *)NULL); } /* now sort by length of names - to get mount order right */ exlist = ex; texlist = NULL; for (; ex; ex = exnext) { exnext = ex->ex_next; exlen = strlen(ex->ex_dir); duplicate = 0; for (texp = &texlist; *texp; texp = &((*texp)->ex_next)) { if (exlen < (int)strlen((*texp)->ex_dir)) break; duplicate = (strcmp(ex->ex_dir, (*texp)->ex_dir) == 0); if (duplicate) { /* disregard duplicate entry */ freeex_ent(ex); break; } } if (!duplicate) { ex->ex_next = *texp; *texp = ex; } } exlist = texlist; (void) strcpy(fstype, MNTTYPE_NFS); get_opts(mapopts, entryopts, fstype, NULL); (void) strcpy(mounter, fstype); /* Now create a mapent from the export list */ ms = NULL; me = NULL; for (ex = exlist; ex; ex = ex->ex_next) { mp = me; me = (struct mapent *)malloc(sizeof (*me)); if (me == NULL) goto alloc_failed; (void) memset((char *)me, 0, sizeof (*me)); if (ms == NULL) ms = me; else mp->map_next = me; if (isdirect) name[0] = '\0'; else { (void) strcpy(name, "/"); (void) strcat(name, host); } me->map_root = strdup(name); if (me->map_root == NULL) goto alloc_failed; *name = '\0'; if (strcmp(ex->ex_dir, "/") != 0) { if (*(ex->ex_dir) != '/') (void) strcpy(name, "/"); (void) strcat(name, ex->ex_dir); } me->map_mntpnt = strdup(name); if (me->map_mntpnt == NULL) goto alloc_failed; me->map_fstype = strdup(fstype); if (me->map_fstype == NULL) goto alloc_failed; me->map_mounter = strdup(mounter); if (me->map_mounter == NULL) goto alloc_failed; me->map_mntopts = strdup(entryopts); if (me->map_mntopts == NULL) goto alloc_failed; mfs = (struct mapfs *)malloc(sizeof (*mfs)); if (mfs == NULL) goto alloc_failed; (void) memset((char *)mfs, 0, sizeof (*mfs)); me->map_fs = mfs; mfs->mfs_host = strdup(host); if (mfs->mfs_host == NULL) goto alloc_failed; mfs->mfs_dir = strdup(ex->ex_dir); if (mfs->mfs_dir == NULL) goto alloc_failed; /* initialize mntlevel and modify values */ me->map_mntlevel = -1; me->map_modified = FALSE; me->map_faked = FALSE; } freeex(exlist); if (trace > 1) trace_prt(1, " do_mapent_hosts: host %s OK\n", host); return (ms); alloc_failed: syslog(LOG_ERR, "do_mapent_hosts: Memory allocation failed"); free_mapent(ms); freeex(exlist); return (NULL); } static void freeex_ent(struct exportnode *ex) { struct groupnode *groups, *tmpgroups; free(ex->ex_dir); groups = ex->ex_groups; while (groups) { free(groups->gr_name); tmpgroups = groups->gr_next; free((char *)groups); groups = tmpgroups; } free(ex); } static void freeex(struct exportnode *ex) { struct exportnode *tmpex; while (ex) { tmpex = ex->ex_next; freeex_ent(ex); ex = tmpex; } } static const char uatfs_err[] = "submount under fstype=autofs not supported"; /* * dump_mapent_err(struct mapent *me, char *key, char *mapname) * syslog appropriate error in mapentries. */ static void dump_mapent_err(struct mapent *me, char *key, char *mapname) { switch (me->map_err) { case MAPENT_NOERR: if (verbose) syslog(LOG_ERR, "map=%s key=%s mntpnt=%s: no error"); break; case MAPENT_UATFS: syslog(LOG_ERR, "mountpoint %s in map %s key %s not mounted: %s", me->map_mntpnt, mapname, key, uatfs_err); break; default: if (verbose) syslog(LOG_ERR, "map=%s key=%s mntpnt=%s: unknown mapentry error"); } } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * autod_readdir.c */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "automount.h" static void build_dir_entry_list(struct autofs_rddir_cache *rdcp, struct dir_entry *list); static int autofs_rddir_cache_enter(char *map, ulong_t bucket_size, struct autofs_rddir_cache **rdcpp); int autofs_rddir_cache_lookup(char *map, struct autofs_rddir_cache **rdcpp); static int autofs_rddir_cache_delete(struct autofs_rddir_cache *rdcp); static int create_dirents(struct autofs_rddir_cache *rdcp, ulong_t offset, autofs_rddirres *res); struct dir_entry *rddir_entry_lookup(char *name, struct dir_entry *list); static void free_offset_tbl(struct off_tbl *head); static void free_dir_list(struct dir_entry *head); #define OFFSET_BUCKET_SIZE 100 rwlock_t autofs_rddir_cache_lock; /* readdir cache lock */ struct autofs_rddir_cache *rddir_head; /* readdir cache head */ int do_readdir(autofs_rddirargs *rda, autofs_rddirres *rd) { struct dir_entry *list = NULL, *l; struct autofs_rddir_cache *rdcp = NULL; int error; int cache_time = RDDIR_CACHE_TIME; if (automountd_nobrowse) { /* * Browsability was disabled return an empty list. */ rd->rd_status = AUTOFS_OK; rd->rd_rddir.rddir_size = 0; rd->rd_rddir.rddir_eof = 1; rd->rd_rddir.rddir_entries = NULL; return (0); } rw_rdlock(&autofs_rddir_cache_lock); error = autofs_rddir_cache_lookup(rda->rda_map, &rdcp); if (error) { rw_unlock(&autofs_rddir_cache_lock); rw_wrlock(&autofs_rddir_cache_lock); error = autofs_rddir_cache_lookup(rda->rda_map, &rdcp); if (error) { if (trace > 2) trace_prt(1, "map %s not found, adding...\n", rda->rda_map); /* * entry doesn't exist, add it. */ error = autofs_rddir_cache_enter(rda->rda_map, OFFSET_BUCKET_SIZE, &rdcp); } } rw_unlock(&autofs_rddir_cache_lock); if (error) return (error); assert(rdcp != NULL); assert(rdcp->in_use); if (!rdcp->full) { rw_wrlock(&rdcp->rwlock); if (!rdcp->full) { /* * cache entry hasn't been filled up, do it now. */ char *stack[STACKSIZ]; char **stkptr; /* * Initialize the stack of open files * for this thread */ stack_op(INIT, NULL, stack, &stkptr); (void) getmapkeys(rda->rda_map, &list, &error, &cache_time, stack, &stkptr, rda->uid); if (!error) build_dir_entry_list(rdcp, list); else if (list) { free_dir_list(list); list = NULL; } } } else rw_rdlock(&rdcp->rwlock); rd->rd_bufsize = rda->rda_count; if (!error) { error = create_dirents(rdcp, rda->rda_offset, rd); if (error) { if (rdcp->offtp) { free_offset_tbl(rdcp->offtp); rdcp->offtp = NULL; } if (rdcp->entp) { free_dir_list(rdcp->entp); rdcp->entp = NULL; } rdcp->full = 0; list = NULL; } } if (trace > 2) { /* * print this list only once */ for (l = list; l != NULL; l = l->next) trace_prt(0, "%s\n", l->name); trace_prt(0, "\n"); } if (!error) { rd->rd_status = AUTOFS_OK; if (cache_time) { /* * keep list of entries for up to * 'cache_time' seconds */ rdcp->ttl = time((time_t *)NULL) + cache_time; } else { /* * the underlying name service indicated not * to cache contents. */ if (rdcp->offtp) { free_offset_tbl(rdcp->offtp); rdcp->offtp = NULL; } if (rdcp->entp) { free_dir_list(rdcp->entp); rdcp->entp = NULL; } rdcp->full = 0; } } else { /* * return an empty list */ rd->rd_rddir.rddir_size = 0; rd->rd_rddir.rddir_eof = 1; rd->rd_rddir.rddir_entries = NULL; /* * Invalidate cache and set error */ switch (error) { case ENOENT: rd->rd_status = AUTOFS_NOENT; break; case ENOMEM: rd->rd_status = AUTOFS_NOMEM; break; default: rd->rd_status = AUTOFS_ECOMM; } } rw_unlock(&rdcp->rwlock); mutex_lock(&rdcp->lock); rdcp->in_use--; mutex_unlock(&rdcp->lock); assert(rdcp->in_use >= 0); return (error); } #define roundtoint(x) (((x) + sizeof (int) - 1) & ~(sizeof (int) - 1)) #define DIRENT64_RECLEN(namelen) \ (((int)(((dirent64_t *)0)->d_name) + 1 + (namelen) + 7) & ~ 7) static int create_dirents( struct autofs_rddir_cache *rdcp, ulong_t offset, autofs_rddirres *res) { uint_t total_bytes_wanted; int bufsize; ushort_t this_reclen; int outcount = 0; int namelen; struct dir_entry *list = NULL, *l, *nl; struct dirent64 *dp; char *outbuf; struct off_tbl *offtp, *next = NULL; int this_bucket = 0; int error = 0; int x = 0, y = 0; assert(RW_LOCK_HELD(&rdcp->rwlock)); for (offtp = rdcp->offtp; offtp != NULL; offtp = next) { x++; next = offtp->next; this_bucket = (next == NULL); if (!this_bucket) this_bucket = (offset < next->offset); if (this_bucket) { /* * has to be in this bucket */ assert(offset >= offtp->offset); list = offtp->first; break; } /* * loop to look in next bucket */ } for (l = list; l != NULL && l->offset < offset; l = l->next) y++; if (l == NULL) { /* * reached end of directory */ error = 0; goto empty; } if (trace > 2) trace_prt(1, "%s: offset searches (%d, %d)\n", rdcp->map, x, y); total_bytes_wanted = res->rd_bufsize; bufsize = total_bytes_wanted + sizeof (struct dirent64); outbuf = malloc(bufsize); if (outbuf == NULL) { syslog(LOG_ERR, "memory allocation error\n"); error = ENOMEM; goto empty; } memset(outbuf, 0, bufsize); /* LINTED pointer alignment */ dp = (struct dirent64 *)outbuf; while (l) { nl = l->next; namelen = strlen(l->name); this_reclen = DIRENT64_RECLEN(namelen); if (outcount + this_reclen > total_bytes_wanted) { break; } dp->d_ino = (ino64_t)l->nodeid; if (nl) { /* * get the next elements offset */ dp->d_off = (off64_t)nl->offset; } else { /* * This is the last element * make offset one plus the current. */ dp->d_off = (off64_t)l->offset + 1; } (void) strcpy(dp->d_name, l->name); dp->d_reclen = (ushort_t)this_reclen; outcount += dp->d_reclen; dp = (struct dirent64 *)((int)dp + dp->d_reclen); assert(outcount <= total_bytes_wanted); l = l->next; } res->rd_rddir.rddir_size = (long)outcount; if (outcount > 0) { /* * have some entries */ res->rd_rddir.rddir_eof = (l == NULL); /* LINTED pointer alignment */ res->rd_rddir.rddir_entries = (struct dirent64 *)outbuf; error = 0; } else { /* * total_bytes_wanted is not large enough for one * directory entry */ res->rd_rddir.rddir_eof = 0; res->rd_rddir.rddir_entries = NULL; free(outbuf); error = EIO; } return (error); empty: res->rd_rddir.rddir_size = 0L; res->rd_rddir.rddir_eof = TRUE; res->rd_rddir.rddir_entries = NULL; return (error); } /* * add new entry to cache for 'map' */ static int autofs_rddir_cache_enter( char *map, ulong_t bucket_size, struct autofs_rddir_cache **rdcpp) { struct autofs_rddir_cache *p; assert(RW_LOCK_HELD(&autofs_rddir_cache_lock)); /* * Add to front of the list at this time */ p = (struct autofs_rddir_cache *)malloc(sizeof (*p)); if (p == NULL) { syslog(LOG_ERR, "autofs_rddir_cache_enter: memory allocation failed\n"); return (ENOMEM); } memset((char *)p, 0, sizeof (*p)); p->map = malloc(strlen(map) + 1); if (p->map == NULL) { syslog(LOG_ERR, "autofs_rddir_cache_enter: memory allocation failed\n"); free(p); return (ENOMEM); } strcpy(p->map, map); p->bucket_size = bucket_size; /* * no need to grab mutex lock since I haven't yet made the * node visible to the list */ p->in_use = 1; (void) rwlock_init(&p->rwlock, USYNC_THREAD, NULL); (void) mutex_init(&p->lock, USYNC_THREAD, NULL); if (rddir_head == NULL) rddir_head = p; else { p->next = rddir_head; rddir_head = p; } *rdcpp = p; return (0); } /* * find 'map' in readdir cache */ int autofs_rddir_cache_lookup(char *map, struct autofs_rddir_cache **rdcpp) { struct autofs_rddir_cache *p; assert(RW_LOCK_HELD(&autofs_rddir_cache_lock)); for (p = rddir_head; p != NULL; p = p->next) { if (strcmp(p->map, map) == 0) { /* * found matching entry */ *rdcpp = p; mutex_lock(&p->lock); p->in_use++; mutex_unlock(&p->lock); return (0); } } /* * didn't find entry */ return (ENOENT); } /* * free the offset table */ static void free_offset_tbl(struct off_tbl *head) { struct off_tbl *p, *next = NULL; for (p = head; p != NULL; p = next) { next = p->next; free(p); } } /* * free the directory entries */ static void free_dir_list(struct dir_entry *head) { struct dir_entry *p, *next = NULL; for (p = head; p != NULL; p = next) { next = p->next; assert(p->name); free(p->name); free(p); } } static void autofs_rddir_cache_entry_free(struct autofs_rddir_cache *p) { assert(RW_LOCK_HELD(&autofs_rddir_cache_lock)); assert(!p->in_use); if (p->map) free(p->map); if (p->offtp) free_offset_tbl(p->offtp); if (p->entp) free_dir_list(p->entp); free(p); } /* * Remove entry from the rddircache * the caller must own the autofs_rddir_cache_lock. */ static int autofs_rddir_cache_delete(struct autofs_rddir_cache *rdcp) { struct autofs_rddir_cache *p, *prev; assert(RW_LOCK_HELD(&autofs_rddir_cache_lock)); /* * Search cache for entry */ prev = NULL; for (p = rddir_head; p != NULL; p = p->next) { if (p == rdcp) { /* * entry found, remove from list if not in use */ if (p->in_use) return (EBUSY); if (prev) prev->next = p->next; else rddir_head = p->next; autofs_rddir_cache_entry_free(p); return (0); } prev = p; } syslog(LOG_ERR, "Couldn't find entry %x in cache\n", p); return (ENOENT); } /* * Return entry that matches name, NULL otherwise. * Assumes the readers lock for this list has been grabed. */ struct dir_entry * rddir_entry_lookup(char *name, struct dir_entry *list) { return (btree_lookup(list, name)); } static void build_dir_entry_list(struct autofs_rddir_cache *rdcp, struct dir_entry *list) { struct dir_entry *p; ulong_t offset = AUTOFS_DAEMONCOOKIE, offset_list = AUTOFS_DAEMONCOOKIE; struct off_tbl *offtp, *last = NULL; ino_t inonum = 4; assert(RW_LOCK_HELD(&rdcp->rwlock)); assert(rdcp->entp == NULL); rdcp->entp = list; for (p = list; p != NULL; p = p->next) { p->nodeid = inonum; p->offset = offset; if (offset >= offset_list) { /* * add node to index table */ offtp = (struct off_tbl *) malloc(sizeof (struct off_tbl)); if (offtp != NULL) { offtp->offset = offset; offtp->first = p; offtp->next = NULL; offset_list += rdcp->bucket_size; } else { syslog(LOG_ERR, "WARNING: build_dir_entry_list: could not add offset to index table\n"); continue; } /* * add to cache */ if (rdcp->offtp == NULL) rdcp->offtp = offtp; else last->next = offtp; last = offtp; } offset++; inonum += 2; /* use even numbers in daemon */ } rdcp->full = 1; } mutex_t cleanup_lock; cond_t cleanup_start_cv; cond_t cleanup_done_cv; /* * cache cleanup thread starting point */ void cache_cleanup(void) { timestruc_t reltime; struct autofs_rddir_cache *p, *next = NULL; int error; mutex_init(&cleanup_lock, USYNC_THREAD, NULL); cond_init(&cleanup_start_cv, USYNC_THREAD, NULL); cond_init(&cleanup_done_cv, USYNC_THREAD, NULL); mutex_lock(&cleanup_lock); for (;;) { reltime.tv_sec = RDDIR_CACHE_TIME/2; reltime.tv_nsec = 0; /* * delay RDDIR_CACHE_TIME seconds, or until some other thread * requests that I cleanup the caches */ if (error = cond_reltimedwait( &cleanup_start_cv, &cleanup_lock, &reltime)) { if (error != ETIME) { if (trace > 1) trace_prt(1, "cleanup thread wakeup (%d)\n", error); continue; } } mutex_unlock(&cleanup_lock); /* * Perform the cache cleanup */ rw_wrlock(&autofs_rddir_cache_lock); for (p = rddir_head; p != NULL; p = next) { next = p->next; if (p->in_use > 0) { /* * cache entry busy, skip it */ if (trace > 1) { trace_prt(1, "%s cache in use\n", p->map); } continue; } /* * Cache entry is not in use, and nobody can grab a * new reference since I'm holding the * autofs_rddir_cache_lock */ /* * error will be zero if some thread signaled us asking * that the caches be freed. In such case, free caches * even if they're still valid and nobody is referencing * them at this time. Otherwise, free caches only * if their time to live (ttl) has expired. */ if (error == ETIME && (p->ttl > time((time_t *)NULL))) { /* * Scheduled cache cleanup, if cache is still * valid don't free. */ if (trace > 1) { trace_prt(1, "%s cache still valid\n", p->map); } continue; } if (trace > 1) trace_prt(1, "%s freeing cache\n", p->map); assert(!p->in_use); error = autofs_rddir_cache_delete(p); assert(!error); } rw_unlock(&autofs_rddir_cache_lock); /* * wakeup the thread/threads waiting for the * cleanup to finish */ mutex_lock(&cleanup_lock); cond_broadcast(&cleanup_done_cv); } /* NOTREACHED */ } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * autod_xdr.c * * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * This file can not be automatically generated by rpcgen from * autofs_prot.x because of the xdr routines that provide readdir * support, and my own implementation of xdr_autofs_netbuf(). */ #include #include /* includes roundup() */ #include #include #include #include #include #include #include bool_t xdr_autofs_stat(register XDR *xdrs, autofs_stat *objp) { if (!xdr_enum(xdrs, (enum_t *)objp)) return (FALSE); return (TRUE); } bool_t xdr_autofs_action(register XDR *xdrs, autofs_action *objp) { if (!xdr_enum(xdrs, (enum_t *)objp)) return (FALSE); return (TRUE); } bool_t xdr_linka(register XDR *xdrs, linka *objp) { if (!xdr_string(xdrs, &objp->dir, AUTOFS_MAXPATHLEN)) return (FALSE); if (!xdr_string(xdrs, &objp->link, AUTOFS_MAXPATHLEN)) return (FALSE); return (TRUE); } bool_t xdr_autofs_netbuf(xdrs, objp) XDR *xdrs; struct netbuf *objp; { bool_t dummy; if (!xdr_u_int(xdrs, &objp->maxlen)) { return (FALSE); } dummy = xdr_bytes(xdrs, (char **)&(objp->buf), (uint_t *)&(objp->len), objp->maxlen); return (dummy); } bool_t xdr_autofs_args(register XDR *xdrs, autofs_args *objp) { if (!xdr_autofs_netbuf(xdrs, &objp->addr)) return (FALSE); if (!xdr_string(xdrs, &objp->path, AUTOFS_MAXPATHLEN)) return (FALSE); if (!xdr_string(xdrs, &objp->opts, AUTOFS_MAXOPTSLEN)) return (FALSE); if (!xdr_string(xdrs, &objp->map, AUTOFS_MAXPATHLEN)) return (FALSE); if (!xdr_string(xdrs, &objp->subdir, AUTOFS_MAXPATHLEN)) return (FALSE); if (!xdr_string(xdrs, &objp->key, AUTOFS_MAXCOMPONENTLEN)) return (FALSE); if (!xdr_int(xdrs, &objp->mount_to)) return (FALSE); if (!xdr_int(xdrs, &objp->rpc_to)) return (FALSE); if (!xdr_int(xdrs, &objp->direct)) return (FALSE); return (TRUE); } bool_t xdr_mounta(register XDR *xdrs, struct mounta *objp) { if (!xdr_string(xdrs, &objp->spec, AUTOFS_MAXPATHLEN)) return (FALSE); if (!xdr_string(xdrs, &objp->dir, AUTOFS_MAXPATHLEN)) return (FALSE); if (!xdr_int(xdrs, &objp->flags)) return (FALSE); if (!xdr_string(xdrs, &objp->fstype, AUTOFS_MAXCOMPONENTLEN)) return (FALSE); if (!xdr_pointer(xdrs, (char **)&objp->dataptr, sizeof (autofs_args), (xdrproc_t)xdr_autofs_args)) return (FALSE); if (!xdr_int(xdrs, &objp->datalen)) return (FALSE); if (!xdr_string(xdrs, &objp->optptr, AUTOFS_MAXOPTSLEN)) return (FALSE); if (!xdr_int(xdrs, &objp->optlen)) return (FALSE); return (TRUE); } bool_t xdr_action_list_entry(register XDR *xdrs, action_list_entry *objp) { if (!xdr_autofs_action(xdrs, &objp->action)) return (FALSE); switch (objp->action) { case AUTOFS_MOUNT_RQ: if (!xdr_mounta(xdrs, &objp->action_list_entry_u.mounta)) return (FALSE); break; case AUTOFS_LINK_RQ: if (!xdr_linka(xdrs, &objp->action_list_entry_u.linka)) return (FALSE); break; } return (TRUE); } bool_t xdr_action_list(XDR *xdrs, action_list *objp) { action_list *tmp_action_list; bool_t more_data = TRUE; bool_t first_objp = TRUE; if (xdrs->x_op == XDR_DECODE) { while (more_data) { if (!xdr_action_list_entry(xdrs, &objp->action)) return (FALSE); if (!xdr_bool(xdrs, &more_data)) return (FALSE); if (!more_data) { objp->next = NULL; break; } if (objp->next == NULL) { objp->next = (action_list *) mem_alloc(sizeof (action_list)); if (objp->next == NULL) return (FALSE); bzero(objp->next, sizeof (action_list)); } objp = objp->next; } } else if (xdrs->x_op == XDR_ENCODE) { while (more_data) { if (!xdr_action_list_entry(xdrs, &objp->action)) return (FALSE); objp = objp->next; if (objp == NULL) more_data = FALSE; if (!xdr_bool(xdrs, &more_data)) return (FALSE); } } else { while (more_data) { if (!xdr_action_list_entry(xdrs, &objp->action)) return (FALSE); tmp_action_list = objp; objp = objp->next; if (objp == NULL) more_data = FALSE; if (!first_objp) mem_free(tmp_action_list, sizeof (action_list)); else first_objp = FALSE; } } return (TRUE); } bool_t xdr_umntrequest(register XDR *xdrs, umntrequest *objp) { if (!xdr_bool_t(xdrs, &objp->isdirect)) return (FALSE); if (!xdr_string(xdrs, &objp->mntresource, AUTOFS_MAXPATHLEN)) return (FALSE); if (!xdr_string(xdrs, &objp->mntpnt, AUTOFS_MAXPATHLEN)) return (FALSE); if (!xdr_string(xdrs, &objp->fstype, AUTOFS_MAXCOMPONENTLEN)) return (FALSE); if (!xdr_string(xdrs, &objp->mntopts, AUTOFS_MAXOPTSLEN)) return (FALSE); if (!xdr_pointer(xdrs, (char **)&objp->next, sizeof (umntrequest), (xdrproc_t)xdr_umntrequest)) return (FALSE); return (TRUE); } bool_t xdr_umntres(register XDR *xdrs, umntres *objp) { if (!xdr_int(xdrs, &objp->status)) return (FALSE); return (TRUE); } bool_t xdr_autofs_res(xdrs, objp) register XDR *xdrs; autofs_res *objp; { if (!xdr_enum(xdrs, (enum_t *)objp)) return (FALSE); return (TRUE); } bool_t xdr_autofs_lookupargs(xdrs, objp) register XDR *xdrs; autofs_lookupargs *objp; { if (!xdr_string(xdrs, &objp->map, AUTOFS_MAXPATHLEN)) return (FALSE); if (!xdr_string(xdrs, &objp->path, AUTOFS_MAXPATHLEN)) return (FALSE); if (!xdr_string(xdrs, &objp->name, AUTOFS_MAXCOMPONENTLEN)) return (FALSE); if (!xdr_string(xdrs, &objp->subdir, AUTOFS_MAXPATHLEN)) return (FALSE); if (!xdr_string(xdrs, &objp->opts, AUTOFS_MAXOPTSLEN)) return (FALSE); if (!xdr_bool_t(xdrs, &objp->isdirect)) return (FALSE); if (!xdr_u_int(xdrs, (uint_t *)&objp->uid)) return (FALSE); return (TRUE); } bool_t xdr_mount_result_type(xdrs, objp) register XDR *xdrs; mount_result_type *objp; { if (!xdr_autofs_stat(xdrs, &objp->status)) return (FALSE); switch (objp->status) { case AUTOFS_ACTION: if (!xdr_pointer(xdrs, (char **)&objp->mount_result_type_u.list, sizeof (action_list), (xdrproc_t)xdr_action_list)) return (FALSE); break; case AUTOFS_DONE: if (!xdr_int(xdrs, &objp->mount_result_type_u.error)) return (FALSE); break; } return (TRUE); } bool_t xdr_autofs_mountres(xdrs, objp) register XDR *xdrs; autofs_mountres *objp; { if (!xdr_mount_result_type(xdrs, &objp->mr_type)) return (FALSE); if (!xdr_int(xdrs, &objp->mr_verbose)) return (FALSE); return (TRUE); } bool_t xdr_lookup_result_type(xdrs, objp) register XDR *xdrs; lookup_result_type *objp; { if (!xdr_autofs_action(xdrs, &objp->action)) return (FALSE); switch (objp->action) { case AUTOFS_LINK_RQ: if (!xdr_linka(xdrs, &objp->lookup_result_type_u.lt_linka)) return (FALSE); break; } return (TRUE); } bool_t xdr_autofs_lookupres(xdrs, objp) register XDR *xdrs; autofs_lookupres *objp; { if (!xdr_autofs_res(xdrs, &objp->lu_res)) return (FALSE); if (!xdr_lookup_result_type(xdrs, &objp->lu_type)) return (FALSE); if (!xdr_int(xdrs, &objp->lu_verbose)) return (FALSE); return (TRUE); } /* * ****************************************************** * Readdir XDR support * ****************************************************** */ bool_t xdr_autofs_rddirargs(xdrs, objp) register XDR *xdrs; autofs_rddirargs *objp; { if (!xdr_string(xdrs, &objp->rda_map, AUTOFS_MAXPATHLEN)) return (FALSE); if (!xdr_u_int(xdrs, &objp->rda_offset)) return (FALSE); if (!xdr_u_int(xdrs, &objp->rda_count)) return (FALSE); if (!xdr_u_int(xdrs, (uint_t *)&objp->uid)) return (FALSE); return (TRUE); } /* * Directory read reply: * union (enum autofs_res) { * AUTOFS_OK: entlist; * boolean eof; * default: * } * * Directory entries * struct direct { * off_t d_off; * offset of next entry * * u_long d_fileno; * inode number of entry * * u_short d_reclen; * length of this record * * u_short d_namlen; * length of string in d_name * * char d_name[MAXNAMLEN + 1]; * name no longer than this * * }; * are on the wire as: * union entlist (boolean valid) { * TRUE: struct otw_dirent; * u_long nxtoffset; * union entlist; * FALSE: * } * where otw_dirent is: * struct dirent { * u_long de_fid; * string de_name; * } */ #ifdef nextdp #undef nextdp #endif #define nextdp(dp) ((struct dirent64 *)((char *)(dp) + (dp)->d_reclen)) /* * ENCODE ONLY */ bool_t xdr_autofs_putrddirres(xdrs, rddir, reqsize) XDR *xdrs; struct autofsrddir *rddir; uint_t reqsize; /* requested size */ { struct dirent64 *dp; char *name; int size; uint_t namlen; bool_t true = TRUE; bool_t false = FALSE; int entrysz; int tofit; int bufsize; uint_t ino, off; bufsize = 1 * BYTES_PER_XDR_UNIT; for (size = rddir->rddir_size, dp = rddir->rddir_entries; size > 0; /* LINTED pointer alignment */ size -= dp->d_reclen, dp = nextdp(dp)) { if (dp->d_reclen == 0 /* || DIRSIZ(dp) > dp->d_reclen */) { return (FALSE); } if (dp->d_ino == 0) { continue; } name = dp->d_name; namlen = strlen(name); ino = (uint_t)dp->d_ino; off = (uint_t)dp->d_off; entrysz = (1 + 1 + 1 + 1) * BYTES_PER_XDR_UNIT + roundup(namlen, BYTES_PER_XDR_UNIT); tofit = entrysz + 2 * BYTES_PER_XDR_UNIT; if (bufsize + tofit > reqsize) { rddir->rddir_eof = FALSE; break; } if (!xdr_bool(xdrs, &true) || !xdr_u_int(xdrs, &ino) || !xdr_bytes(xdrs, &name, &namlen, AUTOFS_MAXPATHLEN) || !xdr_u_int(xdrs, &off)) { return (FALSE); } bufsize += entrysz; } if (!xdr_bool(xdrs, &false)) { return (FALSE); } if (!xdr_bool(xdrs, &rddir->rddir_eof)) { return (FALSE); } return (TRUE); } #define DIRENT64_RECLEN(namelen) \ (((int)(((dirent64_t *)0)->d_name) + 1 + (namelen) + 7) & ~ 7) #define reclen(namlen) DIRENT64_RECLEN((namlen)) /* * DECODE ONLY */ bool_t xdr_autofs_getrddirres(xdrs, rddir) XDR *xdrs; struct autofsrddir *rddir; { struct dirent64 *dp; uint_t namlen; int size; bool_t valid; int offset = -1; uint_t fileid; size = rddir->rddir_size; dp = rddir->rddir_entries; for (;;) { if (!xdr_bool(xdrs, &valid)) { return (FALSE); } if (!valid) { break; } if (!xdr_u_int(xdrs, &fileid) || !xdr_u_int(xdrs, &namlen)) { return (FALSE); } if (reclen(namlen) > size) { rddir->rddir_eof = FALSE; goto bufovflw; } if (!xdr_opaque(xdrs, dp->d_name, namlen)|| !xdr_int(xdrs, &offset)) { return (FALSE); } dp->d_ino = fileid; dp->d_reclen = reclen(namlen); dp->d_name[namlen] = '\0'; dp->d_off = offset; size -= dp->d_reclen; /* LINTED pointer alignment */ dp = nextdp(dp); } if (!xdr_bool(xdrs, &rddir->rddir_eof)) { return (FALSE); } bufovflw: rddir->rddir_size = (char *)dp - (char *)(rddir->rddir_entries); rddir->rddir_offset = offset; return (TRUE); } bool_t xdr_autofs_rddirres(register XDR *xdrs, autofs_rddirres *objp) { if (!xdr_enum(xdrs, (enum_t *)&objp->rd_status)) return (FALSE); if (objp->rd_status != AUTOFS_OK) return (TRUE); if (xdrs->x_op == XDR_ENCODE) return (xdr_autofs_putrddirres( xdrs, (struct autofsrddir *)&objp->rd_rddir, objp->rd_bufsize)); else if (xdrs->x_op == XDR_DECODE) return (xdr_autofs_getrddirres(xdrs, (struct autofsrddir *)&objp->rd_rddir)); else return (FALSE); } # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. # # # Moved to SMF. Use sharectl(8) to manage AUTOFS properties. # /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * automount.c * * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "automount.h" #include "smfcfg.h" static int mkdir_r(char *); struct autodir *dir_head; struct autodir *dir_tail; static struct extmnttab *find_mount(); int verbose = 0; int trace = 0; static void usage(); static int compare_opts(char *, char *); static void do_unmounts(); static int mount_timeout = AUTOFS_MOUNT_TIMEOUT; static char *service_list[] = { AUTOMOUNTD, NULL }; /* * XXX * The following are needed because they're used in auto_subr.c and * we link with it. Should avoid this. */ mutex_t cleanup_lock; cond_t cleanup_start_cv; cond_t cleanup_done_cv; int main(int argc, char *argv[]) { int c; struct autofs_args ai; struct utsname utsname; char autofs_addr[MAXADDRLEN]; struct autodir *dir, *d; struct stat stbuf; char *master_map = "auto_master"; int null; struct extmnttab mnt, *mntp; struct mnttab *omntp; char mntopts[MAX_MNTOPT_STR]; int mntflgs; int count = 0; char *stack[STACKSIZ]; char **stkptr; char *defval; struct sigaction sigintact; int ret = 0, bufsz = 0; char valbuf[6]; /* * protect this command from session termination when run in background * we test background by whether SIGINT is ignored */ (void) sigaction(SIGINT, NULL, &sigintact); if (sigintact.sa_handler == SIG_IGN) { (void) signal(SIGHUP, SIG_IGN); (void) setsid(); } /* * Read in the values from SMF first before we check * commandline options so the options override the SMF values. */ bufsz = 6; ret = autofs_smf_get_prop("timeout", valbuf, DEFAULT_INSTANCE, SCF_TYPE_INTEGER, AUTOMOUNTD, &bufsz); if (ret == SA_OK) /* * Ignore errno. In event of failure, mount_timeout is * already initialized to the correct value. */ mount_timeout = strtol(valbuf, (char **)NULL, 10); bufsz = 6; ret = autofs_smf_get_prop("automount_verbose", valbuf, DEFAULT_INSTANCE, SCF_TYPE_BOOLEAN, AUTOMOUNTD, &bufsz); if (ret == SA_OK) { if (strncasecmp("true", valbuf, 4) == 0) verbose = TRUE; } put_automountd_env(); while ((c = getopt(argc, argv, "mM:D:f:t:v?")) != EOF) { switch (c) { case 'm': pr_msg("Warning: -m option not supported"); break; case 'M': pr_msg("Warning: -M option not supported"); break; case 'D': pr_msg("Warning: -D option not supported"); break; case 'f': pr_msg("Error: -f option no longer supported"); usage(); break; case 't': if (strchr(optarg, '=')) { pr_msg("Error: invalid value for -t"); usage(); } mount_timeout = atoi(optarg); break; case 'v': verbose++; break; default: usage(); break; } } if (optind < argc) { pr_msg("%s: command line mountpoints/maps " "no longer supported", argv[optind]); usage(); } current_mounts = getmntlist(); if (current_mounts == NULL) { pr_msg("Couldn't establish current mounts"); exit(1); } (void) umask(0); ns_setup(stack, &stkptr); openlog("automount", LOG_PID, LOG_DAEMON); (void) loadmaster_map(master_map, "", stack, &stkptr); if (dir_head != NULL) { /* * automount maps found. enable services as needed. */ _check_services(service_list); } closelog(); if (uname(&utsname) < 0) { pr_msg("uname: %m"); exit(1); } (void) strcpy(autofs_addr, utsname.nodename); (void) strcat(autofs_addr, ".autofs"); ai.addr.buf = autofs_addr; ai.addr.len = strlen(ai.addr.buf); ai.addr.maxlen = ai.addr.len; ai.mount_to = mount_timeout; ai.rpc_to = AUTOFS_RPC_TIMEOUT; /* * Mount the daemon at its mount points. */ for (dir = dir_head; dir; dir = dir->dir_next) { /* * Skip null entries */ if (strcmp(dir->dir_map, "-null") == 0) continue; /* * Skip null'ed entries */ null = 0; for (d = dir->dir_prev; d; d = d->dir_prev) { if (strcmp(dir->dir_name, d->dir_name) == 0) null = 1; } if (null) continue; /* * Check whether there's already an entry * in the mnttab for this mountpoint. */ if (mntp = find_mount(dir->dir_name, 1)) { /* * If it's not an autofs mount - don't * mount over it. */ if (strcmp(mntp->mnt_fstype, MNTTYPE_AUTOFS) != 0) { pr_msg("%s: already mounted", mntp->mnt_mountp); continue; } /* * Compare the mnttab entry with the master map * entry. If the map or mount options are * different, then update this information * with a remount. */ if (strcmp(mntp->mnt_special, dir->dir_map) == 0 && compare_opts(dir->dir_opts, mntp->mnt_mntopts) == 0) { continue; /* no change */ } /* * Check for an overlaid direct autofs mount. * Cannot remount since it's inaccessible. */ omntp = (struct mnttab *)mntp; if (hasmntopt(omntp, "direct") != NULL) { mntp = find_mount(dir->dir_name, 0); omntp = (struct mnttab *)mntp; if (hasmntopt(omntp, "direct") == NULL) { if (verbose) pr_msg("%s: cannot remount", dir->dir_name); continue; } } dir->dir_remount = 1; } /* * Create a mount point if necessary * If the path refers to an existing symbolic * link, refuse to mount on it. This avoids * future problems. */ if (lstat(dir->dir_name, &stbuf) == 0) { if ((stbuf.st_mode & S_IFMT) != S_IFDIR) { pr_msg("%s: Not a directory", dir->dir_name); continue; } } else { if (mkdir_r(dir->dir_name)) { pr_msg("%s: %m", dir->dir_name); continue; } } ai.path = dir->dir_name; ai.opts = dir->dir_opts; ai.map = dir->dir_map; ai.subdir = ""; ai.direct = dir->dir_direct; if (dir->dir_direct) ai.key = dir->dir_name; else ai.key = ""; (void) sprintf(mntopts, "ignore,%s", dir->dir_direct ? "direct" : "indirect"); if (dir->dir_opts && *dir->dir_opts) { (void) strcat(mntopts, ","); (void) strcat(mntopts, dir->dir_opts); } mntflgs = MS_OPTIONSTR | (dir->dir_remount ? MS_REMOUNT : 0); if (mount(dir->dir_map, dir->dir_name, MS_DATA | mntflgs, MNTTYPE_AUTOFS, &ai, sizeof (ai), mntopts, MAX_MNTOPT_STR) < 0) { pr_msg("mount %s: %m", dir->dir_name); continue; } count++; if (verbose) { if (dir->dir_remount) pr_msg("%s remounted", dir->dir_name); else pr_msg("%s mounted", dir->dir_name); } } if (verbose && count == 0) pr_msg("no mounts"); /* * Now compare the /etc/mnttab with the master * map. Any autofs mounts in the /etc/mnttab * that are not in the master map must be * unmounted */ do_unmounts(); return (0); } /* * Find a mount entry given * the mountpoint path. * Optionally return the first * or last entry. */ static struct extmnttab * find_mount(mntpnt, first) char *mntpnt; int first; { struct mntlist *mntl; struct extmnttab *found = NULL; for (mntl = current_mounts; mntl; mntl = mntl->mntl_next) { if (strcmp(mntpnt, mntl->mntl_mnt->mnt_mountp) == 0) { found = mntl->mntl_mnt; if (first) break; } } return (found); } static char *ignore_opts[] = {"ignore", "direct", "indirect", "dev", NULL}; /* * Compare mount options * ignoring "ignore", "direct", "indirect" * and "dev=". */ static int compare_opts(opts, mntopts) char *opts, *mntopts; { char optbuf1[MAX_MNTOPT_STR], *s = optbuf1; char optbuf2[MAX_MNTOPT_STR]; char **opttbl1, **opttbl2; int nopts1, nopts2; char *ostart, *optr, *valp; int j, i, notsame; opttbl1 = opttbl2 = NULL; /* * Parse the two option strings to split them both into * lists of individual options. */ if (mntopts != NULL) (void) strcpy(s, mntopts); else *s = '\0'; if (*s != '\0') nopts1 = 1; else nopts1 = 0; for (s = strchr(s, ','); s != NULL; s = strchr(s, ',')) { nopts1++; s++; } if (nopts1) if ((opttbl1 = memalign(sizeof (char *), nopts1 * sizeof (char *))) == NULL) return (1); nopts1 = 0; s = optbuf1; for (ostart = optr = s; *optr != '\0'; ostart = optr) { if (getsubopt(&optr, ignore_opts, &valp) == -1) { opttbl1[nopts1++] = ostart; } } s = optbuf2; if (opts != NULL) (void) strcpy(s, opts); else *s = '\0'; if (*s != '\0') nopts2 = 1; else nopts2 = 0; for (s = strchr(s, ','); s != NULL; s = strchr(s, ',')) { nopts2++; s++; } if (nopts2) if ((opttbl2 = memalign(sizeof (char *), nopts2 * sizeof (char *))) == NULL) { notsame = 1; goto done; } nopts2 = 0; s = optbuf2; for (ostart = optr = s; *optr != '\0'; ostart = optr) { if (getsubopt(&optr, ignore_opts, &valp) == -1) { opttbl2[nopts2++] = ostart; } } if (nopts2 != nopts1) { notsame = 1; goto done; } notsame = 0; for (i = 0; i < nopts1; i++) { notsame = 1; for (j = 0; j < nopts2; j++) { if (strcmp(opttbl1[i], opttbl2[j]) == 0) { notsame = 0; break; } } if (notsame) break; } done: if (opttbl1 != NULL) free(opttbl1); if (opttbl2 != NULL) free(opttbl2); return (notsame); } static void usage() { pr_msg("Usage: automount [ -v ] [ -t duration ]"); exit(1); /* NOTREACHED */ } /* * Unmount any autofs mounts that * aren't in the master map */ static void do_unmounts() { struct mntlist *mntl; struct extmnttab *mnt; struct mnttab *omnt; struct autodir *dir; int current; int count = 0; struct zone_summary *zsp; zsp = fs_get_zone_summaries(); if (zsp == NULL) { pr_msg("Couldn't establish active zones"); exit(1); } for (mntl = current_mounts; mntl; mntl = mntl->mntl_next) { mnt = mntl->mntl_mnt; omnt = (struct mnttab *)mnt; if (strcmp(mnt->mnt_fstype, MNTTYPE_AUTOFS) != 0) continue; if (fs_mount_in_other_zone(zsp, mnt->mnt_mountp)) continue; /* * Don't unmount autofs mounts done * from the autofs mount command. * How do we tell them apart ? * Autofs mounts not eligible for auto-unmount * have the "nest" pseudo-option. */ if (hasmntopt(omnt, "nest") != NULL) continue; current = 0; for (dir = dir_head; dir; dir = dir->dir_next) { if (strcmp(dir->dir_name, mnt->mnt_mountp) == 0) { current = strcmp(dir->dir_map, "-null"); break; } } if (current) continue; if (umount(mnt->mnt_mountp) == 0) { if (verbose) { pr_msg("%s unmounted", mnt->mnt_mountp); } count++; } } if (verbose && count == 0) pr_msg("no unmounts"); } static int mkdir_r(dir) char *dir; { int err; char *slash; if (mkdir(dir, 0555) == 0 || errno == EEXIST) return (0); if (errno != ENOENT) return (-1); slash = strrchr(dir, '/'); if (slash == NULL) return (-1); *slash = '\0'; err = mkdir_r(dir); *slash++ = '/'; if (err || !*slash) return (err); return (mkdir(dir, 0555)); } /* * Print an error. * Works like printf (fmt string and variable args) * except that it will subsititute an error message * for a "%m" string (like syslog). */ /* VARARGS1 */ void pr_msg(const char *fmt, ...) { va_list ap; char buf[BUFSIZ], *p2; char *p1; char *nfmt; (void) strcpy(buf, "automount: "); p2 = buf + strlen(buf); nfmt = gettext(fmt); for (p1 = nfmt; *p1; p1++) { if (*p1 == '%' && *(p1+1) == 'm') { (void) strcpy(p2, strerror(errno)); p2 += strlen(p2); p1++; } else { *p2++ = *p1; } } if (p2 > buf && *(p2-1) != '\n') *p2++ = '\n'; *p2 = '\0'; va_start(ap, fmt); (void) vfprintf(stderr, buf, ap); va_end(ap); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2015 Nexenta Systems, Inc. All rights reserved. */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _AUTOMOUNT_H #define _AUTOMOUNT_H #include /* needed for mntlist_t declaration */ #include #include /* " " MNTTYPE_* declarations */ #include /* needed for mutex_t declaration */ #include #include #include #include /* needed for sockaddr_in declaration */ #include #ifdef MALLOC_DEBUG #include #endif #ifdef __cplusplus extern "C" { #endif #ifndef _REENTRANT #define fork1 vfork #define rpc_control(a, b) 1 #endif #define DOMOUNT_USER 1 #define DOMOUNT_KERNEL 2 /* * Solaris autofs configuration file location */ #define AUTOFSADMIN "/etc/default/autofs" #define MXHOSTNAMELEN 64 #define MAXNETNAMELEN 255 #define MAXFILENAMELEN 255 #define LINESZ 4096 #define MAXADDRLEN 128 /* max autofs address length */ #define MAXOPTSLEN 1024 #define AUTOFS_MOUNT_TIMEOUT 600 /* default min time mount will */ /* remain mounted (in seconds) */ #define AUTOFS_RPC_TIMEOUT 60 /* secs autofs will wait for */ /* automountd's reply before */ /* retransmitting */ /* stack ops */ #define ERASE 0 #define PUSH 1 #define POP 2 #define INIT 3 #define STACKSIZ 30 #define DIST_SELF 1 #define DIST_MYSUB 2 #define DIST_MYNET 3 #define DIST_OTHER 4 #define MAXIFS 32 /* * Retry operation related definitions. */ #define RET_OK 0 #define RET_RETRY 32 #define RET_ERR 33 #define INITDELAY 5 #define DELAY_BACKOFF 2 #define MAXDELAY 120 #define ARGV_MAX 16 #define VFS_PATH "/usr/lib/fs" #define DELAY(delay) { \ (void) sleep(delay); \ delay *= DELAY_BACKOFF; \ if (delay > MAXDELAY) \ delay = MAXDELAY; \ } struct mapline { char linebuf[LINESZ]; char lineqbuf[LINESZ]; }; /* * Structure describing a host/filesystem/dir tuple in a NIS map entry */ struct mapfs { struct mapfs *mfs_next; /* next in entry */ int mfs_ignore; /* ignore this entry */ char *mfs_host; /* host name */ char *mfs_dir; /* dir to mount */ int mfs_penalty; /* mount penalty for this host */ int mfs_distance; /* distance hint */ struct nfs_args *mfs_args; /* nfs_args */ struct netconfig *mfs_nconf; rpcvers_t mfs_version; /* NFS version */ #define MFS_ALLOC_DIR 0x1 /* mfs_dir now points to different */ /* buffer */ #define MFS_URL 0x2 /* is NFS url listed in this tuple. */ #define MFS_FH_VIA_WEBNFS 0x4 /* got file handle during ping phase */ uint_t mfs_flags; uint_t mfs_port; /* port# in NFS url */ }; /* * NIS entry - lookup of name in DIR gets us this */ struct mapent { char *map_fstype; /* file system type e.g. "nfs" */ char *map_mounter; /* base fs */ char *map_root; /* path to mount root */ char *map_mntpnt; /* path from mount root */ char *map_mntopts; /* mount options */ char *map_fsw; /* mount fs information */ char *map_fswq; /* quoted mountfs information */ int map_mntlevel; /* mapentry hierarchy level */ bool_t map_modified; /* flags modified mapentries */ bool_t map_faked; /* flags faked mapentries */ int map_err; /* flags any bad entries in the map */ struct mapfs *map_fs; /* list of replicas for nfs */ struct mapent *map_next; }; /* * Descriptor for each directory served by the automounter */ struct autodir { char *dir_name; /* mount point */ char *dir_map; /* name of map for dir */ char *dir_opts; /* default mount options */ int dir_direct; /* direct mountpoint ? */ int dir_remount; /* a remount */ struct autodir *dir_next; /* next entry */ struct autodir *dir_prev; /* prev entry */ }; /* * This structure is used to build an array of * hostnames with associated penalties to be * passed to the nfs_cast procedure */ struct host_names { char *host; int penalty; }; /* * structure used to build list of contents for a map on * a readdir request */ struct dir_entry { char *name; /* name of entry */ ino_t nodeid; off_t offset; struct dir_entry *next; struct dir_entry *left; /* left element in binary tree */ struct dir_entry *right; /* right element in binary tree */ }; /* * offset index table */ struct off_tbl { off_t offset; struct dir_entry *first; struct off_tbl *next; }; #ifndef NO_RDDIR_CACHE /* * directory cache for 'map' */ struct autofs_rddir_cache { char *map; struct off_tbl *offtp; ulong_t bucket_size; time_t ttl; struct dir_entry *entp; mutex_t lock; /* protects 'in_use' field */ int in_use; /* # threads referencing it */ rwlock_t rwlock; /* protects 'full' and 'next' */ int full; /* full == 1 when cache full */ struct autofs_rddir_cache *next; }; #define RDDIR_CACHE_TIME 300 /* in seconds */ #endif /* NO_RDDIR_CACHE */ /* * structure used to maintain address list for localhost */ struct myaddrs { struct sockaddr_in sin; struct myaddrs *myaddrs_next; }; /* * structure used to pass commands to the door servers */ typedef struct command { char file[MAXPATHLEN]; char argv[ARGV_MAX][MAXOPTSLEN]; char key[MAXOPTSLEN]; int console; } command_t; /* * globally visible door_server file descriptor */ extern int did_exec_map; extern int did_fork_exec; extern time_t timenow; /* set at start of processing of each RPC call */ extern char self[]; extern int verbose; extern int trace; extern int automountd_nobrowse; extern struct autodir *dir_head; extern struct autodir *dir_tail; extern struct mntlist *current_mounts; struct mounta; /* defined in sys/vfs.h */ extern struct myaddrs *myaddrs_head; extern rwlock_t cache_lock; extern rwlock_t portmap_cache_lock; extern rwlock_t autofs_rddir_cache_lock; extern mutex_t cleanup_lock; extern cond_t cleanup_start_cv; extern cond_t cleanup_done_cv; /* * mnttab handling routines */ extern void free_mapent(struct mapent *); extern struct mntlist *getmntlist(void); extern dev_t get_devid(struct extmnttab *); /* * utilities */ extern struct mapent *parse_entry(char *, char *, char *, struct mapline *, char *, uint_t, bool_t); extern int macro_expand(char *, char *, char *, int); extern void unquote(char *, char *); extern void unbracket(char **); extern void trim(char *); extern char *get_line(FILE *, char *, char *, int); extern int getword(char *, char *, char **, char **, char, int); extern int get_retry(char *); extern int str_opt(struct mnttab *, char *, char **); extern void put_automountd_env(void); extern void dirinit(char *, char *, char *, int, char **, char ***); extern void pr_msg(const char *, ...); extern void trace_prt(int, char *, ...); extern void free_autofs_args(autofs_args *); extern void free_nfs_args(struct nfs_args *); extern void free_mounta(struct mounta *); extern int nopt(struct mnttab *, char *, int *); extern int set_versrange(rpcvers_t, rpcvers_t *, rpcvers_t *); extern enum clnt_stat pingnfs(char *, int, rpcvers_t *, rpcvers_t, ushort_t, bool_t, char *, char *); extern void *autofs_get_buffer(size_t); extern int self_check(char *); extern int do_mount1(char *, char *, char *, char *, char *, uint_t, uid_t, action_list **, int); extern int do_lookup1(char *, char *, char *, char *, char *, uint_t, uid_t, autofs_action_t *, struct linka *); extern int do_unmount1(umntrequest *); extern int do_readdir(autofs_rddirargs *, autofs_rddirres *); extern int nfsunmount(struct mnttab *); extern int loopbackmount(char *, char *, char *, int); extern int mount_nfs(struct mapent *, char *, char *, int, uid_t, action_list **); extern int mount_autofs(struct mapent *, char *, action_list *, char *rootp, char *subdir, char *key); extern int mount_generic(char *, char *, char *, char *, int); extern enum clnt_stat nfs_cast(struct mapfs *, struct mapfs **, int); extern bool_t hasrestrictopt(char *); #ifndef NO_RDDIR_CACHE /* * readdir handling routines */ extern char *auto_rddir_malloc(unsigned); extern char *auto_rddir_strdup(const char *); extern struct dir_entry *btree_lookup(struct dir_entry *, char *); extern void btree_enter(struct dir_entry **, struct dir_entry *); extern int add_dir_entry(char *, struct dir_entry **, struct dir_entry **); extern void cache_cleanup(void); extern int autofs_rddir_cache_lookup(char *, struct autofs_rddir_cache **); extern struct dir_entry *rddir_entry_lookup(char *, struct dir_entry *); #endif /* NO_RDDIR_CACHE */ /* * generic interface to specific name service functions */ extern void ns_setup(char **, char ***); extern int getmapent(char *, char *, struct mapline *, char **, char ***, bool_t *, bool_t); extern int getmapkeys(char *, struct dir_entry **, int *, int *, char **, char ***, uid_t); extern int loadmaster_map(char *, char *, char **, char ***); extern int loaddirect_map(char *, char *, char *, char **, char ***); /* * these name service specific functions should not be * accessed directly, use the generic functions. */ extern void init_files(char **, char ***); extern int getmapent_files(char *, char *, struct mapline *, char **, char ***, bool_t *, bool_t); extern int loadmaster_files(char *, char *, char **, char ***); extern int loaddirect_files(char *, char *, char *, char **, char ***); extern int getmapkeys_files(char *, struct dir_entry **, int *, int *, char **, char ***); extern int stack_op(int, char *, char **, char ***); extern void init_nis(char **, char ***); extern int getmapent_nis(char *, char *, struct mapline *, char **, char ***, bool_t *, bool_t); extern int loadmaster_nis(char *, char *, char **, char ***); extern int loaddirect_nis(char *, char *, char *, char **, char ***); extern int getmapkeys_nis(char *, struct dir_entry **, int *, int *, char **, char ***); /* Hammerhead: ns_ldap.c removed (libsldap chain removal, Phase 1). */ /* * end of name service specific functions */ /* * not defined in any header file */ extern int __clnt_bindresvport(CLIENT *); extern int getnetmaskbynet(const struct in_addr, struct in_addr *); /* * Hidden rpc functions */ extern int __nis_reset_state(); extern int __rpc_negotiate_uid(int); /* * door_server functions to handle fork activity within the automounter */ void automountd_do_fork_exec(void *, char *, size_t, door_desc_t *, uint_t); void automountd_do_exec_map(void *, char *, size_t, door_desc_t *, uint_t); #ifdef __cplusplus } #endif #endif /* _AUTOMOUNT_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 1998,2001 by Sun Microsystems, Inc. * All rights reserved. */ #ifdef MALLOC_DEBUG #include #include #include #include #include #include #include #include #include #include /* * To use debugging facility, compile with * -DMALLOC_DEBUG. * You can do this by setting the environment variable * MALLOC_DEBUG to "-DMALLOC_DEBUG" * * To make automountd dump trace records (i.e. make it call check_leaks), * run: * make malloc_dump */ struct alloc_list { char type[20]; void *addr; int size; char file[80]; int line; struct alloc_list *next; }; static struct alloc_list *halist = NULL; static mutex_t alloc_list_lock = DEFAULTMUTEX; int add_alloc(char *type, void *addr, size_t size, const char *file, int line) { struct alloc_list *alist = NULL; /* allocate the list item */ alist = (struct alloc_list *)malloc(sizeof (*alist)); if (alist == NULL) { syslog(LOG_ERR, "add_alloc: out of memory\n"); return (-1); } strcpy(alist->type, type); alist->addr = addr; alist->size = size; strcpy(alist->file, file); alist->line = line; /* add it to the head of the list */ if (halist == NULL) alist->next = NULL; else alist->next = halist; halist = alist; return (0); } int drop_alloc(const char *type, void *addr, const char *file, int line) { struct alloc_list *alist, *alist_prev; alist = halist; while (alist != NULL) { if (addr == alist->addr) { if (alist == halist) halist = halist->next; else alist_prev->next = alist->next; free(alist); break; } alist_prev = alist; alist = alist->next; } if (alist == NULL) { syslog(LOG_ERR, "*** POSSIBLE CORRUPTION ****\n"); syslog(LOG_ERR, "\tduplicate free, type %s, at %p in %s/%d\n", type, addr, file, line); return (-1); } return (0); } void * my_malloc(size_t size, const char *file, int line) { void *addr; addr = (void *)malloc(size); if (addr == NULL) return (NULL); mutex_lock(&alloc_list_lock); add_alloc("MALLOC", addr, size, file, line); mutex_unlock(&alloc_list_lock); return (addr); } void * my_realloc(void *addr, size_t size, const char *file, int line) { void *ptr; ptr = (void *)realloc(addr, size); if (ptr == NULL) return (NULL); mutex_lock(&alloc_list_lock); drop_alloc("MALLOC", addr, file, line); add_alloc("MALLOC", ptr, size, file, line); mutex_unlock(&alloc_list_lock); return (ptr); } void my_free(void *addr, const char *file, int line) { mutex_lock(&alloc_list_lock); drop_alloc("MALLOC", addr, file, line); mutex_unlock(&alloc_list_lock); free(addr); } char * my_strdup(const char *straddr, const char *file, int line) { void *addr; size_t size; addr = strdup(straddr); if (addr == NULL) return (NULL); size = strlen(straddr); mutex_lock(&alloc_list_lock); add_alloc("STRDUP", addr, size, file, line); mutex_unlock(&alloc_list_lock); return ((char *)addr); } int my_sethostent(int stay, const char *file, int line) { (void) sethostent(stay); mutex_lock(&alloc_list_lock); add_alloc("SETHOSTENT", NULL, 0, file, line); mutex_unlock(&alloc_list_lock); return (0); } int my_endhostent(const char *file, int line) { int ret; ret = endhostent(); if (ret != 0) return (ret); mutex_lock(&alloc_list_lock); drop_alloc("SETHOSTENT", NULL, file, line); mutex_unlock(&alloc_list_lock); return (ret); } void * my_setnetconfig(const char *file, int line) { void *nconf; nconf = setnetconfig(); if (nconf == NULL) return (NULL); mutex_lock(&alloc_list_lock); add_alloc("SETNETCONFIG", nconf, 0, file, line); mutex_unlock(&alloc_list_lock); return (nconf); } int my_endnetconfig(void *nconf, const char *file, int line) { int res; res = endnetconfig(nconf); if (res != 0) return (res); mutex_lock(&alloc_list_lock); drop_alloc("SETNETCONFIG", nconf, file, line); mutex_unlock(&alloc_list_lock); return (0); } void * my_setnetpath(const char *file, int line) { void *npath; npath = setnetpath(); if (npath == NULL) return (NULL); mutex_lock(&alloc_list_lock); add_alloc("SETNETPATH", npath, 0, file, line); mutex_unlock(&alloc_list_lock); return (npath); } int my_endnetpath(void *npath, const char *file, int line) { int res; res = endnetpath(npath); if (res != 0) return (res); mutex_lock(&alloc_list_lock); drop_alloc("SETNETPATH", npath, file, line); mutex_unlock(&alloc_list_lock); return (0); } int my_netdir_getbyname( struct netconfig *tp, struct nd_hostserv *serv, struct nd_addrlist **addrs, const char *file, int line) { int res; res = netdir_getbyname(tp, serv, addrs); if (res != 0) return (res); mutex_lock(&alloc_list_lock); add_alloc("NETDIR_GETBYNAME", *addrs, 0, file, line); mutex_unlock(&alloc_list_lock); return (0); } void my_netdir_free(void *ptr, int type, const char *file, int line) { netdir_free(ptr, type); mutex_lock(&alloc_list_lock); drop_alloc("NETDIR_GETBYNAME", ptr, file, line); mutex_unlock(&alloc_list_lock); } struct hostent * my_getipnodebyname( const char *name, int af, int flags, int *error_num, char *file, int line) { struct hostent *res; res = getipnodebyname(name, af, flags, error_num); if (res == NULL) return (NULL); mutex_lock(&alloc_list_lock); add_alloc("GETIPNODEBYNAME", res, 0, file, line); mutex_unlock(&alloc_list_lock); return (res); } void my_freehostent(struct hostent *hent, char *file, int line) { freehostent(hent); mutex_lock(&alloc_list_lock); drop_alloc("GETIPNODEBYNAME", hent, file, line); mutex_unlock(&alloc_list_lock); } struct netconfig * my_getnetconfigent(char *netid, char *file, int line) { struct netconfig *res; res = getnetconfigent(netid); if (res == NULL) return (NULL); mutex_lock(&alloc_list_lock); add_alloc("GETNETCONFIGENT", res, 0, file, line); mutex_unlock(&alloc_list_lock); return (res); } void my_freenetconfigent(struct netconfig *netp, char *file, int line) { freenetconfigent(netp); mutex_lock(&alloc_list_lock); drop_alloc("GETNETCONFIGENT", netp, file, line); mutex_unlock(&alloc_list_lock); } void * my__rpc_setconf(char *nettype, char *file, int line) { void *res; res = __rpc_setconf(nettype); if (res == NULL) return (NULL); mutex_lock(&alloc_list_lock); add_alloc("RPC_SETCONF", res, 0, file, line); mutex_unlock(&alloc_list_lock); return (res); } void my__rpc_endconf(void *vhandle, char *file, int line) { __rpc_endconf(vhandle); mutex_lock(&alloc_list_lock); drop_alloc("RPC_SETCONF", vhandle, file, line); mutex_unlock(&alloc_list_lock); } extern void flush_caches(); void _flush_caches() { } #pragma weak flush_caches = _flush_caches void check_leaks(char *filename) { struct alloc_list *alist; FILE *fp; fp = fopen(filename, "a"); if (fp == NULL) { syslog(LOG_ERR, "check_leaks, could not open file: %s", filename); return; } flush_caches(); fprintf(fp, "*** POSSIBLE LEAKS ****\n"); mutex_lock(&alloc_list_lock); alist = halist; while (alist != NULL) { fprintf(fp, "\t%s: %d bytes at %p in %s/%d\n", alist->type, alist->size, alist->addr, alist->file, alist->line); alist = alist->next; } mutex_unlock(&alloc_list_lock); (void) fclose(fp); } #else /* * To prevent a compiler warning. */ static char filler; #endif /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 1998,2001 by Sun Microsystems, Inc. * All rights reserved. */ #ifndef _MY_ALLOC_H #define _MY_ALLOC_H #ifdef __cplusplus extern "C" { #endif #include #include #include int add_alloc(char *, void *, size_t, const char *, int); int drop_alloc(const char *, void *, const char *, int); void *my_malloc(size_t, const char *, int); void *my_realloc(void *, size_t, const char *, int); void my_free(void *, const char *, int); char *my_strdup(const char *, const char *, int); int my_sethostent(int, const char *, int); int my_endhostent(const char *, int); void *my_setnetconfig(const char *, int); int my_endnetconfig(void *, const char *, int); void *my_setnetpath(const char *, int); int my_endnetpath(void *, const char *, int); int my_netdir_getbyname(struct netconfig *, struct nd_hostserv *, struct nd_addrlist **, const char *, int); int my_netdir_free(void *, int, const char *, int); struct hostent *my_getipnodebyname(const char *, int, int, int *, char *, int); void my_freehostent(struct hostent *, char *, int); struct netconfig *my_getnetconfigent(char *, char *, int); void my_freenetconfigent(struct netconfig *, char *, int); void *my__rpc_setconf(char *, char *, int); void my__rpc_endconf(void *, char *, int); void check_leaks(char *); #define AUTOFS_DUMP_DEBUG 1000000 #define free(a) my_free(a, __FILE__, __LINE__) #define malloc(a) my_malloc(a, __FILE__, __LINE__) #define realloc(a, s) my_realloc(a, s, __FILE__, __LINE__) #define strdup(a) my_strdup(a, __FILE__, __LINE__) #define sethostent(s) my_sethostent(s, __FILE__, __LINE__) #define endhostent() my_endhostent(__FILE__, __LINE__) #define setnetconfig() my_setnetconfig(__FILE__, __LINE__) #define endnetconfig(v) my_endnetconfig(v, __FILE__, __LINE__) #define setnetpath() my_setnetpath(__FILE__, __LINE__) #define endnetpath(v) my_endnetpath(v, __FILE__, __LINE__) #define netdir_getbyname(t, s, a) \ my_netdir_getbyname(t, s, a, __FILE__, __LINE__) #define netdir_free(a, t) my_netdir_free(a, t, __FILE__, __LINE__) #define getipnodebyname(n, a, f, e) \ my_getipnodebyname(n, a, f, e, __FILE__, __LINE__) #define freehostent(h) my_freehostent(h, __FILE__, __LINE__) #define getnetconfigent(n) my_getnetconfigent(n, __FILE__, __LINE__) #define freenetconfigent(n) my_freenetconfigent(n, __FILE__, __LINE__) #ifdef __cplusplus } #endif #endif /* _MY_ALLOC_H */ #!/usr/bin/sh # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License, Version 1.0 only # (the "License"). You may not use this file except in compliance # with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright (c) 1999 by Sun Microsystems, Inc. # All rights reserved. # #ident "%Z%%M% %I% %E% SMI" # dfshares is a server utility but autofs is a client # filesystem, so dfshares for autofs will do nothing. # This utility is needed because autofs is included in # /etc/dfs/fstypes. exit 0 /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * autofs mount.c * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "automount.h" #define MNTTAB_OPTS "ignore,nest" static void usage(); static void process_opts(char *options, int *directp); static char *concat_opts(const char *opts1, const char *opts2); static int ro_given(char *options); /* * list of support services needed */ static char *service_list[] = { AUTOMOUNTD, NULL }; int main(int argc, char *argv[]) { int error; int c; int mntflags = 0; int nmflg = 0; int roflg = 0; char *mntpnt, *mapname; struct utsname utsname; char autofs_addr[MAXADDRLEN]; struct autofs_args fni; char *options = ""; int mount_timeout = AUTOFS_MOUNT_TIMEOUT; char obuf[MAX_MNTOPT_STR]; while ((c = getopt(argc, argv, "o:mrq")) != EOF) { switch (c) { case '?': usage(); exit(1); /* NOTREACHED */ case 'o': options = optarg; break; case 'm': nmflg++; break; case 'r': /* converted to -o ro always */ roflg++; break; /* * The "quiet" flag can be ignored, since this * program never complains about invalid -o options * anyway. */ case 'q': break; default: usage(); } } if (argc - optind != 2) usage(); mapname = argv[optind]; mntpnt = argv[optind + 1]; if (strcmp(mntpnt, "/-") == 0) { (void) fprintf(stderr, "invalid mountpoint: /-\n"); exit(1); } if (uname(&utsname) < 0) { perror("uname"); exit(1); } (void) strcpy(autofs_addr, utsname.nodename); (void) strcat(autofs_addr, ".autofs"); process_opts(options, &fni.direct); if (roflg && !ro_given(options)) options = concat_opts(options, "ro"); fni.addr.buf = autofs_addr; fni.addr.len = strlen(fni.addr.buf); fni.addr.maxlen = fni.addr.len; fni.path = mntpnt; fni.opts = options; fni.map = mapname; fni.subdir = ""; if (fni.direct) fni.key = mntpnt; else fni.key = ""; fni.mount_to = mount_timeout; fni.rpc_to = AUTOFS_RPC_TIMEOUT; strcpy(obuf, options); if (*obuf != '\0') strcat(obuf, ","); strcat(obuf, fni.direct ? MNTTAB_OPTS ",direct" : MNTTAB_OPTS ",indirect"); /* * enable services as needed. */ _check_services(service_list); error = mount(fni.map, mntpnt, mntflags | MS_DATA | MS_OPTIONSTR, MNTTYPE_AUTOFS, &fni, sizeof (fni), obuf, MAX_MNTOPT_STR); if (error < 0) { perror("autofs mount"); exit(1); } return (0); } static void usage() { (void) fprintf(stderr, "Usage: autofs mount [-r] [-o opts] map dir\n"); exit(1); } /* * Remove pseudo-options "direct", "indirect", "nest", and "ignore" from * option list. Set *directp to 1 if "direct" is found, and 0 otherwise * (mounts are indirect by default). If both "direct" and "indirect" are * found, the last one wins. */ static void process_opts(char *options, int *directp) { char *opt; char *opts; if ((opts = strdup(options)) == NULL) { (void) fprintf(stderr, "autofs mount: memory allocation failed\n"); exit(1); } options[0] = '\0'; *directp = 0; while ((opt = strtok(opts, ",")) != NULL) { opts = NULL; while (isspace(*opt)) { opt++; } if (strcmp(opt, "direct") == 0) { *directp = 1; } else if (strcmp(opt, "indirect") == 0) { *directp = 0; } else if ((strcmp(opt, "nest") != 0) && (strcmp(opt, "ignore") != 0)) { if (options[0] != '\0') { (void) strcat(options, ","); } (void) strcat(options, opt); } }; } /* * Concatenate two options strings, with a comma between them. */ static char * concat_opts(const char *opts1, const char *opts2) { char *opts = malloc(strlen(opts1) + strlen(opts2) + 2); if (opts == NULL) { (void) fprintf(stderr, "autofs mount: memory allocation failed\n"); exit(1); } strcpy(opts, opts1); if (opts1[0] != '\0' && opts2[0] != '\0') { strcat(opts, ","); } return (strcat(opts, opts2)); } /* * check the options string for 'ro' options * if present returns 1 otherwise return 0; */ static int ro_given(char *options) { char *op = options; if (!*op) return (0); while (op != 0) { if (*op == 'r' && *(op+1) == 'o' && (*(op+2) == ',' || *(op+2) == '\0')) return (1); if ((op = strchr(op, ',')) != NULL) op++; } return (0); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * nfs_cast.c : broadcast to a specific group of NFS servers * * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define NFSCLIENT #include #include "automount.h" #define PENALTY_WEIGHT 100000 struct tstamps { struct tstamps *ts_next; int ts_penalty; int ts_inx; int ts_rcvd; struct timeval ts_timeval; }; /* A list of addresses - all belonging to the same transport */ struct addrs { struct addrs *addr_next; struct mapfs *addr_mfs; struct nd_addrlist *addr_addrs; struct tstamps *addr_if_tstamps; }; /* A list of connectionless transports */ struct transp { struct transp *tr_next; int tr_fd; char *tr_device; struct t_bind *tr_taddr; struct addrs *tr_addrs; }; /* A list of map entries and their roundtrip times, for sorting */ struct sm { struct mapfs *mfs; struct timeval timeval; }; static void free_transports(struct transp *); static void calc_resp_time(struct timeval *); static struct mapfs *sort_responses(struct transp *); static int host_sm(const void *, const void *b); static int time_sm(const void *, const void *b); extern struct mapfs *add_mfs(struct mapfs *, int, struct mapfs **, struct mapfs **); /* * This routine is designed to be able to "ping" * a list of hosts and create a list of responding * hosts sorted by response time. * This must be done without any prior * contact with the host - therefore the "ping" * must be to a "well-known" address. The outstanding * candidate here is the address of "rpcbind". * * A response to a ping is no guarantee that the host * is running NFS, has a mount daemon, or exports * the required filesystem. If the subsequent * mount attempt fails then the host will be marked * "ignore" and the host list will be re-pinged * (sans the bad host). This process continues * until a successful mount is achieved or until * there are no hosts left to try. */ enum clnt_stat nfs_cast(struct mapfs *mfs_in, struct mapfs **mfs_out, int timeout) { enum clnt_stat stat; AUTH *sys_auth = authsys_create_default(); XDR xdr_stream; register XDR *xdrs = &xdr_stream; int outlen; int if_inx; int tsec; int flag; int sent, addr_cnt, rcvd, if_cnt; fd_set readfds, mask; register ulong_t xid; /* xid - unique per addr */ register int i; struct rpc_msg msg; struct timeval t, rcv_timeout; char outbuf[UDPMSGSIZE], inbuf[UDPMSGSIZE]; struct t_unitdata t_udata, t_rdata; struct nd_hostserv hs; struct nd_addrlist *retaddrs; struct transp *tr_head; struct transp *trans, *prev_trans; struct addrs *a, *prev_addr; struct tstamps *ts, *prev_ts; NCONF_HANDLE *nc = NULL; struct netconfig *nconf; struct rlimit rl; int dtbsize; struct mapfs *mfs; /* * For each connectionless transport get a list of * host addresses. Any single host may have * addresses on several transports. */ addr_cnt = sent = rcvd = 0; tr_head = NULL; FD_ZERO(&mask); /* * Set the default select size to be the maximum FD_SETSIZE, unless * the current rlimit is lower. */ dtbsize = FD_SETSIZE; if (getrlimit(RLIMIT_NOFILE, &rl) == 0) { if (rl.rlim_cur < FD_SETSIZE) dtbsize = rl.rlim_cur; } prev_trans = NULL; prev_addr = NULL; prev_ts = NULL; for (mfs = mfs_in; mfs; mfs = mfs->mfs_next) { if (trace > 2) trace_prt(1, "nfs_cast: host=%s\n", mfs->mfs_host); nc = setnetconfig(); if (nc == NULL) { stat = RPC_CANTSEND; goto done_broad; } while (nconf = getnetconfig(nc)) { if (!(nconf->nc_flag & NC_VISIBLE) || nconf->nc_semantics != NC_TPI_CLTS || (strcmp(nconf->nc_protofmly, NC_LOOPBACK) == 0)) continue; trans = (struct transp *)malloc(sizeof (*trans)); if (trans == NULL) { syslog(LOG_ERR, "no memory"); stat = RPC_CANTSEND; goto done_broad; } (void) memset(trans, 0, sizeof (*trans)); if (tr_head == NULL) tr_head = trans; else prev_trans->tr_next = trans; prev_trans = trans; trans->tr_fd = t_open(nconf->nc_device, O_RDWR, NULL); if (trans->tr_fd < 0) { syslog(LOG_ERR, "nfscast: t_open: %s:%m", nconf->nc_device); stat = RPC_CANTSEND; goto done_broad; } if (t_bind(trans->tr_fd, (struct t_bind *)NULL, (struct t_bind *)NULL) < 0) { syslog(LOG_ERR, "nfscast: t_bind: %m"); stat = RPC_CANTSEND; goto done_broad; } trans->tr_taddr = /* LINTED pointer alignment */ (struct t_bind *)t_alloc(trans->tr_fd, T_BIND, T_ADDR); if (trans->tr_taddr == (struct t_bind *)NULL) { syslog(LOG_ERR, "nfscast: t_alloc: %m"); stat = RPC_SYSTEMERROR; goto done_broad; } trans->tr_device = nconf->nc_device; FD_SET(trans->tr_fd, &mask); if_inx = 0; hs.h_host = mfs->mfs_host; hs.h_serv = "rpcbind"; if (netdir_getbyname(nconf, &hs, &retaddrs) == ND_OK) { /* * If mfs->ignore is previously set for * this map, clear it. Because a host can * have either v6 or v4 address */ if (mfs->mfs_ignore == 1) mfs->mfs_ignore = 0; a = (struct addrs *)malloc(sizeof (*a)); if (a == NULL) { syslog(LOG_ERR, "no memory"); stat = RPC_CANTSEND; goto done_broad; } (void) memset(a, 0, sizeof (*a)); if (trans->tr_addrs == NULL) trans->tr_addrs = a; else prev_addr->addr_next = a; prev_addr = a; a->addr_if_tstamps = NULL; a->addr_mfs = mfs; a->addr_addrs = retaddrs; if_cnt = retaddrs->n_cnt; while (if_cnt--) { ts = (struct tstamps *) malloc(sizeof (*ts)); if (ts == NULL) { syslog(LOG_ERR, "no memory"); stat = RPC_CANTSEND; goto done_broad; } (void) memset(ts, 0, sizeof (*ts)); ts->ts_penalty = mfs->mfs_penalty; if (a->addr_if_tstamps == NULL) a->addr_if_tstamps = ts; else prev_ts->ts_next = ts; prev_ts = ts; ts->ts_inx = if_inx++; addr_cnt++; } break; } else { mfs->mfs_ignore = 1; if (verbose) syslog(LOG_ERR, "%s:%s address not known", mfs->mfs_host, strcmp(nconf->nc_proto, NC_INET)?"IPv6":"IPv4"); } } /* while */ endnetconfig(nc); nc = NULL; } /* for */ if (addr_cnt == 0) { syslog(LOG_ERR, "nfscast: couldn't find addresses"); stat = RPC_CANTSEND; goto done_broad; } (void) gettimeofday(&t, (struct timezone *)0); xid = (getpid() ^ t.tv_sec ^ t.tv_usec) & ~0xFF; t.tv_usec = 0; /* serialize the RPC header */ msg.rm_direction = CALL; msg.rm_call.cb_rpcvers = RPC_MSG_VERSION; msg.rm_call.cb_prog = RPCBPROG; /* * we can not use RPCBVERS here since it doesn't exist in 4.X, * the fix to bug 1139883 has made the 4.X portmapper silent to * version mismatches. This causes the RPC call to the remote * portmapper to simply be ignored if it's not Version 2. */ msg.rm_call.cb_vers = PMAPVERS; msg.rm_call.cb_proc = NULLPROC; if (sys_auth == (AUTH *)NULL) { stat = RPC_SYSTEMERROR; goto done_broad; } msg.rm_call.cb_cred = sys_auth->ah_cred; msg.rm_call.cb_verf = sys_auth->ah_verf; xdrmem_create(xdrs, outbuf, sizeof (outbuf), XDR_ENCODE); if (! xdr_callmsg(xdrs, &msg)) { stat = RPC_CANTENCODEARGS; goto done_broad; } outlen = (int)xdr_getpos(xdrs); xdr_destroy(xdrs); t_udata.opt.len = 0; t_udata.udata.buf = outbuf; t_udata.udata.len = outlen; /* * Basic loop: send packet to all hosts and wait for response(s). * The response timeout grows larger per iteration. * A unique xid is assigned to each address in order to * correctly match the replies. */ for (tsec = 4; timeout > 0; tsec *= 2) { timeout -= tsec; if (timeout <= 0) tsec += timeout; rcv_timeout.tv_sec = tsec; rcv_timeout.tv_usec = 0; sent = 0; for (trans = tr_head; trans; trans = trans->tr_next) { for (a = trans->tr_addrs; a; a = a->addr_next) { struct netbuf *if_netbuf = a->addr_addrs->n_addrs; ts = a->addr_if_tstamps; if_cnt = a->addr_addrs->n_cnt; while (if_cnt--) { /* * xid is the first thing in * preserialized buffer */ /* LINTED pointer alignment */ *((ulong_t *)outbuf) = htonl(xid + ts->ts_inx); (void) gettimeofday(&(ts->ts_timeval), (struct timezone *)0); /* * Check if already received * from a previous iteration. */ if (ts->ts_rcvd) { sent++; ts = ts->ts_next; continue; } t_udata.addr = *if_netbuf++; if (t_sndudata(trans->tr_fd, &t_udata) == 0) { sent++; } ts = ts->ts_next; } } } if (sent == 0) { /* no packets sent ? */ stat = RPC_CANTSEND; goto done_broad; } /* * Have sent all the packets. Now collect the responses... */ rcvd = 0; recv_again: msg.acpted_rply.ar_verf = _null_auth; msg.acpted_rply.ar_results.proc = xdr_void; readfds = mask; switch (select(dtbsize, &readfds, (fd_set *)NULL, (fd_set *)NULL, &rcv_timeout)) { case 0: /* Timed out */ /* * If we got at least one response in the * last interval, then don't wait for any * more. In theory we should wait for * the max weighting (penalty) value so * that a very slow server has a chance to * respond but this could take a long time * if the admin has set a high weighting * value. */ if (rcvd > 0) goto done_broad; stat = RPC_TIMEDOUT; continue; case -1: /* some kind of error */ if (errno == EINTR) goto recv_again; syslog(LOG_ERR, "nfscast: select: %m"); if (rcvd == 0) stat = RPC_CANTRECV; goto done_broad; } /* end of select results switch */ for (trans = tr_head; trans; trans = trans->tr_next) { if (FD_ISSET(trans->tr_fd, &readfds)) break; } if (trans == NULL) goto recv_again; try_again: t_rdata.addr = trans->tr_taddr->addr; t_rdata.udata.buf = inbuf; t_rdata.udata.maxlen = sizeof (inbuf); t_rdata.udata.len = 0; t_rdata.opt.len = 0; if (t_rcvudata(trans->tr_fd, &t_rdata, &flag) < 0) { if (errno == EINTR) goto try_again; syslog(LOG_ERR, "nfscast: t_rcvudata: %s:%m", trans->tr_device); stat = RPC_CANTRECV; continue; } if (t_rdata.udata.len < sizeof (ulong_t)) goto recv_again; if (flag & T_MORE) { syslog(LOG_ERR, "nfscast: t_rcvudata: %s: buffer overflow", trans->tr_device); goto recv_again; } /* * see if reply transaction id matches sent id. * If so, decode the results. * Note: received addr is ignored, it could be * different from the send addr if the host has * more than one addr. */ xdrmem_create(xdrs, inbuf, (uint_t)t_rdata.udata.len, XDR_DECODE); if (xdr_replymsg(xdrs, &msg)) { if (msg.rm_reply.rp_stat == MSG_ACCEPTED && (msg.rm_xid & ~0xFF) == xid) { struct addrs *curr_addr; i = msg.rm_xid & 0xFF; for (curr_addr = trans->tr_addrs; curr_addr; curr_addr = curr_addr->addr_next) { for (ts = curr_addr->addr_if_tstamps; ts; ts = ts->ts_next) if (ts->ts_inx == i && !ts->ts_rcvd) { ts->ts_rcvd = 1; calc_resp_time(&ts->ts_timeval); stat = RPC_SUCCESS; rcvd++; break; } } } /* otherwise, we just ignore the errors ... */ } xdrs->x_op = XDR_FREE; msg.acpted_rply.ar_results.proc = xdr_void; (void) xdr_replymsg(xdrs, &msg); XDR_DESTROY(xdrs); if (rcvd == sent) goto done_broad; else goto recv_again; } if (!rcvd) stat = RPC_TIMEDOUT; done_broad: if (rcvd) { *mfs_out = sort_responses(tr_head); stat = RPC_SUCCESS; } if (nc) endnetconfig(nc); free_transports(tr_head); AUTH_DESTROY(sys_auth); return (stat); } /* * Go through all the responses and sort fastest to slowest. * Note that any penalty is added to the response time - so the * fastest response isn't necessarily the one that arrived first. */ static struct mapfs * sort_responses(trans) struct transp *trans; { struct transp *t; struct addrs *a; struct tstamps *ti; int i, size = 0, allocsize = 10; struct mapfs *p, *mfs_head = NULL, *mfs_tail = NULL; struct sm *buffer; buffer = (struct sm *)malloc(allocsize * sizeof (struct sm)); if (!buffer) { syslog(LOG_ERR, "sort_responses: malloc error.\n"); return (NULL); } for (t = trans; t; t = t->tr_next) { for (a = t->tr_addrs; a; a = a->addr_next) { for (ti = a->addr_if_tstamps; ti; ti = ti->ts_next) { if (!ti->ts_rcvd) continue; ti->ts_timeval.tv_usec += (ti->ts_penalty * PENALTY_WEIGHT); if (ti->ts_timeval.tv_usec >= 1000000) { ti->ts_timeval.tv_sec += (ti->ts_timeval.tv_usec / 1000000); ti->ts_timeval.tv_usec = (ti->ts_timeval.tv_usec % 1000000); } if (size >= allocsize) { allocsize += 10; buffer = (struct sm *)realloc(buffer, allocsize * sizeof (struct sm)); if (!buffer) { syslog(LOG_ERR, "sort_responses: malloc error.\n"); return (NULL); } } buffer[size].timeval = ti->ts_timeval; buffer[size].mfs = a->addr_mfs; size++; } } } #ifdef DEBUG if (trace > 3) { trace_prt(1, " sort_responses: before host sort:\n"); for (i = 0; i < size; i++) trace_prt(1, " %s %d.%d\n", buffer[i].mfs->mfs_host, buffer[i].timeval.tv_sec, buffer[i].timeval.tv_usec); trace_prt(0, "\n"); } #endif qsort((void *)buffer, size, sizeof (struct sm), host_sm); /* * Cope with multiply listed hosts by choosing first time */ for (i = 1; i < size; i++) { #ifdef DEBUG if (trace > 3) { trace_prt(1, " sort_responses: comparing %s and %s\n", buffer[i-1].mfs->mfs_host, buffer[i].mfs->mfs_host); } #endif if (strcmp(buffer[i-1].mfs->mfs_host, buffer[i].mfs->mfs_host) == 0) memcpy(&buffer[i].timeval, &buffer[i-1].timeval, sizeof (struct timeval)); } if (trace > 3) trace_prt(0, "\n"); #ifdef DEBUG if (trace > 3) { trace_prt(1, " sort_responses: before time sort:\n"); for (i = 0; i < size; i++) trace_prt(1, " %s %d.%d\n", buffer[i].mfs->mfs_host, buffer[i].timeval.tv_sec, buffer[i].timeval.tv_usec); trace_prt(0, "\n"); } #endif qsort((void *)buffer, size, sizeof (struct sm), time_sm); #ifdef DEBUG if (trace > 3) { trace_prt(1, " sort_responses: after sort:\n"); for (i = 0; i < size; i++) trace_prt(1, " %s %d.%d\n", buffer[i].mfs->mfs_host, buffer[i].timeval.tv_sec, buffer[i].timeval.tv_usec); trace_prt(0, "\n"); } #endif for (i = 0; i < size; i++) { #ifdef DEBUG if (trace > 3) { trace_prt(1, " sort_responses: adding %s\n", buffer[i].mfs->mfs_host); } #endif p = add_mfs(buffer[i].mfs, 0, &mfs_head, &mfs_tail); if (!p) return (NULL); } free(buffer); return (mfs_head); } /* * Comparison routines called by qsort(3). */ static int host_sm(const void *a, const void *b) { return (strcmp(((struct sm *)a)->mfs->mfs_host, ((struct sm *)b)->mfs->mfs_host)); } static int time_sm(const void *a, const void *b) { if (timercmp(&(((struct sm *)a)->timeval), &(((struct sm *)b)->timeval), < /* cstyle */)) return (-1); else if (timercmp(&(((struct sm *)a)->timeval), &(((struct sm *)b)->timeval), > /* cstyle */)) return (1); else return (0); } /* * Given send_time which is the time a request * was transmitted to a server, subtract it * from the time "now" thereby converting it * to an elapsed time. */ static void calc_resp_time(send_time) struct timeval *send_time; { struct timeval time_now; (void) gettimeofday(&time_now, (struct timezone *)0); if (time_now.tv_usec < send_time->tv_usec) { time_now.tv_sec--; time_now.tv_usec += 1000000; } send_time->tv_sec = time_now.tv_sec - send_time->tv_sec; send_time->tv_usec = time_now.tv_usec - send_time->tv_usec; } static void free_transports(trans) struct transp *trans; { struct transp *t, *tmpt = NULL; struct addrs *a, *tmpa = NULL; struct tstamps *ts, *tmpts = NULL; for (t = trans; t; t = tmpt) { if (t->tr_taddr) (void) t_free((char *)t->tr_taddr, T_BIND); if (t->tr_fd > 0) (void) t_close(t->tr_fd); for (a = t->tr_addrs; a; a = tmpa) { for (ts = a->addr_if_tstamps; ts; ts = tmpts) { tmpts = ts->ts_next; free(ts); } (void) netdir_free((char *)a->addr_addrs, ND_ADDRLIST); tmpa = a->addr_next; free(a); } tmpt = t->tr_next; free(t); } } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * ns_files.c * * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "automount.h" int did_exec_map; static int read_execout(char *, char **, char *, char *, int); static int call_read_execout(char *, char *, char *, int); static FILE *file_open(char *, char *, char **, char ***); /* * Initialize the stack */ void init_files(char **stack, char ***stkptr) { /* * The call is bogus for automountd since the stack is * is more appropriately initialized in the thread-private * routines */ if (stack == NULL && stkptr == NULL) return; (void) stack_op(INIT, NULL, stack, stkptr); } int getmapent_files(char *key, char *mapname, struct mapline *ml, char **stack, char ***stkptr, bool_t *iswildcard, bool_t isrestricted) { int nserr; FILE *fp; char word[MAXPATHLEN+1], wordq[MAXPATHLEN+1]; char linebuf[LINESZ], lineqbuf[LINESZ]; char *lp, *lq; struct stat stbuf; char fname[MAXFILENAMELEN]; /* /etc prepended to mapname if reqd */ int syntaxok = 1; if (iswildcard) *iswildcard = FALSE; if ((fp = file_open(mapname, fname, stack, stkptr)) == NULL) { nserr = __NSW_UNAVAIL; goto done; } if (stat(fname, &stbuf) < 0) { nserr = __NSW_UNAVAIL; goto done; } /* * If the file has its execute bit on then * assume it's an executable map. * Execute it and pass the key as an argument. * Expect to get a map entry on the stdout. * Ignore the "x" bit on restricted maps. */ if (!isrestricted && (stbuf.st_mode & S_IXUSR)) { int rc; if (trace > 1) { trace_prt(1, "\tExecutable map: map=%s key=%s\n", fname, key); } rc = call_read_execout(key, fname, ml->linebuf, LINESZ); if (rc != 0) { nserr = __NSW_UNAVAIL; goto done; } if (strlen(ml->linebuf) == 0) { nserr = __NSW_NOTFOUND; goto done; } unquote(ml->linebuf, ml->lineqbuf); nserr = __NSW_SUCCESS; goto done; } /* * It's just a normal map file. * Search for the entry with the required key. */ for (;;) { lp = get_line(fp, fname, linebuf, sizeof (linebuf)); if (lp == NULL) { nserr = __NSW_NOTFOUND; goto done; } if (verbose && syntaxok && isspace(*(uchar_t *)lp)) { syntaxok = 0; syslog(LOG_ERR, "leading space in map entry \"%s\" in %s", lp, mapname); } lq = lineqbuf; unquote(lp, lq); if ((getword(word, wordq, &lp, &lq, ' ', sizeof (word)) == -1) || (word[0] == '\0')) continue; if (strcmp(word, key) == 0) break; if (word[0] == '*' && word[1] == '\0') { if (iswildcard) *iswildcard = TRUE; break; } if (word[0] == '+') { nserr = getmapent(key, word+1, ml, stack, stkptr, iswildcard, isrestricted); if (nserr == __NSW_SUCCESS) goto done; continue; } /* * sanity check each map entry key against * the lookup key as the map is searched. */ if (verbose && syntaxok) { /* sanity check entry */ if (*key == '/') { if (*word != '/') { syntaxok = 0; syslog(LOG_ERR, "bad key \"%s\" in " "direct map %s\n", word, mapname); } } else { if (strchr(word, '/')) { syntaxok = 0; syslog(LOG_ERR, "bad key \"%s\" in " "indirect map %s\n", word, mapname); } } } } (void) strcpy(ml->linebuf, lp); (void) strcpy(ml->lineqbuf, lq); nserr = __NSW_SUCCESS; done: if (fp) { (void) stack_op(POP, (char *)NULL, stack, stkptr); (void) fclose(fp); } return (nserr); } int getmapkeys_files(char *mapname, struct dir_entry **list, int *error, int *cache_time, char **stack, char ***stkptr) { FILE *fp = NULL; char word[MAXPATHLEN+1], wordq[MAXPATHLEN+1]; char linebuf[LINESZ], lineqbuf[LINESZ]; char *lp, *lq; struct stat stbuf; char fname[MAXFILENAMELEN]; /* /etc prepended to mapname if reqd */ int syntaxok = 1; int nserr; struct dir_entry *last = NULL; if (trace > 1) trace_prt(1, "getmapkeys_files %s\n", mapname); *cache_time = RDDIR_CACHE_TIME; if ((fp = file_open(mapname, fname, stack, stkptr)) == NULL) { *error = ENOENT; nserr = __NSW_UNAVAIL; goto done; } if (fseek(fp, 0L, SEEK_SET) == -1) { *error = ENOENT; nserr = __NSW_UNAVAIL; goto done; } if (stat(fname, &stbuf) < 0) { *error = ENOENT; nserr = __NSW_UNAVAIL; goto done; } /* * If the file has its execute bit on then * assume it's an executable map. * I don't know how to list executable maps, return * an empty map. */ if (stbuf.st_mode & S_IXUSR) { *error = 0; nserr = __NSW_SUCCESS; goto done; } /* * It's just a normal map file. * List entries one line at a time. */ for (;;) { lp = get_line(fp, fname, linebuf, sizeof (linebuf)); if (lp == NULL) { nserr = __NSW_SUCCESS; goto done; } if (syntaxok && isspace(*(uchar_t *)lp)) { syntaxok = 0; syslog(LOG_ERR, "leading space in map entry \"%s\" in %s", lp, mapname); } lq = lineqbuf; unquote(lp, lq); if ((getword(word, wordq, &lp, &lq, ' ', MAXFILENAMELEN) == -1) || (word[0] == '\0')) continue; /* * Wildcard entries should be ignored and this should be * the last entry read to corroborate the search through * files, i.e., search for key until a wildcard is reached. */ if (word[0] == '*' && word[1] == '\0') break; if (word[0] == '+') { /* * Name switch here */ getmapkeys(word+1, list, error, cache_time, stack, stkptr, 0); /* * the list may have been updated, therefore * our 'last' may no longer be valid */ last = NULL; continue; } if (add_dir_entry(word, list, &last) != 0) { *error = ENOMEM; goto done; } assert(last != NULL); } nserr = __NSW_SUCCESS; done: if (fp) { (void) stack_op(POP, (char *)NULL, stack, stkptr); (void) fclose(fp); } if (*list != NULL) { /* * list of entries found */ *error = 0; } return (nserr); } int loadmaster_files(char *mastermap, char *defopts, char **stack, char ***stkptr) { FILE *fp; int done = 0; char *line, *dir, *map, *opts; char linebuf[LINESZ]; char lineq[LINESZ]; char fname[MAXFILENAMELEN]; /* /etc prepended to mapname if reqd */ if ((fp = file_open(mastermap, fname, stack, stkptr)) == NULL) return (__NSW_UNAVAIL); while ((line = get_line(fp, fname, linebuf, sizeof (linebuf))) != NULL) { unquote(line, lineq); if (macro_expand("", line, lineq, LINESZ)) { syslog(LOG_ERR, "map %s: line too long (max %d chars)", mastermap, LINESZ - 1); continue; } dir = line; while (*dir && isspace(*dir)) dir++; if (*dir == '\0') continue; map = dir; while (*map && !isspace(*map)) map++; if (*map) *map++ = '\0'; if (*dir == '+') { opts = map; while (*opts && isspace(*opts)) opts++; if (*opts != '-') opts = defopts; else opts++; /* * Check for no embedded blanks. */ if (strcspn(opts, " \t") == strlen(opts)) { dir++; (void) loadmaster_map(dir, opts, stack, stkptr); } else { pr_msg("Warning: invalid entry for %s in %s ignored.\n", dir, fname); continue; } } else { while (*map && isspace(*map)) map++; if (*map == '\0') continue; opts = map; while (*opts && !isspace(*opts)) opts++; if (*opts) { *opts++ = '\0'; while (*opts && isspace(*opts)) opts++; } if (*opts != '-') opts = defopts; else opts++; /* * Check for no embedded blanks. */ if (strcspn(opts, " \t") == strlen(opts)) { dirinit(dir, map, opts, 0, stack, stkptr); } else { pr_msg("Warning: invalid entry for %s in %s ignored.\n", dir, fname); continue; } } done++; } (void) stack_op(POP, (char *)NULL, stack, stkptr); (void) fclose(fp); return (done ? __NSW_SUCCESS : __NSW_NOTFOUND); } int loaddirect_files(char *map, char *local_map, char *opts, char **stack, char ***stkptr) { FILE *fp; int done = 0; char *line, *p1, *p2; char linebuf[LINESZ]; char fname[MAXFILENAMELEN]; /* /etc prepended to mapname if reqd */ if ((fp = file_open(map, fname, stack, stkptr)) == NULL) return (__NSW_UNAVAIL); while ((line = get_line(fp, fname, linebuf, sizeof (linebuf))) != NULL) { p1 = line; while (*p1 && isspace(*p1)) p1++; if (*p1 == '\0') continue; p2 = p1; while (*p2 && !isspace(*p2)) p2++; *p2 = '\0'; if (*p1 == '+') { p1++; (void) loaddirect_map(p1, local_map, opts, stack, stkptr); } else { dirinit(p1, local_map, opts, 1, stack, stkptr); } done++; } (void) stack_op(POP, (char *)NULL, stack, stkptr); (void) fclose(fp); return (done ? __NSW_SUCCESS : __NSW_NOTFOUND); } /* * This procedure opens the file and pushes it onto the * the stack. Only if a file is opened successfully, is * it pushed onto the stack */ static FILE * file_open(char *map, char *fname, char **stack, char ***stkptr) { FILE *fp; if (*map != '/') { /* prepend an "/etc" */ (void) strcpy(fname, "/etc/"); (void) strcat(fname, map); } else { (void) strcpy(fname, map); } fp = fopen(fname, "r"); if (fp != NULL) { if (!stack_op(PUSH, fname, stack, stkptr)) { (void) fclose(fp); return (NULL); } } return (fp); } /* * reimplemnted to be MT-HOT. */ int stack_op(int op, char *name, char **stack, char ***stkptr) { char **ptr = NULL; char **stk_top = &stack[STACKSIZ - 1]; /* * the stackptr points to the next empty slot * for PUSH: put the element and increment stkptr * for POP: decrement stkptr and free */ switch (op) { case INIT: for (ptr = stack; ptr != stk_top; ptr++) *ptr = (char *)NULL; *stkptr = stack; return (1); case ERASE: for (ptr = stack; ptr != stk_top; ptr++) if (*ptr) { if (trace > 1) trace_prt(1, " ERASE %s\n", *ptr); free (*ptr); *ptr = (char *)NULL; } *stkptr = stack; return (1); case PUSH: if (*stkptr == stk_top) return (0); for (ptr = stack; ptr != *stkptr; ptr++) if (*ptr && (strcmp(*ptr, name) == 0)) { return (0); } if (trace > 1) trace_prt(1, " PUSH %s\n", name); if ((**stkptr = strdup(name)) == NULL) { syslog(LOG_ERR, "stack_op: Memory alloc failed : %m"); return (0); } (*stkptr)++; return (1); case POP: if (*stkptr != stack) (*stkptr)--; else syslog(LOG_ERR, "Attempt to pop empty stack\n"); if (*stkptr && **stkptr) { if (trace > 1) trace_prt(1, " POP %s\n", **stkptr); free (**stkptr); **stkptr = (char *)NULL; } return (1); default: return (0); } } #define READ_EXECOUT_ARGS 3 /* * read_execout(char *key, char **lp, char *fname, char *line, int linesz) * A simpler, multithreaded implementation of popen(). Used due to * non multithreaded implementation of popen() (it calls vfork()) and a * significant bug in execl(). * Returns 0 on OK or -1 on error. */ static int read_execout(char *key, char **lp, char *fname, char *line, int linesz) { int p[2]; int status = 0; int child_pid; char *args[READ_EXECOUT_ARGS]; FILE *fp0; if (pipe(p) < 0) { syslog(LOG_ERR, "read_execout: Cannot create pipe"); return (-1); } /* setup args for execv */ if (((args[0] = strdup(fname)) == NULL) || ((args[1] = strdup(key)) == NULL)) { if (args[0] != NULL) free(args[0]); syslog(LOG_ERR, "read_execout: Memory allocation failed"); return (-1); } args[2] = NULL; if (trace > 3) trace_prt(1, "\tread_execout: forking .....\n"); switch ((child_pid = fork1())) { case -1: syslog(LOG_ERR, "read_execout: Cannot fork"); return (-1); case 0: /* * Child */ close(p[0]); close(1); if (fcntl(p[1], F_DUPFD, 1) != 1) { syslog(LOG_ERR, "read_execout: dup of stdout failed"); _exit(-1); } close(p[1]); execv(fname, &args[0]); _exit(-1); default: /* * Parent */ close(p[1]); /* * wait for child to complete. Note we read after the * child exits to guarantee a full pipe. */ while (waitpid(child_pid, &status, 0) < 0) { /* if waitpid fails with EINTR, restart */ if (errno != EINTR) { status = -1; break; } } if (status != -1) { if ((fp0 = fdopen(p[0], "r")) != NULL) { *lp = get_line(fp0, fname, line, linesz); fclose(fp0); } else { close(p[0]); status = -1; } } else { close(p[0]); } /* free args */ free(args[0]); free(args[1]); if (trace > 3) { trace_prt(1, "\tread_execout: map=%s key=%s line=%s\n", fname, key, line); } return (status); } } void automountd_do_exec_map(void *cookie, char *argp, size_t arg_size, door_desc_t *dfd, uint_t n_desc) { command_t *command; char line[LINESZ]; char *lp; int rc; command = (command_t *)argp; if (sizeof (*command) != arg_size) { rc = 0; syslog(LOG_ERR, "read_execout: invalid door arguments"); door_return((char *)&rc, sizeof (rc), NULL, 0); } rc = read_execout(command->key, &lp, command->file, line, LINESZ); if (rc != 0) { /* * read_execout returned an error, return 0 to the door_client * to indicate failure */ rc = 0; door_return((char *)&rc, sizeof (rc), NULL, 0); } else { door_return((char *)line, LINESZ, NULL, 0); } trace_prt(1, "automountd_do_exec_map, door return failed %s, %s\n", command->file, strerror(errno)); door_return(NULL, 0, NULL, 0); } static int call_read_execout(char *key, char *fname, char *line, int linesz) { command_t command; door_arg_t darg; int ret; bzero(&command, sizeof (command)); (void) strlcpy(command.file, fname, MAXPATHLEN); (void) strlcpy(command.key, key, MAXOPTSLEN); if (trace >= 1) trace_prt(1, "call_read_execout %s %s\n", fname, key); darg.data_ptr = (char *)&command; darg.data_size = sizeof (command); darg.desc_ptr = NULL; darg.desc_num = 0; darg.rbuf = line; darg.rsize = linesz; ret = door_call(did_exec_map, &darg); return (ret); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * ns_fnmount.c * * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include #include "automount.h" #include "ns_fnutils.h" /* * The maximum sizes of map names, key names, composite names, and status * descriptions, including the trailing '\0'. */ #define MAPNAMESZ (size_t)(AUTOFS_MAXCOMPONENTLEN + 1) #define KEYNAMESZ (size_t)(AUTOFS_MAXCOMPONENTLEN + 1) #define COMPNAMESZ (size_t)(MAPNAMESZ - FNPREFIXLEN + KEYNAMESZ - 2) #define DESCSZ (size_t)512 typedef struct mapent mapent; typedef struct mapline mapline; /* * The name of an attribute. */ static const FN_identifier_t attr_exported = {FN_ID_STRING, 8, "exported"}; /* * Given a request by a particular user to mount the name "key" under * map/context "map", and a set of default mount options, return (in * "res") either a list of mapents giving the mounts that need to be * performed, or a symbolic link to be created for a user-relative * context. If "shallow" is true return, in place of the list of * mapents, a single mapent representing an indirect mount point. * * void * getmapent_fn(char *key, char *map, char *opts, uid_t uid, * bool_t shallow, getmapent_fn_res *res); */ /* * Given a reference, its composite name, default mount options, and a * mapent root, return a list of mapents to mount. If "shallow" is * true return, in place of the list of mapents, a single mapent * representing an indirect mount point. The map and key strings are * pieces of the composite name such that: * "FNPREFIX/cname" == "map/key". */ static mapent * process_ref(const FN_ref_t *ref, const char *cname, char *map, char *key, char *opts, char *root, bool_t shallow, FN_status_t *status); /* * Traverse the namespace to find a frontier below ref along which * future mounts may need to be triggered. Add to mapents the * corresponding direct autofs mount points. * map: map name for ref * maplen: strlen(map) * mntpnt: suffix of map where the current mount request begins * (starts off as "", and grows as we traverse the namespace) * opts: default mount options * status: passed from above to avoid having to allocate one on each call * Works by calling frontier_aux() on each name bound under ref. * Return the new mapents, or free mapents and return NULL on failure. */ static mapent * frontier(mapent *mapents, const FN_ref_t *ref, char *map, size_t maplen, char *mntpnt, char *opts, FN_status_t *status); /* * Called by frontier(), once for each "name" that it finds. map is * passed unchanged from frontier(). ref is the reference named by * "map/name". If ref is found to be along the frontier, add the * corresponding direct autofs mount point to mapents. Otherwise * continue traversing the namespace to find the frontier. Other * arguments and the return value are as for frontier(). */ static mapent * frontier_aux(mapent *mapents, const FN_ref_t *ref, char *map, size_t maplen, char *mntpnt, const char *name, char *opts, FN_status_t *status); /* * Given a reference with an address type of ADDR_HOST and its * composite name, check the attr_exported attribute to determine if * the corresponding directory is exported. Return FALSE on error. */ static bool_t exported(const FN_ref_t *ref, const char *cname, FN_status_t *status); /* * Find a reference's address type and, if "data" is not NULL, its * data string. If there is no address of a known type, set *typep to * NUM_ADDRTYPES; if there are several, stop after finding the first. * Return 0 on success. */ static int addr_from_ref(const FN_ref_t *ref, const char *cname, addrtype_t *typep, char *data, size_t datasz); /* * Decode an address's data into a string. Return 0 on success. */ static int str_from_addr(const char *cname, const FN_ref_addr_t *addr, char str[], size_t strsz); /* * Given a map name and its current length, append "/name". Return * the new length. On error, syslog a warning and return 0. */ static size_t append_mapname(char *map, size_t maplen, const char *name); /* * Concatenate two strings using the given separator. The result is a * newly-allocated string, or NULL on error. */ static char * concat(const char *s1, char sep, const char *s2); /* * Add the "nosuid" option to a mapent. Also check for a sneaky * hacker trying to override this option by manually inserting a * multiple mount entry into the XFN namespace. Return FALSE on error. */ static bool_t safe_mapent(mapent *me); /* * Append "nosuid" to a list of options. The result is a * newly-allocated string, or NULL on error. */ static char * safe_opts(const char *opts); /* * Trim comments and trailing whitespace from ml->linebuf, then * unquote it and leave the result in ml. Return 0 on success. */ static int trim_line(mapline *ml); /* * Determine whether ml contains an option string (such as "-ro") and * nothing else. */ static bool_t opts_only(const mapline *ml); /* * Allocate a new mapent structure. The arguments must have been * malloc'ed, and are owned by the mapent; they are freed if * new_mapent() fails. If any argument is NULL, the call fails and a * memory allocation failure is logged. A root argument of 'noroot' * indicates that the map_root field does not need to be set (it's * only needed in the first of a list of mapents). */ static char *noroot = "[no root]"; static mapent * new_mapent(char *root, char *mntpnt, char *fstype, char *mntopts, char *host, char *dir); /* * Determine whether cname is a user-relative binding -- such as "myself" -- * in the initial context. */ static bool_t is_user_relative(const char *cname); /* * Given the name of a user-relative binding, return an equivalent * name that is not user-relative. */ static char * equiv_name(FN_ctx_t *, const char *cname, FN_status_t *); void getmapent_fn(char *key, char *map, char *opts, uid_t uid, bool_t shallow, getmapent_fn_res *res) { size_t maplen; FN_status_t *status; FN_ctx_t *init_ctx = NULL; int statcode; char cname[COMPNAMESZ]; FN_composite_name_t *compname; FN_ref_t *ref; char mapname[MAPNAMESZ]; char *root; res->type = FN_NONE; res->m_or_l.mapents = NULL; if (init_fn() != 0) { return; } /* * For direct mounts, the key is the entire path, and the map * name already has the final key component appended. Split * apart the map name and key. The "root" of the mapent is * "/key" for indirect mounts, and "" for direct mounts. */ strcpy(mapname, map); if (key[0] == '/') { key = strrchr(key, '/') + 1; *strrchr(mapname, '/') = '\0'; root = strdup(""); } else { root = concat("", '/', key); } map = mapname; maplen = strlen(map); if ((maplen - FNPREFIXLEN + strlen(key)) >= COMPNAMESZ) { if (verbose) { syslog(LOG_ERR, "name %s/%s too long", map, key); } return; } if (maplen == FNPREFIXLEN) { strcpy(cname, key); } else { sprintf(cname, "%s/%s", map + FNPREFIXLEN + 1, key); } status = fn_status_create(); if (status == NULL) { if (verbose) { syslog(LOG_ERR, "Could not create FNS status object"); } return; } init_ctx = _fn_ctx_handle_from_initial_with_uid(uid, 0, status); if (init_ctx == NULL) { logstat(status, "", "No initial context"); goto done; } #ifndef XFN1ENV if (is_user_relative(cname)) { res->type = FN_SYMLINK; res->m_or_l.symlink = equiv_name(init_ctx, cname, status); goto done; } #endif if ((compname = new_cname(cname)) == NULL) { goto done; } ref = fn_ctx_lookup(init_ctx, compname, status); statcode = fn_status_code(status); fn_composite_name_destroy(compname); if (trace > 1 && !shallow) { trace_prt(1, " FNS traversal: %s\n", cname); } if (ref == NULL) { if ((statcode != FN_E_NAME_NOT_FOUND) && (statcode != FN_E_NOT_A_CONTEXT)) { logstat(status, "lookup failed on", cname); } goto done; } res->type = FN_MAPENTS; res->m_or_l.mapents = process_ref(ref, cname, map, key, opts, root, shallow, status); fn_ref_destroy(ref); done: fn_ctx_handle_destroy(init_ctx); fn_status_destroy(status); } static mapent * process_ref(const FN_ref_t *ref, const char *cname, char *map, char *key, char *opts, char *root, bool_t shallow, FN_status_t *status) { addrtype_t addrtype; mapline ml; char *addrdata = ml.linebuf; mapent *mapents; bool_t self; char *homedir; size_t maplen; char *colon; char *nfshost; char *nfsdir; if ((reftype(ref) < NUM_REFTYPES) && (addr_from_ref(ref, cname, &addrtype, addrdata, LINESZ) == 0)) { switch (addrtype) { case ADDR_MOUNT: if (trim_line(&ml) != 0) { return (NULL); } if (opts_only(&ml)) { /* parse_entry() can't handle such lines */ if (macro_expand("&", ml.linebuf, ml.lineqbuf, LINESZ)) { syslog(LOG_ERR, "%s/%s: opts too long (max %d chars)", FNPREFIX, cname, LINESZ - 1); return (NULL); } opts = ml.linebuf + 1; /* skip '-' */ goto indirect; } mapents = parse_entry(key, map, opts, &ml, NULL, 0, TRUE); if (mapents == NULL || !safe_mapent(mapents)) { free_mapent(mapents); return (NULL); } free(mapents->map_root); mapents->map_root = root; break; case ADDR_HOST: /* * Address is of the form "host:dir". * If "dir" is not supplied, it defaults to "/". */ colon = strchr(addrdata, ':'); if (colon == NULL || colon[1] == '\0') { nfsdir = strdup("/"); } else { *colon = '\0'; nfsdir = strdup(colon + 1); } nfshost = strdup(addrdata); /* * If nfshost is the local host, the NFS mount * request will be converted to a loopback * mount. Otherwise check that the file system * is exported. */ if (nfshost != NULL) { self = self_check(nfshost); if (!self && !exported(ref, cname, status)) { if (transient(status)) { return (NULL); } else { goto indirect; } } } mapents = new_mapent(root, strdup(""), strdup("nfs"), safe_opts(opts), nfshost, nfsdir); if (self && !shallow) { return (mapents); } break; case ADDR_USER: homedir = strdup(addrdata); homedir[strcspn(homedir, " \t\r\n")] = '\0'; mapents = new_mapent(root, strdup(""), strdup("lofs"), strdup(opts), strdup(""), homedir); break; } if (mapents == NULL) { return (NULL); } if (shallow) { mapents->map_root = NULL; /* don't free "root" */ free_mapent(mapents); goto indirect; } /* "map" => "map/key" */ if ((maplen = append_mapname(map, strlen(map), key)) == 0) { return (mapents); } return (frontier(mapents, ref, map, maplen, map + maplen, opts, status)); } /* Ref type wasn't recognized. */ indirect: /* Install an indirect autofs mount point. */ return (new_mapent(root, strdup(""), strdup("autofs"), strdup(opts), strdup(""), concat(map, '/', key))); } /* * All that this function really does is call frontier_aux() on every * name bound under ref. The rest is error checking(!) * * The error handling strategy is to reject the entire mount request * (by freeing mapents) if any (potentially) transient error occurs, * and to treat nontransient errors as holes in the affected portions * of the namespace. */ static mapent * frontier(mapent *mapents, const FN_ref_t *ref, char *map, size_t maplen, char *mntpnt, char *opts, FN_status_t *status) { FN_ctx_t *ctx; FN_bindinglist_t *bindings = NULL; FN_ref_t *child_ref; FN_string_t *child_s; const char *child; unsigned int statcode; ctx = fn_ctx_handle_from_ref(ref, XFN2(0) status); if (ctx == NULL) { if (fn_status_code(status) != FN_E_NO_SUPPORTED_ADDRESS) { logstat(status, "from_ref failed for", map); } goto checkerr_return; } bindings = fn_ctx_list_bindings(ctx, empty_cname, status); fn_ctx_handle_destroy(ctx); if (bindings == NULL) { logstat(status, "list_bindings failed for", map); goto checkerr_return; } while ((child_s = fn_bindinglist_next(bindings, &child_ref, status)) != NULL) { child = (const char *)fn_string_str(child_s, &statcode); if (child == NULL) { if (verbose) { syslog(LOG_ERR, "FNS string error listing %s", map); } fn_string_destroy(child_s); goto err_return; } mapents = frontier_aux(mapents, child_ref, map, maplen, mntpnt, child, opts, status); fn_string_destroy(child_s); fn_ref_destroy(child_ref); if (mapents == NULL) { goto noerr_return; } } if (fn_status_is_success(status)) { goto noerr_return; } else { logstat(status, "error while listing", map); /* Fall through to checkerr_return. */ } checkerr_return: if (!transient(status)) { goto noerr_return; } err_return: free_mapent(mapents); mapents = NULL; noerr_return: fn_bindinglist_destroy(bindings XFN1(status)); return (mapents); } static mapent * frontier_aux(mapent *mapents, const FN_ref_t *ref, char *map, size_t maplen, char *mntpnt, const char *name, char *opts, FN_status_t *status) { addrtype_t addrtype; bool_t at_frontier; mapent *me; size_t maplen_save = maplen; char *cname = map + FNPREFIXLEN + 1; /* for error msgs */ if (reftype(ref) >= NUM_REFTYPES) { /* * We could instead install an indirect autofs mount point * here. That would allow, for example, a user to be bound * beneath a file system. */ return (mapents); } /* "map" => "map/name" */ if ((maplen = append_mapname(map, maplen, name)) == 0) { return (mapents); } if (trace > 1) { trace_prt(1, " FNS traversal: %s/\n", cname); } /* * If this is an address type that we know how to mount, then * we have reached the frontier. */ at_frontier = (addr_from_ref(ref, cname, &addrtype, NULL, 0) == 0); /* * For an ADDR_HOST address, treat a non-exported directory as * if the address type were not known: continue searching for * exported subdirectories. */ if (at_frontier && (addrtype == ADDR_HOST)) { if (!exported(ref, cname, status)) { if (transient(status)) { free_mapent(mapents); return (NULL); } else { at_frontier = FALSE; } } } /* * If we have reached the frontier, install a direct autofs * mount point (which will trigger the actual mount if the * user steps on it later). Otherwise, continue traversing * the namespace looking for known address types. */ if (at_frontier) { opts = (opts[0] != '\0') ? concat(opts, ',', "direct") : strdup("direct"); me = new_mapent(noroot, strdup(mntpnt), strdup("autofs"), opts, strdup(""), strdup(map)); if (me != NULL) { /* Link new mapent into list (not at the head). */ me->map_next = mapents->map_next; mapents->map_next = me; } else { free_mapent(mapents); mapents = NULL; } } else { mapents = frontier(mapents, ref, map, maplen, mntpnt, opts, status); } map[maplen_save] = '\0'; /* "map/name" => "map" */ return (mapents); } static bool_t exported(const FN_ref_t *ref, const char *cname, FN_status_t *status) { FN_ctx_t *ctx; FN_attribute_t *attr; ctx = fn_ctx_handle_from_ref(ref, XFN2(0) status); if (ctx == NULL) { logstat(status, "from_ref failed for", cname); return (FALSE); } attr = fn_attr_get(ctx, empty_cname, &attr_exported, XFN2(1) status); fn_ctx_handle_destroy(ctx); switch (fn_status_code(status)) { case FN_SUCCESS: fn_attribute_destroy(attr); break; case FN_E_NO_SUCH_ATTRIBUTE: break; default: logstat(status, "could not get attributes for", cname); } return (attr != NULL); } static int addr_from_ref(const FN_ref_t *ref, const char *cname, addrtype_t *typep, char *data, size_t datasz) { const FN_ref_addr_t *addr; void *iter_pos; addr = fn_ref_first(ref, &iter_pos); if (addr == NULL) { if (verbose) { syslog(LOG_ERR, "FNS ref with no address: %s", cname); } return (-1); } while (addr != NULL) { *typep = addrtype(addr); if (*typep < NUM_ADDRTYPES) { return ((data != NULL) ? str_from_addr(cname, addr, data, datasz) : 0); } addr = fn_ref_next(ref, &iter_pos); } return (-1); } static int str_from_addr(const char *cname, const FN_ref_addr_t *addr, char str[], size_t strsz) { XDR xdr; int res; xdrmem_create(&xdr, (caddr_t)fn_ref_addr_data(addr), fn_ref_addr_length(addr), XDR_DECODE); if (!xdr_string(&xdr, &str, strsz)) { if (verbose) { syslog(LOG_ERR, "Could not decode FNS address for %s", cname); } res = -1; } else { res = 0; } xdr_destroy(&xdr); return (res); } static size_t append_mapname(char *map, size_t maplen, const char *name) { size_t namelen = strlen(name); if (maplen + 1 + namelen >= MAPNAMESZ) { if (verbose) { syslog(LOG_ERR, "FNS name %s/%s too long", map + FNPREFIXLEN + 1, name); } return (0); } sprintf(map + maplen, "/%s", name); return (maplen + 1 + namelen); } static char * concat(const char *s1, char sep, const char *s2) { char *s = malloc(strlen(s1) + 1 + strlen(s2) + 1); if (s != NULL) { sprintf(s, "%s%c%s", s1, sep, s2); } return (s); } static bool_t safe_mapent(mapent *me) { char *opts; if (me->map_next != NULL) { /* Multiple mounts don't belong in XFN namespace. */ return (NULL); } opts = me->map_mntopts; me->map_mntopts = safe_opts(opts); free(opts); return (me->map_mntopts != NULL); } static char * safe_opts(const char *opts) { char *start; size_t len; if (opts[0] == '\0') { return (strdup(MNTOPT_NOSUID)); } /* A quick-and-dirty check to see if "nosuid" is already there. */ start = strstr(opts, MNTOPT_NOSUID); len = sizeof (MNTOPT_NOSUID) - 1; /* "-1" for trailing '\0' */ if (start != NULL) { while (start > opts && isspace(*(start - 1))) { start--; } if ((start == opts || *(start - 1) == ',') && opts[len] == ',' || opts[len] == '\0') { return (strdup(opts)); } } return (concat(opts, ',', MNTOPT_NOSUID)); } static int trim_line(mapline *ml) { char *end; /* pointer to '\0' at end of linebuf */ end = ml->linebuf + strcspn(ml->linebuf, "#"); while ((end > ml->linebuf) && isspace(end[-1])) { end--; } if (end <= ml->linebuf) { return (-1); } *end = '\0'; unquote(ml->linebuf, ml->lineqbuf); return (0); } static bool_t opts_only(const mapline *ml) { const char *s = ml->linebuf; const char *q = ml->lineqbuf; if (*s != '-') { return (FALSE); } for (; *s != '\0'; s++, q++) { if (isspace(*s) && (*q == ' ')) { return (FALSE); } } return (TRUE); } static mapent * new_mapent(char *root, char *mntpnt, char *fstype, char *mntopts, char *host, char *dir) { mapent *me; struct mapfs *mfs; char *mounter = NULL; me = calloc(1, sizeof (*me)); mfs = calloc(1, sizeof (*mfs)); if (fstype != NULL) { mounter = strdup(fstype); } if ((mntpnt == NULL) || (fstype == NULL) || (mntopts == NULL) || (host == NULL) || (dir == NULL) || (me == NULL) || (mfs == NULL) || (mounter == NULL) || (root == NULL)) { log_mem_failure(); free(me); free(mfs); free(mounter); free(root); free(mntpnt); free(fstype); free(mntopts); free(host); free(dir); return (NULL); } me->map_root = (root != noroot) ? root : NULL; me->map_fstype = fstype; me->map_mounter = mounter; me->map_mntpnt = mntpnt; me->map_mntopts = mntopts; me->map_fsw = NULL; me->map_fswq = NULL; me->map_fs = mfs; mfs->mfs_host = host; mfs->mfs_dir = dir; me->map_mntlevel = -1; me->map_modified = FALSE; me->map_faked = FALSE; me->map_err = 0; /* MAPENT_NOERR */ return (me); } #ifndef XFN1ENV /* * User-relative bindings in the initial context, and the leading components * of their non-user-relative equivalents. Leading components are listed in * the order in which they should be tried. Each list is NULL-terminated * (the compiler generously does this for us). * For "myorgunit", for example, we first check if it is equivalent to * "thisorgunit". If not, we translate it into "org/". */ #define MAX_LEADS 3 static struct { const char *binding; const char *leads[MAX_LEADS + 1]; } user_rel[] = { {"thisuser", {"user", "thisorgunit", "org"}}, {"myself", {"user", "thisorgunit", "org"}}, {"_myself", {"_user", "_thisorgunit", "_orgunit"}}, {"myorgunit", {"thisorgunit", "org"}}, {"_myorgunit", {"_thisorgunit", "_orgunit"}}, {"myens", {"thisens"}}, {"_myens", {"_thisens"}} }; static bool_t is_user_relative(const char *cname) { int i; for (i = 0; i < sizeof (user_rel) / sizeof (user_rel[0]); i++) { if (strcmp(cname, user_rel[i].binding) == 0) { return (TRUE); } } return (FALSE); } static char * equiv_name(FN_ctx_t *ctx, const char *cname, FN_status_t *status) { FN_composite_name_t *name; FN_string_t *leading_name; FN_composite_name_t *equiv; FN_string_t *equiv_string; const char *equiv_str; char *equiv_str_dup; const char **leads; unsigned int stat; int i; for (i = 0; i < sizeof (user_rel) / sizeof (user_rel[0]); i++) { if (strcmp(cname, user_rel[i].binding) == 0) { break; } } if ((name = new_cname(cname)) == NULL) { return (NULL); } leads = user_rel[i].leads; /* array of leading names to try */ do { leading_name = fn_string_from_str((unsigned char *)*leads); if (leading_name == NULL) { log_mem_failure(); fn_composite_name_destroy(name); return (NULL); } equiv = prelim_fn_ctx_equivalent_name(ctx, name, leading_name, status); fn_string_destroy(leading_name); } while (equiv == NULL && *++leads != NULL); fn_composite_name_destroy(name); if (equiv == NULL) { if (transient(status)) { logstat(status, "could not find equivalent of", cname); } return (NULL); } equiv_string = fn_string_from_composite_name(equiv, &stat); fn_composite_name_destroy(equiv); if (equiv_string == NULL) { log_mem_failure(); return (NULL); } equiv_str = (const char *)fn_string_str(equiv_string, &stat); if (equiv_str == NULL || (equiv_str_dup = strdup(equiv_str)) == NULL) { log_mem_failure(); fn_string_destroy(equiv_string); return (NULL); } fn_string_destroy(equiv_string); return (equiv_str_dup); } #endif /* XFN1ENV */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * ns_fnreaddir.c * * Copyright (c) 1995 - 1996, by Sun Microsystems, Inc. * All rights reserved. */ #include #include #include #include #include #include #include "automount.h" #include "ns_fnutils.h" /* * Given the name of an XFN map, create a list of the map entries for a * given user. Set error to zero on success. * * extern void * getmapkeys_fn(const char *map, struct dir_entry **, int *error, * int *cache_time, uid_t); */ /* * Given a multi-component composite name, construct the corresponding * context handle and the context handle of its prefix. The prefix is * that part of the name up to (and possibly including) the last slash * in the name. Return zero on success. * * eg: user/jane/service => user/jane + service * org/ssi.eng/user => org/ssi.eng/ + user */ static int get_contexts(const FN_composite_name_t *, FN_ctx_t **ctxp, FN_ctx_t **prefix_ctxp, FN_ctx_t *init_ctx, FN_status_t *); /* * Split a multi-component composite name into its last component and * its other components. Return zero on success. */ static int split_cname(const FN_composite_name_t *name, FN_composite_name_t **last, FN_composite_name_t **lead); /* * Given a context and its prefix context (defined above), determine * whether the context, its NNS context, or both should be listed. * (The syntaxes of the contexts are used to help make this * determination.) Add the subdirectories of the appropriate * context(s) to the dir_entry list. Return zero on success. * * eg: "ls /xfn/user => list context only * "ls /xfn/org/ssi.eng" => list NNS only * "ls /xfn/.../c=us" => list context and NNS */ static int list_ctx_and_or_nns(FN_ctx_t *ctx, FN_ctx_t *prefix_ctx, struct dir_entry **, FN_status_t *); /* * Given a context and its prefix context (defined above), return true * if the NNS of the context should be listed but the context itself * should not. */ static bool_t need_nns_only(FN_ctx_t *ctx, FN_ctx_t *prefix_ctx, FN_status_t *); /* * Return true if both the given context and its NNS should be listed. */ static bool_t need_ctx_and_nns(FN_ctx_t *, FN_status_t *); /* * Add the subdirectories of a context to the dir_entry list. Return * zero on success. */ static int list_ctx(FN_ctx_t *, struct dir_entry **, FN_status_t *); /* * Given a context and its name relative to the root of its rightmost * naming system, add the context's subdirectories to the dir_entry * list. If syntax is non-NULL recursively list names until a context * with a different syntax is encountered, otherwise list one level * only. May modify "name". Return zero on success. * * eg: For the context org/eng with syntax "dot-separated, right-to-left", * the compound name "eng" would be passed in, and the following might * be added to the dir_entry list: * ssi.eng * feds.ssi.eng * ste.eng */ static int list_ctx_aux(FN_ctx_t *, FN_compound_name_t *name, const FN_attrset_t *syntax, struct dir_entry **, FN_status_t *); /* * Add a name to a dir_entry list. Return zero on success. */ static int add_name_to_dirlist(const FN_compound_name_t *, struct dir_entry **); /* * Return true if a set of syntax attributes correspond to a * hierarchical namespace with a slash separator. Return false on * error. */ static bool_t slash_hierarchy(const FN_attrset_t *syntax); /* * Return true if a set of syntax attributes correspond to a * hierarchical namespace with a separator other than a slash. * Return false on error. */ static bool_t non_slash_hierarchy(const FN_attrset_t *syntax); /* * Return true if two syntax attribute sets are equal. */ static bool_t syntax_attrs_equal(const FN_attrset_t *, const FN_attrset_t *); /* * Return a value of a given attribute in an attribute set, or NULL * on error. */ static const FN_attrvalue_t * get_attrval(const FN_attrset_t *, const FN_identifier_t *attr_id); /* * Lookup a name and return the corresponding context handle. On * error return NULL and, if "log" is true or the error is transient, * log an error message. */ static FN_ctx_t * lookup_ctx(FN_ctx_t *, const FN_composite_name_t *, bool_t log, FN_status_t *); /* * Unlike during a lookup or mount, transient errors are tolerated. A * potentially transient error during a readdir() (such as no response * from an X.500 server) could result in an incomplete listing, but at * least readdir() will return everything that it can. Note that it * is still possible to mount a directory that for some reason did not * show up in a prior readdir(). */ void getmapkeys_fn(const char *map, struct dir_entry **entries_p, int *error, int *cache_time, uid_t uid) { FN_composite_name_t *name; FN_status_t *status; FN_ctx_t *init_ctx; FN_ctx_t *ctx; FN_ctx_t *prefix_ctx; struct dir_entry *p; *cache_time = RDDIR_CACHE_TIME; if ((init_fn() != 0) || (status = fn_status_create()) == NULL) { log_mem_failure(); *error = -1; return; } init_ctx = _fn_ctx_handle_from_initial_with_uid(uid, 0, status); if (init_ctx == NULL) { logstat(status, "", "No initial context"); fn_status_destroy(status); return; } if (strcmp(map, FNPREFIX) == 0) { /* * List the initial context. * Contents of initial ctx is user-relative */ *cache_time = 0; *error = list_ctx(init_ctx, entries_p, status); } else if (strcmp(map, FNPREFIX "/_dns") == 0) { /* Cannot list DNS; report success but no entries. */ *cache_time = 1000000; /* no sense trying again */ *error = 0; } else { if (strcmp(map, FNPREFIX "/...") == 0) { /* List X.500 but not DNS. */ name = new_cname("_x500"); } else { name = new_cname(map + FNPREFIXLEN + 1); } if (name == NULL) { *error = -1; } else if (fn_composite_name_count(name) == 1) { /* List an atomic name. */ ctx = lookup_ctx(init_ctx, name, TRUE, status); if (ctx != NULL) { *error = list_ctx_and_or_nns(ctx, init_ctx, entries_p, status); fn_ctx_handle_destroy(ctx); } else { *error = -1; } } else { /* List a multi-component name. */ *error = get_contexts(name, &ctx, &prefix_ctx, init_ctx, status); if (*error == 0) { *error = list_ctx_and_or_nns(ctx, prefix_ctx, entries_p, status); fn_ctx_handle_destroy(ctx); fn_ctx_handle_destroy(prefix_ctx); } } fn_composite_name_destroy(name); } fn_status_destroy(status); fn_ctx_handle_destroy(init_ctx); if (*error == 0) { /* * create the binary tree of entries */ for (p = *entries_p; p != NULL; p = p->next) btree_enter(entries_p, p); } } static int get_contexts(const FN_composite_name_t *name, FN_ctx_t **ctxp, FN_ctx_t **prefix_ctxp, FN_ctx_t *init_ctx, FN_status_t *status) { FN_composite_name_t *prefix = NULL; FN_composite_name_t *suffix = NULL; FN_ctx_t *nns_ctx; /* * Break a name such as "pre/fix/suffix" into "pre/fix/" and * "suffix". If that fails, try "pre/fix" and "suffix". This * can be more efficient than doing it the reverse order. */ if (split_cname(name, &suffix, &prefix) != 0) { return (-1); } *ctxp = NULL; *prefix_ctxp = lookup_ctx(init_ctx, prefix, TRUE, status); fn_composite_name_destroy(prefix); if (*prefix_ctxp != NULL) { nns_ctx = lookup_ctx(*prefix_ctxp, slash_cname, FALSE, status); if (nns_ctx != NULL) { *ctxp = lookup_ctx(nns_ctx, suffix, FALSE, status); if (*ctxp != NULL) { fn_ctx_handle_destroy(*prefix_ctxp); *prefix_ctxp = nns_ctx; } else { fn_ctx_handle_destroy(nns_ctx); } } if (*ctxp == NULL) { *ctxp = lookup_ctx(*prefix_ctxp, suffix, FALSE, status); } } fn_composite_name_destroy(suffix); return (*ctxp != NULL ? 0 : -1); } static int split_cname(const FN_composite_name_t *name, FN_composite_name_t **last, FN_composite_name_t **lead) { void *iter; (void) fn_composite_name_last(name, &iter); *last = fn_composite_name_suffix(name, iter); *lead = fn_composite_name_prefix(name, iter); if (*last == NULL || *lead == NULL) { log_mem_failure(); fn_composite_name_destroy(*last); fn_composite_name_destroy(*lead); return (-1); } return (0); } static int list_ctx_and_or_nns(FN_ctx_t *ctx, FN_ctx_t *prefix_ctx, struct dir_entry **entries_p, FN_status_t *status) { FN_ctx_t *nns_ctx; int rc; if (!need_nns_only(ctx, prefix_ctx, status)) { if (list_ctx(ctx, entries_p, status) != 0) { return (-1); } if (!need_ctx_and_nns(ctx, status)) { return (0); } } nns_ctx = lookup_ctx(ctx, slash_cname, FALSE, status); if (nns_ctx == NULL) { return (0); } rc = list_ctx(nns_ctx, entries_p, status); fn_ctx_handle_destroy(nns_ctx); return (rc); } /* * True if ctx has a hierarchical syntax with a non-slash separator * and prefix_ctx either has the same syntax or does not provide any * syntax ("..." should be the only example of the latter condition). */ static bool_t need_nns_only(FN_ctx_t *ctx, FN_ctx_t *prefix_ctx, FN_status_t *status) { FN_attrset_t *syn; FN_attrset_t *prefix_syn; bool_t retval; syn = fn_ctx_get_syntax_attrs(ctx, empty_cname, status); if (syn == NULL || !non_slash_hierarchy(syn)) { fn_attrset_destroy(syn); return (FALSE); } /* * ctx is hierarchical and not slash-separated. How about prefix_ctx? */ prefix_syn = fn_ctx_get_syntax_attrs(prefix_ctx, empty_cname, status); retval = (prefix_syn == NULL) || syntax_attrs_equal(syn, prefix_syn); fn_attrset_destroy(syn); fn_attrset_destroy(prefix_syn); return (retval); } /* * True if ctx has a slash-separated hierarchical syntax. */ static bool_t need_ctx_and_nns(FN_ctx_t *ctx, FN_status_t *status) { FN_attrset_t *syn; bool_t retval; syn = fn_ctx_get_syntax_attrs(ctx, empty_cname, status); if (syn == NULL) { return (FALSE); } retval = slash_hierarchy(syn); fn_attrset_destroy(syn); return (retval); } static int list_ctx(FN_ctx_t *ctx, struct dir_entry **entries_p, FN_status_t *status) { FN_attrset_t *syntax; FN_compound_name_t *name; int retval; syntax = fn_ctx_get_syntax_attrs(ctx, empty_cname, status); if (syntax == NULL) { logstat(status, "", "bad syntax attributes"); return (-1); } name = fn_compound_name_from_syntax_attrs(syntax, empty_string, status); if (name == NULL) { logstat(status, "", "could not create compound name"); fn_attrset_destroy(syntax); return (-1); } if (!non_slash_hierarchy(syntax)) { fn_attrset_destroy(syntax); syntax = NULL; } retval = list_ctx_aux(ctx, name, syntax, entries_p, status); fn_attrset_destroy(syntax); fn_compound_name_destroy(name); return (retval); } static int list_ctx_aux(FN_ctx_t *ctx, FN_compound_name_t *name, const FN_attrset_t *syntax, struct dir_entry **entries_p, FN_status_t *status) { FN_bindinglist_t *bindings; FN_string_t *child; FN_ref_t *ref; unsigned int stat; int rc = 0; void *iter; bindings = fn_ctx_list_bindings(ctx, empty_cname, status); if (bindings == NULL) { return (0); } while ((child = fn_bindinglist_next(bindings, &ref, status)) != NULL) { if (fn_compound_name_append_comp(name, child, &stat) == 0) { rc = -1; break; } if (add_name_to_dirlist(name, entries_p) != 0) { rc = -1; break; } if (syntax != NULL) { /* Traverse hierarchy. */ ctx = fn_ctx_handle_from_ref(ref, XFN2(0) status); if (ctx != NULL) { rc = list_ctx_aux(ctx, name, syntax, entries_p, status); fn_ctx_handle_destroy(ctx); if (rc != 0) { break; } } } fn_ref_destroy(ref); fn_string_destroy(child); (void) fn_compound_name_last(name, &iter); (void) fn_compound_name_next(name, &iter); (void) fn_compound_name_delete_comp(name, &iter); } fn_string_destroy(child); fn_bindinglist_destroy(bindings XFN1(status)); return (rc); } static int add_name_to_dirlist(const FN_compound_name_t *name, struct dir_entry **entries_p) { FN_string_t *string; char *str; unsigned int stat; struct dir_entry *entry; string = fn_string_from_compound_name(name); if (string == NULL) { log_mem_failure(); return (-1); } str = (char *)fn_string_str(string, &stat); if (str != NULL) { str = auto_rddir_strdup(str); } fn_string_destroy(string); if (str == NULL) { log_mem_failure(); return (-1); } /* LINTED pointer alignment */ entry = (struct dir_entry *) auto_rddir_malloc(sizeof (*entry)); if (entry == NULL) { log_mem_failure(); free(str); return (-1); } (void) memset((char *)entry, 0, sizeof (*entry)); entry->name = str; entry->next = *entries_p; *entries_p = entry; return (0); } /* * Identifiers of syntax attributes for direction and separator. */ static const FN_identifier_t syntax_direction = { FN_ID_STRING, sizeof ("fn_std_syntax_direction") - 1, "fn_std_syntax_direction" }; static const FN_identifier_t syntax_separator = { FN_ID_STRING, sizeof ("fn_std_syntax_separator") - 1, "fn_std_syntax_separator" }; static bool_t slash_hierarchy(const FN_attrset_t *syntax) { const FN_attrvalue_t *dir = get_attrval(syntax, &syntax_direction); const FN_attrvalue_t *sep = get_attrval(syntax, &syntax_separator); return (dir != NULL && memcmp("flat", dir->contents, dir->length) != 0 && sep != NULL && memcmp("/", sep->contents, sep->length) == 0); } static bool_t non_slash_hierarchy(const FN_attrset_t *syntax) { const FN_attrvalue_t *dir = get_attrval(syntax, &syntax_direction); const FN_attrvalue_t *sep = get_attrval(syntax, &syntax_separator); return (dir != NULL && memcmp("flat", dir->contents, dir->length) != 0 && sep != NULL && memcmp("/", sep->contents, sep->length) != 0); } static bool_t syntax_attrs_equal(const FN_attrset_t *syn1, const FN_attrset_t *syn2) { const FN_attribute_t *attr; const FN_attrvalue_t *val1; const FN_attrvalue_t *val2; void *iter1; void *iter2; if (fn_attrset_count(syn1) != fn_attrset_count(syn2)) { return (FALSE); } for (attr = fn_attrset_first(syn1, &iter1); attr != NULL; attr = fn_attrset_next(syn1, &iter1)) { val1 = fn_attribute_first(attr, &iter2); val2 = get_attrval(syn2, fn_attribute_identifier(attr)); if ((val1 == NULL && val2 != NULL) || (val1 != NULL && val2 == NULL)) { return (FALSE); } if (val1 != NULL && val2 != NULL) { if (val1->length != val2->length || memcmp(val1->contents, val2->contents, val1->length) != 0) { return (FALSE); } } } return (TRUE); } static const FN_attrvalue_t * get_attrval(const FN_attrset_t *attrs, const FN_identifier_t *attr_id) { const FN_attribute_t *attr; void *iter; attr = fn_attrset_get(attrs, attr_id); if (attr != NULL) { return (fn_attribute_first(attr, &iter)); } else { return (NULL); } } static FN_ctx_t * lookup_ctx(FN_ctx_t *ctx, const FN_composite_name_t *name, bool_t log, FN_status_t *status) { FN_ref_t *ref; char *msg; ref = fn_ctx_lookup(ctx, name, status); if (ref == NULL) { ctx = NULL; msg = "lookup failed"; } else { ctx = fn_ctx_handle_from_ref(ref, XFN2(0) status); fn_ref_destroy(ref); if (ctx == NULL) { msg = "could not construct context handle"; } } if (ctx == NULL && verbose && (log || transient(status))) { logstat(status, "", msg); } return (ctx); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * ns_fnutils.c * * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include "automount.h" #include "ns_fnutils.h" /* * FNS file system reference and address types. Each array is indexed * using the corresponding enumeration (reftype_t or addrtype_t). */ const char *reftypes[] = { "onc_fn_fs", }; const char *addrtypes[] = { "onc_fn_fs_mount", "onc_fn_fs_host", "onc_fn_fs_user", }; FN_string_t *empty_string = NULL; FN_composite_name_t *empty_cname = NULL; FN_composite_name_t *slash_cname = NULL; int init_fn(void) { static mutex_t init_lock = DEFAULTMUTEX; if (slash_cname != NULL) { return (0); } mutex_lock(&init_lock); if (empty_string == NULL) { if ((empty_string = fn_string_create()) == NULL) { log_mem_failure(); goto unlock; } } if (empty_cname == NULL) { if ((empty_cname = new_cname("")) == NULL) { goto unlock; } } if (slash_cname == NULL) { if ((slash_cname = new_cname("/")) == NULL) { goto unlock; } } unlock: mutex_unlock(&init_lock); return ((slash_cname != NULL) ? 0 : -1); } FN_composite_name_t * new_cname(const char *str) { FN_string_t *string; FN_composite_name_t *cname; string = fn_string_from_str((unsigned char *)str); if (string == NULL) { if (verbose) { syslog(LOG_ERR, "Could not create FNS string object"); } return (NULL); } cname = fn_composite_name_from_string(string); fn_string_destroy(string); if ((cname == NULL) && verbose) { syslog(LOG_ERR, "Could not create FNS composite name object"); } return (cname); } reftype_t reftype(const FN_ref_t *ref) { reftype_t rtype; for (rtype = 0; rtype < NUM_REFTYPES; rtype++) { if (ident_str_equal(fn_ref_type(ref), reftypes[rtype])) { break; } } return (rtype); } addrtype_t addrtype(const FN_ref_addr_t *addr) { addrtype_t atype; const FN_identifier_t *ident = fn_ref_addr_type(addr); for (atype = 0; atype < NUM_ADDRTYPES; atype++) { if (ident_str_equal(ident, addrtypes[atype])) { break; } } return (atype); } bool_t ident_equal(const FN_identifier_t *id1, const FN_identifier_t *id2) { return ((id1->format == id2->format) && (id1->length == id2->length) && (memcmp(id1->contents, id2->contents, id1->length) == 0)); } bool_t ident_str_equal(const FN_identifier_t *id, const char *str) { return ((id->format == FN_ID_STRING) && (id->length == strlen(str)) && (strncmp(str, id->contents, id->length) == 0)); } void logstat(const FN_status_t *status, const char *msg1, const char *msg2) { FN_string_t *desc_string; const char *desc = NULL; if (verbose) { desc_string = fn_status_description(status, DETAIL, NULL); if (desc_string != NULL) { desc = (const char *)fn_string_str(desc_string, NULL); } if (desc == NULL) { desc = "(no status description)"; } syslog(LOG_ERR, "FNS %s %s: %s (%u)", msg1, msg2, desc, fn_status_code(status)); fn_string_destroy(desc_string); } } bool_t transient(const FN_status_t *status) { unsigned int statcode; statcode = fn_status_code(status); if (statcode == FN_E_LINK_ERROR) { statcode = fn_status_link_code(status); } switch (statcode) { case FN_E_COMMUNICATION_FAILURE: case FN_E_CTX_UNAVAILABLE: case FN_E_INSUFFICIENT_RESOURCES: case FN_E_INVALID_ENUM_HANDLE: case FN_E_PARTIAL_RESULT: case FN_E_UNSPECIFIED_ERROR: return (TRUE); default: return (FALSE); } } void log_mem_failure(void) { if (verbose) { syslog(LOG_ERR, "Memory allocation failed"); } } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * ns_fnutils.h * * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _NS_FNUTILS_H #define _NS_FNUTILS_H #include #include #ifdef __cplusplus extern "C" { #endif /* * Enable compilation for either XFN1 (pre-2.6) or XFN2 environment. */ #ifdef XFN1ENV #define XFN1(x) /* cstyle */, x #define XFN2(x) #define _fn_ctx_handle_from_initial_with_uid(uid, auth, status) \ fn_ctx_handle_from_initial(status) #else #define XFN1(x) #define XFN2(x) x, #endif /* * FNS file system reference and address types. Each (char *) array is indexed * using the corresponding enumeration. */ extern const char *reftypes[]; typedef enum { REF_FN_FS, NUM_REFTYPES /* Not a ref type, but rather a count of them */ } reftype_t; extern const char *addrtypes[]; typedef enum { ADDR_MOUNT, ADDR_HOST, ADDR_USER, NUM_ADDRTYPES /* Not an addr type, but rather a count of them */ } addrtype_t; /* * Initialization for FNS. Return 0 on success. */ extern int init_fn(void); /* * Allocate a new composite name. On error, log an error message and * return NULL. */ extern FN_composite_name_t * new_cname(const char *); /* * Return the type of a reference, or NUM_REFTYPES if the type is unknown. */ extern reftype_t reftype(const FN_ref_t *); /* * Return the type of an address, or NUM_ADDRTYPES if the type is unknown. */ extern addrtype_t addrtype(const FN_ref_addr_t *); /* * Determine whether two identifiers match. */ extern bool_t ident_equal(const FN_identifier_t *, const FN_identifier_t *); /* * Determine whether an identifier and a string match. */ extern bool_t ident_str_equal(const FN_identifier_t *, const char *); /* * Syslog an error message and status info (with detail level DETAIL) * if "verbose" is set. */ #define DETAIL 0 extern void logstat(const FN_status_t *, const char *msg1, const char *msg2); /* * Determine whether an error is potentially transient. */ extern bool_t transient(const FN_status_t *); /* * Log a memory allocation failure if "verbose" is true. */ extern void log_mem_failure(void); extern FN_ctx_t * _fn_ctx_handle_from_initial_with_uid(uid_t, unsigned int, FN_status_t *); extern FN_string_t *empty_string; extern FN_composite_name_t *empty_cname; extern FN_composite_name_t *slash_cname; /* "/" */ #ifdef __cplusplus } #endif #endif /* _NS_FNUTILS_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * ns_generic.c * * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include #include #include #include #include "automount.h" /* * Each name service is represented by a ns_info structure. */ struct ns_info { char *ns_name; /* service name */ void (*ns_init)(); /* initialization routine */ int (*ns_getmapent)(); /* get map entry given key */ int (*ns_loadmaster)(); /* load master map */ int (*ns_loaddirect)(); /* load direct map */ int (*ns_getmapkeys)(); /* readdir */ }; static struct ns_info ns_info[] = { "files", init_files, getmapent_files, loadmaster_files, loaddirect_files, getmapkeys_files, /* Hammerhead: "ldap" backend removed (libsldap chain removal). */ "nis", init_nis, getmapent_nis, loadmaster_nis, loaddirect_nis, getmapkeys_nis, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; static struct ns_info *get_next_ns(struct __nsw_lookup **, int); void ns_setup(char **stack, char ***stkptr) { struct ns_info *nsp; for (nsp = ns_info; nsp->ns_name; nsp++) { nsp->ns_init(stack, stkptr); } } static struct ns_info * get_next_ns(curr_ns, curr_nserr) struct __nsw_lookup **curr_ns; int curr_nserr; { static struct __nsw_switchconfig *conf = NULL; enum __nsw_parse_err pserr; struct __nsw_lookup *lkp; struct ns_info *nsp; if (conf == NULL) { /* __nsw_getconfig() is protected by a lock */ conf = __nsw_getconfig("automount", &pserr); if (conf == NULL) { return (NULL); } } if (*curr_ns == NULL) /* first time */ lkp = conf->lookups; else { lkp = *curr_ns; /* __NSW_ACTION is MT-Safe */ if (__NSW_ACTION(lkp, curr_nserr) == __NSW_RETURN) return (NULL); lkp = lkp->next; } for (; lkp; lkp = lkp->next) { for (nsp = ns_info; nsp->ns_name; nsp++) { if (strcmp(lkp->service_name, nsp->ns_name) == 0) { *curr_ns = lkp; return (nsp); } } /* * Note: if we get here then we've found * an unsupported name service. */ } return (NULL); } int getmapent(key, mapname, ml, stack, stkptr, iswildcard, isrestricted) char *key, *mapname; struct mapline *ml; char **stack, ***stkptr; bool_t *iswildcard; bool_t isrestricted; { struct __nsw_lookup *curr_ns = NULL; int ns_err = __NSW_SUCCESS; struct ns_info *nsp; if (strcmp(mapname, "-hosts") == 0) { (void) strcpy(ml->linebuf, "-hosts"); return (__NSW_SUCCESS); } if (*mapname == '/') /* must be a file */ return (getmapent_files(key, mapname, ml, stack, stkptr, iswildcard, isrestricted)); while ((nsp = get_next_ns(&curr_ns, ns_err)) != NULL) { ns_err = nsp->ns_getmapent(key, mapname, ml, stack, stkptr, iswildcard, isrestricted); if (ns_err == __NSW_SUCCESS) return (__NSW_SUCCESS); } return (__NSW_UNAVAIL); } int loadmaster_map(mapname, defopts, stack, stkptr) char *mapname, *defopts; char **stack, ***stkptr; { struct __nsw_lookup *curr_ns = NULL; int ns_err = __NSW_SUCCESS; struct ns_info *nsp; if (*mapname == '/') /* must be a file */ return (loadmaster_files(mapname, defopts, stack, stkptr)); while ((nsp = get_next_ns(&curr_ns, ns_err)) != NULL) { ns_err = nsp->ns_loadmaster(mapname, defopts, stack, stkptr); if (ns_err == __NSW_SUCCESS) return (__NSW_SUCCESS); } return (__NSW_UNAVAIL); } int loaddirect_map(mapname, localmap, defopts, stack, stkptr) char *mapname, *localmap, *defopts; char **stack, ***stkptr; { struct __nsw_lookup *curr_ns = NULL; int ns_err = __NSW_SUCCESS; struct ns_info *nsp; if (*mapname == '/') /* must be a file */ return (loaddirect_files(mapname, localmap, defopts, stack, stkptr)); while ((nsp = get_next_ns(&curr_ns, ns_err)) != NULL) { ns_err = nsp->ns_loaddirect(mapname, localmap, defopts, stack, stkptr); if (ns_err == __NSW_SUCCESS) return (__NSW_SUCCESS); } return (__NSW_UNAVAIL); } int gethostkeys(mapname, list, error, cache_time) char *mapname; struct dir_entry **list; int *error; int *cache_time; { char *buffer, **p; int bufferlen = 1000; struct dir_entry *last = NULL; struct hostent ent; #ifdef lint mapname = mapname; #endif *cache_time = RDDIR_CACHE_TIME * 2; *error = 0; if (trace > 1) trace_prt(1, "gethostkeys called\n"); if (sethostent(1)) { syslog(LOG_ERR, "gethostkeys: sethostent failed"); *error = EIO; return (__NSW_UNAVAIL); } buffer = (char *)malloc(bufferlen); if (buffer == NULL) { syslog(LOG_ERR, "gethostkeys: malloc of buffer failed"); *error = ENOMEM; return (__NSW_UNAVAIL); } while (gethostent_r(&ent, buffer, bufferlen, error)) { /* * add canonical name */ if (add_dir_entry(ent.h_name, list, &last)) { *error = ENOMEM; goto done; } if (ent.h_aliases == NULL) goto done; /* no aliases */ for (p = ent.h_aliases; *p != 0; p++) { if (strcmp(*p, ent.h_name) != 0) { /* * add alias only if different * from canonical name */ if (add_dir_entry(*p, list, &last)) { *error = ENOMEM; goto done; } } } assert(last != NULL); } done: if (*list != NULL) { /* * list of entries found */ *error = 0; } endhostent(); return (__NSW_SUCCESS); } /* * enumerate all entries in the map in the various name services. */ int getmapkeys(mapname, list, error, cache_time, stack, stkptr, uid) char *mapname; struct dir_entry **list; int *error; int *cache_time; char **stack, ***stkptr; uid_t uid; { struct __nsw_lookup *curr_ns = NULL; int ns_err = __NSW_SUCCESS; int success = 0; struct ns_info *nsp; if (*mapname == '/') /* must be a file */ return (getmapkeys_files(mapname, list, error, cache_time, stack, stkptr)); if (strcmp(mapname, "-hosts") == 0) { return (gethostkeys(mapname, list, error, cache_time)); } while ((nsp = get_next_ns(&curr_ns, ns_err)) != NULL) { ns_err = nsp->ns_getmapkeys(mapname, list, error, cache_time, stack, stkptr); if (*error == 0) { /* * return success if listing was successful * for at least one name service */ success++; } /* * XXX force next name service */ if (ns_err != __NSW_UNAVAIL) ns_err = __NSW_NOTFOUND; } if (success) { /* * if succeeded at least once, return error=0 */ *error = 0; }; return (success ? __NSW_SUCCESS : __NSW_NOTFOUND); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * ns_nis.c * * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "automount.h" #define KEY 0 #define CONTENTS 1 static int replace_undscr_by_dot(char *); static int nis_err(int); static char nis_mydomain[YPMAXDOMAIN]; struct dir_cbdata { struct dir_entry **list; struct dir_entry *last; int error; }; static int readdir_callback(int, char *, int, const char *, int, struct dir_cbdata *); void init_nis(char **stack, char ***stkptr) { #ifdef lint stack = stack; stkptr = stkptr; #endif /* lint */ (void) sysinfo(SI_SRPC_DOMAIN, nis_mydomain, sizeof (nis_mydomain)); } /*ARGSUSED*/ int getmapent_nis(key, map, ml, stack, stkptr, iswildcard, isrestricted) char *key, *map; struct mapline *ml; char **stack; char ***stkptr; bool_t *iswildcard; bool_t isrestricted; { char *nisline = NULL; char *my_map = NULL; char *lp, *lq; int nislen, len; int nserr; if (iswildcard) *iswildcard = FALSE; nserr = yp_match(nis_mydomain, map, key, strlen(key), &nisline, &nislen); if (nserr == YPERR_MAP) { my_map = strdup(map); if (my_map == NULL) { syslog(LOG_ERR, "getmapent_nis: memory alloc failed: %m"); return (__NSW_UNAVAIL); } if (replace_undscr_by_dot(my_map)) nserr = yp_match(nis_mydomain, my_map, key, strlen(key), &nisline, &nislen); } if (nserr) { if (nserr == YPERR_KEY) { /* * Try the default entry "*" */ if (my_map == NULL) nserr = yp_match(nis_mydomain, map, "*", 1, &nisline, &nislen); else nserr = yp_match(nis_mydomain, my_map, "*", 1, &nisline, &nislen); if (!nserr && iswildcard) *iswildcard = TRUE; } else { if (verbose) syslog(LOG_ERR, "%s: %s", map, yperr_string(nserr)); nserr = 1; } } if (my_map != NULL) free(my_map); nserr = nis_err(nserr); if (nserr) goto done; /* * at this point we are sure that yp_match succeeded * so massage the entry by * 1. ignoring # and beyond * 2. trim the trailing whitespace */ if (lp = strchr(nisline, '#')) *lp = '\0'; len = strlen(nisline); if (len == 0) { nserr = __NSW_NOTFOUND; goto done; } lp = &nisline[len - 1]; while (lp > nisline && isspace(*lp)) *lp-- = '\0'; if (lp == nisline) { nserr = __NSW_NOTFOUND; goto done; } (void) strcpy(ml->linebuf, nisline); lp = ml->linebuf; lq = ml->lineqbuf; unquote(lp, lq); /* now we have the correct line */ nserr = __NSW_SUCCESS; done: if (nisline) free((char *)nisline); return (nserr); } int loadmaster_nis(mapname, defopts, stack, stkptr) char *mapname, *defopts; char **stack; char ***stkptr; { int first, err; char *key, *nkey, *val; int kl, nkl, vl; char dir[256], map[256], qbuff[256]; char *pmap, *opts, *my_mapname; int count = 0; first = 1; key = NULL; kl = 0; nkey = NULL; nkl = 0; val = NULL; vl = 0; /* * need a private copy of mapname, because we may change * the underscores by dots. We however do not want the * orignal to be changed, as we may want to use the * original name in some other name service */ my_mapname = strdup(mapname); if (my_mapname == NULL) { syslog(LOG_ERR, "loadmaster_yp: memory alloc failed: %m"); /* not the name svc's fault but ... */ return (__NSW_UNAVAIL); } for (;;) { if (first) { first = 0; err = yp_first(nis_mydomain, my_mapname, &nkey, &nkl, &val, &vl); if ((err == YPERR_MAP) && (replace_undscr_by_dot(my_mapname))) err = yp_first(nis_mydomain, my_mapname, &nkey, &nkl, &val, &vl); if ((err == YPERR_DOMAIN) || (err == YPERR_YPBIND)) { syslog(LOG_ERR, "can't read nis map %s: %s - retrying", my_mapname, yperr_string(err)); while ((err == YPERR_DOMAIN) || (err == YPERR_YPBIND)) { (void) sleep(20); err = yp_first(nis_mydomain, my_mapname, &nkey, &nkl, &val, &vl); } syslog(LOG_ERR, "nis map %s: read OK.", my_mapname); } } else { err = yp_next(nis_mydomain, my_mapname, key, kl, &nkey, &nkl, &val, &vl); } if (err) { if (err != YPERR_NOMORE && err != YPERR_MAP) if (verbose) syslog(LOG_ERR, "%s: %s", my_mapname, yperr_string(err)); break; } if (key) free(key); key = nkey; kl = nkl; if (kl >= 256 || vl >= 256) break; if (kl < 2 || vl < 1) break; if (isspace(*key) || *key == '#') break; (void) strncpy(dir, key, kl); dir[kl] = '\0'; if (macro_expand("", dir, qbuff, sizeof (dir))) { syslog(LOG_ERR, "%s in NIS map %s: entry too long (max %d chars)", dir, my_mapname, sizeof (dir) - 1); break; } (void) strncpy(map, val, vl); map[vl] = '\0'; if (macro_expand(dir, map, qbuff, sizeof (map))) { syslog(LOG_ERR, "%s in NIS map %s: entry too long (max %d chars)", map, my_mapname, sizeof (map) - 1); break; } pmap = map; while (*pmap && isspace(*pmap)) pmap++; /* skip blanks in front of map */ opts = pmap; while (*opts && !isspace(*opts)) opts++; if (*opts) { *opts++ = '\0'; while (*opts && isspace(*opts)) opts++; if (*opts == '-') opts++; else opts = defopts; } free(val); /* * Check for no embedded blanks. */ if (strcspn(opts, " ") == strlen(opts)) { dirinit(dir, pmap, opts, 0, stack, stkptr); count++; } else { pr_msg("Warning: invalid entry for %s in NIS map %s ignored.\n", dir, mapname); } } if (my_mapname) free(my_mapname); /* * In the context of a master map, if no entry is * found, it is like NOTFOUND */ if (count > 0 && err == YPERR_NOMORE) return (__NSW_SUCCESS); else { if (err) return (nis_err(err)); else /* * This case will happen if map is empty * or none of the entries is valid */ return (__NSW_NOTFOUND); } } int loaddirect_nis(nismap, localmap, opts, stack, stkptr) char *nismap, *localmap, *opts; char **stack; char ***stkptr; { int first, err, count; char *key, *nkey, *val, *my_nismap; int kl, nkl, vl; char dir[100]; first = 1; key = NULL; kl = 0; nkey = NULL; nkl = 0; val = NULL; vl = 0; count = 0; my_nismap = NULL; my_nismap = strdup(nismap); if (my_nismap == NULL) { syslog(LOG_ERR, "loadmaster_yp: memory alloc failed: %m"); return (__NSW_UNAVAIL); } for (;;) { if (first) { first = 0; err = yp_first(nis_mydomain, my_nismap, &nkey, &nkl, &val, &vl); if ((err == YPERR_MAP) && (replace_undscr_by_dot(my_nismap))) err = yp_first(nis_mydomain, my_nismap, &nkey, &nkl, &val, &vl); if ((err == YPERR_DOMAIN) || (err == YPERR_YPBIND)) { syslog(LOG_ERR, "can't read nis map %s: %s - retrying", my_nismap, yperr_string(err)); while ((err == YPERR_DOMAIN) || (err == YPERR_YPBIND)) { (void) sleep(20); err = yp_first(nis_mydomain, my_nismap, &nkey, &nkl, &val, &vl); } syslog(LOG_ERR, "nis map %s: read OK.", my_nismap); } } else { err = yp_next(nis_mydomain, my_nismap, key, kl, &nkey, &nkl, &val, &vl); } if (err) { if (err != YPERR_NOMORE && err != YPERR_MAP) syslog(LOG_ERR, "%s: %s", my_nismap, yperr_string(err)); break; } if (key) free(key); key = nkey; kl = nkl; if (kl < 2 || kl >= 100) continue; if (isspace(*key) || *key == '#') continue; (void) strncpy(dir, key, kl); dir[kl] = '\0'; dirinit(dir, localmap, opts, 1, stack, stkptr); count++; free(val); } if (my_nismap) free(my_nismap); if (count > 0 && err == YPERR_NOMORE) return (__NSW_SUCCESS); else return (nis_err(err)); } static int replace_undscr_by_dot(map) char *map; { int ret_val = 0; while (*map) { if (*map == '_') { ret_val = 1; *map = '.'; } map++; } return (ret_val); } static int nis_err(err) int err; { switch (err) { case 0: return (__NSW_SUCCESS); case YPERR_KEY: return (__NSW_NOTFOUND); case YPERR_MAP: return (__NSW_UNAVAIL); default: return (__NSW_UNAVAIL); } } int getmapkeys_nis(nsmap, list, error, cache_time, stack, stkptr) char *nsmap; struct dir_entry **list; int *error; int *cache_time; char **stack; char ***stkptr; { int nserr; struct dir_cbdata readdir_cbdata; struct ypall_callback cback; char *my_map = NULL; char *key = NULL, *val = NULL; int nkl, vl; #ifdef lint stack = stack; stkptr = stkptr; #endif /* lint */ *cache_time = RDDIR_CACHE_TIME; /* * XXX Hack to determine if we need to replace '_' with '.' * Have to use yp_first() since yp_all() simply fails if * the map is not present */ my_map = strdup(nsmap); if (my_map == NULL) { syslog(LOG_ERR, "getmapkeys_nis: memory alloc failed: %m"); *error = ENOMEM; return (__NSW_UNAVAIL); } nserr = yp_first(nis_mydomain, my_map, &key, &nkl, &val, &vl); if (nserr == YPERR_MAP) { if (replace_undscr_by_dot(my_map)) { nserr = yp_first(nis_mydomain, my_map, &key, &nkl, &val, &vl); } if (nserr == YPERR_MAP) { /* * map not found */ *error = 0; /* return an empty list */ if (verbose) { syslog(LOG_ERR, "%s: %s", nsmap, yperr_string(nserr)); } free(my_map); return (nis_err(nserr)); } } if (key) free(key); if (val) free(val); readdir_cbdata.list = list; readdir_cbdata.last = NULL; readdir_cbdata.error = 0; cback.foreach = readdir_callback; cback.data = (char *)&readdir_cbdata; /* * after all this song and dance we finally * ask for the list of entries */ nserr = yp_all(nis_mydomain, my_map, &cback); free(my_map); *error = readdir_cbdata.error; if (nserr) { if (verbose) syslog(LOG_ERR, "%s: %s", nsmap, yperr_string(nserr)); nserr = 1; if (*error == 0) *error = ENOENT; return (nis_err(nserr)); } return (__NSW_SUCCESS); } static int readdir_callback(instatus, inkey, inkeylen, inval, invallen, indata) int instatus; char *inkey; int inkeylen; const char *inval; int invallen; struct dir_cbdata *indata; { struct dir_entry **list = indata->list; struct dir_entry *last = indata->last; char key[MAXPATHLEN]; #ifdef lint inval = inval; invallen = invallen; #endif if (instatus != YP_TRUE) return (0); /* next entry. yp_all may decide otherwise... */ if (inkeylen == 0 || isspace(*inkey) || *inkey == '#') return (0); /* * yp_all allocates inkey with two extra bytes which contain * NEWLINE and null but these two bytes are not reflected in * inkeylen. */ strncpy(key, inkey, inkeylen); key[inkeylen] = '\0'; /* * Wildcard entry should be ignored - following entries should continue * to be read to corroborate with the way we search for entries in yp, * i.e., first for an exact key match and then a wildcard, if there's * no exact key match. */ if (key[0] == '*' && key[1] == '\0') return (0); if (add_dir_entry(key, list, &last)) { indata->error = ENOMEM; return (1); /* get no more entries */ } indata->last = last; indata->error = 0; return (0); } #!/bin/sh # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License, Version 1.0 only # (the "License"). You may not use this file except in compliance # with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright (c) 2000 by Sun Microsystems, Inc. # All rights reserved. # #ident "%Z%%M% %I% %E% SMI" echo_file usr/src/lib/libsldap/common/ns_sldap.h /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 1999,2001 by Sun Microsystems, Inc. * All rights reserved. */ /* * autofs share - dummy utility to accomodate autofs inclusion in * /etc/dfs/fstypes */ #include #include int main(int argc, char **argv) { fprintf(stderr, gettext("autofs share is not supported.\n")); return (1); } #!/sbin/sh # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License, Version 1.0 only # (the "License"). You may not use this file except in compliance # with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2004 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # ident "%Z%%M% %I% %E% SMI" . /lib/svc/share/smf_include.sh case "$1" in 'start') /usr/lib/autofs/automountd /usr/sbin/automount & ;; 'stop') /sbin/umountall -F autofs # Need to kill the entire service contract in case automount is hung # do to a misconfiguration, such as yp not up or responding. smf_kill_contract $2 TERM 1 [ $? -ne 0 ] && exit 1 ;; *) echo "Usage: $0 { start | stop }" ;; esac exit 0 /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 1999,2001 by Sun Microsystems, Inc. * All rights reserved. */ /* * autofs unshare - dummy utility to accomodate autofs inclusion in * /etc/dfs/fstypes */ #include #include int main(int argc, char **argv) { fprintf(stderr, gettext("autofs unshare is not supported.\n")); return (1); } /* * Please do not edit this file. * It was generated using rpcgen. */ #ifndef _AUTOFS_WEBNFS_H_RPCGEN #define _AUTOFS_WEBNFS_H_RPCGEN #include #ifndef _KERNEL #include #include #endif /* !_KERNEL */ #ifdef __cplusplus extern "C" { #endif #define WNL_PORT 2049 #define WNL_MAXDATA 8192 #define WNL_MAXNAMLEN 255 #define WNL_FHSIZE 32 #define WNL_FIFO_DEV -1 #define WNL_NATIVEPATH 0x80 #define WNL_SEC_NEGO 0x81 #define WNLMODE_FMT 0170000 #define WNLMODE_DIR 0040000 #define WNLMODE_CHR 0020000 #define WNLMODE_BLK 0060000 #define WNLMODE_REG 0100000 #define WNLMODE_LNK 0120000 #define WNLMODE_SOCK 0140000 #define WNLMODE_FIFO 0010000 enum wnl_stat { WNL_OK = 0, WNLERR_PERM = 1, WNLERR_NOENT = 2, WNLERR_IO = 5, WNLERR_NXIO = 6, WNLERR_ACCES = 13, WNLERR_EXIST = 17, WNLERR_XDEV = 18, WNLERR_NODEV = 19, WNLERR_NOTDIR = 20, WNLERR_ISDIR = 21, WNLERR_INVAL = 22, WNLERR_FBIG = 27, WNLERR_NOSPC = 28, WNLERR_ROFS = 30, WNLERR_OPNOTSUPP = 45, WNLERR_NAMETOOLONG = 63, WNLERR_NOTEMPTY = 66, WNLERR_DQUOT = 69, WNLERR_STALE = 70, WNLERR_REMOTE = 71, WNLERR_WFLUSH = 72 }; typedef enum wnl_stat wnl_stat; enum wnl_ftype { WNL_NON = 0, WNL_REG = 1, WNL_DIR = 2, WNL_BLK = 3, WNL_CHR = 4, WNL_LNK = 5, WNL_SOCK = 6, WNL_BAD = 7, WNL_FIFO = 8 }; typedef enum wnl_ftype wnl_ftype; struct wnl_fh { char data[WNL_FHSIZE]; }; typedef struct wnl_fh wnl_fh; struct wnl_time { u_int seconds; u_int useconds; }; typedef struct wnl_time wnl_time; struct wnl_fattr { wnl_ftype type; u_int mode; u_int nlink; u_int uid; u_int gid; u_int size; u_int blocksize; u_int rdev; u_int blocks; u_int fsid; u_int fileid; wnl_time atime; wnl_time mtime; wnl_time ctime; }; typedef struct wnl_fattr wnl_fattr; typedef char *wnl_filename; struct wnl_diropargs { wnl_fh dir; wnl_filename name; }; typedef struct wnl_diropargs wnl_diropargs; struct wnl_diropokres { wnl_fh file; wnl_fattr attributes; }; typedef struct wnl_diropokres wnl_diropokres; struct wnl_diropres { wnl_stat status; union { wnl_diropokres wnl_diropres; } wnl_diropres_u; }; typedef struct wnl_diropres wnl_diropres; #define WNL3_FHSIZE 64 typedef u_longlong_t wnl_uint64; typedef longlong_t wnl_int64; typedef u_int wnl_uint32; typedef char *wnl_filename3; typedef wnl_uint64 wnl_fileid3; typedef wnl_uint32 wnl_uid3; typedef wnl_uint32 wnl_gid3; typedef wnl_uint64 wnl_size3; typedef wnl_uint32 wnl_mode3; enum wnl_stat3 { WNL3_OK = 0, WNL3ERR_PERM = 1, WNL3ERR_NOENT = 2, WNL3ERR_IO = 5, WNL3ERR_NXIO = 6, WNL3ERR_ACCES = 13, WNL3ERR_EXIST = 17, WNL3ERR_XDEV = 18, WNL3ERR_NODEV = 19, WNL3ERR_NOTDIR = 20, WNL3ERR_ISDIR = 21, WNL3ERR_INVAL = 22, WNL3ERR_FBIG = 27, WNL3ERR_NOSPC = 28, WNL3ERR_ROFS = 30, WNL3ERR_MLINK = 31, WNL3ERR_NAMETOOLONG = 63, WNL3ERR_NOTEMPTY = 66, WNL3ERR_DQUOT = 69, WNL3ERR_STALE = 70, WNL3ERR_REMOTE = 71, WNL3ERR_BADHANDLE = 10001, WNL3ERR_NOT_SYNC = 10002, WNL3ERR_BAD_COOKIE = 10003, WNL3ERR_NOTSUPP = 10004, WNL3ERR_TOOSMALL = 10005, WNL3ERR_SERVERFAULT = 10006, WNL3ERR_BADTYPE = 10007, WNL3ERR_JUKEBOX = 10008 }; typedef enum wnl_stat3 wnl_stat3; enum wnl_ftype3 { WNL_3REG = 1, WNL_3DIR = 2, WNL_3BLK = 3, WNL_3CHR = 4, WNL_3LNK = 5, WNL_3SOCK = 6, WNL_3FIFO = 7 }; typedef enum wnl_ftype3 wnl_ftype3; struct wnl_specdata3 { wnl_uint32 specdata1; wnl_uint32 specdata2; }; typedef struct wnl_specdata3 wnl_specdata3; struct wnl_fh3 { struct { u_int data_len; char *data_val; } data; }; typedef struct wnl_fh3 wnl_fh3; struct wnl_time3 { wnl_uint32 seconds; wnl_uint32 nseconds; }; typedef struct wnl_time3 wnl_time3; struct wnl_fattr3 { wnl_ftype3 type; wnl_mode3 mode; wnl_uint32 nlink; wnl_uid3 uid; wnl_gid3 gid; wnl_size3 size; wnl_size3 used; wnl_specdata3 rdev; wnl_uint64 fsid; wnl_fileid3 fileid; wnl_time3 atime; wnl_time3 mtime; wnl_time3 ctime; }; typedef struct wnl_fattr3 wnl_fattr3; struct wnl_post_op_attr { bool_t attributes_follow; union { wnl_fattr3 attributes; } wnl_post_op_attr_u; }; typedef struct wnl_post_op_attr wnl_post_op_attr; struct wln_post_op_fh3 { bool_t handle_follows; union { wnl_fh3 handle; } wln_post_op_fh3_u; }; typedef struct wln_post_op_fh3 wln_post_op_fh3; struct wnl_diropargs3 { wnl_fh3 dir; wnl_filename3 name; }; typedef struct wnl_diropargs3 wnl_diropargs3; struct WNL_LOOKUP3args { wnl_diropargs3 what; }; typedef struct WNL_LOOKUP3args WNL_LOOKUP3args; struct WNL_LOOKUP3resok { wnl_fh3 object; wnl_post_op_attr obj_attributes; wnl_post_op_attr dir_attributes; }; typedef struct WNL_LOOKUP3resok WNL_LOOKUP3resok; struct WNL_LOOKUP3resfail { wnl_post_op_attr dir_attributes; }; typedef struct WNL_LOOKUP3resfail WNL_LOOKUP3resfail; struct WNL_LOOKUP3res { wnl_stat3 status; union { WNL_LOOKUP3resok res_ok; WNL_LOOKUP3resfail res_fail; } WNL_LOOKUP3res_u; }; typedef struct WNL_LOOKUP3res WNL_LOOKUP3res; #define MAX_FLAVORS 128 struct snego_t { int cnt; int array[MAX_FLAVORS]; }; typedef struct snego_t snego_t; enum snego_stat { SNEGO_SUCCESS = 0, SNEGO_DEF_VALID = 1, SNEGO_ARRAY_TOO_SMALL = 2, SNEGO_FAILURE = 3 }; typedef enum snego_stat snego_stat; #define WNL_PROGRAM 100003 #define WNL_V2 2 #if defined(__STDC__) || defined(__cplusplus) #define WNLPROC_NULL 0 extern enum clnt_stat wnlproc_null_2(void *, void *, CLIENT *); extern bool_t wnlproc_null_2_svc(void *, void *, struct svc_req *); #define WNLPROC_LOOKUP 4 extern enum clnt_stat wnlproc_lookup_2(wnl_diropargs *, wnl_diropres *, CLIENT *); extern bool_t wnlproc_lookup_2_svc(wnl_diropargs *, wnl_diropres *, struct svc_req *); extern int wnl_program_2_freeresult(SVCXPRT *, xdrproc_t, caddr_t); #else /* K&R C */ #define WNLPROC_NULL 0 extern enum clnt_stat wnlproc_null_2(); extern bool_t wnlproc_null_2_svc(); #define WNLPROC_LOOKUP 4 extern enum clnt_stat wnlproc_lookup_2(); extern bool_t wnlproc_lookup_2_svc(); extern int wnl_program_2_freeresult(); #endif /* K&R C */ #define WNL_V3 3 #if defined(__STDC__) || defined(__cplusplus) #define WNLPROC3_NULL 0 extern enum clnt_stat wnlproc3_null_3(void *, void *, CLIENT *); extern bool_t wnlproc3_null_3_svc(void *, void *, struct svc_req *); #define WNLPROC3_LOOKUP 3 extern enum clnt_stat wnlproc3_lookup_3(WNL_LOOKUP3args *, WNL_LOOKUP3res *, CLIENT *); extern bool_t wnlproc3_lookup_3_svc(WNL_LOOKUP3args *, WNL_LOOKUP3res *, struct svc_req *); extern int wnl_program_3_freeresult(SVCXPRT *, xdrproc_t, caddr_t); #else /* K&R C */ #define WNLPROC3_NULL 0 extern enum clnt_stat wnlproc3_null_3(); extern bool_t wnlproc3_null_3_svc(); #define WNLPROC3_LOOKUP 3 extern enum clnt_stat wnlproc3_lookup_3(); extern bool_t wnlproc3_lookup_3_svc(); extern int wnl_program_3_freeresult(); #endif /* K&R C */ #define WNL_V4 4 #if defined(__STDC__) || defined(__cplusplus) #define WNLPROC4_NULL 0 extern enum clnt_stat wnlproc4_null_4(void *, void *, CLIENT *); extern bool_t wnlproc4_null_4_svc(void *, void *, struct svc_req *); extern int wnl_program_4_freeresult(SVCXPRT *, xdrproc_t, caddr_t); #else /* K&R C */ #define WNLPROC4_NULL 0 extern enum clnt_stat wnlproc4_null_4(); extern bool_t wnlproc4_null_4_svc(); extern int wnl_program_4_freeresult(); #endif /* K&R C */ /* the xdr functions */ #if defined(__STDC__) || defined(__cplusplus) extern bool_t xdr_wnl_stat(XDR *, wnl_stat*); extern bool_t xdr_wnl_ftype(XDR *, wnl_ftype*); extern bool_t xdr_wnl_fh(XDR *, wnl_fh*); extern bool_t xdr_wnl_time(XDR *, wnl_time*); extern bool_t xdr_wnl_fattr(XDR *, wnl_fattr*); extern bool_t xdr_wnl_filename(XDR *, wnl_filename*); extern bool_t xdr_wnl_diropargs(XDR *, wnl_diropargs*); extern bool_t xdr_wnl_diropokres(XDR *, wnl_diropokres*); extern bool_t xdr_wnl_diropres(XDR *, wnl_diropres*); extern bool_t xdr_wnl_uint64(XDR *, wnl_uint64*); extern bool_t xdr_wnl_int64(XDR *, wnl_int64*); extern bool_t xdr_wnl_uint32(XDR *, wnl_uint32*); extern bool_t xdr_wnl_filename3(XDR *, wnl_filename3*); extern bool_t xdr_wnl_fileid3(XDR *, wnl_fileid3*); extern bool_t xdr_wnl_uid3(XDR *, wnl_uid3*); extern bool_t xdr_wnl_gid3(XDR *, wnl_gid3*); extern bool_t xdr_wnl_size3(XDR *, wnl_size3*); extern bool_t xdr_wnl_mode3(XDR *, wnl_mode3*); extern bool_t xdr_wnl_stat3(XDR *, wnl_stat3*); extern bool_t xdr_wnl_ftype3(XDR *, wnl_ftype3*); extern bool_t xdr_wnl_specdata3(XDR *, wnl_specdata3*); extern bool_t xdr_wnl_fh3(XDR *, wnl_fh3*); extern bool_t xdr_wnl_time3(XDR *, wnl_time3*); extern bool_t xdr_wnl_fattr3(XDR *, wnl_fattr3*); extern bool_t xdr_wnl_post_op_attr(XDR *, wnl_post_op_attr*); extern bool_t xdr_wln_post_op_fh3(XDR *, wln_post_op_fh3*); extern bool_t xdr_wnl_diropargs3(XDR *, wnl_diropargs3*); extern bool_t xdr_WNL_LOOKUP3args(XDR *, WNL_LOOKUP3args*); extern bool_t xdr_WNL_LOOKUP3resok(XDR *, WNL_LOOKUP3resok*); extern bool_t xdr_WNL_LOOKUP3resfail(XDR *, WNL_LOOKUP3resfail*); extern bool_t xdr_WNL_LOOKUP3res(XDR *, WNL_LOOKUP3res*); extern bool_t xdr_snego_t(XDR *, snego_t*); extern bool_t xdr_snego_stat(XDR *, snego_stat*); #else /* K&R C */ extern bool_t xdr_wnl_stat(); extern bool_t xdr_wnl_ftype(); extern bool_t xdr_wnl_fh(); extern bool_t xdr_wnl_time(); extern bool_t xdr_wnl_fattr(); extern bool_t xdr_wnl_filename(); extern bool_t xdr_wnl_diropargs(); extern bool_t xdr_wnl_diropokres(); extern bool_t xdr_wnl_diropres(); extern bool_t xdr_wnl_uint64(); extern bool_t xdr_wnl_int64(); extern bool_t xdr_wnl_uint32(); extern bool_t xdr_wnl_filename3(); extern bool_t xdr_wnl_fileid3(); extern bool_t xdr_wnl_uid3(); extern bool_t xdr_wnl_gid3(); extern bool_t xdr_wnl_size3(); extern bool_t xdr_wnl_mode3(); extern bool_t xdr_wnl_stat3(); extern bool_t xdr_wnl_ftype3(); extern bool_t xdr_wnl_specdata3(); extern bool_t xdr_wnl_fh3(); extern bool_t xdr_wnl_time3(); extern bool_t xdr_wnl_fattr3(); extern bool_t xdr_wnl_post_op_attr(); extern bool_t xdr_wln_post_op_fh3(); extern bool_t xdr_wnl_diropargs3(); extern bool_t xdr_WNL_LOOKUP3args(); extern bool_t xdr_WNL_LOOKUP3resok(); extern bool_t xdr_WNL_LOOKUP3resfail(); extern bool_t xdr_WNL_LOOKUP3res(); extern bool_t xdr_snego_t(); extern bool_t xdr_snego_stat(); #endif /* K&R C */ #ifdef __cplusplus } #endif #endif /* !_AUTOFS_WEBNFS_H_RPCGEN */ /* * Please do not edit this file. * It was generated using rpcgen. */ #include /* for memset */ #include "webnfs.h" #ifndef _KERNEL #include #include /* getenv, exit */ #endif /* !_KERNEL */ /* Default timeout can be changed using clnt_control() */ static struct timeval TIMEOUT = { 25, 0 }; enum clnt_stat wnlproc_null_2(void *argp, void *clnt_res, CLIENT *clnt) { return (clnt_call(clnt, WNLPROC_NULL, (xdrproc_t)xdr_void, (caddr_t)argp, (xdrproc_t)xdr_void, (caddr_t)clnt_res, TIMEOUT)); } enum clnt_stat wnlproc_lookup_2(wnl_diropargs *argp, wnl_diropres *clnt_res, CLIENT *clnt) { return (clnt_call(clnt, WNLPROC_LOOKUP, (xdrproc_t)xdr_wnl_diropargs, (caddr_t)argp, (xdrproc_t)xdr_wnl_diropres, (caddr_t)clnt_res, TIMEOUT)); } enum clnt_stat wnlproc3_null_3(void *argp, void *clnt_res, CLIENT *clnt) { return (clnt_call(clnt, WNLPROC3_NULL, (xdrproc_t)xdr_void, (caddr_t)argp, (xdrproc_t)xdr_void, (caddr_t)clnt_res, TIMEOUT)); } enum clnt_stat wnlproc3_lookup_3(WNL_LOOKUP3args *argp, WNL_LOOKUP3res *clnt_res, CLIENT *clnt) { return (clnt_call(clnt, WNLPROC3_LOOKUP, (xdrproc_t)xdr_WNL_LOOKUP3args, (caddr_t)argp, (xdrproc_t)xdr_WNL_LOOKUP3res, (caddr_t)clnt_res, TIMEOUT)); } enum clnt_stat wnlproc4_null_4(void *argp, void *clnt_res, CLIENT *clnt) { return (clnt_call(clnt, WNLPROC4_NULL, (xdrproc_t)xdr_void, (caddr_t)argp, (xdrproc_t)xdr_void, (caddr_t)clnt_res, TIMEOUT)); } /* * Please do not edit this file. * It was generated using rpcgen. */ #include "webnfs.h" #ifndef _KERNEL #include #endif /* !_KERNEL */ bool_t xdr_wnl_stat(XDR *xdrs, wnl_stat *objp) { rpc_inline_t *buf __unused; if (!xdr_enum(xdrs, (enum_t *)objp)) return (FALSE); return (TRUE); } bool_t xdr_wnl_ftype(XDR *xdrs, wnl_ftype *objp) { rpc_inline_t *buf __unused; if (!xdr_enum(xdrs, (enum_t *)objp)) return (FALSE); return (TRUE); } bool_t xdr_wnl_fh(XDR *xdrs, wnl_fh *objp) { rpc_inline_t *buf __unused; if (!xdr_opaque(xdrs, objp->data, WNL_FHSIZE)) return (FALSE); return (TRUE); } bool_t xdr_wnl_time(XDR *xdrs, wnl_time *objp) { rpc_inline_t *buf __unused; if (!xdr_u_int(xdrs, &objp->seconds)) return (FALSE); if (!xdr_u_int(xdrs, &objp->useconds)) return (FALSE); return (TRUE); } bool_t xdr_wnl_fattr(XDR *xdrs, wnl_fattr *objp) { rpc_inline_t *buf __unused; if (xdrs->x_op == XDR_ENCODE) { if (!xdr_wnl_ftype(xdrs, &objp->type)) return (FALSE); buf = XDR_INLINE(xdrs, 10 * BYTES_PER_XDR_UNIT); if (buf == NULL) { if (!xdr_u_int(xdrs, &objp->mode)) return (FALSE); if (!xdr_u_int(xdrs, &objp->nlink)) return (FALSE); if (!xdr_u_int(xdrs, &objp->uid)) return (FALSE); if (!xdr_u_int(xdrs, &objp->gid)) return (FALSE); if (!xdr_u_int(xdrs, &objp->size)) return (FALSE); if (!xdr_u_int(xdrs, &objp->blocksize)) return (FALSE); if (!xdr_u_int(xdrs, &objp->rdev)) return (FALSE); if (!xdr_u_int(xdrs, &objp->blocks)) return (FALSE); if (!xdr_u_int(xdrs, &objp->fsid)) return (FALSE); if (!xdr_u_int(xdrs, &objp->fileid)) return (FALSE); } else { #if defined(_LP64) || defined(_KERNEL) IXDR_PUT_U_INT32(buf, objp->mode); IXDR_PUT_U_INT32(buf, objp->nlink); IXDR_PUT_U_INT32(buf, objp->uid); IXDR_PUT_U_INT32(buf, objp->gid); IXDR_PUT_U_INT32(buf, objp->size); IXDR_PUT_U_INT32(buf, objp->blocksize); IXDR_PUT_U_INT32(buf, objp->rdev); IXDR_PUT_U_INT32(buf, objp->blocks); IXDR_PUT_U_INT32(buf, objp->fsid); IXDR_PUT_U_INT32(buf, objp->fileid); #else IXDR_PUT_U_LONG(buf, objp->mode); IXDR_PUT_U_LONG(buf, objp->nlink); IXDR_PUT_U_LONG(buf, objp->uid); IXDR_PUT_U_LONG(buf, objp->gid); IXDR_PUT_U_LONG(buf, objp->size); IXDR_PUT_U_LONG(buf, objp->blocksize); IXDR_PUT_U_LONG(buf, objp->rdev); IXDR_PUT_U_LONG(buf, objp->blocks); IXDR_PUT_U_LONG(buf, objp->fsid); IXDR_PUT_U_LONG(buf, objp->fileid); #endif } if (!xdr_wnl_time(xdrs, &objp->atime)) return (FALSE); if (!xdr_wnl_time(xdrs, &objp->mtime)) return (FALSE); if (!xdr_wnl_time(xdrs, &objp->ctime)) return (FALSE); return (TRUE); } else if (xdrs->x_op == XDR_DECODE) { if (!xdr_wnl_ftype(xdrs, &objp->type)) return (FALSE); buf = XDR_INLINE(xdrs, 10 * BYTES_PER_XDR_UNIT); if (buf == NULL) { if (!xdr_u_int(xdrs, &objp->mode)) return (FALSE); if (!xdr_u_int(xdrs, &objp->nlink)) return (FALSE); if (!xdr_u_int(xdrs, &objp->uid)) return (FALSE); if (!xdr_u_int(xdrs, &objp->gid)) return (FALSE); if (!xdr_u_int(xdrs, &objp->size)) return (FALSE); if (!xdr_u_int(xdrs, &objp->blocksize)) return (FALSE); if (!xdr_u_int(xdrs, &objp->rdev)) return (FALSE); if (!xdr_u_int(xdrs, &objp->blocks)) return (FALSE); if (!xdr_u_int(xdrs, &objp->fsid)) return (FALSE); if (!xdr_u_int(xdrs, &objp->fileid)) return (FALSE); } else { #if defined(_LP64) || defined(_KERNEL) objp->mode = IXDR_GET_U_INT32(buf); objp->nlink = IXDR_GET_U_INT32(buf); objp->uid = IXDR_GET_U_INT32(buf); objp->gid = IXDR_GET_U_INT32(buf); objp->size = IXDR_GET_U_INT32(buf); objp->blocksize = IXDR_GET_U_INT32(buf); objp->rdev = IXDR_GET_U_INT32(buf); objp->blocks = IXDR_GET_U_INT32(buf); objp->fsid = IXDR_GET_U_INT32(buf); objp->fileid = IXDR_GET_U_INT32(buf); #else objp->mode = IXDR_GET_U_LONG(buf); objp->nlink = IXDR_GET_U_LONG(buf); objp->uid = IXDR_GET_U_LONG(buf); objp->gid = IXDR_GET_U_LONG(buf); objp->size = IXDR_GET_U_LONG(buf); objp->blocksize = IXDR_GET_U_LONG(buf); objp->rdev = IXDR_GET_U_LONG(buf); objp->blocks = IXDR_GET_U_LONG(buf); objp->fsid = IXDR_GET_U_LONG(buf); objp->fileid = IXDR_GET_U_LONG(buf); #endif } if (!xdr_wnl_time(xdrs, &objp->atime)) return (FALSE); if (!xdr_wnl_time(xdrs, &objp->mtime)) return (FALSE); if (!xdr_wnl_time(xdrs, &objp->ctime)) return (FALSE); return (TRUE); } if (!xdr_wnl_ftype(xdrs, &objp->type)) return (FALSE); if (!xdr_u_int(xdrs, &objp->mode)) return (FALSE); if (!xdr_u_int(xdrs, &objp->nlink)) return (FALSE); if (!xdr_u_int(xdrs, &objp->uid)) return (FALSE); if (!xdr_u_int(xdrs, &objp->gid)) return (FALSE); if (!xdr_u_int(xdrs, &objp->size)) return (FALSE); if (!xdr_u_int(xdrs, &objp->blocksize)) return (FALSE); if (!xdr_u_int(xdrs, &objp->rdev)) return (FALSE); if (!xdr_u_int(xdrs, &objp->blocks)) return (FALSE); if (!xdr_u_int(xdrs, &objp->fsid)) return (FALSE); if (!xdr_u_int(xdrs, &objp->fileid)) return (FALSE); if (!xdr_wnl_time(xdrs, &objp->atime)) return (FALSE); if (!xdr_wnl_time(xdrs, &objp->mtime)) return (FALSE); if (!xdr_wnl_time(xdrs, &objp->ctime)) return (FALSE); return (TRUE); } bool_t xdr_wnl_filename(XDR *xdrs, wnl_filename *objp) { rpc_inline_t *buf __unused; if (!xdr_string(xdrs, objp, WNL_MAXNAMLEN)) return (FALSE); return (TRUE); } bool_t xdr_wnl_diropargs(XDR *xdrs, wnl_diropargs *objp) { rpc_inline_t *buf __unused; if (!xdr_wnl_fh(xdrs, &objp->dir)) return (FALSE); if (!xdr_wnl_filename(xdrs, &objp->name)) return (FALSE); return (TRUE); } bool_t xdr_wnl_diropokres(XDR *xdrs, wnl_diropokres *objp) { rpc_inline_t *buf __unused; if (!xdr_wnl_fh(xdrs, &objp->file)) return (FALSE); if (!xdr_wnl_fattr(xdrs, &objp->attributes)) return (FALSE); return (TRUE); } bool_t xdr_wnl_diropres(XDR *xdrs, wnl_diropres *objp) { rpc_inline_t *buf __unused; if (!xdr_wnl_stat(xdrs, &objp->status)) return (FALSE); switch (objp->status) { case WNL_OK: if (!xdr_wnl_diropokres(xdrs, &objp->wnl_diropres_u.wnl_diropres)) return (FALSE); break; default: break; } return (TRUE); } bool_t xdr_wnl_uint64(XDR *xdrs, wnl_uint64 *objp) { rpc_inline_t *buf __unused; if (!xdr_u_longlong_t(xdrs, objp)) return (FALSE); return (TRUE); } bool_t xdr_wnl_int64(XDR *xdrs, wnl_int64 *objp) { rpc_inline_t *buf __unused; if (!xdr_longlong_t(xdrs, objp)) return (FALSE); return (TRUE); } bool_t xdr_wnl_uint32(XDR *xdrs, wnl_uint32 *objp) { rpc_inline_t *buf __unused; if (!xdr_u_int(xdrs, objp)) return (FALSE); return (TRUE); } bool_t xdr_wnl_filename3(XDR *xdrs, wnl_filename3 *objp) { rpc_inline_t *buf __unused; if (!xdr_string(xdrs, objp, ~0)) return (FALSE); return (TRUE); } bool_t xdr_wnl_fileid3(XDR *xdrs, wnl_fileid3 *objp) { rpc_inline_t *buf __unused; if (!xdr_wnl_uint64(xdrs, objp)) return (FALSE); return (TRUE); } bool_t xdr_wnl_uid3(XDR *xdrs, wnl_uid3 *objp) { rpc_inline_t *buf __unused; if (!xdr_wnl_uint32(xdrs, objp)) return (FALSE); return (TRUE); } bool_t xdr_wnl_gid3(XDR *xdrs, wnl_gid3 *objp) { rpc_inline_t *buf __unused; if (!xdr_wnl_uint32(xdrs, objp)) return (FALSE); return (TRUE); } bool_t xdr_wnl_size3(XDR *xdrs, wnl_size3 *objp) { rpc_inline_t *buf __unused; if (!xdr_wnl_uint64(xdrs, objp)) return (FALSE); return (TRUE); } bool_t xdr_wnl_mode3(XDR *xdrs, wnl_mode3 *objp) { rpc_inline_t *buf __unused; if (!xdr_wnl_uint32(xdrs, objp)) return (FALSE); return (TRUE); } bool_t xdr_wnl_stat3(XDR *xdrs, wnl_stat3 *objp) { rpc_inline_t *buf __unused; if (!xdr_enum(xdrs, (enum_t *)objp)) return (FALSE); return (TRUE); } bool_t xdr_wnl_ftype3(XDR *xdrs, wnl_ftype3 *objp) { rpc_inline_t *buf __unused; if (!xdr_enum(xdrs, (enum_t *)objp)) return (FALSE); return (TRUE); } bool_t xdr_wnl_specdata3(XDR *xdrs, wnl_specdata3 *objp) { rpc_inline_t *buf __unused; if (!xdr_wnl_uint32(xdrs, &objp->specdata1)) return (FALSE); if (!xdr_wnl_uint32(xdrs, &objp->specdata2)) return (FALSE); return (TRUE); } bool_t xdr_wnl_fh3(XDR *xdrs, wnl_fh3 *objp) { rpc_inline_t *buf __unused; if (!xdr_bytes(xdrs, (char **)&objp->data.data_val, (u_int *) &objp->data.data_len, WNL3_FHSIZE)) return (FALSE); return (TRUE); } bool_t xdr_wnl_time3(XDR *xdrs, wnl_time3 *objp) { rpc_inline_t *buf __unused; if (!xdr_wnl_uint32(xdrs, &objp->seconds)) return (FALSE); if (!xdr_wnl_uint32(xdrs, &objp->nseconds)) return (FALSE); return (TRUE); } bool_t xdr_wnl_fattr3(XDR *xdrs, wnl_fattr3 *objp) { rpc_inline_t *buf __unused; if (!xdr_wnl_ftype3(xdrs, &objp->type)) return (FALSE); if (!xdr_wnl_mode3(xdrs, &objp->mode)) return (FALSE); if (!xdr_wnl_uint32(xdrs, &objp->nlink)) return (FALSE); if (!xdr_wnl_uid3(xdrs, &objp->uid)) return (FALSE); if (!xdr_wnl_gid3(xdrs, &objp->gid)) return (FALSE); if (!xdr_wnl_size3(xdrs, &objp->size)) return (FALSE); if (!xdr_wnl_size3(xdrs, &objp->used)) return (FALSE); if (!xdr_wnl_specdata3(xdrs, &objp->rdev)) return (FALSE); if (!xdr_wnl_uint64(xdrs, &objp->fsid)) return (FALSE); if (!xdr_wnl_fileid3(xdrs, &objp->fileid)) return (FALSE); if (!xdr_wnl_time3(xdrs, &objp->atime)) return (FALSE); if (!xdr_wnl_time3(xdrs, &objp->mtime)) return (FALSE); if (!xdr_wnl_time3(xdrs, &objp->ctime)) return (FALSE); return (TRUE); } bool_t xdr_wnl_post_op_attr(XDR *xdrs, wnl_post_op_attr *objp) { rpc_inline_t *buf __unused; if (!xdr_bool(xdrs, &objp->attributes_follow)) return (FALSE); switch (objp->attributes_follow) { case TRUE: if (!xdr_wnl_fattr3(xdrs, &objp->wnl_post_op_attr_u.attributes)) return (FALSE); break; case FALSE: break; default: return (FALSE); } return (TRUE); } bool_t xdr_wln_post_op_fh3(XDR *xdrs, wln_post_op_fh3 *objp) { rpc_inline_t *buf __unused; if (!xdr_bool(xdrs, &objp->handle_follows)) return (FALSE); switch (objp->handle_follows) { case TRUE: if (!xdr_wnl_fh3(xdrs, &objp->wln_post_op_fh3_u.handle)) return (FALSE); break; case FALSE: break; default: return (FALSE); } return (TRUE); } bool_t xdr_wnl_diropargs3(XDR *xdrs, wnl_diropargs3 *objp) { rpc_inline_t *buf __unused; if (!xdr_wnl_fh3(xdrs, &objp->dir)) return (FALSE); if (!xdr_wnl_filename3(xdrs, &objp->name)) return (FALSE); return (TRUE); } bool_t xdr_WNL_LOOKUP3args(XDR *xdrs, WNL_LOOKUP3args *objp) { rpc_inline_t *buf __unused; if (!xdr_wnl_diropargs3(xdrs, &objp->what)) return (FALSE); return (TRUE); } bool_t xdr_WNL_LOOKUP3resok(XDR *xdrs, WNL_LOOKUP3resok *objp) { rpc_inline_t *buf __unused; if (!xdr_wnl_fh3(xdrs, &objp->object)) return (FALSE); if (!xdr_wnl_post_op_attr(xdrs, &objp->obj_attributes)) return (FALSE); if (!xdr_wnl_post_op_attr(xdrs, &objp->dir_attributes)) return (FALSE); return (TRUE); } bool_t xdr_WNL_LOOKUP3resfail(XDR *xdrs, WNL_LOOKUP3resfail *objp) { rpc_inline_t *buf __unused; if (!xdr_wnl_post_op_attr(xdrs, &objp->dir_attributes)) return (FALSE); return (TRUE); } bool_t xdr_WNL_LOOKUP3res(XDR *xdrs, WNL_LOOKUP3res *objp) { rpc_inline_t *buf __unused; if (!xdr_wnl_stat3(xdrs, &objp->status)) return (FALSE); switch (objp->status) { case WNL3_OK: if (!xdr_WNL_LOOKUP3resok(xdrs, &objp->WNL_LOOKUP3res_u.res_ok)) return (FALSE); break; default: if (!xdr_WNL_LOOKUP3resfail(xdrs, &objp->WNL_LOOKUP3res_u.res_fail)) return (FALSE); break; } return (TRUE); } bool_t xdr_snego_t(XDR *xdrs, snego_t *objp) { rpc_inline_t *buf __unused; int i; if (xdrs->x_op == XDR_ENCODE) { buf = XDR_INLINE(xdrs, (1 + (MAX_FLAVORS)) * BYTES_PER_XDR_UNIT); if (buf == NULL) { if (!xdr_int(xdrs, &objp->cnt)) return (FALSE); if (!xdr_vector(xdrs, (char *)objp->array, MAX_FLAVORS, sizeof (int), (xdrproc_t)xdr_int)) return (FALSE); } else { #if defined(_LP64) || defined(_KERNEL) IXDR_PUT_INT32(buf, objp->cnt); { int *genp; for (i = 0, genp = objp->array; i < MAX_FLAVORS; i++) { IXDR_PUT_INT32(buf, *genp++); } } #else IXDR_PUT_LONG(buf, objp->cnt); { int *genp; for (i = 0, genp = objp->array; i < MAX_FLAVORS; i++) { IXDR_PUT_LONG(buf, *genp++); } } #endif } return (TRUE); } else if (xdrs->x_op == XDR_DECODE) { buf = XDR_INLINE(xdrs, (1 + (MAX_FLAVORS)) * BYTES_PER_XDR_UNIT); if (buf == NULL) { if (!xdr_int(xdrs, &objp->cnt)) return (FALSE); if (!xdr_vector(xdrs, (char *)objp->array, MAX_FLAVORS, sizeof (int), (xdrproc_t)xdr_int)) return (FALSE); } else { #if defined(_LP64) || defined(_KERNEL) objp->cnt = IXDR_GET_INT32(buf); { int *genp; for (i = 0, genp = objp->array; i < MAX_FLAVORS; i++) { *genp++ = IXDR_GET_INT32(buf); } } #else objp->cnt = IXDR_GET_LONG(buf); { int *genp; for (i = 0, genp = objp->array; i < MAX_FLAVORS; i++) { *genp++ = IXDR_GET_LONG(buf); } } #endif } return (TRUE); } if (!xdr_int(xdrs, &objp->cnt)) return (FALSE); if (!xdr_vector(xdrs, (char *)objp->array, MAX_FLAVORS, sizeof (int), (xdrproc_t)xdr_int)) return (FALSE); return (TRUE); } bool_t xdr_snego_stat(XDR *xdrs, snego_stat *objp) { rpc_inline_t *buf __unused; if (!xdr_enum(xdrs, (enum_t *)objp)) return (FALSE); return (TRUE); }