]> git.decadent.org.uk Git - nfs-utils.git/blob - utils/mount/mount_libmount.c
6dd64846470efc9bb3acc55dc0f4a0b598243ae1
[nfs-utils.git] / utils / mount / mount_libmount.c
1 /*
2  * mount_libmount.c -- Linux NFS [u]mount based on libmount
3  *
4  * Copyright (C) 2011 Karel Zak <kzak@redhat.com>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2, or (at your option)
9  * any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public
17  * License along with this program; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 021110-1307, USA.
20  *
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <unistd.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <errno.h>
31 #include <getopt.h>
32
33 #include <libmount/libmount.h>
34
35 #include "nls.h"
36 #include "mount_config.h"
37
38 #include "nfs_mount.h"
39 #include "nfs4_mount.h"
40 #include "stropts.h"
41 #include "version.h"
42 #include "xcommon.h"
43
44 #include "error.h"
45 #include "utils.h"
46
47 char *progname;
48 int nfs_mount_data_version;
49 int verbose;
50 int sloppy;
51 int string;
52 int nomtab;
53
54 #define FOREGROUND      (0)
55 #define BACKGROUND      (1)
56
57 /*
58  * Store mount options to mtab (or /dev/.mount/utab), called from mount.nfs.
59  *
60  * Note that on systems without /etc/mtab the fs-specific options are not
61  * managed by libmount at all. We have to use "mount attributes" that are
62  * private for mount.<type> helpers.
63  */
64 static void store_mount_options(struct libmnt_fs *fs, const char *opts)
65 {
66         mnt_fs_set_fs_options(fs, opts);        /* for mtab */
67         mnt_fs_set_attributes(fs, opts);        /* for non-mtab systems */
68 }
69
70 /*
71  * Retrieve mount options from mtab (or /dev/.mount/utab) called from umount.nfs.
72  *
73  * The result can passed to free().
74  */
75 char *retrieve_mount_options(struct libmnt_fs *fs)
76 {
77         const char *opts;
78
79         if (!fs)
80                 return NULL;
81
82         opts = mnt_fs_get_attributes(fs);       /* /dev/.mount/utab */
83         if (opts)
84                 return strdup(opts);
85
86         return mnt_fs_strdup_options(fs);       /* /etc/mtab */
87 }
88
89 static int try_mount(struct libmnt_context *cxt, int bg)
90 {
91         struct libmnt_fs *fs;
92         const char *p;
93         char *src = NULL, *tgt = NULL, *type = NULL, *opts = NULL;
94         unsigned long flags = 0;
95         int fake, ret = 0;
96
97         fs = mnt_context_get_fs(cxt);
98
99         /* libmount returns read-only pointers (const char)
100          * so, reallocate for nfsmount() functions.
101          */
102         if ((p = mnt_fs_get_source(fs)))        /* spec */
103                 src = strdup(p);
104         if ((p = mnt_fs_get_target(fs)))        /* mountpoint */
105                 tgt = strdup(p);
106         if ((p = mnt_fs_get_fstype(fs)))        /* FS type */
107                 type = strdup(p);
108         if ((p = mnt_fs_get_fs_options(fs)))    /* mount options */
109                 opts = strdup(p);
110
111         mnt_context_get_mflags(cxt, &flags);    /* mount(2) flags */
112         fake = mnt_context_is_fake(cxt);
113
114         if (string)
115                 ret = nfsmount_string(src, tgt, type, flags, &opts, fake, bg);
116
117         else if (strcmp(type, "nfs4") == 0)
118                 ret = nfs4mount(src, tgt, flags, &opts, fake, bg);
119         else
120                 ret = nfsmount(src, tgt, flags, &opts, fake, bg);
121
122         /* Store mount options if not called with mount --no-mtab */
123         if (!ret && !mnt_context_is_nomtab(cxt))
124                 store_mount_options(fs, opts);
125
126         free(src);
127         free(tgt);
128         free(type);
129         free(opts);
130
131         return ret;
132 }
133
134 /* returns: error = -1, success = 0 , unknown = 1 */
135 static int is_vers4(struct libmnt_context *cxt)
136 {
137         struct libmnt_fs *fs = mnt_context_get_fs(cxt);
138         struct libmnt_table *tb = NULL;
139         const char *src = mnt_context_get_source(cxt),
140                    *tgt = mnt_context_get_target(cxt);
141         int rc = 1;
142
143         if (!src || !tgt)
144                 return -1;
145
146         if (!mnt_fs_is_kernel(fs)) {
147                 struct libmnt_table *tb = mnt_new_table_from_file("/proc/mounts");
148
149                 if (!tb)
150                         return -1;
151                 fs = mnt_table_find_pair(tb, src, tgt, MNT_ITER_BACKWARD);
152         }
153
154         if (fs) {
155                 const char *type = mnt_fs_get_fstype(fs);
156                 if (type && strcmp(type, "nfs4") == 0)
157                         rc = 0;
158         }
159         mnt_free_table(tb);
160         return rc;
161 }
162
163 static int umount_main(struct libmnt_context *cxt, int argc, char **argv)
164 {
165         int rc, c;
166         char *spec = NULL, *opts = NULL;
167
168         static const struct option longopts[] = {
169                 { "force", 0, 0, 'f' },
170                 { "help", 0, 0, 'h' },
171                 { "no-mtab", 0, 0, 'n' },
172                 { "verbose", 0, 0, 'v' },
173                 { "read-only", 0, 0, 'r' },
174                 { "lazy", 0, 0, 'l' },
175                 { "types", 1, 0, 't' },
176                 { NULL, 0, 0, 0 }
177         };
178
179         mnt_context_init_helper(cxt, MNT_ACT_UMOUNT, 0);
180
181         while ((c = getopt_long (argc, argv, "fvnrlh", longopts, NULL)) != -1) {
182
183                 rc = mnt_context_helper_setopt(cxt, c, optarg);
184                 if (rc == 0)            /* valid option */
185                         continue;
186                 if (rc < 0)             /* error (probably ENOMEM) */
187                         goto err;
188                                         /* rc==1 means unknow option */
189                 umount_usage();
190                 return EX_USAGE;
191         }
192
193         if (optind < argc)
194                 spec = argv[optind++];
195
196         if (!spec || (*spec != '/' && strchr(spec,':') == NULL)) {
197                 nfs_error(_("%s: no mount point provided"), progname);
198                 return EX_USAGE;
199         }
200
201         if (mnt_context_set_target(cxt, spec))
202                 goto err;
203         if (mnt_context_set_fstype_pattern(cxt, "nfs,nfs4"))    /* restrict filesystems */
204                 goto err;
205
206         /* read mtab/fstab, evaluate permissions, etc. */
207         rc = mnt_context_prepare_umount(cxt);
208         if (rc) {
209                 nfs_error(_("%s: failed to prepare umount: %s\n"),
210                                         progname, strerror(-rc));
211                 goto err;
212         }
213
214         opts = retrieve_mount_options(mnt_context_get_fs(cxt));
215
216         if (!mnt_context_is_lazy(cxt)) {
217                 if (opts) {
218                         /* we have full FS description (e.g. from mtab or /proc) */
219                         switch (is_vers4(cxt)) {
220                         case 0:
221                                 /* We ignore the error from nfs_umount23.
222                                  * If the actual umount succeeds (in del_mtab),
223                                  * we don't want to signal an error, as that
224                                  * could cause /sbin/mount to retry!
225                                  */
226                                 nfs_umount23(mnt_context_get_source(cxt), opts);
227                                 break;
228                         case 1:                 /* unknown */
229                                 break;
230                         default:                /* error */
231                                 goto err;
232                         }
233                 } else
234                         /* strange, no entry in mtab or /proc not mounted */
235                         nfs_umount23(spec, "tcp,v3");
236         }
237
238         rc = mnt_context_do_umount(cxt);        /* call umount(2) syscall */
239         mnt_context_finalize_mount(cxt);        /* mtab update */
240
241         if (rc && !mnt_context_get_status(cxt)) {
242                 /* mnt_context_do_umount() returns errno if umount(2) failed */
243                 umount_error(rc, spec);
244                 goto err;
245         }
246
247         free(opts);
248         return EX_SUCCESS;
249 err:
250         free(opts);
251         return EX_FAIL;
252 }
253
254 static int mount_main(struct libmnt_context *cxt, int argc, char **argv)
255 {
256         int rc, c;
257         struct libmnt_fs *fs;
258         char *spec = NULL, *mount_point = NULL, *opts = NULL;
259
260         static const struct option longopts[] = {
261           { "fake", 0, 0, 'f' },
262           { "help", 0, 0, 'h' },
263           { "no-mtab", 0, 0, 'n' },
264           { "read-only", 0, 0, 'r' },
265           { "ro", 0, 0, 'r' },
266           { "verbose", 0, 0, 'v' },
267           { "version", 0, 0, 'V' },
268           { "read-write", 0, 0, 'w' },
269           { "rw", 0, 0, 'w' },
270           { "options", 1, 0, 'o' },
271           { "sloppy", 0, 0, 's' },
272           { NULL, 0, 0, 0 }
273         };
274
275         mount_config_init(progname);
276         mnt_context_init_helper(cxt, MNT_ACT_MOUNT, 0);
277
278         while ((c = getopt_long(argc, argv, "fhnrVvwo:s", longopts, NULL)) != -1) {
279
280                 rc = mnt_context_helper_setopt(cxt, c, optarg);
281                 if (rc == 0)            /* valid option */
282                         continue;
283                 if (rc < 0)             /* error (probably ENOMEM) */
284                         goto err;
285                                         /* rc==1 means unknow option */
286                 switch (c) {
287                 case 'V':
288                         printf("%s: ("PACKAGE_STRING")\n", progname);
289                         return EX_SUCCESS;
290                 case 'h':
291                 default:
292                         mount_usage();
293                         return EX_USAGE;
294                 }
295         }
296
297         if (optind < argc)
298                 spec = argv[optind++];
299         if (optind < argc)
300                 mount_point = argv[optind++];
301
302         if (!mount_point) {
303                 nfs_error(_("%s: no mount point provided"), progname);
304                 goto err;
305         }
306         if (!spec) {
307                 nfs_error(_("%s: no mount spec provided"), progname);
308                 goto err;
309         }
310
311         if (geteuid() != 0) {
312                 nfs_error(_("%s: not installed setuid - "
313                             "\"user\" NFS mounts not supported."), progname);
314                 goto err;
315         }
316
317         verbose = mnt_context_is_verbose(cxt);
318         sloppy = mnt_context_is_sloppy(cxt);
319         nomtab = mnt_context_is_nomtab(cxt);
320
321         if (strcmp(progname, "mount.nfs4") == 0)
322                 mnt_context_set_fstype(cxt, "nfs4");
323         else
324                 mnt_context_set_fstype(cxt, "nfs");     /* default */
325
326         rc = mnt_context_set_source(cxt, spec);
327         if (!rc)
328                 mnt_context_set_target(cxt, mount_point);
329         if (rc) {
330                 nfs_error(_("%s: failed to set spec or mountpoint: %s"),
331                                 progname, strerror(errno));
332                 goto err;
333         }
334
335         mount_point = mnt_resolve_path(mount_point,
336                                        mnt_context_get_cache(cxt));
337
338         if (chk_mountpoint(mount_point))
339                 goto err;
340         /*
341          * Concatenate mount options from the configuration file
342          */
343         fs = mnt_context_get_fs(cxt);
344         if (fs) {
345                 opts = mnt_fs_strdup_options(fs);
346
347                 opts = mount_config_opts(spec, mount_point, opts);
348                 mnt_fs_set_options(fs, opts);
349         }
350
351         rc = mnt_context_prepare_mount(cxt);
352         if (rc) {
353                 nfs_error(_("%s: failed to prepare mount: %s\n"),
354                                         progname, strerror(-rc));
355                 goto err;
356         }
357
358         rc = try_mount(cxt, FOREGROUND);
359
360         if (rc == EX_BG) {
361                 printf(_("%s: backgrounding \"%s\"\n"),
362                         progname, mnt_context_get_source(cxt));
363                 printf(_("%s: mount options: \"%s\"\n"),
364                         progname, opts);
365
366                 fflush(stdout);
367
368                 if (daemon(0, 0)) {
369                         nfs_error(_("%s: failed to start "
370                                         "background process: %s\n"),
371                                         progname, strerror(errno));
372                         exit(EX_FAIL);
373                 }
374
375                 rc = try_mount(cxt, BACKGROUND);
376
377                 if (verbose && rc)
378                         printf(_("%s: giving up \"%s\"\n"),
379                                 progname, mnt_context_get_source(cxt));
380         }
381
382         mnt_context_set_syscall_status(cxt, rc == EX_SUCCESS ? 0 : -1);
383         mnt_context_finalize_mount(cxt);        /* mtab update */
384         return rc;
385 err:
386         return EX_FAIL;
387 }
388
389 int main(int argc, char *argv[])
390 {
391         struct libmnt_context *cxt;
392         int rc;
393
394         mnt_init_debug(0);
395         cxt = mnt_new_context();
396         if (!cxt) {
397                 nfs_error(_("Can't initilize libmount: %s"),
398                                         strerror(errno));
399                 rc = EX_FAIL;
400                 goto done;
401         }
402
403         progname = basename(argv[0]);
404         nfs_mount_data_version = discover_nfs_mount_data_version(&string);
405
406         if(strncmp(progname, "umount", 6) == 0)
407                 rc = umount_main(cxt, argc, argv);
408         else
409                 rc = mount_main(cxt, argc, argv);
410 done:
411         mnt_free_context(cxt);
412         return rc;
413 }