📄 afs_ops.c
字号:
/* * Copyright (c) 1990 Jan-Simon Pendry * Copyright (c) 1990 Imperial College of Science, Technology & Medicine * Copyright (c) 1990, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Jan-Simon Pendry at Imperial College, London. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)afs_ops.c 8.1 (Berkeley) 6/6/93 * * $Id: afs_ops.c,v 5.2.2.4 1992/05/31 16:36:36 jsp Exp $ * */#include "am.h"#define NFS#define NFSCLIENT#include <sys/stat.h>#ifdef NFS_3typedef nfs_fh fhandle_t;#endif /* NFS_3 */#ifdef NFS_HDR#include NFS_HDR#endif /* NFS_HDR */#include <sys/mount.h>#include "mount.h"/* * Automount file system * Direct file system * Root file system * Top-level file system *//* * Interval between forced retries of a mount. */#define RETRY_INTERVAL 2/* * AFS needs nothing in particular. */static char *afs_match P((am_opts *fo));static char *afs_match(fo)am_opts *fo;{ char *p = fo->opt_rfs; if (!fo->opt_rfs) { plog(XLOG_USER, "auto: no mount point named (rfs:=)"); return 0; } if (!fo->opt_fs) { plog(XLOG_USER, "auto: no map named (fs:=)"); return 0; } /* * Swap round fs:= and rfs:= options * ... historical (jsp) */ fo->opt_rfs = fo->opt_fs; fo->opt_fs = p; /* * mtab entry turns out to be the name of the mount map */ return strdup(fo->opt_rfs ? fo->opt_rfs : ".");}/* * Mount an automounter directory. * The automounter is connected into the system * as a user-level NFS server. mount_toplvl constructs * the necessary NFS parameters to be given to the * kernel so that it will talk back to us. */static int mount_toplvl P((char *dir, char *opts));static int mount_toplvl(dir, opts)char *dir;char *opts;{ struct nfs_args nfs_args; struct mntent mnt; int retry; struct sockaddr_in sin; unsigned short port; int flags; extern nfs_fh *root_fh(); nfs_fh *fhp; char fs_hostname[MAXHOSTNAMELEN+MAXPATHLEN+1]; MTYPE_TYPE type = MOUNT_TYPE_NFS; bzero((voidp) &nfs_args, sizeof(nfs_args)); /* Paranoid */ mnt.mnt_dir = dir; mnt.mnt_fsname = pid_fsname; mnt.mnt_type = MNTTYPE_AUTO; mnt.mnt_opts = opts; mnt.mnt_freq = 0; mnt.mnt_passno = 0; retry = hasmntval(&mnt, "retry"); if (retry <= 0) retry = 2; /* XXX */ /* * get fhandle of remote path for automount point */ fhp = root_fh(dir); if (!fhp) { plog(XLOG_FATAL, "Can't find root file handle for %s", dir); return EINVAL; } NFS_FH_DREF(nfs_args.fh, (NFS_FH_TYPE) fhp); /* * Create sockaddr to point to the local machine. 127.0.0.1 * is not used since that will not work in HP-UX clusters and * this is no more expensive. */ bzero((voidp) &sin, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_addr = myipaddr; if (port = hasmntval(&mnt, "port")) { sin.sin_port = htons(port); } else { plog(XLOG_ERROR, "no port number specified for %s", dir); return EINVAL; } /* * set mount args */ NFS_SA_DREF(nfs_args, &sin); /* * Make a ``hostname'' string for the kernel */#ifndef HOSTNAMESZ#define SHORT_MOUNT_NAME#endif /* HOSTNAMESZ */#ifdef SHORT_MOUNT_NAME sprintf(fs_hostname, "amd:%d", foreground ? mypid : getppid());#else sprintf(fs_hostname, "pid%d@%s:%s", foreground ? mypid : getppid(), hostname, dir);#endif /* SHORT_MOUNT_NAME */ nfs_args.hostname = fs_hostname; nfs_args.flags |= NFSMNT_HOSTNAME;#ifdef HOSTNAMESZ /* * Most kernels have a name length restriction. */ if (strlen(fs_hostname) >= HOSTNAMESZ) strcpy(fs_hostname + HOSTNAMESZ - 3, "..");#endif /* HOSTNAMESZ */#ifdef NFSMNT_DUMBTIMR nfs_args.flags |= NFSMNT_DUMBTIMR; plog(XLOG_INFO, "defeating nfs window computation");#endif /* * Parse a subset of the standard nfs options. The * others are probably irrelevant for this application */ if (nfs_args.timeo = hasmntval(&mnt, "timeo")) nfs_args.flags |= NFSMNT_TIMEO; if (nfs_args.retrans = hasmntval(&mnt, "retrans")) nfs_args.flags |= NFSMNT_RETRANS;#ifdef NFSMNT_BIODS if (nfs_args.biods = hasmntval(&mnt, "biods")) nfs_args.flags |= NFSMNT_BIODS;#endif /* NFSMNT_BIODS */#if defined(NFSMNT_ACREGMIN) && defined(NFSMNT_ACREGMAX) /* * Don't cache attributes - they are changing under * the kernel's feet... */ nfs_args.acregmin = nfs_args.acregmax = 1; nfs_args.flags |= NFSMNT_ACREGMIN|NFSMNT_ACREGMAX;#endif /* defined(NFSMNT_ACREGMIN) && defined(NFSMNT_ACREGMAX) */ /* * These two are constructed internally by the calling routine */ if (hasmntopt(&mnt, MNTOPT_SOFT) != NULL) nfs_args.flags |= NFSMNT_SOFT;#ifdef MNTOPT_INTR if (hasmntopt(&mnt, MNTOPT_INTR) != NULL) nfs_args.flags |= NFSMNT_INT;#endif /* MNTOPT_INTR */ flags = compute_mount_flags(&mnt);#ifdef ULTRIX_HACK nfs_args.gfs_flags = flags; flags &= M_RDONLY; if (flags & M_RDONLY) nfs_args.flags |= NFSMNT_RONLY;#endif /* ULTRIX_HACK */ return mount_fs(&mnt, flags, (caddr_t) &nfs_args, retry, type);}static void afs_mkcacheref P((mntfs *mf));static void afs_mkcacheref(mf)mntfs *mf;{ /* * Build a new map cache for this node, or re-use * an existing cache for the same map. */ char *cache; if (mf->mf_fo && mf->mf_fo->opt_cache) cache = mf->mf_fo->opt_cache; else cache = "none"; mf->mf_private = (voidp) mapc_find(mf->mf_info, cache); mf->mf_prfree = mapc_free;}/* * Mount the root... */static int root_mount P((am_node *mp));static int root_mount(mp)am_node *mp;{ mntfs *mf = mp->am_mnt; mf->mf_mount = strealloc(mf->mf_mount, pid_fsname); mf->mf_private = (voidp) mapc_find(mf->mf_info, ""); mf->mf_prfree = mapc_free; return 0;}/* * Mount a sub-mount */static int afs_mount P((am_node *mp));static int afs_mount(mp)am_node *mp;{ mntfs *mf = mp->am_mnt; /* * Pseudo-directories are used to provide some structure * to the automounted directories instead * of putting them all in the top-level automount directory. * * Here, just increment the parent's link count. */ mp->am_parent->am_fattr.nlink++; /* * Info field of . means use parent's info field. * Historical - not documented. */ if (mf->mf_info[0] == '.' && mf->mf_info[1] == '\0') mf->mf_info = strealloc(mf->mf_info, mp->am_parent->am_mnt->mf_info); /* * Compute prefix: * * If there is an option prefix then use that else * If the parent had a prefix then use that with name * of this node appended else * Use the name of this node. * * That means if you want no prefix you must say so * in the map. */ if (mf->mf_fo->opt_pref) { /* * the prefix specified as an option */ mp->am_pref = strdup(mf->mf_fo->opt_pref); } else { /* * else the parent's prefix * followed by the name * followed by / */ char *ppref = mp->am_parent->am_pref; if (ppref == 0) ppref = ""; mp->am_pref = str3cat((char *) 0, ppref, mp->am_name, "/"); } /* * Attach a map cache */ afs_mkcacheref(mf); return 0;}/* * Mount the top-level */static int toplvl_mount P((am_node *mp));static int toplvl_mount(mp)am_node *mp;{ mntfs *mf = mp->am_mnt; struct stat stb; char opts[256]; int error; char *mnttype; /* * Mounting the automounter. * Make sure the mount directory exists, construct * the mount options and call the mount_toplvl routine. */ if (stat(mp->am_path, &stb) < 0) { return errno; } else if ((stb.st_mode & S_IFMT) != S_IFDIR) { plog(XLOG_WARNING, "%s is not a directory", mp->am_path); return ENOTDIR; } if (mf->mf_ops == &toplvl_ops) mnttype = "indirect"; else if (mf->mf_ops == &dfs_ops) mnttype = "direct";#ifdef HAS_UNION_FS else if (mf->mf_ops == &union_ops) mnttype = "union";#endif else mnttype = "auto"; /* * Construct some mount options */ sprintf(opts,#ifdef MNTOPT_INTR "%s,%s,%s=%d,%s=%d,%s=%d,%s", MNTOPT_INTR,#else "%s,%s=%d,%s=%d,%s=%d,%s",#endif /* MNTOPT_INTR */ "rw", "port", nfs_port, "timeo", afs_timeo, "retrans", afs_retrans, mnttype); error = mount_toplvl(mf->mf_mount, opts); if (error) { errno = error; plog(XLOG_FATAL, "mount_toplvl: %m"); return error; } return 0;}static void toplvl_mounted P((mntfs *mf));static void toplvl_mounted(mf)mntfs *mf;{ afs_mkcacheref(mf);}#ifdef HAS_UNION_FS/* * Create a reference to a union'ed entry */static int create_union_node P((char *dir, voidp arg));static int create_union_node(dir, arg)char *dir;voidp arg;{ if (strcmp(dir, "/defaults") != 0) { int error = 0; (void) toplvl_ops.lookuppn(arg, dir, &error, VLOOK_CREATE); if (error > 0) { errno = error; /* XXX */ plog(XLOG_ERROR, "Could not mount %s: %m", dir); } return error; } return 0;}static void union_mounted P((mntfs *mf));static void union_mounted(mf)mntfs *mf;{ int i; afs_mkcacheref(mf); /* * Having made the union mount point, * populate all the entries... */ for (i = 0; i <= last_used_map; i++) { am_node *mp = exported_ap[i]; if (mp && mp->am_mnt == mf) { /* return value from create_union_node is ignored by mapc_keyiter */ (void) mapc_keyiter((mnt_map *) mp->am_mnt->mf_private, (void (*)P((char*,void*))) create_union_node, mp); break; } }#ifdef notdef /* * would be nice to flush most of the cache, but we need to * keep the wildcard and /defaults entries... */ mapc_free(mf->mf_private); mf->mf_private = (voidp) mapc_find(mf->mf_info, "inc");/* mapc_add_kv(mf->mf_private, strdup("/defaults"), strdup("type:=link;opts:=nounmount;sublink:=${key}")); */#endif}#endif /* HAS_UNION_FS *//* * Unmount an automount sub-node */static int afs_umount P((am_node *mp));static int afs_umount(mp)am_node *mp;{ return 0;}/* * Unmount a top-level automount node */static int toplvl_umount P((am_node *mp));static int toplvl_umount(mp)am_node *mp;{ int error; struct stat stb;again: /* * The lstat is needed if this mount is type=direct. * When that happens, the kernel cache gets confused * between the underlying type (dir) and the mounted * type (link) and so needs to be re-synced before * the unmount. This is all because the unmount system * call follows links and so can't actually unmount * a link (stupid!). It was noted that doing an ls -ld * of the mount point to see why things were not working * actually fixed the problem - so simulate an ls -ld here. */ if (lstat(mp->am_path, &stb) < 0) {#ifdef DEBUG dlog("lstat(%s): %m", mp->am_path);#endif /* DEBUG */ } error = UMOUNT_FS(mp->am_path); if (error == EBUSY) { plog(XLOG_WARNING, "afs_unmount retrying %s in 1s", mp->am_path); sleep(1); /* XXX */ goto again; } return error;}/* * Unmount an automount node */static void afs_umounted P((am_node *mp));static void afs_umounted(mp)am_node *mp;{ /* * If this is a pseudo-directory then just adjust the link count * in the parent, otherwise call the generic unmount routine */ if (mp->am_parent && mp->am_parent->am_parent) --mp->am_parent->am_fattr.nlink;}/* * Mounting a file system may take a significant period of time. The * problem is that if this is done in the main process thread then * the entire automounter could be blocked, possibly hanging lots of * processes on the system. Instead we use a continuation scheme to * allow mounts to be attempted in a sub-process. When the sub-process * exits we pick up the exit status (by convention a UN*X error number) * and continue in a notifier. The notifier gets handed a data structure * and can then determine whether the mount was successful or not. If * not, it updates the data structure and tries again until there are no * more ways to try the mount, or some other permanent error occurs. * In the mean time no RPC reply is sent, even after the mount is succesful. * We rely on the RPC retry mechanism to resend the lookup request which * can then be handled. */struct continuation { char **ivec; /* Current mount info */ am_node *mp; /* Node we are trying to mount */ char *key; /* Map key */ char *info; /* Info string */ char **xivec; /* Saved strsplit vector */ char *auto_opts; /* Automount options */ am_opts fs_opts; /* Filesystem options */ char *def_opts; /* Default automount options */ int retry; /* Try again? */ int tried; /* Have we tried any yet? */ time_t start; /* Time we started this mount */ int callout; /* Callout identifier */};#define IN_PROGRESS(cp) ((cp)->mp->am_mnt->mf_flags & MFF_MOUNTING)/* * Discard an old continuation */static void free_continuation P((struct continuation *cp));static void free_continuation(cp)struct continuation *cp;{ if (cp->callout) untimeout(cp->callout); free((voidp) cp->key); free((voidp) cp->xivec); free((voidp) cp->info); free((voidp) cp->auto_opts); free((voidp) cp->def_opts); free_opts(&cp->fs_opts); free((voidp) cp);}static int afs_bgmount P((struct continuation*, int));/* * Discard the underlying mount point and replace * with a reference to an error filesystem. */static void assign_error_mntfs P((am_node *mp));static void assign_error_mntfs(mp)am_node *mp;{ if (mp->am_error > 0) { /* * Save the old error code */ int error = mp->am_error; if (error <= 0) error = mp->am_mnt->mf_error; /* * Discard the old filesystem */ free_mntfs(mp->am_mnt); /* * Allocate a new error reference */ mp->am_mnt = new_mntfs(); /* * Put back the error code */ mp->am_mnt->mf_error = error; mp->am_mnt->mf_flags |= MFF_ERROR; /* * Zero the error in the mount point */ mp->am_error = 0; }}/* * The continuation function. This is called by * the task notifier when a background mount attempt
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -