]> 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 6cf24ceb4f9f9d3b919cbe1c24cbc22e290945a5..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,7 +408,7 @@ 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);
@@ -466,7 +466,7 @@ static int dump_to_cache(FILE *f, char *domain, char *path, struct exportent *ex
                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);
                        }
@@ -490,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)
@@ -514,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;
                        }
                }
        }
@@ -594,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");
@@ -602,12 +631,52 @@ 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;
@@ -624,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;
 }