]> git.decadent.org.uk Git - nfs-utils.git/blobdiff - utils/mountd/cache.c
Handle 'crossmnt' when kernel asked for export options for a directory.
[nfs-utils.git] / utils / mountd / cache.c
index a14f4f2987369fe8ae299e99b3a6d118c1cf42ef..2f13f03ec9a61247228d3c4e347488c7393e3af5 100644 (file)
@@ -52,7 +52,7 @@ enum nfsd_fsid {
  * Record is terminated with newline.
  *
  */
-int cache_export_ent(char *domain, struct exportent *exp);
+int cache_export_ent(char *domain, struct exportent *exp, char *p);
 
 
 char *lbuf  = NULL;
@@ -408,12 +408,20 @@ void nfsd_fh(FILE *f)
        }
 
        if (found)
-               cache_export_ent(dom, found);
+               cache_export_ent(dom, found, found->e_path);
 
        qword_print(f, dom);
        qword_printint(f, fsidtype);
        qword_printhex(f, fsid, fsidlen);
-       qword_printint(f, time(0)+30*60);
+       /* The fsid -> path lookup can be quite expensive as it
+        * potentially stats and reads lots of devices, and some of those
+        * might have spun-down.  The Answer is not likely to
+        * change underneath us, and an 'exportfs -f' can always
+        * remove this from the kernel, so use a really log
+        * timeout.  Maybe this should be configurable on the command
+        * line.
+        */
+       qword_printint(f, 0x7fffffff);
        if (found)
                qword_print(f, found->e_path);
        qword_eol(f);
@@ -455,9 +463,10 @@ static int dump_to_cache(FILE *f, char *domain, char *path, struct exportent *ex
                qword_printint(f, exp->e_anonuid);
                qword_printint(f, exp->e_anongid);
                qword_printint(f, exp->e_fsid);
+               write_fsloc(f, exp, path);
                if (exp->e_uuid == NULL) {
                        char u[16];
-                       if (get_uuid(exp->e_path, NULL, 16, u)) {
+                       if (get_uuid(path, NULL, 16, u)) {
                                qword_print(f, "uuid");
                                qword_printhex(f, u, 16);
                        }
@@ -465,7 +474,6 @@ static int dump_to_cache(FILE *f, char *domain, char *path, struct exportent *ex
                        qword_print(f, "uuid");
                        qword_printhex(f, exp->e_uuid, 16);
                }
-               write_fsloc(f, &exp, path);
        }
        return qword_eol(f);
 }
@@ -482,6 +490,7 @@ void nfsd_export(FILE *f)
        int i;
        char *dom, *path;
        nfs_export *exp, *found = NULL;
+       int found_type = 0;
 
 
        if (readline(fileno(f), &lbuf, &lbuflen) != 1)
@@ -506,14 +515,42 @@ void nfsd_export(FILE *f)
                for (exp = exportlist[i]; exp; exp = exp->m_next) {
                        if (!client_member(dom, exp->m_client->m_hostname))
                                continue;
-                       if (strcmp(path, exp->m_export.e_path))
+                       if (exp->m_export.e_flags & NFSEXP_CROSSMOUNT) {
+                               /* if path is a mountpoint below e_path, then OK */
+                               int l = strlen(exp->m_export.e_path);
+                               if (strcmp(path, exp->m_export.e_path) == 0 ||
+                                   (strncmp(path, exp->m_export.e_path, l) == 0 &&
+                                    path[l] == '/' &&
+                                    is_mountpoint(path)))
+                                       /* ok */;
+                               else
+                                       continue;
+                       } else if (strcmp(path, exp->m_export.e_path) != 0)
                                continue;
-                       if (!found)
+                       if (!found) {
                                found = exp;
-                       else {
-                               xlog(L_WARNING, "%s exported to both %s and %s in %s",
-                                    path, exp->m_client->m_hostname, found->m_client->m_hostname,
+                               found_type = i;
+                               continue;
+                       }
+                       /* If one is a CROSSMOUNT, then prefer the longest path */
+                       if (((found->m_export.e_flags & NFSEXP_CROSSMOUNT) ||
+                            (found->m_export.e_flags & NFSEXP_CROSSMOUNT)) &&
+                           strlen(found->m_export.e_path) !=
+                           strlen(found->m_export.e_path)) {
+
+                               if (strlen(exp->m_export.e_path) >
+                                   strlen(found->m_export.e_path)) {
+                                       found = exp;
+                                       found_type = i;
+                               }
+                               continue;
+
+                       } else if (found_type == i && found->m_warned == 0) {
+                               xlog(L_WARNING, "%s exported to both %s and %s, "
+                                    "arbitrarily choosing options from first",
+                                    path, found->m_client->m_hostname, exp->m_client->m_hostname,
                                     dom);
+                               found->m_warned = 1;
                        }
                }
        }
@@ -548,7 +585,7 @@ void cache_open(void)
        int i;
        for (i=0; cachelist[i].cache_name; i++ ) {
                char path[100];
-               if (!manage_gids && cachelist[i].f == auth_unix_gid)
+               if (!manage_gids && cachelist[i].cache_handle == auth_unix_gid)
                        continue;
                sprintf(path, "/proc/net/rpc/%s/channel", cachelist[i].cache_name);
                cachelist[i].f = fopen(path, "r+");
@@ -586,7 +623,7 @@ int cache_process_req(fd_set *readfds)
  * % echo $domain $path $[now+30*60] $options $anonuid $anongid $fsid > /proc/net/rpc/nfsd.export/channel
  */
 
-int cache_export_ent(char *domain, struct exportent *exp)
+int cache_export_ent(char *domain, struct exportent *exp, char *path)
 {
        int err;
        FILE *f = fopen("/proc/net/rpc/nfsd.export/channel", "w");
@@ -594,22 +631,56 @@ int cache_export_ent(char *domain, struct exportent *exp)
                return -1;
 
        err = dump_to_cache(f, domain, exp->e_path, exp);
-       fclose(f);
        mountlist_add(domain, exp->e_path);
+
+       while ((exp->e_flags & NFSEXP_CROSSMOUNT) && path) {
+               /* really an 'if', but we can break out of
+                * a 'while' more easily */
+               /* Look along 'path' for other filesystems
+                * and export them with the same options
+                */
+               struct stat stb;
+               int l = strlen(exp->e_path);
+               int dev;
+
+               if (strlen(path) <= l || path[l] != '/' ||
+                   strncmp(exp->e_path, path, l) != 0)
+                       break;
+               if (stat(exp->e_path, &stb) != 0)
+                       break;
+               dev = stb.st_dev;
+               while(path[l] == '/') {
+                       char c;
+                       int err;
+
+                       l++;
+                       while (path[l] != '/' && path[l])
+                               l++;
+                       c = path[l];
+                       path[l] = 0;
+                       err = lstat(path, &stb);
+                       path[l] = c;
+                       if (err < 0)
+                               break;
+                       if (stb.st_dev == dev)
+                               continue;
+                       dev = stb.st_dev;
+                       path[l] = 0;
+                       dump_to_cache(f, domain, path, exp);
+                       path[l] = c;
+               }
+               break;
+       }
+
+       fclose(f);
        return err;
 }
 
-int cache_export(nfs_export *exp)
+int cache_export(nfs_export *exp, char *path)
 {
        int err;
        FILE *f;
 
-       if (exp->m_export.e_maptype != CLE_MAP_IDENT) {
-               xlog(L_ERROR, "%s: unsupported mapping; kernel supports only 'identity' (default)",
-                   exp->m_export.m_path);
-               return -1;
-       }
-
        f = fopen("/proc/net/rpc/auth.unix.ip/channel", "w");
        if (!f)
                return -1;
@@ -622,7 +693,7 @@ int cache_export(nfs_export *exp)
        
        fclose(f);
 
-       err = cache_export_ent(exp->m_client->m_hostname, &exp->m_export)
+       err = cache_export_ent(exp->m_client->m_hostname, &exp->m_export, path)
                || err;
        return err;
 }