+static inline int isnetfs(const char * type)
+{
+ static const char* netfs[] = {"nfs", "nfs4", "smbfs", "cifs", "afs", "ncpfs", (char*)0};
+ int n;
+ for (n = 0; netfs[n]; n++) {
+ if (!strcasecmp(netfs[n], type))
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Remember all NFS typed partitions.
+ */
+void init_nfs(void)
+{
+ struct stat st;
+ struct mntent * ent;
+ FILE * mnt;
+
+ nlist = (NFS*)0;
+
+ if (stat("/proc/version", &st) < 0)
+ return;
+ if ((mnt = setmntent("/proc/mounts", "r")) == (FILE*)0)
+ return;
+
+ while ((ent = getmntent(mnt))) {
+ if (isnetfs(ent->mnt_type)) {
+ size_t nlen = strlen(ent->mnt_dir);
+ NFS *restrict p;
+ xmemalign((void*)&p, sizeof(void*), alignof(NFS)+(nlen+1));
+ p->name = ((char*)p)+alignof(NFS);
+ p->nlen = nlen;
+ p->shadow = (SHADOW*)0;
+
+ strcpy(p->name, ent->mnt_dir);
+ if (nlist)
+ nlist->prev = p;
+ p->next = nlist;
+ p->prev = (NFS*)0;
+ nlist = p;
+ }
+ }
+ endmntent(mnt);
+
+ if ((mnt = setmntent("/proc/mounts", "r")) == (FILE*)0)
+ return;
+
+ while ((ent = getmntent(mnt))) {
+ NFS *p;
+
+ for (p = nlist; p; p = p->next) {
+ SHADOW * restrict s;
+ size_t nlen;
+
+ if (strcmp(ent->mnt_dir, p->name) == 0)
+ continue;
+ if (strncmp(ent->mnt_dir, p->name, p->nlen) != 0)
+ continue;
+
+ nlen = strlen(ent->mnt_dir);
+ xmemalign((void*)&s, sizeof(void*), alignof(SHADOW)+(nlen+1));
+ s->name = ((char*)s)+alignof(SHADOW);
+ s->nlen = nlen;
+
+ strcpy(s->name, ent->mnt_dir);
+ if (p->shadow)
+ p->shadow->prev = s;
+ s->next = p->shadow;
+ s->prev = (SHADOW*)0;
+ p->shadow = s;
+ }
+ }
+ endmntent(mnt);
+}
+
+static void clear_shadow(SHADOW *restrict shadow)
+{
+ SHADOW *s, *n, *l;
+
+ n = shadow;
+ l = (SHADOW*)0;
+ for (s = shadow; n; s = n) {
+ l = s->prev;
+ n = s->next;
+ if (s == shadow) {
+ if (n) n->prev = (SHADOW*)0;
+ shadow = n;
+ } else if (l) {
+ if (n) n->prev = l;
+ l->next = n;
+ }
+ free(s);
+ }
+}
+
+static void clear_mnt(void)
+{
+ NFS *p, *n, *l;
+
+ n = nlist;
+ l = (NFS*)0;
+ for (p = nlist; n; p = n) {
+ l = p->prev;
+ n = p->next;
+ if (p == nlist) {
+ if (n) n->prev = (NFS*)0;
+ nlist = n;
+ } else if (l) {
+ if (n) n->prev = l;
+ l->next = n;
+ }
+ if (p->shadow)
+ clear_shadow(p->shadow);
+ free(p);
+ }
+}
+
+/*
+ * Check if path is ia shadow off a NFS partition.
+ */
+static int shadow(SHADOW *restrict this, const char *restrict name, const size_t nlen)
+{
+ SHADOW *s;
+
+ if (!this)
+ goto out;
+ for (s = this; s; s = s->next) {
+ if (nlen < s->nlen)
+ continue;
+ if (name[s->nlen] != '\0' && name[s->nlen] != '/')
+ continue;
+ if (strncmp(name, s->name, s->nlen) == 0)
+ return 1;
+ }
+out:
+ return 0;
+}
+
+/*
+ * Check path is located on a network based partition.
+ */
+int check4nfs(const char * path, char * real)
+{
+ char buf[PATH_MAX+1];
+ const char *curr;
+ int deep = MAXSYMLINKS;
+
+ if (!nlist) return 0;
+
+ curr = path;
+ do {
+ const char *prev;
+ int len;
+
+ if ((prev = strdupa(curr)) == NULL) {
+ nsyslog(LOG_ERR, "strdupa(): %s\n", strerror(errno));
+ return 0;
+ }
+
+ errno = 0;
+ if ((len = readlink(curr, buf, PATH_MAX)) < 0)
+ break;
+ buf[len] = '\0';
+
+ if (buf[0] != '/') {
+ const char *slash;
+
+ if ((slash = strrchr(prev, '/'))) {
+ size_t off = slash - prev + 1;
+
+ if (off + len > PATH_MAX)
+ len = PATH_MAX - off;
+
+ memmove(&buf[off], &buf[0], len + 1);
+ memcpy(&buf[0], prev, off);
+ }
+ }
+ curr = &buf[0];
+
+ if (deep-- <= 0) return 0;
+
+ } while (1);
+
+ if (real) strcpy(real, curr);
+
+ if (errno == EINVAL) {
+ const size_t nlen = strlen(curr);
+ NFS *p;
+ for (p = nlist; p; p = p->next) {
+ if (nlen < p->nlen)
+ continue;
+ if (curr[p->nlen] != '\0' && curr[p->nlen] != '/')
+ continue;
+ if (!strncmp(curr, p->name, p->nlen)) {
+ if (shadow(p->shadow, curr, nlen))
+ continue;
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+