/* $Id: findfs.C,v 1.9 2001/10/08 22:40:13 dm Exp $ */ /* * * Copyright (C) 2001 David Mazieres (dm@uun.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA * */ #include "sfsmisc.h" #include "getfh3.h" #include "rxx.h" #include "parseopt.h" const u_int64_t nodev (static_cast (-1)); #define NFSDB ".nfsdb" static rxx nfsdbrx ("^(0x[0-9a-f]+)\\s+([\\w\\.:\\-]+)\\s+([\\w\\-]+)" "\\s+([0-9a-f]+)\\s+(\\w+):(\\d+)"); void pipe2str (int fd, cbs cb, int *fdp = NULL, strbuf *sb = NULL) { if (!sb) { sb = New strbuf (); make_async (fd); fdcb (fd, selread, wrap (pipe2str, fd, cb, fdp, sb)); } int n; if (fdp && *fdp < 0) { char *buf = sb->tosuio ()->getspace (8192); n = readfd (fd, buf, 8192, fdp); if (n > 0) sb->tosuio ()->print (buf, n); } else n = sb->tosuio ()->input (fd); if (!n) (*cb) (*sb); else if (n < 0 && errno != EAGAIN) (*cb) (NULL); else return; fdcb (fd, selread, NULL); close (fd); delete sb; } void chldrun (cbi chld, cbs cb) { int fds[2]; if (pipe (fds) < 0) (*cb) (NULL); switch (afork ()) { case -1: (*cb) (NULL); return; case 0: close (fds[0]); (*chld) (fds[1]); _exit (0); } close (fds[1]); pipe2str (fds[0], cb); } static void nfsdbcat (int out) { str nfsdbpath = strbuf ("%s/%s", sfsroot, NFSDB); str nfsdb; do { nfsdb = file2str (nfsdbpath); } while (!nfsdb && errno == ESTALE); if (nfsdb) write (out, nfsdb, nfsdb.len ()); } void nfsdbfetch (cbs cb) { chldrun (wrap (nfsdbcat), cb); } inline bool hexconv (int &out, const char in) { if (in >= '0' && in <= '9') out = in - '0'; else if (in >= 'a' && in <= 'f') out = in - ('a' - 10); else if (in >= 'A' && in <= 'F') out = in - ('A' - 10); else return false; return true; } template inline bool a2bytes (rpc_bytes &b, str a) { int i = 0; if (a.len () & 1) return false; b.setsize (a.len () / 2); for (const char *p = a.cstr (), *e = p + a.len (); p < e; p += 2) { int h, l; if (!hexconv (h, p[0]) || !hexconv (l, p[1])) return false; b[i++] = h << 4 | l; } return true; } static void getsfsnfs_2 (str sspath, str path, ref c, callback, const nfs_fh3 *>::ref cb, const nfs_fh3 *fhp, const fattr3exp *, str err) { if (err) { warn << sspath << "/" << path << ": " << err << "\n"; (*cb) (NULL, NULL); } else (*cb) (c, fhp); } static void getsfsnfs_1 (u_int64_t dev, str path, const authunix_parms *aup, callback, const nfs_fh3 *>::ref cb, str nfsdb) { sockaddr_in sin; nfs_fh3 fh; u_int64_t d2; strbuf sb; if (!nfsdb) { (*cb) (NULL, NULL); return; } bzero (&sin, sizeof (&sin)); sb << nfsdb; while (str line = suio_getline (sb.tosuio ())) if (nfsdbrx.match (line) && convertint (nfsdbrx[1], &d2) && d2 == dev && a2bytes (fh.data, nfsdbrx[4]) && nfsdbrx[5] == "UDP" && convertint (nfsdbrx[6], &sin.sin_port)) { sin.sin_family = AF_INET; sin.sin_port = htons (sin.sin_port); sin.sin_addr.s_addr = htonl (INADDR_LOOPBACK); ref c = aclnt::alloc (udpxprt, nfs_program_3, (sockaddr *) &sin); lookupfh3 (c, fh, path, wrap (getsfsnfs_2, nfsdbrx[2], path, c, cb), aup); return; } (*cb) (NULL, NULL); } static void getsfsnfs (u_int64_t dev, str path, const authunix_parms *aup, callback, const nfs_fh3 *>::ref cb) { nfsdbfetch (wrap (getsfsnfs_1, dev, path, aup, cb)); } static void aupsetgroups (const authunix_parms *aup) { int ng = min (NGROUPS_MAX, aup->aup_len); GETGROUPS_T gids[NGROUPS_MAX]; for (int i = 0; i < ng; i++) gids[i] = aup->aup_gids[i]; if (setgroups (ng, (gid_t*)gids) < 0) { warn ("setgroups: %m\n"); _exit (1); } if (setgid (aup->aup_gid) < 0) { warn ("setgid: %m\n"); _exit (1); } } static void pathinfofetch_cb (callback::ref cb, str s) { static rxx pirx ("^(0x[0-9a-f]+)\n(.*)\n([\\x00-\\xff]*)\n$"); u_int64_t dev; if (!s || !pirx.search (s) || !convertint (pirx[1], &dev)) (*cb) (nodev, NULL, NULL); else (*cb) (dev, pirx[2], pirx[3]); } void pathinfofetch (const authunix_parms *aup, str path, callback::ref cb, int *fdp) { static str pathinfo; if (!pathinfo) pathinfo = fix_exec_path ("pathinfo"); int fds[2]; if (socketpair (AF_UNIX, SOCK_STREAM, 0, fds) < 0) { warn ("socketpair: %m\n"); (*cb) (nodev, NULL, NULL); return; } close_on_exec (fds[0]); str uid (strbuf ("%d", aup ? aup->aup_uid : getuid ())); const char *av[] = { pathinfo.cstr (), "-u", uid.cstr (), path.cstr (), NULL }; if (aup) aspawn (pathinfo, const_cast (av), 0, fds[1], 2, wrap (&aupsetgroups, aup)); else aspawn (pathinfo, const_cast (av), 0, fds[1]); close (fds[1]); if (fdp) *fdp = -1; pipe2str (fds[0], wrap (pathinfofetch_cb, cb), fdp); } class fsfetch { const int flags; const authunix_parms *aup; str path; findfscb_t cb; str rpath; str devname; str hostname; sockaddr_in hostaddr; nfs_fh3 fh; ptr nfsc; int pathfd; u_int64_t rdev; void fail (str err) { close (pathfd); (*cb) (NULL, err); delete this; } void finish (const nfs_fh3 *fhp); void gotpi (u_int64_t dev, str devname, str rp); void gotsfsi (ptr c, const nfs_fh3 *fhp); void gotnfsi (const nfs_fh3 *fhp, str); void gotnfsc (ptr c, clnt_stat stat); void lookupcb (const nfs_fh3 *fhp, const fattr3exp *, str err); public: fsfetch (const authunix_parms *aup, str path, findfscb_t cb, int flags); }; fsfetch::fsfetch (const authunix_parms *aup, str path, findfscb_t cb, int fl) : flags (fl), aup (aup), path (path), cb (cb), pathfd (-1) { pathinfofetch (aup, path, wrap (this, &fsfetch::gotpi), &pathfd); } void fsfetch::finish (const nfs_fh3 *fhp) { ref info = New refcounted (nfsc, hostname, *fhp, rdev); info->fd = pathfd; (*cb) (info, NULL); delete this; } void fsfetch::gotpi (u_int64_t dev, str dn, str rp) { if (!dn || !rp) { fail (path << ": no information on path"); return; } rpath = rp; devname = dn; rdev = dev; if (flags & FINDFS_NOSFS) gotsfsi (NULL, NULL); else getsfsnfs (rdev, rpath, aup, wrap (this, &fsfetch::gotsfsi)); } void fsfetch::gotsfsi (ptr c, const nfs_fh3 *fhp) { if (c && fhp) { nfsc = c; finish (fhp); return; } static rxx nfspath ("^([a-zA-Z0-9.\\-]+):(/.*)$"); if (nfspath.match (devname)) { hostname = nfspath[1]; getfh3 (hostname, nfspath[2], wrap (this, &fsfetch::gotnfsi)); } else if (!(flags & FINDFS_NOLOCAL)) { hostname = "127.0.0.1"; devname = hostname << ":" << path; rpath = ""; getfh3 (hostname, path, wrap (this, &fsfetch::gotnfsi)); } else { fail (path << ": not NFS or SFS file system"); return; } } void fsfetch::gotnfsi (const nfs_fh3 *fhp, str err) { if (err && !(flags & FINDFS_NOLOCAL) && hostname == "127.0.0.1") { vec addrs; myipaddrs (&addrs); while (!addrs.empty () && ntohl (addrs[0].s_addr) == INADDR_LOOPBACK) addrs.pop_front (); if (!addrs.empty ()) { hostname = inet_ntoa (addrs[0]); // devname = hostname << ":" << path; getfh3 (hostname, path, wrap (this, &fsfetch::gotnfsi)); return; } } if (err) { fail (devname << ": NFS mount: " << err); return; } fh = *fhp; aclntudp_create (hostname, 0, nfs_program_3, wrap (this, &fsfetch::gotnfsc)); } void fsfetch::gotnfsc (ptr c, clnt_stat stat) { if (stat) { fail (hostname << ": NFS server: " << stat); return; } nfsc = c; if (rpath == "/" || rpath == "") lookupcb (&fh, NULL, NULL); else lookupfh3 (nfsc, fh, rpath, wrap (this, &fsfetch::lookupcb), aup); } void fsfetch::lookupcb (const nfs_fh3 *fhp, const fattr3exp *, str err) { if (fhp) finish (fhp); else fail (devname << rpath << ": " << err); } void findfs (const authunix_parms *aup, str path, findfscb_t cb, int flags) { vNew fsfetch (aup, path, cb, flags); }