]> git.decadent.org.uk Git - nfs-utils.git/blob - utils/mount/nfsumount.c
Imported Debian patch 1.0.8+1.0.9pre1-3
[nfs-utils.git] / utils / mount / nfsumount.c
1 /*
2  * nfsumount.c -- Linux NFS umount
3  * Copyright (C) 2006 Amit Gud <agud@redhat.com>
4  *
5  * - Basic code and wrapper around NFS umount code originally
6  *   in util-linux/mount/nfsmount.c
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2, or (at your option)
11  * any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  */
19
20 #include <stdio.h>
21 #include <errno.h>
22 #include <getopt.h>
23 #include <mntent.h>
24 #include <sys/mount.h>
25 #include <ctype.h>
26 #include <pwd.h>
27
28 #include "xcommon.h"
29 #include "fstab.h"
30 #include "nls.h"
31 #include "conn.h"
32
33 #include "mount_constants.h"
34 #include "mount.h"
35 #include "nfsumount.h"
36
37 #if !defined(MNT_FORCE)
38 /* dare not try to include <linux/mount.h> -- lots of errors */
39 #define MNT_FORCE 1
40 #endif
41
42 #if !defined(MNT_DETACH)
43 #define MNT_DETACH 2
44 #endif
45
46 extern char *progname;
47 extern int nfs_mount_version;
48 extern int nomtab;
49 extern int verbose;
50 int force;
51 int lazy;
52 int remount;
53
54 extern int find_kernel_nfs_mount_version(void);
55 extern int probe_mntport(clnt_addr_t *);
56 extern int nfs_gethostbyname(const char *, struct sockaddr_in *);
57
58 static inline enum clnt_stat
59 nfs3_umount(dirpath *argp, CLIENT *clnt)
60 {
61         static char clnt_res;
62         memset (&clnt_res, 0, sizeof(clnt_res));
63         return clnt_call(clnt, MOUNTPROC_UMNT,
64                          (xdrproc_t) xdr_dirpath, (caddr_t)argp,
65                          (xdrproc_t) xdr_void, (caddr_t) &clnt_res,
66                          TIMEOUT);
67 }
68
69 static inline enum clnt_stat
70 nfs2_umount(dirpath *argp, CLIENT *clnt)
71 {
72         static char clnt_res;
73         memset (&clnt_res, 0, sizeof(clnt_res));
74         return clnt_call(clnt, MOUNTPROC_UMNT,
75                          (xdrproc_t) xdr_dirpath, (caddr_t)argp,
76                          (xdrproc_t) xdr_void, (caddr_t) &clnt_res,
77                          TIMEOUT);
78 }
79
80 int nfs_call_umount(clnt_addr_t *mnt_server, dirpath *argp)
81 {
82         CLIENT *clnt;
83         enum clnt_stat res = 0;
84         int msock;
85
86         clnt = mnt_openclnt(mnt_server, &msock);
87         if (!clnt)
88                 goto out_bad;
89         switch (mnt_server->pmap.pm_vers) {
90         case 3:
91                 res = nfs3_umount(argp, clnt);
92                 break;
93         case 2:
94         case 1:
95                 res = nfs2_umount(argp, clnt);
96                 break;
97         default:
98                 break;
99         }
100         mnt_closeclnt(clnt, msock);
101         if (res == RPC_SUCCESS)
102                 return 1;
103  out_bad:
104         return 0;
105 }
106
107 u_int get_mntproto(const char *);
108 u_int
109 get_mntproto(const char *dirname)
110 {
111         FILE *mtab;
112         struct mntent mntbuf;
113         char tmpbuf[BUFSIZ];
114         u_int proto = IPPROTO_TCP; /* assume tcp */
115
116          mtab = setmntent ("/proc/mounts", "r");
117          if (mtab == NULL)
118                 mtab = setmntent (_PATH_MOUNTED, "r");
119         if (mtab == NULL)
120                 return proto;
121
122         while(getmntent_r(mtab, &mntbuf, tmpbuf, sizeof (tmpbuf))) {
123                 if (strcmp(mntbuf.mnt_type, "nfs"))
124                         continue;
125                 if (strcmp(dirname,  mntbuf.mnt_fsname))
126                         continue;
127                 if (hasmntopt(&mntbuf, "udp"))
128                         proto = IPPROTO_UDP;
129                 break;
130         }
131         endmntent (mtab);
132
133         return proto;
134 }
135
136 /* complain about a failed umount */
137 static void complain(int err, const char *dev) {
138   switch (err) {
139     case ENXIO:
140       nfs_error (_("umount: %s: invalid block device"), dev); break;
141     case EINVAL:
142       nfs_error (_("umount: %s: not mounted"), dev); break;
143     case EIO:
144       nfs_error (_("umount: %s: can't write superblock"), dev); break;
145     case EBUSY:
146      /* Let us hope fstab has a line "proc /proc ..."
147         and not "none /proc ..."*/
148       nfs_error (_("umount: %s: device is busy"), dev); break;
149     case ENOENT:
150       nfs_error (_("umount: %s: not found"), dev); break;
151     case EPERM:
152       nfs_error (_("umount: %s: must be superuser to umount"), dev); break;
153     case EACCES:
154       nfs_error (_("umount: %s: block devices not permitted on fs"), dev); break;
155     default:
156       nfs_error (_("umount: %s: %s"), dev, strerror (err)); break;
157   }
158 }
159
160 /*
161  * Look for an option in a comma-separated list
162  */
163 static int
164 contains(const char *list, const char *s) {
165         int n = strlen(s);
166
167         while (*list) {
168                 if (strncmp(list, s, n) == 0 &&
169                   (list[n] == 0 || list[n] == ','))
170                         return 1;
171                 while (*list && *list++ != ',') ;
172         }
173         return 0;
174 }
175
176 /*
177  * If list contains "user=peter" and we ask for "user=", return "peter"
178  */
179 static char *
180 get_value(const char *list, const char *s) {
181         const char *t;
182         int n = strlen(s);
183
184         while (*list) {
185                 if (strncmp(list, s, n) == 0) {
186                         s = t = list+n;
187                         while (*s && *s != ',')
188                                 s++;
189                         return xstrndup(t, s-t);
190                 }
191                 while (*list && *list++ != ',') ;
192         }
193         return 0;
194 }
195
196 int add_mtab2(const char *spec, const char *node, const char *type,
197                 const char *opts, struct mntentchn *mc)
198 {
199         int umnt_err, umnt_err2, res;
200
201         umnt_err = umnt_err2 = 0;
202         if (lazy) {
203                 res = umount2 (node, MNT_DETACH);
204                 if (res < 0)
205                         umnt_err = errno;
206                 goto writemtab;
207         }
208
209         if (force) {
210                 res = umount2 (node, MNT_FORCE);
211                 if (res == -1) {
212                         int errsv = errno;
213                         perror("umount2");
214                         errno = errsv;
215                         if (errno == ENOSYS) {
216                                 if (verbose)
217                                         printf(_("no umount2, trying umount...\n"));
218                                 res = umount (node);
219                         }
220                 }
221         } else
222                 res = umount (node);
223
224         if (res < 0) {
225                 umnt_err = errno;
226                 /* A device might have been mounted on a node that has since
227                    been deleted or renamed, so if node fails, also try spec. */
228                 /* Note that this is incorrect in case spec was mounted
229                    several times. */
230                 /* if (umnt_err == ENOENT || umnt_err == EINVAL) */
231                 if (umnt_err != EBUSY && strcmp(node, spec)) {
232                         if (verbose)
233                                 printf (_("could not umount %s - trying %s instead\n"),
234                                         node, spec);
235                         res = umount (spec);
236                         if (res < 0)
237                                 umnt_err2 = errno;
238                        /* Do not complain about remote NFS mount points */
239                         if (errno == ENOENT && index(spec, ':'))
240                                 umnt_err2 = 0;
241                 }
242         }
243
244         if (res < 0 && remount && (umnt_err == EBUSY || umnt_err2 == EBUSY)) {
245                 /* Umount failed - let us try a remount */
246                 res = mount(spec, node, NULL,
247                             MS_MGC_VAL | MS_REMOUNT | MS_RDONLY, NULL);
248                 if (res == 0) {
249                         nfs_mntent_t remnt;
250                         fprintf(stderr,
251                                 _("umount: %s busy - remounted read-only\n"),
252                                 spec);
253                         remnt.mnt_type = remnt.mnt_fsname = NULL;
254                         remnt.mnt_dir = xstrdup(node);
255                         remnt.mnt_opts = xstrdup("ro");
256                         if (!nomtab)
257                                 update_mtab(node, &remnt);
258                         return 0;
259                 } else if (errno != EBUSY) {    /* hmm ... */
260                         perror("remount");
261                         fprintf(stderr,
262                                 _("umount: could not remount %s read-only\n"),
263                                 spec);
264                 }
265         }
266
267         if (res >= 0) {
268                 /* Umount succeeded */
269                 if (verbose)
270                         printf (_("%s umounted\n"), spec);
271         }
272
273  writemtab:
274         if (!nomtab &&
275             (umnt_err == 0 || umnt_err == EINVAL || umnt_err == ENOENT)) {
276                update_mtab (node, NULL);
277         }
278
279         if (res >= 0)
280                 return 0;
281
282         if (umnt_err2)
283                 complain(umnt_err2, spec);
284         if (umnt_err && umnt_err != umnt_err2)
285                 complain(umnt_err, node);
286         return 1;
287 }
288
289 /*
290  * Returns 1 if everything went well, else 0.
291  */
292 int _nfsumount(const char *spec, const char *opts)
293 {
294         char *hostname;
295         char *dirname;
296         clnt_addr_t mnt_server = { &hostname, };
297         struct pmap *pmap = &mnt_server.pmap;
298         char *p;
299
300         nfs_mount_version = find_kernel_nfs_mount_version();
301         if (spec == NULL || (p = strchr(spec,':')) == NULL)
302                 goto out_bad;
303         hostname = xstrndup(spec, p-spec);
304         dirname = xstrdup(p+1);
305 #ifdef NFS_MOUNT_DEBUG
306         printf(_("host: %s, directory: %s\n"), hostname, dirname);
307 #endif
308
309         if (opts && (p = strstr(opts, "addr="))) {
310                 char *q;
311
312                 free(hostname);
313                 p += 5;
314                 q = p;
315                 while (*q && *q != ',') q++;
316                 hostname = xstrndup(p,q-p);
317         }
318
319         if (opts && (p = strstr(opts, "mounthost="))) {
320                 char *q;
321
322                 free(hostname);
323                 p += 10;
324                 q = p;
325                 while (*q && *q != ',') q++;
326                 hostname = xstrndup(p,q-p);
327         }
328
329         pmap->pm_prog = MOUNTPROG;
330         pmap->pm_vers = MOUNTVERS;
331         pmap->pm_prot = get_mntproto(spec);
332         if (opts && (p = strstr(opts, "mountprog=")) && isdigit(*(p+10)))
333                 pmap->pm_prog = atoi(p+10);
334         if (opts && (p = strstr(opts, "mountport=")) && isdigit(*(p+10)))
335                 pmap->pm_port = atoi(p+10);
336         if (opts && (p = strstr(opts, "nfsvers=")) && isdigit(*(p+8)))
337                 pmap->pm_vers = nfsvers_to_mnt(atoi(p+8));
338         if (opts && (p = strstr(opts, "mountvers=")) && isdigit(*(p+10)))
339                 pmap->pm_vers = atoi(p+10);
340
341         if (!nfs_gethostbyname(hostname, &mnt_server.saddr))
342                 goto out_bad;
343         if (!probe_mntport(&mnt_server))
344                 goto out_bad;
345         return nfs_call_umount(&mnt_server, &dirname);
346  out_bad:
347         fprintf(stderr, "%s: %s: not found or not mounted\n", progname, spec);
348         return 0;
349 }
350
351 static struct option umount_longopts[] =
352 {
353   { "force", 0, 0, 'f' },
354   { "help", 0, 0, 'h' },
355   { "no-mtab", 0, 0, 'n' },
356   { "verbose", 0, 0, 'v' },
357   { "read-only", 0, 0, 'r' },
358   { NULL, 0, 0, 0 }
359 };
360
361 void umount_usage()
362 {
363         printf("usage: %s dir [-fvnrlh]\n", progname);
364         printf("options:\n\t-f\t\tforce unmount\n");
365         printf("\t-v\t\tverbose\n");
366         printf("\t-n\t\tDo not update /etc/mtab\n");
367         printf("\t-r\t\tremount\n");
368         printf("\t-l\t\tlazy unmount\n");
369         printf("\t-h\t\tprint this help\n\n");
370 }
371
372 int nfsumount(int argc, char *argv[])
373 {
374         int c, ret;
375         char *spec;
376         struct mntentchn *mc;
377
378         spec = argv[1];
379
380         argv += 1;
381         argc -= 1;
382
383         while ((c = getopt_long (argc, argv, "fvnrlh",
384                                 umount_longopts, NULL)) != -1) {
385
386                 switch (c) {
387                 case 'f':
388                         ++force;
389                         break;
390                 case 'v':
391                         ++verbose;
392                         break;
393                 case 'n':
394                         ++nomtab;
395                         break;
396                 case 'r':
397                         ++remount;
398                         break;
399                 case 'l':
400                         ++lazy;
401                         break;
402                 case 'h':
403                 default:
404                         umount_usage();
405                         return 0;
406                 }
407         }
408
409         mc = getmntdirbackward(spec, NULL);
410         if (!mc)
411                 mc = getmntdevbackward(spec, NULL);
412         if (!mc && verbose)
413                 printf(_("Could not find %s in mtab\n"), spec);
414
415         if(mc) {
416                 if(contains(mc->m.mnt_opts, "user") && getuid() != 0) {
417                         struct passwd *pw = getpwuid(getuid());
418                         if(!pw || strcmp(pw->pw_name, get_value(mc->m.mnt_opts, "user="))) {
419                                 fprintf(stderr, "%s: permission denied to unmount %s\n",
420                                                 progname, spec);
421                                 exit(1);
422                         }
423                 } else {
424                         if(!contains(mc->m.mnt_opts, "users") && getuid() != 0) {
425                                 fprintf(stderr, "%s: only root can unmount %s from %s\n",
426                                                 progname, mc->m.mnt_fsname, mc->m.mnt_dir);
427                                 exit(1);
428                         }
429                 }
430
431                 ret = _nfsumount(mc->m.mnt_fsname, mc->m.mnt_opts);
432                 if(ret)
433                         ret = add_mtab2(mc->m.mnt_fsname, mc->m.mnt_dir,
434                                 mc->m.mnt_type, mc->m.mnt_opts, mc);
435         }
436         else {
437                 ret = _nfsumount(spec, NULL);
438                 if(ret)
439                         ret = add_mtab2(spec, spec, spec, spec, NULL);
440         }
441
442         return(ret);
443 }
444