]> git.decadent.org.uk Git - nfs-utils.git/blob - utils/mountd/mountd.c
Work towards support new cache in 2.5
[nfs-utils.git] / utils / mountd / mountd.c
1 /*
2  * utils/mountd/mountd.c
3  *
4  * Authenticate mount requests and retrieve file handle.
5  *
6  * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
7  */
8
9 #include "config.h"
10
11 #include <signal.h>
12 #include <sys/stat.h>
13 #include <netinet/in.h>
14 #include <arpa/inet.h>
15 #include <unistd.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <getopt.h>
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <sys/resource.h>
22 #include "xmalloc.h"
23 #include "misc.h"
24 #include "mountd.h"
25 #include "rpcmisc.h"
26
27 extern void     cache_open(void);
28 extern struct nfs_fh_len *cache_get_filehandle(nfs_export *exp, int len);
29 extern void cache_export(nfs_export *exp);
30
31 extern void my_svc_run(void);
32
33
34 static void             usage(const char *, int exitcode);
35 static exports          get_exportlist(void);
36 static struct nfs_fh_len *get_rootfh(struct svc_req *, dirpath *, int *, int v3);
37
38 int new_cache;
39
40 static struct option longopts[] =
41 {
42         { "foreground", 0, 0, 'F' },
43         { "descriptors", 1, 0, 'o' },
44         { "debug", 1, 0, 'd' },
45         { "help", 0, 0, 'h' },
46         { "exports-file", 1, 0, 'f' },
47         { "nfs-version", 1, 0, 'V' },
48         { "no-nfs-version", 1, 0, 'N' },
49         { "version", 0, 0, 'v' },
50         { "port", 1, 0, 'p' },
51         { "no-tcp", 0, 0, 'n' },
52         { NULL, 0, 0, 0 }
53 };
54
55 static int nfs_version = -1;
56
57 /*
58  * Signal handler.
59  */
60 static void 
61 killer (int sig)
62 {
63   if (nfs_version & 0x1)
64     pmap_unset (MOUNTPROG, MOUNTVERS);
65   if (nfs_version & (0x1 << 1))
66     pmap_unset (MOUNTPROG, MOUNTVERS_POSIX);
67   if (nfs_version & (0x1 << 2))
68     pmap_unset (MOUNTPROG, MOUNTVERS_NFSV3);
69   xlog (L_FATAL, "Caught signal %d, un-registering and exiting.", sig);
70 }
71
72 bool_t
73 mount_null_1_svc(struct svc_req *rqstp, void *argp, void *resp)
74 {
75         return 1;
76 }
77
78 bool_t
79 mount_mnt_1_svc(struct svc_req *rqstp, dirpath *path, fhstatus *res)
80 {
81         struct nfs_fh_len *fh;
82
83         xlog(D_CALL, "MNT1(%s) called", *path);
84         if ((fh = get_rootfh(rqstp, path, &res->fhs_status, 0)) != NULL)
85                 memcpy(&res->fhstatus_u.fhs_fhandle, fh->fh_handle, 32);
86         return 1;
87 }
88
89 bool_t
90 mount_dump_1_svc(struct svc_req *rqstp, void *argp, mountlist *res)
91 {
92         struct sockaddr_in *addr =
93                 (struct sockaddr_in *) svc_getcaller(rqstp->rq_xprt);
94         xlog(L_NOTICE, "dump request from %s",
95                 inet_ntoa(addr->sin_addr));
96
97         *res = mountlist_list();
98         return 1;
99 }
100
101 bool_t
102 mount_umnt_1_svc(struct svc_req *rqstp, dirpath *argp, void *resp)
103 {
104         struct sockaddr_in *sin
105                 = (struct sockaddr_in *) svc_getcaller(rqstp->rq_xprt);
106         nfs_export      *exp;
107         char            *p = *argp;
108         char            rpath[MAXPATHLEN+1];
109
110         if (*p == '\0')
111                 p = "/";
112
113         if (realpath(p, rpath) != NULL) {
114                 rpath[sizeof (rpath) - 1] = '\0';
115                 p = rpath;
116         }
117
118         if (!(exp = auth_authenticate("unmount", sin, p))) {
119                 return 1;
120         }
121         mountlist_del(exp, p);
122         export_reset (exp);
123         return 1;
124 }
125
126 bool_t
127 mount_umntall_1_svc(struct svc_req *rqstp, void *argp, void *resp)
128 {
129         /* Reload /etc/xtab if necessary */
130         auth_reload();
131
132         mountlist_del_all((struct sockaddr_in *) svc_getcaller(rqstp->rq_xprt));
133         return 1;
134 }
135
136 bool_t
137 mount_export_1_svc(struct svc_req *rqstp, void *argp, exports *resp)
138 {
139         struct sockaddr_in *addr =
140                 (struct sockaddr_in *) svc_getcaller(rqstp->rq_xprt);
141         xlog(L_NOTICE, "export request from %s",
142                 inet_ntoa(addr->sin_addr));
143         *resp = get_exportlist();
144         return 1;
145 }
146
147 bool_t
148 mount_exportall_1_svc(struct svc_req *rqstp, void *argp, exports *resp)
149 {
150         struct sockaddr_in *addr =
151                 (struct sockaddr_in *) svc_getcaller(rqstp->rq_xprt);
152         xlog(L_NOTICE, "exportall request from %s",
153                 inet_ntoa(addr->sin_addr));
154         *resp = get_exportlist();
155         return 1;
156 }
157
158 /*
159  * MNTv2 pathconf procedure
160  *
161  * The protocol doesn't include a status field, so Sun apparently considers
162  * it good practice to let anyone snoop on your system, even if it's
163  * pretty harmless data such as pathconf. We don't.
164  *
165  * Besides, many of the pathconf values don't make much sense on NFS volumes.
166  * FIFOs and tty device files represent devices on the *client*, so there's
167  * no point in getting the server's buffer sizes etc.
168  */
169 bool_t
170 mount_pathconf_2_svc(struct svc_req *rqstp, dirpath *path, ppathcnf *res)
171 {
172         struct sockaddr_in *sin
173                 = (struct sockaddr_in *) svc_getcaller(rqstp->rq_xprt);
174         struct stat     stb;
175         nfs_export      *exp;
176         char            rpath[MAXPATHLEN+1];
177         char            *p = *path;
178
179         memset(res, 0, sizeof(*res));
180
181         if (*p == '\0')
182                 p = "/";
183
184         /* Reload /etc/xtab if necessary */
185         auth_reload();
186
187         /* Resolve symlinks */
188         if (realpath(p, rpath) != NULL) {
189                 rpath[sizeof (rpath) - 1] = '\0';
190                 p = rpath;
191         }
192
193         /* Now authenticate the intruder... */
194         if (!(exp = auth_authenticate("pathconf", sin, p))) {
195                 return 1;
196         } else if (stat(p, &stb) < 0) {
197                 xlog(L_WARNING, "can't stat exported dir %s: %s",
198                                 p, strerror(errno));
199                 export_reset (exp);
200                 return 1;
201         }
202
203         res->pc_link_max  = pathconf(p, _PC_LINK_MAX);
204         res->pc_max_canon = pathconf(p, _PC_MAX_CANON);
205         res->pc_max_input = pathconf(p, _PC_MAX_INPUT);
206         res->pc_name_max  = pathconf(p, _PC_NAME_MAX);
207         res->pc_path_max  = pathconf(p, _PC_PATH_MAX);
208         res->pc_pipe_buf  = pathconf(p, _PC_PIPE_BUF);
209         res->pc_vdisable  = pathconf(p, _PC_VDISABLE);
210
211         /* Can't figure out what to do with pc_mask */
212         res->pc_mask[0]   = 0;
213         res->pc_mask[1]   = 0;
214
215         export_reset (exp);
216
217         return 1;
218 }
219
220 /*
221  * NFSv3 MOUNT procedure
222  */
223 bool_t
224 mount_mnt_3_svc(struct svc_req *rqstp, dirpath *path, mountres3 *res)
225 {
226         static int      flavors[] = { AUTH_NULL, AUTH_UNIX };
227         struct nfs_fh_len *fh;
228
229         xlog(D_CALL, "MNT3(%s) called", *path);
230         if ((fh = get_rootfh(rqstp, path, (int *) &res->fhs_status, 1)) != NULL) {
231                 struct mountres3_ok     *ok = &res->mountres3_u.mountinfo;
232
233                 ok->fhandle.fhandle3_len = fh->fh_size;
234                 ok->fhandle.fhandle3_val = fh->fh_handle;
235                 ok->auth_flavors.auth_flavors_len = 2;
236                 ok->auth_flavors.auth_flavors_val = flavors;
237         }
238         return 1;
239 }
240
241 static struct nfs_fh_len *
242 get_rootfh(struct svc_req *rqstp, dirpath *path, int *error, int v3)
243 {
244         struct sockaddr_in *sin =
245                 (struct sockaddr_in *) svc_getcaller(rqstp->rq_xprt);
246         struct stat     stb;
247         nfs_export      *exp;
248         char            rpath[MAXPATHLEN+1];
249         char            *p = *path;
250
251         if (*p == '\0')
252                 p = "/";
253
254         /* Reload /var/lib/nfs/etab if necessary */
255         auth_reload();
256
257         /* Resolve symlinks */
258         if (realpath(p, rpath) != NULL) {
259                 rpath[sizeof (rpath) - 1] = '\0';
260                 p = rpath;
261         }
262
263         /* Now authenticate the intruder... */
264         if (!(exp = auth_authenticate("mount", sin, p))) {
265                 *error = NFSERR_ACCES;
266         } else if (stat(p, &stb) < 0) {
267                 xlog(L_WARNING, "can't stat exported dir %s: %s",
268                                 p, strerror(errno));
269                 if (errno == ENOENT)
270                         *error = NFSERR_NOENT;
271                 else
272                         *error = NFSERR_ACCES;
273         } else if (!S_ISDIR(stb.st_mode) && !S_ISREG(stb.st_mode)) {
274                 xlog(L_WARNING, "%s is not a directory or regular file", p);
275                 *error = NFSERR_NOTDIR;
276         } else if (new_cache) {
277                 /* This will be a static private nfs_export with just one
278                  * address.  We feed it to kernel then extract the filehandle,
279                  * 
280                  */
281                 struct nfs_fh_len  *fh;
282
283                 cache_export(exp);
284                 fh = cache_get_filehandle(exp, v3?64:32);
285                 if (fh == NULL) 
286                         *error = NFSERR_ACCES;
287                 else
288                         *error = NFS_OK;
289                 return fh;
290         } else {
291                 struct nfs_fh_len  *fh;
292
293                 if (exp->m_exported<1)
294                         export_export(exp);
295                 if (!exp->m_xtabent)
296                         xtab_append(exp);
297
298                 if (v3)
299                         fh = getfh_size ((struct sockaddr *) sin, p, 64);
300                 if (!v3 || (fh == NULL && errno == EINVAL)) {
301                         /* We first try the new nfs syscall. */
302                         fh = getfh ((struct sockaddr *) sin, p);
303                         if (fh == NULL && errno == EINVAL)
304                                 /* Let's try the old one. */
305                                 fh = getfh_old ((struct sockaddr *) sin,
306                                                 stb.st_dev, stb.st_ino);
307                 }
308                 if (fh != NULL) {
309                         mountlist_add(exp, p);
310                         *error = NFS_OK;
311                         export_reset (exp);
312                         return fh;
313                 }
314                 xlog(L_WARNING, "getfh failed: %s", strerror(errno));
315                 *error = NFSERR_ACCES;
316         }
317         export_reset (exp);
318         return NULL;
319 }
320
321 static exports
322 get_exportlist(void)
323 {
324         static exports          elist = NULL;
325         struct exportnode       *e, *ne;
326         struct groupnode        *g, *ng, *c, **cp;
327         nfs_export              *exp;
328         int                     i;
329
330         if (!auth_reload() && elist)
331                 return elist;
332
333         for (e = elist; e != NULL; e = ne) {
334                 ne = e->ex_next;
335                 for (g = e->ex_groups; g != NULL; g = ng) {
336                         ng = g->gr_next;
337                         xfree(g->gr_name);
338                         xfree(g);
339                 }
340                 xfree(e->ex_dir);
341                 xfree(e);
342         }
343         elist = NULL;
344
345         for (i = 0; i < MCL_MAXTYPES; i++) {
346                 for (exp = exportlist[i]; exp; exp = exp->m_next) {
347                         for (e = elist; e != NULL; e = e->ex_next) {
348                                 if (!strcmp(exp->m_export.m_path, e->ex_dir))
349                                         break;
350                         }
351                         if (!e) {
352                                 e = (struct exportnode *) xmalloc(sizeof(*e));
353                                 e->ex_next = elist;
354                                 e->ex_groups = NULL;
355                                 e->ex_dir = xstrdup(exp->m_export.m_path);
356                                 elist = e;
357                         }
358
359                         /* We need to check if we should remove
360                            previous ones. */
361                         if (i == MCL_ANONYMOUS && e->ex_groups) {
362                                 for (g = e->ex_groups; g; g = ng) {
363                                         ng = g->gr_next;
364                                         xfree(g->gr_name);
365                                         xfree(g);
366                                 }
367                                 e->ex_groups = NULL;
368                                 continue;
369                         }
370
371                         if (i != MCL_FQDN && e->ex_groups) {
372                           struct hostent        *hp;
373
374                           cp = &e->ex_groups;
375                           while ((c = *cp) != NULL) {
376                             if (client_gettype (c->gr_name) == MCL_FQDN
377                                 && (hp = gethostbyname(c->gr_name))) {
378                               hp = hostent_dup (hp);
379                               if (client_check(exp->m_client, hp)) {
380                                 *cp = c->gr_next;
381                                 xfree(c->gr_name);
382                                 xfree(c);
383                                 xfree (hp);
384                                 if ((c = *cp) == NULL)
385                                   break;
386                               }
387                               else
388                                 xfree (hp);
389                             }
390                             cp = &(c->gr_next);
391                           }
392                         }
393
394                         if (exp->m_export.e_hostname [0] != '\0') {
395                                 for (g = e->ex_groups; g; g = g->gr_next)
396                                         if (strcmp (exp->m_export.e_hostname,
397                                                     g->gr_name) == 0)
398                                                 break;
399                                 if (g)
400                                         continue;
401                                 g = (struct groupnode *) xmalloc(sizeof(*g));
402                                 g->gr_name = xstrdup(exp->m_export.e_hostname);
403                                 g->gr_next = e->ex_groups;
404                                 e->ex_groups = g;
405                         }
406                 }
407         }
408
409         return elist;
410 }
411
412 int
413 main(int argc, char **argv)
414 {
415         char    *export_file = _PATH_EXPORTS;
416         int     foreground = 0;
417         int     port = 0;
418         int     descriptors = 256;
419         int     c;
420         struct sigaction sa;
421         struct rlimit rlim;
422
423         /* Parse the command line options and arguments. */
424         opterr = 0;
425         while ((c = getopt_long(argc, argv, "on:Fd:f:p:P:hN:V:v", longopts, NULL)) != EOF)
426                 switch (c) {
427                 case 'o':
428                         descriptors = atoi(optarg);
429                         if (descriptors <= 0) {
430                                 fprintf(stderr, "%s: bad descriptors: %s\n",
431                                         argv [0], optarg);
432                                 usage(argv [0], 1);
433                         }
434                         break;
435                 case 'F':
436                         foreground = 1;
437                         break;
438                 case 'd':
439                         xlog_sconfig(optarg, 1);
440                         break;
441                 case 'f':
442                         export_file = optarg;
443                         break;
444                 case 'h':
445                         usage(argv [0], 0);
446                         break;
447                 case 'P':       /* XXX for nfs-server compatibility */
448                 case 'p':
449                         port = atoi(optarg);
450                         if (port <= 0 || port > 65535) {
451                                 fprintf(stderr, "%s: bad port number: %s\n",
452                                         argv [0], optarg);
453                                 usage(argv [0], 1);
454                         }
455                         break;
456                 case 'N':
457                         nfs_version &= ~(1 << (atoi (optarg) - 1));
458                         break;
459                 case 'n':
460                         _rpcfdtype = SOCK_DGRAM;
461                         break;
462                 case 'V':
463                         nfs_version |= 1 << (atoi (optarg) - 1);
464                         break;
465                 case 'v':
466                         printf("kmountd %s\n", VERSION);
467                         exit(0);
468                 case 0:
469                         break;
470                 case '?':
471                 default:
472                         usage(argv [0], 1);
473                 }
474
475         /* No more arguments allowed. */
476         if (optind != argc || !(nfs_version & 0x7))
477                 usage(argv [0], 1);
478
479         if (chdir(NFS_STATEDIR)) {
480                 fprintf(stderr, "%s: chdir(%s) failed: %s\n",
481                         argv [0], NFS_STATEDIR, strerror(errno));
482                 exit(1);
483         }
484
485         if (getrlimit (RLIMIT_NOFILE, &rlim) != 0) {
486                 fprintf(stderr, "%s: getrlimit (RLIMIT_NOFILE) failed: %s\n",
487                         argv [0], strerror(errno));
488                 exit(1);
489         }
490
491         rlim.rlim_cur = descriptors;
492         if (setrlimit (RLIMIT_NOFILE, &rlim) != 0) {
493                 fprintf(stderr, "%s: setrlimit (RLIMIT_NOFILE) failed: %s\n",
494                         argv [0], strerror(errno));
495                 exit(1);
496         }
497
498         /* Initialize logging. */
499 /*      xlog_open("mountd"); */
500
501         sa.sa_handler = SIG_IGN;
502         sa.sa_flags = 0;
503         sigemptyset(&sa.sa_mask);
504         sigaction(SIGHUP, &sa, NULL);
505         sigaction(SIGINT, &sa, NULL);
506         sigaction(SIGTERM, &sa, NULL);
507         /* WARNING: the following works on Linux and SysV, but not BSD! */
508         sigaction(SIGCHLD, &sa, NULL);
509
510         /* Daemons should close all extra filehandles ... *before* RPC init. */
511         if (!foreground) {
512                 int fd = sysconf (_SC_OPEN_MAX);
513                 while (--fd > 2)
514                         (void) close(fd);
515         }
516
517         new_cache = check_new_cache();
518         if (new_cache)
519                 cache_open();
520
521         if (nfs_version & 0x1)
522                 rpc_init("mountd", MOUNTPROG, MOUNTVERS,
523                          mount_dispatch, port);
524         if (nfs_version & (0x1 << 1))
525                 rpc_init("mountd", MOUNTPROG, MOUNTVERS_POSIX,
526                          mount_dispatch, port);
527         if (nfs_version & (0x1 << 2))
528                 rpc_init("mountd", MOUNTPROG, MOUNTVERS_NFSV3,
529                          mount_dispatch, port);
530
531         sa.sa_handler = killer;
532         sigaction(SIGHUP, &sa, NULL);
533         sigaction(SIGINT, &sa, NULL);
534         sigaction(SIGTERM, &sa, NULL);
535
536         auth_init(export_file);
537
538         if (!foreground) {
539                 /* We first fork off a child. */
540                 if ((c = fork()) > 0)
541                         exit(0);
542                 if (c < 0) {
543                         xlog(L_FATAL, "mountd: cannot fork: %s\n",
544                                                 strerror(errno));
545                 }
546                 /* Now we remove ourselves from the foreground.
547                    Redirect stdin/stdout/stderr first. */
548                 {
549                         int fd = open("/dev/null", O_RDWR);
550                         (void) dup2(fd, 0);
551                         (void) dup2(fd, 1);
552                         (void) dup2(fd, 2);
553                         if (fd > 2) (void) close(fd);
554                 }
555                 setsid();
556                 xlog_background();
557         }
558
559         my_svc_run();
560
561         xlog(L_ERROR, "Ack! Gack! svc_run returned!\n");
562         exit(1);
563 }
564
565 static void
566 usage(const char *prog, int n)
567 {
568         fprintf(stderr,
569 "Usage: %s [-F|--foreground] [-h|--help] [-v|--version] [-d kind|--debug kind]\n"
570 "       [-o num|--descriptors num] [-f exports-file|--exports-file=file]\n"
571 "       [-p|--port port] [-V version|--nfs-version version]\n"
572 "       [-N version|--no-nfs-version version] [-n|--no-tcp]\n", prog);
573         exit(n);
574 }