This introduces a new dependancy on libblkid.
If a filesystem being exported has a UUID that libblkid
can extract, then that is passed to the kernel for use
in identifying the filesystem in filehandles.
This means that 'fsid=' is no longer needed to work around the
problem of device numbers changing.
fsid= is still needed for fielsystems that have no device,
and can now be given 16byute uuid instead of just a 32bit one.
if test "$knfsd_cv_glibc2" = no; then
AC_CHECK_LIB(bsd, daemon, [LIBBSD="-lbsd"])
fi
+AC_CHECK_LIB(blkid, blkid_get_cache, [LIBBLKID="-lblkid"], AC_MSG_ERROR([libblkid needed]))
+AC_CHECK_HEADER(blkid/blkid.h, , AC_MSG_ERROR([Cannot file libblkid header file blkid/blkid.h]))
AC_SUBST(LIBSOCKET)
AC_SUBST(LIBCRYPT)
AC_SUBST(LIBBSD)
+AC_SUBST(LIBBLKID)
if test "$enable_gss" = yes; then
dnl 'gss' also depends on nfsidmap.h - at least for svcgssd_proc.c
int e_nsqgids;
int e_fsid;
char * e_mountpoint;
+ char * e_uuid;
};
struct rmtabent {
char path[200];
time_t now;
/* Note: the order of these caches is important.
- * The need to be flushed in dependancy order. So
+ * They need to be flushed in dependancy order. So
* a cache that references items in another cache,
* as nfsd.fh entries reference items in nfsd.export,
* must be flushed before the cache that it references.
if (ep->e_flags & NFSEXP_FSID) {
fprintf(fp, "fsid=%d,", ep->e_fsid);
}
+ if (ep->e_uuid)
+ fprintf(fp, "fsid=%s,", ep->e_uuid);
if (ep->e_mountpoint)
fprintf(fp, "mountpoint%s%s,",
ep->e_mountpoint[0]?"=":"", ep->e_mountpoint);
ee.e_mountpoint = NULL;
ee.e_nsquids = 0;
ee.e_nsqgids = 0;
+ ee.e_uuid = NULL;
if (strlen(hname) >= sizeof(ee.e_hostname)) {
xlog(L_WARNING, "client name %s too long", hname);
return 1;
}
+
+static int valid_uuid(char *uuid)
+{
+ /* must have 32 hex digits */
+ int cnt;
+ for (cnt = 0 ; *uuid; uuid++)
+ if (isxdigit(*uuid))
+ cnt++;
+ return cnt == 32;
+}
+
/*
* Parse option string pointed to by cp and set mount options accordingly.
*/
}
} else if (strncmp(opt, "fsid=", 5) == 0) {
char *oe;
- ep->e_fsid = strtoul(opt+5, &oe, 0);
- if (opt[5]=='\0' || *oe != '\0') {
- xlog(L_ERROR, "%s: %d: bad fsid \"%s\"\n",
- flname, flline, opt);
- goto bad_option;
+ if (strcmp(opt+5, "root") == 0) {
+ ep->e_fsid = 0;
+ ep->e_flags |= NFSEXP_FSID;
+ } else {
+ ep->e_fsid = strtoul(opt+5, &oe, 0);
+ if (opt[5]!='\0' && *oe == '\0')
+ ep->e_flags |= NFSEXP_FSID;
+ else if (valid_uuid(opt+5))
+ ep->e_uuid = strdup(opt+7);
+ else {
+ xlog(L_ERROR, "%s: %d: bad fsid \"%s\"\n",
+ flname, flline, opt);
+ goto bad_option;
+ }
}
- ep->e_flags |= NFSEXP_FSID;
} else if (strcmp(opt, "mountpoint")==0 ||
strcmp(opt, "mp") == 0 ||
strncmp(opt, "mountpoint=", 11)==0 ||
c = dumpopt(c, "no_acl");
if (ep->e_flags & NFSEXP_FSID)
c = dumpopt(c, "fsid=%d", ep->e_fsid);
+ if (ep->e_uuid)
+ c = dumpopt(c, "fsid=%s", ep->e_uuid);
if (ep->e_mountpoint)
c = dumpopt(c, "mountpoint%s%s",
ep->e_mountpoint[0]?"=":"",
exported.
.TP
-.IR fsid= num
-This option forces the filesystem identification portion of the file
-handle and file attributes used on the wire to be
-.I num
-instead of a number derived from the major and minor number of the
-block device on which the filesystem is mounted. Any 32 bit number
-can be used, but it must be unique amongst all the exported filesystems.
+.IR fsid= num|root|uuid
+NFS needs to be able to identify each filesystem that it exports.
+Normally it will use a UUID for the filesystem (if the filesystem has
+such a thing) or the device number of the device holding the
+filesystem (if the filesystem is stored on the device).
-This can be useful for NFS failover, to ensure that both servers of
-the failover pair use the same NFS file handles for the shared filesystem
-thus avoiding stale file handles after failover.
+As not all filesystems are stored on devices, and not all filesystems
+have UUIDs, it is sometimes necessary to explicitly tell NFS how to
+identify a filesystem. This is done with the
+.I fsid=
+option.
-Some Linux filesystems are not mounted on a block device; exporting
-these via NFS requires the use of the
-.I fsid
-option (although that may still not be enough).
+For NFSv4, there is a distinguished filesystem which is the root of
+all exported filesystem. This is specified with
+.I fsid=root
+or
+.I fsid=0
+both of which mean exactly the same thing.
+
+Other filesystems can be identified with a small integer, or a UUID
+which should contain 32 hex digits and arbitrary punctuation.
-The value 0 has a special meaning when use with NFSv4. NFSv4 has a
-concept of a root of the overall exported filesystem. The export point
-exported with fsid=0 will be used as this root.
+Linux kernels version 2.6.20 and earlier do not understand the UUID
+setting so a small integer must be used if an fsid option needs to be
+set for such kernels. Setting both a small number and a UUID is
+supported so the same configuration can be made to work on old and new
+kernels alike.
.SS User ID Mapping
.PP
mountd_LDADD = ../../support/export/libexport.a \
../../support/nfs/libnfs.a \
../../support/misc/libmisc.a \
- $(LIBBSD) $(LIBWRAP) $(LIBNSL)
+ $(LIBBSD) $(LIBWRAP) $(LIBNSL) $(LIBBLKID)
mountd_CPPFLAGS = $(AM_CPPFLAGS) $(CPPFLAGS) \
-I$(top_builddir)/support/include \
-I$(top_srcdir)/support/export
#include "mountd.h"
#include "xmalloc.h"
+#include "blkid/blkid.h"
+
+
+enum nfsd_fsid {
+ FSID_DEV = 0,
+ FSID_NUM,
+ FSID_MAJOR_MINOR,
+ FSID_ENCODE_DEV,
+ FSID_UUID4_INUM,
+ FSID_UUID8,
+ FSID_UUID16,
+ FSID_UUID16_INUM,
+};
+
/*
* Support routines for text-based upcalls.
* Fields are separated by spaces.
}
+int get_uuid(char *path, char *uuid, int uuidlen, char *u)
+{
+ /* extract hex digits from uuidstr and compose a uuid
+ * of the given length (max 16), xoring bytes to make
+ * a smaller uuid. Then compare with uuid
+ */
+ int i = 0;
+ const char *val;
+
+ if (path) {
+ static blkid_cache cache = NULL;
+ struct stat stb;
+ char *devname;
+ blkid_tag_iterate iter;
+ blkid_dev dev;
+ const char *type;
+ if (cache == NULL)
+ blkid_get_cache(&cache, NULL);
+
+ blkid_probe_all_new(cache);
+
+ if (stat(path, &stb) != 0)
+ return 0;
+ devname = blkid_devno_to_devname(stb.st_dev);
+ if (!devname)
+ return 0;
+ dev = blkid_get_dev(cache, devname, BLKID_DEV_NORMAL);
+ free(devname);
+ if (!dev)
+ return 0;
+ iter = blkid_tag_iterate_begin(dev);
+ if (!iter)
+ return 0;
+ while (blkid_tag_next(iter, &type, &val) == 0)
+ if (strcmp(type, "UUID") == 0)
+ break;
+ blkid_tag_iterate_end(iter);
+ if (!type)
+ return 0;
+ } else {
+ val = uuid;
+ }
+
+ memset(u, 0, uuidlen);
+ for ( ; *val ; val++) {
+ char c = *val;
+ if (!isxdigit(c))
+ continue;
+ if (isalpha(c)) {
+ if (isupper(c))
+ c = c - 'A' + 10;
+ else
+ c = c - 'a' + 10;
+ } else
+ c = c - '0' + 0;
+ if ((i&1) == 0)
+ c <<= 4;
+ u[i/2] ^= c;
+ i++;
+ if (i == uuidlen*2)
+ i = 0;
+ }
+ return 1;
+}
+
+
void nfsd_fh(FILE *f)
{
/* request are:
int fsidlen;
unsigned int dev, major=0, minor=0;
unsigned int inode=0;
+ unsigned long long inode64;
unsigned int fsidnum=0;
char fsid[32];
struct exportent *found = NULL;
nfs_export *exp;
int i;
int dev_missing = 0;
+ int uuidlen = 0;
+ char *fhuuid = NULL;
if (readline(fileno(f), &lbuf, &lbuflen) != 1)
return;
goto out;
if (qword_get_int(&cp, &fsidtype) != 0)
goto out;
- if (fsidtype < 0 || fsidtype > 3)
+ if (fsidtype < 0 || fsidtype > 7)
goto out; /* unknown type */
if ((fsidlen = qword_get(&cp, fsid, 32)) <= 0)
goto out;
switch(fsidtype) {
- case 0: /* 4 bytes: 2 major, 2 minor, 4 inode */
+ case FSID_DEV: /* 4 bytes: 2 major, 2 minor, 4 inode */
if (fsidlen != 8)
goto out;
memcpy(&dev, fsid, 4);
minor = ntohl(dev) & 0xFFFF;
break;
- case 1: /* 4 bytes - fsid */
+ case FSID_NUM: /* 4 bytes - fsid */
if (fsidlen != 4)
goto out;
memcpy(&fsidnum, fsid, 4);
break;
- case 2: /* 12 bytes: 4 major, 4 minor, 4 inode
+ case FSID_MAJOR_MINOR: /* 12 bytes: 4 major, 4 minor, 4 inode
* This format is never actually used but was
* an historical accident
*/
memcpy(&inode, fsid+8, 4);
break;
- case 3: /* 8 bytes: 4 byte packed device number, 4 inode */
+ 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
*/
minor = (dev & 0xff) | ((dev >> 12) & 0xfff00);
break;
+ case FSID_UUID4_INUM: /* 4 byte inode number and 4 byte uuid */
+ if (fsidlen != 8)
+ goto out;
+ memcpy(&inode, fsid, 4);
+ uuidlen = 4;
+ fhuuid = fsid+4;
+ break;
+ case FSID_UUID8: /* 8 byte uuid */
+ if (fsidlen != 8)
+ goto out;
+ uuidlen = 8;
+ fhuuid = fsid;
+ break;
+ case FSID_UUID16: /* 16 byte uuid */
+ if (fsidlen != 16)
+ goto out;
+ uuidlen = 16;
+ fhuuid = fsid;
+ break;
+ case FSID_UUID16_INUM: /* 8 byte inode number and 16 byte uuid */
+ if (fsidlen != 24)
+ goto out;
+ memcpy(&inode64, fsid, 8);
+ inode = inode64;
+ uuidlen = 16;
+ fhuuid = fsid+8;
+ break;
}
auth_reload();
for (i=0 ; i < MCL_MAXTYPES; i++) {
for (exp = exportlist[i]; exp; exp = exp->m_next) {
struct stat stb;
+ char u[16];
if (!client_member(dom, exp->m_client->m_hostname))
continue;
dev_missing ++;
if (stat(exp->m_export.e_path, &stb) != 0)
continue;
- if (fsidtype == 1 &&
- ((exp->m_export.e_flags & NFSEXP_FSID) == 0 ||
- exp->m_export.e_fsid != fsidnum))
- continue;
- if (fsidtype != 1) {
+ switch(fsidtype){
+ case FSID_DEV:
+ case FSID_MAJOR_MINOR:
+ case FSID_ENCODE_DEV:
if (stb.st_ino != inode)
continue;
if (major != major(stb.st_dev) ||
minor != minor(stb.st_dev))
continue;
+ break;
+ case FSID_NUM:
+ if (((exp->m_export.e_flags & NFSEXP_FSID) == 0 ||
+ exp->m_export.e_fsid != fsidnum))
+ continue;
+ break;
+ case FSID_UUID4_INUM:
+ case FSID_UUID16_INUM:
+ if (stb.st_ino != inode)
+ continue;
+ goto check_uuid;
+ case FSID_UUID8:
+ case FSID_UUID16:
+ if (!is_mountpoint(exp->m_export.e_path))
+ continue;
+ check_uuid:
+ if (exp->m_export.e_uuid)
+ get_uuid(NULL, exp->m_export.e_uuid,
+ uuidlen, u);
+ else if (get_uuid(exp->m_export.e_path, NULL,
+ uuidlen, u) == 0)
+ continue;
+
+ if (memcmp(u, fhuuid, uuidlen) != 0)
+ continue;
+ break;
}
/* It's a match !! */
if (!found)
qword_printint(f, exp->e_anonuid);
qword_printint(f, exp->e_anongid);
qword_printint(f, exp->e_fsid);
+ if (exp->e_uuid == NULL) {
+ char u[16];
+ if (get_uuid(exp->e_path, NULL, 16, u)) {
+ qword_print(f, "uuid");
+ qword_printhex(f, u, 16);
+ }
+ } else if (exp->e_uuid) {
+ qword_print(f, "uuid");
+ qword_printhex(f, exp->e_uuid, 16);
+ }
}
return qword_eol(f);
}