+static int uuid_by_path(char *path, int type, int uuidlen, char *uuid)
+{
+ /* get a uuid for the filesystem found at 'path'.
+ * There are several possible ways of generating the
+ * uuids (types).
+ * Type 0 is used for new filehandles, while other types
+ * may be used to interpret old filehandle - to ensure smooth
+ * forward migration.
+ * We return 1 if a uuid was found (and it might be worth
+ * trying the next type) or 0 if no more uuid types can be
+ * extracted.
+ */
+
+ /* Possible sources of uuid are
+ * - blkid uuid
+ * - statfs64 uuid
+ *
+ * On some filesystems (e.g. vfat) the statfs64 uuid is simply an
+ * encoding of the device that the filesystem is mounted from, so
+ * it we be very bad to use that (as device numbers change). blkid
+ * must be preferred.
+ * On other filesystems (e.g. btrfs) the statfs64 uuid contains
+ * important info that the blkid uuid cannot contain: This happens
+ * when multiple subvolumes are exported (they have the same
+ * blkid uuid but different statfs64 uuids).
+ * We rely on get_uuid_blkdev *knowing* which is which and not returning
+ * a uuid for filesystems where the statfs64 uuid is better.
+ *
+ */
+ struct statfs64 st;
+ char fsid_val[17];
+ const char *blkid_val;
+ const char *val;
+
+ blkid_val = get_uuid_blkdev(path);
+
+ if (statfs64(path, &st) == 0 &&
+ (st.f_fsid.__val[0] || st.f_fsid.__val[1]))
+ snprintf(fsid_val, 17, "%08x%08x",
+ st.f_fsid.__val[0], st.f_fsid.__val[1]);
+ else
+ fsid_val[0] = 0;
+
+ if (blkid_val && (type--) == 0)
+ val = blkid_val;
+ else if (fsid_val[0] && (type--) == 0)
+ val = fsid_val;
+ else
+ return 0;
+
+ get_uuid(val, uuidlen, uuid);
+ return 1;
+}
+
+/* Iterate through /etc/mtab, finding mountpoints
+ * at or below a given path
+ */
+static char *next_mnt(void **v, char *p)
+{
+ FILE *f;
+ struct mntent *me;
+ int l = strlen(p);
+ if (*v == NULL) {
+ f = setmntent("/etc/mtab", "r");
+ *v = f;
+ } else
+ f = *v;
+ while ((me = getmntent(f)) != NULL &&
+ (strncmp(me->mnt_dir, p, l) != 0 ||
+ me->mnt_dir[l] != '/'))
+ ;
+ if (me == NULL) {
+ endmntent(f);
+ *v = NULL;
+ return NULL;
+ }
+ return me->mnt_dir;
+}
+
+/* True iff e1 is a child of e2 and e2 has crossmnt set: */
+static bool subexport(struct exportent *e1, struct exportent *e2)
+{
+ char *p1 = e1->e_path, *p2 = e2->e_path;
+ int l2 = strlen(p2);
+
+ return e2->e_flags & NFSEXP_CROSSMOUNT
+ && strncmp(p1, p2, l2) == 0
+ && p1[l2] == '/';
+}
+
+struct parsed_fsid {
+ int fsidtype;
+ /* We could use a union for this, but it would be more
+ * complicated; why bother? */
+ unsigned int inode;
+ unsigned int minor;
+ unsigned int major;
+ unsigned int fsidnum;
+ int uuidlen;
+ char *fhuuid;
+};
+
+int parse_fsid(int fsidtype, int fsidlen, char *fsid, struct parsed_fsid *parsed)
+{
+ unsigned int dev;
+ unsigned long long inode64;
+
+ parsed->fsidtype = fsidtype;
+ switch(fsidtype) {
+ case FSID_DEV: /* 4 bytes: 2 major, 2 minor, 4 inode */
+ if (fsidlen != 8)
+ return -1;
+ memcpy(&dev, fsid, 4);
+ memcpy(&parsed->inode, fsid+4, 4);
+ parsed->major = ntohl(dev)>>16;
+ parsed->minor = ntohl(dev) & 0xFFFF;
+ break;
+
+ case FSID_NUM: /* 4 bytes - fsid */
+ if (fsidlen != 4)
+ return -1;
+ memcpy(&parsed->fsidnum, fsid, 4);
+ break;
+
+ case FSID_MAJOR_MINOR: /* 12 bytes: 4 major, 4 minor, 4 inode
+ * This format is never actually used but was
+ * an historical accident
+ */
+ if (fsidlen != 12)
+ return -1;
+ memcpy(&dev, fsid, 4);
+ parsed->major = ntohl(dev);
+ memcpy(&dev, fsid+4, 4);
+ parsed->minor = ntohl(dev);
+ memcpy(&parsed->inode, fsid+8, 4);
+ break;
+
+ case FSID_ENCODE_DEV: /* 8 bytes: 4 byte packed device number, 4 inode */
+ /* This is *host* endian, not net-byte-order, because
+ * no-one outside this host has any business interpreting it
+ */
+ if (fsidlen != 8)
+ return -1;
+ memcpy(&dev, fsid, 4);
+ memcpy(&parsed->inode, fsid+4, 4);
+ parsed->major = (dev & 0xfff00) >> 8;
+ parsed->minor = (dev & 0xff) | ((dev >> 12) & 0xfff00);
+ break;
+
+ case FSID_UUID4_INUM: /* 4 byte inode number and 4 byte uuid */
+ if (fsidlen != 8)
+ return -1;
+ memcpy(&parsed->inode, fsid, 4);
+ parsed->uuidlen = 4;
+ parsed->fhuuid = fsid+4;
+ break;
+ case FSID_UUID8: /* 8 byte uuid */
+ if (fsidlen != 8)
+ return -1;
+ parsed->uuidlen = 8;
+ parsed->fhuuid = fsid;
+ break;
+ case FSID_UUID16: /* 16 byte uuid */
+ if (fsidlen != 16)
+ return -1;
+ parsed->uuidlen = 16;
+ parsed->fhuuid = fsid;
+ break;
+ case FSID_UUID16_INUM: /* 8 byte inode number and 16 byte uuid */
+ if (fsidlen != 24)
+ return -1;
+ memcpy(&inode64, fsid, 8);
+ parsed->inode = inode64;
+ parsed->uuidlen = 16;
+ parsed->fhuuid = fsid+8;
+ break;
+ }
+ return 0;
+}
+
+static bool match_fsid(struct parsed_fsid *parsed, nfs_export *exp, char *path)
+{
+ struct stat stb;
+ int type;
+ char u[16];
+
+ if (stat(path, &stb) != 0)
+ return false;
+ if (!S_ISDIR(stb.st_mode) && !S_ISREG(stb.st_mode))
+ return false;
+
+ switch (parsed->fsidtype) {
+ case FSID_DEV:
+ case FSID_MAJOR_MINOR:
+ case FSID_ENCODE_DEV:
+ if (stb.st_ino != parsed->inode)
+ return false;
+ if (parsed->major != major(stb.st_dev) ||
+ parsed->minor != minor(stb.st_dev))
+ return false;
+ return true;
+ case FSID_NUM:
+ if (((exp->m_export.e_flags & NFSEXP_FSID) == 0 ||
+ exp->m_export.e_fsid != parsed->fsidnum))
+ return false;
+ return true;
+ case FSID_UUID4_INUM:
+ case FSID_UUID16_INUM:
+ if (stb.st_ino != parsed->inode)
+ return false;
+ goto check_uuid;
+ case FSID_UUID8:
+ case FSID_UUID16:
+ if (!is_mountpoint(path))
+ return false;
+ check_uuid:
+ if (exp->m_export.e_uuid)
+ get_uuid(exp->m_export.e_uuid, parsed->uuidlen, u);
+ else
+ for (type = 0;
+ uuid_by_path(path, type, parsed->uuidlen, u);
+ type++)
+ if (memcmp(u, parsed->fhuuid, parsed->uuidlen) == 0)
+ return true;
+
+ if (memcmp(u, parsed->fhuuid, parsed->uuidlen) != 0)
+ return false;
+ return true;
+ }
+ /* Well, unreachable, actually: */
+ return false;
+}
+
+static void nfsd_fh(FILE *f)