cf6e58c512ade3235ab63cbf1089c6c85cd1f756
[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 *nfs_opts)
65 {
66         char *o = NULL;
67
68         mnt_fs_set_attributes(fs, nfs_opts);    /* for non-mtab systems */
69
70         /* for mtab create a new options list */
71         mnt_optstr_append_option(&o, mnt_fs_get_vfs_options(fs), NULL);
72         mnt_optstr_append_option(&o, nfs_opts, NULL);
73         mnt_optstr_append_option(&o, mnt_fs_get_user_options(fs), NULL);
74
75         mnt_fs_set_options(fs, o);
76         free(o);
77 }
78
79 /*
80  * Retrieve mount options from mtab (or /dev/.mount/utab) called from umount.nfs.
81  *
82  * The result can passed to free().
83  */
84 char *retrieve_mount_options(struct libmnt_fs *fs)
85 {
86         const char *opts;
87
88         if (!fs)
89                 return NULL;
90
91         opts = mnt_fs_get_attributes(fs);       /* /dev/.mount/utab */
92         if (opts)
93                 return strdup(opts);
94
95         return mnt_fs_strdup_options(fs);       /* /etc/mtab */
96 }
97
98 static int try_mount(struct libmnt_context *cxt, int bg)
99 {
100         struct libmnt_fs *fs;
101         const char *p;
102         char *src = NULL, *tgt = NULL, *type = NULL, *opts = NULL;
103         unsigned long flags = 0;
104         int fake, ret = 0;
105
106         fs = mnt_context_get_fs(cxt);
107
108         /* libmount returns read-only pointers (const char)
109          * so, reallocate for nfsmount() functions.
110          */
111         if ((p = mnt_fs_get_source(fs)))        /* spec */
112                 src = strdup(p);
113         if ((p = mnt_fs_get_target(fs)))        /* mountpoint */
114                 tgt = strdup(p);
115         if ((p = mnt_fs_get_fstype(fs)))        /* FS type */
116                 type = strdup(p);
117         if ((p = mnt_fs_get_fs_options(fs)))    /* mount options */
118                 opts = strdup(p);
119
120         mnt_context_get_mflags(cxt, &flags);    /* mount(2) flags */
121         fake = mnt_context_is_fake(cxt);
122
123         if (string)
124                 ret = nfsmount_string(src, tgt, type, flags, &opts, fake, bg);
125
126         else if (strcmp(type, "nfs4") == 0)
127                 ret = nfs4mount(src, tgt, flags, &opts, fake, bg);
128         else
129                 ret = nfsmount(src, tgt, flags, &opts, fake, bg);
130
131         /* Store mount options if not called with mount --no-mtab */
132         if (!ret && !mnt_context_is_nomtab(cxt))
133                 store_mount_options(fs, opts);
134
135         free(src);
136         free(tgt);
137         free(type);
138         free(opts);
139
140         return ret;
141 }
142
143 /* returns: error = -1, success = 0 , unknown = 1 */
144 static int is_vers4(struct libmnt_context *cxt)
145 {
146         struct libmnt_fs *fs = mnt_context_get_fs(cxt);
147         struct libmnt_table *tb = NULL;
148         const char *src = mnt_context_get_source(cxt),
149                    *tgt = mnt_context_get_target(cxt);
150         int rc = 1;
151
152         if (!src || !tgt)
153                 return -1;
154
155         if (!mnt_fs_is_kernel(fs)) {
156                 struct libmnt_table *tb = mnt_new_table_from_file("/proc/mounts");
157
158                 if (!tb)
159                         return -1;
160                 fs = mnt_table_find_pair(tb, src, tgt, MNT_ITER_BACKWARD);
161         }
162
163         if (fs) {
164                 const char *type = mnt_fs_get_fstype(fs);
165                 if (type && strcmp(type, "nfs4") == 0)
166                         rc = 0;
167         }
168         mnt_free_table(tb);
169         return rc;
170 }
171
172 static int umount_main(struct libmnt_context *cxt, int argc, char **argv)
173 {
174         int rc, c;
175         char *spec = NULL, *opts = NULL;
176
177         static const struct option longopts[] = {
178                 { "force", 0, 0, 'f' },
179                 { "help", 0, 0, 'h' },
180                 { "no-mtab", 0, 0, 'n' },
181                 { "verbose", 0, 0, 'v' },
182                 { "read-only", 0, 0, 'r' },
183                 { "lazy", 0, 0, 'l' },
184                 { "types", 1, 0, 't' },
185                 { NULL, 0, 0, 0 }
186         };
187
188         mnt_context_init_helper(cxt, MNT_ACT_UMOUNT, 0);
189
190         while ((c = getopt_long (argc, argv, "fvnrlh", longopts, NULL)) != -1) {
191
192                 rc = mnt_context_helper_setopt(cxt, c, optarg);
193                 if (rc == 0)            /* valid option */
194                         continue;
195                 if (rc < 0)             /* error (probably ENOMEM) */
196                         goto err;
197                                         /* rc==1 means unknow option */
198                 umount_usage();
199                 return EX_USAGE;
200         }
201
202         if (optind < argc)
203                 spec = argv[optind++];
204
205         if (!spec || (*spec != '/' && strchr(spec,':') == NULL)) {
206                 nfs_error(_("%s: no mount point provided"), progname);
207                 return EX_USAGE;
208         }
209
210         if (mnt_context_set_target(cxt, spec))
211                 goto err;
212         if (mnt_context_set_fstype_pattern(cxt, "nfs,nfs4"))    /* restrict filesystems */
213                 goto err;
214
215         /* read mtab/fstab, evaluate permissions, etc. */
216         rc = mnt_context_prepare_umount(cxt);
217         if (rc) {
218                 nfs_error(_("%s: failed to prepare umount: %s\n"),
219                                         progname, strerror(-rc));
220                 goto err;
221         }
222
223         opts = retrieve_mount_options(mnt_context_get_fs(cxt));
224
225         if (!mnt_context_is_lazy(cxt)) {
226                 if (opts) {
227                         /* we have full FS description (e.g. from mtab or /proc) */
228                         switch (is_vers4(cxt)) {
229                         case 0:
230                                 /* We ignore the error from nfs_umount23.
231                                  * If the actual umount succeeds (in del_mtab),
232                                  * we don't want to signal an error, as that
233                                  * could cause /sbin/mount to retry!
234                                  */
235                                 nfs_umount23(mnt_context_get_source(cxt), opts);
236                                 break;
237                         case 1:                 /* unknown */
238                                 break;
239                         default:                /* error */
240                                 goto err;
241                         }
242                 } else
243                         /* strange, no entry in mtab or /proc not mounted */
244                         nfs_umount23(spec, "tcp,v3");
245         }
246
247         rc = mnt_context_do_umount(cxt);        /* call umount(2) syscall */
248         mnt_context_finalize_mount(cxt);        /* mtab update */
249
250         if (rc && !mnt_context_get_status(cxt)) {
251                 /* mnt_context_do_umount() returns errno if umount(2) failed */
252                 umount_error(rc, spec);
253                 goto err;
254         }
255
256         free(opts);
257         return EX_SUCCESS;
258 err:
259         free(opts);
260         return EX_FAIL;
261 }
262
263 static int mount_main(struct libmnt_context *cxt, int argc, char **argv)
264 {
265         int rc, c;
266         struct libmnt_fs *fs;
267         char *spec = NULL, *mount_point = NULL, *opts = NULL;
268
269         static const struct option longopts[] = {
270           { "fake", 0, 0, 'f' },
271           { "help", 0, 0, 'h' },
272           { "no-mtab", 0, 0, 'n' },
273           { "read-only", 0, 0, 'r' },
274           { "ro", 0, 0, 'r' },
275           { "verbose", 0, 0, 'v' },
276           { "version", 0, 0, 'V' },
277           { "read-write", 0, 0, 'w' },
278           { "rw", 0, 0, 'w' },
279           { "options", 1, 0, 'o' },
280           { "sloppy", 0, 0, 's' },
281           { NULL, 0, 0, 0 }
282         };
283
284         mount_config_init(progname);
285         mnt_context_init_helper(cxt, MNT_ACT_MOUNT, 0);
286
287         while ((c = getopt_long(argc, argv, "fhnrVvwo:s", longopts, NULL)) != -1) {
288
289                 rc = mnt_context_helper_setopt(cxt, c, optarg);
290                 if (rc == 0)            /* valid option */
291                         continue;
292                 if (rc < 0)             /* error (probably ENOMEM) */
293                         goto err;
294                                         /* rc==1 means unknow option */
295                 switch (c) {
296                 case 'V':
297                         printf("%s: ("PACKAGE_STRING")\n", progname);
298                         return EX_SUCCESS;
299                 case 'h':
300                 default:
301                         mount_usage();
302                         return EX_USAGE;
303                 }
304         }
305
306         if (optind < argc)
307                 spec = argv[optind++];
308         if (optind < argc)
309                 mount_point = argv[optind++];
310
311         if (!mount_point) {
312                 nfs_error(_("%s: no mount point provided"), progname);
313                 goto err;
314         }
315         if (!spec) {
316                 nfs_error(_("%s: no mount spec provided"), progname);
317                 goto err;
318         }
319
320         if (geteuid() != 0) {
321                 nfs_error(_("%s: not installed setuid - "
322                             "\"user\" NFS mounts not supported."), progname);
323                 goto err;
324         }
325
326         verbose = mnt_context_is_verbose(cxt);
327         sloppy = mnt_context_is_sloppy(cxt);
328         nomtab = mnt_context_is_nomtab(cxt);
329
330         if (strcmp(progname, "mount.nfs4") == 0)
331                 mnt_context_set_fstype(cxt, "nfs4");
332         else
333                 mnt_context_set_fstype(cxt, "nfs");     /* default */
334
335         rc = mnt_context_set_source(cxt, spec);
336         if (!rc)
337                 mnt_context_set_target(cxt, mount_point);
338         if (rc) {
339                 nfs_error(_("%s: failed to set spec or mountpoint: %s"),
340                                 progname, strerror(errno));
341                 goto err;
342         }
343
344         mount_point = mnt_resolve_path(mount_point,
345                                        mnt_context_get_cache(cxt));
346
347         if (chk_mountpoint(mount_point))
348                 goto err;
349         /*
350          * Concatenate mount options from the configuration file
351          */
352         fs = mnt_context_get_fs(cxt);
353         if (fs) {
354                 opts = mnt_fs_strdup_options(fs);
355
356                 opts = mount_config_opts(spec, mount_point, opts);
357                 mnt_fs_set_options(fs, opts);
358         }
359
360         rc = mnt_context_prepare_mount(cxt);
361         if (rc) {
362                 nfs_error(_("%s: failed to prepare mount: %s\n"),
363                                         progname, strerror(-rc));
364                 goto err;
365         }
366
367         rc = try_mount(cxt, FOREGROUND);
368
369         if (rc == EX_BG) {
370                 printf(_("%s: backgrounding \"%s\"\n"),
371                         progname, mnt_context_get_source(cxt));
372                 printf(_("%s: mount options: \"%s\"\n"),
373                         progname, opts);
374
375                 fflush(stdout);
376
377                 if (daemon(0, 0)) {
378                         nfs_error(_("%s: failed to start "
379                                         "background process: %s\n"),
380                                         progname, strerror(errno));
381                         exit(EX_FAIL);
382                 }
383
384                 rc = try_mount(cxt, BACKGROUND);
385
386                 if (verbose && rc)
387                         printf(_("%s: giving up \"%s\"\n"),
388                                 progname, mnt_context_get_source(cxt));
389         }
390
391         mnt_context_set_syscall_status(cxt, rc == EX_SUCCESS ? 0 : -1);
392         mnt_context_finalize_mount(cxt);        /* mtab update */
393         return rc;
394 err:
395         return EX_FAIL;
396 }
397
398 int main(int argc, char *argv[])
399 {
400         struct libmnt_context *cxt;
401         int rc;
402
403         mnt_init_debug(0);
404         cxt = mnt_new_context();
405         if (!cxt) {
406                 nfs_error(_("Can't initilize libmount: %s"),
407                                         strerror(errno));
408                 rc = EX_FAIL;
409                 goto done;
410         }
411
412         progname = basename(argv[0]);
413         nfs_mount_data_version = discover_nfs_mount_data_version(&string);
414
415         if(strncmp(progname, "umount", 6) == 0)
416                 rc = umount_main(cxt, argc, argv);
417         else
418                 rc = mount_main(cxt, argc, argv);
419 done:
420         mnt_free_context(cxt);
421         return rc;
422 }